blob: f6e10a735b7a0b6a0b545b990ec7e03f47350810 [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
Prashanth Bhatta9e143052015-12-04 11:56:47 -08002 * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28/**
29 * DOC: wlan_hdd_ipa.c
30 *
31 * WLAN HDD and ipa interface implementation
32 * Originally written by Qualcomm Atheros, Inc
33 */
34
35#ifdef IPA_OFFLOAD
36
37/* Include Files */
Mohit Khannafa99aea2016-05-12 21:43:13 -070038#include <linux/ipa.h>
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080039#include <wlan_hdd_includes.h>
40#include <wlan_hdd_ipa.h>
41
42#include <linux/etherdevice.h>
43#include <linux/atomic.h>
44#include <linux/netdevice.h>
45#include <linux/skbuff.h>
46#include <linux/list.h>
47#include <linux/debugfs.h>
48#include <linux/inetdevice.h>
49#include <linux/ip.h>
50#include <wlan_hdd_softap_tx_rx.h>
51#include <ol_txrx_osif_api.h>
Manjunathappa Prakash3454fd62016-04-01 08:52:06 -070052#include <cdp_txrx_peer_ops.h>
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080053
54#include "cds_sched.h"
55
56#include "wma.h"
57#include "wma_api.h"
58
Dhanashri Atreb08959a2016-03-01 17:28:03 -080059#include "cdp_txrx_ipa.h"
60
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080061#define HDD_IPA_DESC_BUFFER_RATIO 4
62#define HDD_IPA_IPV4_NAME_EXT "_ipv4"
63#define HDD_IPA_IPV6_NAME_EXT "_ipv6"
64
65#define HDD_IPA_RX_INACTIVITY_MSEC_DELAY 1000
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080066#define HDD_IPA_UC_WLAN_8023_HDR_SIZE 14
67/* WDI TX and RX PIPE */
68#define HDD_IPA_UC_NUM_WDI_PIPE 2
69#define HDD_IPA_UC_MAX_PENDING_EVENT 33
70
71#define HDD_IPA_UC_DEBUG_DUMMY_MEM_SIZE 32000
72#define HDD_IPA_UC_RT_DEBUG_PERIOD 300
73#define HDD_IPA_UC_RT_DEBUG_BUF_COUNT 30
74#define HDD_IPA_UC_RT_DEBUG_FILL_INTERVAL 10000
75
76#define HDD_IPA_WLAN_HDR_DES_MAC_OFFSET 0
77#define HDD_IPA_MAX_IFACE 3
78#define HDD_IPA_MAX_SYSBAM_PIPE 4
79#define HDD_IPA_RX_PIPE HDD_IPA_MAX_IFACE
80#define HDD_IPA_ENABLE_MASK BIT(0)
81#define HDD_IPA_PRE_FILTER_ENABLE_MASK BIT(1)
82#define HDD_IPA_IPV6_ENABLE_MASK BIT(2)
83#define HDD_IPA_RM_ENABLE_MASK BIT(3)
84#define HDD_IPA_CLK_SCALING_ENABLE_MASK BIT(4)
85#define HDD_IPA_UC_ENABLE_MASK BIT(5)
86#define HDD_IPA_UC_STA_ENABLE_MASK BIT(6)
87#define HDD_IPA_REAL_TIME_DEBUGGING BIT(8)
88
Yun Parkf19e07d2015-11-20 11:34:27 -080089#define HDD_IPA_MAX_PENDING_EVENT_COUNT 20
90
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080091typedef enum {
92 HDD_IPA_UC_OPCODE_TX_SUSPEND = 0,
93 HDD_IPA_UC_OPCODE_TX_RESUME = 1,
94 HDD_IPA_UC_OPCODE_RX_SUSPEND = 2,
95 HDD_IPA_UC_OPCODE_RX_RESUME = 3,
96 HDD_IPA_UC_OPCODE_STATS = 4,
97 /* keep this last */
98 HDD_IPA_UC_OPCODE_MAX
99} hdd_ipa_uc_op_code;
100
101/**
102 * enum - Reason codes for stat query
103 *
104 * @HDD_IPA_UC_STAT_REASON_NONE: Initial value
105 * @HDD_IPA_UC_STAT_REASON_DEBUG: For debug/info
106 * @HDD_IPA_UC_STAT_REASON_BW_CAL: For bandwidth calibration
107 */
108enum {
109 HDD_IPA_UC_STAT_REASON_NONE,
110 HDD_IPA_UC_STAT_REASON_DEBUG,
111 HDD_IPA_UC_STAT_REASON_BW_CAL
112};
113
114/**
115 * enum hdd_ipa_rm_state - IPA resource manager state
116 * @HDD_IPA_RM_RELEASED: PROD pipe resource released
117 * @HDD_IPA_RM_GRANT_PENDING: PROD pipe resource requested but not granted yet
118 * @HDD_IPA_RM_GRANTED: PROD pipe resource granted
119 */
120enum hdd_ipa_rm_state {
121 HDD_IPA_RM_RELEASED,
122 HDD_IPA_RM_GRANT_PENDING,
123 HDD_IPA_RM_GRANTED,
124};
125
126struct llc_snap_hdr {
127 uint8_t dsap;
128 uint8_t ssap;
129 uint8_t resv[4];
130 __be16 eth_type;
131} __packed;
132
Leo Chang3bc8fed2015-11-13 10:59:47 -0800133/**
134 * struct hdd_ipa_tx_hdr - header type which IPA should handle to TX packet
135 * @eth: ether II header
136 * @llc_snap: LLC snap header
137 *
138 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800139struct hdd_ipa_tx_hdr {
140 struct ethhdr eth;
141 struct llc_snap_hdr llc_snap;
142} __packed;
143
Leo Chang3bc8fed2015-11-13 10:59:47 -0800144/**
145 * struct frag_header - fragment header type registered to IPA hardware
146 * @length: fragment length
147 * @reserved1: Reserved not used
148 * @reserved2: Reserved not used
149 *
150 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800151struct frag_header {
Leo Chang3bc8fed2015-11-13 10:59:47 -0800152 uint16_t length;
153 uint32_t reserved1;
154 uint32_t reserved2;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800155} __packed;
156
Leo Chang3bc8fed2015-11-13 10:59:47 -0800157/**
158 * struct ipa_header - ipa header type registered to IPA hardware
159 * @vdev_id: vdev id
160 * @reserved: Reserved not used
161 *
162 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800163struct ipa_header {
164 uint32_t
165 vdev_id:8, /* vdev_id field is LSB of IPA DESC */
166 reserved:24;
167} __packed;
168
Leo Chang3bc8fed2015-11-13 10:59:47 -0800169/**
170 * struct hdd_ipa_uc_tx_hdr - full tx header registered to IPA hardware
171 * @frag_hd: fragment header
172 * @ipa_hd: ipa header
173 * @eth: ether II header
174 *
175 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800176struct hdd_ipa_uc_tx_hdr {
177 struct frag_header frag_hd;
178 struct ipa_header ipa_hd;
179 struct ethhdr eth;
180} __packed;
181
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800182/**
183 * struct hdd_ipa_cld_hdr - IPA CLD Header
184 * @reserved: reserved fields
185 * @iface_id: interface ID
186 * @sta_id: Station ID
187 *
188 * Packed 32-bit structure
189 * +----------+----------+--------------+--------+
190 * | Reserved | QCMAP ID | interface id | STA ID |
191 * +----------+----------+--------------+--------+
192 */
193struct hdd_ipa_cld_hdr {
194 uint8_t reserved[2];
195 uint8_t iface_id;
196 uint8_t sta_id;
197} __packed;
198
199struct hdd_ipa_rx_hdr {
200 struct hdd_ipa_cld_hdr cld_hdr;
201 struct ethhdr eth;
202} __packed;
203
204struct hdd_ipa_pm_tx_cb {
205 struct hdd_ipa_iface_context *iface_context;
206 struct ipa_rx_data *ipa_tx_desc;
207};
208
209struct hdd_ipa_uc_rx_hdr {
210 struct ethhdr eth;
211} __packed;
212
213struct hdd_ipa_sys_pipe {
214 uint32_t conn_hdl;
215 uint8_t conn_hdl_valid;
216 struct ipa_sys_connect_params ipa_sys_params;
217};
218
219struct hdd_ipa_iface_stats {
220 uint64_t num_tx;
221 uint64_t num_tx_drop;
222 uint64_t num_tx_err;
223 uint64_t num_tx_cac_drop;
224 uint64_t num_rx_prefilter;
225 uint64_t num_rx_ipa_excep;
226 uint64_t num_rx_recv;
227 uint64_t num_rx_recv_mul;
228 uint64_t num_rx_send_desc_err;
229 uint64_t max_rx_mul;
230};
231
232struct hdd_ipa_priv;
233
234struct hdd_ipa_iface_context {
235 struct hdd_ipa_priv *hdd_ipa;
236 hdd_adapter_t *adapter;
237 void *tl_context;
238
239 enum ipa_client_type cons_client;
240 enum ipa_client_type prod_client;
241
242 uint8_t iface_id; /* This iface ID */
243 uint8_t sta_id; /* This iface station ID */
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530244 qdf_spinlock_t interface_lock;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800245 uint32_t ifa_address;
246 struct hdd_ipa_iface_stats stats;
Yun Park8292dcb2016-10-07 16:46:06 -0700247 uint32_t offload_enabled;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800248};
249
250struct hdd_ipa_stats {
251 uint32_t event[IPA_WLAN_EVENT_MAX];
252 uint64_t num_send_msg;
253 uint64_t num_free_msg;
254
255 uint64_t num_rm_grant;
256 uint64_t num_rm_release;
257 uint64_t num_rm_grant_imm;
258 uint64_t num_cons_perf_req;
259 uint64_t num_prod_perf_req;
260
261 uint64_t num_rx_drop;
262 uint64_t num_rx_ipa_tx_dp;
263 uint64_t num_rx_ipa_splice;
264 uint64_t num_rx_ipa_loop;
265 uint64_t num_rx_ipa_tx_dp_err;
266 uint64_t num_rx_ipa_write_done;
267 uint64_t num_max_ipa_tx_mul;
268 uint64_t num_rx_ipa_hw_maxed_out;
269 uint64_t max_pend_q_cnt;
270
271 uint64_t num_tx_comp_cnt;
272 uint64_t num_tx_queued;
273 uint64_t num_tx_dequeued;
274 uint64_t num_max_pm_queue;
275
276 uint64_t num_freeq_empty;
277 uint64_t num_pri_freeq_empty;
278 uint64_t num_rx_excep;
279 uint64_t num_tx_bcmc;
280 uint64_t num_tx_bcmc_err;
281};
282
283struct ipa_uc_stas_map {
284 bool is_reserved;
285 uint8_t sta_id;
286};
287struct op_msg_type {
288 uint8_t msg_t;
289 uint8_t rsvd;
290 uint16_t op_code;
291 uint16_t len;
292 uint16_t rsvd_snd;
293};
294
295struct ipa_uc_fw_stats {
296 uint32_t tx_comp_ring_base;
297 uint32_t tx_comp_ring_size;
298 uint32_t tx_comp_ring_dbell_addr;
299 uint32_t tx_comp_ring_dbell_ind_val;
300 uint32_t tx_comp_ring_dbell_cached_val;
301 uint32_t tx_pkts_enqueued;
302 uint32_t tx_pkts_completed;
303 uint32_t tx_is_suspend;
304 uint32_t tx_reserved;
305 uint32_t rx_ind_ring_base;
306 uint32_t rx_ind_ring_size;
307 uint32_t rx_ind_ring_dbell_addr;
308 uint32_t rx_ind_ring_dbell_ind_val;
309 uint32_t rx_ind_ring_dbell_ind_cached_val;
310 uint32_t rx_ind_ring_rdidx_addr;
311 uint32_t rx_ind_ring_rd_idx_cached_val;
312 uint32_t rx_refill_idx;
313 uint32_t rx_num_pkts_indicated;
314 uint32_t rx_buf_refilled;
315 uint32_t rx_num_ind_drop_no_space;
316 uint32_t rx_num_ind_drop_no_buf;
317 uint32_t rx_is_suspend;
318 uint32_t rx_reserved;
319};
320
321struct ipa_uc_pending_event {
Anurag Chouhanffb21542016-02-17 14:33:03 +0530322 qdf_list_node_t node;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800323 hdd_adapter_t *adapter;
324 enum ipa_wlan_event type;
325 uint8_t sta_id;
Anurag Chouhan6d760662016-02-20 16:05:43 +0530326 uint8_t mac_addr[QDF_MAC_ADDR_SIZE];
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800327};
328
329/**
330 * struct uc_rm_work_struct
331 * @work: uC RM work
332 * @event: IPA RM event
333 */
334struct uc_rm_work_struct {
335 struct work_struct work;
336 enum ipa_rm_event event;
337};
338
339/**
340 * struct uc_op_work_struct
341 * @work: uC OP work
342 * @msg: OP message
343 */
344struct uc_op_work_struct {
345 struct work_struct work;
346 struct op_msg_type *msg;
347};
348static uint8_t vdev_to_iface[CSR_ROAM_SESSION_MAX];
349
350/**
351 * struct uc_rt_debug_info
352 * @time: system time
353 * @ipa_excep_count: IPA exception packet count
354 * @rx_drop_count: IPA Rx drop packet count
355 * @net_sent_count: IPA Rx packet sent to network stack count
356 * @rx_discard_count: IPA Rx discard packet count
357 * @rx_mcbc_count: IPA Rx BCMC packet count
358 * @tx_mcbc_count: IPA Tx BCMC packet countt
359 * @tx_fwd_count: IPA Tx forward packet count
360 * @rx_destructor_call: IPA Rx packet destructor count
361 */
362struct uc_rt_debug_info {
Anurag Chouhan6d760662016-02-20 16:05:43 +0530363 unsigned long time;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800364 uint64_t ipa_excep_count;
365 uint64_t rx_drop_count;
366 uint64_t net_sent_count;
367 uint64_t rx_discard_count;
368 uint64_t rx_mcbc_count;
369 uint64_t tx_mcbc_count;
370 uint64_t tx_fwd_count;
371 uint64_t rx_destructor_call;
372};
373
374struct hdd_ipa_priv {
375 struct hdd_ipa_sys_pipe sys_pipe[HDD_IPA_MAX_SYSBAM_PIPE];
376 struct hdd_ipa_iface_context iface_context[HDD_IPA_MAX_IFACE];
377 uint8_t num_iface;
378 enum hdd_ipa_rm_state rm_state;
379 /*
Nirav Shahcbc6d722016-03-01 16:24:53 +0530380 * IPA driver can send RM notifications with IRQ disabled so using qdf
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800381 * APIs as it is taken care gracefully. Without this, kernel would throw
382 * an warning if spin_lock_bh is used while IRQ is disabled
383 */
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530384 qdf_spinlock_t rm_lock;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800385 struct uc_rm_work_struct uc_rm_work;
386 struct uc_op_work_struct uc_op_work[HDD_IPA_UC_OPCODE_MAX];
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530387 qdf_wake_lock_t wake_lock;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800388 struct delayed_work wake_lock_work;
389 bool wake_lock_released;
390
391 enum ipa_client_type prod_client;
392
393 atomic_t tx_ref_cnt;
Nirav Shahcbc6d722016-03-01 16:24:53 +0530394 qdf_nbuf_queue_t pm_queue_head;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800395 struct work_struct pm_work;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530396 qdf_spinlock_t pm_lock;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800397 bool suspended;
398
399 uint32_t pending_hw_desc_cnt;
400 uint32_t hw_desc_cnt;
401 spinlock_t q_lock;
402 uint32_t freeq_cnt;
403 struct list_head free_desc_head;
404
405 uint32_t pend_q_cnt;
406 struct list_head pend_desc_head;
407
408 hdd_context_t *hdd_ctx;
409
410 struct dentry *debugfs_dir;
411 struct hdd_ipa_stats stats;
412
413 struct notifier_block ipv4_notifier;
414 uint32_t curr_prod_bw;
415 uint32_t curr_cons_bw;
416
417 uint8_t activated_fw_pipe;
418 uint8_t sap_num_connected_sta;
419 uint8_t sta_connected;
420 uint32_t tx_pipe_handle;
421 uint32_t rx_pipe_handle;
422 bool resource_loading;
423 bool resource_unloading;
424 bool pending_cons_req;
425 struct ipa_uc_stas_map assoc_stas_map[WLAN_MAX_STA_COUNT];
Anurag Chouhanffb21542016-02-17 14:33:03 +0530426 qdf_list_t pending_event;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530427 qdf_mutex_t event_lock;
Leo Change3e49442015-10-26 20:07:13 -0700428 bool ipa_pipes_down;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800429 uint32_t ipa_tx_packets_diff;
430 uint32_t ipa_rx_packets_diff;
431 uint32_t ipa_p_tx_packets;
432 uint32_t ipa_p_rx_packets;
433 uint32_t stat_req_reason;
434 uint64_t ipa_tx_forward;
435 uint64_t ipa_rx_discard;
436 uint64_t ipa_rx_net_send_count;
437 uint64_t ipa_rx_internel_drop_count;
438 uint64_t ipa_rx_destructor_count;
Anurag Chouhan210db072016-02-22 18:42:15 +0530439 qdf_mc_timer_t rt_debug_timer;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800440 struct uc_rt_debug_info rt_bug_buffer[HDD_IPA_UC_RT_DEBUG_BUF_COUNT];
441 unsigned int rt_buf_fill_index;
Anurag Chouhan210db072016-02-22 18:42:15 +0530442 qdf_mc_timer_t rt_debug_fill_timer;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530443 qdf_mutex_t rt_debug_lock;
444 qdf_mutex_t ipa_lock;
Dhanashri Atreb08959a2016-03-01 17:28:03 -0800445 struct ol_txrx_ipa_resources ipa_resource;
Leo Chang3bc8fed2015-11-13 10:59:47 -0800446 /* IPA UC doorbell registers paddr */
Anurag Chouhan6d760662016-02-20 16:05:43 +0530447 qdf_dma_addr_t tx_comp_doorbell_paddr;
448 qdf_dma_addr_t rx_ready_doorbell_paddr;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800449};
450
Houston Hoffman43d47fa2016-02-24 16:34:30 -0800451/**
Houston Hoffman23e76f92016-02-26 12:19:11 -0800452 * FIXME: The following conversion routines are just stubs.
Houston Hoffman43d47fa2016-02-24 16:34:30 -0800453 * They will be implemented fully by another update.
454 * The stubs will let the compile go ahead, and functionality
455 * is broken.
456 * This should be OK and IPA is not enabled yet
457 */
Jeff Johnsond7720632016-10-05 16:04:32 -0700458static void *wlan_hdd_stub_priv_to_addr(uint32_t priv)
Houston Hoffman43d47fa2016-02-24 16:34:30 -0800459{
460 void *vaddr;
461 uint32_t ipa_priv = priv;
462
463 vaddr = &ipa_priv; /* just to use the var */
464 vaddr = NULL;
465 return vaddr;
466}
467
Jeff Johnsond7720632016-10-05 16:04:32 -0700468static uint32_t wlan_hdd_stub_addr_to_priv(void *ptr)
Houston Hoffman43d47fa2016-02-24 16:34:30 -0800469{
470 uint32_t ipa_priv = 0;
471
472 BUG_ON(ptr == NULL);
473 return ipa_priv;
474}
Leo Changcc923e22016-06-16 15:29:03 -0700475
476#define HDD_IPA_WLAN_FRAG_HEADER sizeof(struct frag_header)
477#define HDD_IPA_WLAN_IPA_HEADER sizeof(struct ipa_header)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800478#define HDD_IPA_WLAN_CLD_HDR_LEN sizeof(struct hdd_ipa_cld_hdr)
479#define HDD_IPA_UC_WLAN_CLD_HDR_LEN 0
480#define HDD_IPA_WLAN_TX_HDR_LEN sizeof(struct hdd_ipa_tx_hdr)
481#define HDD_IPA_UC_WLAN_TX_HDR_LEN sizeof(struct hdd_ipa_uc_tx_hdr)
482#define HDD_IPA_WLAN_RX_HDR_LEN sizeof(struct hdd_ipa_rx_hdr)
483#define HDD_IPA_UC_WLAN_RX_HDR_LEN sizeof(struct hdd_ipa_uc_rx_hdr)
Leo Changcc923e22016-06-16 15:29:03 -0700484#define HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET \
485 (HDD_IPA_WLAN_FRAG_HEADER + HDD_IPA_WLAN_IPA_HEADER)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800486
Leo Chang3bc8fed2015-11-13 10:59:47 -0800487#define HDD_IPA_FW_RX_DESC_DISCARD_M 0x1
488#define HDD_IPA_FW_RX_DESC_FORWARD_M 0x2
489
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800490#define HDD_IPA_GET_IFACE_ID(_data) \
491 (((struct hdd_ipa_cld_hdr *) (_data))->iface_id)
492
493#define HDD_IPA_LOG(LVL, fmt, args ...) \
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530494 QDF_TRACE(QDF_MODULE_ID_HDD, LVL, \
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800495 "%s:%d: "fmt, __func__, __LINE__, ## args)
496
Govind Singhb6a89772016-08-12 11:23:35 +0530497#define HDD_IPA_DP_LOG(LVL, fmt, args...) \
498 QDF_TRACE(QDF_MODULE_ID_HDD_DATA, LVL, \
499 "%s:%d: "fmt, __func__, __LINE__, ## args)
500
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800501#define HDD_IPA_DBG_DUMP(_lvl, _prefix, _buf, _len) \
502 do { \
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530503 QDF_TRACE(QDF_MODULE_ID_HDD, _lvl, "%s:", _prefix); \
504 QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, _lvl, _buf, _len); \
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800505 } while (0)
506
507#define HDD_IPA_IS_CONFIG_ENABLED(_hdd_ctx, _mask) \
508 (((_hdd_ctx)->config->IpaConfig & (_mask)) == (_mask))
509
510#define HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa) \
511 do { \
512 hdd_ipa->ipa_rx_internel_drop_count++; \
513 } while (0)
514#define HDD_IPA_INCREASE_NET_SEND_COUNT(hdd_ipa) \
515 do { \
516 hdd_ipa->ipa_rx_net_send_count++; \
517 } while (0)
518#define HDD_BW_GET_DIFF(_x, _y) (unsigned long)((ULONG_MAX - (_y)) + (_x) + 1)
519
Leo Chang07b28f62016-05-11 12:29:22 -0700520#if defined (QCA_WIFI_3_0) && defined (CONFIG_IPA3)
Dhanashri Atreb08959a2016-03-01 17:28:03 -0800521#define HDD_IPA_WDI2_SET(pipe_in, ipa_ctxt) \
522do { \
523 pipe_in.u.ul.rdy_ring_rp_va = \
524 ipa_ctxt->ipa_resource.rx_proc_done_idx_vaddr; \
525 pipe_in.u.ul.rdy_comp_ring_base_pa = \
526 ipa_ctxt->ipa_resource.rx2_rdy_ring_base_paddr;\
527 pipe_in.u.ul.rdy_comp_ring_size = \
528 ipa_ctxt->ipa_resource.rx2_rdy_ring_size; \
529 pipe_in.u.ul.rdy_comp_ring_wp_pa = \
530 ipa_ctxt->ipa_resource.rx2_proc_done_idx_paddr; \
531 pipe_in.u.ul.rdy_comp_ring_wp_va = \
532 ipa_ctxt->ipa_resource.rx2_proc_done_idx_vaddr; \
Leo Chang3bc8fed2015-11-13 10:59:47 -0800533} while (0)
534#else
535/* Do nothing */
536#define HDD_IPA_WDI2_SET(pipe_in, ipa_ctxt)
Leo Chang07b28f62016-05-11 12:29:22 -0700537#endif /* IPA3 */
Leo Chang3bc8fed2015-11-13 10:59:47 -0800538
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800539static struct hdd_ipa_adapter_2_client {
540 enum ipa_client_type cons_client;
541 enum ipa_client_type prod_client;
542} hdd_ipa_adapter_2_client[HDD_IPA_MAX_IFACE] = {
543 {
544 IPA_CLIENT_WLAN2_CONS, IPA_CLIENT_WLAN1_PROD
545 }, {
546 IPA_CLIENT_WLAN3_CONS, IPA_CLIENT_WLAN1_PROD
547 }, {
548 IPA_CLIENT_WLAN4_CONS, IPA_CLIENT_WLAN1_PROD
549 },
550};
551
552/* For Tx pipes, use Ethernet-II Header format */
553struct hdd_ipa_uc_tx_hdr ipa_uc_tx_hdr = {
554 {
Leo Chang3bc8fed2015-11-13 10:59:47 -0800555 0x0000,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800556 0x00000000,
557 0x00000000
558 },
559 {
560 0x00000000
561 },
562 {
563 {0x00, 0x03, 0x7f, 0xaa, 0xbb, 0xcc},
564 {0x00, 0x03, 0x7f, 0xdd, 0xee, 0xff},
565 0x0008
566 }
567};
568
569/* For Tx pipes, use 802.3 Header format */
570static struct hdd_ipa_tx_hdr ipa_tx_hdr = {
571 {
572 {0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xFF},
573 {0xDE, 0xAD, 0xBE, 0xEF, 0xFF, 0xFF},
574 0x00 /* length can be zero */
575 },
576 {
577 /* LLC SNAP header 8 bytes */
578 0xaa, 0xaa,
579 {0x03, 0x00, 0x00, 0x00},
580 0x0008 /* type value(2 bytes) ,filled by wlan */
581 /* 0x0800 - IPV4, 0x86dd - IPV6 */
582 }
583};
584
585static const char *op_string[] = {
586 "TX_SUSPEND",
587 "TX_RESUME",
588 "RX_SUSPEND",
589 "RX_RESUME",
590 "STATS",
591};
592
593static struct hdd_ipa_priv *ghdd_ipa;
594
595/* Local Function Prototypes */
596static void hdd_ipa_i2w_cb(void *priv, enum ipa_dp_evt_type evt,
597 unsigned long data);
598static void hdd_ipa_w2i_cb(void *priv, enum ipa_dp_evt_type evt,
599 unsigned long data);
600
601static void hdd_ipa_cleanup_iface(struct hdd_ipa_iface_context *iface_context);
Mohit Khannafa99aea2016-05-12 21:43:13 -0700602static void hdd_ipa_uc_proc_pending_event (struct hdd_ipa_priv *hdd_ipa);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800603
604/**
605 * hdd_ipa_is_enabled() - Is IPA enabled?
606 * @hdd_ctx: Global HDD context
607 *
608 * Return: true if IPA is enabled, false otherwise
609 */
610bool hdd_ipa_is_enabled(hdd_context_t *hdd_ctx)
611{
612 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_ENABLE_MASK);
613}
614
615/**
616 * hdd_ipa_uc_is_enabled() - Is IPA uC offload enabled?
617 * @hdd_ctx: Global HDD context
618 *
619 * Return: true if IPA uC offload is enabled, false otherwise
620 */
621bool hdd_ipa_uc_is_enabled(hdd_context_t *hdd_ctx)
622{
623 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_UC_ENABLE_MASK);
624}
625
626/**
627 * hdd_ipa_uc_sta_is_enabled() - Is STA mode IPA uC offload enabled?
628 * @hdd_ctx: Global HDD context
629 *
630 * Return: true if STA mode IPA uC offload is enabled, false otherwise
631 */
632static inline bool hdd_ipa_uc_sta_is_enabled(hdd_context_t *hdd_ctx)
633{
634 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_UC_STA_ENABLE_MASK);
635}
636
637/**
638 * hdd_ipa_is_pre_filter_enabled() - Is IPA pre-filter enabled?
639 * @hdd_ipa: Global HDD IPA context
640 *
641 * Return: true if pre-filter is enabled, otherwise false
642 */
643static inline bool hdd_ipa_is_pre_filter_enabled(hdd_context_t *hdd_ctx)
644{
645 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx,
646 HDD_IPA_PRE_FILTER_ENABLE_MASK);
647}
648
649/**
650 * hdd_ipa_is_ipv6_enabled() - Is IPA IPv6 enabled?
651 * @hdd_ipa: Global HDD IPA context
652 *
653 * Return: true if IPv6 is enabled, otherwise false
654 */
655static inline bool hdd_ipa_is_ipv6_enabled(hdd_context_t *hdd_ctx)
656{
657 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_IPV6_ENABLE_MASK);
658}
659
660/**
661 * hdd_ipa_is_rm_enabled() - Is IPA resource manager enabled?
662 * @hdd_ipa: Global HDD IPA context
663 *
664 * Return: true if resource manager is enabled, otherwise false
665 */
666static inline bool hdd_ipa_is_rm_enabled(hdd_context_t *hdd_ctx)
667{
668 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_RM_ENABLE_MASK);
669}
670
671/**
672 * hdd_ipa_is_rt_debugging_enabled() - Is IPA real-time debug enabled?
673 * @hdd_ipa: Global HDD IPA context
674 *
675 * Return: true if resource manager is enabled, otherwise false
676 */
677static inline bool hdd_ipa_is_rt_debugging_enabled(hdd_context_t *hdd_ctx)
678{
679 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx, HDD_IPA_REAL_TIME_DEBUGGING);
680}
681
682/**
683 * hdd_ipa_is_clk_scaling_enabled() - Is IPA clock scaling enabled?
684 * @hdd_ipa: Global HDD IPA context
685 *
686 * Return: true if clock scaling is enabled, otherwise false
687 */
688static inline bool hdd_ipa_is_clk_scaling_enabled(hdd_context_t *hdd_ctx)
689{
690 return HDD_IPA_IS_CONFIG_ENABLED(hdd_ctx,
691 HDD_IPA_CLK_SCALING_ENABLE_MASK |
692 HDD_IPA_RM_ENABLE_MASK);
693}
694
695/**
696 * hdd_ipa_uc_rt_debug_host_fill - fill rt debug buffer
697 * @ctext: pointer to hdd context.
698 *
699 * If rt debug enabled, periodically called, and fill debug buffer
700 *
701 * Return: none
702 */
703static void hdd_ipa_uc_rt_debug_host_fill(void *ctext)
704{
705 hdd_context_t *hdd_ctx = (hdd_context_t *)ctext;
706 struct hdd_ipa_priv *hdd_ipa;
707 struct uc_rt_debug_info *dump_info = NULL;
708
709 if (wlan_hdd_validate_context(hdd_ctx))
710 return;
711
712 if (!hdd_ctx->hdd_ipa || !hdd_ipa_uc_is_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530713 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800714 "%s: IPA UC is not enabled", __func__);
715 return;
716 }
717
718 hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
719
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530720 qdf_mutex_acquire(&hdd_ipa->rt_debug_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800721 dump_info = &hdd_ipa->rt_bug_buffer[
722 hdd_ipa->rt_buf_fill_index % HDD_IPA_UC_RT_DEBUG_BUF_COUNT];
723
Anurag Chouhan210db072016-02-22 18:42:15 +0530724 dump_info->time = qdf_mc_timer_get_system_time();
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800725 dump_info->ipa_excep_count = hdd_ipa->stats.num_rx_excep;
726 dump_info->rx_drop_count = hdd_ipa->ipa_rx_internel_drop_count;
727 dump_info->net_sent_count = hdd_ipa->ipa_rx_net_send_count;
728 dump_info->rx_discard_count = hdd_ipa->ipa_rx_discard;
729 dump_info->tx_mcbc_count = hdd_ipa->stats.num_tx_bcmc;
730 dump_info->tx_fwd_count = hdd_ipa->ipa_tx_forward;
731 dump_info->rx_destructor_call = hdd_ipa->ipa_rx_destructor_count;
732 hdd_ipa->rt_buf_fill_index++;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530733 qdf_mutex_release(&hdd_ipa->rt_debug_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800734
Anurag Chouhan210db072016-02-22 18:42:15 +0530735 qdf_mc_timer_start(&hdd_ipa->rt_debug_fill_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800736 HDD_IPA_UC_RT_DEBUG_FILL_INTERVAL);
737}
738
739/**
740 * hdd_ipa_uc_rt_debug_host_dump - dump rt debug buffer
741 * @hdd_ctx: pointer to hdd context.
742 *
743 * If rt debug enabled, dump debug buffer contents based on requirement
744 *
745 * Return: none
746 */
747void hdd_ipa_uc_rt_debug_host_dump(hdd_context_t *hdd_ctx)
748{
749 struct hdd_ipa_priv *hdd_ipa;
750 unsigned int dump_count;
751 unsigned int dump_index;
752 struct uc_rt_debug_info *dump_info = NULL;
753
754 if (wlan_hdd_validate_context(hdd_ctx))
755 return;
756
757 hdd_ipa = hdd_ctx->hdd_ipa;
758 if (!hdd_ipa || !hdd_ipa_uc_is_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530759 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800760 "%s: IPA UC is not enabled", __func__);
761 return;
762 }
763
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530764 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800765 "========= WLAN-IPA DEBUG BUF DUMP ==========\n");
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530766 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800767 " TM : EXEP : DROP : NETS : MCBC : TXFD : DSTR : DSCD\n");
768
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530769 qdf_mutex_acquire(&hdd_ipa->rt_debug_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800770 for (dump_count = 0;
771 dump_count < HDD_IPA_UC_RT_DEBUG_BUF_COUNT;
772 dump_count++) {
773 dump_index = (hdd_ipa->rt_buf_fill_index + dump_count) %
774 HDD_IPA_UC_RT_DEBUG_BUF_COUNT;
775 dump_info = &hdd_ipa->rt_bug_buffer[dump_index];
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530776 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800777 "%12lu:%10llu:%10llu:%10llu:%10llu:%10llu:%10llu:%10llu\n",
778 dump_info->time, dump_info->ipa_excep_count,
779 dump_info->rx_drop_count, dump_info->net_sent_count,
780 dump_info->tx_mcbc_count, dump_info->tx_fwd_count,
781 dump_info->rx_destructor_call,
782 dump_info->rx_discard_count);
783 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530784 qdf_mutex_release(&hdd_ipa->rt_debug_lock);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530785 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800786 "======= WLAN-IPA DEBUG BUF DUMP END ========\n");
787}
788
789/**
790 * hdd_ipa_uc_rt_debug_handler - periodic memory health monitor handler
791 * @ctext: pointer to hdd context.
792 *
793 * periodically called by timer expire
794 * will try to alloc dummy memory and detect out of memory condition
795 * if out of memory detected, dump wlan-ipa stats
796 *
797 * Return: none
798 */
799static void hdd_ipa_uc_rt_debug_handler(void *ctext)
800{
801 hdd_context_t *hdd_ctx = (hdd_context_t *)ctext;
802 struct hdd_ipa_priv *hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
803 void *dummy_ptr = NULL;
804
805 if (wlan_hdd_validate_context(hdd_ctx))
806 return;
807
808 if (!hdd_ipa_is_rt_debugging_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530809 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800810 "%s: IPA RT debug is not enabled", __func__);
811 return;
812 }
813
814 /* Allocate dummy buffer periodically and free immediately. this will
815 * proactively detect OOM and if allocation fails dump ipa stats
816 */
817 dummy_ptr = kmalloc(HDD_IPA_UC_DEBUG_DUMMY_MEM_SIZE,
818 GFP_KERNEL | GFP_ATOMIC);
819 if (!dummy_ptr) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530820 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800821 "%s: Dummy alloc fail", __func__);
822 hdd_ipa_uc_rt_debug_host_dump(hdd_ctx);
823 hdd_ipa_uc_stat_request(
Krunal Sonibe766b02016-03-10 13:00:44 -0800824 hdd_get_adapter(hdd_ctx, QDF_SAP_MODE), 1);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800825 } else {
826 kfree(dummy_ptr);
827 }
828
Anurag Chouhan210db072016-02-22 18:42:15 +0530829 qdf_mc_timer_start(&hdd_ipa->rt_debug_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800830 HDD_IPA_UC_RT_DEBUG_PERIOD);
831}
832
833/**
834 * hdd_ipa_uc_rt_debug_destructor - called by data packet free
835 * @skb: packet pinter
836 *
837 * when free data packet, will be invoked by wlan client and will increase
838 * free counter
839 *
840 * Return: none
841 */
Jeff Johnsond7720632016-10-05 16:04:32 -0700842static void hdd_ipa_uc_rt_debug_destructor(struct sk_buff *skb)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800843{
844 if (!ghdd_ipa) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530845 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800846 "%s: invalid hdd context", __func__);
847 return;
848 }
849
850 ghdd_ipa->ipa_rx_destructor_count++;
851}
852
853/**
854 * hdd_ipa_uc_rt_debug_deinit - remove resources to handle rt debugging
855 * @hdd_ctx: hdd main context
856 *
857 * free all rt debugging resources
858 *
859 * Return: none
860 */
861static void hdd_ipa_uc_rt_debug_deinit(hdd_context_t *hdd_ctx)
862{
863 struct hdd_ipa_priv *hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
864
Anurag Chouhan210db072016-02-22 18:42:15 +0530865 if (QDF_TIMER_STATE_STOPPED !=
866 qdf_mc_timer_get_current_state(&hdd_ipa->rt_debug_fill_timer)) {
867 qdf_mc_timer_stop(&hdd_ipa->rt_debug_fill_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800868 }
Anurag Chouhan210db072016-02-22 18:42:15 +0530869 qdf_mc_timer_destroy(&hdd_ipa->rt_debug_fill_timer);
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530870 qdf_mutex_destroy(&hdd_ipa->rt_debug_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800871
872 if (!hdd_ipa_is_rt_debugging_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530873 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800874 "%s: IPA RT debug is not enabled", __func__);
875 return;
876 }
877
Anurag Chouhan210db072016-02-22 18:42:15 +0530878 if (QDF_TIMER_STATE_STOPPED !=
879 qdf_mc_timer_get_current_state(&hdd_ipa->rt_debug_timer)) {
880 qdf_mc_timer_stop(&hdd_ipa->rt_debug_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800881 }
Anurag Chouhan210db072016-02-22 18:42:15 +0530882 qdf_mc_timer_destroy(&hdd_ipa->rt_debug_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800883}
884
885/**
886 * hdd_ipa_uc_rt_debug_init - intialize resources to handle rt debugging
887 * @hdd_ctx: hdd main context
888 *
889 * alloc and initialize all rt debugging resources
890 *
891 * Return: none
892 */
893static void hdd_ipa_uc_rt_debug_init(hdd_context_t *hdd_ctx)
894{
895 struct hdd_ipa_priv *hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
896
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530897 qdf_mutex_create(&hdd_ipa->rt_debug_lock);
Anurag Chouhan210db072016-02-22 18:42:15 +0530898 qdf_mc_timer_init(&hdd_ipa->rt_debug_fill_timer, QDF_TIMER_TYPE_SW,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800899 hdd_ipa_uc_rt_debug_host_fill, (void *)hdd_ctx);
900 hdd_ipa->rt_buf_fill_index = 0;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530901 qdf_mem_zero(hdd_ipa->rt_bug_buffer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800902 sizeof(struct uc_rt_debug_info) *
903 HDD_IPA_UC_RT_DEBUG_BUF_COUNT);
904 hdd_ipa->ipa_tx_forward = 0;
905 hdd_ipa->ipa_rx_discard = 0;
906 hdd_ipa->ipa_rx_net_send_count = 0;
907 hdd_ipa->ipa_rx_internel_drop_count = 0;
908 hdd_ipa->ipa_rx_destructor_count = 0;
909
Anurag Chouhan210db072016-02-22 18:42:15 +0530910 qdf_mc_timer_start(&hdd_ipa->rt_debug_fill_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800911 HDD_IPA_UC_RT_DEBUG_FILL_INTERVAL);
912
913 /* Reatime debug enable on feature enable */
914 if (!hdd_ipa_is_rt_debugging_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530915 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800916 "%s: IPA RT debug is not enabled", __func__);
917 return;
918 }
Anurag Chouhan210db072016-02-22 18:42:15 +0530919 qdf_mc_timer_init(&hdd_ipa->rt_debug_timer, QDF_TIMER_TYPE_SW,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800920 hdd_ipa_uc_rt_debug_handler, (void *)hdd_ctx);
Anurag Chouhan210db072016-02-22 18:42:15 +0530921 qdf_mc_timer_start(&hdd_ipa->rt_debug_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800922 HDD_IPA_UC_RT_DEBUG_PERIOD);
923
924}
925
926/**
927 * hdd_ipa_uc_stat_query() - Query the IPA stats
928 * @hdd_ctx: Global HDD context
929 * @ipa_tx_diff: tx packet count diff from previous
930 * tx packet count
931 * @ipa_rx_diff: rx packet count diff from previous
932 * rx packet count
933 *
934 * Return: true if IPA is enabled, false otherwise
935 */
936void hdd_ipa_uc_stat_query(hdd_context_t *pHddCtx,
937 uint32_t *ipa_tx_diff, uint32_t *ipa_rx_diff)
938{
939 struct hdd_ipa_priv *hdd_ipa;
940
941 hdd_ipa = (struct hdd_ipa_priv *)pHddCtx->hdd_ipa;
942 *ipa_tx_diff = 0;
943 *ipa_rx_diff = 0;
944
945 if (!hdd_ipa_is_enabled(pHddCtx) ||
946 !(hdd_ipa_uc_is_enabled(pHddCtx))) {
947 return;
948 }
949
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530950 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800951 if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) &&
952 (false == hdd_ipa->resource_loading)) {
953 *ipa_tx_diff = hdd_ipa->ipa_tx_packets_diff;
954 *ipa_rx_diff = hdd_ipa->ipa_rx_packets_diff;
955 HDD_IPA_LOG(LOG1, "STAT Query TX DIFF %d, RX DIFF %d",
956 *ipa_tx_diff, *ipa_rx_diff);
957 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530958 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800959 return;
960}
961
962/**
963 * hdd_ipa_uc_stat_request() - Get IPA stats from IPA.
964 * @adapter: network adapter
965 * @reason: STAT REQ Reason
966 *
967 * Return: None
968 */
969void hdd_ipa_uc_stat_request(hdd_adapter_t *adapter, uint8_t reason)
970{
971 hdd_context_t *pHddCtx;
972 struct hdd_ipa_priv *hdd_ipa;
973
974 if (!adapter) {
975 return;
976 }
977
978 pHddCtx = (hdd_context_t *)adapter->pHddCtx;
979 hdd_ipa = (struct hdd_ipa_priv *)pHddCtx->hdd_ipa;
980 if (!hdd_ipa_is_enabled(pHddCtx) ||
981 !(hdd_ipa_uc_is_enabled(pHddCtx))) {
982 return;
983 }
984
985 HDD_IPA_LOG(LOG1, "STAT REQ Reason %d", reason);
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530986 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800987 if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) &&
988 (false == hdd_ipa->resource_loading)) {
989 hdd_ipa->stat_req_reason = reason;
990 wma_cli_set_command(
991 (int)adapter->sessionId,
992 (int)WMA_VDEV_TXRX_GET_IPA_UC_FW_STATS_CMDID,
993 0, VDEV_CMD);
994 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530995 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800996}
997
998/**
999 * hdd_ipa_uc_find_add_assoc_sta() - Find associated station
1000 * @hdd_ipa: Global HDD IPA context
1001 * @sta_add: Should station be added
1002 * @sta_id: ID of the station being queried
1003 *
1004 * Return: true if the station was found
1005 */
1006static bool hdd_ipa_uc_find_add_assoc_sta(struct hdd_ipa_priv *hdd_ipa,
1007 bool sta_add, uint8_t sta_id)
1008{
1009 bool sta_found = false;
1010 uint8_t idx;
1011 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
1012 if ((hdd_ipa->assoc_stas_map[idx].is_reserved) &&
1013 (hdd_ipa->assoc_stas_map[idx].sta_id == sta_id)) {
1014 sta_found = true;
1015 break;
1016 }
1017 }
1018 if (sta_add && sta_found) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301019 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001020 "%s: STA ID %d already exist, cannot add",
1021 __func__, sta_id);
1022 return sta_found;
1023 }
1024 if (sta_add) {
1025 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
1026 if (!hdd_ipa->assoc_stas_map[idx].is_reserved) {
1027 hdd_ipa->assoc_stas_map[idx].is_reserved = true;
1028 hdd_ipa->assoc_stas_map[idx].sta_id = sta_id;
1029 return sta_found;
1030 }
1031 }
1032 }
1033 if (!sta_add && !sta_found) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301034 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001035 "%s: STA ID %d does not exist, cannot delete",
1036 __func__, sta_id);
1037 return sta_found;
1038 }
1039 if (!sta_add) {
1040 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
1041 if ((hdd_ipa->assoc_stas_map[idx].is_reserved) &&
1042 (hdd_ipa->assoc_stas_map[idx].sta_id == sta_id)) {
1043 hdd_ipa->assoc_stas_map[idx].is_reserved =
1044 false;
1045 hdd_ipa->assoc_stas_map[idx].sta_id = 0xFF;
1046 return sta_found;
1047 }
1048 }
1049 }
1050 return sta_found;
1051}
1052
1053/**
1054 * hdd_ipa_uc_enable_pipes() - Enable IPA uC pipes
1055 * @hdd_ipa: Global HDD IPA context
1056 *
1057 * Return: 0 on success, negative errno if error
1058 */
1059static int hdd_ipa_uc_enable_pipes(struct hdd_ipa_priv *hdd_ipa)
1060{
1061 int result;
1062 p_cds_contextType cds_ctx = hdd_ipa->hdd_ctx->pcds_context;
1063
1064 /* ACTIVATE TX PIPE */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301065 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Yun Park4cab6ee2015-10-27 11:43:40 -07001066 "%s: Enable TX PIPE(tx_pipe_handle=%d)",
1067 __func__, hdd_ipa->tx_pipe_handle);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001068 result = ipa_enable_wdi_pipe(hdd_ipa->tx_pipe_handle);
1069 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301070 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001071 "%s: Enable TX PIPE fail, code %d",
1072 __func__, result);
1073 return result;
1074 }
1075 result = ipa_resume_wdi_pipe(hdd_ipa->tx_pipe_handle);
1076 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301077 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001078 "%s: Resume TX PIPE fail, code %d",
1079 __func__, result);
1080 return result;
1081 }
1082 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, true, true);
1083
1084 /* ACTIVATE RX PIPE */
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301085 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Yun Park4cab6ee2015-10-27 11:43:40 -07001086 "%s: Enable RX PIPE(rx_pipe_handle=%d)",
1087 __func__, hdd_ipa->rx_pipe_handle);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001088 result = ipa_enable_wdi_pipe(hdd_ipa->rx_pipe_handle);
1089 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301090 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001091 "%s: Enable RX PIPE fail, code %d",
1092 __func__, result);
1093 return result;
1094 }
1095 result = ipa_resume_wdi_pipe(hdd_ipa->rx_pipe_handle);
1096 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301097 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001098 "%s: Resume RX PIPE fail, code %d",
1099 __func__, result);
1100 return result;
1101 }
1102 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, true, false);
Leo Change3e49442015-10-26 20:07:13 -07001103 hdd_ipa->ipa_pipes_down = false;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001104 return 0;
1105}
1106
1107/**
1108 * hdd_ipa_uc_disable_pipes() - Disable IPA uC pipes
1109 * @hdd_ipa: Global HDD IPA context
1110 *
1111 * Return: 0 on success, negative errno if error
1112 */
1113static int hdd_ipa_uc_disable_pipes(struct hdd_ipa_priv *hdd_ipa)
1114{
1115 int result;
1116
Leo Change3e49442015-10-26 20:07:13 -07001117 hdd_ipa->ipa_pipes_down = true;
1118
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301119 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Disable RX PIPE", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001120 result = ipa_suspend_wdi_pipe(hdd_ipa->rx_pipe_handle);
1121 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301122 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001123 "%s: Suspend RX PIPE fail, code %d",
1124 __func__, result);
1125 return result;
1126 }
1127 result = ipa_disable_wdi_pipe(hdd_ipa->rx_pipe_handle);
1128 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301129 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001130 "%s: Disable RX PIPE fail, code %d",
1131 __func__, result);
1132 return result;
1133 }
1134
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301135 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Disable TX PIPE", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001136 result = ipa_suspend_wdi_pipe(hdd_ipa->tx_pipe_handle);
1137 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301138 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001139 "%s: Suspend TX PIPE fail, code %d",
1140 __func__, result);
1141 return result;
1142 }
1143 result = ipa_disable_wdi_pipe(hdd_ipa->tx_pipe_handle);
1144 if (result) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301145 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001146 "%s: Disable TX PIPE fail, code %d",
1147 __func__, result);
1148 return result;
1149 }
1150
1151 return 0;
1152}
1153
1154/**
1155 * hdd_ipa_uc_handle_first_con() - Handle first uC IPA connection
1156 * @hdd_ipa: Global HDD IPA context
1157 *
1158 * Return: 0 on success, negative errno if error
1159 */
1160static int hdd_ipa_uc_handle_first_con(struct hdd_ipa_priv *hdd_ipa)
1161{
1162 hdd_ipa->activated_fw_pipe = 0;
1163 hdd_ipa->resource_loading = true;
Yun Park4cab6ee2015-10-27 11:43:40 -07001164
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001165 /* If RM feature enabled
1166 * Request PROD Resource first
1167 * PROD resource may return sync or async manners */
Yun Park4cab6ee2015-10-27 11:43:40 -07001168 if (hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx)) {
1169 if (!ipa_rm_request_resource(IPA_RM_RESOURCE_WLAN_PROD)) {
1170 /* RM PROD request sync return
1171 * enable pipe immediately
1172 */
1173 if (hdd_ipa_uc_enable_pipes(hdd_ipa)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301174 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Yun Park4cab6ee2015-10-27 11:43:40 -07001175 "%s: IPA WDI Pipe activation failed",
1176 __func__);
1177 hdd_ipa->resource_loading = false;
1178 return -EBUSY;
1179 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001180 }
1181 } else {
1182 /* RM Disabled
Yun Park4cab6ee2015-10-27 11:43:40 -07001183 * Just enabled all the PIPEs
1184 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001185 if (hdd_ipa_uc_enable_pipes(hdd_ipa)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301186 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Yun Park4cab6ee2015-10-27 11:43:40 -07001187 "%s: IPA WDI Pipe activation failed",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001188 __func__);
1189 hdd_ipa->resource_loading = false;
1190 return -EBUSY;
1191 }
1192 hdd_ipa->resource_loading = false;
1193 }
Yun Park4cab6ee2015-10-27 11:43:40 -07001194
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301195 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Yun Park4cab6ee2015-10-27 11:43:40 -07001196 "%s: IPA WDI Pipes activated successfully", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001197 return 0;
1198}
1199
1200/**
1201 * hdd_ipa_uc_handle_last_discon() - Handle last uC IPA disconnection
1202 * @hdd_ipa: Global HDD IPA context
1203 *
1204 * Return: None
1205 */
1206static void hdd_ipa_uc_handle_last_discon(struct hdd_ipa_priv *hdd_ipa)
1207{
1208 p_cds_contextType cds_ctx = hdd_ipa->hdd_ctx->pcds_context;
1209
1210 hdd_ipa->resource_unloading = true;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301211 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Disable FW RX PIPE", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001212 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, false, false);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301213 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Disable FW TX PIPE", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001214 ol_txrx_ipa_uc_set_active(cds_ctx->pdev_txrx_ctx, false, true);
1215}
1216
1217/**
1218 * hdd_ipa_uc_rm_notify_handler() - IPA uC resource notification handler
1219 * @context: User context registered with TL (the IPA Global context is
1220 * registered
1221 * @rxpkt: Packet containing the notification
1222 * @staid: ID of the station associated with the packet
1223 *
1224 * Return: None
1225 */
1226static void
1227hdd_ipa_uc_rm_notify_handler(void *context, enum ipa_rm_event event)
1228{
1229 struct hdd_ipa_priv *hdd_ipa = context;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301230 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001231
1232 /*
1233 * When SSR is going on or driver is unloading, just return.
1234 */
1235 status = wlan_hdd_validate_context(hdd_ipa->hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301236 if (status)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001237 return;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001238
1239 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
1240 return;
1241
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301242 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s, event code %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001243 __func__, event);
1244
1245 switch (event) {
1246 case IPA_RM_RESOURCE_GRANTED:
1247 /* Differed RM Granted */
1248 hdd_ipa_uc_enable_pipes(hdd_ipa);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301249 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001250 if ((false == hdd_ipa->resource_unloading) &&
1251 (!hdd_ipa->activated_fw_pipe)) {
1252 hdd_ipa_uc_enable_pipes(hdd_ipa);
1253 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301254 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001255 break;
1256
1257 case IPA_RM_RESOURCE_RELEASED:
1258 /* Differed RM Released */
1259 hdd_ipa->resource_unloading = false;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001260 break;
1261
1262 default:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301263 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001264 "%s, invalid event code %d", __func__, event);
1265 break;
1266 }
1267}
1268
1269/**
1270 * hdd_ipa_uc_rm_notify_defer() - Defer IPA uC notification
1271 * @hdd_ipa: Global HDD IPA context
1272 * @event: IPA resource manager event to be deferred
1273 *
1274 * This function is called when a resource manager event is received
1275 * from firmware in interrupt context. This function will defer the
1276 * handling to the OL RX thread
1277 *
1278 * Return: None
1279 */
1280static void hdd_ipa_uc_rm_notify_defer(struct work_struct *work)
1281{
1282 enum ipa_rm_event event;
1283 struct uc_rm_work_struct *uc_rm_work = container_of(work,
1284 struct uc_rm_work_struct, work);
1285 struct hdd_ipa_priv *hdd_ipa = container_of(uc_rm_work,
1286 struct hdd_ipa_priv, uc_rm_work);
1287
1288 cds_ssr_protect(__func__);
1289 event = uc_rm_work->event;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301290 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001291 "%s, posted event %d", __func__, event);
1292
1293 hdd_ipa_uc_rm_notify_handler(hdd_ipa, event);
1294 cds_ssr_unprotect(__func__);
1295
1296 return;
1297}
1298
1299/**
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001300 * hdd_ipa_uc_op_cb() - IPA uC operation callback
1301 * @op_msg: operation message received from firmware
1302 * @usr_ctxt: user context registered with TL (we register the HDD Global
1303 * context)
1304 *
1305 * Return: None
1306 */
1307static void hdd_ipa_uc_op_cb(struct op_msg_type *op_msg, void *usr_ctxt)
1308{
1309 struct op_msg_type *msg = op_msg;
1310 struct ipa_uc_fw_stats *uc_fw_stat;
1311 struct IpaHwStatsWDIInfoData_t ipa_stat;
1312 struct hdd_ipa_priv *hdd_ipa;
1313 hdd_context_t *hdd_ctx;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301314 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001315
1316 if (!op_msg || !usr_ctxt) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301317 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "%s, INVALID ARG", __func__);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001318 return;
1319 }
1320
1321 if (HDD_IPA_UC_OPCODE_MAX <= msg->op_code) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301322 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001323 "%s, INVALID OPCODE %d", __func__, msg->op_code);
1324 return;
1325 }
1326
1327 hdd_ctx = (hdd_context_t *) usr_ctxt;
1328
1329 /*
1330 * When SSR is going on or driver is unloading, just return.
1331 */
1332 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301333 if (status) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301334 qdf_mem_free(op_msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001335 return;
1336 }
1337
1338 hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
1339
Govind Singhb6a89772016-08-12 11:23:35 +05301340 HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001341 "%s, OPCODE %s", __func__, op_string[msg->op_code]);
1342
1343 if ((HDD_IPA_UC_OPCODE_TX_RESUME == msg->op_code) ||
1344 (HDD_IPA_UC_OPCODE_RX_RESUME == msg->op_code)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301345 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001346 hdd_ipa->activated_fw_pipe++;
1347 if (HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) {
1348 hdd_ipa->resource_loading = false;
1349 hdd_ipa_uc_proc_pending_event(hdd_ipa);
Yun Parkccc6d7a2015-12-02 14:50:13 -08001350 if (hdd_ipa->pending_cons_req)
1351 ipa_rm_notify_completion(
1352 IPA_RM_RESOURCE_GRANTED,
1353 IPA_RM_RESOURCE_WLAN_CONS);
Yun Park5b635012015-12-02 15:05:01 -08001354 hdd_ipa->pending_cons_req = false;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001355 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301356 qdf_mutex_release(&hdd_ipa->ipa_lock);
Yun Park8292dcb2016-10-07 16:46:06 -07001357 } else if ((HDD_IPA_UC_OPCODE_TX_SUSPEND == msg->op_code) ||
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001358 (HDD_IPA_UC_OPCODE_RX_SUSPEND == msg->op_code)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301359 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001360 hdd_ipa->activated_fw_pipe--;
1361 if (!hdd_ipa->activated_fw_pipe) {
1362 hdd_ipa_uc_disable_pipes(hdd_ipa);
Yun Park5b635012015-12-02 15:05:01 -08001363 if (hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
1364 ipa_rm_release_resource(
1365 IPA_RM_RESOURCE_WLAN_PROD);
1366 /* Sync return success from IPA
1367 * Enable/resume all the PIPEs */
1368 hdd_ipa->resource_unloading = false;
1369 hdd_ipa_uc_proc_pending_event(hdd_ipa);
1370 hdd_ipa->pending_cons_req = false;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001371 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301372 qdf_mutex_release(&hdd_ipa->ipa_lock);
Yun Park8292dcb2016-10-07 16:46:06 -07001373 } else if ((HDD_IPA_UC_OPCODE_STATS == msg->op_code) &&
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001374 (HDD_IPA_UC_STAT_REASON_DEBUG == hdd_ipa->stat_req_reason)) {
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001375 struct ol_txrx_ipa_resources *res = &hdd_ipa->ipa_resource;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001376 /* STATs from host */
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301377 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001378 "==== IPA_UC WLAN_HOST CE ====\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001379 "CE RING BASE: 0x%llx\n"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001380 "CE RING SIZE: %d\n"
1381 "CE REG ADDR : 0x%llx",
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001382 (unsigned long long)res->ce_sr_base_paddr,
1383 res->ce_sr_ring_size,
1384 (unsigned long long)res->ce_reg_paddr);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301385 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001386 "==== IPA_UC WLAN_HOST TX ====\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001387 "COMP RING BASE: 0x%llx\n"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001388 "COMP RING SIZE: %d\n"
1389 "NUM ALLOC BUF: %d\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001390 "COMP RING DBELL : 0x%llx",
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001391 (unsigned long long)res->tx_comp_ring_base_paddr,
1392 res->tx_comp_ring_size,
1393 res->tx_num_alloc_buffer,
Manikandan Mohan22b83722015-12-15 15:03:23 -08001394 (unsigned long long)hdd_ipa->tx_comp_doorbell_paddr);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301395 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001396 "==== IPA_UC WLAN_HOST RX ====\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001397 "IND RING BASE: 0x%llx\n"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001398 "IND RING SIZE: %d\n"
Leo Chang3bc8fed2015-11-13 10:59:47 -08001399 "IND RING DBELL : 0x%llx\n"
1400 "PROC DONE IND ADDR : 0x%llx\n"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001401 "NUM EXCP PKT : %llu\n"
1402 "NUM TX BCMC : %llu\n"
1403 "NUM TX BCMC ERR : %llu",
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001404 (unsigned long long)res->rx_rdy_ring_base_paddr,
1405 res->rx_rdy_ring_size,
Manikandan Mohan22b83722015-12-15 15:03:23 -08001406 (unsigned long long)hdd_ipa->rx_ready_doorbell_paddr,
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001407 (unsigned long long)hdd_ipa->ipa_resource.
1408 rx_proc_done_idx_paddr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001409 hdd_ipa->stats.num_rx_excep,
1410 hdd_ipa->stats.num_tx_bcmc,
Manikandan Mohan22b83722015-12-15 15:03:23 -08001411 (unsigned long long)hdd_ipa->stats.num_tx_bcmc_err);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301412 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001413 "==== IPA_UC WLAN_HOST CONTROL ====\n"
1414 "SAP NUM STAs: %d\n"
1415 "STA CONNECTED: %d\n"
1416 "TX PIPE HDL: %d\n"
1417 "RX PIPE HDL : %d\n"
1418 "RSC LOADING : %d\n"
1419 "RSC UNLOADING : %d\n"
1420 "PNDNG CNS RQT : %d",
1421 hdd_ipa->sap_num_connected_sta,
1422 hdd_ipa->sta_connected,
1423 hdd_ipa->tx_pipe_handle,
1424 hdd_ipa->rx_pipe_handle,
1425 (unsigned int)hdd_ipa->resource_loading,
1426 (unsigned int)hdd_ipa->resource_unloading,
1427 (unsigned int)hdd_ipa->pending_cons_req);
1428
1429 /* STATs from FW */
1430 uc_fw_stat = (struct ipa_uc_fw_stats *)
1431 ((uint8_t *)op_msg + sizeof(struct op_msg_type));
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301432 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001433 "==== IPA_UC WLAN_FW TX ====\n"
1434 "COMP RING BASE: 0x%x\n"
1435 "COMP RING SIZE: %d\n"
1436 "COMP RING DBELL : 0x%x\n"
1437 "COMP RING DBELL IND VAL : %d\n"
1438 "COMP RING DBELL CACHED VAL : %d\n"
1439 "COMP RING DBELL CACHED VAL : %d\n"
1440 "PKTS ENQ : %d\n"
1441 "PKTS COMP : %d\n"
1442 "IS SUSPEND : %d\n"
1443 "RSVD : 0x%x",
1444 uc_fw_stat->tx_comp_ring_base,
1445 uc_fw_stat->tx_comp_ring_size,
1446 uc_fw_stat->tx_comp_ring_dbell_addr,
1447 uc_fw_stat->tx_comp_ring_dbell_ind_val,
1448 uc_fw_stat->tx_comp_ring_dbell_cached_val,
1449 uc_fw_stat->tx_comp_ring_dbell_cached_val,
1450 uc_fw_stat->tx_pkts_enqueued,
1451 uc_fw_stat->tx_pkts_completed,
1452 uc_fw_stat->tx_is_suspend, uc_fw_stat->tx_reserved);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301453 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001454 "==== IPA_UC WLAN_FW RX ====\n"
1455 "IND RING BASE: 0x%x\n"
1456 "IND RING SIZE: %d\n"
1457 "IND RING DBELL : 0x%x\n"
1458 "IND RING DBELL IND VAL : %d\n"
1459 "IND RING DBELL CACHED VAL : %d\n"
1460 "RDY IND ADDR : 0x%x\n"
1461 "RDY IND CACHE VAL : %d\n"
1462 "RFIL IND : %d\n"
1463 "NUM PKT INDICAT : %d\n"
1464 "BUF REFIL : %d\n"
1465 "NUM DROP NO SPC : %d\n"
1466 "NUM DROP NO BUF : %d\n"
1467 "IS SUSPND : %d\n"
1468 "RSVD : 0x%x\n",
1469 uc_fw_stat->rx_ind_ring_base,
1470 uc_fw_stat->rx_ind_ring_size,
1471 uc_fw_stat->rx_ind_ring_dbell_addr,
1472 uc_fw_stat->rx_ind_ring_dbell_ind_val,
1473 uc_fw_stat->rx_ind_ring_dbell_ind_cached_val,
1474 uc_fw_stat->rx_ind_ring_rdidx_addr,
1475 uc_fw_stat->rx_ind_ring_rd_idx_cached_val,
1476 uc_fw_stat->rx_refill_idx,
1477 uc_fw_stat->rx_num_pkts_indicated,
1478 uc_fw_stat->rx_buf_refilled,
1479 uc_fw_stat->rx_num_ind_drop_no_space,
1480 uc_fw_stat->rx_num_ind_drop_no_buf,
1481 uc_fw_stat->rx_is_suspend, uc_fw_stat->rx_reserved);
1482 /* STATs from IPA */
1483 ipa_get_wdi_stats(&ipa_stat);
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301484 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001485 "==== IPA_UC IPA TX ====\n"
1486 "NUM PROCD : %d\n"
1487 "CE DBELL : 0x%x\n"
1488 "NUM DBELL FIRED : %d\n"
1489 "COMP RNG FULL : %d\n"
1490 "COMP RNG EMPT : %d\n"
1491 "COMP RNG USE HGH : %d\n"
1492 "COMP RNG USE LOW : %d\n"
1493 "BAM FIFO FULL : %d\n"
1494 "BAM FIFO EMPT : %d\n"
1495 "BAM FIFO USE HGH : %d\n"
1496 "BAM FIFO USE LOW : %d\n"
1497 "NUM DBELL : %d\n"
1498 "NUM UNEXP DBELL : %d\n"
1499 "NUM BAM INT HDL : 0x%x\n"
1500 "NUM BAM INT NON-RUN : 0x%x\n"
1501 "NUM QMB INT HDL : 0x%x",
1502 ipa_stat.tx_ch_stats.num_pkts_processed,
1503 ipa_stat.tx_ch_stats.copy_engine_doorbell_value,
1504 ipa_stat.tx_ch_stats.num_db_fired,
1505 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringFull,
1506 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringEmpty,
1507 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringUsageHigh,
1508 ipa_stat.tx_ch_stats.tx_comp_ring_stats.ringUsageLow,
1509 ipa_stat.tx_ch_stats.bam_stats.bamFifoFull,
1510 ipa_stat.tx_ch_stats.bam_stats.bamFifoEmpty,
1511 ipa_stat.tx_ch_stats.bam_stats.bamFifoUsageHigh,
1512 ipa_stat.tx_ch_stats.bam_stats.bamFifoUsageLow,
1513 ipa_stat.tx_ch_stats.num_db,
1514 ipa_stat.tx_ch_stats.num_unexpected_db,
1515 ipa_stat.tx_ch_stats.num_bam_int_handled,
1516 ipa_stat.tx_ch_stats.
1517 num_bam_int_in_non_runnning_state,
1518 ipa_stat.tx_ch_stats.num_qmb_int_handled);
1519
Anurag Chouhandf2b2682016-02-29 14:15:27 +05301520 QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001521 "==== IPA_UC IPA RX ====\n"
1522 "MAX OST PKT : %d\n"
1523 "NUM PKT PRCSD : %d\n"
1524 "RNG RP : 0x%x\n"
1525 "COMP RNG FULL : %d\n"
1526 "COMP RNG EMPT : %d\n"
1527 "COMP RNG USE HGH : %d\n"
1528 "COMP RNG USE LOW : %d\n"
1529 "BAM FIFO FULL : %d\n"
1530 "BAM FIFO EMPT : %d\n"
1531 "BAM FIFO USE HGH : %d\n"
1532 "BAM FIFO USE LOW : %d\n"
1533 "NUM DB : %d\n"
1534 "NUM UNEXP DB : %d\n"
1535 "NUM BAM INT HNDL : 0x%x\n",
1536 ipa_stat.rx_ch_stats.max_outstanding_pkts,
1537 ipa_stat.rx_ch_stats.num_pkts_processed,
1538 ipa_stat.rx_ch_stats.rx_ring_rp_value,
1539 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringFull,
1540 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringEmpty,
1541 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringUsageHigh,
1542 ipa_stat.rx_ch_stats.rx_ind_ring_stats.ringUsageLow,
1543 ipa_stat.rx_ch_stats.bam_stats.bamFifoFull,
1544 ipa_stat.rx_ch_stats.bam_stats.bamFifoEmpty,
1545 ipa_stat.rx_ch_stats.bam_stats.bamFifoUsageHigh,
1546 ipa_stat.rx_ch_stats.bam_stats.bamFifoUsageLow,
1547 ipa_stat.rx_ch_stats.num_db,
1548 ipa_stat.rx_ch_stats.num_unexpected_db,
1549 ipa_stat.rx_ch_stats.num_bam_int_handled);
1550 } else if ((HDD_IPA_UC_OPCODE_STATS == msg->op_code) &&
1551 (HDD_IPA_UC_STAT_REASON_BW_CAL == hdd_ipa->stat_req_reason)) {
1552 /* STATs from FW */
1553 uc_fw_stat = (struct ipa_uc_fw_stats *)
1554 ((uint8_t *)op_msg + sizeof(struct op_msg_type));
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301555 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001556 hdd_ipa->ipa_tx_packets_diff = HDD_BW_GET_DIFF(
1557 uc_fw_stat->tx_pkts_completed,
1558 hdd_ipa->ipa_p_tx_packets);
1559 hdd_ipa->ipa_rx_packets_diff = HDD_BW_GET_DIFF(
1560 (uc_fw_stat->rx_num_ind_drop_no_space +
1561 uc_fw_stat->rx_num_ind_drop_no_buf +
1562 uc_fw_stat->rx_num_pkts_indicated),
1563 hdd_ipa->ipa_p_rx_packets);
1564
1565 hdd_ipa->ipa_p_tx_packets = uc_fw_stat->tx_pkts_completed;
1566 hdd_ipa->ipa_p_rx_packets =
1567 (uc_fw_stat->rx_num_ind_drop_no_space +
1568 uc_fw_stat->rx_num_ind_drop_no_buf +
1569 uc_fw_stat->rx_num_pkts_indicated);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301570 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001571 } else {
Yun Park8292dcb2016-10-07 16:46:06 -07001572 HDD_IPA_LOG(LOGE, "Invalid message: op_code=%d, reason=%d",
1573 msg->op_code, hdd_ipa->stat_req_reason);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001574 }
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301575 qdf_mem_free(op_msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001576}
1577
1578
1579/**
1580 * hdd_ipa_uc_offload_enable_disable() - wdi enable/disable notify to fw
1581 * @adapter: device adapter instance
1582 * @offload_type: MCC or SCC
1583 * @enable: TX offload enable or disable
1584 *
1585 * Return: none
1586 */
1587static void hdd_ipa_uc_offload_enable_disable(hdd_adapter_t *adapter,
1588 uint32_t offload_type, uint32_t enable)
1589{
1590 struct sir_ipa_offload_enable_disable ipa_offload_enable_disable;
Yun Park8292dcb2016-10-07 16:46:06 -07001591 struct hdd_ipa_iface_context *iface_context = NULL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001592
Yun Parka37592b2016-06-11 17:10:28 -07001593 if (!adapter)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001594 return;
1595
Yun Park8292dcb2016-10-07 16:46:06 -07001596 iface_context = adapter->ipa_context;
1597
1598 if (!iface_context || (enable == iface_context->offload_enabled)) {
1599 /* IPA offload status is already set as desired */
1600 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
1601 "offload_type=%d, vdev_id=%d, enable=%d",
1602 offload_type, adapter->sessionId, enable);
1603 WARN_ON(1);
1604 return;
1605 }
1606
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001607 /* Lower layer may send multiple START_BSS_EVENT in DFS mode or during
1608 * channel change indication. Since these indications are sent by lower
1609 * layer as SAP updates and IPA doesn't have to do anything for these
1610 * updates so ignoring!
Yun Parka37592b2016-06-11 17:10:28 -07001611 */
1612 if (QDF_SAP_MODE == adapter->device_mode && adapter->ipa_context)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001613 return;
1614
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301615 qdf_mem_zero(&ipa_offload_enable_disable,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001616 sizeof(ipa_offload_enable_disable));
1617 ipa_offload_enable_disable.offload_type = offload_type;
1618 ipa_offload_enable_disable.vdev_id = adapter->sessionId;
1619 ipa_offload_enable_disable.enable = enable;
1620
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301621 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Yun Park8292dcb2016-10-07 16:46:06 -07001622 "offload_type=%d, vdev_id=%d, enable=%d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001623 ipa_offload_enable_disable.offload_type,
1624 ipa_offload_enable_disable.vdev_id,
1625 ipa_offload_enable_disable.enable);
1626
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301627 if (QDF_STATUS_SUCCESS !=
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001628 sme_ipa_offload_enable_disable(WLAN_HDD_GET_HAL_CTX(adapter),
1629 adapter->sessionId, &ipa_offload_enable_disable)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301630 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001631 "%s: Failure to enable IPA offload \
1632 (offload_type=%d, vdev_id=%d, enable=%d)", __func__,
1633 ipa_offload_enable_disable.offload_type,
1634 ipa_offload_enable_disable.vdev_id,
1635 ipa_offload_enable_disable.enable);
Yun Park8292dcb2016-10-07 16:46:06 -07001636 } else {
1637 /* Update the IPA offload status */
1638 iface_context->offload_enabled =
1639 ipa_offload_enable_disable.enable;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001640 }
1641}
1642
1643/**
1644 * hdd_ipa_uc_fw_op_event_handler - IPA uC FW OPvent handler
1645 * @work: uC OP work
1646 *
1647 * Return: None
1648 */
1649static void hdd_ipa_uc_fw_op_event_handler(struct work_struct *work)
1650{
1651 struct op_msg_type *msg;
1652 struct uc_op_work_struct *uc_op_work = container_of(work,
1653 struct uc_op_work_struct, work);
1654 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
1655
1656 cds_ssr_protect(__func__);
1657
1658 msg = uc_op_work->msg;
1659 uc_op_work->msg = NULL;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301660 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001661 "%s, posted msg %d", __func__, msg->op_code);
1662
1663 hdd_ipa_uc_op_cb(msg, hdd_ipa->hdd_ctx);
1664
1665 cds_ssr_unprotect(__func__);
1666
1667 return;
1668}
1669
1670/**
1671 * hdd_ipa_uc_op_event_handler() - Adapter lookup
1672 * hdd_ipa_uc_fw_op_event_handler - IPA uC FW OPvent handler
1673 * @op_msg: operation message received from firmware
1674 * @hdd_ctx: Global HDD context
1675 *
1676 * Return: None
1677 */
1678static void hdd_ipa_uc_op_event_handler(uint8_t *op_msg, void *hdd_ctx)
1679{
1680 struct hdd_ipa_priv *hdd_ipa;
1681 struct op_msg_type *msg;
1682 struct uc_op_work_struct *uc_op_work;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301683 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001684
1685 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +05301686 if (status)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001687 goto end;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001688
1689 msg = (struct op_msg_type *)op_msg;
1690 hdd_ipa = ((hdd_context_t *)hdd_ctx)->hdd_ipa;
1691
1692 if (unlikely(!hdd_ipa))
1693 goto end;
1694
1695 if (HDD_IPA_UC_OPCODE_MAX <= msg->op_code) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301696 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "%s: Invalid OP Code (%d)",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001697 __func__, msg->op_code);
1698 goto end;
1699 }
1700
1701 uc_op_work = &hdd_ipa->uc_op_work[msg->op_code];
1702 if (uc_op_work->msg)
1703 /* When the same uC OPCODE is already pended, just return */
1704 goto end;
1705
1706 uc_op_work->msg = msg;
1707 schedule_work(&uc_op_work->work);
1708 return;
1709
1710end:
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301711 qdf_mem_free(op_msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001712}
1713
1714/**
Rajeev Kumar217f2172016-01-06 18:11:55 -08001715 * hdd_ipa_init_uc_op_work - init ipa uc op work
1716 * @work: struct work_struct
1717 * @work_handler: work_handler
1718 *
1719 * Return: none
1720 */
Rajeev Kumar217f2172016-01-06 18:11:55 -08001721static void hdd_ipa_init_uc_op_work(struct work_struct *work,
1722 work_func_t work_handler)
1723{
1724 INIT_WORK(work, work_handler);
1725}
Rajeev Kumar217f2172016-01-06 18:11:55 -08001726
1727
1728/**
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001729 * hdd_ipa_uc_ol_init() - Initialize IPA uC offload
1730 * @hdd_ctx: Global HDD context
1731 *
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301732 * Return: QDF_STATUS
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001733 */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301734static QDF_STATUS hdd_ipa_uc_ol_init(hdd_context_t *hdd_ctx)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001735{
1736 struct ipa_wdi_in_params pipe_in;
1737 struct ipa_wdi_out_params pipe_out;
1738 struct hdd_ipa_priv *ipa_ctxt = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
1739 p_cds_contextType cds_ctx = hdd_ctx->pcds_context;
1740 uint8_t i;
1741
Anurag Chouhan600c3a02016-03-01 10:33:54 +05301742 qdf_mem_zero(&pipe_in, sizeof(struct ipa_wdi_in_params));
1743 qdf_mem_zero(&pipe_out, sizeof(struct ipa_wdi_out_params));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001744
Anurag Chouhanffb21542016-02-17 14:33:03 +05301745 qdf_list_create(&ipa_ctxt->pending_event, 1000);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301746 qdf_mutex_create(&ipa_ctxt->event_lock);
1747 qdf_mutex_create(&ipa_ctxt->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001748
1749 /* TX PIPE */
1750 pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
1751 pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN;
1752 pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
1753 pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 0;
1754 pipe_in.sys.ipa_ep_cfg.hdr.hdr_additional_const_len =
1755 HDD_IPA_UC_WLAN_8023_HDR_SIZE;
1756 pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC;
1757 pipe_in.sys.client = IPA_CLIENT_WLAN1_CONS;
1758 pipe_in.sys.desc_fifo_sz = hdd_ctx->config->IpaDescSize;
1759 pipe_in.sys.priv = hdd_ctx->hdd_ipa;
1760 pipe_in.sys.ipa_ep_cfg.hdr_ext.hdr_little_endian = true;
1761 pipe_in.sys.notify = hdd_ipa_i2w_cb;
1762 if (!hdd_ipa_is_rm_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301763 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001764 "%s: IPA RM DISABLED, IPA AWAKE", __func__);
1765 pipe_in.sys.keep_ipa_awake = true;
1766 }
1767
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001768 pipe_in.u.dl.comp_ring_base_pa =
1769 ipa_ctxt->ipa_resource.tx_comp_ring_base_paddr;
Leo Chang3bc8fed2015-11-13 10:59:47 -08001770 pipe_in.u.dl.comp_ring_size =
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001771 ipa_ctxt->ipa_resource.tx_comp_ring_size *
1772 sizeof(qdf_dma_addr_t);
1773 pipe_in.u.dl.ce_ring_base_pa =
1774 ipa_ctxt->ipa_resource.ce_sr_base_paddr;
1775 pipe_in.u.dl.ce_door_bell_pa = ipa_ctxt->ipa_resource.ce_reg_paddr;
1776 pipe_in.u.dl.ce_ring_size =
1777 ipa_ctxt->ipa_resource.ce_sr_ring_size;
1778 pipe_in.u.dl.num_tx_buffers =
1779 ipa_ctxt->ipa_resource.tx_num_alloc_buffer;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001780
1781 /* Connect WDI IPA PIPE */
1782 ipa_connect_wdi_pipe(&pipe_in, &pipe_out);
1783 /* Micro Controller Doorbell register */
Govind Singh0487bf22016-08-24 23:08:57 +05301784 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
1785 "%s CONS DB pipe out 0x%x TX PIPE Handle 0x%x",
1786 __func__, (unsigned int)pipe_out.uc_door_bell_pa,
1787 ipa_ctxt->tx_pipe_handle);
Leo Chang3bc8fed2015-11-13 10:59:47 -08001788 ipa_ctxt->tx_comp_doorbell_paddr = pipe_out.uc_door_bell_pa;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001789 /* WLAN TX PIPE Handle */
1790 ipa_ctxt->tx_pipe_handle = pipe_out.clnt_hdl;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301791 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001792 "TX : CRBPA 0x%x, CRS %d, CERBPA 0x%x, CEDPA 0x%x,"
1793 " CERZ %d, NB %d, CDBPAD 0x%x",
1794 (unsigned int)pipe_in.u.dl.comp_ring_base_pa,
1795 pipe_in.u.dl.comp_ring_size,
1796 (unsigned int)pipe_in.u.dl.ce_ring_base_pa,
1797 (unsigned int)pipe_in.u.dl.ce_door_bell_pa,
1798 pipe_in.u.dl.ce_ring_size,
1799 pipe_in.u.dl.num_tx_buffers,
Leo Chang3bc8fed2015-11-13 10:59:47 -08001800 (unsigned int)ipa_ctxt->tx_comp_doorbell_paddr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001801
1802 /* RX PIPE */
1803 pipe_in.sys.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
1804 pipe_in.sys.ipa_ep_cfg.hdr.hdr_len = HDD_IPA_UC_WLAN_RX_HDR_LEN;
1805 pipe_in.sys.ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 0;
1806 pipe_in.sys.ipa_ep_cfg.hdr.hdr_metadata_reg_valid = 1;
1807 pipe_in.sys.ipa_ep_cfg.mode.mode = IPA_BASIC;
1808 pipe_in.sys.client = IPA_CLIENT_WLAN1_PROD;
1809 pipe_in.sys.desc_fifo_sz = hdd_ctx->config->IpaDescSize +
1810 sizeof(struct sps_iovec);
1811 pipe_in.sys.notify = hdd_ipa_w2i_cb;
1812 if (!hdd_ipa_is_rm_enabled(hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301813 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001814 "%s: IPA RM DISABLED, IPA AWAKE", __func__);
1815 pipe_in.sys.keep_ipa_awake = true;
1816 }
1817
Dhanashri Atreb08959a2016-03-01 17:28:03 -08001818 pipe_in.u.ul.rdy_ring_base_pa =
1819 ipa_ctxt->ipa_resource.rx_rdy_ring_base_paddr;
1820 pipe_in.u.ul.rdy_ring_size =
1821 ipa_ctxt->ipa_resource.rx_rdy_ring_size;
1822 pipe_in.u.ul.rdy_ring_rp_pa =
1823 ipa_ctxt->ipa_resource.rx_proc_done_idx_paddr;
Leo Chang3bc8fed2015-11-13 10:59:47 -08001824 HDD_IPA_WDI2_SET(pipe_in, ipa_ctxt);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001825 ipa_connect_wdi_pipe(&pipe_in, &pipe_out);
Leo Chang3bc8fed2015-11-13 10:59:47 -08001826 ipa_ctxt->rx_ready_doorbell_paddr = pipe_out.uc_door_bell_pa;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001827 ipa_ctxt->rx_pipe_handle = pipe_out.clnt_hdl;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301828 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001829 "RX : RRBPA 0x%x, RRS %d, PDIPA 0x%x, RDY_DB_PAD 0x%x",
1830 (unsigned int)pipe_in.u.ul.rdy_ring_base_pa,
1831 pipe_in.u.ul.rdy_ring_size,
1832 (unsigned int)pipe_in.u.ul.rdy_ring_rp_pa,
Leo Chang3bc8fed2015-11-13 10:59:47 -08001833 (unsigned int)ipa_ctxt->rx_ready_doorbell_paddr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001834
1835 ol_txrx_ipa_uc_set_doorbell_paddr(cds_ctx->pdev_txrx_ctx,
Leo Chang3bc8fed2015-11-13 10:59:47 -08001836 ipa_ctxt->tx_comp_doorbell_paddr,
1837 ipa_ctxt->rx_ready_doorbell_paddr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001838
1839 ol_txrx_ipa_uc_register_op_cb(cds_ctx->pdev_txrx_ctx,
1840 hdd_ipa_uc_op_event_handler, (void *)hdd_ctx);
1841
1842 for (i = 0; i < HDD_IPA_UC_OPCODE_MAX; i++) {
Rajeev Kumar217f2172016-01-06 18:11:55 -08001843 hdd_ipa_init_uc_op_work(&ipa_ctxt->uc_op_work[i].work,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001844 hdd_ipa_uc_fw_op_event_handler);
1845 ipa_ctxt->uc_op_work[i].msg = NULL;
1846 }
1847
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05301848 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001849}
1850
Leo Change3e49442015-10-26 20:07:13 -07001851/**
1852 * hdd_ipa_uc_force_pipe_shutdown() - Force shutdown IPA pipe
1853 * @hdd_ctx: hdd main context
1854 *
1855 * Force shutdown IPA pipe
1856 * Independent of FW pipe status, IPA pipe shutdonw progress
1857 * in case, any STA does not leave properly, IPA HW pipe should cleaned up
1858 * independent from FW pipe status
1859 *
1860 * Return: NONE
1861 */
1862void hdd_ipa_uc_force_pipe_shutdown(hdd_context_t *hdd_ctx)
1863{
1864 struct hdd_ipa_priv *hdd_ipa;
1865
1866 if (!hdd_ipa_is_enabled(hdd_ctx) || !hdd_ctx->hdd_ipa)
1867 return;
1868
1869 hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa;
1870 if (false == hdd_ipa->ipa_pipes_down) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301871 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Leo Change3e49442015-10-26 20:07:13 -07001872 "IPA pipes are not down yet, force shutdown");
1873 hdd_ipa_uc_disable_pipes(hdd_ipa);
1874 } else {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05301875 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Leo Change3e49442015-10-26 20:07:13 -07001876 "IPA pipes are down, do nothing");
1877 }
1878
1879 return;
1880}
1881
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001882/**
1883 * hdd_ipa_uc_ssr_deinit() - handle ipa deinit for SSR
1884 *
1885 * Deinit basic IPA UC host side to be in sync reloaded FW during
1886 * SSR
1887 *
1888 * Return: 0 - Success
1889 */
1890int hdd_ipa_uc_ssr_deinit(void)
1891{
1892 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
1893 int idx;
1894 struct hdd_ipa_iface_context *iface_context;
1895
Leo Chang3bc8fed2015-11-13 10:59:47 -08001896 if ((!hdd_ipa) || (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001897 return 0;
1898
1899 /* Clean up HDD IPA interfaces */
1900 for (idx = 0; (hdd_ipa->num_iface > 0) &&
1901 (idx < HDD_IPA_MAX_IFACE); idx++) {
1902 iface_context = &hdd_ipa->iface_context[idx];
1903 if (iface_context && iface_context->adapter)
1904 hdd_ipa_cleanup_iface(iface_context);
1905 }
1906
1907 /* After SSR, wlan driver reloads FW again. But we need to protect
1908 * IPA submodule during SSR transient state. So deinit basic IPA
1909 * UC host side to be in sync with reloaded FW during SSR
1910 */
Yun Parkf7dc8cd2015-11-17 15:25:12 -08001911 if (!hdd_ipa->ipa_pipes_down)
1912 hdd_ipa_uc_disable_pipes(hdd_ipa);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001913
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301914 qdf_mutex_acquire(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001915 for (idx = 0; idx < WLAN_MAX_STA_COUNT; idx++) {
1916 hdd_ipa->assoc_stas_map[idx].is_reserved = false;
1917 hdd_ipa->assoc_stas_map[idx].sta_id = 0xFF;
1918 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05301919 qdf_mutex_release(&hdd_ipa->ipa_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001920
1921 /* Full IPA driver cleanup not required since wlan driver is now
1922 * unloaded and reloaded after SSR.
1923 */
1924 return 0;
1925}
1926
1927/**
1928 * hdd_ipa_uc_ssr_reinit() - handle ipa reinit after SSR
1929 *
1930 * Init basic IPA UC host side to be in sync with reloaded FW after
1931 * SSR to resume IPA UC operations
1932 *
1933 * Return: 0 - Success
1934 */
1935int hdd_ipa_uc_ssr_reinit(void)
1936{
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001937
1938 /* After SSR is complete, IPA UC can resume operation. But now wlan
1939 * driver will be unloaded and reloaded, which takes care of IPA cleanup
1940 * and initialization. This is a placeholder func if IPA has to resume
1941 * operations without driver reload.
1942 */
1943 return 0;
1944}
Leo Chang3bc8fed2015-11-13 10:59:47 -08001945
1946/**
1947 * hdd_ipa_tx_packet_ipa() - send packet to IPA
1948 * @hdd_ctx: Global HDD context
1949 * @skb: skb sent to IPA
1950 * @session_id: send packet instance session id
1951 *
1952 * Send TX packet which generated by system to IPA.
1953 * This routine only will be used for function verification
1954 *
1955 * Return: NULL packet sent to IPA properly
1956 * NULL invalid packet drop
1957 * skb packet not sent to IPA. legacy data path should handle
1958 */
1959struct sk_buff *hdd_ipa_tx_packet_ipa(hdd_context_t *hdd_ctx,
1960 struct sk_buff *skb, uint8_t session_id)
Leo Change3e49442015-10-26 20:07:13 -07001961{
Leo Chang3bc8fed2015-11-13 10:59:47 -08001962 struct ipa_header *ipa_header;
1963 struct frag_header *frag_header;
Leo Chang07b28f62016-05-11 12:29:22 -07001964 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
Leo Chang3bc8fed2015-11-13 10:59:47 -08001965
1966 if (!hdd_ipa_uc_is_enabled(hdd_ctx))
1967 return skb;
1968
Leo Chang07b28f62016-05-11 12:29:22 -07001969 if (!hdd_ipa)
1970 return skb;
1971
1972 if (HDD_IPA_UC_NUM_WDI_PIPE != hdd_ipa->activated_fw_pipe)
1973 return skb;
1974
Leo Changcc923e22016-06-16 15:29:03 -07001975 if (skb_headroom(skb) <
1976 (sizeof(struct ipa_header) + sizeof(struct frag_header)))
Leo Chang07b28f62016-05-11 12:29:22 -07001977 return skb;
1978
Leo Chang3bc8fed2015-11-13 10:59:47 -08001979 ipa_header = (struct ipa_header *) skb_push(skb,
1980 sizeof(struct ipa_header));
1981 if (!ipa_header) {
1982 /* No headroom, legacy */
1983 return skb;
1984 }
1985 memset(ipa_header, 0, sizeof(*ipa_header));
1986 ipa_header->vdev_id = 0;
1987
1988 frag_header = (struct frag_header *) skb_push(skb,
1989 sizeof(struct frag_header));
1990 if (!frag_header) {
1991 /* No headroom, drop */
1992 kfree_skb(skb);
1993 return NULL;
1994 }
1995 memset(frag_header, 0, sizeof(*frag_header));
1996 frag_header->length = skb->len - sizeof(struct frag_header)
1997 - sizeof(struct ipa_header);
1998
1999 ipa_tx_dp(IPA_CLIENT_WLAN1_CONS, skb, NULL);
2000 return NULL;
Leo Change3e49442015-10-26 20:07:13 -07002001}
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002002
2003/**
2004 * hdd_ipa_wake_lock_timer_func() - Wake lock work handler
2005 * @work: scheduled work
2006 *
2007 * When IPA resources are released in hdd_ipa_rm_try_release() we do
2008 * not want to immediately release the wake lock since the system
2009 * would then potentially try to suspend when there is a healthy data
2010 * rate. Deferred work is scheduled and this function handles the
2011 * work. When this function is called, if the IPA resource is still
2012 * released then we release the wake lock.
2013 *
2014 * Return: None
2015 */
2016static void hdd_ipa_wake_lock_timer_func(struct work_struct *work)
2017{
2018 struct hdd_ipa_priv *hdd_ipa = container_of(to_delayed_work(work),
2019 struct hdd_ipa_priv,
2020 wake_lock_work);
2021
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302022 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002023
2024 if (hdd_ipa->rm_state != HDD_IPA_RM_RELEASED)
2025 goto end;
2026
2027 hdd_ipa->wake_lock_released = true;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302028 qdf_wake_lock_release(&hdd_ipa->wake_lock,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002029 WIFI_POWER_EVENT_WAKELOCK_IPA);
2030
2031end:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302032 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002033}
2034
2035/**
2036 * hdd_ipa_rm_request() - Request resource from IPA
2037 * @hdd_ipa: Global HDD IPA context
2038 *
2039 * Return: 0 on success, negative errno on error
2040 */
2041static int hdd_ipa_rm_request(struct hdd_ipa_priv *hdd_ipa)
2042{
2043 int ret = 0;
2044
2045 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2046 return 0;
2047
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302048 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002049
2050 switch (hdd_ipa->rm_state) {
2051 case HDD_IPA_RM_GRANTED:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302052 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002053 return 0;
2054 case HDD_IPA_RM_GRANT_PENDING:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302055 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002056 return -EINPROGRESS;
2057 case HDD_IPA_RM_RELEASED:
2058 hdd_ipa->rm_state = HDD_IPA_RM_GRANT_PENDING;
2059 break;
2060 }
2061
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302062 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002063
2064 ret = ipa_rm_inactivity_timer_request_resource(
2065 IPA_RM_RESOURCE_WLAN_PROD);
2066
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302067 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002068 if (ret == 0) {
2069 hdd_ipa->rm_state = HDD_IPA_RM_GRANTED;
2070 hdd_ipa->stats.num_rm_grant_imm++;
2071 }
2072
2073 cancel_delayed_work(&hdd_ipa->wake_lock_work);
2074 if (hdd_ipa->wake_lock_released) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302075 qdf_wake_lock_acquire(&hdd_ipa->wake_lock,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002076 WIFI_POWER_EVENT_WAKELOCK_IPA);
2077 hdd_ipa->wake_lock_released = false;
2078 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302079 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002080
2081 return ret;
2082}
2083
2084/**
2085 * hdd_ipa_rm_try_release() - Attempt to release IPA resource
2086 * @hdd_ipa: Global HDD IPA context
2087 *
2088 * Return: 0 if resources released, negative errno otherwise
2089 */
2090static int hdd_ipa_rm_try_release(struct hdd_ipa_priv *hdd_ipa)
2091{
2092 int ret = 0;
2093
2094 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2095 return 0;
2096
2097 if (atomic_read(&hdd_ipa->tx_ref_cnt))
2098 return -EAGAIN;
2099
2100 spin_lock_bh(&hdd_ipa->q_lock);
2101 if (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) &&
2102 (hdd_ipa->pending_hw_desc_cnt || hdd_ipa->pend_q_cnt)) {
2103 spin_unlock_bh(&hdd_ipa->q_lock);
2104 return -EAGAIN;
2105 }
2106 spin_unlock_bh(&hdd_ipa->q_lock);
2107
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302108 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002109
Nirav Shahcbc6d722016-03-01 16:24:53 +05302110 if (!qdf_nbuf_is_queue_empty(&hdd_ipa->pm_queue_head)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302111 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002112 return -EAGAIN;
2113 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302114 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002115
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302116 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002117 switch (hdd_ipa->rm_state) {
2118 case HDD_IPA_RM_GRANTED:
2119 break;
2120 case HDD_IPA_RM_GRANT_PENDING:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302121 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002122 return -EINPROGRESS;
2123 case HDD_IPA_RM_RELEASED:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302124 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002125 return 0;
2126 }
2127
2128 /* IPA driver returns immediately so set the state here to avoid any
2129 * race condition.
2130 */
2131 hdd_ipa->rm_state = HDD_IPA_RM_RELEASED;
2132 hdd_ipa->stats.num_rm_release++;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302133 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002134
2135 ret =
2136 ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_WLAN_PROD);
2137
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302138 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002139 if (unlikely(ret != 0)) {
2140 hdd_ipa->rm_state = HDD_IPA_RM_GRANTED;
2141 WARN_ON(1);
2142 }
2143
2144 /*
2145 * If wake_lock is released immediately, kernel would try to suspend
2146 * immediately as well, Just avoid ping-pong between suspend-resume
2147 * while there is healthy amount of data transfer going on by
2148 * releasing the wake_lock after some delay.
2149 */
2150 schedule_delayed_work(&hdd_ipa->wake_lock_work,
2151 msecs_to_jiffies
2152 (HDD_IPA_RX_INACTIVITY_MSEC_DELAY));
2153
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302154 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002155
2156 return ret;
2157}
2158
2159/**
2160 * hdd_ipa_rm_notify() - IPA resource manager notifier callback
2161 * @user_data: user data registered with IPA
2162 * @event: the IPA resource manager event that occurred
2163 * @data: the data associated with the event
2164 *
2165 * Return: None
2166 */
2167static void hdd_ipa_rm_notify(void *user_data, enum ipa_rm_event event,
2168 unsigned long data)
2169{
2170 struct hdd_ipa_priv *hdd_ipa = user_data;
2171
2172 if (unlikely(!hdd_ipa))
2173 return;
2174
2175 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2176 return;
2177
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302178 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "Evt: %d", event);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002179
2180 switch (event) {
2181 case IPA_RM_RESOURCE_GRANTED:
2182 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
2183 /* RM Notification comes with ISR context
2184 * it should be serialized into work queue to avoid
2185 * ISR sleep problem
2186 */
2187 hdd_ipa->uc_rm_work.event = event;
2188 schedule_work(&hdd_ipa->uc_rm_work.work);
2189 break;
2190 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302191 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002192 hdd_ipa->rm_state = HDD_IPA_RM_GRANTED;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302193 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002194 hdd_ipa->stats.num_rm_grant++;
2195 break;
2196
2197 case IPA_RM_RESOURCE_RELEASED:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302198 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "RM Release");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002199 hdd_ipa->resource_unloading = false;
2200 break;
2201
2202 default:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302203 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Unknown RM Evt: %d", event);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002204 break;
2205 }
2206}
2207
2208/**
2209 * hdd_ipa_rm_cons_release() - WLAN consumer resource release handler
2210 *
2211 * Callback function registered with IPA that is called when IPA wants
2212 * to release the WLAN consumer resource
2213 *
2214 * Return: 0 if the request is granted, negative errno otherwise
2215 */
2216static int hdd_ipa_rm_cons_release(void)
2217{
2218 return 0;
2219}
2220
2221/**
2222 * hdd_ipa_rm_cons_request() - WLAN consumer resource request handler
2223 *
2224 * Callback function registered with IPA that is called when IPA wants
2225 * to access the WLAN consumer resource
2226 *
2227 * Return: 0 if the request is granted, negative errno otherwise
2228 */
2229static int hdd_ipa_rm_cons_request(void)
2230{
Yun Park4d8b60a2015-10-22 13:59:32 -07002231 int ret = 0;
2232
2233 if (ghdd_ipa->resource_loading) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302234 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL,
Yun Park4d8b60a2015-10-22 13:59:32 -07002235 "%s: IPA resource loading in progress",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002236 __func__);
2237 ghdd_ipa->pending_cons_req = true;
Yun Park4d8b60a2015-10-22 13:59:32 -07002238 ret = -EINPROGRESS;
2239 } else if (ghdd_ipa->resource_unloading) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302240 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL,
Yun Park4d8b60a2015-10-22 13:59:32 -07002241 "%s: IPA resource unloading in progress",
2242 __func__);
2243 ghdd_ipa->pending_cons_req = true;
2244 ret = -EPERM;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002245 }
Yun Park4d8b60a2015-10-22 13:59:32 -07002246
2247 return ret;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002248}
2249
2250/**
2251 * hdd_ipa_set_perf_level() - Set IPA performance level
2252 * @hdd_ctx: Global HDD context
2253 * @tx_packets: Number of packets transmitted in the last sample period
2254 * @rx_packets: Number of packets received in the last sample period
2255 *
2256 * Return: 0 on success, negative errno on error
2257 */
2258int hdd_ipa_set_perf_level(hdd_context_t *hdd_ctx, uint64_t tx_packets,
2259 uint64_t rx_packets)
2260{
2261 uint32_t next_cons_bw, next_prod_bw;
2262 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
2263 struct ipa_rm_perf_profile profile;
2264 int ret;
2265
2266 if ((!hdd_ipa_is_enabled(hdd_ctx)) ||
2267 (!hdd_ipa_is_clk_scaling_enabled(hdd_ctx)))
2268 return 0;
2269
2270 memset(&profile, 0, sizeof(profile));
2271
2272 if (tx_packets > (hdd_ctx->config->busBandwidthHighThreshold / 2))
2273 next_cons_bw = hdd_ctx->config->IpaHighBandwidthMbps;
2274 else if (tx_packets >
2275 (hdd_ctx->config->busBandwidthMediumThreshold / 2))
2276 next_cons_bw = hdd_ctx->config->IpaMediumBandwidthMbps;
2277 else
2278 next_cons_bw = hdd_ctx->config->IpaLowBandwidthMbps;
2279
2280 if (rx_packets > (hdd_ctx->config->busBandwidthHighThreshold / 2))
2281 next_prod_bw = hdd_ctx->config->IpaHighBandwidthMbps;
2282 else if (rx_packets >
2283 (hdd_ctx->config->busBandwidthMediumThreshold / 2))
2284 next_prod_bw = hdd_ctx->config->IpaMediumBandwidthMbps;
2285 else
2286 next_prod_bw = hdd_ctx->config->IpaLowBandwidthMbps;
2287
2288 HDD_IPA_LOG(LOG1,
2289 "CONS perf curr: %d, next: %d",
2290 hdd_ipa->curr_cons_bw, next_cons_bw);
2291 HDD_IPA_LOG(LOG1,
2292 "PROD perf curr: %d, next: %d",
2293 hdd_ipa->curr_prod_bw, next_prod_bw);
2294
2295 if (hdd_ipa->curr_cons_bw != next_cons_bw) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302296 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002297 "Requesting CONS perf curr: %d, next: %d",
2298 hdd_ipa->curr_cons_bw, next_cons_bw);
2299 profile.max_supported_bandwidth_mbps = next_cons_bw;
2300 ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WLAN_CONS,
2301 &profile);
2302 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302303 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002304 "RM CONS set perf profile failed: %d", ret);
2305
2306 return ret;
2307 }
2308 hdd_ipa->curr_cons_bw = next_cons_bw;
2309 hdd_ipa->stats.num_cons_perf_req++;
2310 }
2311
2312 if (hdd_ipa->curr_prod_bw != next_prod_bw) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302313 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002314 "Requesting PROD perf curr: %d, next: %d",
2315 hdd_ipa->curr_prod_bw, next_prod_bw);
2316 profile.max_supported_bandwidth_mbps = next_prod_bw;
2317 ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WLAN_PROD,
2318 &profile);
2319 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302320 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002321 "RM PROD set perf profile failed: %d", ret);
2322 return ret;
2323 }
2324 hdd_ipa->curr_prod_bw = next_prod_bw;
2325 hdd_ipa->stats.num_prod_perf_req++;
2326 }
2327
2328 return 0;
2329}
2330
2331/**
Rajeev Kumar217f2172016-01-06 18:11:55 -08002332 * hdd_ipa_init_uc_rm_work - init ipa uc resource manager work
2333 * @work: struct work_struct
2334 * @work_handler: work_handler
2335 *
2336 * Return: none
2337 */
Rajeev Kumar217f2172016-01-06 18:11:55 -08002338static void hdd_ipa_init_uc_rm_work(struct work_struct *work,
2339 work_func_t work_handler)
2340{
2341 INIT_WORK(work, work_handler);
2342}
Rajeev Kumar217f2172016-01-06 18:11:55 -08002343
2344/**
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002345 * hdd_ipa_setup_rm() - Setup IPA resource management
2346 * @hdd_ipa: Global HDD IPA context
2347 *
2348 * Return: 0 on success, negative errno on error
2349 */
2350static int hdd_ipa_setup_rm(struct hdd_ipa_priv *hdd_ipa)
2351{
2352 struct ipa_rm_create_params create_params = { 0 };
2353 int ret;
2354
2355 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2356 return 0;
2357
Rajeev Kumar217f2172016-01-06 18:11:55 -08002358 hdd_ipa_init_uc_rm_work(&hdd_ipa->uc_rm_work.work,
2359 hdd_ipa_uc_rm_notify_defer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002360 memset(&create_params, 0, sizeof(create_params));
2361 create_params.name = IPA_RM_RESOURCE_WLAN_PROD;
2362 create_params.reg_params.user_data = hdd_ipa;
2363 create_params.reg_params.notify_cb = hdd_ipa_rm_notify;
2364 create_params.floor_voltage = IPA_VOLTAGE_SVS;
2365
2366 ret = ipa_rm_create_resource(&create_params);
2367 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302368 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002369 "Create RM resource failed: %d", ret);
2370 goto setup_rm_fail;
2371 }
2372
2373 memset(&create_params, 0, sizeof(create_params));
2374 create_params.name = IPA_RM_RESOURCE_WLAN_CONS;
2375 create_params.request_resource = hdd_ipa_rm_cons_request;
2376 create_params.release_resource = hdd_ipa_rm_cons_release;
2377 create_params.floor_voltage = IPA_VOLTAGE_SVS;
2378
2379 ret = ipa_rm_create_resource(&create_params);
2380 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302381 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002382 "Create RM CONS resource failed: %d", ret);
2383 goto delete_prod;
2384 }
2385
2386 ipa_rm_add_dependency(IPA_RM_RESOURCE_WLAN_PROD,
2387 IPA_RM_RESOURCE_APPS_CONS);
2388
2389 ret = ipa_rm_inactivity_timer_init(IPA_RM_RESOURCE_WLAN_PROD,
2390 HDD_IPA_RX_INACTIVITY_MSEC_DELAY);
2391 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302392 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Timer init failed: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002393 ret);
2394 goto timer_init_failed;
2395 }
2396
2397 /* Set the lowest bandwidth to start with */
2398 ret = hdd_ipa_set_perf_level(hdd_ipa->hdd_ctx, 0, 0);
2399
2400 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302401 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002402 "Set perf level failed: %d", ret);
2403 goto set_perf_failed;
2404 }
2405
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302406 qdf_wake_lock_create(&hdd_ipa->wake_lock, "wlan_ipa");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002407 INIT_DELAYED_WORK(&hdd_ipa->wake_lock_work,
2408 hdd_ipa_wake_lock_timer_func);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302409 qdf_spinlock_create(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002410 hdd_ipa->rm_state = HDD_IPA_RM_RELEASED;
2411 hdd_ipa->wake_lock_released = true;
2412 atomic_set(&hdd_ipa->tx_ref_cnt, 0);
2413
2414 return ret;
2415
2416set_perf_failed:
2417 ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WLAN_PROD);
2418
2419timer_init_failed:
2420 ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS);
2421
2422delete_prod:
2423 ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD);
2424
2425setup_rm_fail:
2426 return ret;
2427}
2428
2429/**
2430 * hdd_ipa_destroy_rm_resource() - Destroy IPA resources
2431 * @hdd_ipa: Global HDD IPA context
2432 *
2433 * Destroys all resources associated with the IPA resource manager
2434 *
2435 * Return: None
2436 */
2437static void hdd_ipa_destroy_rm_resource(struct hdd_ipa_priv *hdd_ipa)
2438{
2439 int ret;
2440
2441 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2442 return;
2443
2444 cancel_delayed_work_sync(&hdd_ipa->wake_lock_work);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302445 qdf_wake_lock_destroy(&hdd_ipa->wake_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002446
2447#ifdef WLAN_OPEN_SOURCE
2448 cancel_work_sync(&hdd_ipa->uc_rm_work.work);
2449#endif
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302450 qdf_spinlock_destroy(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002451
2452 ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WLAN_PROD);
2453
2454 ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD);
2455 if (ret)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302456 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002457 "RM PROD resource delete failed %d", ret);
2458
2459 ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS);
2460 if (ret)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302461 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002462 "RM CONS resource delete failed %d", ret);
2463}
2464
2465/**
2466 * hdd_ipa_send_skb_to_network() - Send skb to kernel
2467 * @skb: network buffer
2468 * @adapter: network adapter
2469 *
2470 * Called when a network buffer is received which should not be routed
2471 * to the IPA module.
2472 *
2473 * Return: None
2474 */
Nirav Shahcbc6d722016-03-01 16:24:53 +05302475static void hdd_ipa_send_skb_to_network(qdf_nbuf_t skb,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002476 hdd_adapter_t *adapter)
2477{
2478 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
2479 unsigned int cpu_index;
2480
2481 if (!adapter || adapter->magic != WLAN_HDD_ADAPTER_MAGIC) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302482 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_LOW, "Invalid adapter: 0x%p",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002483 adapter);
2484 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302485 qdf_nbuf_free(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002486 return;
2487 }
2488
Prashanth Bhatta9e143052015-12-04 11:56:47 -08002489 if (cds_is_driver_unloading()) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002490 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302491 qdf_nbuf_free(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002492 return;
2493 }
2494
2495 skb->destructor = hdd_ipa_uc_rt_debug_destructor;
2496 skb->dev = adapter->dev;
2497 skb->protocol = eth_type_trans(skb, skb->dev);
2498 skb->ip_summed = CHECKSUM_NONE;
2499
2500 cpu_index = wlan_hdd_get_cpu();
2501
2502 ++adapter->hdd_stats.hddTxRxStats.rxPackets[cpu_index];
2503 if (netif_rx_ni(skb) == NET_RX_SUCCESS)
2504 ++adapter->hdd_stats.hddTxRxStats.rxDelivered[cpu_index];
2505 else
2506 ++adapter->hdd_stats.hddTxRxStats.rxRefused[cpu_index];
2507
2508 HDD_IPA_INCREASE_NET_SEND_COUNT(hdd_ipa);
2509 adapter->dev->last_rx = jiffies;
2510}
2511
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002512/**
2513 * hdd_ipa_w2i_cb() - WLAN to IPA callback handler
2514 * @priv: pointer to private data registered with IPA (we register a
2515 * pointer to the global IPA context)
2516 * @evt: the IPA event which triggered the callback
2517 * @data: data associated with the event
2518 *
2519 * Return: None
2520 */
2521static void hdd_ipa_w2i_cb(void *priv, enum ipa_dp_evt_type evt,
2522 unsigned long data)
2523{
2524 struct hdd_ipa_priv *hdd_ipa = NULL;
2525 hdd_adapter_t *adapter = NULL;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302526 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002527 uint8_t iface_id;
2528 uint8_t session_id;
2529 struct hdd_ipa_iface_context *iface_context;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302530 qdf_nbuf_t copy;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002531 uint8_t fw_desc;
2532 int ret;
2533
2534 hdd_ipa = (struct hdd_ipa_priv *)priv;
2535
2536 switch (evt) {
2537 case IPA_RECEIVE:
Nirav Shahcbc6d722016-03-01 16:24:53 +05302538 skb = (qdf_nbuf_t) data;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002539 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
2540 session_id = (uint8_t)skb->cb[0];
2541 iface_id = vdev_to_iface[session_id];
Govind Singhb6a89772016-08-12 11:23:35 +05302542 HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002543 "IPA_RECEIVE: session_id=%u, iface_id=%u",
2544 session_id, iface_id);
2545 } else {
2546 iface_id = HDD_IPA_GET_IFACE_ID(skb->data);
2547 }
2548
2549 if (iface_id >= HDD_IPA_MAX_IFACE) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302550 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002551 "IPA_RECEIVE: Invalid iface_id: %u",
2552 iface_id);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302553 HDD_IPA_DBG_DUMP(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002554 "w2i -- skb", skb->data, 8);
2555 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302556 qdf_nbuf_free(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002557 return;
2558 }
2559
2560 iface_context = &hdd_ipa->iface_context[iface_id];
2561 adapter = iface_context->adapter;
2562
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302563 HDD_IPA_DBG_DUMP(QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002564 "w2i -- skb", skb->data, 8);
2565 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
2566 hdd_ipa->stats.num_rx_excep++;
2567 skb_pull(skb, HDD_IPA_UC_WLAN_CLD_HDR_LEN);
2568 } else {
2569 skb_pull(skb, HDD_IPA_WLAN_CLD_HDR_LEN);
2570 }
2571
2572 iface_context->stats.num_rx_ipa_excep++;
2573
2574 /* Disable to forward Intra-BSS Rx packets when
2575 * ap_isolate=1 in hostapd.conf
2576 */
Yun Park046101c2016-09-02 15:32:14 -07002577 if (!adapter->sessionCtx.ap.apDisableIntraBssFwd) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002578 /*
2579 * When INTRA_BSS_FWD_OFFLOAD is enabled, FW will send
2580 * all Rx packets to IPA uC, which need to be forwarded
2581 * to other interface.
2582 * And, IPA driver will send back to WLAN host driver
2583 * through exception pipe with fw_desc field set by FW.
2584 * Here we are checking fw_desc field for FORWARD bit
2585 * set, and forward to Tx. Then copy to kernel stack
2586 * only when DISCARD bit is not set.
2587 */
2588 fw_desc = (uint8_t)skb->cb[1];
2589
Leo Chang3bc8fed2015-11-13 10:59:47 -08002590 if (fw_desc & HDD_IPA_FW_RX_DESC_FORWARD_M) {
Govind Singhb6a89772016-08-12 11:23:35 +05302591 HDD_IPA_DP_LOG(
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302592 QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002593 "Forward packet to Tx (fw_desc=%d)",
2594 fw_desc);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302595 copy = qdf_nbuf_copy(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002596 if (copy) {
2597 hdd_ipa->ipa_tx_forward++;
2598 ret = hdd_softap_hard_start_xmit(
2599 (struct sk_buff *)copy,
2600 adapter->dev);
2601 if (ret) {
2602 HDD_IPA_LOG(
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302603 QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002604 "Forward packet tx fail");
2605 hdd_ipa->stats.
2606 num_tx_bcmc_err++;
2607 } else {
2608 hdd_ipa->stats.num_tx_bcmc++;
2609 }
2610 }
2611 }
Mahesh Kumar Kalikot Veetil221dc672015-11-06 14:27:28 -08002612
Leo Chang3bc8fed2015-11-13 10:59:47 -08002613 if (fw_desc & HDD_IPA_FW_RX_DESC_DISCARD_M) {
Mahesh Kumar Kalikot Veetil221dc672015-11-06 14:27:28 -08002614 HDD_IPA_INCREASE_INTERNAL_DROP_COUNT(hdd_ipa);
2615 hdd_ipa->ipa_rx_discard++;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302616 qdf_nbuf_free(skb);
Mahesh Kumar Kalikot Veetil221dc672015-11-06 14:27:28 -08002617 break;
2618 }
2619
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002620 } else {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302621 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002622 "Intra-BSS FWD is disabled-skip forward to Tx");
2623 }
2624
2625 hdd_ipa_send_skb_to_network(skb, adapter);
2626 break;
2627
2628 default:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302629 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002630 "w2i cb wrong event: 0x%x", evt);
2631 return;
2632 }
2633}
2634
2635/**
2636 * hdd_ipa_nbuf_cb() - IPA TX complete callback
2637 * @skb: packet buffer which was transmitted
2638 *
2639 * Return: None
2640 */
Nirav Shahcbc6d722016-03-01 16:24:53 +05302641void hdd_ipa_nbuf_cb(qdf_nbuf_t skb)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002642{
2643 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
2644
Govind Singhb6a89772016-08-12 11:23:35 +05302645 HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, "%p",
Nirav Shahcbc6d722016-03-01 16:24:53 +05302646 wlan_hdd_stub_priv_to_addr(QDF_NBUF_CB_TX_IPA_PRIV(skb)));
Houston Hoffman43d47fa2016-02-24 16:34:30 -08002647 /* FIXME: This is broken; PRIV_DATA is now 31 bits */
Nirav Shahcbc6d722016-03-01 16:24:53 +05302648 ipa_free_skb((struct ipa_rx_data *)
2649 wlan_hdd_stub_priv_to_addr(QDF_NBUF_CB_TX_IPA_PRIV(skb)));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002650
2651 hdd_ipa->stats.num_tx_comp_cnt++;
2652
2653 atomic_dec(&hdd_ipa->tx_ref_cnt);
2654
2655 hdd_ipa_rm_try_release(hdd_ipa);
2656}
2657
2658/**
2659 * hdd_ipa_send_pkt_to_tl() - Send an IPA packet to TL
2660 * @iface_context: interface-specific IPA context
2661 * @ipa_tx_desc: packet data descriptor
2662 *
2663 * Return: None
2664 */
2665static void hdd_ipa_send_pkt_to_tl(
2666 struct hdd_ipa_iface_context *iface_context,
2667 struct ipa_rx_data *ipa_tx_desc)
2668{
2669 struct hdd_ipa_priv *hdd_ipa = iface_context->hdd_ipa;
2670 uint8_t interface_id;
2671 hdd_adapter_t *adapter = NULL;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302672 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002673
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302674 qdf_spin_lock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002675 adapter = iface_context->adapter;
2676 if (!adapter) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302677 HDD_IPA_LOG(QDF_TRACE_LEVEL_WARN, "Interface Down");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002678 ipa_free_skb(ipa_tx_desc);
2679 iface_context->stats.num_tx_drop++;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302680 qdf_spin_unlock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002681 hdd_ipa_rm_try_release(hdd_ipa);
2682 return;
2683 }
2684
2685 /*
2686 * During CAC period, data packets shouldn't be sent over the air so
2687 * drop all the packets here
2688 */
2689 if (WLAN_HDD_GET_AP_CTX_PTR(adapter)->dfs_cac_block_tx) {
2690 ipa_free_skb(ipa_tx_desc);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302691 qdf_spin_unlock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002692 iface_context->stats.num_tx_cac_drop++;
2693 hdd_ipa_rm_try_release(hdd_ipa);
2694 return;
2695 }
2696
2697 interface_id = adapter->sessionId;
2698 ++adapter->stats.tx_packets;
2699
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302700 qdf_spin_unlock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002701
2702 skb = ipa_tx_desc->skb;
2703
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302704 qdf_mem_set(skb->cb, sizeof(skb->cb), 0);
Nirav Shahcbc6d722016-03-01 16:24:53 +05302705 qdf_nbuf_ipa_owned_set(skb);
Houston Hoffman43d47fa2016-02-24 16:34:30 -08002706 /* FIXME: This is broken. No such field in cb any more:
2707 NBUF_CALLBACK_FN(skb) = hdd_ipa_nbuf_cb; */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002708 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
Nirav Shahcbc6d722016-03-01 16:24:53 +05302709 qdf_nbuf_mapped_paddr_set(skb,
Houston Hoffman43d47fa2016-02-24 16:34:30 -08002710 ipa_tx_desc->dma_addr
2711 + HDD_IPA_WLAN_FRAG_HEADER
2712 + HDD_IPA_WLAN_IPA_HEADER);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002713 ipa_tx_desc->skb->len -=
2714 HDD_IPA_WLAN_FRAG_HEADER + HDD_IPA_WLAN_IPA_HEADER;
2715 } else
Nirav Shahcbc6d722016-03-01 16:24:53 +05302716 qdf_nbuf_mapped_paddr_set(skb, ipa_tx_desc->dma_addr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002717
Houston Hoffman43d47fa2016-02-24 16:34:30 -08002718 /* FIXME: This is broken: priv_data is 31 bits */
Nirav Shahcbc6d722016-03-01 16:24:53 +05302719 qdf_nbuf_ipa_priv_set(skb, wlan_hdd_stub_addr_to_priv(ipa_tx_desc));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002720
2721 adapter->stats.tx_bytes += ipa_tx_desc->skb->len;
2722
2723 skb = ol_tx_send_ipa_data_frame(iface_context->tl_context,
2724 ipa_tx_desc->skb);
2725 if (skb) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302726 HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, "TLSHIM tx fail");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002727 ipa_free_skb(ipa_tx_desc);
2728 iface_context->stats.num_tx_err++;
2729 hdd_ipa_rm_try_release(hdd_ipa);
2730 return;
2731 }
2732
2733 atomic_inc(&hdd_ipa->tx_ref_cnt);
2734
2735 iface_context->stats.num_tx++;
2736
2737}
2738
2739/**
2740 * hdd_ipa_pm_send_pkt_to_tl() - Send queued packets to TL
2741 * @work: pointer to the scheduled work
2742 *
2743 * Called during PM resume to send packets to TL which were queued
2744 * while host was in the process of suspending.
2745 *
2746 * Return: None
2747 */
2748static void hdd_ipa_pm_send_pkt_to_tl(struct work_struct *work)
2749{
2750 struct hdd_ipa_priv *hdd_ipa = container_of(work,
2751 struct hdd_ipa_priv,
2752 pm_work);
2753 struct hdd_ipa_pm_tx_cb *pm_tx_cb = NULL;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302754 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002755 uint32_t dequeued = 0;
2756
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302757 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002758
Nirav Shahcbc6d722016-03-01 16:24:53 +05302759 while (((skb = qdf_nbuf_queue_remove(&hdd_ipa->pm_queue_head))
2760 != NULL)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302761 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002762
2763 pm_tx_cb = (struct hdd_ipa_pm_tx_cb *)skb->cb;
2764
2765 dequeued++;
2766
2767 hdd_ipa_send_pkt_to_tl(pm_tx_cb->iface_context,
2768 pm_tx_cb->ipa_tx_desc);
2769
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302770 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002771 }
2772
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302773 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002774
2775 hdd_ipa->stats.num_tx_dequeued += dequeued;
2776 if (dequeued > hdd_ipa->stats.num_max_pm_queue)
2777 hdd_ipa->stats.num_max_pm_queue = dequeued;
2778}
2779
2780/**
2781 * hdd_ipa_i2w_cb() - IPA to WLAN callback
2782 * @priv: pointer to private data registered with IPA (we register a
2783 * pointer to the interface-specific IPA context)
2784 * @evt: the IPA event which triggered the callback
2785 * @data: data associated with the event
2786 *
2787 * Return: None
2788 */
2789static void hdd_ipa_i2w_cb(void *priv, enum ipa_dp_evt_type evt,
2790 unsigned long data)
2791{
2792 struct hdd_ipa_priv *hdd_ipa = NULL;
2793 struct ipa_rx_data *ipa_tx_desc;
2794 struct hdd_ipa_iface_context *iface_context;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302795 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002796 struct hdd_ipa_pm_tx_cb *pm_tx_cb = NULL;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302797 QDF_STATUS status = QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002798
Mukul Sharma81661ae2015-10-30 20:26:02 +05302799 iface_context = (struct hdd_ipa_iface_context *)priv;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002800 if (evt != IPA_RECEIVE) {
Nirav Shahcbc6d722016-03-01 16:24:53 +05302801 skb = (qdf_nbuf_t) data;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002802 dev_kfree_skb_any(skb);
2803 iface_context->stats.num_tx_drop++;
2804 return;
2805 }
2806
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002807 ipa_tx_desc = (struct ipa_rx_data *)data;
2808
2809 hdd_ipa = iface_context->hdd_ipa;
2810
2811 /*
2812 * When SSR is going on or driver is unloading, just drop the packets.
2813 * During SSR, there is no use in queueing the packets as STA has to
2814 * connect back any way
2815 */
2816 status = wlan_hdd_validate_context(hdd_ipa->hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +05302817 if (status) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002818 ipa_free_skb(ipa_tx_desc);
2819 iface_context->stats.num_tx_drop++;
2820 return;
2821 }
2822
2823 skb = ipa_tx_desc->skb;
2824
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302825 HDD_IPA_DBG_DUMP(QDF_TRACE_LEVEL_DEBUG, "i2w", skb->data, 8);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002826
2827 /*
2828 * If PROD resource is not requested here then there may be cases where
2829 * IPA hardware may be clocked down because of not having proper
2830 * dependency graph between WLAN CONS and modem PROD pipes. Adding the
2831 * workaround to request PROD resource while data is going over CONS
2832 * pipe to prevent the IPA hardware clockdown.
2833 */
2834 hdd_ipa_rm_request(hdd_ipa);
2835
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302836 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002837 /*
2838 * If host is still suspended then queue the packets and these will be
2839 * drained later when resume completes. When packet is arrived here and
2840 * host is suspended, this means that there is already resume is in
2841 * progress.
2842 */
2843 if (hdd_ipa->suspended) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05302844 qdf_mem_set(skb->cb, sizeof(skb->cb), 0);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002845 pm_tx_cb = (struct hdd_ipa_pm_tx_cb *)skb->cb;
2846 pm_tx_cb->iface_context = iface_context;
2847 pm_tx_cb->ipa_tx_desc = ipa_tx_desc;
Nirav Shahcbc6d722016-03-01 16:24:53 +05302848 qdf_nbuf_queue_add(&hdd_ipa->pm_queue_head, skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002849 hdd_ipa->stats.num_tx_queued++;
2850
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302851 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002852 return;
2853 }
2854
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302855 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002856
2857 /*
2858 * If we are here means, host is not suspended, wait for the work queue
2859 * to finish.
2860 */
2861#ifdef WLAN_OPEN_SOURCE
2862 flush_work(&hdd_ipa->pm_work);
2863#endif
2864
2865 return hdd_ipa_send_pkt_to_tl(iface_context, ipa_tx_desc);
2866}
2867
2868/**
2869 * hdd_ipa_suspend() - Suspend IPA
2870 * @hdd_ctx: Global HDD context
2871 *
2872 * Return: 0 on success, negativer errno on error
2873 */
2874int hdd_ipa_suspend(hdd_context_t *hdd_ctx)
2875{
2876 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
2877
2878 if (!hdd_ipa_is_enabled(hdd_ctx))
2879 return 0;
2880
2881 /*
2882 * Check if IPA is ready for suspend, If we are here means, there is
2883 * high chance that suspend would go through but just to avoid any race
2884 * condition after suspend started, these checks are conducted before
2885 * allowing to suspend.
2886 */
2887 if (atomic_read(&hdd_ipa->tx_ref_cnt))
2888 return -EAGAIN;
2889
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302890 qdf_spin_lock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002891
2892 if (hdd_ipa->rm_state != HDD_IPA_RM_RELEASED) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302893 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002894 return -EAGAIN;
2895 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302896 qdf_spin_unlock_bh(&hdd_ipa->rm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002897
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302898 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002899 hdd_ipa->suspended = true;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302900 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002901
2902 return 0;
2903}
2904
2905/**
2906 * hdd_ipa_resume() - Resume IPA following suspend
2907 * hdd_ctx: Global HDD context
2908 *
2909 * Return: 0 on success, negative errno on error
2910 */
2911int hdd_ipa_resume(hdd_context_t *hdd_ctx)
2912{
2913 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
2914
2915 if (!hdd_ipa_is_enabled(hdd_ctx))
2916 return 0;
2917
2918 schedule_work(&hdd_ipa->pm_work);
2919
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302920 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002921 hdd_ipa->suspended = false;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05302922 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002923
2924 return 0;
2925}
2926
2927/**
2928 * hdd_ipa_setup_sys_pipe() - Setup all IPA Sys pipes
2929 * @hdd_ipa: Global HDD IPA context
2930 *
2931 * Return: 0 on success, negative errno on error
2932 */
2933static int hdd_ipa_setup_sys_pipe(struct hdd_ipa_priv *hdd_ipa)
2934{
2935 int i, ret = 0;
2936 struct ipa_sys_connect_params *ipa;
2937 uint32_t desc_fifo_sz;
2938
2939 /* The maximum number of descriptors that can be provided to a BAM at
2940 * once is one less than the total number of descriptors that the buffer
2941 * can contain.
2942 * If max_num_of_descriptors = (BAM_PIPE_DESCRIPTOR_FIFO_SIZE / sizeof
2943 * (SPS_DESCRIPTOR)), then (max_num_of_descriptors - 1) descriptors can
2944 * be provided at once.
2945 * Because of above requirement, one extra descriptor will be added to
2946 * make sure hardware always has one descriptor.
2947 */
2948 desc_fifo_sz = hdd_ipa->hdd_ctx->config->IpaDescSize
2949 + sizeof(struct sps_iovec);
2950
2951 /*setup TX pipes */
2952 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
2953 ipa = &hdd_ipa->sys_pipe[i].ipa_sys_params;
2954
2955 ipa->client = hdd_ipa_adapter_2_client[i].cons_client;
2956 ipa->desc_fifo_sz = desc_fifo_sz;
2957 ipa->priv = &hdd_ipa->iface_context[i];
2958 ipa->notify = hdd_ipa_i2w_cb;
2959
2960 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
2961 ipa->ipa_ep_cfg.hdr.hdr_len =
2962 HDD_IPA_UC_WLAN_TX_HDR_LEN;
2963 ipa->ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
2964 ipa->ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
2965 ipa->ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 0;
2966 ipa->ipa_ep_cfg.hdr.hdr_additional_const_len =
2967 HDD_IPA_UC_WLAN_8023_HDR_SIZE;
2968 ipa->ipa_ep_cfg.hdr_ext.hdr_little_endian = true;
2969 } else {
2970 ipa->ipa_ep_cfg.hdr.hdr_len = HDD_IPA_WLAN_TX_HDR_LEN;
2971 }
2972 ipa->ipa_ep_cfg.mode.mode = IPA_BASIC;
2973
2974 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
2975 ipa->keep_ipa_awake = 1;
2976
2977 ret = ipa_setup_sys_pipe(ipa, &(hdd_ipa->sys_pipe[i].conn_hdl));
2978 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05302979 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Failed for pipe %d"
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08002980 " ret: %d", i, ret);
2981 goto setup_sys_pipe_fail;
2982 }
2983 hdd_ipa->sys_pipe[i].conn_hdl_valid = 1;
2984 }
2985
2986 if (!hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
2987 /*
2988 * Hard code it here, this can be extended if in case
2989 * PROD pipe is also per interface.
2990 * Right now there is no advantage of doing this.
2991 */
2992 hdd_ipa->prod_client = IPA_CLIENT_WLAN1_PROD;
2993
2994 ipa = &hdd_ipa->sys_pipe[HDD_IPA_RX_PIPE].ipa_sys_params;
2995
2996 ipa->client = hdd_ipa->prod_client;
2997
2998 ipa->desc_fifo_sz = desc_fifo_sz;
2999 ipa->priv = hdd_ipa;
3000 ipa->notify = hdd_ipa_w2i_cb;
3001
3002 ipa->ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
3003 ipa->ipa_ep_cfg.hdr.hdr_len = HDD_IPA_WLAN_RX_HDR_LEN;
3004 ipa->ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 1;
3005 ipa->ipa_ep_cfg.mode.mode = IPA_BASIC;
3006
3007 if (!hdd_ipa_is_rm_enabled(hdd_ipa->hdd_ctx))
3008 ipa->keep_ipa_awake = 1;
3009
3010 ret = ipa_setup_sys_pipe(ipa, &(hdd_ipa->sys_pipe[i].conn_hdl));
3011 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303012 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003013 "Failed for RX pipe: %d", ret);
3014 goto setup_sys_pipe_fail;
3015 }
3016 hdd_ipa->sys_pipe[HDD_IPA_RX_PIPE].conn_hdl_valid = 1;
3017 }
3018
3019 return ret;
3020
3021setup_sys_pipe_fail:
3022
3023 while (--i >= 0) {
3024 ipa_teardown_sys_pipe(hdd_ipa->sys_pipe[i].conn_hdl);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303025 qdf_mem_zero(&hdd_ipa->sys_pipe[i],
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003026 sizeof(struct hdd_ipa_sys_pipe));
3027 }
3028
3029 return ret;
3030}
3031
3032/**
3033 * hdd_ipa_teardown_sys_pipe() - Tear down all IPA Sys pipes
3034 * @hdd_ipa: Global HDD IPA context
3035 *
3036 * Return: None
3037 */
3038static void hdd_ipa_teardown_sys_pipe(struct hdd_ipa_priv *hdd_ipa)
3039{
3040 int ret = 0, i;
3041 for (i = 0; i < HDD_IPA_MAX_SYSBAM_PIPE; i++) {
3042 if (hdd_ipa->sys_pipe[i].conn_hdl_valid) {
3043 ret =
3044 ipa_teardown_sys_pipe(hdd_ipa->sys_pipe[i].
3045 conn_hdl);
3046 if (ret)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303047 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Failed: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003048 ret);
3049
3050 hdd_ipa->sys_pipe[i].conn_hdl_valid = 0;
3051 }
3052 }
3053}
3054
3055/**
3056 * hdd_ipa_register_interface() - register IPA interface
3057 * @hdd_ipa: Global IPA context
3058 * @iface_context: Per-interface IPA context
3059 *
3060 * Return: 0 on success, negative errno on error
3061 */
3062static int hdd_ipa_register_interface(struct hdd_ipa_priv *hdd_ipa,
3063 struct hdd_ipa_iface_context
3064 *iface_context)
3065{
3066 struct ipa_tx_intf tx_intf;
3067 struct ipa_rx_intf rx_intf;
3068 struct ipa_ioc_tx_intf_prop *tx_prop = NULL;
3069 struct ipa_ioc_rx_intf_prop *rx_prop = NULL;
3070 char *ifname = iface_context->adapter->dev->name;
3071
3072 char ipv4_hdr_name[IPA_RESOURCE_NAME_MAX];
3073 char ipv6_hdr_name[IPA_RESOURCE_NAME_MAX];
3074
3075 int num_prop = 1;
3076 int ret = 0;
3077
3078 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx))
3079 num_prop++;
3080
3081 /* Allocate TX properties for TOS categories, 1 each for IPv4 & IPv6 */
3082 tx_prop =
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303083 qdf_mem_malloc(sizeof(struct ipa_ioc_tx_intf_prop) * num_prop);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003084 if (!tx_prop) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303085 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "tx_prop allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003086 goto register_interface_fail;
3087 }
3088
3089 /* Allocate RX properties, 1 each for IPv4 & IPv6 */
3090 rx_prop =
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303091 qdf_mem_malloc(sizeof(struct ipa_ioc_rx_intf_prop) * num_prop);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003092 if (!rx_prop) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303093 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "rx_prop allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003094 goto register_interface_fail;
3095 }
3096
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303097 qdf_mem_zero(&tx_intf, sizeof(tx_intf));
3098 qdf_mem_zero(&rx_intf, sizeof(rx_intf));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003099
3100 snprintf(ipv4_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s",
3101 ifname, HDD_IPA_IPV4_NAME_EXT);
3102 snprintf(ipv6_hdr_name, IPA_RESOURCE_NAME_MAX, "%s%s",
3103 ifname, HDD_IPA_IPV6_NAME_EXT);
3104
3105 rx_prop[IPA_IP_v4].ip = IPA_IP_v4;
3106 rx_prop[IPA_IP_v4].src_pipe = iface_context->prod_client;
3107 rx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
3108 rx_prop[IPA_IP_v4].attrib.attrib_mask = IPA_FLT_META_DATA;
3109
3110 /*
3111 * Interface ID is 3rd byte in the CLD header. Add the meta data and
3112 * mask to identify the interface in IPA hardware
3113 */
3114 rx_prop[IPA_IP_v4].attrib.meta_data =
3115 htonl(iface_context->adapter->sessionId << 16);
3116 rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000);
3117
3118 rx_intf.num_props++;
3119 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3120 rx_prop[IPA_IP_v6].ip = IPA_IP_v6;
3121 rx_prop[IPA_IP_v6].src_pipe = iface_context->prod_client;
3122 rx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
3123 rx_prop[IPA_IP_v4].attrib.attrib_mask = IPA_FLT_META_DATA;
3124 rx_prop[IPA_IP_v4].attrib.meta_data =
3125 htonl(iface_context->adapter->sessionId << 16);
3126 rx_prop[IPA_IP_v4].attrib.meta_data_mask = htonl(0x00FF0000);
3127
3128 rx_intf.num_props++;
3129 }
3130
3131 tx_prop[IPA_IP_v4].ip = IPA_IP_v4;
3132 tx_prop[IPA_IP_v4].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
3133 tx_prop[IPA_IP_v4].dst_pipe = IPA_CLIENT_WLAN1_CONS;
3134 tx_prop[IPA_IP_v4].alt_dst_pipe = iface_context->cons_client;
3135 strlcpy(tx_prop[IPA_IP_v4].hdr_name, ipv4_hdr_name,
3136 IPA_RESOURCE_NAME_MAX);
3137 tx_intf.num_props++;
3138
3139 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3140 tx_prop[IPA_IP_v6].ip = IPA_IP_v6;
3141 tx_prop[IPA_IP_v6].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
3142 tx_prop[IPA_IP_v6].dst_pipe = IPA_CLIENT_WLAN1_CONS;
3143 tx_prop[IPA_IP_v6].alt_dst_pipe = iface_context->cons_client;
3144 strlcpy(tx_prop[IPA_IP_v6].hdr_name, ipv6_hdr_name,
3145 IPA_RESOURCE_NAME_MAX);
3146 tx_intf.num_props++;
3147 }
3148
3149 tx_intf.prop = tx_prop;
3150 rx_intf.prop = rx_prop;
3151
3152 /* Call the ipa api to register interface */
3153 ret = ipa_register_intf(ifname, &tx_intf, &rx_intf);
3154
3155register_interface_fail:
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303156 qdf_mem_free(tx_prop);
3157 qdf_mem_free(rx_prop);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003158 return ret;
3159}
3160
3161/**
3162 * hdd_remove_ipa_header() - Remove a specific header from IPA
3163 * @name: Name of the header to be removed
3164 *
3165 * Return: None
3166 */
3167static void hdd_ipa_remove_header(char *name)
3168{
3169 struct ipa_ioc_get_hdr hdrlookup;
3170 int ret = 0, len;
3171 struct ipa_ioc_del_hdr *ipa_hdr;
3172
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303173 qdf_mem_zero(&hdrlookup, sizeof(hdrlookup));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003174 strlcpy(hdrlookup.name, name, sizeof(hdrlookup.name));
3175 ret = ipa_get_hdr(&hdrlookup);
3176 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303177 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "Hdr deleted already %s, %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003178 name, ret);
3179 return;
3180 }
3181
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303182 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "hdl: 0x%x", hdrlookup.hdl);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003183 len = sizeof(struct ipa_ioc_del_hdr) + sizeof(struct ipa_hdr_del) * 1;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303184 ipa_hdr = (struct ipa_ioc_del_hdr *)qdf_mem_malloc(len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003185 if (ipa_hdr == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303186 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "ipa_hdr allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003187 return;
3188 }
3189 ipa_hdr->num_hdls = 1;
3190 ipa_hdr->commit = 0;
3191 ipa_hdr->hdl[0].hdl = hdrlookup.hdl;
3192 ipa_hdr->hdl[0].status = -1;
3193 ret = ipa_del_hdr(ipa_hdr);
3194 if (ret != 0)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303195 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "Delete header failed: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003196 ret);
3197
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303198 qdf_mem_free(ipa_hdr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003199}
3200
3201/**
3202 * hdd_ipa_add_header_info() - Add IPA header for a given interface
3203 * @hdd_ipa: Global HDD IPA context
3204 * @iface_context: Interface-specific HDD IPA context
3205 * @mac_addr: Interface MAC address
3206 *
3207 * Return: 0 on success, negativer errno value on error
3208 */
3209static int hdd_ipa_add_header_info(struct hdd_ipa_priv *hdd_ipa,
3210 struct hdd_ipa_iface_context *iface_context,
3211 uint8_t *mac_addr)
3212{
3213 hdd_adapter_t *adapter = iface_context->adapter;
3214 char *ifname;
3215 struct ipa_ioc_add_hdr *ipa_hdr = NULL;
3216 int ret = -EINVAL;
3217 struct hdd_ipa_tx_hdr *tx_hdr = NULL;
3218 struct hdd_ipa_uc_tx_hdr *uc_tx_hdr = NULL;
3219
3220 ifname = adapter->dev->name;
3221
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303222 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "Add Partial hdr: %s, %pM",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003223 ifname, mac_addr);
3224
3225 /* dynamically allocate the memory to add the hdrs */
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303226 ipa_hdr = qdf_mem_malloc(sizeof(struct ipa_ioc_add_hdr)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003227 + sizeof(struct ipa_hdr_add));
3228 if (!ipa_hdr) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303229 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003230 "%s: ipa_hdr allocation failed", ifname);
3231 ret = -ENOMEM;
3232 goto end;
3233 }
3234
3235 ipa_hdr->commit = 0;
3236 ipa_hdr->num_hdrs = 1;
3237
3238 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3239 uc_tx_hdr = (struct hdd_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr;
3240 memcpy(uc_tx_hdr, &ipa_uc_tx_hdr, HDD_IPA_UC_WLAN_TX_HDR_LEN);
3241 memcpy(uc_tx_hdr->eth.h_source, mac_addr, ETH_ALEN);
3242 uc_tx_hdr->ipa_hd.vdev_id = iface_context->adapter->sessionId;
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303243 HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003244 "ifname=%s, vdev_id=%d",
3245 ifname, uc_tx_hdr->ipa_hd.vdev_id);
3246 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3247 ifname, HDD_IPA_IPV4_NAME_EXT);
3248 ipa_hdr->hdr[0].hdr_len = HDD_IPA_UC_WLAN_TX_HDR_LEN;
3249 ipa_hdr->hdr[0].type = IPA_HDR_L2_ETHERNET_II;
3250 ipa_hdr->hdr[0].is_partial = 1;
3251 ipa_hdr->hdr[0].hdr_hdl = 0;
3252 ipa_hdr->hdr[0].is_eth2_ofst_valid = 1;
3253 ipa_hdr->hdr[0].eth2_ofst = HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET;
3254
3255 ret = ipa_add_hdr(ipa_hdr);
3256 } else {
3257 tx_hdr = (struct hdd_ipa_tx_hdr *)ipa_hdr->hdr[0].hdr;
3258
3259 /* Set the Source MAC */
3260 memcpy(tx_hdr, &ipa_tx_hdr, HDD_IPA_WLAN_TX_HDR_LEN);
3261 memcpy(tx_hdr->eth.h_source, mac_addr, ETH_ALEN);
3262
3263 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3264 ifname, HDD_IPA_IPV4_NAME_EXT);
3265 ipa_hdr->hdr[0].hdr_len = HDD_IPA_WLAN_TX_HDR_LEN;
3266 ipa_hdr->hdr[0].is_partial = 1;
3267 ipa_hdr->hdr[0].hdr_hdl = 0;
3268 ipa_hdr->hdr[0].is_eth2_ofst_valid = 1;
3269 ipa_hdr->hdr[0].eth2_ofst = HDD_IPA_WLAN_HDR_DES_MAC_OFFSET;
3270
3271 /* Set the type to IPV4 in the header */
3272 tx_hdr->llc_snap.eth_type = cpu_to_be16(ETH_P_IP);
3273
3274 ret = ipa_add_hdr(ipa_hdr);
3275 }
3276 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303277 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "%s IPv4 add hdr failed: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003278 ifname, ret);
3279 goto end;
3280 }
3281
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303282 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: IPv4 hdr_hdl: 0x%x",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003283 ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl);
3284
3285 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3286 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3287 ifname, HDD_IPA_IPV6_NAME_EXT);
3288
3289 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3290 uc_tx_hdr =
3291 (struct hdd_ipa_uc_tx_hdr *)ipa_hdr->hdr[0].hdr;
3292 uc_tx_hdr->eth.h_proto = cpu_to_be16(ETH_P_IPV6);
3293 } else {
3294 /* Set the type to IPV6 in the header */
3295 tx_hdr = (struct hdd_ipa_tx_hdr *)ipa_hdr->hdr[0].hdr;
3296 tx_hdr->llc_snap.eth_type = cpu_to_be16(ETH_P_IPV6);
3297 }
3298
3299 ret = ipa_add_hdr(ipa_hdr);
3300 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303301 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003302 "%s: IPv6 add hdr failed: %d", ifname, ret);
3303 goto clean_ipv4_hdr;
3304 }
3305
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303306 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: IPv6 hdr_hdl: 0x%x",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003307 ipa_hdr->hdr[0].name, ipa_hdr->hdr[0].hdr_hdl);
3308 }
3309
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303310 qdf_mem_free(ipa_hdr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003311
3312 return ret;
3313
3314clean_ipv4_hdr:
3315 snprintf(ipa_hdr->hdr[0].name, IPA_RESOURCE_NAME_MAX, "%s%s",
3316 ifname, HDD_IPA_IPV4_NAME_EXT);
3317 hdd_ipa_remove_header(ipa_hdr->hdr[0].name);
3318end:
3319 if (ipa_hdr)
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303320 qdf_mem_free(ipa_hdr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003321
3322 return ret;
3323}
3324
3325/**
3326 * hdd_ipa_clean_hdr() - Cleanup IPA on a given adapter
3327 * @adapter: Adapter upon which IPA was previously configured
3328 *
3329 * Return: None
3330 */
3331static void hdd_ipa_clean_hdr(hdd_adapter_t *adapter)
3332{
3333 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
3334 int ret;
3335 char name_ipa[IPA_RESOURCE_NAME_MAX];
3336
3337 /* Remove the headers */
3338 snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s",
3339 adapter->dev->name, HDD_IPA_IPV4_NAME_EXT);
3340 hdd_ipa_remove_header(name_ipa);
3341
3342 if (hdd_ipa_is_ipv6_enabled(hdd_ipa->hdd_ctx)) {
3343 snprintf(name_ipa, IPA_RESOURCE_NAME_MAX, "%s%s",
3344 adapter->dev->name, HDD_IPA_IPV6_NAME_EXT);
3345 hdd_ipa_remove_header(name_ipa);
3346 }
3347 /* unregister the interface with IPA */
3348 ret = ipa_deregister_intf(adapter->dev->name);
3349 if (ret)
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303350 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003351 "%s: ipa_deregister_intf fail: %d",
3352 adapter->dev->name, ret);
3353}
3354
3355/**
3356 * hdd_ipa_cleanup_iface() - Cleanup IPA on a given interface
3357 * @iface_context: interface-specific IPA context
3358 *
3359 * Return: None
3360 */
3361static void hdd_ipa_cleanup_iface(struct hdd_ipa_iface_context *iface_context)
3362{
3363 if (iface_context == NULL)
3364 return;
3365
3366 hdd_ipa_clean_hdr(iface_context->adapter);
3367
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303368 qdf_spin_lock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003369 iface_context->adapter->ipa_context = NULL;
3370 iface_context->adapter = NULL;
3371 iface_context->tl_context = NULL;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303372 qdf_spin_unlock_bh(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003373 iface_context->ifa_address = 0;
3374 if (!iface_context->hdd_ipa->num_iface) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303375 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003376 "NUM INTF 0, Invalid");
Anurag Chouhandf2b2682016-02-29 14:15:27 +05303377 QDF_ASSERT(0);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003378 }
3379 iface_context->hdd_ipa->num_iface--;
3380}
3381
3382/**
3383 * hdd_ipa_setup_iface() - Setup IPA on a given interface
3384 * @hdd_ipa: HDD IPA global context
3385 * @adapter: Interface upon which IPA is being setup
3386 * @sta_id: Station ID of the API instance
3387 *
3388 * Return: 0 on success, negative errno value on error
3389 */
3390static int hdd_ipa_setup_iface(struct hdd_ipa_priv *hdd_ipa,
3391 hdd_adapter_t *adapter, uint8_t sta_id)
3392{
3393 struct hdd_ipa_iface_context *iface_context = NULL;
3394 void *tl_context = NULL;
3395 int i, ret = 0;
3396
3397 /* Lower layer may send multiple START_BSS_EVENT in DFS mode or during
3398 * channel change indication. Since these indications are sent by lower
3399 * layer as SAP updates and IPA doesn't have to do anything for these
3400 * updates so ignoring!
3401 */
Krunal Sonibe766b02016-03-10 13:00:44 -08003402 if (QDF_SAP_MODE == adapter->device_mode && adapter->ipa_context)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003403 return 0;
3404
3405 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
3406 if (hdd_ipa->iface_context[i].adapter == NULL) {
3407 iface_context = &(hdd_ipa->iface_context[i]);
3408 break;
3409 }
3410 }
3411
3412 if (iface_context == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303413 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003414 "All the IPA interfaces are in use");
3415 ret = -ENOMEM;
3416 goto end;
3417 }
3418
3419 adapter->ipa_context = iface_context;
3420 iface_context->adapter = adapter;
3421 iface_context->sta_id = sta_id;
3422 tl_context = ol_txrx_get_vdev_by_sta_id(sta_id);
3423
3424 if (tl_context == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303425 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003426 "Not able to get TL context sta_id: %d", sta_id);
3427 ret = -EINVAL;
3428 goto end;
3429 }
3430
3431 iface_context->tl_context = tl_context;
3432
3433 ret = hdd_ipa_add_header_info(hdd_ipa, iface_context,
3434 adapter->dev->dev_addr);
3435
3436 if (ret)
3437 goto end;
3438
3439 /* Configure the TX and RX pipes filter rules */
3440 ret = hdd_ipa_register_interface(hdd_ipa, iface_context);
3441 if (ret)
3442 goto cleanup_header;
3443
3444 hdd_ipa->num_iface++;
3445 return ret;
3446
3447cleanup_header:
3448
3449 hdd_ipa_clean_hdr(adapter);
3450end:
3451 if (iface_context)
3452 hdd_ipa_cleanup_iface(iface_context);
3453 return ret;
3454}
3455
3456/**
3457 * hdd_ipa_msg_free_fn() - Free an IPA message
3458 * @buff: pointer to the IPA message
3459 * @len: length of the IPA message
3460 * @type: type of IPA message
3461 *
3462 * Return: None
3463 */
3464static void hdd_ipa_msg_free_fn(void *buff, uint32_t len, uint32_t type)
3465{
3466 hddLog(LOG1, "msg type:%d, len:%d", type, len);
3467 ghdd_ipa->stats.num_free_msg++;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303468 qdf_mem_free(buff);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003469}
3470
Yun Parka27049a2016-10-11 12:30:49 -07003471#ifndef QCA_LL_TX_FLOW_CONTROL_V2
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003472/**
3473 * hdd_ipa_send_mcc_scc_msg() - send IPA WLAN_SWITCH_TO_MCC/SCC message
3474 * @mcc_mode: 0=MCC/1=SCC
3475 *
3476 * Return: 0 on success, negative errno value on error
3477 */
3478int hdd_ipa_send_mcc_scc_msg(hdd_context_t *pHddCtx, bool mcc_mode)
3479{
3480 hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303481 QDF_STATUS status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003482 hdd_adapter_t *pAdapter;
3483 struct ipa_msg_meta meta;
3484 struct ipa_wlan_msg *msg;
3485 int ret;
3486
3487 if (!hdd_ipa_uc_sta_is_enabled(pHddCtx))
3488 return -EINVAL;
3489
3490 if (!pHddCtx->mcc_mode) {
3491 /* Flush TxRx queue for each adapter before switch to SCC */
3492 status = hdd_get_front_adapter(pHddCtx, &adapter_node);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05303493 while (NULL != adapter_node && QDF_STATUS_SUCCESS == status) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003494 pAdapter = adapter_node->pAdapter;
Krunal Sonibe766b02016-03-10 13:00:44 -08003495 if (pAdapter->device_mode == QDF_STA_MODE ||
3496 pAdapter->device_mode == QDF_SAP_MODE) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303497 hddLog(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003498 "MCC->SCC: Flush TxRx queue(d_mode=%d)",
3499 pAdapter->device_mode);
3500 hdd_deinit_tx_rx(pAdapter);
3501 }
3502 status = hdd_get_next_adapter(
3503 pHddCtx, adapter_node, &next);
3504 adapter_node = next;
3505 }
3506 }
3507
3508 /* Send SCC/MCC Switching event to IPA */
3509 meta.msg_len = sizeof(*msg);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303510 msg = qdf_mem_malloc(meta.msg_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003511 if (msg == NULL) {
3512 hddLog(LOGE, "msg allocation failed");
3513 return -ENOMEM;
3514 }
3515
3516 meta.msg_type = mcc_mode ?
3517 WLAN_SWITCH_TO_MCC : WLAN_SWITCH_TO_SCC;
3518 hddLog(LOG1, "ipa_send_msg(Evt:%d)", meta.msg_type);
3519
3520 ret = ipa_send_msg(&meta, msg, hdd_ipa_msg_free_fn);
3521
3522 if (ret) {
3523 hddLog(LOGE, "ipa_send_msg(Evt:%d) - fail=%d",
3524 meta.msg_type, ret);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303525 qdf_mem_free(msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003526 }
3527
3528 return ret;
3529}
Yun Parka27049a2016-10-11 12:30:49 -07003530#endif
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003531
3532/**
3533 * hdd_ipa_wlan_event_to_str() - convert IPA WLAN event to string
3534 * @event: IPA WLAN event to be converted to a string
3535 *
3536 * Return: ASCII string representing the IPA WLAN event
3537 */
3538static inline char *hdd_ipa_wlan_event_to_str(enum ipa_wlan_event event)
3539{
3540 switch (event) {
3541 case WLAN_CLIENT_CONNECT:
3542 return "WLAN_CLIENT_CONNECT";
3543 case WLAN_CLIENT_DISCONNECT:
3544 return "WLAN_CLIENT_DISCONNECT";
3545 case WLAN_CLIENT_POWER_SAVE_MODE:
3546 return "WLAN_CLIENT_POWER_SAVE_MODE";
3547 case WLAN_CLIENT_NORMAL_MODE:
3548 return "WLAN_CLIENT_NORMAL_MODE";
3549 case SW_ROUTING_ENABLE:
3550 return "SW_ROUTING_ENABLE";
3551 case SW_ROUTING_DISABLE:
3552 return "SW_ROUTING_DISABLE";
3553 case WLAN_AP_CONNECT:
3554 return "WLAN_AP_CONNECT";
3555 case WLAN_AP_DISCONNECT:
3556 return "WLAN_AP_DISCONNECT";
3557 case WLAN_STA_CONNECT:
3558 return "WLAN_STA_CONNECT";
3559 case WLAN_STA_DISCONNECT:
3560 return "WLAN_STA_DISCONNECT";
3561 case WLAN_CLIENT_CONNECT_EX:
3562 return "WLAN_CLIENT_CONNECT_EX";
3563
3564 case IPA_WLAN_EVENT_MAX:
3565 default:
3566 return "UNKNOWN";
3567 }
3568}
3569
3570/**
Mohit Khannafa99aea2016-05-12 21:43:13 -07003571 * hdd_to_ipa_wlan_event() - convert hdd_ipa_wlan_event to ipa_wlan_event
3572 * @hdd_ipa_event_type: HDD IPA WLAN event to be converted to an ipa_wlan_event
3573 *
3574 * Return: ipa_wlan_event representing the hdd_ipa_wlan_event
3575 */
3576static enum ipa_wlan_event
3577hdd_to_ipa_wlan_event(enum hdd_ipa_wlan_event hdd_ipa_event_type)
3578{
3579 enum ipa_wlan_event ipa_event;
3580
3581 switch (hdd_ipa_event_type) {
3582 case HDD_IPA_CLIENT_CONNECT:
3583 ipa_event = WLAN_CLIENT_CONNECT;
3584 break;
3585 case HDD_IPA_CLIENT_DISCONNECT:
3586 ipa_event = WLAN_CLIENT_DISCONNECT;
3587 break;
3588 case HDD_IPA_AP_CONNECT:
3589 ipa_event = WLAN_AP_CONNECT;
3590 break;
3591 case HDD_IPA_AP_DISCONNECT:
3592 ipa_event = WLAN_AP_DISCONNECT;
3593 break;
3594 case HDD_IPA_STA_CONNECT:
3595 ipa_event = WLAN_STA_CONNECT;
3596 break;
3597 case HDD_IPA_STA_DISCONNECT:
3598 ipa_event = WLAN_STA_DISCONNECT;
3599 break;
3600 case HDD_IPA_CLIENT_CONNECT_EX:
3601 ipa_event = WLAN_CLIENT_CONNECT_EX;
3602 break;
3603 case HDD_IPA_WLAN_EVENT_MAX:
3604 default:
3605 ipa_event = IPA_WLAN_EVENT_MAX;
3606 break;
3607 }
3608 return ipa_event;
3609
3610}
3611
3612/**
3613 * __hdd_ipa_wlan_evt() - IPA event handler
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003614 * @adapter: adapter upon which the event was received
3615 * @sta_id: station id for the event
Mohit Khannafa99aea2016-05-12 21:43:13 -07003616 * @type: event enum of type ipa_wlan_event
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003617 * @mac_address: MAC address associated with the event
3618 *
Mohit Khannafa99aea2016-05-12 21:43:13 -07003619 * This function is meant to be called from within wlan_hdd_ipa.c
3620 *
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003621 * Return: 0 on success, negative errno value on error
3622 */
Mohit Khannafa99aea2016-05-12 21:43:13 -07003623static int __hdd_ipa_wlan_evt(hdd_adapter_t *adapter, uint8_t sta_id,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003624 enum ipa_wlan_event type, uint8_t *mac_addr)
3625{
3626 struct hdd_ipa_priv *hdd_ipa = ghdd_ipa;
3627 struct ipa_msg_meta meta;
3628 struct ipa_wlan_msg *msg;
3629 struct ipa_wlan_msg_ex *msg_ex = NULL;
3630 int ret;
3631
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303632 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: %s evt, MAC: %pM sta_id: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003633 adapter->dev->name, hdd_ipa_wlan_event_to_str(type),
3634 mac_addr, sta_id);
3635
3636 if (type >= IPA_WLAN_EVENT_MAX)
3637 return -EINVAL;
3638
3639 if (WARN_ON(is_zero_ether_addr(mac_addr)))
3640 return -EINVAL;
3641
3642 if (!hdd_ipa || !hdd_ipa_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303643 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "IPA OFFLOAD NOT ENABLED");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003644 return -EINVAL;
3645 }
3646
3647 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx) &&
3648 !hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) &&
Krunal Sonibe766b02016-03-10 13:00:44 -08003649 (QDF_SAP_MODE != adapter->device_mode)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003650 return 0;
3651 }
3652
3653 /*
3654 * During IPA UC resource loading/unloading new events can be issued.
3655 * Store the events separately and handle them later.
3656 */
3657 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx) &&
3658 ((hdd_ipa->resource_loading) ||
3659 (hdd_ipa->resource_unloading))) {
Yun Parkf19e07d2015-11-20 11:34:27 -08003660 unsigned int pending_event_count;
3661 struct ipa_uc_pending_event *pending_event = NULL;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003662
Yun Parkf19e07d2015-11-20 11:34:27 -08003663 hdd_err("IPA resource %s inprogress",
3664 hdd_ipa->resource_loading ? "load":"unload");
3665
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303666 qdf_mutex_acquire(&hdd_ipa->event_lock);
Yun Parkf19e07d2015-11-20 11:34:27 -08003667
Anurag Chouhanffb21542016-02-17 14:33:03 +05303668 pending_event_count = qdf_list_size(&hdd_ipa->pending_event);
Yun Parkf19e07d2015-11-20 11:34:27 -08003669 if (pending_event_count >= HDD_IPA_MAX_PENDING_EVENT_COUNT) {
3670 hdd_notice("Reached max pending event count");
Anurag Chouhanffb21542016-02-17 14:33:03 +05303671 qdf_list_remove_front(&hdd_ipa->pending_event,
3672 (qdf_list_node_t **)&pending_event);
Yun Parkf19e07d2015-11-20 11:34:27 -08003673 } else {
3674 pending_event =
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303675 (struct ipa_uc_pending_event *)qdf_mem_malloc(
Yun Parkf19e07d2015-11-20 11:34:27 -08003676 sizeof(struct ipa_uc_pending_event));
3677 }
3678
3679 if (!pending_event) {
3680 hdd_err("Pending event memory alloc fail");
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303681 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003682 return -ENOMEM;
3683 }
Yun Parkf19e07d2015-11-20 11:34:27 -08003684
3685 pending_event->adapter = adapter;
3686 pending_event->sta_id = sta_id;
3687 pending_event->type = type;
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303688 qdf_mem_copy(pending_event->mac_addr,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003689 mac_addr,
Anurag Chouhan6d760662016-02-20 16:05:43 +05303690 QDF_MAC_ADDR_SIZE);
Anurag Chouhanffb21542016-02-17 14:33:03 +05303691 qdf_list_insert_back(&hdd_ipa->pending_event,
Yun Parkf19e07d2015-11-20 11:34:27 -08003692 &pending_event->node);
3693
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303694 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003695 return 0;
3696 }
3697
3698 hdd_ipa->stats.event[type]++;
3699
Leo Chang3bc8fed2015-11-13 10:59:47 -08003700 meta.msg_type = type;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003701 switch (type) {
3702 case WLAN_STA_CONNECT:
3703 /* STA already connected and without disconnect, connect again
3704 * This is Roaming scenario
3705 */
3706 if (hdd_ipa->sta_connected)
3707 hdd_ipa_cleanup_iface(adapter->ipa_context);
3708
Yun Parka37592b2016-06-11 17:10:28 -07003709 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) &&
3710 (hdd_ipa->sap_num_connected_sta > 0) &&
3711 !hdd_ipa->sta_connected)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003712 hdd_ipa_uc_offload_enable_disable(adapter,
3713 SIR_STA_RX_DATA_OFFLOAD, 1);
3714
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303715 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003716
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003717 ret = hdd_ipa_setup_iface(hdd_ipa, adapter, sta_id);
3718 if (ret) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303719 qdf_mutex_release(&hdd_ipa->event_lock);
Yun Parka37592b2016-06-11 17:10:28 -07003720 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) &&
3721 (hdd_ipa->sap_num_connected_sta > 0) &&
3722 !hdd_ipa->sta_connected)
3723 hdd_ipa_uc_offload_enable_disable(adapter,
3724 SIR_STA_RX_DATA_OFFLOAD, 0);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003725 goto end;
Yun Parka37592b2016-06-11 17:10:28 -07003726 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003727
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003728 vdev_to_iface[adapter->sessionId] =
3729 ((struct hdd_ipa_iface_context *)
Yun Parka37592b2016-06-11 17:10:28 -07003730 (adapter->ipa_context))->iface_id;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003731
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303732 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003733
3734 hdd_ipa->sta_connected = 1;
3735 break;
3736
3737 case WLAN_AP_CONNECT:
3738 /* For DFS channel we get two start_bss event (before and after
3739 * CAC). Also when ACS range includes both DFS and non DFS
3740 * channels, we could possibly change channel many times due to
3741 * RADAR detection and chosen channel may not be a DFS channels.
3742 * So dont return error here. Just discard the event.
3743 */
3744 if (adapter->ipa_context)
3745 return 0;
3746
Yun Parka37592b2016-06-11 17:10:28 -07003747 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003748 hdd_ipa_uc_offload_enable_disable(adapter,
3749 SIR_AP_RX_DATA_OFFLOAD, 1);
Yun Parka37592b2016-06-11 17:10:28 -07003750
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303751 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003752 ret = hdd_ipa_setup_iface(hdd_ipa, adapter, sta_id);
3753 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303754 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003755 "%s: Evt: %d, Interface setup failed",
3756 msg_ex->name, meta.msg_type);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303757 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003758 goto end;
Yun Parka37592b2016-06-11 17:10:28 -07003759 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003760
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003761 vdev_to_iface[adapter->sessionId] =
3762 ((struct hdd_ipa_iface_context *)
Yun Parka37592b2016-06-11 17:10:28 -07003763 (adapter->ipa_context))->iface_id;
3764
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303765 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003766 break;
3767
3768 case WLAN_STA_DISCONNECT:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303769 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003770 hdd_ipa_cleanup_iface(adapter->ipa_context);
3771
3772 if (!hdd_ipa->sta_connected) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303773 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003774 "%s: Evt: %d, STA already disconnected",
3775 msg_ex->name, meta.msg_type);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303776 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003777 return -EINVAL;
3778 }
Yun Parka37592b2016-06-11 17:10:28 -07003779
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003780 hdd_ipa->sta_connected = 0;
Yun Parka37592b2016-06-11 17:10:28 -07003781
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003782 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303783 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003784 "%s: IPA UC OFFLOAD NOT ENABLED",
3785 msg_ex->name);
3786 } else {
3787 /* Disable IPA UC TX PIPE when STA disconnected */
Yun Parka37592b2016-06-11 17:10:28 -07003788 if (!hdd_ipa->num_iface &&
3789 (HDD_IPA_UC_NUM_WDI_PIPE ==
3790 hdd_ipa->activated_fw_pipe))
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003791 hdd_ipa_uc_handle_last_discon(hdd_ipa);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003792 }
3793
Yun Park74127cf2016-09-18 11:22:41 -07003794 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) &&
3795 (hdd_ipa->sap_num_connected_sta > 0)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003796 hdd_ipa_uc_offload_enable_disable(adapter,
3797 SIR_STA_RX_DATA_OFFLOAD, 0);
3798 vdev_to_iface[adapter->sessionId] = HDD_IPA_MAX_IFACE;
3799 }
3800
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303801 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003802 break;
3803
3804 case WLAN_AP_DISCONNECT:
3805 if (!adapter->ipa_context) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303806 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003807 "%s: Evt: %d, SAP already disconnected",
3808 msg_ex->name, meta.msg_type);
3809 return -EINVAL;
3810 }
3811
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303812 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003813 hdd_ipa_cleanup_iface(adapter->ipa_context);
3814 if ((!hdd_ipa->num_iface) &&
3815 (HDD_IPA_UC_NUM_WDI_PIPE ==
3816 hdd_ipa->activated_fw_pipe)) {
Prashanth Bhatta9e143052015-12-04 11:56:47 -08003817 if (cds_is_driver_unloading()) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003818 /*
3819 * We disable WDI pipes directly here since
3820 * IPA_OPCODE_TX/RX_SUSPEND message will not be
3821 * processed when unloading WLAN driver is in
3822 * progress
3823 */
3824 hdd_ipa_uc_disable_pipes(hdd_ipa);
3825 } else {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303826 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003827 "NO INTF left but still pipe clean up");
3828 hdd_ipa_uc_handle_last_discon(hdd_ipa);
3829 }
3830 }
3831
3832 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3833 hdd_ipa_uc_offload_enable_disable(adapter,
3834 SIR_AP_RX_DATA_OFFLOAD, 0);
3835 vdev_to_iface[adapter->sessionId] = HDD_IPA_MAX_IFACE;
3836 }
Yun Parka37592b2016-06-11 17:10:28 -07003837
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303838 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003839 break;
3840
3841 case WLAN_CLIENT_CONNECT_EX:
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303842 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%d %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003843 adapter->dev->ifindex, sta_id);
3844
3845 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303846 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003847 "%s: Evt: %d, IPA UC OFFLOAD NOT ENABLED",
3848 adapter->dev->name, meta.msg_type);
3849 return 0;
3850 }
3851
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303852 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003853 if (hdd_ipa_uc_find_add_assoc_sta(hdd_ipa,
3854 true, sta_id)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303855 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003856 "%s: STA ID %d found, not valid",
3857 adapter->dev->name, sta_id);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303858 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003859 return 0;
3860 }
Yun Park312f71a2015-12-08 10:22:42 -08003861
3862 /* Enable IPA UC Data PIPEs when first STA connected */
Yun Parka37592b2016-06-11 17:10:28 -07003863 if (0 == hdd_ipa->sap_num_connected_sta) {
3864 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) &&
3865 hdd_ipa->sta_connected)
3866 hdd_ipa_uc_offload_enable_disable(
3867 hdd_get_adapter(hdd_ipa->hdd_ctx,
3868 QDF_STA_MODE),
3869 SIR_STA_RX_DATA_OFFLOAD, 1);
3870
Yun Park312f71a2015-12-08 10:22:42 -08003871 ret = hdd_ipa_uc_handle_first_con(hdd_ipa);
3872 if (ret) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303873 qdf_mutex_release(&hdd_ipa->event_lock);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303874 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Yun Park312f71a2015-12-08 10:22:42 -08003875 "%s: handle 1st con ret %d",
3876 adapter->dev->name, ret);
Yun Parka37592b2016-06-11 17:10:28 -07003877
3878 if (hdd_ipa_uc_sta_is_enabled(
3879 hdd_ipa->hdd_ctx) &&
3880 hdd_ipa->sta_connected)
3881 hdd_ipa_uc_offload_enable_disable(
3882 hdd_get_adapter(
3883 hdd_ipa->hdd_ctx,
3884 QDF_STA_MODE),
3885 SIR_STA_RX_DATA_OFFLOAD, 0);
3886
Yun Park312f71a2015-12-08 10:22:42 -08003887 return ret;
3888 }
3889 }
3890
3891 hdd_ipa->sap_num_connected_sta++;
Yun Park312f71a2015-12-08 10:22:42 -08003892
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303893 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003894
3895 meta.msg_type = type;
3896 meta.msg_len = (sizeof(struct ipa_wlan_msg_ex) +
3897 sizeof(struct ipa_wlan_hdr_attrib_val));
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303898 msg_ex = qdf_mem_malloc(meta.msg_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003899
3900 if (msg_ex == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303901 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003902 "msg_ex allocation failed");
3903 return -ENOMEM;
3904 }
3905 strlcpy(msg_ex->name, adapter->dev->name,
3906 IPA_RESOURCE_NAME_MAX);
3907 msg_ex->num_of_attribs = 1;
3908 msg_ex->attribs[0].attrib_type = WLAN_HDR_ATTRIB_MAC_ADDR;
3909 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
3910 msg_ex->attribs[0].offset =
3911 HDD_IPA_UC_WLAN_HDR_DES_MAC_OFFSET;
3912 } else {
3913 msg_ex->attribs[0].offset =
3914 HDD_IPA_WLAN_HDR_DES_MAC_OFFSET;
3915 }
3916 memcpy(msg_ex->attribs[0].u.mac_addr, mac_addr,
3917 IPA_MAC_ADDR_SIZE);
3918
3919 ret = ipa_send_msg(&meta, msg_ex, hdd_ipa_msg_free_fn);
3920
3921 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303922 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Evt: %d : %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003923 msg_ex->name, meta.msg_type, ret);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303924 qdf_mem_free(msg_ex);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003925 return ret;
3926 }
3927 hdd_ipa->stats.num_send_msg++;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003928 return ret;
3929
3930 case WLAN_CLIENT_DISCONNECT:
3931 if (!hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303932 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003933 "%s: IPA UC OFFLOAD NOT ENABLED",
3934 msg_ex->name);
3935 return 0;
3936 }
3937
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303938 qdf_mutex_acquire(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003939 if (!hdd_ipa_uc_find_add_assoc_sta(hdd_ipa, false, sta_id)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303940 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003941 "%s: STA ID %d NOT found, not valid",
3942 msg_ex->name, sta_id);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303943 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003944 return 0;
3945 }
3946 hdd_ipa->sap_num_connected_sta--;
3947 /* Disable IPA UC TX PIPE when last STA disconnected */
3948 if (!hdd_ipa->sap_num_connected_sta
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003949 && (false == hdd_ipa->resource_unloading)
3950 && (HDD_IPA_UC_NUM_WDI_PIPE ==
3951 hdd_ipa->activated_fw_pipe))
3952 hdd_ipa_uc_handle_last_discon(hdd_ipa);
Yun Parka37592b2016-06-11 17:10:28 -07003953
3954 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx) &&
3955 hdd_ipa->sta_connected)
3956 hdd_ipa_uc_offload_enable_disable(
3957 hdd_get_adapter(hdd_ipa->hdd_ctx,
3958 QDF_STA_MODE),
3959 SIR_STA_RX_DATA_OFFLOAD, 0);
3960
Anurag Chouhana37b5b72016-02-21 14:53:42 +05303961 qdf_mutex_release(&hdd_ipa->event_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003962 break;
3963
3964 default:
3965 return 0;
3966 }
3967
3968 meta.msg_len = sizeof(struct ipa_wlan_msg);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303969 msg = qdf_mem_malloc(meta.msg_len);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003970 if (msg == NULL) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303971 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, "msg allocation failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003972 return -ENOMEM;
3973 }
3974
3975 meta.msg_type = type;
3976 strlcpy(msg->name, adapter->dev->name, IPA_RESOURCE_NAME_MAX);
3977 memcpy(msg->mac_addr, mac_addr, ETH_ALEN);
3978
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303979 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Evt: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003980 msg->name, meta.msg_type);
3981
3982 ret = ipa_send_msg(&meta, msg, hdd_ipa_msg_free_fn);
3983
3984 if (ret) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05303985 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "%s: Evt: %d fail:%d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003986 msg->name, meta.msg_type, ret);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05303987 qdf_mem_free(msg);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003988 return ret;
3989 }
3990
3991 hdd_ipa->stats.num_send_msg++;
3992
3993end:
3994 return ret;
3995}
3996
3997/**
Mohit Khannafa99aea2016-05-12 21:43:13 -07003998 * hdd_ipa_wlan_evt() - IPA event handler
3999 * @adapter: adapter upon which the event was received
4000 * @sta_id: station id for the event
4001 * @hdd_event_type: event enum of type hdd_ipa_wlan_event
4002 * @mac_address: MAC address associated with the event
4003 *
4004 * This function is meant to be called from outside of wlan_hdd_ipa.c.
4005 *
4006 * Return: 0 on success, negative errno value on error
4007 */
4008int hdd_ipa_wlan_evt(hdd_adapter_t *adapter, uint8_t sta_id,
4009 enum hdd_ipa_wlan_event hdd_event_type, uint8_t *mac_addr)
4010{
4011 enum ipa_wlan_event type = hdd_to_ipa_wlan_event(hdd_event_type);
4012
4013 return __hdd_ipa_wlan_evt(adapter, sta_id, type, mac_addr);
4014}
4015
4016/**
4017 * hdd_ipa_uc_proc_pending_event() - Process IPA uC pending events
4018 * @hdd_ipa: Global HDD IPA context
4019 *
4020 * Return: None
4021 */
4022static void
4023hdd_ipa_uc_proc_pending_event(struct hdd_ipa_priv *hdd_ipa)
4024{
4025 unsigned int pending_event_count;
4026 struct ipa_uc_pending_event *pending_event = NULL;
4027
4028 pending_event_count = qdf_list_size(&hdd_ipa->pending_event);
4029 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
4030 "%s, Pending Event Count %d", __func__, pending_event_count);
4031 if (!pending_event_count) {
4032 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
4033 "%s, No Pending Event", __func__);
4034 return;
4035 }
4036
4037 qdf_list_remove_front(&hdd_ipa->pending_event,
4038 (qdf_list_node_t **)&pending_event);
4039 while (pending_event != NULL) {
4040 __hdd_ipa_wlan_evt(pending_event->adapter,
4041 pending_event->type,
4042 pending_event->sta_id,
4043 pending_event->mac_addr);
4044 qdf_mem_free(pending_event);
4045 pending_event = NULL;
4046 qdf_list_remove_front(&hdd_ipa->pending_event,
4047 (qdf_list_node_t **)&pending_event);
4048 }
4049}
4050
4051/**
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004052 * hdd_ipa_rm_state_to_str() - Convert IPA RM state to string
4053 * @state: IPA RM state value
4054 *
4055 * Return: ASCII string representing the IPA RM state
4056 */
4057static inline char *hdd_ipa_rm_state_to_str(enum hdd_ipa_rm_state state)
4058{
4059 switch (state) {
4060 case HDD_IPA_RM_RELEASED:
4061 return "RELEASED";
4062 case HDD_IPA_RM_GRANT_PENDING:
4063 return "GRANT_PENDING";
4064 case HDD_IPA_RM_GRANTED:
4065 return "GRANTED";
4066 }
4067
4068 return "UNKNOWN";
4069}
4070
4071/**
4072 * hdd_ipa_init() - IPA initialization function
4073 * @hdd_ctx: HDD global context
4074 *
4075 * Allocate hdd_ipa resources, ipa pipe resource and register
4076 * wlan interface with IPA module.
4077 *
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304078 * Return: QDF_STATUS enumeration
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004079 */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304080QDF_STATUS hdd_ipa_init(hdd_context_t *hdd_ctx)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004081{
4082 struct hdd_ipa_priv *hdd_ipa = NULL;
4083 int ret, i;
4084 struct hdd_ipa_iface_context *iface_context = NULL;
Yun Park7f171ab2016-07-29 15:44:22 -07004085 struct ol_txrx_pdev_t *pdev = cds_get_context(QDF_MODULE_ID_TXRX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004086
4087 if (!hdd_ipa_is_enabled(hdd_ctx))
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304088 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004089
Yun Park7f171ab2016-07-29 15:44:22 -07004090 if (!pdev) {
4091 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL, "pdev is NULL");
4092 goto fail_return;
4093 }
4094
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304095 hdd_ipa = qdf_mem_malloc(sizeof(*hdd_ipa));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004096 if (!hdd_ipa) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304097 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL, "hdd_ipa allocation failed");
Leo Chang3bc8fed2015-11-13 10:59:47 -08004098 goto fail_return;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004099 }
4100
4101 hdd_ctx->hdd_ipa = hdd_ipa;
4102 ghdd_ipa = hdd_ipa;
4103 hdd_ipa->hdd_ctx = hdd_ctx;
4104 hdd_ipa->num_iface = 0;
Yun Park7f171ab2016-07-29 15:44:22 -07004105 ol_txrx_ipa_uc_get_resource(pdev, &hdd_ipa->ipa_resource);
Dhanashri Atreb08959a2016-03-01 17:28:03 -08004106 if ((0 == hdd_ipa->ipa_resource.ce_sr_base_paddr) ||
4107 (0 == hdd_ipa->ipa_resource.tx_comp_ring_base_paddr) ||
4108 (0 == hdd_ipa->ipa_resource.rx_rdy_ring_base_paddr) ||
4109 (0 == hdd_ipa->ipa_resource.rx2_rdy_ring_base_paddr)) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304110 HDD_IPA_LOG(QDF_TRACE_LEVEL_FATAL,
Leo Chang3bc8fed2015-11-13 10:59:47 -08004111 "IPA UC resource alloc fail");
4112 goto fail_get_resource;
4113 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004114
4115 /* Create the interface context */
4116 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
4117 iface_context = &hdd_ipa->iface_context[i];
4118 iface_context->hdd_ipa = hdd_ipa;
4119 iface_context->cons_client =
4120 hdd_ipa_adapter_2_client[i].cons_client;
4121 iface_context->prod_client =
4122 hdd_ipa_adapter_2_client[i].prod_client;
4123 iface_context->iface_id = i;
4124 iface_context->adapter = NULL;
Yun Park8292dcb2016-10-07 16:46:06 -07004125 iface_context->offload_enabled = 0;
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304126 qdf_spinlock_create(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004127 }
4128
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004129 INIT_WORK(&hdd_ipa->pm_work, hdd_ipa_pm_send_pkt_to_tl);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304130 qdf_spinlock_create(&hdd_ipa->pm_lock);
Nirav Shahcbc6d722016-03-01 16:24:53 +05304131 qdf_nbuf_queue_init(&hdd_ipa->pm_queue_head);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004132
4133 ret = hdd_ipa_setup_rm(hdd_ipa);
4134 if (ret)
4135 goto fail_setup_rm;
4136
4137 if (hdd_ipa_uc_is_enabled(hdd_ipa->hdd_ctx)) {
4138 hdd_ipa_uc_rt_debug_init(hdd_ctx);
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304139 qdf_mem_zero(&hdd_ipa->stats, sizeof(hdd_ipa->stats));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004140 hdd_ipa->sap_num_connected_sta = 0;
4141 hdd_ipa->ipa_tx_packets_diff = 0;
4142 hdd_ipa->ipa_rx_packets_diff = 0;
4143 hdd_ipa->ipa_p_tx_packets = 0;
4144 hdd_ipa->ipa_p_rx_packets = 0;
4145 hdd_ipa->resource_loading = false;
4146 hdd_ipa->resource_unloading = false;
4147 hdd_ipa->sta_connected = 0;
Leo Change3e49442015-10-26 20:07:13 -07004148 hdd_ipa->ipa_pipes_down = true;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004149 /* Setup IPA sys_pipe for MCC */
4150 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx)) {
4151 ret = hdd_ipa_setup_sys_pipe(hdd_ipa);
4152 if (ret)
4153 goto fail_create_sys_pipe;
4154 }
4155 hdd_ipa_uc_ol_init(hdd_ctx);
4156 } else {
4157 ret = hdd_ipa_setup_sys_pipe(hdd_ipa);
4158 if (ret)
4159 goto fail_create_sys_pipe;
4160 }
4161
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304162 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004163
4164fail_create_sys_pipe:
4165 hdd_ipa_destroy_rm_resource(hdd_ipa);
4166fail_setup_rm:
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304167 qdf_spinlock_destroy(&hdd_ipa->pm_lock);
Leo Chang3bc8fed2015-11-13 10:59:47 -08004168fail_get_resource:
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304169 qdf_mem_free(hdd_ipa);
Leo Chang3bc8fed2015-11-13 10:59:47 -08004170 hdd_ctx->hdd_ipa = NULL;
4171 ghdd_ipa = NULL;
4172fail_return:
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304173 return QDF_STATUS_E_FAILURE;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004174}
4175
4176/**
Yun Parkf19e07d2015-11-20 11:34:27 -08004177 * hdd_ipa_cleanup_pending_event() - Cleanup IPA pending event list
4178 * @hdd_ipa: pointer to HDD IPA struct
4179 *
4180 * Return: none
4181 */
Jeff Johnsond7720632016-10-05 16:04:32 -07004182static void hdd_ipa_cleanup_pending_event(struct hdd_ipa_priv *hdd_ipa)
Yun Parkf19e07d2015-11-20 11:34:27 -08004183{
4184 struct ipa_uc_pending_event *pending_event = NULL;
4185
Anurag Chouhanffb21542016-02-17 14:33:03 +05304186 while (qdf_list_remove_front(&hdd_ipa->pending_event,
4187 (qdf_list_node_t **)&pending_event) == QDF_STATUS_SUCCESS) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304188 qdf_mem_free(pending_event);
Yun Parkf19e07d2015-11-20 11:34:27 -08004189 }
4190
Anurag Chouhanffb21542016-02-17 14:33:03 +05304191 qdf_list_destroy(&hdd_ipa->pending_event);
Yun Parkf19e07d2015-11-20 11:34:27 -08004192}
4193
4194/**
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004195 * hdd_ipa_cleanup - IPA cleanup function
4196 * @hdd_ctx: HDD global context
4197 *
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304198 * Return: QDF_STATUS enumeration
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004199 */
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304200QDF_STATUS hdd_ipa_cleanup(hdd_context_t *hdd_ctx)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004201{
4202 struct hdd_ipa_priv *hdd_ipa = hdd_ctx->hdd_ipa;
4203 int i;
4204 struct hdd_ipa_iface_context *iface_context = NULL;
Nirav Shahcbc6d722016-03-01 16:24:53 +05304205 qdf_nbuf_t skb;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004206 struct hdd_ipa_pm_tx_cb *pm_tx_cb = NULL;
4207
4208 if (!hdd_ipa_is_enabled(hdd_ctx))
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304209 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004210
4211 if (!hdd_ipa_uc_is_enabled(hdd_ctx)) {
4212 unregister_inetaddr_notifier(&hdd_ipa->ipv4_notifier);
4213 hdd_ipa_teardown_sys_pipe(hdd_ipa);
4214 }
4215
4216 /* Teardown IPA sys_pipe for MCC */
4217 if (hdd_ipa_uc_sta_is_enabled(hdd_ipa->hdd_ctx))
4218 hdd_ipa_teardown_sys_pipe(hdd_ipa);
4219
4220 hdd_ipa_destroy_rm_resource(hdd_ipa);
4221
4222#ifdef WLAN_OPEN_SOURCE
4223 cancel_work_sync(&hdd_ipa->pm_work);
4224#endif
4225
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304226 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004227
Nirav Shahcbc6d722016-03-01 16:24:53 +05304228 while (((skb = qdf_nbuf_queue_remove(&hdd_ipa->pm_queue_head))
4229 != NULL)) {
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304230 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004231
4232 pm_tx_cb = (struct hdd_ipa_pm_tx_cb *)skb->cb;
4233 ipa_free_skb(pm_tx_cb->ipa_tx_desc);
4234
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304235 qdf_spin_lock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004236 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304237 qdf_spin_unlock_bh(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004238
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304239 qdf_spinlock_destroy(&hdd_ipa->pm_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004240
4241 /* destory the interface lock */
4242 for (i = 0; i < HDD_IPA_MAX_IFACE; i++) {
4243 iface_context = &hdd_ipa->iface_context[i];
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304244 qdf_spinlock_destroy(&iface_context->interface_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004245 }
4246
4247 /* This should never hit but still make sure that there are no pending
4248 * descriptor in IPA hardware
4249 */
4250 if (hdd_ipa->pending_hw_desc_cnt != 0) {
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304251 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004252 "IPA Pending write done: %d Waiting!",
4253 hdd_ipa->pending_hw_desc_cnt);
4254
4255 for (i = 0; hdd_ipa->pending_hw_desc_cnt != 0 && i < 10; i++) {
4256 usleep_range(100, 100);
4257 }
4258
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304259 HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004260 "IPA Pending write done: desc: %d %s(%d)!",
4261 hdd_ipa->pending_hw_desc_cnt,
4262 hdd_ipa->pending_hw_desc_cnt == 0 ? "completed"
4263 : "leak", i);
4264 }
4265 if (hdd_ipa_uc_is_enabled(hdd_ctx)) {
4266 hdd_ipa_uc_rt_debug_deinit(hdd_ctx);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304267 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Govind Singh0487bf22016-08-24 23:08:57 +05304268 "%s: Disconnect TX PIPE tx_pipe_handle=0x%x",
4269 __func__, hdd_ipa->tx_pipe_handle);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004270 ipa_disconnect_wdi_pipe(hdd_ipa->tx_pipe_handle);
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +05304271 HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO,
Govind Singh0487bf22016-08-24 23:08:57 +05304272 "%s: Disconnect RX PIPE rx_pipe_handle=0x%x",
4273 __func__, hdd_ipa->rx_pipe_handle);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004274 ipa_disconnect_wdi_pipe(hdd_ipa->rx_pipe_handle);
Anurag Chouhana37b5b72016-02-21 14:53:42 +05304275 qdf_mutex_destroy(&hdd_ipa->event_lock);
4276 qdf_mutex_destroy(&hdd_ipa->ipa_lock);
Yun Parkf19e07d2015-11-20 11:34:27 -08004277 hdd_ipa_cleanup_pending_event(hdd_ipa);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004278
4279#ifdef WLAN_OPEN_SOURCE
4280 for (i = 0; i < HDD_IPA_UC_OPCODE_MAX; i++) {
4281 cancel_work_sync(&hdd_ipa->uc_op_work[i].work);
4282 hdd_ipa->uc_op_work[i].msg = NULL;
4283 }
4284#endif
4285 }
4286
Anurag Chouhan600c3a02016-03-01 10:33:54 +05304287 qdf_mem_free(hdd_ipa);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004288 hdd_ctx->hdd_ipa = NULL;
4289
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05304290 return QDF_STATUS_SUCCESS;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08004291}
4292#endif /* IPA_OFFLOAD */