1828c91f7SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2277b024eSKalle Valo /*
3932183aaSGanapathi Bhat * NXP Wireless LAN device driver: major functions
4277b024eSKalle Valo *
5932183aaSGanapathi Bhat * Copyright 2011-2020 NXP
6277b024eSKalle Valo */
7277b024eSKalle Valo
8ef7e0714SJeffy Chen #include <linux/suspend.h>
9ef7e0714SJeffy Chen
10277b024eSKalle Valo #include "main.h"
11277b024eSKalle Valo #include "wmm.h"
12277b024eSKalle Valo #include "cfg80211.h"
13277b024eSKalle Valo #include "11n.h"
14277b024eSKalle Valo
15277b024eSKalle Valo #define VERSION "1.0"
16cf5383b0SXinming Hu #define MFG_FIRMWARE "mwifiex_mfg.bin"
17277b024eSKalle Valo
18277b024eSKalle Valo static unsigned int debug_mask = MWIFIEX_DEFAULT_DEBUG_MASK;
19277b024eSKalle Valo module_param(debug_mask, uint, 0);
20277b024eSKalle Valo MODULE_PARM_DESC(debug_mask, "bitmap for debug flags");
21277b024eSKalle Valo
22277b024eSKalle Valo const char driver_version[] = "mwifiex " VERSION " (%s) ";
23277b024eSKalle Valo static char *cal_data_cfg;
24277b024eSKalle Valo module_param(cal_data_cfg, charp, 0);
25277b024eSKalle Valo
26277b024eSKalle Valo static unsigned short driver_mode;
27277b024eSKalle Valo module_param(driver_mode, ushort, 0);
28277b024eSKalle Valo MODULE_PARM_DESC(driver_mode,
29277b024eSKalle Valo "station=0x1(default), ap-sta=0x3, station-p2p=0x5, ap-sta-p2p=0x7");
30277b024eSKalle Valo
31cf5383b0SXinming Hu bool mfg_mode;
32cf5383b0SXinming Hu module_param(mfg_mode, bool, 0);
33cf5383b0SXinming Hu MODULE_PARM_DESC(mfg_mode, "manufacturing mode enable:1, disable:0");
34cf5383b0SXinming Hu
35c5994293SXinming Hu bool aggr_ctrl;
36c5994293SXinming Hu module_param(aggr_ctrl, bool, 0000);
37c5597172SColin Ian King MODULE_PARM_DESC(aggr_ctrl, "usb tx aggregation enable:1, disable:0");
38c5994293SXinming Hu
39d56ee19aSYueHaibing const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
40d56ee19aSYueHaibing
41277b024eSKalle Valo /*
42277b024eSKalle Valo * This function registers the device and performs all the necessary
43277b024eSKalle Valo * initializations.
44277b024eSKalle Valo *
45277b024eSKalle Valo * The following initialization operations are performed -
46277b024eSKalle Valo * - Allocate adapter structure
47277b024eSKalle Valo * - Save interface specific operations table in adapter
48277b024eSKalle Valo * - Call interface specific initialization routine
49277b024eSKalle Valo * - Allocate private structures
50277b024eSKalle Valo * - Set default adapter structure parameters
51277b024eSKalle Valo * - Initialize locks
52277b024eSKalle Valo *
53277b024eSKalle Valo * In case of any errors during inittialization, this function also ensures
54277b024eSKalle Valo * proper cleanup before exiting.
55277b024eSKalle Valo */
mwifiex_register(void * card,struct device * dev,struct mwifiex_if_ops * if_ops,void ** padapter)56ba1c7e45SBrian Norris static int mwifiex_register(void *card, struct device *dev,
57ba1c7e45SBrian Norris struct mwifiex_if_ops *if_ops, void **padapter)
58277b024eSKalle Valo {
59277b024eSKalle Valo struct mwifiex_adapter *adapter;
60277b024eSKalle Valo int i;
61277b024eSKalle Valo
62277b024eSKalle Valo adapter = kzalloc(sizeof(struct mwifiex_adapter), GFP_KERNEL);
63277b024eSKalle Valo if (!adapter)
64277b024eSKalle Valo return -ENOMEM;
65277b024eSKalle Valo
66277b024eSKalle Valo *padapter = adapter;
67ba1c7e45SBrian Norris adapter->dev = dev;
68277b024eSKalle Valo adapter->card = card;
69277b024eSKalle Valo
70277b024eSKalle Valo /* Save interface specific operations in adapter */
71277b024eSKalle Valo memmove(&adapter->if_ops, if_ops, sizeof(struct mwifiex_if_ops));
72277b024eSKalle Valo adapter->debug_mask = debug_mask;
73277b024eSKalle Valo
74277b024eSKalle Valo /* card specific initialization has been deferred until now .. */
75277b024eSKalle Valo if (adapter->if_ops.init_if)
76277b024eSKalle Valo if (adapter->if_ops.init_if(adapter))
77277b024eSKalle Valo goto error;
78277b024eSKalle Valo
79277b024eSKalle Valo adapter->priv_num = 0;
80277b024eSKalle Valo
81277b024eSKalle Valo for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) {
82277b024eSKalle Valo /* Allocate memory for private structure */
83277b024eSKalle Valo adapter->priv[i] =
84277b024eSKalle Valo kzalloc(sizeof(struct mwifiex_private), GFP_KERNEL);
85277b024eSKalle Valo if (!adapter->priv[i])
86277b024eSKalle Valo goto error;
87277b024eSKalle Valo
88277b024eSKalle Valo adapter->priv[i]->adapter = adapter;
89277b024eSKalle Valo adapter->priv_num++;
90277b024eSKalle Valo }
91277b024eSKalle Valo mwifiex_init_lock_list(adapter);
92277b024eSKalle Valo
9308c2eb8eSKees Cook timer_setup(&adapter->cmd_timer, mwifiex_cmd_timeout_func, 0);
94277b024eSKalle Valo
95277b024eSKalle Valo return 0;
96277b024eSKalle Valo
97277b024eSKalle Valo error:
98277b024eSKalle Valo mwifiex_dbg(adapter, ERROR,
99277b024eSKalle Valo "info: leave mwifiex_register with error\n");
100277b024eSKalle Valo
101277b024eSKalle Valo for (i = 0; i < adapter->priv_num; i++)
102277b024eSKalle Valo kfree(adapter->priv[i]);
103277b024eSKalle Valo
104277b024eSKalle Valo kfree(adapter);
105277b024eSKalle Valo
106277b024eSKalle Valo return -1;
107277b024eSKalle Valo }
108277b024eSKalle Valo
109277b024eSKalle Valo /*
110277b024eSKalle Valo * This function unregisters the device and performs all the necessary
111277b024eSKalle Valo * cleanups.
112277b024eSKalle Valo *
113277b024eSKalle Valo * The following cleanup operations are performed -
114277b024eSKalle Valo * - Free the timers
115277b024eSKalle Valo * - Free beacon buffers
116277b024eSKalle Valo * - Free private structures
117277b024eSKalle Valo * - Free adapter structure
118277b024eSKalle Valo */
mwifiex_unregister(struct mwifiex_adapter * adapter)119277b024eSKalle Valo static int mwifiex_unregister(struct mwifiex_adapter *adapter)
120277b024eSKalle Valo {
121277b024eSKalle Valo s32 i;
122277b024eSKalle Valo
123277b024eSKalle Valo if (adapter->if_ops.cleanup_if)
124277b024eSKalle Valo adapter->if_ops.cleanup_if(adapter);
125277b024eSKalle Valo
126292a089dSSteven Rostedt (Google) timer_shutdown_sync(&adapter->cmd_timer);
127277b024eSKalle Valo
128277b024eSKalle Valo /* Free private structures */
129277b024eSKalle Valo for (i = 0; i < adapter->priv_num; i++) {
130277b024eSKalle Valo if (adapter->priv[i]) {
131277b024eSKalle Valo mwifiex_free_curr_bcn(adapter->priv[i]);
132277b024eSKalle Valo kfree(adapter->priv[i]);
133277b024eSKalle Valo }
134277b024eSKalle Valo }
135277b024eSKalle Valo
1367d7f07d8Schunfan chen if (adapter->nd_info) {
1377d7f07d8Schunfan chen for (i = 0 ; i < adapter->nd_info->n_matches ; i++)
1387d7f07d8Schunfan chen kfree(adapter->nd_info->matches[i]);
1397d7f07d8Schunfan chen kfree(adapter->nd_info);
1407d7f07d8Schunfan chen adapter->nd_info = NULL;
1417d7f07d8Schunfan chen }
1427d7f07d8Schunfan chen
14372539799SAmitkumar Karwar kfree(adapter->regd);
14472539799SAmitkumar Karwar
145277b024eSKalle Valo kfree(adapter);
146277b024eSKalle Valo return 0;
147277b024eSKalle Valo }
148277b024eSKalle Valo
mwifiex_queue_main_work(struct mwifiex_adapter * adapter)149277b024eSKalle Valo void mwifiex_queue_main_work(struct mwifiex_adapter *adapter)
150277b024eSKalle Valo {
151277b024eSKalle Valo unsigned long flags;
152277b024eSKalle Valo
153277b024eSKalle Valo spin_lock_irqsave(&adapter->main_proc_lock, flags);
154277b024eSKalle Valo if (adapter->mwifiex_processing) {
155277b024eSKalle Valo adapter->more_task_flag = true;
156277b024eSKalle Valo spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
157277b024eSKalle Valo } else {
158277b024eSKalle Valo spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
159277b024eSKalle Valo queue_work(adapter->workqueue, &adapter->main_work);
160277b024eSKalle Valo }
161277b024eSKalle Valo }
162277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_queue_main_work);
163277b024eSKalle Valo
mwifiex_queue_rx_work(struct mwifiex_adapter * adapter)164277b024eSKalle Valo static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter)
165277b024eSKalle Valo {
1668a7f9fd8SBrian Norris spin_lock_bh(&adapter->rx_proc_lock);
167277b024eSKalle Valo if (adapter->rx_processing) {
1688a7f9fd8SBrian Norris spin_unlock_bh(&adapter->rx_proc_lock);
169277b024eSKalle Valo } else {
1708a7f9fd8SBrian Norris spin_unlock_bh(&adapter->rx_proc_lock);
171277b024eSKalle Valo queue_work(adapter->rx_workqueue, &adapter->rx_work);
172277b024eSKalle Valo }
173277b024eSKalle Valo }
174277b024eSKalle Valo
mwifiex_process_rx(struct mwifiex_adapter * adapter)175277b024eSKalle Valo static int mwifiex_process_rx(struct mwifiex_adapter *adapter)
176277b024eSKalle Valo {
177277b024eSKalle Valo struct sk_buff *skb;
178277b024eSKalle Valo struct mwifiex_rxinfo *rx_info;
179277b024eSKalle Valo
1808a7f9fd8SBrian Norris spin_lock_bh(&adapter->rx_proc_lock);
181277b024eSKalle Valo if (adapter->rx_processing || adapter->rx_locked) {
1828a7f9fd8SBrian Norris spin_unlock_bh(&adapter->rx_proc_lock);
183277b024eSKalle Valo goto exit_rx_proc;
184277b024eSKalle Valo } else {
185277b024eSKalle Valo adapter->rx_processing = true;
1868a7f9fd8SBrian Norris spin_unlock_bh(&adapter->rx_proc_lock);
187277b024eSKalle Valo }
188277b024eSKalle Valo
189277b024eSKalle Valo /* Check for Rx data */
190277b024eSKalle Valo while ((skb = skb_dequeue(&adapter->rx_data_q))) {
191277b024eSKalle Valo atomic_dec(&adapter->rx_pending);
192277b024eSKalle Valo if ((adapter->delay_main_work ||
193277b024eSKalle Valo adapter->iface_type == MWIFIEX_USB) &&
194277b024eSKalle Valo (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) {
195277b024eSKalle Valo if (adapter->if_ops.submit_rem_rx_urbs)
196277b024eSKalle Valo adapter->if_ops.submit_rem_rx_urbs(adapter);
197277b024eSKalle Valo adapter->delay_main_work = false;
198277b024eSKalle Valo mwifiex_queue_main_work(adapter);
199277b024eSKalle Valo }
200277b024eSKalle Valo rx_info = MWIFIEX_SKB_RXCB(skb);
201277b024eSKalle Valo if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) {
202277b024eSKalle Valo if (adapter->if_ops.deaggr_pkt)
203277b024eSKalle Valo adapter->if_ops.deaggr_pkt(adapter, skb);
204277b024eSKalle Valo dev_kfree_skb_any(skb);
205277b024eSKalle Valo } else {
206277b024eSKalle Valo mwifiex_handle_rx_packet(adapter, skb);
207277b024eSKalle Valo }
208277b024eSKalle Valo }
2098a7f9fd8SBrian Norris spin_lock_bh(&adapter->rx_proc_lock);
210277b024eSKalle Valo adapter->rx_processing = false;
2118a7f9fd8SBrian Norris spin_unlock_bh(&adapter->rx_proc_lock);
212277b024eSKalle Valo
213277b024eSKalle Valo exit_rx_proc:
214277b024eSKalle Valo return 0;
215277b024eSKalle Valo }
216277b024eSKalle Valo
maybe_quirk_fw_disable_ds(struct mwifiex_adapter * adapter)217939b571aSJonas Dreßler static void maybe_quirk_fw_disable_ds(struct mwifiex_adapter *adapter)
218939b571aSJonas Dreßler {
219939b571aSJonas Dreßler struct mwifiex_private *priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
220939b571aSJonas Dreßler struct mwifiex_ver_ext ver_ext;
221939b571aSJonas Dreßler
222939b571aSJonas Dreßler if (test_and_set_bit(MWIFIEX_IS_REQUESTING_FW_VEREXT, &adapter->work_flags))
223939b571aSJonas Dreßler return;
224939b571aSJonas Dreßler
225939b571aSJonas Dreßler memset(&ver_ext, 0, sizeof(ver_ext));
226939b571aSJonas Dreßler ver_ext.version_str_sel = 1;
227939b571aSJonas Dreßler if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT,
228939b571aSJonas Dreßler HostCmd_ACT_GEN_GET, 0, &ver_ext, false)) {
229939b571aSJonas Dreßler mwifiex_dbg(priv->adapter, MSG,
230939b571aSJonas Dreßler "Checking hardware revision failed.\n");
231939b571aSJonas Dreßler }
232939b571aSJonas Dreßler }
233939b571aSJonas Dreßler
234277b024eSKalle Valo /*
235277b024eSKalle Valo * The main process.
236277b024eSKalle Valo *
237277b024eSKalle Valo * This function is the main procedure of the driver and handles various driver
238277b024eSKalle Valo * operations. It runs in a loop and provides the core functionalities.
239277b024eSKalle Valo *
240277b024eSKalle Valo * The main responsibilities of this function are -
241277b024eSKalle Valo * - Ensure concurrency control
242277b024eSKalle Valo * - Handle pending interrupts and call interrupt handlers
243277b024eSKalle Valo * - Wake up the card if required
244277b024eSKalle Valo * - Handle command responses and call response handlers
245277b024eSKalle Valo * - Handle events and call event handlers
246277b024eSKalle Valo * - Execute pending commands
247277b024eSKalle Valo * - Transmit pending data packets
248277b024eSKalle Valo */
mwifiex_main_process(struct mwifiex_adapter * adapter)249277b024eSKalle Valo int mwifiex_main_process(struct mwifiex_adapter *adapter)
250277b024eSKalle Valo {
251277b024eSKalle Valo int ret = 0;
252277b024eSKalle Valo unsigned long flags;
253277b024eSKalle Valo
254277b024eSKalle Valo spin_lock_irqsave(&adapter->main_proc_lock, flags);
255277b024eSKalle Valo
256277b024eSKalle Valo /* Check if already processing */
257277b024eSKalle Valo if (adapter->mwifiex_processing || adapter->main_locked) {
258277b024eSKalle Valo adapter->more_task_flag = true;
259277b024eSKalle Valo spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
2605bf15e3fSXinming Hu return 0;
261277b024eSKalle Valo } else {
262277b024eSKalle Valo adapter->mwifiex_processing = true;
263277b024eSKalle Valo spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
264277b024eSKalle Valo }
265277b024eSKalle Valo process_start:
266277b024eSKalle Valo do {
2675bf15e3fSXinming Hu if (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)
268277b024eSKalle Valo break;
269277b024eSKalle Valo
270277b024eSKalle Valo /* For non-USB interfaces, If we process interrupts first, it
271277b024eSKalle Valo * would increase RX pending even further. Avoid this by
272277b024eSKalle Valo * checking if rx_pending has crossed high threshold and
273277b024eSKalle Valo * schedule rx work queue and then process interrupts.
274277b024eSKalle Valo * For USB interface, there are no interrupts. We already have
275277b024eSKalle Valo * HIGH_RX_PENDING check in usb.c
276277b024eSKalle Valo */
277277b024eSKalle Valo if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING &&
278277b024eSKalle Valo adapter->iface_type != MWIFIEX_USB) {
279277b024eSKalle Valo adapter->delay_main_work = true;
280277b024eSKalle Valo mwifiex_queue_rx_work(adapter);
281277b024eSKalle Valo break;
282277b024eSKalle Valo }
283277b024eSKalle Valo
284277b024eSKalle Valo /* Handle pending interrupt if any */
285277b024eSKalle Valo if (adapter->int_status) {
286277b024eSKalle Valo if (adapter->hs_activated)
287277b024eSKalle Valo mwifiex_process_hs_config(adapter);
288277b024eSKalle Valo if (adapter->if_ops.process_int_status)
289277b024eSKalle Valo adapter->if_ops.process_int_status(adapter);
290277b024eSKalle Valo }
291277b024eSKalle Valo
292277b024eSKalle Valo if (adapter->rx_work_enabled && adapter->data_received)
293277b024eSKalle Valo mwifiex_queue_rx_work(adapter);
294277b024eSKalle Valo
295277b024eSKalle Valo /* Need to wake up the card ? */
296277b024eSKalle Valo if ((adapter->ps_state == PS_STATE_SLEEP) &&
297277b024eSKalle Valo (adapter->pm_wakeup_card_req &&
298277b024eSKalle Valo !adapter->pm_wakeup_fw_try) &&
299277b024eSKalle Valo (is_command_pending(adapter) ||
300277b024eSKalle Valo !skb_queue_empty(&adapter->tx_data_q) ||
301277b024eSKalle Valo !mwifiex_bypass_txlist_empty(adapter) ||
302277b024eSKalle Valo !mwifiex_wmm_lists_empty(adapter))) {
303277b024eSKalle Valo adapter->pm_wakeup_fw_try = true;
304277b024eSKalle Valo mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3));
305277b024eSKalle Valo adapter->if_ops.wakeup(adapter);
306277b024eSKalle Valo continue;
307277b024eSKalle Valo }
308277b024eSKalle Valo
309277b024eSKalle Valo if (IS_CARD_RX_RCVD(adapter)) {
310277b024eSKalle Valo adapter->data_received = false;
311277b024eSKalle Valo adapter->pm_wakeup_fw_try = false;
312277b024eSKalle Valo del_timer(&adapter->wakeup_timer);
313277b024eSKalle Valo if (adapter->ps_state == PS_STATE_SLEEP)
314277b024eSKalle Valo adapter->ps_state = PS_STATE_AWAKE;
315277b024eSKalle Valo } else {
316277b024eSKalle Valo /* We have tried to wakeup the card already */
317277b024eSKalle Valo if (adapter->pm_wakeup_fw_try)
318277b024eSKalle Valo break;
31967120768SShengzhen Li if (adapter->ps_state == PS_STATE_PRE_SLEEP)
32067120768SShengzhen Li mwifiex_check_ps_cond(adapter);
32167120768SShengzhen Li
322277b024eSKalle Valo if (adapter->ps_state != PS_STATE_AWAKE)
323277b024eSKalle Valo break;
324277b024eSKalle Valo if (adapter->tx_lock_flag) {
325277b024eSKalle Valo if (adapter->iface_type == MWIFIEX_USB) {
326277b024eSKalle Valo if (!adapter->usb_mc_setup)
327277b024eSKalle Valo break;
328277b024eSKalle Valo } else
329277b024eSKalle Valo break;
330277b024eSKalle Valo }
331277b024eSKalle Valo
332277b024eSKalle Valo if ((!adapter->scan_chan_gap_enabled &&
333277b024eSKalle Valo adapter->scan_processing) || adapter->data_sent ||
334277b024eSKalle Valo mwifiex_is_tdls_chan_switching
335277b024eSKalle Valo (mwifiex_get_priv(adapter,
336277b024eSKalle Valo MWIFIEX_BSS_ROLE_STA)) ||
337277b024eSKalle Valo (mwifiex_wmm_lists_empty(adapter) &&
338277b024eSKalle Valo mwifiex_bypass_txlist_empty(adapter) &&
339277b024eSKalle Valo skb_queue_empty(&adapter->tx_data_q))) {
340277b024eSKalle Valo if (adapter->cmd_sent || adapter->curr_cmd ||
341277b024eSKalle Valo !mwifiex_is_send_cmd_allowed
342277b024eSKalle Valo (mwifiex_get_priv(adapter,
343277b024eSKalle Valo MWIFIEX_BSS_ROLE_STA)) ||
344277b024eSKalle Valo (!is_command_pending(adapter)))
345277b024eSKalle Valo break;
346277b024eSKalle Valo }
347277b024eSKalle Valo }
348277b024eSKalle Valo
349277b024eSKalle Valo /* Check for event */
350277b024eSKalle Valo if (adapter->event_received) {
351277b024eSKalle Valo adapter->event_received = false;
352277b024eSKalle Valo mwifiex_process_event(adapter);
353277b024eSKalle Valo }
354277b024eSKalle Valo
355277b024eSKalle Valo /* Check for Cmd Resp */
356277b024eSKalle Valo if (adapter->cmd_resp_received) {
357277b024eSKalle Valo adapter->cmd_resp_received = false;
358277b024eSKalle Valo mwifiex_process_cmdresp(adapter);
359277b024eSKalle Valo
360277b024eSKalle Valo /* call mwifiex back when init_fw is done */
361277b024eSKalle Valo if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) {
362277b024eSKalle Valo adapter->hw_status = MWIFIEX_HW_STATUS_READY;
363277b024eSKalle Valo mwifiex_init_fw_complete(adapter);
364939b571aSJonas Dreßler maybe_quirk_fw_disable_ds(adapter);
365277b024eSKalle Valo }
366277b024eSKalle Valo }
367277b024eSKalle Valo
368277b024eSKalle Valo /* Check if we need to confirm Sleep Request
369277b024eSKalle Valo received previously */
37067120768SShengzhen Li if (adapter->ps_state == PS_STATE_PRE_SLEEP)
371277b024eSKalle Valo mwifiex_check_ps_cond(adapter);
372277b024eSKalle Valo
373277b024eSKalle Valo /* * The ps_state may have been changed during processing of
374277b024eSKalle Valo * Sleep Request event.
375277b024eSKalle Valo */
376277b024eSKalle Valo if ((adapter->ps_state == PS_STATE_SLEEP) ||
377277b024eSKalle Valo (adapter->ps_state == PS_STATE_PRE_SLEEP) ||
378277b024eSKalle Valo (adapter->ps_state == PS_STATE_SLEEP_CFM)) {
379277b024eSKalle Valo continue;
380277b024eSKalle Valo }
381277b024eSKalle Valo
382277b024eSKalle Valo if (adapter->tx_lock_flag) {
383277b024eSKalle Valo if (adapter->iface_type == MWIFIEX_USB) {
384277b024eSKalle Valo if (!adapter->usb_mc_setup)
385277b024eSKalle Valo continue;
386277b024eSKalle Valo } else
387277b024eSKalle Valo continue;
388277b024eSKalle Valo }
389277b024eSKalle Valo
390277b024eSKalle Valo if (!adapter->cmd_sent && !adapter->curr_cmd &&
391277b024eSKalle Valo mwifiex_is_send_cmd_allowed
392277b024eSKalle Valo (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
393277b024eSKalle Valo if (mwifiex_exec_next_cmd(adapter) == -1) {
394277b024eSKalle Valo ret = -1;
395277b024eSKalle Valo break;
396277b024eSKalle Valo }
397277b024eSKalle Valo }
398277b024eSKalle Valo
399277b024eSKalle Valo /** If USB Multi channel setup ongoing,
400277b024eSKalle Valo * wait for ready to tx data.
401277b024eSKalle Valo */
402277b024eSKalle Valo if (adapter->iface_type == MWIFIEX_USB &&
403277b024eSKalle Valo adapter->usb_mc_setup)
404277b024eSKalle Valo continue;
405277b024eSKalle Valo
406277b024eSKalle Valo if ((adapter->scan_chan_gap_enabled ||
407277b024eSKalle Valo !adapter->scan_processing) &&
408277b024eSKalle Valo !adapter->data_sent &&
409277b024eSKalle Valo !skb_queue_empty(&adapter->tx_data_q)) {
4105943a864SJonas Dreßler if (adapter->hs_activated_manually) {
4115943a864SJonas Dreßler mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY),
4125943a864SJonas Dreßler MWIFIEX_ASYNC_CMD);
4135943a864SJonas Dreßler adapter->hs_activated_manually = false;
4145943a864SJonas Dreßler }
4155943a864SJonas Dreßler
416277b024eSKalle Valo mwifiex_process_tx_queue(adapter);
417277b024eSKalle Valo if (adapter->hs_activated) {
418fc3a2fcaSGanapathi Bhat clear_bit(MWIFIEX_IS_HS_CONFIGURED,
419fc3a2fcaSGanapathi Bhat &adapter->work_flags);
420277b024eSKalle Valo mwifiex_hs_activated_event
421277b024eSKalle Valo (mwifiex_get_priv
422277b024eSKalle Valo (adapter, MWIFIEX_BSS_ROLE_ANY),
423277b024eSKalle Valo false);
424277b024eSKalle Valo }
425277b024eSKalle Valo }
426277b024eSKalle Valo
427277b024eSKalle Valo if ((adapter->scan_chan_gap_enabled ||
428277b024eSKalle Valo !adapter->scan_processing) &&
429277b024eSKalle Valo !adapter->data_sent &&
430277b024eSKalle Valo !mwifiex_bypass_txlist_empty(adapter) &&
431277b024eSKalle Valo !mwifiex_is_tdls_chan_switching
432277b024eSKalle Valo (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
4335943a864SJonas Dreßler if (adapter->hs_activated_manually) {
4345943a864SJonas Dreßler mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY),
4355943a864SJonas Dreßler MWIFIEX_ASYNC_CMD);
4365943a864SJonas Dreßler adapter->hs_activated_manually = false;
4375943a864SJonas Dreßler }
4385943a864SJonas Dreßler
439277b024eSKalle Valo mwifiex_process_bypass_tx(adapter);
440277b024eSKalle Valo if (adapter->hs_activated) {
441fc3a2fcaSGanapathi Bhat clear_bit(MWIFIEX_IS_HS_CONFIGURED,
442fc3a2fcaSGanapathi Bhat &adapter->work_flags);
443277b024eSKalle Valo mwifiex_hs_activated_event
444277b024eSKalle Valo (mwifiex_get_priv
445277b024eSKalle Valo (adapter, MWIFIEX_BSS_ROLE_ANY),
446277b024eSKalle Valo false);
447277b024eSKalle Valo }
448277b024eSKalle Valo }
449277b024eSKalle Valo
450277b024eSKalle Valo if ((adapter->scan_chan_gap_enabled ||
451277b024eSKalle Valo !adapter->scan_processing) &&
452277b024eSKalle Valo !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter) &&
453277b024eSKalle Valo !mwifiex_is_tdls_chan_switching
454277b024eSKalle Valo (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
4555943a864SJonas Dreßler if (adapter->hs_activated_manually) {
4565943a864SJonas Dreßler mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY),
4575943a864SJonas Dreßler MWIFIEX_ASYNC_CMD);
4585943a864SJonas Dreßler adapter->hs_activated_manually = false;
4595943a864SJonas Dreßler }
4605943a864SJonas Dreßler
461277b024eSKalle Valo mwifiex_wmm_process_tx(adapter);
462277b024eSKalle Valo if (adapter->hs_activated) {
463fc3a2fcaSGanapathi Bhat clear_bit(MWIFIEX_IS_HS_CONFIGURED,
464fc3a2fcaSGanapathi Bhat &adapter->work_flags);
465277b024eSKalle Valo mwifiex_hs_activated_event
466277b024eSKalle Valo (mwifiex_get_priv
467277b024eSKalle Valo (adapter, MWIFIEX_BSS_ROLE_ANY),
468277b024eSKalle Valo false);
469277b024eSKalle Valo }
470277b024eSKalle Valo }
471277b024eSKalle Valo
472277b024eSKalle Valo if (adapter->delay_null_pkt && !adapter->cmd_sent &&
473277b024eSKalle Valo !adapter->curr_cmd && !is_command_pending(adapter) &&
474277b024eSKalle Valo (mwifiex_wmm_lists_empty(adapter) &&
475277b024eSKalle Valo mwifiex_bypass_txlist_empty(adapter) &&
476277b024eSKalle Valo skb_queue_empty(&adapter->tx_data_q))) {
477277b024eSKalle Valo if (!mwifiex_send_null_packet
478277b024eSKalle Valo (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
479277b024eSKalle Valo MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET |
480277b024eSKalle Valo MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) {
481277b024eSKalle Valo adapter->delay_null_pkt = false;
482277b024eSKalle Valo adapter->ps_state = PS_STATE_SLEEP;
483277b024eSKalle Valo }
484277b024eSKalle Valo break;
485277b024eSKalle Valo }
486277b024eSKalle Valo } while (true);
487277b024eSKalle Valo
488277b024eSKalle Valo spin_lock_irqsave(&adapter->main_proc_lock, flags);
489277b024eSKalle Valo if (adapter->more_task_flag) {
490277b024eSKalle Valo adapter->more_task_flag = false;
491277b024eSKalle Valo spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
492277b024eSKalle Valo goto process_start;
493277b024eSKalle Valo }
494277b024eSKalle Valo adapter->mwifiex_processing = false;
495277b024eSKalle Valo spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
496277b024eSKalle Valo
497277b024eSKalle Valo return ret;
498277b024eSKalle Valo }
499277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_main_process);
500277b024eSKalle Valo
501277b024eSKalle Valo /*
502277b024eSKalle Valo * This function frees the adapter structure.
503277b024eSKalle Valo *
504277b024eSKalle Valo * Additionally, this closes the netlink socket, frees the timers
505277b024eSKalle Valo * and private structures.
506277b024eSKalle Valo */
mwifiex_free_adapter(struct mwifiex_adapter * adapter)507277b024eSKalle Valo static void mwifiex_free_adapter(struct mwifiex_adapter *adapter)
508277b024eSKalle Valo {
509277b024eSKalle Valo if (!adapter) {
510277b024eSKalle Valo pr_err("%s: adapter is NULL\n", __func__);
511277b024eSKalle Valo return;
512277b024eSKalle Valo }
513277b024eSKalle Valo
514277b024eSKalle Valo mwifiex_unregister(adapter);
515277b024eSKalle Valo pr_debug("info: %s: free adapter\n", __func__);
516277b024eSKalle Valo }
517277b024eSKalle Valo
518277b024eSKalle Valo /*
519277b024eSKalle Valo * This function cancels all works in the queue and destroys
520277b024eSKalle Valo * the main workqueue.
521277b024eSKalle Valo */
mwifiex_terminate_workqueue(struct mwifiex_adapter * adapter)522277b024eSKalle Valo static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
523277b024eSKalle Valo {
5244c5dae59SAmitkumar Karwar if (adapter->workqueue) {
525277b024eSKalle Valo destroy_workqueue(adapter->workqueue);
526277b024eSKalle Valo adapter->workqueue = NULL;
5274c5dae59SAmitkumar Karwar }
528277b024eSKalle Valo
529277b024eSKalle Valo if (adapter->rx_workqueue) {
530277b024eSKalle Valo destroy_workqueue(adapter->rx_workqueue);
531277b024eSKalle Valo adapter->rx_workqueue = NULL;
532277b024eSKalle Valo }
533277b024eSKalle Valo }
534277b024eSKalle Valo
535277b024eSKalle Valo /*
536277b024eSKalle Valo * This function gets firmware and initializes it.
537277b024eSKalle Valo *
538277b024eSKalle Valo * The main initialization steps followed are -
539277b024eSKalle Valo * - Download the correct firmware to card
540277b024eSKalle Valo * - Issue the init commands to firmware
541277b024eSKalle Valo */
_mwifiex_fw_dpc(const struct firmware * firmware,void * context)542755b37c9SBrian Norris static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context)
543277b024eSKalle Valo {
544277b024eSKalle Valo int ret;
545277b024eSKalle Valo char fmt[64];
546277b024eSKalle Valo struct mwifiex_adapter *adapter = context;
547277b024eSKalle Valo struct mwifiex_fw_image fw;
548277b024eSKalle Valo bool init_failed = false;
549277b024eSKalle Valo struct wireless_dev *wdev;
5504a79aa17SBrian Norris struct completion *fw_done = adapter->fw_done;
551277b024eSKalle Valo
552277b024eSKalle Valo if (!firmware) {
553277b024eSKalle Valo mwifiex_dbg(adapter, ERROR,
554277b024eSKalle Valo "Failed to get firmware %s\n", adapter->fw_name);
555277b024eSKalle Valo goto err_dnld_fw;
556277b024eSKalle Valo }
557277b024eSKalle Valo
558277b024eSKalle Valo memset(&fw, 0, sizeof(struct mwifiex_fw_image));
559277b024eSKalle Valo adapter->firmware = firmware;
560277b024eSKalle Valo fw.fw_buf = (u8 *) adapter->firmware->data;
561277b024eSKalle Valo fw.fw_len = adapter->firmware->size;
562277b024eSKalle Valo
56365c71efeSWei-Ning Huang if (adapter->if_ops.dnld_fw) {
564277b024eSKalle Valo ret = adapter->if_ops.dnld_fw(adapter, &fw);
56565c71efeSWei-Ning Huang } else {
566277b024eSKalle Valo ret = mwifiex_dnld_fw(adapter, &fw);
56765c71efeSWei-Ning Huang }
56865c71efeSWei-Ning Huang
569277b024eSKalle Valo if (ret == -1)
570277b024eSKalle Valo goto err_dnld_fw;
571277b024eSKalle Valo
572277b024eSKalle Valo mwifiex_dbg(adapter, MSG, "WLAN FW is active\n");
573277b024eSKalle Valo
574277b024eSKalle Valo if (cal_data_cfg) {
575277b024eSKalle Valo if ((request_firmware(&adapter->cal_data, cal_data_cfg,
576277b024eSKalle Valo adapter->dev)) < 0)
577277b024eSKalle Valo mwifiex_dbg(adapter, ERROR,
578277b024eSKalle Valo "Cal data request_firmware() failed\n");
579277b024eSKalle Valo }
580277b024eSKalle Valo
581277b024eSKalle Valo /* enable host interrupt after fw dnld is successful */
582277b024eSKalle Valo if (adapter->if_ops.enable_int) {
583277b024eSKalle Valo if (adapter->if_ops.enable_int(adapter))
584277b024eSKalle Valo goto err_dnld_fw;
585277b024eSKalle Valo }
586277b024eSKalle Valo
587277b024eSKalle Valo adapter->init_wait_q_woken = false;
588277b024eSKalle Valo ret = mwifiex_init_fw(adapter);
589277b024eSKalle Valo if (ret == -1) {
590277b024eSKalle Valo goto err_init_fw;
591277b024eSKalle Valo } else if (!ret) {
592277b024eSKalle Valo adapter->hw_status = MWIFIEX_HW_STATUS_READY;
593277b024eSKalle Valo goto done;
594277b024eSKalle Valo }
595277b024eSKalle Valo /* Wait for mwifiex_init to complete */
596cf5383b0SXinming Hu if (!adapter->mfg_mode) {
597277b024eSKalle Valo wait_event_interruptible(adapter->init_wait_q,
598277b024eSKalle Valo adapter->init_wait_q_woken);
599277b024eSKalle Valo if (adapter->hw_status != MWIFIEX_HW_STATUS_READY)
600277b024eSKalle Valo goto err_init_fw;
601cf5383b0SXinming Hu }
602277b024eSKalle Valo
6034c5dae59SAmitkumar Karwar if (!adapter->wiphy) {
604277b024eSKalle Valo if (mwifiex_register_cfg80211(adapter)) {
605277b024eSKalle Valo mwifiex_dbg(adapter, ERROR,
606277b024eSKalle Valo "cannot register with cfg80211\n");
607277b024eSKalle Valo goto err_init_fw;
608277b024eSKalle Valo }
6094c5dae59SAmitkumar Karwar }
610277b024eSKalle Valo
611277b024eSKalle Valo if (mwifiex_init_channel_scan_gap(adapter)) {
612277b024eSKalle Valo mwifiex_dbg(adapter, ERROR,
613277b024eSKalle Valo "could not init channel stats table\n");
614c253a62dSBrian Norris goto err_init_chan_scan;
615277b024eSKalle Valo }
616277b024eSKalle Valo
617277b024eSKalle Valo if (driver_mode) {
618277b024eSKalle Valo driver_mode &= MWIFIEX_DRIVER_MODE_BITMASK;
619277b024eSKalle Valo driver_mode |= MWIFIEX_DRIVER_MODE_STA;
620277b024eSKalle Valo }
621277b024eSKalle Valo
622277b024eSKalle Valo rtnl_lock();
623a05829a7SJohannes Berg wiphy_lock(adapter->wiphy);
624277b024eSKalle Valo /* Create station interface by default */
625277b024eSKalle Valo wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM,
626818a986eSJohannes Berg NL80211_IFTYPE_STATION, NULL);
627277b024eSKalle Valo if (IS_ERR(wdev)) {
628277b024eSKalle Valo mwifiex_dbg(adapter, ERROR,
629277b024eSKalle Valo "cannot create default STA interface\n");
630a05829a7SJohannes Berg wiphy_unlock(adapter->wiphy);
631277b024eSKalle Valo rtnl_unlock();
632277b024eSKalle Valo goto err_add_intf;
633277b024eSKalle Valo }
634277b024eSKalle Valo
635277b024eSKalle Valo if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) {
636277b024eSKalle Valo wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM,
637818a986eSJohannes Berg NL80211_IFTYPE_AP, NULL);
638277b024eSKalle Valo if (IS_ERR(wdev)) {
639277b024eSKalle Valo mwifiex_dbg(adapter, ERROR,
640277b024eSKalle Valo "cannot create AP interface\n");
641a05829a7SJohannes Berg wiphy_unlock(adapter->wiphy);
642277b024eSKalle Valo rtnl_unlock();
643277b024eSKalle Valo goto err_add_intf;
644277b024eSKalle Valo }
645277b024eSKalle Valo }
646277b024eSKalle Valo
647277b024eSKalle Valo if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) {
648277b024eSKalle Valo wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM,
649818a986eSJohannes Berg NL80211_IFTYPE_P2P_CLIENT, NULL);
650277b024eSKalle Valo if (IS_ERR(wdev)) {
651277b024eSKalle Valo mwifiex_dbg(adapter, ERROR,
652277b024eSKalle Valo "cannot create p2p client interface\n");
653a05829a7SJohannes Berg wiphy_unlock(adapter->wiphy);
654277b024eSKalle Valo rtnl_unlock();
655277b024eSKalle Valo goto err_add_intf;
656277b024eSKalle Valo }
657277b024eSKalle Valo }
658a05829a7SJohannes Berg wiphy_unlock(adapter->wiphy);
659277b024eSKalle Valo rtnl_unlock();
660277b024eSKalle Valo
661277b024eSKalle Valo mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
662277b024eSKalle Valo mwifiex_dbg(adapter, MSG, "driver_version = %s\n", fmt);
663cdb2256fSUlf Hansson adapter->is_up = true;
664277b024eSKalle Valo goto done;
665277b024eSKalle Valo
666277b024eSKalle Valo err_add_intf:
667fb9e67beSBrian Norris vfree(adapter->chan_stats);
668c253a62dSBrian Norris err_init_chan_scan:
669277b024eSKalle Valo wiphy_unregister(adapter->wiphy);
670277b024eSKalle Valo wiphy_free(adapter->wiphy);
671277b024eSKalle Valo err_init_fw:
672277b024eSKalle Valo if (adapter->if_ops.disable_int)
673277b024eSKalle Valo adapter->if_ops.disable_int(adapter);
674277b024eSKalle Valo err_dnld_fw:
675277b024eSKalle Valo mwifiex_dbg(adapter, ERROR,
676277b024eSKalle Valo "info: %s: unregister device\n", __func__);
677277b024eSKalle Valo if (adapter->if_ops.unregister_dev)
678277b024eSKalle Valo adapter->if_ops.unregister_dev(adapter);
679277b024eSKalle Valo
680fc3a2fcaSGanapathi Bhat set_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
681277b024eSKalle Valo mwifiex_terminate_workqueue(adapter);
6825bf15e3fSXinming Hu
6835bf15e3fSXinming Hu if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
6845bf15e3fSXinming Hu pr_debug("info: %s: shutdown mwifiex\n", __func__);
6855bf15e3fSXinming Hu mwifiex_shutdown_drv(adapter);
686ce32d1d8SBrian Norris mwifiex_free_cmd_buffers(adapter);
6875bf15e3fSXinming Hu }
6885bf15e3fSXinming Hu
689277b024eSKalle Valo init_failed = true;
690277b024eSKalle Valo done:
691277b024eSKalle Valo if (adapter->cal_data) {
692277b024eSKalle Valo release_firmware(adapter->cal_data);
693277b024eSKalle Valo adapter->cal_data = NULL;
694277b024eSKalle Valo }
695277b024eSKalle Valo if (adapter->firmware) {
696277b024eSKalle Valo release_firmware(adapter->firmware);
697277b024eSKalle Valo adapter->firmware = NULL;
698277b024eSKalle Valo }
699f101d964SJeffy Chen if (init_failed) {
700f101d964SJeffy Chen if (adapter->irq_wakeup >= 0)
701f101d964SJeffy Chen device_init_wakeup(adapter->dev, false);
702277b024eSKalle Valo mwifiex_free_adapter(adapter);
703f101d964SJeffy Chen }
7044a79aa17SBrian Norris /* Tell all current and future waiters we're finished */
7054a79aa17SBrian Norris complete_all(fw_done);
706755b37c9SBrian Norris
707755b37c9SBrian Norris return init_failed ? -EIO : 0;
708755b37c9SBrian Norris }
709755b37c9SBrian Norris
mwifiex_fw_dpc(const struct firmware * firmware,void * context)710755b37c9SBrian Norris static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
711755b37c9SBrian Norris {
712755b37c9SBrian Norris _mwifiex_fw_dpc(firmware, context);
713277b024eSKalle Valo }
714277b024eSKalle Valo
715277b024eSKalle Valo /*
716755b37c9SBrian Norris * This function gets the firmware and (if called asynchronously) kicks off the
717755b37c9SBrian Norris * HW init when done.
718277b024eSKalle Valo */
mwifiex_init_hw_fw(struct mwifiex_adapter * adapter,bool req_fw_nowait)7194c5dae59SAmitkumar Karwar static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter,
7204c5dae59SAmitkumar Karwar bool req_fw_nowait)
721277b024eSKalle Valo {
722277b024eSKalle Valo int ret;
723277b024eSKalle Valo
724cf5383b0SXinming Hu /* Override default firmware with manufacturing one if
725cf5383b0SXinming Hu * manufacturing mode is enabled
726cf5383b0SXinming Hu */
727caf9ead2SDmitry Antipov if (mfg_mode)
728caf9ead2SDmitry Antipov strscpy(adapter->fw_name, MFG_FIRMWARE,
729caf9ead2SDmitry Antipov sizeof(adapter->fw_name));
7304c5dae59SAmitkumar Karwar
7314c5dae59SAmitkumar Karwar if (req_fw_nowait) {
732277b024eSKalle Valo ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name,
733277b024eSKalle Valo adapter->dev, GFP_KERNEL, adapter,
734277b024eSKalle Valo mwifiex_fw_dpc);
7354c5dae59SAmitkumar Karwar } else {
7364c5dae59SAmitkumar Karwar ret = request_firmware(&adapter->firmware,
7374c5dae59SAmitkumar Karwar adapter->fw_name,
7384c5dae59SAmitkumar Karwar adapter->dev);
7394c5dae59SAmitkumar Karwar }
7404c5dae59SAmitkumar Karwar
741755b37c9SBrian Norris if (ret < 0)
742755b37c9SBrian Norris mwifiex_dbg(adapter, ERROR, "request_firmware%s error %d\n",
743755b37c9SBrian Norris req_fw_nowait ? "_nowait" : "", ret);
744277b024eSKalle Valo return ret;
745277b024eSKalle Valo }
746277b024eSKalle Valo
747277b024eSKalle Valo /*
748277b024eSKalle Valo * CFG802.11 network device handler for open.
749277b024eSKalle Valo *
750277b024eSKalle Valo * Starts the data queue.
751277b024eSKalle Valo */
752277b024eSKalle Valo static int
mwifiex_open(struct net_device * dev)753277b024eSKalle Valo mwifiex_open(struct net_device *dev)
754277b024eSKalle Valo {
755277b024eSKalle Valo netif_carrier_off(dev);
756277b024eSKalle Valo
757277b024eSKalle Valo return 0;
758277b024eSKalle Valo }
759277b024eSKalle Valo
760277b024eSKalle Valo /*
761277b024eSKalle Valo * CFG802.11 network device handler for close.
762277b024eSKalle Valo */
763277b024eSKalle Valo static int
mwifiex_close(struct net_device * dev)764277b024eSKalle Valo mwifiex_close(struct net_device *dev)
765277b024eSKalle Valo {
766277b024eSKalle Valo struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
767277b024eSKalle Valo
768277b024eSKalle Valo if (priv->scan_request) {
7691d76250bSAvraham Stern struct cfg80211_scan_info info = {
7701d76250bSAvraham Stern .aborted = true,
7711d76250bSAvraham Stern };
7721d76250bSAvraham Stern
773277b024eSKalle Valo mwifiex_dbg(priv->adapter, INFO,
774277b024eSKalle Valo "aborting scan on ndo_stop\n");
7751d76250bSAvraham Stern cfg80211_scan_done(priv->scan_request, &info);
776277b024eSKalle Valo priv->scan_request = NULL;
777277b024eSKalle Valo priv->scan_aborting = true;
778277b024eSKalle Valo }
779277b024eSKalle Valo
780eaf46b5fSXinming Hu if (priv->sched_scanning) {
781eaf46b5fSXinming Hu mwifiex_dbg(priv->adapter, INFO,
782eaf46b5fSXinming Hu "aborting bgscan on ndo_stop\n");
783eaf46b5fSXinming Hu mwifiex_stop_bg_scan(priv);
784b34939b9SArend Van Spriel cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0);
785eaf46b5fSXinming Hu }
786eaf46b5fSXinming Hu
787277b024eSKalle Valo return 0;
788277b024eSKalle Valo }
789277b024eSKalle Valo
790277b024eSKalle Valo static bool
mwifiex_bypass_tx_queue(struct mwifiex_private * priv,struct sk_buff * skb)791277b024eSKalle Valo mwifiex_bypass_tx_queue(struct mwifiex_private *priv,
792277b024eSKalle Valo struct sk_buff *skb)
793277b024eSKalle Valo {
794277b024eSKalle Valo struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
795277b024eSKalle Valo
796277b024eSKalle Valo if (ntohs(eth_hdr->h_proto) == ETH_P_PAE ||
797277b024eSKalle Valo mwifiex_is_skb_mgmt_frame(skb) ||
798277b024eSKalle Valo (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA &&
799277b024eSKalle Valo ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
800277b024eSKalle Valo (ntohs(eth_hdr->h_proto) == ETH_P_TDLS))) {
801277b024eSKalle Valo mwifiex_dbg(priv->adapter, DATA,
802277b024eSKalle Valo "bypass txqueue; eth type %#x, mgmt %d\n",
803277b024eSKalle Valo ntohs(eth_hdr->h_proto),
804277b024eSKalle Valo mwifiex_is_skb_mgmt_frame(skb));
805277b024eSKalle Valo return true;
806277b024eSKalle Valo }
807277b024eSKalle Valo
808277b024eSKalle Valo return false;
809277b024eSKalle Valo }
810277b024eSKalle Valo /*
811277b024eSKalle Valo * Add buffer into wmm tx queue and queue work to transmit it.
812277b024eSKalle Valo */
mwifiex_queue_tx_pkt(struct mwifiex_private * priv,struct sk_buff * skb)813277b024eSKalle Valo int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb)
814277b024eSKalle Valo {
815277b024eSKalle Valo struct netdev_queue *txq;
816277b024eSKalle Valo int index = mwifiex_1d_to_wmm_queue[skb->priority];
817277b024eSKalle Valo
818277b024eSKalle Valo if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) {
819277b024eSKalle Valo txq = netdev_get_tx_queue(priv->netdev, index);
820277b024eSKalle Valo if (!netif_tx_queue_stopped(txq)) {
821277b024eSKalle Valo netif_tx_stop_queue(txq);
822277b024eSKalle Valo mwifiex_dbg(priv->adapter, DATA,
823277b024eSKalle Valo "stop queue: %d\n", index);
824277b024eSKalle Valo }
825277b024eSKalle Valo }
826277b024eSKalle Valo
827277b024eSKalle Valo if (mwifiex_bypass_tx_queue(priv, skb)) {
828277b024eSKalle Valo atomic_inc(&priv->adapter->tx_pending);
829277b024eSKalle Valo atomic_inc(&priv->adapter->bypass_tx_pending);
830277b024eSKalle Valo mwifiex_wmm_add_buf_bypass_txqueue(priv, skb);
831277b024eSKalle Valo } else {
832277b024eSKalle Valo atomic_inc(&priv->adapter->tx_pending);
833277b024eSKalle Valo mwifiex_wmm_add_buf_txqueue(priv, skb);
834277b024eSKalle Valo }
835277b024eSKalle Valo
836277b024eSKalle Valo mwifiex_queue_main_work(priv->adapter);
837277b024eSKalle Valo
838277b024eSKalle Valo return 0;
839277b024eSKalle Valo }
840277b024eSKalle Valo
841277b024eSKalle Valo struct sk_buff *
mwifiex_clone_skb_for_tx_status(struct mwifiex_private * priv,struct sk_buff * skb,u8 flag,u64 * cookie)842277b024eSKalle Valo mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
843277b024eSKalle Valo struct sk_buff *skb, u8 flag, u64 *cookie)
844277b024eSKalle Valo {
845277b024eSKalle Valo struct sk_buff *orig_skb = skb;
846277b024eSKalle Valo struct mwifiex_txinfo *tx_info, *orig_tx_info;
847277b024eSKalle Valo
848277b024eSKalle Valo skb = skb_clone(skb, GFP_ATOMIC);
849277b024eSKalle Valo if (skb) {
850277b024eSKalle Valo int id;
851277b024eSKalle Valo
8528a7f9fd8SBrian Norris spin_lock_bh(&priv->ack_status_lock);
853277b024eSKalle Valo id = idr_alloc(&priv->ack_status_frames, orig_skb,
854ee548d4bSAmitkumar Karwar 1, 0x10, GFP_ATOMIC);
8558a7f9fd8SBrian Norris spin_unlock_bh(&priv->ack_status_lock);
856277b024eSKalle Valo
857277b024eSKalle Valo if (id >= 0) {
858277b024eSKalle Valo tx_info = MWIFIEX_SKB_TXCB(skb);
859277b024eSKalle Valo tx_info->ack_frame_id = id;
860277b024eSKalle Valo tx_info->flags |= flag;
861277b024eSKalle Valo orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb);
862277b024eSKalle Valo orig_tx_info->ack_frame_id = id;
863277b024eSKalle Valo orig_tx_info->flags |= flag;
864277b024eSKalle Valo
865277b024eSKalle Valo if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie)
866277b024eSKalle Valo orig_tx_info->cookie = *cookie;
867277b024eSKalle Valo
868277b024eSKalle Valo } else if (skb_shared(skb)) {
869277b024eSKalle Valo kfree_skb(orig_skb);
870277b024eSKalle Valo } else {
871277b024eSKalle Valo kfree_skb(skb);
872277b024eSKalle Valo skb = orig_skb;
873277b024eSKalle Valo }
874277b024eSKalle Valo } else {
875277b024eSKalle Valo /* couldn't clone -- lose tx status ... */
876277b024eSKalle Valo skb = orig_skb;
877277b024eSKalle Valo }
878277b024eSKalle Valo
879277b024eSKalle Valo return skb;
880277b024eSKalle Valo }
881277b024eSKalle Valo
882277b024eSKalle Valo /*
883277b024eSKalle Valo * CFG802.11 network device handler for data transmission.
884277b024eSKalle Valo */
885c126e199SLuc Van Oostenryck static netdev_tx_t
mwifiex_hard_start_xmit(struct sk_buff * skb,struct net_device * dev)886277b024eSKalle Valo mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
887277b024eSKalle Valo {
888277b024eSKalle Valo struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
889277b024eSKalle Valo struct sk_buff *new_skb;
890277b024eSKalle Valo struct mwifiex_txinfo *tx_info;
891277b024eSKalle Valo bool multicast;
892277b024eSKalle Valo
893277b024eSKalle Valo mwifiex_dbg(priv->adapter, DATA,
894277b024eSKalle Valo "data: %lu BSS(%d-%d): Data <= kernel\n",
895277b024eSKalle Valo jiffies, priv->bss_type, priv->bss_num);
896277b024eSKalle Valo
897fc3a2fcaSGanapathi Bhat if (test_bit(MWIFIEX_SURPRISE_REMOVED, &priv->adapter->work_flags)) {
898277b024eSKalle Valo kfree_skb(skb);
899277b024eSKalle Valo priv->stats.tx_dropped++;
900277b024eSKalle Valo return 0;
901277b024eSKalle Valo }
902277b024eSKalle Valo if (!skb->len || (skb->len > ETH_FRAME_LEN)) {
903277b024eSKalle Valo mwifiex_dbg(priv->adapter, ERROR,
904277b024eSKalle Valo "Tx: bad skb len %d\n", skb->len);
905277b024eSKalle Valo kfree_skb(skb);
906277b024eSKalle Valo priv->stats.tx_dropped++;
907277b024eSKalle Valo return 0;
908277b024eSKalle Valo }
909277b024eSKalle Valo if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) {
910277b024eSKalle Valo mwifiex_dbg(priv->adapter, DATA,
911277b024eSKalle Valo "data: Tx: insufficient skb headroom %d\n",
912277b024eSKalle Valo skb_headroom(skb));
913277b024eSKalle Valo /* Insufficient skb headroom - allocate a new skb */
914277b024eSKalle Valo new_skb =
915277b024eSKalle Valo skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
916277b024eSKalle Valo if (unlikely(!new_skb)) {
917277b024eSKalle Valo mwifiex_dbg(priv->adapter, ERROR,
918277b024eSKalle Valo "Tx: cannot alloca new_skb\n");
919277b024eSKalle Valo kfree_skb(skb);
920277b024eSKalle Valo priv->stats.tx_dropped++;
921277b024eSKalle Valo return 0;
922277b024eSKalle Valo }
923277b024eSKalle Valo kfree_skb(skb);
924277b024eSKalle Valo skb = new_skb;
925277b024eSKalle Valo mwifiex_dbg(priv->adapter, INFO,
926277b024eSKalle Valo "info: new skb headroomd %d\n",
927277b024eSKalle Valo skb_headroom(skb));
928277b024eSKalle Valo }
929277b024eSKalle Valo
930277b024eSKalle Valo tx_info = MWIFIEX_SKB_TXCB(skb);
931277b024eSKalle Valo memset(tx_info, 0, sizeof(*tx_info));
932277b024eSKalle Valo tx_info->bss_num = priv->bss_num;
933277b024eSKalle Valo tx_info->bss_type = priv->bss_type;
934277b024eSKalle Valo tx_info->pkt_len = skb->len;
935277b024eSKalle Valo
936277b024eSKalle Valo multicast = is_multicast_ether_addr(skb->data);
937277b024eSKalle Valo
938277b024eSKalle Valo if (unlikely(!multicast && skb->sk &&
939277b024eSKalle Valo skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS &&
940277b024eSKalle Valo priv->adapter->fw_api_ver == MWIFIEX_FW_V15))
941277b024eSKalle Valo skb = mwifiex_clone_skb_for_tx_status(priv,
942277b024eSKalle Valo skb,
943277b024eSKalle Valo MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL);
944277b024eSKalle Valo
945277b024eSKalle Valo /* Record the current time the packet was queued; used to
946277b024eSKalle Valo * determine the amount of time the packet was queued in
947277b024eSKalle Valo * the driver before it was sent to the firmware.
948277b024eSKalle Valo * The delay is then sent along with the packet to the
949277b024eSKalle Valo * firmware for aggregate delay calculation for stats and
950277b024eSKalle Valo * MSDU lifetime expiry.
951277b024eSKalle Valo */
952277b024eSKalle Valo __net_timestamp(skb);
953277b024eSKalle Valo
954277b024eSKalle Valo if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
955277b024eSKalle Valo priv->bss_type == MWIFIEX_BSS_TYPE_STA &&
956277b024eSKalle Valo !ether_addr_equal_unaligned(priv->cfg_bssid, skb->data)) {
957277b024eSKalle Valo if (priv->adapter->auto_tdls && priv->check_tdls_tx)
958277b024eSKalle Valo mwifiex_tdls_check_tx(priv, skb);
959277b024eSKalle Valo }
960277b024eSKalle Valo
961277b024eSKalle Valo mwifiex_queue_tx_pkt(priv, skb);
962277b024eSKalle Valo
963277b024eSKalle Valo return 0;
964277b024eSKalle Valo }
965277b024eSKalle Valo
mwifiex_set_mac_address(struct mwifiex_private * priv,struct net_device * dev,bool external,u8 * new_mac)9664ba28f93SXinming Hu int mwifiex_set_mac_address(struct mwifiex_private *priv,
967307857dbSXinming Hu struct net_device *dev, bool external,
968307857dbSXinming Hu u8 *new_mac)
969277b024eSKalle Valo {
970277b024eSKalle Valo int ret;
97186416468SXinming Hu u64 mac_addr, old_mac_addr;
972277b024eSKalle Valo
973307857dbSXinming Hu old_mac_addr = ether_addr_to_u64(priv->curr_addr);
974307857dbSXinming Hu
975307857dbSXinming Hu if (external) {
976307857dbSXinming Hu mac_addr = ether_addr_to_u64(new_mac);
977307857dbSXinming Hu } else {
978307857dbSXinming Hu /* Internal mac address change */
97986416468SXinming Hu if (priv->bss_type == MWIFIEX_BSS_TYPE_ANY)
9809187f4e8SPali Rohár return -EOPNOTSUPP;
9814ba28f93SXinming Hu
982307857dbSXinming Hu mac_addr = old_mac_addr;
98386416468SXinming Hu
9847afb94daSSharvari Harisangam if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) {
9854ba28f93SXinming Hu mac_addr |= BIT_ULL(MWIFIEX_MAC_LOCAL_ADMIN_BIT);
9867afb94daSSharvari Harisangam mac_addr += priv->bss_num;
9877afb94daSSharvari Harisangam } else if (priv->adapter->priv[0] != priv) {
98886416468SXinming Hu /* Set mac address based on bss_type/bss_num */
98986416468SXinming Hu mac_addr ^= BIT_ULL(priv->bss_type + 8);
99086416468SXinming Hu mac_addr += priv->bss_num;
99186416468SXinming Hu }
992307857dbSXinming Hu }
99386416468SXinming Hu
9944ba28f93SXinming Hu u64_to_ether_addr(mac_addr, priv->curr_addr);
995277b024eSKalle Valo
996277b024eSKalle Valo /* Send request to firmware */
997277b024eSKalle Valo ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS,
998277b024eSKalle Valo HostCmd_ACT_GEN_SET, 0, NULL, true);
999277b024eSKalle Valo
10004ba28f93SXinming Hu if (ret) {
100186416468SXinming Hu u64_to_ether_addr(old_mac_addr, priv->curr_addr);
1002277b024eSKalle Valo mwifiex_dbg(priv->adapter, ERROR,
1003277b024eSKalle Valo "set mac address failed: ret=%d\n", ret);
1004277b024eSKalle Valo return ret;
1005277b024eSKalle Valo }
1006277b024eSKalle Valo
1007fcb79f31SJakub Kicinski eth_hw_addr_set(dev, priv->curr_addr);
10084ba28f93SXinming Hu return 0;
10094ba28f93SXinming Hu }
10104ba28f93SXinming Hu
10114ba28f93SXinming Hu /* CFG802.11 network device handler for setting MAC address.
10124ba28f93SXinming Hu */
10134ba28f93SXinming Hu static int
mwifiex_ndo_set_mac_address(struct net_device * dev,void * addr)10144ba28f93SXinming Hu mwifiex_ndo_set_mac_address(struct net_device *dev, void *addr)
10154ba28f93SXinming Hu {
10164ba28f93SXinming Hu struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
10174ba28f93SXinming Hu struct sockaddr *hw_addr = addr;
10184ba28f93SXinming Hu
1019307857dbSXinming Hu return mwifiex_set_mac_address(priv, dev, true, hw_addr->sa_data);
10204ba28f93SXinming Hu }
10214ba28f93SXinming Hu
1022277b024eSKalle Valo /*
1023277b024eSKalle Valo * CFG802.11 network device handler for setting multicast list.
1024277b024eSKalle Valo */
mwifiex_set_multicast_list(struct net_device * dev)1025277b024eSKalle Valo static void mwifiex_set_multicast_list(struct net_device *dev)
1026277b024eSKalle Valo {
1027277b024eSKalle Valo struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
1028277b024eSKalle Valo struct mwifiex_multicast_list mcast_list;
1029277b024eSKalle Valo
1030277b024eSKalle Valo if (dev->flags & IFF_PROMISC) {
1031277b024eSKalle Valo mcast_list.mode = MWIFIEX_PROMISC_MODE;
1032277b024eSKalle Valo } else if (dev->flags & IFF_ALLMULTI ||
1033277b024eSKalle Valo netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) {
1034277b024eSKalle Valo mcast_list.mode = MWIFIEX_ALL_MULTI_MODE;
1035277b024eSKalle Valo } else {
1036277b024eSKalle Valo mcast_list.mode = MWIFIEX_MULTICAST_MODE;
1037277b024eSKalle Valo mcast_list.num_multicast_addr =
1038277b024eSKalle Valo mwifiex_copy_mcast_addr(&mcast_list, dev);
1039277b024eSKalle Valo }
1040277b024eSKalle Valo mwifiex_request_set_multicast_list(priv, &mcast_list);
1041277b024eSKalle Valo }
1042277b024eSKalle Valo
1043277b024eSKalle Valo /*
1044277b024eSKalle Valo * CFG802.11 network device handler for transmission timeout.
1045277b024eSKalle Valo */
1046277b024eSKalle Valo static void
mwifiex_tx_timeout(struct net_device * dev,unsigned int txqueue)10470290bd29SMichael S. Tsirkin mwifiex_tx_timeout(struct net_device *dev, unsigned int txqueue)
1048277b024eSKalle Valo {
1049277b024eSKalle Valo struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
1050277b024eSKalle Valo
1051277b024eSKalle Valo priv->num_tx_timeout++;
1052277b024eSKalle Valo priv->tx_timeout_cnt++;
1053277b024eSKalle Valo mwifiex_dbg(priv->adapter, ERROR,
1054277b024eSKalle Valo "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n",
1055277b024eSKalle Valo jiffies, priv->tx_timeout_cnt, priv->bss_type,
1056277b024eSKalle Valo priv->bss_num);
1057277b024eSKalle Valo mwifiex_set_trans_start(dev);
1058277b024eSKalle Valo
1059277b024eSKalle Valo if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD &&
1060277b024eSKalle Valo priv->adapter->if_ops.card_reset) {
1061277b024eSKalle Valo mwifiex_dbg(priv->adapter, ERROR,
1062277b024eSKalle Valo "tx_timeout_cnt exceeds threshold.\t"
1063277b024eSKalle Valo "Triggering card reset!\n");
1064277b024eSKalle Valo priv->adapter->if_ops.card_reset(priv->adapter);
1065277b024eSKalle Valo }
1066277b024eSKalle Valo }
1067277b024eSKalle Valo
mwifiex_multi_chan_resync(struct mwifiex_adapter * adapter)1068277b024eSKalle Valo void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter)
1069277b024eSKalle Valo {
1070277b024eSKalle Valo struct usb_card_rec *card = adapter->card;
1071277b024eSKalle Valo struct mwifiex_private *priv;
1072277b024eSKalle Valo u16 tx_buf_size;
1073277b024eSKalle Valo int i, ret;
1074277b024eSKalle Valo
1075277b024eSKalle Valo card->mc_resync_flag = true;
1076277b024eSKalle Valo for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) {
1077277b024eSKalle Valo if (atomic_read(&card->port[i].tx_data_urb_pending)) {
1078277b024eSKalle Valo mwifiex_dbg(adapter, WARN, "pending data urb in sys\n");
1079277b024eSKalle Valo return;
1080277b024eSKalle Valo }
1081277b024eSKalle Valo }
1082277b024eSKalle Valo
1083277b024eSKalle Valo card->mc_resync_flag = false;
1084277b024eSKalle Valo tx_buf_size = 0xffff;
1085277b024eSKalle Valo priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
1086277b024eSKalle Valo ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
1087277b024eSKalle Valo HostCmd_ACT_GEN_SET, 0, &tx_buf_size, false);
1088277b024eSKalle Valo if (ret)
1089277b024eSKalle Valo mwifiex_dbg(adapter, ERROR,
1090277b024eSKalle Valo "send reconfig tx buf size cmd err\n");
1091277b024eSKalle Valo }
1092277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_multi_chan_resync);
1093277b024eSKalle Valo
mwifiex_upload_device_dump(struct mwifiex_adapter * adapter)1094d0e2b44eSXinming Hu void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter)
1095277b024eSKalle Valo {
1096d0e2b44eSXinming Hu /* Dump all the memory data into single file, a userspace script will
1097d0e2b44eSXinming Hu * be used to split all the memory data to multiple files
1098d0e2b44eSXinming Hu */
1099d0e2b44eSXinming Hu mwifiex_dbg(adapter, MSG,
1100d0e2b44eSXinming Hu "== mwifiex dump information to /sys/class/devcoredump start\n");
1101d0e2b44eSXinming Hu dev_coredumpv(adapter->dev, adapter->devdump_data, adapter->devdump_len,
1102d0e2b44eSXinming Hu GFP_KERNEL);
1103d0e2b44eSXinming Hu mwifiex_dbg(adapter, MSG,
1104d0e2b44eSXinming Hu "== mwifiex dump information to /sys/class/devcoredump end\n");
1105d0e2b44eSXinming Hu
1106d0e2b44eSXinming Hu /* Device dump data will be freed in device coredump release function
1107d0e2b44eSXinming Hu * after 5 min. Here reset adapter->devdump_data and ->devdump_len
1108d0e2b44eSXinming Hu * to avoid it been accidentally reused.
1109d0e2b44eSXinming Hu */
1110d0e2b44eSXinming Hu adapter->devdump_data = NULL;
1111d0e2b44eSXinming Hu adapter->devdump_len = 0;
1112d0e2b44eSXinming Hu }
1113d0e2b44eSXinming Hu EXPORT_SYMBOL_GPL(mwifiex_upload_device_dump);
1114d0e2b44eSXinming Hu
mwifiex_drv_info_dump(struct mwifiex_adapter * adapter)1115d0e2b44eSXinming Hu void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter)
1116d0e2b44eSXinming Hu {
1117d0e2b44eSXinming Hu char *p;
1118277b024eSKalle Valo char drv_version[64];
1119277b024eSKalle Valo struct usb_card_rec *cardp;
1120277b024eSKalle Valo struct sdio_mmc_card *sdio_card;
1121277b024eSKalle Valo struct mwifiex_private *priv;
1122277b024eSKalle Valo int i, idx;
1123277b024eSKalle Valo struct netdev_queue *txq;
1124277b024eSKalle Valo struct mwifiex_debug_info *debug_info;
1125277b024eSKalle Valo
1126277b024eSKalle Valo mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump start===\n");
1127277b024eSKalle Valo
1128d0e2b44eSXinming Hu p = adapter->devdump_data;
1129d0e2b44eSXinming Hu strcpy(p, "========Start dump driverinfo========\n");
1130d0e2b44eSXinming Hu p += strlen("========Start dump driverinfo========\n");
1131277b024eSKalle Valo p += sprintf(p, "driver_name = " "\"mwifiex\"\n");
1132277b024eSKalle Valo
1133277b024eSKalle Valo mwifiex_drv_get_driver_version(adapter, drv_version,
1134277b024eSKalle Valo sizeof(drv_version) - 1);
1135277b024eSKalle Valo p += sprintf(p, "driver_version = %s\n", drv_version);
1136277b024eSKalle Valo
1137277b024eSKalle Valo if (adapter->iface_type == MWIFIEX_USB) {
1138277b024eSKalle Valo cardp = (struct usb_card_rec *)adapter->card;
1139277b024eSKalle Valo p += sprintf(p, "tx_cmd_urb_pending = %d\n",
1140277b024eSKalle Valo atomic_read(&cardp->tx_cmd_urb_pending));
1141277b024eSKalle Valo p += sprintf(p, "tx_data_urb_pending_port_0 = %d\n",
1142277b024eSKalle Valo atomic_read(&cardp->port[0].tx_data_urb_pending));
1143277b024eSKalle Valo p += sprintf(p, "tx_data_urb_pending_port_1 = %d\n",
1144277b024eSKalle Valo atomic_read(&cardp->port[1].tx_data_urb_pending));
1145277b024eSKalle Valo p += sprintf(p, "rx_cmd_urb_pending = %d\n",
1146277b024eSKalle Valo atomic_read(&cardp->rx_cmd_urb_pending));
1147277b024eSKalle Valo p += sprintf(p, "rx_data_urb_pending = %d\n",
1148277b024eSKalle Valo atomic_read(&cardp->rx_data_urb_pending));
1149277b024eSKalle Valo }
1150277b024eSKalle Valo
1151277b024eSKalle Valo p += sprintf(p, "tx_pending = %d\n",
1152277b024eSKalle Valo atomic_read(&adapter->tx_pending));
1153277b024eSKalle Valo p += sprintf(p, "rx_pending = %d\n",
1154277b024eSKalle Valo atomic_read(&adapter->rx_pending));
1155277b024eSKalle Valo
1156277b024eSKalle Valo if (adapter->iface_type == MWIFIEX_SDIO) {
1157277b024eSKalle Valo sdio_card = (struct sdio_mmc_card *)adapter->card;
1158277b024eSKalle Valo p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n",
1159277b024eSKalle Valo sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port);
1160277b024eSKalle Valo p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n",
1161277b024eSKalle Valo sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port);
1162277b024eSKalle Valo }
1163277b024eSKalle Valo
1164277b024eSKalle Valo for (i = 0; i < adapter->priv_num; i++) {
1165277b024eSKalle Valo if (!adapter->priv[i] || !adapter->priv[i]->netdev)
1166277b024eSKalle Valo continue;
1167277b024eSKalle Valo priv = adapter->priv[i];
1168277b024eSKalle Valo p += sprintf(p, "\n[interface : \"%s\"]\n",
1169277b024eSKalle Valo priv->netdev->name);
1170277b024eSKalle Valo p += sprintf(p, "wmm_tx_pending[0] = %d\n",
1171277b024eSKalle Valo atomic_read(&priv->wmm_tx_pending[0]));
1172277b024eSKalle Valo p += sprintf(p, "wmm_tx_pending[1] = %d\n",
1173277b024eSKalle Valo atomic_read(&priv->wmm_tx_pending[1]));
1174277b024eSKalle Valo p += sprintf(p, "wmm_tx_pending[2] = %d\n",
1175277b024eSKalle Valo atomic_read(&priv->wmm_tx_pending[2]));
1176277b024eSKalle Valo p += sprintf(p, "wmm_tx_pending[3] = %d\n",
1177277b024eSKalle Valo atomic_read(&priv->wmm_tx_pending[3]));
1178277b024eSKalle Valo p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ?
1179277b024eSKalle Valo "Disconnected" : "Connected");
1180277b024eSKalle Valo p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev)
1181277b024eSKalle Valo ? "on" : "off"));
1182277b024eSKalle Valo for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) {
1183277b024eSKalle Valo txq = netdev_get_tx_queue(priv->netdev, idx);
1184277b024eSKalle Valo p += sprintf(p, "tx queue %d:%s ", idx,
1185277b024eSKalle Valo netif_tx_queue_stopped(txq) ?
1186277b024eSKalle Valo "stopped" : "started");
1187277b024eSKalle Valo }
1188277b024eSKalle Valo p += sprintf(p, "\n%s: num_tx_timeout = %d\n",
1189277b024eSKalle Valo priv->netdev->name, priv->num_tx_timeout);
1190277b024eSKalle Valo }
1191277b024eSKalle Valo
11924646968bSXinming Hu if (adapter->iface_type == MWIFIEX_SDIO ||
11934646968bSXinming Hu adapter->iface_type == MWIFIEX_PCIE) {
11944646968bSXinming Hu p += sprintf(p, "\n=== %s register dump===\n",
11954646968bSXinming Hu adapter->iface_type == MWIFIEX_SDIO ?
11964646968bSXinming Hu "SDIO" : "PCIE");
1197277b024eSKalle Valo if (adapter->if_ops.reg_dump)
1198277b024eSKalle Valo p += adapter->if_ops.reg_dump(adapter, p);
1199277b024eSKalle Valo }
1200277b024eSKalle Valo p += sprintf(p, "\n=== more debug information\n");
1201277b024eSKalle Valo debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL);
1202277b024eSKalle Valo if (debug_info) {
1203277b024eSKalle Valo for (i = 0; i < adapter->priv_num; i++) {
1204277b024eSKalle Valo if (!adapter->priv[i] || !adapter->priv[i]->netdev)
1205277b024eSKalle Valo continue;
1206277b024eSKalle Valo priv = adapter->priv[i];
1207277b024eSKalle Valo mwifiex_get_debug_info(priv, debug_info);
1208277b024eSKalle Valo p += mwifiex_debug_info_to_buffer(priv, p, debug_info);
1209277b024eSKalle Valo break;
1210277b024eSKalle Valo }
1211277b024eSKalle Valo kfree(debug_info);
1212277b024eSKalle Valo }
1213277b024eSKalle Valo
1214d0e2b44eSXinming Hu strcpy(p, "\n========End dump========\n");
1215d0e2b44eSXinming Hu p += strlen("\n========End dump========\n");
1216277b024eSKalle Valo mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump end===\n");
1217d0e2b44eSXinming Hu adapter->devdump_len = p - (char *)adapter->devdump_data;
1218277b024eSKalle Valo }
1219277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_drv_info_dump);
1220277b024eSKalle Valo
mwifiex_prepare_fw_dump_info(struct mwifiex_adapter * adapter)1221d0e2b44eSXinming Hu void mwifiex_prepare_fw_dump_info(struct mwifiex_adapter *adapter)
1222277b024eSKalle Valo {
1223d0e2b44eSXinming Hu u8 idx;
1224d0e2b44eSXinming Hu char *fw_dump_ptr;
1225d0e2b44eSXinming Hu u32 dump_len = 0;
1226277b024eSKalle Valo
1227277b024eSKalle Valo for (idx = 0; idx < adapter->num_mem_types; idx++) {
1228277b024eSKalle Valo struct memory_type_mapping *entry =
1229277b024eSKalle Valo &adapter->mem_type_mapping_tbl[idx];
1230277b024eSKalle Valo
1231277b024eSKalle Valo if (entry->mem_ptr) {
1232277b024eSKalle Valo dump_len += (strlen("========Start dump ") +
1233277b024eSKalle Valo strlen(entry->mem_name) +
1234277b024eSKalle Valo strlen("========\n") +
1235277b024eSKalle Valo (entry->mem_size + 1) +
1236277b024eSKalle Valo strlen("\n========End dump========\n"));
1237277b024eSKalle Valo }
1238277b024eSKalle Valo }
1239277b024eSKalle Valo
1240d0e2b44eSXinming Hu if (dump_len + 1 + adapter->devdump_len > MWIFIEX_FW_DUMP_SIZE) {
1241d0e2b44eSXinming Hu /* Realloc in case buffer overflow */
1242d0e2b44eSXinming Hu fw_dump_ptr = vzalloc(dump_len + 1 + adapter->devdump_len);
1243d0e2b44eSXinming Hu mwifiex_dbg(adapter, MSG, "Realloc device dump data.\n");
1244d0e2b44eSXinming Hu if (!fw_dump_ptr) {
1245d0e2b44eSXinming Hu vfree(adapter->devdump_data);
1246d0e2b44eSXinming Hu mwifiex_dbg(adapter, ERROR,
1247d0e2b44eSXinming Hu "vzalloc devdump data failure!\n");
1248d0e2b44eSXinming Hu return;
1249d0e2b44eSXinming Hu }
1250277b024eSKalle Valo
1251d0e2b44eSXinming Hu memmove(fw_dump_ptr, adapter->devdump_data,
1252d0e2b44eSXinming Hu adapter->devdump_len);
1253d0e2b44eSXinming Hu vfree(adapter->devdump_data);
1254d0e2b44eSXinming Hu adapter->devdump_data = fw_dump_ptr;
1255d0e2b44eSXinming Hu }
1256277b024eSKalle Valo
1257d0e2b44eSXinming Hu fw_dump_ptr = (char *)adapter->devdump_data + adapter->devdump_len;
1258277b024eSKalle Valo
1259277b024eSKalle Valo for (idx = 0; idx < adapter->num_mem_types; idx++) {
1260277b024eSKalle Valo struct memory_type_mapping *entry =
1261277b024eSKalle Valo &adapter->mem_type_mapping_tbl[idx];
1262277b024eSKalle Valo
1263277b024eSKalle Valo if (entry->mem_ptr) {
1264277b024eSKalle Valo strcpy(fw_dump_ptr, "========Start dump ");
1265277b024eSKalle Valo fw_dump_ptr += strlen("========Start dump ");
1266277b024eSKalle Valo
1267277b024eSKalle Valo strcpy(fw_dump_ptr, entry->mem_name);
1268277b024eSKalle Valo fw_dump_ptr += strlen(entry->mem_name);
1269277b024eSKalle Valo
1270277b024eSKalle Valo strcpy(fw_dump_ptr, "========\n");
1271277b024eSKalle Valo fw_dump_ptr += strlen("========\n");
1272277b024eSKalle Valo
1273277b024eSKalle Valo memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size);
1274277b024eSKalle Valo fw_dump_ptr += entry->mem_size;
1275277b024eSKalle Valo
1276277b024eSKalle Valo strcpy(fw_dump_ptr, "\n========End dump========\n");
1277277b024eSKalle Valo fw_dump_ptr += strlen("\n========End dump========\n");
1278277b024eSKalle Valo }
1279277b024eSKalle Valo }
1280277b024eSKalle Valo
1281d0e2b44eSXinming Hu adapter->devdump_len = fw_dump_ptr - (char *)adapter->devdump_data;
1282277b024eSKalle Valo
1283277b024eSKalle Valo for (idx = 0; idx < adapter->num_mem_types; idx++) {
1284277b024eSKalle Valo struct memory_type_mapping *entry =
1285277b024eSKalle Valo &adapter->mem_type_mapping_tbl[idx];
1286277b024eSKalle Valo
1287277b024eSKalle Valo vfree(entry->mem_ptr);
1288277b024eSKalle Valo entry->mem_ptr = NULL;
1289277b024eSKalle Valo entry->mem_size = 0;
1290277b024eSKalle Valo }
1291277b024eSKalle Valo }
1292d0e2b44eSXinming Hu EXPORT_SYMBOL_GPL(mwifiex_prepare_fw_dump_info);
1293277b024eSKalle Valo
1294277b024eSKalle Valo /*
1295277b024eSKalle Valo * CFG802.11 network device handler for statistics retrieval.
1296277b024eSKalle Valo */
mwifiex_get_stats(struct net_device * dev)1297277b024eSKalle Valo static struct net_device_stats *mwifiex_get_stats(struct net_device *dev)
1298277b024eSKalle Valo {
1299277b024eSKalle Valo struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
1300277b024eSKalle Valo
1301277b024eSKalle Valo return &priv->stats;
1302277b024eSKalle Valo }
1303277b024eSKalle Valo
1304277b024eSKalle Valo static u16
mwifiex_netdev_select_wmm_queue(struct net_device * dev,struct sk_buff * skb,struct net_device * sb_dev)1305277b024eSKalle Valo mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb,
1306a350ecceSPaolo Abeni struct net_device *sb_dev)
1307277b024eSKalle Valo {
1308277b024eSKalle Valo skb->priority = cfg80211_classify8021d(skb, NULL);
1309277b024eSKalle Valo return mwifiex_1d_to_wmm_queue[skb->priority];
1310277b024eSKalle Valo }
1311277b024eSKalle Valo
1312277b024eSKalle Valo /* Network device handlers */
1313277b024eSKalle Valo static const struct net_device_ops mwifiex_netdev_ops = {
1314277b024eSKalle Valo .ndo_open = mwifiex_open,
1315277b024eSKalle Valo .ndo_stop = mwifiex_close,
1316277b024eSKalle Valo .ndo_start_xmit = mwifiex_hard_start_xmit,
13174ba28f93SXinming Hu .ndo_set_mac_address = mwifiex_ndo_set_mac_address,
1318277b024eSKalle Valo .ndo_validate_addr = eth_validate_addr,
1319277b024eSKalle Valo .ndo_tx_timeout = mwifiex_tx_timeout,
1320277b024eSKalle Valo .ndo_get_stats = mwifiex_get_stats,
1321277b024eSKalle Valo .ndo_set_rx_mode = mwifiex_set_multicast_list,
1322277b024eSKalle Valo .ndo_select_queue = mwifiex_netdev_select_wmm_queue,
1323277b024eSKalle Valo };
1324277b024eSKalle Valo
1325277b024eSKalle Valo /*
1326277b024eSKalle Valo * This function initializes the private structure parameters.
1327277b024eSKalle Valo *
1328277b024eSKalle Valo * The following wait queues are initialized -
1329277b024eSKalle Valo * - IOCTL wait queue
1330277b024eSKalle Valo * - Command wait queue
1331277b024eSKalle Valo * - Statistics wait queue
1332277b024eSKalle Valo *
1333277b024eSKalle Valo * ...and the following default parameters are set -
1334277b024eSKalle Valo * - Current key index : Set to 0
1335277b024eSKalle Valo * - Rate index : Set to auto
1336277b024eSKalle Valo * - Media connected : Set to disconnected
1337277b024eSKalle Valo * - Adhoc link sensed : Set to false
1338277b024eSKalle Valo * - Nick name : Set to null
1339277b024eSKalle Valo * - Number of Tx timeout : Set to 0
1340277b024eSKalle Valo * - Device address : Set to current address
1341277b024eSKalle Valo * - Rx histogram statistc : Set to 0
1342277b024eSKalle Valo *
1343277b024eSKalle Valo * In addition, the CFG80211 work queue is also created.
1344277b024eSKalle Valo */
mwifiex_init_priv_params(struct mwifiex_private * priv,struct net_device * dev)1345277b024eSKalle Valo void mwifiex_init_priv_params(struct mwifiex_private *priv,
1346277b024eSKalle Valo struct net_device *dev)
1347277b024eSKalle Valo {
1348277b024eSKalle Valo dev->netdev_ops = &mwifiex_netdev_ops;
1349cf124db5SDavid S. Miller dev->needs_free_netdev = true;
1350277b024eSKalle Valo /* Initialize private structure */
1351277b024eSKalle Valo priv->current_key_index = 0;
1352277b024eSKalle Valo priv->media_connected = false;
1353277b024eSKalle Valo memset(priv->mgmt_ie, 0,
1354277b024eSKalle Valo sizeof(struct mwifiex_ie) * MAX_MGMT_IE_INDEX);
1355277b024eSKalle Valo priv->beacon_idx = MWIFIEX_AUTO_IDX_MASK;
1356277b024eSKalle Valo priv->proberesp_idx = MWIFIEX_AUTO_IDX_MASK;
1357277b024eSKalle Valo priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK;
1358277b024eSKalle Valo priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
1359277b024eSKalle Valo priv->num_tx_timeout = 0;
13607cce1395SXinming Hu if (is_valid_ether_addr(dev->dev_addr))
13617cce1395SXinming Hu ether_addr_copy(priv->curr_addr, dev->dev_addr);
13627cce1395SXinming Hu else
1363277b024eSKalle Valo ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr);
1364277b024eSKalle Valo
1365277b024eSKalle Valo if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
1366277b024eSKalle Valo GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
1367277b024eSKalle Valo priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL);
1368277b024eSKalle Valo if (priv->hist_data)
1369277b024eSKalle Valo mwifiex_hist_data_reset(priv);
1370277b024eSKalle Valo }
1371277b024eSKalle Valo }
1372277b024eSKalle Valo
1373277b024eSKalle Valo /*
1374277b024eSKalle Valo * This function check if command is pending.
1375277b024eSKalle Valo */
is_command_pending(struct mwifiex_adapter * adapter)1376277b024eSKalle Valo int is_command_pending(struct mwifiex_adapter *adapter)
1377277b024eSKalle Valo {
1378277b024eSKalle Valo int is_cmd_pend_q_empty;
1379277b024eSKalle Valo
13808a7f9fd8SBrian Norris spin_lock_bh(&adapter->cmd_pending_q_lock);
1381277b024eSKalle Valo is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q);
13828a7f9fd8SBrian Norris spin_unlock_bh(&adapter->cmd_pending_q_lock);
1383277b024eSKalle Valo
1384277b024eSKalle Valo return !is_cmd_pend_q_empty;
1385277b024eSKalle Valo }
1386277b024eSKalle Valo
1387277b024eSKalle Valo /*
1388277b024eSKalle Valo * This is the RX work queue function.
1389277b024eSKalle Valo *
1390277b024eSKalle Valo * It handles the RX operations.
1391277b024eSKalle Valo */
mwifiex_rx_work_queue(struct work_struct * work)1392277b024eSKalle Valo static void mwifiex_rx_work_queue(struct work_struct *work)
1393277b024eSKalle Valo {
1394277b024eSKalle Valo struct mwifiex_adapter *adapter =
1395277b024eSKalle Valo container_of(work, struct mwifiex_adapter, rx_work);
1396277b024eSKalle Valo
1397fc3a2fcaSGanapathi Bhat if (test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags))
1398277b024eSKalle Valo return;
1399277b024eSKalle Valo mwifiex_process_rx(adapter);
1400277b024eSKalle Valo }
1401277b024eSKalle Valo
1402277b024eSKalle Valo /*
1403277b024eSKalle Valo * This is the main work queue function.
1404277b024eSKalle Valo *
1405277b024eSKalle Valo * It handles the main process, which in turn handles the complete
1406277b024eSKalle Valo * driver operations.
1407277b024eSKalle Valo */
mwifiex_main_work_queue(struct work_struct * work)1408277b024eSKalle Valo static void mwifiex_main_work_queue(struct work_struct *work)
1409277b024eSKalle Valo {
1410277b024eSKalle Valo struct mwifiex_adapter *adapter =
1411277b024eSKalle Valo container_of(work, struct mwifiex_adapter, main_work);
1412277b024eSKalle Valo
1413fc3a2fcaSGanapathi Bhat if (test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags))
1414277b024eSKalle Valo return;
1415277b024eSKalle Valo mwifiex_main_process(adapter);
1416277b024eSKalle Valo }
1417277b024eSKalle Valo
1418b6658b66SBrian Norris /* Common teardown code used for both device removal and reset */
mwifiex_uninit_sw(struct mwifiex_adapter * adapter)1419b6658b66SBrian Norris static void mwifiex_uninit_sw(struct mwifiex_adapter *adapter)
14204c5dae59SAmitkumar Karwar {
14214c5dae59SAmitkumar Karwar struct mwifiex_private *priv;
14224c5dae59SAmitkumar Karwar int i;
14234c5dae59SAmitkumar Karwar
14244c5dae59SAmitkumar Karwar /* We can no longer handle interrupts once we start doing the teardown
14254c5dae59SAmitkumar Karwar * below.
14264c5dae59SAmitkumar Karwar */
14274c5dae59SAmitkumar Karwar if (adapter->if_ops.disable_int)
14284c5dae59SAmitkumar Karwar adapter->if_ops.disable_int(adapter);
14294c5dae59SAmitkumar Karwar
1430fc3a2fcaSGanapathi Bhat set_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
14314c5dae59SAmitkumar Karwar mwifiex_terminate_workqueue(adapter);
14324b1f5a0dSBrian Norris adapter->int_status = 0;
14334c5dae59SAmitkumar Karwar
14344c5dae59SAmitkumar Karwar /* Stop data */
14354c5dae59SAmitkumar Karwar for (i = 0; i < adapter->priv_num; i++) {
14364c5dae59SAmitkumar Karwar priv = adapter->priv[i];
14374c5dae59SAmitkumar Karwar if (priv && priv->netdev) {
14384c5dae59SAmitkumar Karwar mwifiex_stop_net_dev_queue(priv->netdev, adapter);
14394c5dae59SAmitkumar Karwar if (netif_carrier_ok(priv->netdev))
14404c5dae59SAmitkumar Karwar netif_carrier_off(priv->netdev);
14414c5dae59SAmitkumar Karwar netif_device_detach(priv->netdev);
14424c5dae59SAmitkumar Karwar }
14434c5dae59SAmitkumar Karwar }
14444c5dae59SAmitkumar Karwar
14454c5dae59SAmitkumar Karwar mwifiex_dbg(adapter, CMD, "cmd: calling mwifiex_shutdown_drv...\n");
14465bf15e3fSXinming Hu mwifiex_shutdown_drv(adapter);
14474c5dae59SAmitkumar Karwar mwifiex_dbg(adapter, CMD, "cmd: mwifiex_shutdown_drv done\n");
1448b6658b66SBrian Norris
14494c5dae59SAmitkumar Karwar if (atomic_read(&adapter->rx_pending) ||
14504c5dae59SAmitkumar Karwar atomic_read(&adapter->tx_pending) ||
14514c5dae59SAmitkumar Karwar atomic_read(&adapter->cmd_pending)) {
14524c5dae59SAmitkumar Karwar mwifiex_dbg(adapter, ERROR,
14534c5dae59SAmitkumar Karwar "rx_pending=%d, tx_pending=%d,\t"
14544c5dae59SAmitkumar Karwar "cmd_pending=%d\n",
14554c5dae59SAmitkumar Karwar atomic_read(&adapter->rx_pending),
14564c5dae59SAmitkumar Karwar atomic_read(&adapter->tx_pending),
14574c5dae59SAmitkumar Karwar atomic_read(&adapter->cmd_pending));
14584c5dae59SAmitkumar Karwar }
14594c5dae59SAmitkumar Karwar
14604c5dae59SAmitkumar Karwar for (i = 0; i < adapter->priv_num; i++) {
14614c5dae59SAmitkumar Karwar priv = adapter->priv[i];
14624c5dae59SAmitkumar Karwar if (!priv)
14634c5dae59SAmitkumar Karwar continue;
14644c5dae59SAmitkumar Karwar rtnl_lock();
14654c5dae59SAmitkumar Karwar if (priv->netdev &&
14661f9482aaSBrian Norris priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) {
14671f9482aaSBrian Norris /*
14681f9482aaSBrian Norris * Close the netdev now, because if we do it later, the
14691f9482aaSBrian Norris * netdev notifiers will need to acquire the wiphy lock
14701f9482aaSBrian Norris * again --> deadlock.
14711f9482aaSBrian Norris */
14721f9482aaSBrian Norris dev_close(priv->wdev.netdev);
14731f9482aaSBrian Norris wiphy_lock(adapter->wiphy);
14744c5dae59SAmitkumar Karwar mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev);
1475a05829a7SJohannes Berg wiphy_unlock(adapter->wiphy);
14761f9482aaSBrian Norris }
14774c5dae59SAmitkumar Karwar rtnl_unlock();
14784c5dae59SAmitkumar Karwar }
1479643acea6SBrian Norris
1480643acea6SBrian Norris wiphy_unregister(adapter->wiphy);
1481643acea6SBrian Norris wiphy_free(adapter->wiphy);
1482643acea6SBrian Norris adapter->wiphy = NULL;
1483ce32d1d8SBrian Norris
1484ce32d1d8SBrian Norris vfree(adapter->chan_stats);
1485ce32d1d8SBrian Norris mwifiex_free_cmd_buffers(adapter);
1486b6658b66SBrian Norris }
14874c5dae59SAmitkumar Karwar
1488b6658b66SBrian Norris /*
1489566b4cb9STsuchiya Yuto * This function can be used for shutting down the adapter SW.
1490b6658b66SBrian Norris */
mwifiex_shutdown_sw(struct mwifiex_adapter * adapter)1491b6658b66SBrian Norris int mwifiex_shutdown_sw(struct mwifiex_adapter *adapter)
1492b6658b66SBrian Norris {
1493b6658b66SBrian Norris struct mwifiex_private *priv;
1494b6658b66SBrian Norris
1495b6658b66SBrian Norris if (!adapter)
1496b6658b66SBrian Norris return 0;
1497b6658b66SBrian Norris
1498b6658b66SBrian Norris wait_for_completion(adapter->fw_done);
1499b6658b66SBrian Norris /* Caller should ensure we aren't suspending while this happens */
1500b6658b66SBrian Norris reinit_completion(adapter->fw_done);
1501b6658b66SBrian Norris
1502b6658b66SBrian Norris priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
1503b6658b66SBrian Norris mwifiex_deauthenticate(priv, NULL);
1504b6658b66SBrian Norris
1505fa74cb1dSTsuchiya Yuto mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN);
1506fa74cb1dSTsuchiya Yuto
1507b6658b66SBrian Norris mwifiex_uninit_sw(adapter);
1508cdb2256fSUlf Hansson adapter->is_up = false;
1509b6658b66SBrian Norris
1510b6658b66SBrian Norris if (adapter->if_ops.down_dev)
1511b6658b66SBrian Norris adapter->if_ops.down_dev(adapter);
1512b6658b66SBrian Norris
15134c5dae59SAmitkumar Karwar return 0;
15144c5dae59SAmitkumar Karwar }
15158750ab62SXinming Hu EXPORT_SYMBOL_GPL(mwifiex_shutdown_sw);
15164c5dae59SAmitkumar Karwar
1517566b4cb9STsuchiya Yuto /* This function can be used for reinitting the adapter SW. Required
15184c5dae59SAmitkumar Karwar * code is extracted from mwifiex_add_card()
15194c5dae59SAmitkumar Karwar */
15208750ab62SXinming Hu int
mwifiex_reinit_sw(struct mwifiex_adapter * adapter)15218750ab62SXinming Hu mwifiex_reinit_sw(struct mwifiex_adapter *adapter)
15224c5dae59SAmitkumar Karwar {
1523755b37c9SBrian Norris int ret;
1524755b37c9SBrian Norris
15254c5dae59SAmitkumar Karwar mwifiex_init_lock_list(adapter);
15264c5dae59SAmitkumar Karwar if (adapter->if_ops.up_dev)
15274c5dae59SAmitkumar Karwar adapter->if_ops.up_dev(adapter);
15284c5dae59SAmitkumar Karwar
15294c5dae59SAmitkumar Karwar adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
1530fc3a2fcaSGanapathi Bhat clear_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
15314c5dae59SAmitkumar Karwar init_waitqueue_head(&adapter->init_wait_q);
1532fc3a2fcaSGanapathi Bhat clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags);
15334c5dae59SAmitkumar Karwar adapter->hs_activated = false;
1534fc3a2fcaSGanapathi Bhat clear_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags);
15354c5dae59SAmitkumar Karwar init_waitqueue_head(&adapter->hs_activate_wait_q);
15364c5dae59SAmitkumar Karwar init_waitqueue_head(&adapter->cmd_wait_q.wait);
15374c5dae59SAmitkumar Karwar adapter->cmd_wait_q.status = 0;
15384c5dae59SAmitkumar Karwar adapter->scan_wait_q_woken = false;
15394c5dae59SAmitkumar Karwar
15404c5dae59SAmitkumar Karwar if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB)
15414c5dae59SAmitkumar Karwar adapter->rx_work_enabled = true;
15424c5dae59SAmitkumar Karwar
15434c5dae59SAmitkumar Karwar adapter->workqueue =
15444c5dae59SAmitkumar Karwar alloc_workqueue("MWIFIEX_WORK_QUEUE",
154558b18011STejun Heo WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
15464c5dae59SAmitkumar Karwar if (!adapter->workqueue)
15474c5dae59SAmitkumar Karwar goto err_kmalloc;
15484c5dae59SAmitkumar Karwar
15494c5dae59SAmitkumar Karwar INIT_WORK(&adapter->main_work, mwifiex_main_work_queue);
15504c5dae59SAmitkumar Karwar
15514c5dae59SAmitkumar Karwar if (adapter->rx_work_enabled) {
15524c5dae59SAmitkumar Karwar adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE",
15534c5dae59SAmitkumar Karwar WQ_HIGHPRI |
15544c5dae59SAmitkumar Karwar WQ_MEM_RECLAIM |
155558b18011STejun Heo WQ_UNBOUND, 0);
15564c5dae59SAmitkumar Karwar if (!adapter->rx_workqueue)
15574c5dae59SAmitkumar Karwar goto err_kmalloc;
15584c5dae59SAmitkumar Karwar INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue);
15594c5dae59SAmitkumar Karwar }
15604c5dae59SAmitkumar Karwar
15614c5dae59SAmitkumar Karwar /* Register the device. Fill up the private data structure with
15624c5dae59SAmitkumar Karwar * relevant information from the card. Some code extracted from
15634c5dae59SAmitkumar Karwar * mwifiex_register_dev()
15644c5dae59SAmitkumar Karwar */
15654c5dae59SAmitkumar Karwar mwifiex_dbg(adapter, INFO, "%s, mwifiex_init_hw_fw()...\n", __func__);
15664c5dae59SAmitkumar Karwar
15674c5dae59SAmitkumar Karwar if (mwifiex_init_hw_fw(adapter, false)) {
15684c5dae59SAmitkumar Karwar mwifiex_dbg(adapter, ERROR,
15694c5dae59SAmitkumar Karwar "%s: firmware init failed\n", __func__);
15704c5dae59SAmitkumar Karwar goto err_init_fw;
15714c5dae59SAmitkumar Karwar }
1572755b37c9SBrian Norris
1573755b37c9SBrian Norris /* _mwifiex_fw_dpc() does its own cleanup */
1574755b37c9SBrian Norris ret = _mwifiex_fw_dpc(adapter->firmware, adapter);
1575755b37c9SBrian Norris if (ret) {
1576755b37c9SBrian Norris pr_err("Failed to bring up adapter: %d\n", ret);
1577755b37c9SBrian Norris return ret;
1578755b37c9SBrian Norris }
15794c5dae59SAmitkumar Karwar mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__);
15804a79aa17SBrian Norris
15814c5dae59SAmitkumar Karwar return 0;
15824c5dae59SAmitkumar Karwar
15834c5dae59SAmitkumar Karwar err_init_fw:
15844c5dae59SAmitkumar Karwar mwifiex_dbg(adapter, ERROR, "info: %s: unregister device\n", __func__);
15854c5dae59SAmitkumar Karwar if (adapter->if_ops.unregister_dev)
15864c5dae59SAmitkumar Karwar adapter->if_ops.unregister_dev(adapter);
15875bf15e3fSXinming Hu
15885bf15e3fSXinming Hu err_kmalloc:
1589fc3a2fcaSGanapathi Bhat set_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
15905bf15e3fSXinming Hu mwifiex_terminate_workqueue(adapter);
15914c5dae59SAmitkumar Karwar if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
15924c5dae59SAmitkumar Karwar mwifiex_dbg(adapter, ERROR,
15934c5dae59SAmitkumar Karwar "info: %s: shutdown mwifiex\n", __func__);
15945bf15e3fSXinming Hu mwifiex_shutdown_drv(adapter);
1595ce32d1d8SBrian Norris mwifiex_free_cmd_buffers(adapter);
15964c5dae59SAmitkumar Karwar }
15974c5dae59SAmitkumar Karwar
15984a79aa17SBrian Norris complete_all(adapter->fw_done);
15994c5dae59SAmitkumar Karwar mwifiex_dbg(adapter, INFO, "%s, error\n", __func__);
16004c5dae59SAmitkumar Karwar
16014c5dae59SAmitkumar Karwar return -1;
16024c5dae59SAmitkumar Karwar }
16038750ab62SXinming Hu EXPORT_SYMBOL_GPL(mwifiex_reinit_sw);
16044c5dae59SAmitkumar Karwar
mwifiex_irq_wakeup_handler(int irq,void * priv)1605853402a0SRajat Jain static irqreturn_t mwifiex_irq_wakeup_handler(int irq, void *priv)
1606853402a0SRajat Jain {
1607853402a0SRajat Jain struct mwifiex_adapter *adapter = priv;
1608853402a0SRajat Jain
1609853402a0SRajat Jain dev_dbg(adapter->dev, "%s: wake by wifi", __func__);
1610853402a0SRajat Jain adapter->wake_by_wifi = true;
1611853402a0SRajat Jain disable_irq_nosync(irq);
1612853402a0SRajat Jain
1613853402a0SRajat Jain /* Notify PM core we are wakeup source */
1614853402a0SRajat Jain pm_wakeup_event(adapter->dev, 0);
1615ef7e0714SJeffy Chen pm_system_wakeup();
1616853402a0SRajat Jain
1617853402a0SRajat Jain return IRQ_HANDLED;
1618853402a0SRajat Jain }
1619853402a0SRajat Jain
mwifiex_probe_of(struct mwifiex_adapter * adapter)16205e28e5fbSRajat Jain static void mwifiex_probe_of(struct mwifiex_adapter *adapter)
16215e28e5fbSRajat Jain {
1622853402a0SRajat Jain int ret;
16235e28e5fbSRajat Jain struct device *dev = adapter->dev;
16245e28e5fbSRajat Jain
16255e28e5fbSRajat Jain if (!dev->of_node)
16262447e2caSBrian Norris goto err_exit;
16275e28e5fbSRajat Jain
16285e28e5fbSRajat Jain adapter->dt_node = dev->of_node;
1629853402a0SRajat Jain adapter->irq_wakeup = irq_of_parse_and_map(adapter->dt_node, 0);
1630853402a0SRajat Jain if (!adapter->irq_wakeup) {
16312447e2caSBrian Norris dev_dbg(dev, "fail to parse irq_wakeup from device tree\n");
16322447e2caSBrian Norris goto err_exit;
1633853402a0SRajat Jain }
1634853402a0SRajat Jain
1635853402a0SRajat Jain ret = devm_request_irq(dev, adapter->irq_wakeup,
1636*521d4b3fSJinjie Ruan mwifiex_irq_wakeup_handler,
1637*521d4b3fSJinjie Ruan IRQF_TRIGGER_LOW | IRQF_NO_AUTOEN,
1638853402a0SRajat Jain "wifi_wake", adapter);
1639853402a0SRajat Jain if (ret) {
1640853402a0SRajat Jain dev_err(dev, "Failed to request irq_wakeup %d (%d)\n",
1641853402a0SRajat Jain adapter->irq_wakeup, ret);
1642853402a0SRajat Jain goto err_exit;
1643853402a0SRajat Jain }
1644853402a0SRajat Jain
1645853402a0SRajat Jain if (device_init_wakeup(dev, true)) {
1646853402a0SRajat Jain dev_err(dev, "fail to init wakeup for mwifiex\n");
1647853402a0SRajat Jain goto err_exit;
1648853402a0SRajat Jain }
1649853402a0SRajat Jain return;
1650853402a0SRajat Jain
1651853402a0SRajat Jain err_exit:
16522447e2caSBrian Norris adapter->irq_wakeup = -1;
16535e28e5fbSRajat Jain }
16545e28e5fbSRajat Jain
16554c5dae59SAmitkumar Karwar /*
1656277b024eSKalle Valo * This function adds the card.
1657277b024eSKalle Valo *
1658277b024eSKalle Valo * This function follows the following major steps to set up the device -
1659277b024eSKalle Valo * - Initialize software. This includes probing the card, registering
1660277b024eSKalle Valo * the interface operations table, and allocating/initializing the
1661277b024eSKalle Valo * adapter structure
1662277b024eSKalle Valo * - Set up the netlink socket
1663277b024eSKalle Valo * - Create and start the main work queue
1664277b024eSKalle Valo * - Register the device
1665277b024eSKalle Valo * - Initialize firmware and hardware
1666277b024eSKalle Valo * - Add logical interfaces
1667277b024eSKalle Valo */
1668277b024eSKalle Valo int
mwifiex_add_card(void * card,struct completion * fw_done,struct mwifiex_if_ops * if_ops,u8 iface_type,struct device * dev)16694a79aa17SBrian Norris mwifiex_add_card(void *card, struct completion *fw_done,
16702e02b581SRajat Jain struct mwifiex_if_ops *if_ops, u8 iface_type,
16712e02b581SRajat Jain struct device *dev)
1672277b024eSKalle Valo {
1673277b024eSKalle Valo struct mwifiex_adapter *adapter;
1674277b024eSKalle Valo
1675ba1c7e45SBrian Norris if (mwifiex_register(card, dev, if_ops, (void **)&adapter)) {
1676277b024eSKalle Valo pr_err("%s: software init failed\n", __func__);
1677277b024eSKalle Valo goto err_init_sw;
1678277b024eSKalle Valo }
1679277b024eSKalle Valo
16805e28e5fbSRajat Jain mwifiex_probe_of(adapter);
16815e28e5fbSRajat Jain
1682277b024eSKalle Valo adapter->iface_type = iface_type;
16834a79aa17SBrian Norris adapter->fw_done = fw_done;
1684277b024eSKalle Valo
1685277b024eSKalle Valo adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
1686fc3a2fcaSGanapathi Bhat clear_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
1687277b024eSKalle Valo init_waitqueue_head(&adapter->init_wait_q);
1688fc3a2fcaSGanapathi Bhat clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags);
1689277b024eSKalle Valo adapter->hs_activated = false;
1690277b024eSKalle Valo init_waitqueue_head(&adapter->hs_activate_wait_q);
1691277b024eSKalle Valo init_waitqueue_head(&adapter->cmd_wait_q.wait);
1692277b024eSKalle Valo adapter->cmd_wait_q.status = 0;
1693277b024eSKalle Valo adapter->scan_wait_q_woken = false;
1694277b024eSKalle Valo
16950bc03cfdSBrian Norris if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB)
1696277b024eSKalle Valo adapter->rx_work_enabled = true;
1697277b024eSKalle Valo
1698277b024eSKalle Valo adapter->workqueue =
1699277b024eSKalle Valo alloc_workqueue("MWIFIEX_WORK_QUEUE",
170058b18011STejun Heo WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
1701277b024eSKalle Valo if (!adapter->workqueue)
1702277b024eSKalle Valo goto err_kmalloc;
1703277b024eSKalle Valo
1704277b024eSKalle Valo INIT_WORK(&adapter->main_work, mwifiex_main_work_queue);
1705277b024eSKalle Valo
1706277b024eSKalle Valo if (adapter->rx_work_enabled) {
1707277b024eSKalle Valo adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE",
1708277b024eSKalle Valo WQ_HIGHPRI |
1709277b024eSKalle Valo WQ_MEM_RECLAIM |
171058b18011STejun Heo WQ_UNBOUND, 0);
1711277b024eSKalle Valo if (!adapter->rx_workqueue)
1712277b024eSKalle Valo goto err_kmalloc;
1713277b024eSKalle Valo
1714277b024eSKalle Valo INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue);
1715277b024eSKalle Valo }
1716277b024eSKalle Valo
1717277b024eSKalle Valo /* Register the device. Fill up the private data structure with relevant
1718277b024eSKalle Valo information from the card. */
1719277b024eSKalle Valo if (adapter->if_ops.register_dev(adapter)) {
1720277b024eSKalle Valo pr_err("%s: failed to register mwifiex device\n", __func__);
1721277b024eSKalle Valo goto err_registerdev;
1722277b024eSKalle Valo }
1723277b024eSKalle Valo
17244c5dae59SAmitkumar Karwar if (mwifiex_init_hw_fw(adapter, true)) {
1725277b024eSKalle Valo pr_err("%s: firmware init failed\n", __func__);
1726277b024eSKalle Valo goto err_init_fw;
1727277b024eSKalle Valo }
1728277b024eSKalle Valo
1729277b024eSKalle Valo return 0;
1730277b024eSKalle Valo
1731277b024eSKalle Valo err_init_fw:
1732277b024eSKalle Valo pr_debug("info: %s: unregister device\n", __func__);
1733277b024eSKalle Valo if (adapter->if_ops.unregister_dev)
1734277b024eSKalle Valo adapter->if_ops.unregister_dev(adapter);
1735277b024eSKalle Valo err_registerdev:
1736fc3a2fcaSGanapathi Bhat set_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
1737277b024eSKalle Valo mwifiex_terminate_workqueue(adapter);
17385bf15e3fSXinming Hu if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
17395bf15e3fSXinming Hu pr_debug("info: %s: shutdown mwifiex\n", __func__);
17405bf15e3fSXinming Hu mwifiex_shutdown_drv(adapter);
1741ce32d1d8SBrian Norris mwifiex_free_cmd_buffers(adapter);
17425bf15e3fSXinming Hu }
1743277b024eSKalle Valo err_kmalloc:
1744f101d964SJeffy Chen if (adapter->irq_wakeup >= 0)
1745f101d964SJeffy Chen device_init_wakeup(adapter->dev, false);
1746277b024eSKalle Valo mwifiex_free_adapter(adapter);
1747277b024eSKalle Valo
1748277b024eSKalle Valo err_init_sw:
1749277b024eSKalle Valo
1750277b024eSKalle Valo return -1;
1751277b024eSKalle Valo }
1752277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_add_card);
1753277b024eSKalle Valo
1754277b024eSKalle Valo /*
1755277b024eSKalle Valo * This function removes the card.
1756277b024eSKalle Valo *
1757277b024eSKalle Valo * This function follows the following major steps to remove the device -
1758277b024eSKalle Valo * - Stop data traffic
1759277b024eSKalle Valo * - Shutdown firmware
1760277b024eSKalle Valo * - Remove the logical interfaces
1761277b024eSKalle Valo * - Terminate the work queue
1762277b024eSKalle Valo * - Unregister the device
1763277b024eSKalle Valo * - Free the adapter structure
1764277b024eSKalle Valo */
mwifiex_remove_card(struct mwifiex_adapter * adapter)17654a79aa17SBrian Norris int mwifiex_remove_card(struct mwifiex_adapter *adapter)
1766277b024eSKalle Valo {
1767277b024eSKalle Valo if (!adapter)
1768b6658b66SBrian Norris return 0;
1769277b024eSKalle Valo
1770cdb2256fSUlf Hansson if (adapter->is_up)
1771b6658b66SBrian Norris mwifiex_uninit_sw(adapter);
1772277b024eSKalle Valo
177336908c4eSBrian Norris if (adapter->irq_wakeup >= 0)
177436908c4eSBrian Norris device_init_wakeup(adapter->dev, false);
177536908c4eSBrian Norris
1776277b024eSKalle Valo /* Unregister device */
1777277b024eSKalle Valo mwifiex_dbg(adapter, INFO,
1778277b024eSKalle Valo "info: unregister device\n");
1779277b024eSKalle Valo if (adapter->if_ops.unregister_dev)
1780277b024eSKalle Valo adapter->if_ops.unregister_dev(adapter);
1781277b024eSKalle Valo /* Free adapter structure */
1782277b024eSKalle Valo mwifiex_dbg(adapter, INFO,
1783277b024eSKalle Valo "info: free adapter\n");
1784277b024eSKalle Valo mwifiex_free_adapter(adapter);
1785277b024eSKalle Valo
1786277b024eSKalle Valo return 0;
1787277b024eSKalle Valo }
1788277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_remove_card);
1789277b024eSKalle Valo
_mwifiex_dbg(const struct mwifiex_adapter * adapter,int mask,const char * fmt,...)1790277b024eSKalle Valo void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask,
1791277b024eSKalle Valo const char *fmt, ...)
1792277b024eSKalle Valo {
1793277b024eSKalle Valo struct va_format vaf;
1794277b024eSKalle Valo va_list args;
1795277b024eSKalle Valo
1796ef6c7d3cSXinming Hu if (!(adapter->debug_mask & mask))
1797277b024eSKalle Valo return;
1798277b024eSKalle Valo
1799277b024eSKalle Valo va_start(args, fmt);
1800277b024eSKalle Valo
1801277b024eSKalle Valo vaf.fmt = fmt;
1802277b024eSKalle Valo vaf.va = &args;
1803277b024eSKalle Valo
1804ef6c7d3cSXinming Hu if (adapter->dev)
1805277b024eSKalle Valo dev_info(adapter->dev, "%pV", &vaf);
1806ef6c7d3cSXinming Hu else
1807ef6c7d3cSXinming Hu pr_info("%pV", &vaf);
1808277b024eSKalle Valo
1809277b024eSKalle Valo va_end(args);
1810277b024eSKalle Valo }
1811277b024eSKalle Valo EXPORT_SYMBOL_GPL(_mwifiex_dbg);
1812277b024eSKalle Valo
1813277b024eSKalle Valo /*
1814277b024eSKalle Valo * This function initializes the module.
1815277b024eSKalle Valo *
1816277b024eSKalle Valo * The debug FS is also initialized if configured.
1817277b024eSKalle Valo */
1818277b024eSKalle Valo static int
mwifiex_init_module(void)1819277b024eSKalle Valo mwifiex_init_module(void)
1820277b024eSKalle Valo {
1821277b024eSKalle Valo #ifdef CONFIG_DEBUG_FS
1822277b024eSKalle Valo mwifiex_debugfs_init();
1823277b024eSKalle Valo #endif
1824277b024eSKalle Valo return 0;
1825277b024eSKalle Valo }
1826277b024eSKalle Valo
1827277b024eSKalle Valo /*
1828277b024eSKalle Valo * This function cleans up the module.
1829277b024eSKalle Valo *
1830277b024eSKalle Valo * The debug FS is removed if available.
1831277b024eSKalle Valo */
1832277b024eSKalle Valo static void
mwifiex_cleanup_module(void)1833277b024eSKalle Valo mwifiex_cleanup_module(void)
1834277b024eSKalle Valo {
1835277b024eSKalle Valo #ifdef CONFIG_DEBUG_FS
1836277b024eSKalle Valo mwifiex_debugfs_remove();
1837277b024eSKalle Valo #endif
1838277b024eSKalle Valo }
1839277b024eSKalle Valo
1840277b024eSKalle Valo module_init(mwifiex_init_module);
1841277b024eSKalle Valo module_exit(mwifiex_cleanup_module);
1842277b024eSKalle Valo
1843277b024eSKalle Valo MODULE_AUTHOR("Marvell International Ltd.");
1844277b024eSKalle Valo MODULE_DESCRIPTION("Marvell WiFi-Ex Driver version " VERSION);
1845277b024eSKalle Valo MODULE_VERSION(VERSION);
1846277b024eSKalle Valo MODULE_LICENSE("GPL v2");
1847