blob: eca4378eddda3249e3ecd63e81a1a639fa8003f2 [file] [log] [blame]
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001/*
2 * Marvell Wireless LAN device driver: major functions
3 *
Xinming Hu65da33f2014-06-19 21:38:57 -07004 * Copyright (C) 2011-2014, Marvell International Ltd.
Bing Zhao5e6e3a92011-03-21 18:00:50 -07005 *
6 * This software file (the "File") is distributed by Marvell International
7 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
8 * (the "License"). You may use, redistribute and/or modify this File in
9 * accordance with the terms and conditions of the License, a copy of which
10 * is available by writing to the Free Software Foundation, Inc.,
11 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
12 * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
13 *
14 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
16 * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
17 * this warranty disclaimer.
18 */
19
20#include "main.h"
21#include "wmm.h"
22#include "cfg80211.h"
23#include "11n.h"
24
25#define VERSION "1.0"
26
Zhaoyang Liuc687a002015-05-12 00:48:18 +053027static unsigned int debug_mask = MWIFIEX_DEFAULT_DEBUG_MASK;
28module_param(debug_mask, uint, 0);
29MODULE_PARM_DESC(debug_mask, "bitmap for debug flags");
30
Bing Zhao5e6e3a92011-03-21 18:00:50 -070031const char driver_version[] = "mwifiex " VERSION " (%s) ";
Amitkumar Karwar388ec382013-05-17 17:50:25 -070032static char *cal_data_cfg;
33module_param(cal_data_cfg, charp, 0);
Bing Zhao5e6e3a92011-03-21 18:00:50 -070034
Avinash Patil0013c7c2014-11-05 19:38:11 +053035static unsigned short driver_mode;
36module_param(driver_mode, ushort, 0);
37MODULE_PARM_DESC(driver_mode,
38 "station=0x1(default), ap-sta=0x3, station-p2p=0x5, ap-sta-p2p=0x7");
39
Bing Zhao5e6e3a92011-03-21 18:00:50 -070040/*
41 * This function registers the device and performs all the necessary
42 * initializations.
43 *
44 * The following initialization operations are performed -
45 * - Allocate adapter structure
46 * - Save interface specific operations table in adapter
47 * - Call interface specific initialization routine
48 * - Allocate private structures
49 * - Set default adapter structure parameters
50 * - Initialize locks
51 *
52 * In case of any errors during inittialization, this function also ensures
53 * proper cleanup before exiting.
54 */
55static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops,
Amitkumar Karwar287546d2011-06-08 20:39:20 +053056 void **padapter)
Bing Zhao5e6e3a92011-03-21 18:00:50 -070057{
Amitkumar Karwar2be78592011-04-15 20:50:42 -070058 struct mwifiex_adapter *adapter;
59 int i;
Bing Zhao5e6e3a92011-03-21 18:00:50 -070060
61 adapter = kzalloc(sizeof(struct mwifiex_adapter), GFP_KERNEL);
Bing Zhao5e6e3a92011-03-21 18:00:50 -070062 if (!adapter)
Christoph Fritzb53575e2011-05-08 22:50:09 +020063 return -ENOMEM;
Bing Zhao5e6e3a92011-03-21 18:00:50 -070064
Amitkumar Karwar287546d2011-06-08 20:39:20 +053065 *padapter = adapter;
Bing Zhao5e6e3a92011-03-21 18:00:50 -070066 adapter->card = card;
67
68 /* Save interface specific operations in adapter */
69 memmove(&adapter->if_ops, if_ops, sizeof(struct mwifiex_if_ops));
Zhaoyang Liuc687a002015-05-12 00:48:18 +053070 adapter->debug_mask = debug_mask;
Bing Zhao5e6e3a92011-03-21 18:00:50 -070071
72 /* card specific initialization has been deferred until now .. */
Amitkumar Karwar4daffe32012-04-18 20:08:28 -070073 if (adapter->if_ops.init_if)
74 if (adapter->if_ops.init_if(adapter))
75 goto error;
Bing Zhao5e6e3a92011-03-21 18:00:50 -070076
77 adapter->priv_num = 0;
Bing Zhao5e6e3a92011-03-21 18:00:50 -070078
Avinash Patil64b05e22012-05-08 18:30:13 -070079 for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) {
80 /* Allocate memory for private structure */
81 adapter->priv[i] =
82 kzalloc(sizeof(struct mwifiex_private), GFP_KERNEL);
83 if (!adapter->priv[i])
84 goto error;
85
86 adapter->priv[i]->adapter = adapter;
Avinash Patil64b05e22012-05-08 18:30:13 -070087 adapter->priv_num++;
Bing Zhao5e6e3a92011-03-21 18:00:50 -070088 }
Amitkumar Karwar44b815c2011-09-29 20:43:41 -070089 mwifiex_init_lock_list(adapter);
Bing Zhao5e6e3a92011-03-21 18:00:50 -070090
Julia Lawallc6c33e72014-12-26 15:35:56 +010091 setup_timer(&adapter->cmd_timer, mwifiex_cmd_timeout_func,
92 (unsigned long)adapter);
Bing Zhao5e6e3a92011-03-21 18:00:50 -070093
Bing Zhao5e6e3a92011-03-21 18:00:50 -070094 return 0;
95
96error:
97 dev_dbg(adapter->dev, "info: leave mwifiex_register with error\n");
98
Yogesh Ashok Powar93a1df42011-09-26 20:37:26 -070099 for (i = 0; i < adapter->priv_num; i++)
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700100 kfree(adapter->priv[i]);
Yogesh Ashok Powar93a1df42011-09-26 20:37:26 -0700101
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700102 kfree(adapter);
103
104 return -1;
105}
106
107/*
108 * This function unregisters the device and performs all the necessary
109 * cleanups.
110 *
111 * The following cleanup operations are performed -
112 * - Free the timers
113 * - Free beacon buffers
114 * - Free private structures
115 * - Free adapter structure
116 */
117static int mwifiex_unregister(struct mwifiex_adapter *adapter)
118{
Yogesh Ashok Powar270e58e2011-05-03 20:11:46 -0700119 s32 i;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700120
Amitkumar Karwar4cfda672013-07-22 19:17:50 -0700121 if (adapter->if_ops.cleanup_if)
122 adapter->if_ops.cleanup_if(adapter);
123
Avinash Patil629873f2014-02-18 15:47:55 -0800124 del_timer_sync(&adapter->cmd_timer);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700125
126 /* Free private structures */
127 for (i = 0; i < adapter->priv_num; i++) {
128 if (adapter->priv[i]) {
129 mwifiex_free_curr_bcn(adapter->priv[i]);
130 kfree(adapter->priv[i]);
131 }
132 }
133
Avinash Patilbf354432014-10-31 16:08:26 +0530134 vfree(adapter->chan_stats);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700135 kfree(adapter);
136 return 0;
137}
138
Shengzhen Lib2713f62015-03-13 17:37:54 +0530139void mwifiex_queue_main_work(struct mwifiex_adapter *adapter)
140{
141 unsigned long flags;
142
143 spin_lock_irqsave(&adapter->main_proc_lock, flags);
144 if (adapter->mwifiex_processing) {
145 adapter->more_task_flag = true;
146 spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
147 } else {
148 spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
149 queue_work(adapter->workqueue, &adapter->main_work);
150 }
151}
152EXPORT_SYMBOL_GPL(mwifiex_queue_main_work);
153
154static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter)
155{
156 unsigned long flags;
157
158 spin_lock_irqsave(&adapter->rx_proc_lock, flags);
159 if (adapter->rx_processing) {
160 spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
161 } else {
162 spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
163 queue_work(adapter->rx_workqueue, &adapter->rx_work);
164 }
165}
166
Avinash Patil6e251172014-09-12 20:08:59 +0530167static int mwifiex_process_rx(struct mwifiex_adapter *adapter)
168{
169 unsigned long flags;
170 struct sk_buff *skb;
Zhaoyang Liu92263a82015-03-13 17:37:58 +0530171 struct mwifiex_rxinfo *rx_info;
Avinash Patil6e251172014-09-12 20:08:59 +0530172
173 spin_lock_irqsave(&adapter->rx_proc_lock, flags);
174 if (adapter->rx_processing || adapter->rx_locked) {
175 spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
176 goto exit_rx_proc;
177 } else {
178 adapter->rx_processing = true;
179 spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
180 }
181
182 /* Check for Rx data */
183 while ((skb = skb_dequeue(&adapter->rx_data_q))) {
184 atomic_dec(&adapter->rx_pending);
Amitkumar Karwar381e9ff2014-11-25 06:43:04 -0800185 if ((adapter->delay_main_work ||
186 adapter->iface_type == MWIFIEX_USB) &&
Avinash Patilb43a0d92014-09-29 21:44:14 +0530187 (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) {
Amitkumar Karwarcf6a64f2014-11-05 17:04:29 +0530188 if (adapter->if_ops.submit_rem_rx_urbs)
189 adapter->if_ops.submit_rem_rx_urbs(adapter);
Avinash Patil6e251172014-09-12 20:08:59 +0530190 adapter->delay_main_work = false;
Shengzhen Lib2713f62015-03-13 17:37:54 +0530191 mwifiex_queue_main_work(adapter);
Avinash Patil6e251172014-09-12 20:08:59 +0530192 }
Zhaoyang Liu92263a82015-03-13 17:37:58 +0530193 rx_info = MWIFIEX_SKB_RXCB(skb);
194 if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) {
195 if (adapter->if_ops.deaggr_pkt)
196 adapter->if_ops.deaggr_pkt(adapter, skb);
197 dev_kfree_skb_any(skb);
198 } else {
199 mwifiex_handle_rx_packet(adapter, skb);
200 }
Avinash Patil6e251172014-09-12 20:08:59 +0530201 }
202 spin_lock_irqsave(&adapter->rx_proc_lock, flags);
203 adapter->rx_processing = false;
204 spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
205
Avinash Patil6e251172014-09-12 20:08:59 +0530206exit_rx_proc:
207 return 0;
208}
209
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700210/*
211 * The main process.
212 *
213 * This function is the main procedure of the driver and handles various driver
214 * operations. It runs in a loop and provides the core functionalities.
215 *
216 * The main responsibilities of this function are -
217 * - Ensure concurrency control
218 * - Handle pending interrupts and call interrupt handlers
219 * - Wake up the card if required
220 * - Handle command responses and call response handlers
221 * - Handle events and call event handlers
222 * - Execute pending commands
223 * - Transmit pending data packets
224 */
225int mwifiex_main_process(struct mwifiex_adapter *adapter)
226{
227 int ret = 0;
228 unsigned long flags;
229
230 spin_lock_irqsave(&adapter->main_proc_lock, flags);
231
232 /* Check if already processing */
Avinash Patila9adbcb2015-03-13 17:37:51 +0530233 if (adapter->mwifiex_processing || adapter->main_locked) {
Shengzhen Li04c7b362015-02-11 23:12:24 +0530234 adapter->more_task_flag = true;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700235 spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
236 goto exit_main_proc;
237 } else {
238 adapter->mwifiex_processing = true;
Cathy Luo91457ea2015-04-17 04:18:29 -0700239 spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700240 }
241process_start:
242 do {
243 if ((adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) ||
244 (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY))
245 break;
246
Amitkumar Karwar381e9ff2014-11-25 06:43:04 -0800247 /* For non-USB interfaces, If we process interrupts first, it
248 * would increase RX pending even further. Avoid this by
249 * checking if rx_pending has crossed high threshold and
250 * schedule rx work queue and then process interrupts.
251 * For USB interface, there are no interrupts. We already have
252 * HIGH_RX_PENDING check in usb.c
Avinash Patil6e251172014-09-12 20:08:59 +0530253 */
Amitkumar Karwar381e9ff2014-11-25 06:43:04 -0800254 if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING &&
255 adapter->iface_type != MWIFIEX_USB) {
Avinash Patil6e251172014-09-12 20:08:59 +0530256 adapter->delay_main_work = true;
Shengzhen Lib2713f62015-03-13 17:37:54 +0530257 mwifiex_queue_rx_work(adapter);
Avinash Patil6e251172014-09-12 20:08:59 +0530258 break;
259 }
260
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700261 /* Handle pending interrupt if any */
262 if (adapter->int_status) {
263 if (adapter->hs_activated)
264 mwifiex_process_hs_config(adapter);
Amitkumar Karwar4daffe32012-04-18 20:08:28 -0700265 if (adapter->if_ops.process_int_status)
266 adapter->if_ops.process_int_status(adapter);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700267 }
268
Avinash Patil6e251172014-09-12 20:08:59 +0530269 if (adapter->rx_work_enabled && adapter->data_received)
Shengzhen Lib2713f62015-03-13 17:37:54 +0530270 mwifiex_queue_rx_work(adapter);
Avinash Patil6e251172014-09-12 20:08:59 +0530271
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700272 /* Need to wake up the card ? */
273 if ((adapter->ps_state == PS_STATE_SLEEP) &&
274 (adapter->pm_wakeup_card_req &&
275 !adapter->pm_wakeup_fw_try) &&
Yogesh Ashok Powarf57c1ed2012-03-13 19:22:37 -0700276 (is_command_pending(adapter) ||
Zhaoyang Liue35000e2015-03-13 17:37:57 +0530277 !skb_queue_empty(&adapter->tx_data_q) ||
Yogesh Ashok Powarf57c1ed2012-03-13 19:22:37 -0700278 !mwifiex_wmm_lists_empty(adapter))) {
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700279 adapter->pm_wakeup_fw_try = true;
Amitkumar Karwar46361872014-12-31 02:36:41 -0800280 mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3));
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700281 adapter->if_ops.wakeup(adapter);
282 continue;
283 }
Amitkumar Karwar4daffe32012-04-18 20:08:28 -0700284
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700285 if (IS_CARD_RX_RCVD(adapter)) {
Avinash Patil6e251172014-09-12 20:08:59 +0530286 adapter->data_received = false;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700287 adapter->pm_wakeup_fw_try = false;
Amitkumar Karwar6e9344f2015-03-12 00:38:40 -0700288 del_timer(&adapter->wakeup_timer);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700289 if (adapter->ps_state == PS_STATE_SLEEP)
290 adapter->ps_state = PS_STATE_AWAKE;
291 } else {
292 /* We have tried to wakeup the card already */
293 if (adapter->pm_wakeup_fw_try)
294 break;
295 if (adapter->ps_state != PS_STATE_AWAKE ||
296 adapter->tx_lock_flag)
297 break;
298
Avinash Patil5ec39ef2014-09-12 20:08:56 +0530299 if ((!adapter->scan_chan_gap_enabled &&
Avinash Patil5ec39ef2014-09-12 20:08:56 +0530300 adapter->scan_processing) || adapter->data_sent ||
Zhaoyang Liue35000e2015-03-13 17:37:57 +0530301 (mwifiex_wmm_lists_empty(adapter) &&
302 skb_queue_empty(&adapter->tx_data_q))) {
Yogesh Ashok Powarf57c1ed2012-03-13 19:22:37 -0700303 if (adapter->cmd_sent || adapter->curr_cmd ||
304 (!is_command_pending(adapter)))
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700305 break;
306 }
307 }
308
Amitkumar Karwar20474122014-04-14 15:31:05 -0700309 /* Check for event */
310 if (adapter->event_received) {
311 adapter->event_received = false;
312 mwifiex_process_event(adapter);
313 }
314
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700315 /* Check for Cmd Resp */
316 if (adapter->cmd_resp_received) {
317 adapter->cmd_resp_received = false;
318 mwifiex_process_cmdresp(adapter);
319
320 /* call mwifiex back when init_fw is done */
321 if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) {
322 adapter->hw_status = MWIFIEX_HW_STATUS_READY;
323 mwifiex_init_fw_complete(adapter);
324 }
325 }
326
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700327 /* Check if we need to confirm Sleep Request
328 received previously */
329 if (adapter->ps_state == PS_STATE_PRE_SLEEP) {
330 if (!adapter->cmd_sent && !adapter->curr_cmd)
331 mwifiex_check_ps_cond(adapter);
332 }
333
334 /* * The ps_state may have been changed during processing of
335 * Sleep Request event.
336 */
Yogesh Ashok Powarf57c1ed2012-03-13 19:22:37 -0700337 if ((adapter->ps_state == PS_STATE_SLEEP) ||
338 (adapter->ps_state == PS_STATE_PRE_SLEEP) ||
339 (adapter->ps_state == PS_STATE_SLEEP_CFM) ||
Shengzhen Li04c7b362015-02-11 23:12:24 +0530340 adapter->tx_lock_flag){
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700341 continue;
Shengzhen Li04c7b362015-02-11 23:12:24 +0530342 }
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700343
344 if (!adapter->cmd_sent && !adapter->curr_cmd) {
345 if (mwifiex_exec_next_cmd(adapter) == -1) {
346 ret = -1;
347 break;
348 }
349 }
350
Avinash Patil5ec39ef2014-09-12 20:08:56 +0530351 if ((adapter->scan_chan_gap_enabled ||
Amitkumar Karward8d91252014-09-12 20:08:58 +0530352 !adapter->scan_processing) &&
Zhaoyang Liue35000e2015-03-13 17:37:57 +0530353 !adapter->data_sent &&
354 !skb_queue_empty(&adapter->tx_data_q)) {
355 mwifiex_process_tx_queue(adapter);
356 if (adapter->hs_activated) {
357 adapter->is_hs_configured = false;
358 mwifiex_hs_activated_event
359 (mwifiex_get_priv
360 (adapter, MWIFIEX_BSS_ROLE_ANY),
361 false);
362 }
363 }
364
365 if ((adapter->scan_chan_gap_enabled ||
366 !adapter->scan_processing) &&
Amitkumar Karwar3249ba72012-06-06 21:12:41 -0700367 !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter)) {
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700368 mwifiex_wmm_process_tx(adapter);
369 if (adapter->hs_activated) {
370 adapter->is_hs_configured = false;
371 mwifiex_hs_activated_event
372 (mwifiex_get_priv
373 (adapter, MWIFIEX_BSS_ROLE_ANY),
374 false);
375 }
376 }
377
378 if (adapter->delay_null_pkt && !adapter->cmd_sent &&
Yogesh Ashok Powarf57c1ed2012-03-13 19:22:37 -0700379 !adapter->curr_cmd && !is_command_pending(adapter) &&
Zhaoyang Liue35000e2015-03-13 17:37:57 +0530380 (mwifiex_wmm_lists_empty(adapter) &&
381 skb_queue_empty(&adapter->tx_data_q))) {
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700382 if (!mwifiex_send_null_packet
383 (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
384 MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET |
385 MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) {
386 adapter->delay_null_pkt = false;
387 adapter->ps_state = PS_STATE_SLEEP;
388 }
389 break;
390 }
391 } while (true);
392
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700393 spin_lock_irqsave(&adapter->main_proc_lock, flags);
Cathy Luo91457ea2015-04-17 04:18:29 -0700394 if (adapter->more_task_flag) {
395 adapter->more_task_flag = false;
396 spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
Amitkumar Karwar453b0c32013-09-27 10:55:38 -0700397 goto process_start;
Cathy Luo91457ea2015-04-17 04:18:29 -0700398 }
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700399 adapter->mwifiex_processing = false;
400 spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
401
402exit_main_proc:
403 if (adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING)
404 mwifiex_shutdown_drv(adapter);
405 return ret;
406}
Bing Zhao601216e2012-11-05 16:59:15 -0800407EXPORT_SYMBOL_GPL(mwifiex_main_process);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700408
409/*
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700410 * This function frees the adapter structure.
411 *
412 * Additionally, this closes the netlink socket, frees the timers
413 * and private structures.
414 */
415static void mwifiex_free_adapter(struct mwifiex_adapter *adapter)
416{
417 if (!adapter) {
418 pr_err("%s: adapter is NULL\n", __func__);
419 return;
420 }
421
422 mwifiex_unregister(adapter);
423 pr_debug("info: %s: free adapter\n", __func__);
424}
425
426/*
Amitkumar Karwar6b41f942013-07-22 19:17:55 -0700427 * This function cancels all works in the queue and destroys
428 * the main workqueue.
429 */
430static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
431{
432 flush_workqueue(adapter->workqueue);
433 destroy_workqueue(adapter->workqueue);
434 adapter->workqueue = NULL;
Avinash Patil6e251172014-09-12 20:08:59 +0530435
436 if (adapter->rx_workqueue) {
437 flush_workqueue(adapter->rx_workqueue);
438 destroy_workqueue(adapter->rx_workqueue);
439 adapter->rx_workqueue = NULL;
440 }
Amitkumar Karwar6b41f942013-07-22 19:17:55 -0700441}
442
443/*
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700444 * This function gets firmware and initializes it.
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700445 *
446 * The main initialization steps followed are -
447 * - Download the correct firmware to card
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700448 * - Issue the init commands to firmware
449 */
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700450static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700451{
Amitkumar Karwarbec568f2013-11-14 19:10:38 -0800452 int ret;
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700453 char fmt[64];
454 struct mwifiex_private *priv;
455 struct mwifiex_adapter *adapter = context;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700456 struct mwifiex_fw_image fw;
Amitkumar Karwarc3afd992013-07-30 17:18:15 -0700457 struct semaphore *sem = adapter->card_sem;
458 bool init_failed = false;
Amitkumar Karwar68b76e92013-11-14 19:10:37 -0800459 struct wireless_dev *wdev;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700460
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700461 if (!firmware) {
462 dev_err(adapter->dev,
463 "Failed to get firmware %s\n", adapter->fw_name);
Amitkumar Karwar6b41f942013-07-22 19:17:55 -0700464 goto err_dnld_fw;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700465 }
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700466
467 memset(&fw, 0, sizeof(struct mwifiex_fw_image));
468 adapter->firmware = firmware;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700469 fw.fw_buf = (u8 *) adapter->firmware->data;
470 fw.fw_len = adapter->firmware->size;
471
Amitkumar Karwar4daffe32012-04-18 20:08:28 -0700472 if (adapter->if_ops.dnld_fw)
473 ret = adapter->if_ops.dnld_fw(adapter, &fw);
474 else
475 ret = mwifiex_dnld_fw(adapter, &fw);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700476 if (ret == -1)
Amitkumar Karwar6b41f942013-07-22 19:17:55 -0700477 goto err_dnld_fw;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700478
479 dev_notice(adapter->dev, "WLAN FW is active\n");
480
Amitkumar Karwar388ec382013-05-17 17:50:25 -0700481 if (cal_data_cfg) {
482 if ((request_firmware(&adapter->cal_data, cal_data_cfg,
483 adapter->dev)) < 0)
484 dev_err(adapter->dev,
485 "Cal data request_firmware() failed\n");
486 }
487
Daniel Drake232fde02013-07-13 10:57:10 -0400488 /* enable host interrupt after fw dnld is successful */
Amitkumar Karwar6b41f942013-07-22 19:17:55 -0700489 if (adapter->if_ops.enable_int) {
490 if (adapter->if_ops.enable_int(adapter))
491 goto err_dnld_fw;
492 }
Daniel Drake232fde02013-07-13 10:57:10 -0400493
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700494 adapter->init_wait_q_woken = false;
495 ret = mwifiex_init_fw(adapter);
496 if (ret == -1) {
Amitkumar Karwar6b41f942013-07-22 19:17:55 -0700497 goto err_init_fw;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700498 } else if (!ret) {
499 adapter->hw_status = MWIFIEX_HW_STATUS_READY;
500 goto done;
501 }
502 /* Wait for mwifiex_init to complete */
503 wait_event_interruptible(adapter->init_wait_q,
504 adapter->init_wait_q_woken);
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700505 if (adapter->hw_status != MWIFIEX_HW_STATUS_READY)
Amitkumar Karwar6b41f942013-07-22 19:17:55 -0700506 goto err_init_fw;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700507
Avinash Patild6bffe82012-05-08 18:30:15 -0700508 priv = adapter->priv[MWIFIEX_BSS_ROLE_STA];
509 if (mwifiex_register_cfg80211(adapter)) {
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700510 dev_err(adapter->dev, "cannot register with cfg80211\n");
Amitkumar Karward1af2942013-11-14 19:10:39 -0800511 goto err_init_fw;
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700512 }
513
Avinash Patilbf354432014-10-31 16:08:26 +0530514 if (mwifiex_init_channel_scan_gap(adapter)) {
515 dev_err(adapter->dev, "could not init channel stats table\n");
516 goto err_init_fw;
517 }
518
Avinash Patil0013c7c2014-11-05 19:38:11 +0530519 if (driver_mode) {
520 driver_mode &= MWIFIEX_DRIVER_MODE_BITMASK;
521 driver_mode |= MWIFIEX_DRIVER_MODE_STA;
522 }
523
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700524 rtnl_lock();
525 /* Create station interface by default */
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100526 wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM,
Amitkumar Karwar68b76e92013-11-14 19:10:37 -0800527 NL80211_IFTYPE_STATION, NULL, NULL);
528 if (IS_ERR(wdev)) {
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700529 dev_err(adapter->dev, "cannot create default STA interface\n");
Amitkumar Karwarbec568f2013-11-14 19:10:38 -0800530 rtnl_unlock();
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700531 goto err_add_intf;
532 }
Avinash Patil0013c7c2014-11-05 19:38:11 +0530533
534 if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) {
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100535 wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM,
Avinash Patil0013c7c2014-11-05 19:38:11 +0530536 NL80211_IFTYPE_AP, NULL, NULL);
537 if (IS_ERR(wdev)) {
538 dev_err(adapter->dev, "cannot create AP interface\n");
539 rtnl_unlock();
540 goto err_add_intf;
541 }
542 }
543
544 if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) {
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100545 wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM,
Avinash Patil0013c7c2014-11-05 19:38:11 +0530546 NL80211_IFTYPE_P2P_CLIENT, NULL,
547 NULL);
548 if (IS_ERR(wdev)) {
549 dev_err(adapter->dev,
550 "cannot create p2p client interface\n");
551 rtnl_unlock();
552 goto err_add_intf;
553 }
554 }
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700555 rtnl_unlock();
556
557 mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
558 dev_notice(adapter->dev, "driver_version = %s\n", fmt);
559 goto done;
560
561err_add_intf:
Amitkumar Karwar6b41f942013-07-22 19:17:55 -0700562 wiphy_unregister(adapter->wiphy);
563 wiphy_free(adapter->wiphy);
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700564err_init_fw:
Daniel Drake232fde02013-07-13 10:57:10 -0400565 if (adapter->if_ops.disable_int)
566 adapter->if_ops.disable_int(adapter);
Amitkumar Karwar6b41f942013-07-22 19:17:55 -0700567err_dnld_fw:
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700568 pr_debug("info: %s: unregister device\n", __func__);
Amitkumar Karwar6b41f942013-07-22 19:17:55 -0700569 if (adapter->if_ops.unregister_dev)
570 adapter->if_ops.unregister_dev(adapter);
571
Amitkumar Karwar71973252014-12-31 02:36:38 -0800572 if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
Amitkumar Karwar6b41f942013-07-22 19:17:55 -0700573 pr_debug("info: %s: shutdown mwifiex\n", __func__);
574 adapter->init_wait_q_woken = false;
575
576 if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
577 wait_event_interruptible(adapter->init_wait_q,
578 adapter->init_wait_q_woken);
579 }
580 adapter->surprise_removed = true;
581 mwifiex_terminate_workqueue(adapter);
Amitkumar Karwarc3afd992013-07-30 17:18:15 -0700582 init_failed = true;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700583done:
Amitkumar Karwar388ec382013-05-17 17:50:25 -0700584 if (adapter->cal_data) {
585 release_firmware(adapter->cal_data);
586 adapter->cal_data = NULL;
587 }
Amitkumar Karwarc3afd992013-07-30 17:18:15 -0700588 if (adapter->firmware) {
589 release_firmware(adapter->firmware);
590 adapter->firmware = NULL;
591 }
Amitkumar Karwarc3afd992013-07-30 17:18:15 -0700592 if (init_failed)
593 mwifiex_free_adapter(adapter);
594 up(sem);
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700595 return;
596}
597
598/*
599 * This function initializes the hardware and gets firmware.
600 */
601static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter)
602{
603 int ret;
604
Amitkumar Karwar59a4cc22012-04-09 20:06:57 -0700605 ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name,
606 adapter->dev, GFP_KERNEL, adapter,
607 mwifiex_fw_dpc);
608 if (ret < 0)
609 dev_err(adapter->dev,
610 "request_firmware_nowait() returned error %d\n", ret);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700611 return ret;
612}
613
614/*
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700615 * CFG802.11 network device handler for open.
616 *
617 * Starts the data queue.
618 */
619static int
620mwifiex_open(struct net_device *dev)
621{
Johannes Bergea2325b2015-01-19 15:54:36 +0530622 netif_carrier_off(dev);
623
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700624 return 0;
625}
626
627/*
628 * CFG802.11 network device handler for close.
629 */
630static int
631mwifiex_close(struct net_device *dev)
632{
Amitkumar Karwarf162cac2012-10-19 19:19:16 -0700633 struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
634
635 if (priv->scan_request) {
636 dev_dbg(priv->adapter->dev, "aborting scan on ndo_stop\n");
637 cfg80211_scan_done(priv->scan_request, 1);
638 priv->scan_request = NULL;
Amitkumar Karwar75ab7532013-05-17 17:50:20 -0700639 priv->scan_aborting = true;
Amitkumar Karwarf162cac2012-10-19 19:19:16 -0700640 }
641
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700642 return 0;
643}
644
645/*
Stone Piaoe39faa72012-09-25 20:23:32 -0700646 * Add buffer into wmm tx queue and queue work to transmit it.
647 */
648int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb)
649{
Avinash Patil47411a02012-11-01 18:44:16 -0700650 struct netdev_queue *txq;
651 int index = mwifiex_1d_to_wmm_queue[skb->priority];
652
653 if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) {
654 txq = netdev_get_tx_queue(priv->netdev, index);
655 if (!netif_tx_queue_stopped(txq)) {
656 netif_tx_stop_queue(txq);
657 dev_dbg(priv->adapter->dev, "stop queue: %d\n", index);
658 }
659 }
660
Stone Piaoe39faa72012-09-25 20:23:32 -0700661 atomic_inc(&priv->adapter->tx_pending);
Avinash Patil47411a02012-11-01 18:44:16 -0700662 mwifiex_wmm_add_buf_txqueue(priv, skb);
Stone Piaoe39faa72012-09-25 20:23:32 -0700663
Shengzhen Lib2713f62015-03-13 17:37:54 +0530664 mwifiex_queue_main_work(priv->adapter);
Stone Piaoe39faa72012-09-25 20:23:32 -0700665
666 return 0;
667}
668
Amitkumar Karwar18ca4382014-11-25 06:43:06 -0800669struct sk_buff *
Amitkumar Karwar808bbeb2014-11-25 06:43:05 -0800670mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
Amitkumar Karwar18ca4382014-11-25 06:43:06 -0800671 struct sk_buff *skb, u8 flag, u64 *cookie)
Amitkumar Karwar808bbeb2014-11-25 06:43:05 -0800672{
673 struct sk_buff *orig_skb = skb;
674 struct mwifiex_txinfo *tx_info, *orig_tx_info;
675
676 skb = skb_clone(skb, GFP_ATOMIC);
677 if (skb) {
678 unsigned long flags;
679 int id;
680
681 spin_lock_irqsave(&priv->ack_status_lock, flags);
682 id = idr_alloc(&priv->ack_status_frames, orig_skb,
683 1, 0xff, GFP_ATOMIC);
684 spin_unlock_irqrestore(&priv->ack_status_lock, flags);
685
686 if (id >= 0) {
687 tx_info = MWIFIEX_SKB_TXCB(skb);
688 tx_info->ack_frame_id = id;
689 tx_info->flags |= flag;
690 orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb);
691 orig_tx_info->ack_frame_id = id;
692 orig_tx_info->flags |= flag;
Amitkumar Karwar18ca4382014-11-25 06:43:06 -0800693
694 if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie)
695 orig_tx_info->cookie = *cookie;
696
Amitkumar Karwar808bbeb2014-11-25 06:43:05 -0800697 } else if (skb_shared(skb)) {
698 kfree_skb(orig_skb);
699 } else {
700 kfree_skb(skb);
701 skb = orig_skb;
702 }
703 } else {
704 /* couldn't clone -- lose tx status ... */
705 skb = orig_skb;
706 }
707
708 return skb;
709}
710
Stone Piaoe39faa72012-09-25 20:23:32 -0700711/*
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700712 * CFG802.11 network device handler for data transmission.
713 */
714static int
715mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
716{
717 struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
Yogesh Ashok Powar270e58e2011-05-03 20:11:46 -0700718 struct sk_buff *new_skb;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700719 struct mwifiex_txinfo *tx_info;
Amitkumar Karwar808bbeb2014-11-25 06:43:05 -0800720 bool multicast;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700721
Yogesh Ashok Powar9da9a3b2012-01-11 20:06:11 -0800722 dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n",
Yogesh Ashok Powarf57c1ed2012-03-13 19:22:37 -0700723 jiffies, priv->bss_type, priv->bss_num);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700724
725 if (priv->adapter->surprise_removed) {
Christoph Fritzb53575e2011-05-08 22:50:09 +0200726 kfree_skb(skb);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700727 priv->stats.tx_dropped++;
728 return 0;
729 }
730 if (!skb->len || (skb->len > ETH_FRAME_LEN)) {
731 dev_err(priv->adapter->dev, "Tx: bad skb len %d\n", skb->len);
Christoph Fritzb53575e2011-05-08 22:50:09 +0200732 kfree_skb(skb);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700733 priv->stats.tx_dropped++;
734 return 0;
735 }
736 if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) {
737 dev_dbg(priv->adapter->dev,
738 "data: Tx: insufficient skb headroom %d\n",
Yogesh Ashok Powarf57c1ed2012-03-13 19:22:37 -0700739 skb_headroom(skb));
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700740 /* Insufficient skb headroom - allocate a new skb */
741 new_skb =
742 skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
743 if (unlikely(!new_skb)) {
744 dev_err(priv->adapter->dev, "Tx: cannot alloca new_skb\n");
Christoph Fritzb53575e2011-05-08 22:50:09 +0200745 kfree_skb(skb);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700746 priv->stats.tx_dropped++;
747 return 0;
748 }
749 kfree_skb(skb);
750 skb = new_skb;
751 dev_dbg(priv->adapter->dev, "info: new skb headroomd %d\n",
Yogesh Ashok Powarf57c1ed2012-03-13 19:22:37 -0700752 skb_headroom(skb));
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700753 }
754
755 tx_info = MWIFIEX_SKB_TXCB(skb);
Amitkumar Karward76744a2014-06-20 11:45:25 -0700756 memset(tx_info, 0, sizeof(*tx_info));
Yogesh Ashok Powar9da9a3b2012-01-11 20:06:11 -0800757 tx_info->bss_num = priv->bss_num;
758 tx_info->bss_type = priv->bss_type;
Ujjal Roya1ed4842013-12-02 23:17:55 -0800759 tx_info->pkt_len = skb->len;
Avinash Patil47411a02012-11-01 18:44:16 -0700760
Amitkumar Karwar808bbeb2014-11-25 06:43:05 -0800761 multicast = is_multicast_ether_addr(skb->data);
762
763 if (unlikely(!multicast && skb->sk &&
764 skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS &&
765 priv->adapter->fw_api_ver == MWIFIEX_FW_V15))
766 skb = mwifiex_clone_skb_for_tx_status(priv,
767 skb,
Amitkumar Karwar18ca4382014-11-25 06:43:06 -0800768 MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL);
Amitkumar Karwar808bbeb2014-11-25 06:43:05 -0800769
Avinash Patil47411a02012-11-01 18:44:16 -0700770 /* Record the current time the packet was queued; used to
771 * determine the amount of time the packet was queued in
772 * the driver before it was sent to the firmware.
773 * The delay is then sent along with the packet to the
774 * firmware for aggregate delay calculation for stats and
775 * MSDU lifetime expiry.
776 */
Thomas Gleixnerc64800e2014-06-12 08:31:34 +0100777 __net_timestamp(skb);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700778
Avinash Patil9927baa2014-11-13 21:54:16 +0530779 if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
780 priv->bss_type == MWIFIEX_BSS_TYPE_STA &&
781 !ether_addr_equal_unaligned(priv->cfg_bssid, skb->data)) {
782 if (priv->adapter->auto_tdls && priv->check_tdls_tx)
783 mwifiex_tdls_check_tx(priv, skb);
784 }
785
Stone Piaoe39faa72012-09-25 20:23:32 -0700786 mwifiex_queue_tx_pkt(priv, skb);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700787
788 return 0;
789}
790
791/*
792 * CFG802.11 network device handler for setting MAC address.
793 */
794static int
795mwifiex_set_mac_address(struct net_device *dev, void *addr)
796{
797 struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
Amitkumar Karwara5ffddb2011-06-20 15:21:48 -0700798 struct sockaddr *hw_addr = addr;
Yogesh Ashok Powar270e58e2011-05-03 20:11:46 -0700799 int ret;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700800
801 memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN);
802
Amitkumar Karwar600f5d92011-04-13 17:27:06 -0700803 /* Send request to firmware */
Bing Zhaofa0ecbb2014-02-27 19:35:12 -0800804 ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS,
805 HostCmd_ACT_GEN_SET, 0, NULL, true);
Amitkumar Karwar600f5d92011-04-13 17:27:06 -0700806
807 if (!ret)
808 memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN);
809 else
Yogesh Ashok Powarf57c1ed2012-03-13 19:22:37 -0700810 dev_err(priv->adapter->dev,
811 "set mac address failed: ret=%d\n", ret);
Amitkumar Karwar600f5d92011-04-13 17:27:06 -0700812
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700813 memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN);
814
Amitkumar Karwar600f5d92011-04-13 17:27:06 -0700815 return ret;
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700816}
817
818/*
819 * CFG802.11 network device handler for setting multicast list.
820 */
821static void mwifiex_set_multicast_list(struct net_device *dev)
822{
823 struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
Amitkumar Karwar600f5d92011-04-13 17:27:06 -0700824 struct mwifiex_multicast_list mcast_list;
825
826 if (dev->flags & IFF_PROMISC) {
827 mcast_list.mode = MWIFIEX_PROMISC_MODE;
828 } else if (dev->flags & IFF_ALLMULTI ||
829 netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) {
830 mcast_list.mode = MWIFIEX_ALL_MULTI_MODE;
831 } else {
832 mcast_list.mode = MWIFIEX_MULTICAST_MODE;
Daniel Drake6390d882013-06-14 15:24:24 -0400833 mcast_list.num_multicast_addr =
834 mwifiex_copy_mcast_addr(&mcast_list, dev);
Amitkumar Karwar600f5d92011-04-13 17:27:06 -0700835 }
836 mwifiex_request_set_multicast_list(priv, &mcast_list);
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700837}
838
839/*
840 * CFG802.11 network device handler for transmission timeout.
841 */
842static void
843mwifiex_tx_timeout(struct net_device *dev)
844{
845 struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
846
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700847 priv->num_tx_timeout++;
Ashok Nagarajan8908c7d2013-03-08 10:58:45 -0800848 priv->tx_timeout_cnt++;
849 dev_err(priv->adapter->dev,
850 "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n",
851 jiffies, priv->tx_timeout_cnt, priv->bss_type, priv->bss_num);
852 mwifiex_set_trans_start(dev);
853
854 if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD &&
855 priv->adapter->if_ops.card_reset) {
856 dev_err(priv->adapter->dev,
857 "tx_timeout_cnt exceeds threshold. Triggering card reset!\n");
858 priv->adapter->if_ops.card_reset(priv->adapter);
859 }
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700860}
861
Xinming Hu11cd07a2014-12-23 19:14:10 +0530862void mwifiex_dump_drv_info(struct mwifiex_adapter *adapter)
863{
864 void *p;
865 char drv_version[64];
866 struct usb_card_rec *cardp;
867 struct sdio_mmc_card *sdio_card;
868 struct mwifiex_private *priv;
869 int i, idx;
870 struct netdev_queue *txq;
871 struct mwifiex_debug_info *debug_info;
872
873 if (adapter->drv_info_dump) {
874 vfree(adapter->drv_info_dump);
875 adapter->drv_info_size = 0;
876 }
877
878 dev_info(adapter->dev, "=== DRIVER INFO DUMP START===\n");
879
880 adapter->drv_info_dump = vzalloc(MWIFIEX_DRV_INFO_SIZE_MAX);
881
882 if (!adapter->drv_info_dump)
883 return;
884
885 p = (char *)(adapter->drv_info_dump);
886 p += sprintf(p, "driver_name = " "\"mwifiex\"\n");
887
888 mwifiex_drv_get_driver_version(adapter, drv_version,
889 sizeof(drv_version) - 1);
890 p += sprintf(p, "driver_version = %s\n", drv_version);
891
892 if (adapter->iface_type == MWIFIEX_USB) {
893 cardp = (struct usb_card_rec *)adapter->card;
894 p += sprintf(p, "tx_cmd_urb_pending = %d\n",
895 atomic_read(&cardp->tx_cmd_urb_pending));
896 p += sprintf(p, "tx_data_urb_pending = %d\n",
897 atomic_read(&cardp->tx_data_urb_pending));
898 p += sprintf(p, "rx_cmd_urb_pending = %d\n",
899 atomic_read(&cardp->rx_cmd_urb_pending));
900 p += sprintf(p, "rx_data_urb_pending = %d\n",
901 atomic_read(&cardp->rx_data_urb_pending));
902 }
903
904 p += sprintf(p, "tx_pending = %d\n",
905 atomic_read(&adapter->tx_pending));
906 p += sprintf(p, "rx_pending = %d\n",
907 atomic_read(&adapter->rx_pending));
908
909 if (adapter->iface_type == MWIFIEX_SDIO) {
910 sdio_card = (struct sdio_mmc_card *)adapter->card;
911 p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n",
912 sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port);
913 p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n",
914 sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port);
915 }
916
917 for (i = 0; i < adapter->priv_num; i++) {
918 if (!adapter->priv[i] || !adapter->priv[i]->netdev)
919 continue;
920 priv = adapter->priv[i];
921 p += sprintf(p, "\n[interface : \"%s\"]\n",
922 priv->netdev->name);
923 p += sprintf(p, "wmm_tx_pending[0] = %d\n",
924 atomic_read(&priv->wmm_tx_pending[0]));
925 p += sprintf(p, "wmm_tx_pending[1] = %d\n",
926 atomic_read(&priv->wmm_tx_pending[1]));
927 p += sprintf(p, "wmm_tx_pending[2] = %d\n",
928 atomic_read(&priv->wmm_tx_pending[2]));
929 p += sprintf(p, "wmm_tx_pending[3] = %d\n",
930 atomic_read(&priv->wmm_tx_pending[3]));
931 p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ?
932 "Disconnected" : "Connected");
933 p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev)
934 ? "on" : "off"));
935 for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) {
936 txq = netdev_get_tx_queue(priv->netdev, idx);
937 p += sprintf(p, "tx queue %d:%s ", idx,
938 netif_tx_queue_stopped(txq) ?
939 "stopped" : "started");
940 }
941 p += sprintf(p, "\n%s: num_tx_timeout = %d\n",
942 priv->netdev->name, priv->num_tx_timeout);
943 }
944
Xinming Hu809c6ea2014-12-23 19:14:11 +0530945 if (adapter->iface_type == MWIFIEX_SDIO) {
946 p += sprintf(p, "\n=== SDIO register DUMP===\n");
947 if (adapter->if_ops.reg_dump)
948 p += adapter->if_ops.reg_dump(adapter, p);
949 }
950
Xinming Hu11cd07a2014-12-23 19:14:10 +0530951 p += sprintf(p, "\n=== MORE DEBUG INFORMATION\n");
952 debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL);
953 if (debug_info) {
954 for (i = 0; i < adapter->priv_num; i++) {
955 if (!adapter->priv[i] || !adapter->priv[i]->netdev)
956 continue;
957 priv = adapter->priv[i];
958 mwifiex_get_debug_info(priv, debug_info);
959 p += mwifiex_debug_info_to_buffer(priv, p, debug_info);
960 break;
961 }
962 kfree(debug_info);
963 }
964
965 adapter->drv_info_size = p - adapter->drv_info_dump;
966 dev_info(adapter->dev, "=== DRIVER INFO DUMP END===\n");
967}
968EXPORT_SYMBOL_GPL(mwifiex_dump_drv_info);
969
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700970/*
971 * CFG802.11 network device handler for statistics retrieval.
972 */
973static struct net_device_stats *mwifiex_get_stats(struct net_device *dev)
974{
975 struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
976
977 return &priv->stats;
978}
979
Avinash Patil47411a02012-11-01 18:44:16 -0700980static u16
Jason Wangf663dd92014-01-10 16:18:26 +0800981mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb,
Daniel Borkmann99932d42014-02-16 15:55:20 +0100982 void *accel_priv, select_queue_fallback_t fallback)
Avinash Patil47411a02012-11-01 18:44:16 -0700983{
Kyeyoon Parkfa9ffc72013-12-16 23:01:30 -0800984 skb->priority = cfg80211_classify8021d(skb, NULL);
Avinash Patil47411a02012-11-01 18:44:16 -0700985 return mwifiex_1d_to_wmm_queue[skb->priority];
986}
987
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700988/* Network device handlers */
989static const struct net_device_ops mwifiex_netdev_ops = {
990 .ndo_open = mwifiex_open,
991 .ndo_stop = mwifiex_close,
992 .ndo_start_xmit = mwifiex_hard_start_xmit,
993 .ndo_set_mac_address = mwifiex_set_mac_address,
994 .ndo_tx_timeout = mwifiex_tx_timeout,
995 .ndo_get_stats = mwifiex_get_stats,
Jiri Pirkoafc4b132011-08-16 06:29:01 +0000996 .ndo_set_rx_mode = mwifiex_set_multicast_list,
Avinash Patil47411a02012-11-01 18:44:16 -0700997 .ndo_select_queue = mwifiex_netdev_select_wmm_queue,
Bing Zhao5e6e3a92011-03-21 18:00:50 -0700998};
999
1000/*
1001 * This function initializes the private structure parameters.
1002 *
1003 * The following wait queues are initialized -
1004 * - IOCTL wait queue
1005 * - Command wait queue
1006 * - Statistics wait queue
1007 *
1008 * ...and the following default parameters are set -
1009 * - Current key index : Set to 0
1010 * - Rate index : Set to auto
1011 * - Media connected : Set to disconnected
1012 * - Adhoc link sensed : Set to false
1013 * - Nick name : Set to null
1014 * - Number of Tx timeout : Set to 0
1015 * - Device address : Set to current address
Xinming Hucbf6e052014-12-23 19:14:07 +05301016 * - Rx histogram statistc : Set to 0
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001017 *
1018 * In addition, the CFG80211 work queue is also created.
1019 */
Yogesh Ashok Powar93a1df42011-09-26 20:37:26 -07001020void mwifiex_init_priv_params(struct mwifiex_private *priv,
Avinash Patil047eaaf2015-01-28 15:42:05 +05301021 struct net_device *dev)
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001022{
1023 dev->netdev_ops = &mwifiex_netdev_ops;
Amitkumar Karwarf16fdc92013-05-06 19:46:54 -07001024 dev->destructor = free_netdev;
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001025 /* Initialize private structure */
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001026 priv->current_key_index = 0;
1027 priv->media_connected = false;
Avinash Patilede98bf2012-05-08 18:30:28 -07001028 memset(priv->mgmt_ie, 0,
1029 sizeof(struct mwifiex_ie) * MAX_MGMT_IE_INDEX);
Avinash Patilf31acab2012-05-08 18:30:29 -07001030 priv->beacon_idx = MWIFIEX_AUTO_IDX_MASK;
1031 priv->proberesp_idx = MWIFIEX_AUTO_IDX_MASK;
1032 priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK;
Avinash Patil2ade5662015-01-28 15:54:20 +05301033 priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001034 priv->num_tx_timeout = 0;
Avinash Patil8d05eb22015-01-28 15:42:01 +05301035 ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr);
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001036 memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN);
Xinming Hucbf6e052014-12-23 19:14:07 +05301037
1038 if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
1039 GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
1040 priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL);
1041 if (priv->hist_data)
1042 mwifiex_hist_data_reset(priv);
1043 }
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001044}
1045
1046/*
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001047 * This function check if command is pending.
1048 */
1049int is_command_pending(struct mwifiex_adapter *adapter)
1050{
1051 unsigned long flags;
1052 int is_cmd_pend_q_empty;
1053
1054 spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
1055 is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q);
1056 spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
1057
1058 return !is_cmd_pend_q_empty;
1059}
1060
1061/*
Avinash Patil6e251172014-09-12 20:08:59 +05301062 * This is the RX work queue function.
1063 *
1064 * It handles the RX operations.
1065 */
1066static void mwifiex_rx_work_queue(struct work_struct *work)
1067{
1068 struct mwifiex_adapter *adapter =
1069 container_of(work, struct mwifiex_adapter, rx_work);
1070
1071 if (adapter->surprise_removed)
1072 return;
1073 mwifiex_process_rx(adapter);
1074}
1075
1076/*
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001077 * This is the main work queue function.
1078 *
1079 * It handles the main process, which in turn handles the complete
1080 * driver operations.
1081 */
1082static void mwifiex_main_work_queue(struct work_struct *work)
1083{
1084 struct mwifiex_adapter *adapter =
1085 container_of(work, struct mwifiex_adapter, main_work);
1086
1087 if (adapter->surprise_removed)
1088 return;
1089 mwifiex_main_process(adapter);
1090}
1091
1092/*
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001093 * This function adds the card.
1094 *
1095 * This function follows the following major steps to set up the device -
1096 * - Initialize software. This includes probing the card, registering
1097 * the interface operations table, and allocating/initializing the
1098 * adapter structure
1099 * - Set up the netlink socket
1100 * - Create and start the main work queue
1101 * - Register the device
1102 * - Initialize firmware and hardware
1103 * - Add logical interfaces
1104 */
1105int
1106mwifiex_add_card(void *card, struct semaphore *sem,
Amitkumar Karward930fae2011-10-11 17:41:21 -07001107 struct mwifiex_if_ops *if_ops, u8 iface_type)
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001108{
Amitkumar Karwar2be78592011-04-15 20:50:42 -07001109 struct mwifiex_adapter *adapter;
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001110
1111 if (down_interruptible(sem))
1112 goto exit_sem_err;
1113
Yogesh Ashok Powar93a1df42011-09-26 20:37:26 -07001114 if (mwifiex_register(card, if_ops, (void **)&adapter)) {
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001115 pr_err("%s: software init failed\n", __func__);
1116 goto err_init_sw;
1117 }
1118
Amitkumar Karward930fae2011-10-11 17:41:21 -07001119 adapter->iface_type = iface_type;
Amitkumar Karwar6b41f942013-07-22 19:17:55 -07001120 adapter->card_sem = sem;
Amitkumar Karward930fae2011-10-11 17:41:21 -07001121
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001122 adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001123 adapter->surprise_removed = false;
1124 init_waitqueue_head(&adapter->init_wait_q);
1125 adapter->is_suspended = false;
1126 adapter->hs_activated = false;
1127 init_waitqueue_head(&adapter->hs_activate_wait_q);
Amitkumar Karwar600f5d92011-04-13 17:27:06 -07001128 init_waitqueue_head(&adapter->cmd_wait_q.wait);
Amitkumar Karwar600f5d92011-04-13 17:27:06 -07001129 adapter->cmd_wait_q.status = 0;
Amitkumar Karwarefaaa8b2011-10-12 20:28:06 -07001130 adapter->scan_wait_q_woken = false;
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001131
Avinash Patilec4a16b2014-11-05 17:04:27 +05301132 if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB) {
Avinash Patil6e251172014-09-12 20:08:59 +05301133 adapter->rx_work_enabled = true;
1134 pr_notice("rx work enabled, cpus %d\n", num_possible_cpus());
1135 }
1136
Amitkumar Karwar448a71c2013-10-11 18:33:04 -07001137 adapter->workqueue =
1138 alloc_workqueue("MWIFIEX_WORK_QUEUE",
1139 WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001140 if (!adapter->workqueue)
1141 goto err_kmalloc;
1142
1143 INIT_WORK(&adapter->main_work, mwifiex_main_work_queue);
Avinash Patil6e251172014-09-12 20:08:59 +05301144
1145 if (adapter->rx_work_enabled) {
1146 adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE",
1147 WQ_HIGHPRI |
1148 WQ_MEM_RECLAIM |
1149 WQ_UNBOUND, 1);
1150 if (!adapter->rx_workqueue)
1151 goto err_kmalloc;
1152
1153 INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue);
1154 }
1155
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001156 /* Register the device. Fill up the private data structure with relevant
Daniel Drake232fde02013-07-13 10:57:10 -04001157 information from the card. */
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001158 if (adapter->if_ops.register_dev(adapter)) {
1159 pr_err("%s: failed to register mwifiex device\n", __func__);
1160 goto err_registerdev;
1161 }
1162
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001163 if (mwifiex_init_hw_fw(adapter)) {
1164 pr_err("%s: firmware init failed\n", __func__);
1165 goto err_init_fw;
1166 }
Amitkumar Karwar2be78592011-04-15 20:50:42 -07001167
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001168 return 0;
1169
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001170err_init_fw:
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001171 pr_debug("info: %s: unregister device\n", __func__);
Amitkumar Karwar4daffe32012-04-18 20:08:28 -07001172 if (adapter->if_ops.unregister_dev)
1173 adapter->if_ops.unregister_dev(adapter);
Amitkumar Karwar71973252014-12-31 02:36:38 -08001174 if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001175 pr_debug("info: %s: shutdown mwifiex\n", __func__);
1176 adapter->init_wait_q_woken = false;
Yogesh Ashok Powar636c4592011-04-15 20:50:40 -07001177
1178 if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001179 wait_event_interruptible(adapter->init_wait_q,
1180 adapter->init_wait_q_woken);
1181 }
Amitkumar Karwar6b41f942013-07-22 19:17:55 -07001182err_registerdev:
1183 adapter->surprise_removed = true;
1184 mwifiex_terminate_workqueue(adapter);
1185err_kmalloc:
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001186 mwifiex_free_adapter(adapter);
1187
1188err_init_sw:
1189 up(sem);
1190
1191exit_sem_err:
1192 return -1;
1193}
1194EXPORT_SYMBOL_GPL(mwifiex_add_card);
1195
1196/*
1197 * This function removes the card.
1198 *
1199 * This function follows the following major steps to remove the device -
1200 * - Stop data traffic
1201 * - Shutdown firmware
1202 * - Remove the logical interfaces
1203 * - Terminate the work queue
1204 * - Unregister the device
1205 * - Free the adapter structure
1206 */
1207int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem)
1208{
1209 struct mwifiex_private *priv = NULL;
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001210 int i;
1211
1212 if (down_interruptible(sem))
1213 goto exit_sem_err;
1214
1215 if (!adapter)
1216 goto exit_remove;
1217
Daniel Drake232fde02013-07-13 10:57:10 -04001218 /* We can no longer handle interrupts once we start doing the teardown
1219 * below. */
1220 if (adapter->if_ops.disable_int)
1221 adapter->if_ops.disable_int(adapter);
1222
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001223 adapter->surprise_removed = true;
1224
Marc Yang9d2e85e2014-12-31 02:36:39 -08001225 mwifiex_terminate_workqueue(adapter);
1226
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001227 /* Stop data */
1228 for (i = 0; i < adapter->priv_num; i++) {
1229 priv = adapter->priv[i];
Yogesh Ashok Powar93a1df42011-09-26 20:37:26 -07001230 if (priv && priv->netdev) {
Avinash Patil47411a02012-11-01 18:44:16 -07001231 mwifiex_stop_net_dev_queue(priv->netdev, adapter);
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001232 if (netif_carrier_ok(priv->netdev))
1233 netif_carrier_off(priv->netdev);
1234 }
1235 }
1236
1237 dev_dbg(adapter->dev, "cmd: calling mwifiex_shutdown_drv...\n");
1238 adapter->init_wait_q_woken = false;
Yogesh Ashok Powar636c4592011-04-15 20:50:40 -07001239
1240 if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001241 wait_event_interruptible(adapter->init_wait_q,
1242 adapter->init_wait_q_woken);
1243 dev_dbg(adapter->dev, "cmd: mwifiex_shutdown_drv done\n");
1244 if (atomic_read(&adapter->rx_pending) ||
1245 atomic_read(&adapter->tx_pending) ||
Amitkumar Karwar600f5d92011-04-13 17:27:06 -07001246 atomic_read(&adapter->cmd_pending)) {
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001247 dev_err(adapter->dev, "rx_pending=%d, tx_pending=%d, "
Amitkumar Karwar600f5d92011-04-13 17:27:06 -07001248 "cmd_pending=%d\n",
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001249 atomic_read(&adapter->rx_pending),
1250 atomic_read(&adapter->tx_pending),
Amitkumar Karwar600f5d92011-04-13 17:27:06 -07001251 atomic_read(&adapter->cmd_pending));
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001252 }
1253
Yogesh Ashok Powar93a1df42011-09-26 20:37:26 -07001254 for (i = 0; i < adapter->priv_num; i++) {
1255 priv = adapter->priv[i];
1256
1257 if (!priv)
1258 continue;
1259
1260 rtnl_lock();
Avinash Patil4facc342015-01-28 15:42:00 +05301261 if (priv->netdev &&
1262 priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED)
1263 mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev);
Yogesh Ashok Powar93a1df42011-09-26 20:37:26 -07001264 rtnl_unlock();
1265 }
1266
Amitkumar Karwarc2868ad2013-12-02 23:17:57 -08001267 wiphy_unregister(adapter->wiphy);
1268 wiphy_free(adapter->wiphy);
Avinash Patil64b05e22012-05-08 18:30:13 -07001269
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001270 /* Unregister device */
1271 dev_dbg(adapter->dev, "info: unregister device\n");
Amitkumar Karwar4daffe32012-04-18 20:08:28 -07001272 if (adapter->if_ops.unregister_dev)
1273 adapter->if_ops.unregister_dev(adapter);
Bing Zhao5e6e3a92011-03-21 18:00:50 -07001274 /* Free adapter structure */
1275 dev_dbg(adapter->dev, "info: free adapter\n");
1276 mwifiex_free_adapter(adapter);
1277
1278exit_remove:
1279 up(sem);
1280exit_sem_err:
1281 return 0;
1282}
1283EXPORT_SYMBOL_GPL(mwifiex_remove_card);
1284
1285/*
1286 * This function initializes the module.
1287 *
1288 * The debug FS is also initialized if configured.
1289 */
1290static int
1291mwifiex_init_module(void)
1292{
1293#ifdef CONFIG_DEBUG_FS
1294 mwifiex_debugfs_init();
1295#endif
1296 return 0;
1297}
1298
1299/*
1300 * This function cleans up the module.
1301 *
1302 * The debug FS is removed if available.
1303 */
1304static void
1305mwifiex_cleanup_module(void)
1306{
1307#ifdef CONFIG_DEBUG_FS
1308 mwifiex_debugfs_remove();
1309#endif
1310}
1311
1312module_init(mwifiex_init_module);
1313module_exit(mwifiex_cleanup_module);
1314
1315MODULE_AUTHOR("Marvell International Ltd.");
1316MODULE_DESCRIPTION("Marvell WiFi-Ex Driver version " VERSION);
1317MODULE_VERSION(VERSION);
1318MODULE_LICENSE("GPL v2");