xref: /openbmc/linux/drivers/net/wireless/marvell/mwifiex/main.c (revision eaf46b5fda3acd75df68f02d25754dccb446ec2d)
1277b024eSKalle Valo /*
2277b024eSKalle Valo  * Marvell Wireless LAN device driver: major functions
3277b024eSKalle Valo  *
4277b024eSKalle Valo  * Copyright (C) 2011-2014, Marvell International Ltd.
5277b024eSKalle Valo  *
6277b024eSKalle Valo  * This software file (the "File") is distributed by Marvell International
7277b024eSKalle Valo  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
8277b024eSKalle Valo  * (the "License").  You may use, redistribute and/or modify this File in
9277b024eSKalle Valo  * accordance with the terms and conditions of the License, a copy of which
10277b024eSKalle Valo  * is available by writing to the Free Software Foundation, Inc.,
11277b024eSKalle Valo  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
12277b024eSKalle Valo  * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
13277b024eSKalle Valo  *
14277b024eSKalle Valo  * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
15277b024eSKalle Valo  * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
16277b024eSKalle Valo  * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
17277b024eSKalle Valo  * this warranty disclaimer.
18277b024eSKalle Valo  */
19277b024eSKalle Valo 
20277b024eSKalle Valo #include "main.h"
21277b024eSKalle Valo #include "wmm.h"
22277b024eSKalle Valo #include "cfg80211.h"
23277b024eSKalle Valo #include "11n.h"
24277b024eSKalle Valo 
25277b024eSKalle Valo #define VERSION	"1.0"
26277b024eSKalle Valo 
27277b024eSKalle Valo static unsigned int debug_mask = MWIFIEX_DEFAULT_DEBUG_MASK;
28277b024eSKalle Valo module_param(debug_mask, uint, 0);
29277b024eSKalle Valo MODULE_PARM_DESC(debug_mask, "bitmap for debug flags");
30277b024eSKalle Valo 
31277b024eSKalle Valo const char driver_version[] = "mwifiex " VERSION " (%s) ";
32277b024eSKalle Valo static char *cal_data_cfg;
33277b024eSKalle Valo module_param(cal_data_cfg, charp, 0);
34277b024eSKalle Valo 
35277b024eSKalle Valo static unsigned short driver_mode;
36277b024eSKalle Valo module_param(driver_mode, ushort, 0);
37277b024eSKalle Valo MODULE_PARM_DESC(driver_mode,
38277b024eSKalle Valo 		 "station=0x1(default), ap-sta=0x3, station-p2p=0x5, ap-sta-p2p=0x7");
39277b024eSKalle Valo 
40277b024eSKalle Valo /*
41277b024eSKalle Valo  * This function registers the device and performs all the necessary
42277b024eSKalle Valo  * initializations.
43277b024eSKalle Valo  *
44277b024eSKalle Valo  * The following initialization operations are performed -
45277b024eSKalle Valo  *      - Allocate adapter structure
46277b024eSKalle Valo  *      - Save interface specific operations table in adapter
47277b024eSKalle Valo  *      - Call interface specific initialization routine
48277b024eSKalle Valo  *      - Allocate private structures
49277b024eSKalle Valo  *      - Set default adapter structure parameters
50277b024eSKalle Valo  *      - Initialize locks
51277b024eSKalle Valo  *
52277b024eSKalle Valo  * In case of any errors during inittialization, this function also ensures
53277b024eSKalle Valo  * proper cleanup before exiting.
54277b024eSKalle Valo  */
55277b024eSKalle Valo static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops,
56277b024eSKalle Valo 			    void **padapter)
57277b024eSKalle Valo {
58277b024eSKalle Valo 	struct mwifiex_adapter *adapter;
59277b024eSKalle Valo 	int i;
60277b024eSKalle Valo 
61277b024eSKalle Valo 	adapter = kzalloc(sizeof(struct mwifiex_adapter), GFP_KERNEL);
62277b024eSKalle Valo 	if (!adapter)
63277b024eSKalle Valo 		return -ENOMEM;
64277b024eSKalle Valo 
65277b024eSKalle Valo 	*padapter = adapter;
66277b024eSKalle Valo 	adapter->card = card;
67277b024eSKalle Valo 
68277b024eSKalle Valo 	/* Save interface specific operations in adapter */
69277b024eSKalle Valo 	memmove(&adapter->if_ops, if_ops, sizeof(struct mwifiex_if_ops));
70277b024eSKalle Valo 	adapter->debug_mask = debug_mask;
71277b024eSKalle Valo 
72277b024eSKalle Valo 	/* card specific initialization has been deferred until now .. */
73277b024eSKalle Valo 	if (adapter->if_ops.init_if)
74277b024eSKalle Valo 		if (adapter->if_ops.init_if(adapter))
75277b024eSKalle Valo 			goto error;
76277b024eSKalle Valo 
77277b024eSKalle Valo 	adapter->priv_num = 0;
78277b024eSKalle Valo 
79277b024eSKalle Valo 	for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) {
80277b024eSKalle Valo 		/* Allocate memory for private structure */
81277b024eSKalle Valo 		adapter->priv[i] =
82277b024eSKalle Valo 			kzalloc(sizeof(struct mwifiex_private), GFP_KERNEL);
83277b024eSKalle Valo 		if (!adapter->priv[i])
84277b024eSKalle Valo 			goto error;
85277b024eSKalle Valo 
86277b024eSKalle Valo 		adapter->priv[i]->adapter = adapter;
87277b024eSKalle Valo 		adapter->priv_num++;
88277b024eSKalle Valo 	}
89277b024eSKalle Valo 	mwifiex_init_lock_list(adapter);
90277b024eSKalle Valo 
91277b024eSKalle Valo 	setup_timer(&adapter->cmd_timer, mwifiex_cmd_timeout_func,
92277b024eSKalle Valo 		    (unsigned long)adapter);
93277b024eSKalle Valo 
94277b024eSKalle Valo 	return 0;
95277b024eSKalle Valo 
96277b024eSKalle Valo error:
97277b024eSKalle Valo 	mwifiex_dbg(adapter, ERROR,
98277b024eSKalle Valo 		    "info: leave mwifiex_register with error\n");
99277b024eSKalle Valo 
100277b024eSKalle Valo 	for (i = 0; i < adapter->priv_num; i++)
101277b024eSKalle Valo 		kfree(adapter->priv[i]);
102277b024eSKalle Valo 
103277b024eSKalle Valo 	kfree(adapter);
104277b024eSKalle Valo 
105277b024eSKalle Valo 	return -1;
106277b024eSKalle Valo }
107277b024eSKalle Valo 
108277b024eSKalle Valo /*
109277b024eSKalle Valo  * This function unregisters the device and performs all the necessary
110277b024eSKalle Valo  * cleanups.
111277b024eSKalle Valo  *
112277b024eSKalle Valo  * The following cleanup operations are performed -
113277b024eSKalle Valo  *      - Free the timers
114277b024eSKalle Valo  *      - Free beacon buffers
115277b024eSKalle Valo  *      - Free private structures
116277b024eSKalle Valo  *      - Free adapter structure
117277b024eSKalle Valo  */
118277b024eSKalle Valo static int mwifiex_unregister(struct mwifiex_adapter *adapter)
119277b024eSKalle Valo {
120277b024eSKalle Valo 	s32 i;
121277b024eSKalle Valo 
122277b024eSKalle Valo 	if (adapter->if_ops.cleanup_if)
123277b024eSKalle Valo 		adapter->if_ops.cleanup_if(adapter);
124277b024eSKalle Valo 
125277b024eSKalle Valo 	del_timer_sync(&adapter->cmd_timer);
126277b024eSKalle Valo 
127277b024eSKalle Valo 	/* Free private structures */
128277b024eSKalle Valo 	for (i = 0; i < adapter->priv_num; i++) {
129277b024eSKalle Valo 		if (adapter->priv[i]) {
130277b024eSKalle Valo 			mwifiex_free_curr_bcn(adapter->priv[i]);
131277b024eSKalle Valo 			kfree(adapter->priv[i]);
132277b024eSKalle Valo 		}
133277b024eSKalle Valo 	}
134277b024eSKalle Valo 
1357d7f07d8Schunfan chen 	if (adapter->nd_info) {
1367d7f07d8Schunfan chen 		for (i = 0 ; i < adapter->nd_info->n_matches ; i++)
1377d7f07d8Schunfan chen 			kfree(adapter->nd_info->matches[i]);
1387d7f07d8Schunfan chen 		kfree(adapter->nd_info);
1397d7f07d8Schunfan chen 		adapter->nd_info = NULL;
1407d7f07d8Schunfan chen 	}
1417d7f07d8Schunfan chen 
142277b024eSKalle Valo 	vfree(adapter->chan_stats);
143277b024eSKalle Valo 	kfree(adapter);
144277b024eSKalle Valo 	return 0;
145277b024eSKalle Valo }
146277b024eSKalle Valo 
147277b024eSKalle Valo void mwifiex_queue_main_work(struct mwifiex_adapter *adapter)
148277b024eSKalle Valo {
149277b024eSKalle Valo 	unsigned long flags;
150277b024eSKalle Valo 
151277b024eSKalle Valo 	spin_lock_irqsave(&adapter->main_proc_lock, flags);
152277b024eSKalle Valo 	if (adapter->mwifiex_processing) {
153277b024eSKalle Valo 		adapter->more_task_flag = true;
154277b024eSKalle Valo 		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
155277b024eSKalle Valo 	} else {
156277b024eSKalle Valo 		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
157277b024eSKalle Valo 		queue_work(adapter->workqueue, &adapter->main_work);
158277b024eSKalle Valo 	}
159277b024eSKalle Valo }
160277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_queue_main_work);
161277b024eSKalle Valo 
162277b024eSKalle Valo static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter)
163277b024eSKalle Valo {
164277b024eSKalle Valo 	unsigned long flags;
165277b024eSKalle Valo 
166277b024eSKalle Valo 	spin_lock_irqsave(&adapter->rx_proc_lock, flags);
167277b024eSKalle Valo 	if (adapter->rx_processing) {
168277b024eSKalle Valo 		spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
169277b024eSKalle Valo 	} else {
170277b024eSKalle Valo 		spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
171277b024eSKalle Valo 		queue_work(adapter->rx_workqueue, &adapter->rx_work);
172277b024eSKalle Valo 	}
173277b024eSKalle Valo }
174277b024eSKalle Valo 
175277b024eSKalle Valo static int mwifiex_process_rx(struct mwifiex_adapter *adapter)
176277b024eSKalle Valo {
177277b024eSKalle Valo 	unsigned long flags;
178277b024eSKalle Valo 	struct sk_buff *skb;
179277b024eSKalle Valo 	struct mwifiex_rxinfo *rx_info;
180277b024eSKalle Valo 
181277b024eSKalle Valo 	spin_lock_irqsave(&adapter->rx_proc_lock, flags);
182277b024eSKalle Valo 	if (adapter->rx_processing || adapter->rx_locked) {
183277b024eSKalle Valo 		spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
184277b024eSKalle Valo 		goto exit_rx_proc;
185277b024eSKalle Valo 	} else {
186277b024eSKalle Valo 		adapter->rx_processing = true;
187277b024eSKalle Valo 		spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
188277b024eSKalle Valo 	}
189277b024eSKalle Valo 
190277b024eSKalle Valo 	/* Check for Rx data */
191277b024eSKalle Valo 	while ((skb = skb_dequeue(&adapter->rx_data_q))) {
192277b024eSKalle Valo 		atomic_dec(&adapter->rx_pending);
193277b024eSKalle Valo 		if ((adapter->delay_main_work ||
194277b024eSKalle Valo 		     adapter->iface_type == MWIFIEX_USB) &&
195277b024eSKalle Valo 		    (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) {
196277b024eSKalle Valo 			if (adapter->if_ops.submit_rem_rx_urbs)
197277b024eSKalle Valo 				adapter->if_ops.submit_rem_rx_urbs(adapter);
198277b024eSKalle Valo 			adapter->delay_main_work = false;
199277b024eSKalle Valo 			mwifiex_queue_main_work(adapter);
200277b024eSKalle Valo 		}
201277b024eSKalle Valo 		rx_info = MWIFIEX_SKB_RXCB(skb);
202277b024eSKalle Valo 		if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) {
203277b024eSKalle Valo 			if (adapter->if_ops.deaggr_pkt)
204277b024eSKalle Valo 				adapter->if_ops.deaggr_pkt(adapter, skb);
205277b024eSKalle Valo 			dev_kfree_skb_any(skb);
206277b024eSKalle Valo 		} else {
207277b024eSKalle Valo 			mwifiex_handle_rx_packet(adapter, skb);
208277b024eSKalle Valo 		}
209277b024eSKalle Valo 	}
210277b024eSKalle Valo 	spin_lock_irqsave(&adapter->rx_proc_lock, flags);
211277b024eSKalle Valo 	adapter->rx_processing = false;
212277b024eSKalle Valo 	spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
213277b024eSKalle Valo 
214277b024eSKalle Valo exit_rx_proc:
215277b024eSKalle Valo 	return 0;
216277b024eSKalle Valo }
217277b024eSKalle Valo 
218277b024eSKalle Valo /*
219277b024eSKalle Valo  * The main process.
220277b024eSKalle Valo  *
221277b024eSKalle Valo  * This function is the main procedure of the driver and handles various driver
222277b024eSKalle Valo  * operations. It runs in a loop and provides the core functionalities.
223277b024eSKalle Valo  *
224277b024eSKalle Valo  * The main responsibilities of this function are -
225277b024eSKalle Valo  *      - Ensure concurrency control
226277b024eSKalle Valo  *      - Handle pending interrupts and call interrupt handlers
227277b024eSKalle Valo  *      - Wake up the card if required
228277b024eSKalle Valo  *      - Handle command responses and call response handlers
229277b024eSKalle Valo  *      - Handle events and call event handlers
230277b024eSKalle Valo  *      - Execute pending commands
231277b024eSKalle Valo  *      - Transmit pending data packets
232277b024eSKalle Valo  */
233277b024eSKalle Valo int mwifiex_main_process(struct mwifiex_adapter *adapter)
234277b024eSKalle Valo {
235277b024eSKalle Valo 	int ret = 0;
236277b024eSKalle Valo 	unsigned long flags;
237277b024eSKalle Valo 
238277b024eSKalle Valo 	spin_lock_irqsave(&adapter->main_proc_lock, flags);
239277b024eSKalle Valo 
240277b024eSKalle Valo 	/* Check if already processing */
241277b024eSKalle Valo 	if (adapter->mwifiex_processing || adapter->main_locked) {
242277b024eSKalle Valo 		adapter->more_task_flag = true;
243277b024eSKalle Valo 		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
244277b024eSKalle Valo 		goto exit_main_proc;
245277b024eSKalle Valo 	} else {
246277b024eSKalle Valo 		adapter->mwifiex_processing = true;
247277b024eSKalle Valo 		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
248277b024eSKalle Valo 	}
249277b024eSKalle Valo process_start:
250277b024eSKalle Valo 	do {
251277b024eSKalle Valo 		if ((adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) ||
252277b024eSKalle Valo 		    (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY))
253277b024eSKalle Valo 			break;
254277b024eSKalle Valo 
255277b024eSKalle Valo 		/* For non-USB interfaces, If we process interrupts first, it
256277b024eSKalle Valo 		 * would increase RX pending even further. Avoid this by
257277b024eSKalle Valo 		 * checking if rx_pending has crossed high threshold and
258277b024eSKalle Valo 		 * schedule rx work queue and then process interrupts.
259277b024eSKalle Valo 		 * For USB interface, there are no interrupts. We already have
260277b024eSKalle Valo 		 * HIGH_RX_PENDING check in usb.c
261277b024eSKalle Valo 		 */
262277b024eSKalle Valo 		if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING &&
263277b024eSKalle Valo 		    adapter->iface_type != MWIFIEX_USB) {
264277b024eSKalle Valo 			adapter->delay_main_work = true;
265277b024eSKalle Valo 			mwifiex_queue_rx_work(adapter);
266277b024eSKalle Valo 			break;
267277b024eSKalle Valo 		}
268277b024eSKalle Valo 
269277b024eSKalle Valo 		/* Handle pending interrupt if any */
270277b024eSKalle Valo 		if (adapter->int_status) {
271277b024eSKalle Valo 			if (adapter->hs_activated)
272277b024eSKalle Valo 				mwifiex_process_hs_config(adapter);
273277b024eSKalle Valo 			if (adapter->if_ops.process_int_status)
274277b024eSKalle Valo 				adapter->if_ops.process_int_status(adapter);
275277b024eSKalle Valo 		}
276277b024eSKalle Valo 
277277b024eSKalle Valo 		if (adapter->rx_work_enabled && adapter->data_received)
278277b024eSKalle Valo 			mwifiex_queue_rx_work(adapter);
279277b024eSKalle Valo 
280277b024eSKalle Valo 		/* Need to wake up the card ? */
281277b024eSKalle Valo 		if ((adapter->ps_state == PS_STATE_SLEEP) &&
282277b024eSKalle Valo 		    (adapter->pm_wakeup_card_req &&
283277b024eSKalle Valo 		     !adapter->pm_wakeup_fw_try) &&
284277b024eSKalle Valo 		    (is_command_pending(adapter) ||
285277b024eSKalle Valo 		     !skb_queue_empty(&adapter->tx_data_q) ||
286277b024eSKalle Valo 		     !mwifiex_bypass_txlist_empty(adapter) ||
287277b024eSKalle Valo 		     !mwifiex_wmm_lists_empty(adapter))) {
288277b024eSKalle Valo 			adapter->pm_wakeup_fw_try = true;
289277b024eSKalle Valo 			mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3));
290277b024eSKalle Valo 			adapter->if_ops.wakeup(adapter);
291277b024eSKalle Valo 			continue;
292277b024eSKalle Valo 		}
293277b024eSKalle Valo 
294277b024eSKalle Valo 		if (IS_CARD_RX_RCVD(adapter)) {
295277b024eSKalle Valo 			adapter->data_received = false;
296277b024eSKalle Valo 			adapter->pm_wakeup_fw_try = false;
297277b024eSKalle Valo 			del_timer(&adapter->wakeup_timer);
298277b024eSKalle Valo 			if (adapter->ps_state == PS_STATE_SLEEP)
299277b024eSKalle Valo 				adapter->ps_state = PS_STATE_AWAKE;
300277b024eSKalle Valo 		} else {
301277b024eSKalle Valo 			/* We have tried to wakeup the card already */
302277b024eSKalle Valo 			if (adapter->pm_wakeup_fw_try)
303277b024eSKalle Valo 				break;
304277b024eSKalle Valo 			if (adapter->ps_state != PS_STATE_AWAKE)
305277b024eSKalle Valo 				break;
306277b024eSKalle Valo 			if (adapter->tx_lock_flag) {
307277b024eSKalle Valo 				if (adapter->iface_type == MWIFIEX_USB) {
308277b024eSKalle Valo 					if (!adapter->usb_mc_setup)
309277b024eSKalle Valo 						break;
310277b024eSKalle Valo 				} else
311277b024eSKalle Valo 					break;
312277b024eSKalle Valo 			}
313277b024eSKalle Valo 
314277b024eSKalle Valo 			if ((!adapter->scan_chan_gap_enabled &&
315277b024eSKalle Valo 			     adapter->scan_processing) || adapter->data_sent ||
316277b024eSKalle Valo 			     mwifiex_is_tdls_chan_switching
317277b024eSKalle Valo 			     (mwifiex_get_priv(adapter,
318277b024eSKalle Valo 					       MWIFIEX_BSS_ROLE_STA)) ||
319277b024eSKalle Valo 			    (mwifiex_wmm_lists_empty(adapter) &&
320277b024eSKalle Valo 			     mwifiex_bypass_txlist_empty(adapter) &&
321277b024eSKalle Valo 			     skb_queue_empty(&adapter->tx_data_q))) {
322277b024eSKalle Valo 				if (adapter->cmd_sent || adapter->curr_cmd ||
323277b024eSKalle Valo 					!mwifiex_is_send_cmd_allowed
324277b024eSKalle Valo 						(mwifiex_get_priv(adapter,
325277b024eSKalle Valo 						MWIFIEX_BSS_ROLE_STA)) ||
326277b024eSKalle Valo 				    (!is_command_pending(adapter)))
327277b024eSKalle Valo 					break;
328277b024eSKalle Valo 			}
329277b024eSKalle Valo 		}
330277b024eSKalle Valo 
331277b024eSKalle Valo 		/* Check for event */
332277b024eSKalle Valo 		if (adapter->event_received) {
333277b024eSKalle Valo 			adapter->event_received = false;
334277b024eSKalle Valo 			mwifiex_process_event(adapter);
335277b024eSKalle Valo 		}
336277b024eSKalle Valo 
337277b024eSKalle Valo 		/* Check for Cmd Resp */
338277b024eSKalle Valo 		if (adapter->cmd_resp_received) {
339277b024eSKalle Valo 			adapter->cmd_resp_received = false;
340277b024eSKalle Valo 			mwifiex_process_cmdresp(adapter);
341277b024eSKalle Valo 
342277b024eSKalle Valo 			/* call mwifiex back when init_fw is done */
343277b024eSKalle Valo 			if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) {
344277b024eSKalle Valo 				adapter->hw_status = MWIFIEX_HW_STATUS_READY;
345277b024eSKalle Valo 				mwifiex_init_fw_complete(adapter);
346277b024eSKalle Valo 			}
347277b024eSKalle Valo 		}
348277b024eSKalle Valo 
349277b024eSKalle Valo 		/* Check if we need to confirm Sleep Request
350277b024eSKalle Valo 		   received previously */
351277b024eSKalle Valo 		if (adapter->ps_state == PS_STATE_PRE_SLEEP) {
352277b024eSKalle Valo 			if (!adapter->cmd_sent && !adapter->curr_cmd)
353277b024eSKalle Valo 				mwifiex_check_ps_cond(adapter);
354277b024eSKalle Valo 		}
355277b024eSKalle Valo 
356277b024eSKalle Valo 		/* * The ps_state may have been changed during processing of
357277b024eSKalle Valo 		 * Sleep Request event.
358277b024eSKalle Valo 		 */
359277b024eSKalle Valo 		if ((adapter->ps_state == PS_STATE_SLEEP) ||
360277b024eSKalle Valo 		    (adapter->ps_state == PS_STATE_PRE_SLEEP) ||
361277b024eSKalle Valo 		    (adapter->ps_state == PS_STATE_SLEEP_CFM)) {
362277b024eSKalle Valo 			continue;
363277b024eSKalle Valo 		}
364277b024eSKalle Valo 
365277b024eSKalle Valo 		if (adapter->tx_lock_flag) {
366277b024eSKalle Valo 			if (adapter->iface_type == MWIFIEX_USB) {
367277b024eSKalle Valo 				if (!adapter->usb_mc_setup)
368277b024eSKalle Valo 					continue;
369277b024eSKalle Valo 			} else
370277b024eSKalle Valo 				continue;
371277b024eSKalle Valo 		}
372277b024eSKalle Valo 
373277b024eSKalle Valo 		if (!adapter->cmd_sent && !adapter->curr_cmd &&
374277b024eSKalle Valo 		    mwifiex_is_send_cmd_allowed
375277b024eSKalle Valo 		    (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
376277b024eSKalle Valo 			if (mwifiex_exec_next_cmd(adapter) == -1) {
377277b024eSKalle Valo 				ret = -1;
378277b024eSKalle Valo 				break;
379277b024eSKalle Valo 			}
380277b024eSKalle Valo 		}
381277b024eSKalle Valo 
382277b024eSKalle Valo 		/** If USB Multi channel setup ongoing,
383277b024eSKalle Valo 		 *  wait for ready to tx data.
384277b024eSKalle Valo 		 */
385277b024eSKalle Valo 		if (adapter->iface_type == MWIFIEX_USB &&
386277b024eSKalle Valo 		    adapter->usb_mc_setup)
387277b024eSKalle Valo 			continue;
388277b024eSKalle Valo 
389277b024eSKalle Valo 		if ((adapter->scan_chan_gap_enabled ||
390277b024eSKalle Valo 		     !adapter->scan_processing) &&
391277b024eSKalle Valo 		    !adapter->data_sent &&
392277b024eSKalle Valo 		    !skb_queue_empty(&adapter->tx_data_q)) {
393277b024eSKalle Valo 			mwifiex_process_tx_queue(adapter);
394277b024eSKalle Valo 			if (adapter->hs_activated) {
395277b024eSKalle Valo 				adapter->is_hs_configured = false;
396277b024eSKalle Valo 				mwifiex_hs_activated_event
397277b024eSKalle Valo 					(mwifiex_get_priv
398277b024eSKalle Valo 					(adapter, MWIFIEX_BSS_ROLE_ANY),
399277b024eSKalle Valo 					false);
400277b024eSKalle Valo 			}
401277b024eSKalle Valo 		}
402277b024eSKalle Valo 
403277b024eSKalle Valo 		if ((adapter->scan_chan_gap_enabled ||
404277b024eSKalle Valo 		     !adapter->scan_processing) &&
405277b024eSKalle Valo 		    !adapter->data_sent &&
406277b024eSKalle Valo 		    !mwifiex_bypass_txlist_empty(adapter) &&
407277b024eSKalle Valo 		    !mwifiex_is_tdls_chan_switching
408277b024eSKalle Valo 			(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
409277b024eSKalle Valo 			mwifiex_process_bypass_tx(adapter);
410277b024eSKalle Valo 			if (adapter->hs_activated) {
411277b024eSKalle Valo 				adapter->is_hs_configured = false;
412277b024eSKalle Valo 				mwifiex_hs_activated_event
413277b024eSKalle Valo 					(mwifiex_get_priv
414277b024eSKalle Valo 					 (adapter, MWIFIEX_BSS_ROLE_ANY),
415277b024eSKalle Valo 					 false);
416277b024eSKalle Valo 			}
417277b024eSKalle Valo 		}
418277b024eSKalle Valo 
419277b024eSKalle Valo 		if ((adapter->scan_chan_gap_enabled ||
420277b024eSKalle Valo 		     !adapter->scan_processing) &&
421277b024eSKalle Valo 		    !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter) &&
422277b024eSKalle Valo 		    !mwifiex_is_tdls_chan_switching
423277b024eSKalle Valo 			(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
424277b024eSKalle Valo 			mwifiex_wmm_process_tx(adapter);
425277b024eSKalle Valo 			if (adapter->hs_activated) {
426277b024eSKalle Valo 				adapter->is_hs_configured = false;
427277b024eSKalle Valo 				mwifiex_hs_activated_event
428277b024eSKalle Valo 					(mwifiex_get_priv
429277b024eSKalle Valo 					 (adapter, MWIFIEX_BSS_ROLE_ANY),
430277b024eSKalle Valo 					 false);
431277b024eSKalle Valo 			}
432277b024eSKalle Valo 		}
433277b024eSKalle Valo 
434277b024eSKalle Valo 		if (adapter->delay_null_pkt && !adapter->cmd_sent &&
435277b024eSKalle Valo 		    !adapter->curr_cmd && !is_command_pending(adapter) &&
436277b024eSKalle Valo 		    (mwifiex_wmm_lists_empty(adapter) &&
437277b024eSKalle Valo 		     mwifiex_bypass_txlist_empty(adapter) &&
438277b024eSKalle Valo 		     skb_queue_empty(&adapter->tx_data_q))) {
439277b024eSKalle Valo 			if (!mwifiex_send_null_packet
440277b024eSKalle Valo 			    (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
441277b024eSKalle Valo 			     MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET |
442277b024eSKalle Valo 			     MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) {
443277b024eSKalle Valo 				adapter->delay_null_pkt = false;
444277b024eSKalle Valo 				adapter->ps_state = PS_STATE_SLEEP;
445277b024eSKalle Valo 			}
446277b024eSKalle Valo 			break;
447277b024eSKalle Valo 		}
448277b024eSKalle Valo 	} while (true);
449277b024eSKalle Valo 
450277b024eSKalle Valo 	spin_lock_irqsave(&adapter->main_proc_lock, flags);
451277b024eSKalle Valo 	if (adapter->more_task_flag) {
452277b024eSKalle Valo 		adapter->more_task_flag = false;
453277b024eSKalle Valo 		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
454277b024eSKalle Valo 		goto process_start;
455277b024eSKalle Valo 	}
456277b024eSKalle Valo 	adapter->mwifiex_processing = false;
457277b024eSKalle Valo 	spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
458277b024eSKalle Valo 
459277b024eSKalle Valo exit_main_proc:
460277b024eSKalle Valo 	if (adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING)
461277b024eSKalle Valo 		mwifiex_shutdown_drv(adapter);
462277b024eSKalle Valo 	return ret;
463277b024eSKalle Valo }
464277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_main_process);
465277b024eSKalle Valo 
466277b024eSKalle Valo /*
467277b024eSKalle Valo  * This function frees the adapter structure.
468277b024eSKalle Valo  *
469277b024eSKalle Valo  * Additionally, this closes the netlink socket, frees the timers
470277b024eSKalle Valo  * and private structures.
471277b024eSKalle Valo  */
472277b024eSKalle Valo static void mwifiex_free_adapter(struct mwifiex_adapter *adapter)
473277b024eSKalle Valo {
474277b024eSKalle Valo 	if (!adapter) {
475277b024eSKalle Valo 		pr_err("%s: adapter is NULL\n", __func__);
476277b024eSKalle Valo 		return;
477277b024eSKalle Valo 	}
478277b024eSKalle Valo 
479277b024eSKalle Valo 	mwifiex_unregister(adapter);
480277b024eSKalle Valo 	pr_debug("info: %s: free adapter\n", __func__);
481277b024eSKalle Valo }
482277b024eSKalle Valo 
483277b024eSKalle Valo /*
484277b024eSKalle Valo  * This function cancels all works in the queue and destroys
485277b024eSKalle Valo  * the main workqueue.
486277b024eSKalle Valo  */
487277b024eSKalle Valo static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
488277b024eSKalle Valo {
489277b024eSKalle Valo 	flush_workqueue(adapter->workqueue);
490277b024eSKalle Valo 	destroy_workqueue(adapter->workqueue);
491277b024eSKalle Valo 	adapter->workqueue = NULL;
492277b024eSKalle Valo 
493277b024eSKalle Valo 	if (adapter->rx_workqueue) {
494277b024eSKalle Valo 		flush_workqueue(adapter->rx_workqueue);
495277b024eSKalle Valo 		destroy_workqueue(adapter->rx_workqueue);
496277b024eSKalle Valo 		adapter->rx_workqueue = NULL;
497277b024eSKalle Valo 	}
498277b024eSKalle Valo }
499277b024eSKalle Valo 
500277b024eSKalle Valo /*
501277b024eSKalle Valo  * This function gets firmware and initializes it.
502277b024eSKalle Valo  *
503277b024eSKalle Valo  * The main initialization steps followed are -
504277b024eSKalle Valo  *      - Download the correct firmware to card
505277b024eSKalle Valo  *      - Issue the init commands to firmware
506277b024eSKalle Valo  */
507277b024eSKalle Valo static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
508277b024eSKalle Valo {
509277b024eSKalle Valo 	int ret;
510277b024eSKalle Valo 	char fmt[64];
511277b024eSKalle Valo 	struct mwifiex_private *priv;
512277b024eSKalle Valo 	struct mwifiex_adapter *adapter = context;
513277b024eSKalle Valo 	struct mwifiex_fw_image fw;
514277b024eSKalle Valo 	struct semaphore *sem = adapter->card_sem;
515277b024eSKalle Valo 	bool init_failed = false;
516277b024eSKalle Valo 	struct wireless_dev *wdev;
517277b024eSKalle Valo 
518277b024eSKalle Valo 	if (!firmware) {
519277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR,
520277b024eSKalle Valo 			    "Failed to get firmware %s\n", adapter->fw_name);
521277b024eSKalle Valo 		goto err_dnld_fw;
522277b024eSKalle Valo 	}
523277b024eSKalle Valo 
524277b024eSKalle Valo 	memset(&fw, 0, sizeof(struct mwifiex_fw_image));
525277b024eSKalle Valo 	adapter->firmware = firmware;
526277b024eSKalle Valo 	fw.fw_buf = (u8 *) adapter->firmware->data;
527277b024eSKalle Valo 	fw.fw_len = adapter->firmware->size;
528277b024eSKalle Valo 
529277b024eSKalle Valo 	if (adapter->if_ops.dnld_fw)
530277b024eSKalle Valo 		ret = adapter->if_ops.dnld_fw(adapter, &fw);
531277b024eSKalle Valo 	else
532277b024eSKalle Valo 		ret = mwifiex_dnld_fw(adapter, &fw);
533277b024eSKalle Valo 	if (ret == -1)
534277b024eSKalle Valo 		goto err_dnld_fw;
535277b024eSKalle Valo 
536277b024eSKalle Valo 	mwifiex_dbg(adapter, MSG, "WLAN FW is active\n");
537277b024eSKalle Valo 
538277b024eSKalle Valo 	if (cal_data_cfg) {
539277b024eSKalle Valo 		if ((request_firmware(&adapter->cal_data, cal_data_cfg,
540277b024eSKalle Valo 				      adapter->dev)) < 0)
541277b024eSKalle Valo 			mwifiex_dbg(adapter, ERROR,
542277b024eSKalle Valo 				    "Cal data request_firmware() failed\n");
543277b024eSKalle Valo 	}
544277b024eSKalle Valo 
545277b024eSKalle Valo 	/* enable host interrupt after fw dnld is successful */
546277b024eSKalle Valo 	if (adapter->if_ops.enable_int) {
547277b024eSKalle Valo 		if (adapter->if_ops.enable_int(adapter))
548277b024eSKalle Valo 			goto err_dnld_fw;
549277b024eSKalle Valo 	}
550277b024eSKalle Valo 
551277b024eSKalle Valo 	adapter->init_wait_q_woken = false;
552277b024eSKalle Valo 	ret = mwifiex_init_fw(adapter);
553277b024eSKalle Valo 	if (ret == -1) {
554277b024eSKalle Valo 		goto err_init_fw;
555277b024eSKalle Valo 	} else if (!ret) {
556277b024eSKalle Valo 		adapter->hw_status = MWIFIEX_HW_STATUS_READY;
557277b024eSKalle Valo 		goto done;
558277b024eSKalle Valo 	}
559277b024eSKalle Valo 	/* Wait for mwifiex_init to complete */
560277b024eSKalle Valo 	wait_event_interruptible(adapter->init_wait_q,
561277b024eSKalle Valo 				 adapter->init_wait_q_woken);
562277b024eSKalle Valo 	if (adapter->hw_status != MWIFIEX_HW_STATUS_READY)
563277b024eSKalle Valo 		goto err_init_fw;
564277b024eSKalle Valo 
565277b024eSKalle Valo 	priv = adapter->priv[MWIFIEX_BSS_ROLE_STA];
566277b024eSKalle Valo 	if (mwifiex_register_cfg80211(adapter)) {
567277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR,
568277b024eSKalle Valo 			    "cannot register with cfg80211\n");
569277b024eSKalle Valo 		goto err_init_fw;
570277b024eSKalle Valo 	}
571277b024eSKalle Valo 
572277b024eSKalle Valo 	if (mwifiex_init_channel_scan_gap(adapter)) {
573277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR,
574277b024eSKalle Valo 			    "could not init channel stats table\n");
575277b024eSKalle Valo 		goto err_init_fw;
576277b024eSKalle Valo 	}
577277b024eSKalle Valo 
578277b024eSKalle Valo 	if (driver_mode) {
579277b024eSKalle Valo 		driver_mode &= MWIFIEX_DRIVER_MODE_BITMASK;
580277b024eSKalle Valo 		driver_mode |= MWIFIEX_DRIVER_MODE_STA;
581277b024eSKalle Valo 	}
582277b024eSKalle Valo 
583277b024eSKalle Valo 	rtnl_lock();
584277b024eSKalle Valo 	/* Create station interface by default */
585277b024eSKalle Valo 	wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM,
586277b024eSKalle Valo 					NL80211_IFTYPE_STATION, NULL, NULL);
587277b024eSKalle Valo 	if (IS_ERR(wdev)) {
588277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR,
589277b024eSKalle Valo 			    "cannot create default STA interface\n");
590277b024eSKalle Valo 		rtnl_unlock();
591277b024eSKalle Valo 		goto err_add_intf;
592277b024eSKalle Valo 	}
593277b024eSKalle Valo 
594277b024eSKalle Valo 	if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) {
595277b024eSKalle Valo 		wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM,
596277b024eSKalle Valo 						NL80211_IFTYPE_AP, NULL, NULL);
597277b024eSKalle Valo 		if (IS_ERR(wdev)) {
598277b024eSKalle Valo 			mwifiex_dbg(adapter, ERROR,
599277b024eSKalle Valo 				    "cannot create AP interface\n");
600277b024eSKalle Valo 			rtnl_unlock();
601277b024eSKalle Valo 			goto err_add_intf;
602277b024eSKalle Valo 		}
603277b024eSKalle Valo 	}
604277b024eSKalle Valo 
605277b024eSKalle Valo 	if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) {
606277b024eSKalle Valo 		wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM,
607277b024eSKalle Valo 						NL80211_IFTYPE_P2P_CLIENT, NULL,
608277b024eSKalle Valo 						NULL);
609277b024eSKalle Valo 		if (IS_ERR(wdev)) {
610277b024eSKalle Valo 			mwifiex_dbg(adapter, ERROR,
611277b024eSKalle Valo 				    "cannot create p2p client interface\n");
612277b024eSKalle Valo 			rtnl_unlock();
613277b024eSKalle Valo 			goto err_add_intf;
614277b024eSKalle Valo 		}
615277b024eSKalle Valo 	}
616277b024eSKalle Valo 	rtnl_unlock();
617277b024eSKalle Valo 
618277b024eSKalle Valo 	mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
619277b024eSKalle Valo 	mwifiex_dbg(adapter, MSG, "driver_version = %s\n", fmt);
620277b024eSKalle Valo 	goto done;
621277b024eSKalle Valo 
622277b024eSKalle Valo err_add_intf:
623277b024eSKalle Valo 	wiphy_unregister(adapter->wiphy);
624277b024eSKalle Valo 	wiphy_free(adapter->wiphy);
625277b024eSKalle Valo err_init_fw:
626277b024eSKalle Valo 	if (adapter->if_ops.disable_int)
627277b024eSKalle Valo 		adapter->if_ops.disable_int(adapter);
628277b024eSKalle Valo err_dnld_fw:
629277b024eSKalle Valo 	mwifiex_dbg(adapter, ERROR,
630277b024eSKalle Valo 		    "info: %s: unregister device\n", __func__);
631277b024eSKalle Valo 	if (adapter->if_ops.unregister_dev)
632277b024eSKalle Valo 		adapter->if_ops.unregister_dev(adapter);
633277b024eSKalle Valo 
634277b024eSKalle Valo 	if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
635277b024eSKalle Valo 		pr_debug("info: %s: shutdown mwifiex\n", __func__);
636277b024eSKalle Valo 		adapter->init_wait_q_woken = false;
637277b024eSKalle Valo 
638277b024eSKalle Valo 		if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
639277b024eSKalle Valo 			wait_event_interruptible(adapter->init_wait_q,
640277b024eSKalle Valo 						 adapter->init_wait_q_woken);
641277b024eSKalle Valo 	}
642277b024eSKalle Valo 	adapter->surprise_removed = true;
643277b024eSKalle Valo 	mwifiex_terminate_workqueue(adapter);
644277b024eSKalle Valo 	init_failed = true;
645277b024eSKalle Valo done:
646277b024eSKalle Valo 	if (adapter->cal_data) {
647277b024eSKalle Valo 		release_firmware(adapter->cal_data);
648277b024eSKalle Valo 		adapter->cal_data = NULL;
649277b024eSKalle Valo 	}
650277b024eSKalle Valo 	if (adapter->firmware) {
651277b024eSKalle Valo 		release_firmware(adapter->firmware);
652277b024eSKalle Valo 		adapter->firmware = NULL;
653277b024eSKalle Valo 	}
654277b024eSKalle Valo 	if (init_failed)
655277b024eSKalle Valo 		mwifiex_free_adapter(adapter);
656277b024eSKalle Valo 	up(sem);
657277b024eSKalle Valo 	return;
658277b024eSKalle Valo }
659277b024eSKalle Valo 
660277b024eSKalle Valo /*
661277b024eSKalle Valo  * This function initializes the hardware and gets firmware.
662277b024eSKalle Valo  */
663277b024eSKalle Valo static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter)
664277b024eSKalle Valo {
665277b024eSKalle Valo 	int ret;
666277b024eSKalle Valo 
667277b024eSKalle Valo 	ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name,
668277b024eSKalle Valo 				      adapter->dev, GFP_KERNEL, adapter,
669277b024eSKalle Valo 				      mwifiex_fw_dpc);
670277b024eSKalle Valo 	if (ret < 0)
671277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR,
672277b024eSKalle Valo 			    "request_firmware_nowait error %d\n", ret);
673277b024eSKalle Valo 	return ret;
674277b024eSKalle Valo }
675277b024eSKalle Valo 
676277b024eSKalle Valo /*
677277b024eSKalle Valo  * CFG802.11 network device handler for open.
678277b024eSKalle Valo  *
679277b024eSKalle Valo  * Starts the data queue.
680277b024eSKalle Valo  */
681277b024eSKalle Valo static int
682277b024eSKalle Valo mwifiex_open(struct net_device *dev)
683277b024eSKalle Valo {
684277b024eSKalle Valo 	netif_carrier_off(dev);
685277b024eSKalle Valo 
686277b024eSKalle Valo 	return 0;
687277b024eSKalle Valo }
688277b024eSKalle Valo 
689277b024eSKalle Valo /*
690277b024eSKalle Valo  * CFG802.11 network device handler for close.
691277b024eSKalle Valo  */
692277b024eSKalle Valo static int
693277b024eSKalle Valo mwifiex_close(struct net_device *dev)
694277b024eSKalle Valo {
695277b024eSKalle Valo 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
696277b024eSKalle Valo 
697277b024eSKalle Valo 	if (priv->scan_request) {
698277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, INFO,
699277b024eSKalle Valo 			    "aborting scan on ndo_stop\n");
700277b024eSKalle Valo 		cfg80211_scan_done(priv->scan_request, 1);
701277b024eSKalle Valo 		priv->scan_request = NULL;
702277b024eSKalle Valo 		priv->scan_aborting = true;
703277b024eSKalle Valo 	}
704277b024eSKalle Valo 
705*eaf46b5fSXinming Hu 	if (priv->sched_scanning) {
706*eaf46b5fSXinming Hu 		mwifiex_dbg(priv->adapter, INFO,
707*eaf46b5fSXinming Hu 			    "aborting bgscan on ndo_stop\n");
708*eaf46b5fSXinming Hu 		mwifiex_stop_bg_scan(priv);
709*eaf46b5fSXinming Hu 		cfg80211_sched_scan_stopped(priv->wdev.wiphy);
710*eaf46b5fSXinming Hu 	}
711*eaf46b5fSXinming Hu 
712277b024eSKalle Valo 	return 0;
713277b024eSKalle Valo }
714277b024eSKalle Valo 
715277b024eSKalle Valo static bool
716277b024eSKalle Valo mwifiex_bypass_tx_queue(struct mwifiex_private *priv,
717277b024eSKalle Valo 			struct sk_buff *skb)
718277b024eSKalle Valo {
719277b024eSKalle Valo 	struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
720277b024eSKalle Valo 
721277b024eSKalle Valo 	if (ntohs(eth_hdr->h_proto) == ETH_P_PAE ||
722277b024eSKalle Valo 	    mwifiex_is_skb_mgmt_frame(skb) ||
723277b024eSKalle Valo 	    (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA &&
724277b024eSKalle Valo 	     ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
725277b024eSKalle Valo 	     (ntohs(eth_hdr->h_proto) == ETH_P_TDLS))) {
726277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, DATA,
727277b024eSKalle Valo 			    "bypass txqueue; eth type %#x, mgmt %d\n",
728277b024eSKalle Valo 			     ntohs(eth_hdr->h_proto),
729277b024eSKalle Valo 			     mwifiex_is_skb_mgmt_frame(skb));
730277b024eSKalle Valo 		return true;
731277b024eSKalle Valo 	}
732277b024eSKalle Valo 
733277b024eSKalle Valo 	return false;
734277b024eSKalle Valo }
735277b024eSKalle Valo /*
736277b024eSKalle Valo  * Add buffer into wmm tx queue and queue work to transmit it.
737277b024eSKalle Valo  */
738277b024eSKalle Valo int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb)
739277b024eSKalle Valo {
740277b024eSKalle Valo 	struct netdev_queue *txq;
741277b024eSKalle Valo 	int index = mwifiex_1d_to_wmm_queue[skb->priority];
742277b024eSKalle Valo 
743277b024eSKalle Valo 	if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) {
744277b024eSKalle Valo 		txq = netdev_get_tx_queue(priv->netdev, index);
745277b024eSKalle Valo 		if (!netif_tx_queue_stopped(txq)) {
746277b024eSKalle Valo 			netif_tx_stop_queue(txq);
747277b024eSKalle Valo 			mwifiex_dbg(priv->adapter, DATA,
748277b024eSKalle Valo 				    "stop queue: %d\n", index);
749277b024eSKalle Valo 		}
750277b024eSKalle Valo 	}
751277b024eSKalle Valo 
752277b024eSKalle Valo 	if (mwifiex_bypass_tx_queue(priv, skb)) {
753277b024eSKalle Valo 		atomic_inc(&priv->adapter->tx_pending);
754277b024eSKalle Valo 		atomic_inc(&priv->adapter->bypass_tx_pending);
755277b024eSKalle Valo 		mwifiex_wmm_add_buf_bypass_txqueue(priv, skb);
756277b024eSKalle Valo 	 } else {
757277b024eSKalle Valo 		atomic_inc(&priv->adapter->tx_pending);
758277b024eSKalle Valo 		mwifiex_wmm_add_buf_txqueue(priv, skb);
759277b024eSKalle Valo 	 }
760277b024eSKalle Valo 
761277b024eSKalle Valo 	mwifiex_queue_main_work(priv->adapter);
762277b024eSKalle Valo 
763277b024eSKalle Valo 	return 0;
764277b024eSKalle Valo }
765277b024eSKalle Valo 
766277b024eSKalle Valo struct sk_buff *
767277b024eSKalle Valo mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
768277b024eSKalle Valo 				struct sk_buff *skb, u8 flag, u64 *cookie)
769277b024eSKalle Valo {
770277b024eSKalle Valo 	struct sk_buff *orig_skb = skb;
771277b024eSKalle Valo 	struct mwifiex_txinfo *tx_info, *orig_tx_info;
772277b024eSKalle Valo 
773277b024eSKalle Valo 	skb = skb_clone(skb, GFP_ATOMIC);
774277b024eSKalle Valo 	if (skb) {
775277b024eSKalle Valo 		unsigned long flags;
776277b024eSKalle Valo 		int id;
777277b024eSKalle Valo 
778277b024eSKalle Valo 		spin_lock_irqsave(&priv->ack_status_lock, flags);
779277b024eSKalle Valo 		id = idr_alloc(&priv->ack_status_frames, orig_skb,
780ee548d4bSAmitkumar Karwar 			       1, 0x10, GFP_ATOMIC);
781277b024eSKalle Valo 		spin_unlock_irqrestore(&priv->ack_status_lock, flags);
782277b024eSKalle Valo 
783277b024eSKalle Valo 		if (id >= 0) {
784277b024eSKalle Valo 			tx_info = MWIFIEX_SKB_TXCB(skb);
785277b024eSKalle Valo 			tx_info->ack_frame_id = id;
786277b024eSKalle Valo 			tx_info->flags |= flag;
787277b024eSKalle Valo 			orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb);
788277b024eSKalle Valo 			orig_tx_info->ack_frame_id = id;
789277b024eSKalle Valo 			orig_tx_info->flags |= flag;
790277b024eSKalle Valo 
791277b024eSKalle Valo 			if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie)
792277b024eSKalle Valo 				orig_tx_info->cookie = *cookie;
793277b024eSKalle Valo 
794277b024eSKalle Valo 		} else if (skb_shared(skb)) {
795277b024eSKalle Valo 			kfree_skb(orig_skb);
796277b024eSKalle Valo 		} else {
797277b024eSKalle Valo 			kfree_skb(skb);
798277b024eSKalle Valo 			skb = orig_skb;
799277b024eSKalle Valo 		}
800277b024eSKalle Valo 	} else {
801277b024eSKalle Valo 		/* couldn't clone -- lose tx status ... */
802277b024eSKalle Valo 		skb = orig_skb;
803277b024eSKalle Valo 	}
804277b024eSKalle Valo 
805277b024eSKalle Valo 	return skb;
806277b024eSKalle Valo }
807277b024eSKalle Valo 
808277b024eSKalle Valo /*
809277b024eSKalle Valo  * CFG802.11 network device handler for data transmission.
810277b024eSKalle Valo  */
811277b024eSKalle Valo static int
812277b024eSKalle Valo mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
813277b024eSKalle Valo {
814277b024eSKalle Valo 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
815277b024eSKalle Valo 	struct sk_buff *new_skb;
816277b024eSKalle Valo 	struct mwifiex_txinfo *tx_info;
817277b024eSKalle Valo 	bool multicast;
818277b024eSKalle Valo 
819277b024eSKalle Valo 	mwifiex_dbg(priv->adapter, DATA,
820277b024eSKalle Valo 		    "data: %lu BSS(%d-%d): Data <= kernel\n",
821277b024eSKalle Valo 		    jiffies, priv->bss_type, priv->bss_num);
822277b024eSKalle Valo 
823277b024eSKalle Valo 	if (priv->adapter->surprise_removed) {
824277b024eSKalle Valo 		kfree_skb(skb);
825277b024eSKalle Valo 		priv->stats.tx_dropped++;
826277b024eSKalle Valo 		return 0;
827277b024eSKalle Valo 	}
828277b024eSKalle Valo 	if (!skb->len || (skb->len > ETH_FRAME_LEN)) {
829277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, ERROR,
830277b024eSKalle Valo 			    "Tx: bad skb len %d\n", skb->len);
831277b024eSKalle Valo 		kfree_skb(skb);
832277b024eSKalle Valo 		priv->stats.tx_dropped++;
833277b024eSKalle Valo 		return 0;
834277b024eSKalle Valo 	}
835277b024eSKalle Valo 	if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) {
836277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, DATA,
837277b024eSKalle Valo 			    "data: Tx: insufficient skb headroom %d\n",
838277b024eSKalle Valo 			    skb_headroom(skb));
839277b024eSKalle Valo 		/* Insufficient skb headroom - allocate a new skb */
840277b024eSKalle Valo 		new_skb =
841277b024eSKalle Valo 			skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
842277b024eSKalle Valo 		if (unlikely(!new_skb)) {
843277b024eSKalle Valo 			mwifiex_dbg(priv->adapter, ERROR,
844277b024eSKalle Valo 				    "Tx: cannot alloca new_skb\n");
845277b024eSKalle Valo 			kfree_skb(skb);
846277b024eSKalle Valo 			priv->stats.tx_dropped++;
847277b024eSKalle Valo 			return 0;
848277b024eSKalle Valo 		}
849277b024eSKalle Valo 		kfree_skb(skb);
850277b024eSKalle Valo 		skb = new_skb;
851277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, INFO,
852277b024eSKalle Valo 			    "info: new skb headroomd %d\n",
853277b024eSKalle Valo 			    skb_headroom(skb));
854277b024eSKalle Valo 	}
855277b024eSKalle Valo 
856277b024eSKalle Valo 	tx_info = MWIFIEX_SKB_TXCB(skb);
857277b024eSKalle Valo 	memset(tx_info, 0, sizeof(*tx_info));
858277b024eSKalle Valo 	tx_info->bss_num = priv->bss_num;
859277b024eSKalle Valo 	tx_info->bss_type = priv->bss_type;
860277b024eSKalle Valo 	tx_info->pkt_len = skb->len;
861277b024eSKalle Valo 
862277b024eSKalle Valo 	multicast = is_multicast_ether_addr(skb->data);
863277b024eSKalle Valo 
864277b024eSKalle Valo 	if (unlikely(!multicast && skb->sk &&
865277b024eSKalle Valo 		     skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS &&
866277b024eSKalle Valo 		     priv->adapter->fw_api_ver == MWIFIEX_FW_V15))
867277b024eSKalle Valo 		skb = mwifiex_clone_skb_for_tx_status(priv,
868277b024eSKalle Valo 						      skb,
869277b024eSKalle Valo 					MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL);
870277b024eSKalle Valo 
871277b024eSKalle Valo 	/* Record the current time the packet was queued; used to
872277b024eSKalle Valo 	 * determine the amount of time the packet was queued in
873277b024eSKalle Valo 	 * the driver before it was sent to the firmware.
874277b024eSKalle Valo 	 * The delay is then sent along with the packet to the
875277b024eSKalle Valo 	 * firmware for aggregate delay calculation for stats and
876277b024eSKalle Valo 	 * MSDU lifetime expiry.
877277b024eSKalle Valo 	 */
878277b024eSKalle Valo 	__net_timestamp(skb);
879277b024eSKalle Valo 
880277b024eSKalle Valo 	if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
881277b024eSKalle Valo 	    priv->bss_type == MWIFIEX_BSS_TYPE_STA &&
882277b024eSKalle Valo 	    !ether_addr_equal_unaligned(priv->cfg_bssid, skb->data)) {
883277b024eSKalle Valo 		if (priv->adapter->auto_tdls && priv->check_tdls_tx)
884277b024eSKalle Valo 			mwifiex_tdls_check_tx(priv, skb);
885277b024eSKalle Valo 	}
886277b024eSKalle Valo 
887277b024eSKalle Valo 	mwifiex_queue_tx_pkt(priv, skb);
888277b024eSKalle Valo 
889277b024eSKalle Valo 	return 0;
890277b024eSKalle Valo }
891277b024eSKalle Valo 
892277b024eSKalle Valo /*
893277b024eSKalle Valo  * CFG802.11 network device handler for setting MAC address.
894277b024eSKalle Valo  */
895277b024eSKalle Valo static int
896277b024eSKalle Valo mwifiex_set_mac_address(struct net_device *dev, void *addr)
897277b024eSKalle Valo {
898277b024eSKalle Valo 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
899277b024eSKalle Valo 	struct sockaddr *hw_addr = addr;
900277b024eSKalle Valo 	int ret;
901277b024eSKalle Valo 
902277b024eSKalle Valo 	memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN);
903277b024eSKalle Valo 
904277b024eSKalle Valo 	/* Send request to firmware */
905277b024eSKalle Valo 	ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS,
906277b024eSKalle Valo 			       HostCmd_ACT_GEN_SET, 0, NULL, true);
907277b024eSKalle Valo 
908277b024eSKalle Valo 	if (!ret)
909277b024eSKalle Valo 		memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN);
910277b024eSKalle Valo 	else
911277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, ERROR,
912277b024eSKalle Valo 			    "set mac address failed: ret=%d\n", ret);
913277b024eSKalle Valo 
914277b024eSKalle Valo 	memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN);
915277b024eSKalle Valo 
916277b024eSKalle Valo 	return ret;
917277b024eSKalle Valo }
918277b024eSKalle Valo 
919277b024eSKalle Valo /*
920277b024eSKalle Valo  * CFG802.11 network device handler for setting multicast list.
921277b024eSKalle Valo  */
922277b024eSKalle Valo static void mwifiex_set_multicast_list(struct net_device *dev)
923277b024eSKalle Valo {
924277b024eSKalle Valo 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
925277b024eSKalle Valo 	struct mwifiex_multicast_list mcast_list;
926277b024eSKalle Valo 
927277b024eSKalle Valo 	if (dev->flags & IFF_PROMISC) {
928277b024eSKalle Valo 		mcast_list.mode = MWIFIEX_PROMISC_MODE;
929277b024eSKalle Valo 	} else if (dev->flags & IFF_ALLMULTI ||
930277b024eSKalle Valo 		   netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) {
931277b024eSKalle Valo 		mcast_list.mode = MWIFIEX_ALL_MULTI_MODE;
932277b024eSKalle Valo 	} else {
933277b024eSKalle Valo 		mcast_list.mode = MWIFIEX_MULTICAST_MODE;
934277b024eSKalle Valo 		mcast_list.num_multicast_addr =
935277b024eSKalle Valo 			mwifiex_copy_mcast_addr(&mcast_list, dev);
936277b024eSKalle Valo 	}
937277b024eSKalle Valo 	mwifiex_request_set_multicast_list(priv, &mcast_list);
938277b024eSKalle Valo }
939277b024eSKalle Valo 
940277b024eSKalle Valo /*
941277b024eSKalle Valo  * CFG802.11 network device handler for transmission timeout.
942277b024eSKalle Valo  */
943277b024eSKalle Valo static void
944277b024eSKalle Valo mwifiex_tx_timeout(struct net_device *dev)
945277b024eSKalle Valo {
946277b024eSKalle Valo 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
947277b024eSKalle Valo 
948277b024eSKalle Valo 	priv->num_tx_timeout++;
949277b024eSKalle Valo 	priv->tx_timeout_cnt++;
950277b024eSKalle Valo 	mwifiex_dbg(priv->adapter, ERROR,
951277b024eSKalle Valo 		    "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n",
952277b024eSKalle Valo 		    jiffies, priv->tx_timeout_cnt, priv->bss_type,
953277b024eSKalle Valo 		    priv->bss_num);
954277b024eSKalle Valo 	mwifiex_set_trans_start(dev);
955277b024eSKalle Valo 
956277b024eSKalle Valo 	if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD &&
957277b024eSKalle Valo 	    priv->adapter->if_ops.card_reset) {
958277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, ERROR,
959277b024eSKalle Valo 			    "tx_timeout_cnt exceeds threshold.\t"
960277b024eSKalle Valo 			    "Triggering card reset!\n");
961277b024eSKalle Valo 		priv->adapter->if_ops.card_reset(priv->adapter);
962277b024eSKalle Valo 	}
963277b024eSKalle Valo }
964277b024eSKalle Valo 
965277b024eSKalle Valo void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter)
966277b024eSKalle Valo {
967277b024eSKalle Valo 	struct usb_card_rec *card = adapter->card;
968277b024eSKalle Valo 	struct mwifiex_private *priv;
969277b024eSKalle Valo 	u16 tx_buf_size;
970277b024eSKalle Valo 	int i, ret;
971277b024eSKalle Valo 
972277b024eSKalle Valo 	card->mc_resync_flag = true;
973277b024eSKalle Valo 	for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) {
974277b024eSKalle Valo 		if (atomic_read(&card->port[i].tx_data_urb_pending)) {
975277b024eSKalle Valo 			mwifiex_dbg(adapter, WARN, "pending data urb in sys\n");
976277b024eSKalle Valo 			return;
977277b024eSKalle Valo 		}
978277b024eSKalle Valo 	}
979277b024eSKalle Valo 
980277b024eSKalle Valo 	card->mc_resync_flag = false;
981277b024eSKalle Valo 	tx_buf_size = 0xffff;
982277b024eSKalle Valo 	priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
983277b024eSKalle Valo 	ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
984277b024eSKalle Valo 			       HostCmd_ACT_GEN_SET, 0, &tx_buf_size, false);
985277b024eSKalle Valo 	if (ret)
986277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR,
987277b024eSKalle Valo 			    "send reconfig tx buf size cmd err\n");
988277b024eSKalle Valo }
989277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_multi_chan_resync);
990277b024eSKalle Valo 
991277b024eSKalle Valo void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter)
992277b024eSKalle Valo {
993277b024eSKalle Valo 	void *p;
994277b024eSKalle Valo 	char drv_version[64];
995277b024eSKalle Valo 	struct usb_card_rec *cardp;
996277b024eSKalle Valo 	struct sdio_mmc_card *sdio_card;
997277b024eSKalle Valo 	struct mwifiex_private *priv;
998277b024eSKalle Valo 	int i, idx;
999277b024eSKalle Valo 	struct netdev_queue *txq;
1000277b024eSKalle Valo 	struct mwifiex_debug_info *debug_info;
1001277b024eSKalle Valo 
1002277b024eSKalle Valo 	if (adapter->drv_info_dump) {
1003277b024eSKalle Valo 		vfree(adapter->drv_info_dump);
1004277b024eSKalle Valo 		adapter->drv_info_dump = NULL;
1005277b024eSKalle Valo 		adapter->drv_info_size = 0;
1006277b024eSKalle Valo 	}
1007277b024eSKalle Valo 
1008277b024eSKalle Valo 	mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump start===\n");
1009277b024eSKalle Valo 
1010277b024eSKalle Valo 	adapter->drv_info_dump = vzalloc(MWIFIEX_DRV_INFO_SIZE_MAX);
1011277b024eSKalle Valo 
1012277b024eSKalle Valo 	if (!adapter->drv_info_dump)
1013277b024eSKalle Valo 		return;
1014277b024eSKalle Valo 
1015277b024eSKalle Valo 	p = (char *)(adapter->drv_info_dump);
1016277b024eSKalle Valo 	p += sprintf(p, "driver_name = " "\"mwifiex\"\n");
1017277b024eSKalle Valo 
1018277b024eSKalle Valo 	mwifiex_drv_get_driver_version(adapter, drv_version,
1019277b024eSKalle Valo 				       sizeof(drv_version) - 1);
1020277b024eSKalle Valo 	p += sprintf(p, "driver_version = %s\n", drv_version);
1021277b024eSKalle Valo 
1022277b024eSKalle Valo 	if (adapter->iface_type == MWIFIEX_USB) {
1023277b024eSKalle Valo 		cardp = (struct usb_card_rec *)adapter->card;
1024277b024eSKalle Valo 		p += sprintf(p, "tx_cmd_urb_pending = %d\n",
1025277b024eSKalle Valo 			     atomic_read(&cardp->tx_cmd_urb_pending));
1026277b024eSKalle Valo 		p += sprintf(p, "tx_data_urb_pending_port_0 = %d\n",
1027277b024eSKalle Valo 			     atomic_read(&cardp->port[0].tx_data_urb_pending));
1028277b024eSKalle Valo 		p += sprintf(p, "tx_data_urb_pending_port_1 = %d\n",
1029277b024eSKalle Valo 			     atomic_read(&cardp->port[1].tx_data_urb_pending));
1030277b024eSKalle Valo 		p += sprintf(p, "rx_cmd_urb_pending = %d\n",
1031277b024eSKalle Valo 			     atomic_read(&cardp->rx_cmd_urb_pending));
1032277b024eSKalle Valo 		p += sprintf(p, "rx_data_urb_pending = %d\n",
1033277b024eSKalle Valo 			     atomic_read(&cardp->rx_data_urb_pending));
1034277b024eSKalle Valo 	}
1035277b024eSKalle Valo 
1036277b024eSKalle Valo 	p += sprintf(p, "tx_pending = %d\n",
1037277b024eSKalle Valo 		     atomic_read(&adapter->tx_pending));
1038277b024eSKalle Valo 	p += sprintf(p, "rx_pending = %d\n",
1039277b024eSKalle Valo 		     atomic_read(&adapter->rx_pending));
1040277b024eSKalle Valo 
1041277b024eSKalle Valo 	if (adapter->iface_type == MWIFIEX_SDIO) {
1042277b024eSKalle Valo 		sdio_card = (struct sdio_mmc_card *)adapter->card;
1043277b024eSKalle Valo 		p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n",
1044277b024eSKalle Valo 			     sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port);
1045277b024eSKalle Valo 		p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n",
1046277b024eSKalle Valo 			     sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port);
1047277b024eSKalle Valo 	}
1048277b024eSKalle Valo 
1049277b024eSKalle Valo 	for (i = 0; i < adapter->priv_num; i++) {
1050277b024eSKalle Valo 		if (!adapter->priv[i] || !adapter->priv[i]->netdev)
1051277b024eSKalle Valo 			continue;
1052277b024eSKalle Valo 		priv = adapter->priv[i];
1053277b024eSKalle Valo 		p += sprintf(p, "\n[interface  : \"%s\"]\n",
1054277b024eSKalle Valo 			     priv->netdev->name);
1055277b024eSKalle Valo 		p += sprintf(p, "wmm_tx_pending[0] = %d\n",
1056277b024eSKalle Valo 			     atomic_read(&priv->wmm_tx_pending[0]));
1057277b024eSKalle Valo 		p += sprintf(p, "wmm_tx_pending[1] = %d\n",
1058277b024eSKalle Valo 			     atomic_read(&priv->wmm_tx_pending[1]));
1059277b024eSKalle Valo 		p += sprintf(p, "wmm_tx_pending[2] = %d\n",
1060277b024eSKalle Valo 			     atomic_read(&priv->wmm_tx_pending[2]));
1061277b024eSKalle Valo 		p += sprintf(p, "wmm_tx_pending[3] = %d\n",
1062277b024eSKalle Valo 			     atomic_read(&priv->wmm_tx_pending[3]));
1063277b024eSKalle Valo 		p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ?
1064277b024eSKalle Valo 			     "Disconnected" : "Connected");
1065277b024eSKalle Valo 		p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev)
1066277b024eSKalle Valo 			     ? "on" : "off"));
1067277b024eSKalle Valo 		for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) {
1068277b024eSKalle Valo 			txq = netdev_get_tx_queue(priv->netdev, idx);
1069277b024eSKalle Valo 			p += sprintf(p, "tx queue %d:%s  ", idx,
1070277b024eSKalle Valo 				     netif_tx_queue_stopped(txq) ?
1071277b024eSKalle Valo 				     "stopped" : "started");
1072277b024eSKalle Valo 		}
1073277b024eSKalle Valo 		p += sprintf(p, "\n%s: num_tx_timeout = %d\n",
1074277b024eSKalle Valo 			     priv->netdev->name, priv->num_tx_timeout);
1075277b024eSKalle Valo 	}
1076277b024eSKalle Valo 
10774646968bSXinming Hu 	if (adapter->iface_type == MWIFIEX_SDIO ||
10784646968bSXinming Hu 	    adapter->iface_type == MWIFIEX_PCIE) {
10794646968bSXinming Hu 		p += sprintf(p, "\n=== %s register dump===\n",
10804646968bSXinming Hu 			     adapter->iface_type == MWIFIEX_SDIO ?
10814646968bSXinming Hu 							"SDIO" : "PCIE");
1082277b024eSKalle Valo 		if (adapter->if_ops.reg_dump)
1083277b024eSKalle Valo 			p += adapter->if_ops.reg_dump(adapter, p);
1084277b024eSKalle Valo 	}
1085277b024eSKalle Valo 	p += sprintf(p, "\n=== more debug information\n");
1086277b024eSKalle Valo 	debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL);
1087277b024eSKalle Valo 	if (debug_info) {
1088277b024eSKalle Valo 		for (i = 0; i < adapter->priv_num; i++) {
1089277b024eSKalle Valo 			if (!adapter->priv[i] || !adapter->priv[i]->netdev)
1090277b024eSKalle Valo 				continue;
1091277b024eSKalle Valo 			priv = adapter->priv[i];
1092277b024eSKalle Valo 			mwifiex_get_debug_info(priv, debug_info);
1093277b024eSKalle Valo 			p += mwifiex_debug_info_to_buffer(priv, p, debug_info);
1094277b024eSKalle Valo 			break;
1095277b024eSKalle Valo 		}
1096277b024eSKalle Valo 		kfree(debug_info);
1097277b024eSKalle Valo 	}
1098277b024eSKalle Valo 
1099277b024eSKalle Valo 	adapter->drv_info_size = p - adapter->drv_info_dump;
1100277b024eSKalle Valo 	mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump end===\n");
1101277b024eSKalle Valo }
1102277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_drv_info_dump);
1103277b024eSKalle Valo 
1104277b024eSKalle Valo void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter)
1105277b024eSKalle Valo {
1106277b024eSKalle Valo 	u8 idx, *dump_data, *fw_dump_ptr;
1107277b024eSKalle Valo 	u32 dump_len;
1108277b024eSKalle Valo 
1109277b024eSKalle Valo 	dump_len = (strlen("========Start dump driverinfo========\n") +
1110277b024eSKalle Valo 		       adapter->drv_info_size +
1111277b024eSKalle Valo 		       strlen("\n========End dump========\n"));
1112277b024eSKalle Valo 
1113277b024eSKalle Valo 	for (idx = 0; idx < adapter->num_mem_types; idx++) {
1114277b024eSKalle Valo 		struct memory_type_mapping *entry =
1115277b024eSKalle Valo 				&adapter->mem_type_mapping_tbl[idx];
1116277b024eSKalle Valo 
1117277b024eSKalle Valo 		if (entry->mem_ptr) {
1118277b024eSKalle Valo 			dump_len += (strlen("========Start dump ") +
1119277b024eSKalle Valo 					strlen(entry->mem_name) +
1120277b024eSKalle Valo 					strlen("========\n") +
1121277b024eSKalle Valo 					(entry->mem_size + 1) +
1122277b024eSKalle Valo 					strlen("\n========End dump========\n"));
1123277b024eSKalle Valo 		}
1124277b024eSKalle Valo 	}
1125277b024eSKalle Valo 
1126277b024eSKalle Valo 	dump_data = vzalloc(dump_len + 1);
1127277b024eSKalle Valo 	if (!dump_data)
1128277b024eSKalle Valo 		goto done;
1129277b024eSKalle Valo 
1130277b024eSKalle Valo 	fw_dump_ptr = dump_data;
1131277b024eSKalle Valo 
1132277b024eSKalle Valo 	/* Dump all the memory data into single file, a userspace script will
1133277b024eSKalle Valo 	 * be used to split all the memory data to multiple files
1134277b024eSKalle Valo 	 */
1135277b024eSKalle Valo 	mwifiex_dbg(adapter, MSG,
1136277b024eSKalle Valo 		    "== mwifiex dump information to /sys/class/devcoredump start");
1137277b024eSKalle Valo 
1138277b024eSKalle Valo 	strcpy(fw_dump_ptr, "========Start dump driverinfo========\n");
1139277b024eSKalle Valo 	fw_dump_ptr += strlen("========Start dump driverinfo========\n");
1140277b024eSKalle Valo 	memcpy(fw_dump_ptr, adapter->drv_info_dump, adapter->drv_info_size);
1141277b024eSKalle Valo 	fw_dump_ptr += adapter->drv_info_size;
1142277b024eSKalle Valo 	strcpy(fw_dump_ptr, "\n========End dump========\n");
1143277b024eSKalle Valo 	fw_dump_ptr += strlen("\n========End dump========\n");
1144277b024eSKalle Valo 
1145277b024eSKalle Valo 	for (idx = 0; idx < adapter->num_mem_types; idx++) {
1146277b024eSKalle Valo 		struct memory_type_mapping *entry =
1147277b024eSKalle Valo 					&adapter->mem_type_mapping_tbl[idx];
1148277b024eSKalle Valo 
1149277b024eSKalle Valo 		if (entry->mem_ptr) {
1150277b024eSKalle Valo 			strcpy(fw_dump_ptr, "========Start dump ");
1151277b024eSKalle Valo 			fw_dump_ptr += strlen("========Start dump ");
1152277b024eSKalle Valo 
1153277b024eSKalle Valo 			strcpy(fw_dump_ptr, entry->mem_name);
1154277b024eSKalle Valo 			fw_dump_ptr += strlen(entry->mem_name);
1155277b024eSKalle Valo 
1156277b024eSKalle Valo 			strcpy(fw_dump_ptr, "========\n");
1157277b024eSKalle Valo 			fw_dump_ptr += strlen("========\n");
1158277b024eSKalle Valo 
1159277b024eSKalle Valo 			memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size);
1160277b024eSKalle Valo 			fw_dump_ptr += entry->mem_size;
1161277b024eSKalle Valo 
1162277b024eSKalle Valo 			strcpy(fw_dump_ptr, "\n========End dump========\n");
1163277b024eSKalle Valo 			fw_dump_ptr += strlen("\n========End dump========\n");
1164277b024eSKalle Valo 		}
1165277b024eSKalle Valo 	}
1166277b024eSKalle Valo 
1167277b024eSKalle Valo 	/* device dump data will be free in device coredump release function
1168277b024eSKalle Valo 	 * after 5 min
1169277b024eSKalle Valo 	 */
1170277b024eSKalle Valo 	dev_coredumpv(adapter->dev, dump_data, dump_len, GFP_KERNEL);
1171277b024eSKalle Valo 	mwifiex_dbg(adapter, MSG,
1172277b024eSKalle Valo 		    "== mwifiex dump information to /sys/class/devcoredump end");
1173277b024eSKalle Valo 
1174277b024eSKalle Valo done:
1175277b024eSKalle Valo 	for (idx = 0; idx < adapter->num_mem_types; idx++) {
1176277b024eSKalle Valo 		struct memory_type_mapping *entry =
1177277b024eSKalle Valo 			&adapter->mem_type_mapping_tbl[idx];
1178277b024eSKalle Valo 
1179277b024eSKalle Valo 		if (entry->mem_ptr) {
1180277b024eSKalle Valo 			vfree(entry->mem_ptr);
1181277b024eSKalle Valo 			entry->mem_ptr = NULL;
1182277b024eSKalle Valo 		}
1183277b024eSKalle Valo 		entry->mem_size = 0;
1184277b024eSKalle Valo 	}
1185277b024eSKalle Valo 
1186277b024eSKalle Valo 	if (adapter->drv_info_dump) {
1187277b024eSKalle Valo 		vfree(adapter->drv_info_dump);
1188277b024eSKalle Valo 		adapter->drv_info_dump = NULL;
1189277b024eSKalle Valo 		adapter->drv_info_size = 0;
1190277b024eSKalle Valo 	}
1191277b024eSKalle Valo }
1192277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_upload_device_dump);
1193277b024eSKalle Valo 
1194277b024eSKalle Valo /*
1195277b024eSKalle Valo  * CFG802.11 network device handler for statistics retrieval.
1196277b024eSKalle Valo  */
1197277b024eSKalle Valo static struct net_device_stats *mwifiex_get_stats(struct net_device *dev)
1198277b024eSKalle Valo {
1199277b024eSKalle Valo 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
1200277b024eSKalle Valo 
1201277b024eSKalle Valo 	return &priv->stats;
1202277b024eSKalle Valo }
1203277b024eSKalle Valo 
1204277b024eSKalle Valo static u16
1205277b024eSKalle Valo mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb,
1206277b024eSKalle Valo 				void *accel_priv, select_queue_fallback_t fallback)
1207277b024eSKalle Valo {
1208277b024eSKalle Valo 	skb->priority = cfg80211_classify8021d(skb, NULL);
1209277b024eSKalle Valo 	return mwifiex_1d_to_wmm_queue[skb->priority];
1210277b024eSKalle Valo }
1211277b024eSKalle Valo 
1212277b024eSKalle Valo /* Network device handlers */
1213277b024eSKalle Valo static const struct net_device_ops mwifiex_netdev_ops = {
1214277b024eSKalle Valo 	.ndo_open = mwifiex_open,
1215277b024eSKalle Valo 	.ndo_stop = mwifiex_close,
1216277b024eSKalle Valo 	.ndo_start_xmit = mwifiex_hard_start_xmit,
1217277b024eSKalle Valo 	.ndo_set_mac_address = mwifiex_set_mac_address,
1218277b024eSKalle Valo 	.ndo_validate_addr = eth_validate_addr,
1219277b024eSKalle Valo 	.ndo_tx_timeout = mwifiex_tx_timeout,
1220277b024eSKalle Valo 	.ndo_get_stats = mwifiex_get_stats,
1221277b024eSKalle Valo 	.ndo_set_rx_mode = mwifiex_set_multicast_list,
1222277b024eSKalle Valo 	.ndo_select_queue = mwifiex_netdev_select_wmm_queue,
1223277b024eSKalle Valo };
1224277b024eSKalle Valo 
1225277b024eSKalle Valo /*
1226277b024eSKalle Valo  * This function initializes the private structure parameters.
1227277b024eSKalle Valo  *
1228277b024eSKalle Valo  * The following wait queues are initialized -
1229277b024eSKalle Valo  *      - IOCTL wait queue
1230277b024eSKalle Valo  *      - Command wait queue
1231277b024eSKalle Valo  *      - Statistics wait queue
1232277b024eSKalle Valo  *
1233277b024eSKalle Valo  * ...and the following default parameters are set -
1234277b024eSKalle Valo  *      - Current key index     : Set to 0
1235277b024eSKalle Valo  *      - Rate index            : Set to auto
1236277b024eSKalle Valo  *      - Media connected       : Set to disconnected
1237277b024eSKalle Valo  *      - Adhoc link sensed     : Set to false
1238277b024eSKalle Valo  *      - Nick name             : Set to null
1239277b024eSKalle Valo  *      - Number of Tx timeout  : Set to 0
1240277b024eSKalle Valo  *      - Device address        : Set to current address
1241277b024eSKalle Valo  *      - Rx histogram statistc : Set to 0
1242277b024eSKalle Valo  *
1243277b024eSKalle Valo  * In addition, the CFG80211 work queue is also created.
1244277b024eSKalle Valo  */
1245277b024eSKalle Valo void mwifiex_init_priv_params(struct mwifiex_private *priv,
1246277b024eSKalle Valo 			      struct net_device *dev)
1247277b024eSKalle Valo {
1248277b024eSKalle Valo 	dev->netdev_ops = &mwifiex_netdev_ops;
1249277b024eSKalle Valo 	dev->destructor = free_netdev;
1250277b024eSKalle Valo 	/* Initialize private structure */
1251277b024eSKalle Valo 	priv->current_key_index = 0;
1252277b024eSKalle Valo 	priv->media_connected = false;
1253277b024eSKalle Valo 	memset(priv->mgmt_ie, 0,
1254277b024eSKalle Valo 	       sizeof(struct mwifiex_ie) * MAX_MGMT_IE_INDEX);
1255277b024eSKalle Valo 	priv->beacon_idx = MWIFIEX_AUTO_IDX_MASK;
1256277b024eSKalle Valo 	priv->proberesp_idx = MWIFIEX_AUTO_IDX_MASK;
1257277b024eSKalle Valo 	priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK;
1258277b024eSKalle Valo 	priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
1259277b024eSKalle Valo 	priv->num_tx_timeout = 0;
1260277b024eSKalle Valo 	ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr);
1261277b024eSKalle Valo 	memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN);
1262277b024eSKalle Valo 
1263277b024eSKalle Valo 	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
1264277b024eSKalle Valo 	    GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
1265277b024eSKalle Valo 		priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL);
1266277b024eSKalle Valo 		if (priv->hist_data)
1267277b024eSKalle Valo 			mwifiex_hist_data_reset(priv);
1268277b024eSKalle Valo 	}
1269277b024eSKalle Valo }
1270277b024eSKalle Valo 
1271277b024eSKalle Valo /*
1272277b024eSKalle Valo  * This function check if command is pending.
1273277b024eSKalle Valo  */
1274277b024eSKalle Valo int is_command_pending(struct mwifiex_adapter *adapter)
1275277b024eSKalle Valo {
1276277b024eSKalle Valo 	unsigned long flags;
1277277b024eSKalle Valo 	int is_cmd_pend_q_empty;
1278277b024eSKalle Valo 
1279277b024eSKalle Valo 	spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
1280277b024eSKalle Valo 	is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q);
1281277b024eSKalle Valo 	spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
1282277b024eSKalle Valo 
1283277b024eSKalle Valo 	return !is_cmd_pend_q_empty;
1284277b024eSKalle Valo }
1285277b024eSKalle Valo 
1286277b024eSKalle Valo /*
1287277b024eSKalle Valo  * This is the RX work queue function.
1288277b024eSKalle Valo  *
1289277b024eSKalle Valo  * It handles the RX operations.
1290277b024eSKalle Valo  */
1291277b024eSKalle Valo static void mwifiex_rx_work_queue(struct work_struct *work)
1292277b024eSKalle Valo {
1293277b024eSKalle Valo 	struct mwifiex_adapter *adapter =
1294277b024eSKalle Valo 		container_of(work, struct mwifiex_adapter, rx_work);
1295277b024eSKalle Valo 
1296277b024eSKalle Valo 	if (adapter->surprise_removed)
1297277b024eSKalle Valo 		return;
1298277b024eSKalle Valo 	mwifiex_process_rx(adapter);
1299277b024eSKalle Valo }
1300277b024eSKalle Valo 
1301277b024eSKalle Valo /*
1302277b024eSKalle Valo  * This is the main work queue function.
1303277b024eSKalle Valo  *
1304277b024eSKalle Valo  * It handles the main process, which in turn handles the complete
1305277b024eSKalle Valo  * driver operations.
1306277b024eSKalle Valo  */
1307277b024eSKalle Valo static void mwifiex_main_work_queue(struct work_struct *work)
1308277b024eSKalle Valo {
1309277b024eSKalle Valo 	struct mwifiex_adapter *adapter =
1310277b024eSKalle Valo 		container_of(work, struct mwifiex_adapter, main_work);
1311277b024eSKalle Valo 
1312277b024eSKalle Valo 	if (adapter->surprise_removed)
1313277b024eSKalle Valo 		return;
1314277b024eSKalle Valo 	mwifiex_main_process(adapter);
1315277b024eSKalle Valo }
1316277b024eSKalle Valo 
1317277b024eSKalle Valo /*
1318277b024eSKalle Valo  * This function adds the card.
1319277b024eSKalle Valo  *
1320277b024eSKalle Valo  * This function follows the following major steps to set up the device -
1321277b024eSKalle Valo  *      - Initialize software. This includes probing the card, registering
1322277b024eSKalle Valo  *        the interface operations table, and allocating/initializing the
1323277b024eSKalle Valo  *        adapter structure
1324277b024eSKalle Valo  *      - Set up the netlink socket
1325277b024eSKalle Valo  *      - Create and start the main work queue
1326277b024eSKalle Valo  *      - Register the device
1327277b024eSKalle Valo  *      - Initialize firmware and hardware
1328277b024eSKalle Valo  *      - Add logical interfaces
1329277b024eSKalle Valo  */
1330277b024eSKalle Valo int
1331277b024eSKalle Valo mwifiex_add_card(void *card, struct semaphore *sem,
1332277b024eSKalle Valo 		 struct mwifiex_if_ops *if_ops, u8 iface_type)
1333277b024eSKalle Valo {
1334277b024eSKalle Valo 	struct mwifiex_adapter *adapter;
1335277b024eSKalle Valo 
1336277b024eSKalle Valo 	if (down_interruptible(sem))
1337277b024eSKalle Valo 		goto exit_sem_err;
1338277b024eSKalle Valo 
1339277b024eSKalle Valo 	if (mwifiex_register(card, if_ops, (void **)&adapter)) {
1340277b024eSKalle Valo 		pr_err("%s: software init failed\n", __func__);
1341277b024eSKalle Valo 		goto err_init_sw;
1342277b024eSKalle Valo 	}
1343277b024eSKalle Valo 
1344277b024eSKalle Valo 	adapter->iface_type = iface_type;
1345277b024eSKalle Valo 	adapter->card_sem = sem;
1346277b024eSKalle Valo 
1347277b024eSKalle Valo 	adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
1348277b024eSKalle Valo 	adapter->surprise_removed = false;
1349277b024eSKalle Valo 	init_waitqueue_head(&adapter->init_wait_q);
1350277b024eSKalle Valo 	adapter->is_suspended = false;
1351277b024eSKalle Valo 	adapter->hs_activated = false;
1352277b024eSKalle Valo 	init_waitqueue_head(&adapter->hs_activate_wait_q);
1353277b024eSKalle Valo 	init_waitqueue_head(&adapter->cmd_wait_q.wait);
1354277b024eSKalle Valo 	adapter->cmd_wait_q.status = 0;
1355277b024eSKalle Valo 	adapter->scan_wait_q_woken = false;
1356277b024eSKalle Valo 
1357277b024eSKalle Valo 	if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB) {
1358277b024eSKalle Valo 		adapter->rx_work_enabled = true;
1359277b024eSKalle Valo 		pr_notice("rx work enabled, cpus %d\n", num_possible_cpus());
1360277b024eSKalle Valo 	}
1361277b024eSKalle Valo 
1362277b024eSKalle Valo 	adapter->workqueue =
1363277b024eSKalle Valo 		alloc_workqueue("MWIFIEX_WORK_QUEUE",
1364277b024eSKalle Valo 				WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
1365277b024eSKalle Valo 	if (!adapter->workqueue)
1366277b024eSKalle Valo 		goto err_kmalloc;
1367277b024eSKalle Valo 
1368277b024eSKalle Valo 	INIT_WORK(&adapter->main_work, mwifiex_main_work_queue);
1369277b024eSKalle Valo 
1370277b024eSKalle Valo 	if (adapter->rx_work_enabled) {
1371277b024eSKalle Valo 		adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE",
1372277b024eSKalle Valo 							WQ_HIGHPRI |
1373277b024eSKalle Valo 							WQ_MEM_RECLAIM |
1374277b024eSKalle Valo 							WQ_UNBOUND, 1);
1375277b024eSKalle Valo 		if (!adapter->rx_workqueue)
1376277b024eSKalle Valo 			goto err_kmalloc;
1377277b024eSKalle Valo 
1378277b024eSKalle Valo 		INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue);
1379277b024eSKalle Valo 	}
1380277b024eSKalle Valo 
1381277b024eSKalle Valo 	/* Register the device. Fill up the private data structure with relevant
1382277b024eSKalle Valo 	   information from the card. */
1383277b024eSKalle Valo 	if (adapter->if_ops.register_dev(adapter)) {
1384277b024eSKalle Valo 		pr_err("%s: failed to register mwifiex device\n", __func__);
1385277b024eSKalle Valo 		goto err_registerdev;
1386277b024eSKalle Valo 	}
1387277b024eSKalle Valo 
1388277b024eSKalle Valo 	if (mwifiex_init_hw_fw(adapter)) {
1389277b024eSKalle Valo 		pr_err("%s: firmware init failed\n", __func__);
1390277b024eSKalle Valo 		goto err_init_fw;
1391277b024eSKalle Valo 	}
1392277b024eSKalle Valo 
1393277b024eSKalle Valo 	return 0;
1394277b024eSKalle Valo 
1395277b024eSKalle Valo err_init_fw:
1396277b024eSKalle Valo 	pr_debug("info: %s: unregister device\n", __func__);
1397277b024eSKalle Valo 	if (adapter->if_ops.unregister_dev)
1398277b024eSKalle Valo 		adapter->if_ops.unregister_dev(adapter);
1399277b024eSKalle Valo 	if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
1400277b024eSKalle Valo 		pr_debug("info: %s: shutdown mwifiex\n", __func__);
1401277b024eSKalle Valo 		adapter->init_wait_q_woken = false;
1402277b024eSKalle Valo 
1403277b024eSKalle Valo 		if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
1404277b024eSKalle Valo 			wait_event_interruptible(adapter->init_wait_q,
1405277b024eSKalle Valo 						 adapter->init_wait_q_woken);
1406277b024eSKalle Valo 	}
1407277b024eSKalle Valo err_registerdev:
1408277b024eSKalle Valo 	adapter->surprise_removed = true;
1409277b024eSKalle Valo 	mwifiex_terminate_workqueue(adapter);
1410277b024eSKalle Valo err_kmalloc:
1411277b024eSKalle Valo 	mwifiex_free_adapter(adapter);
1412277b024eSKalle Valo 
1413277b024eSKalle Valo err_init_sw:
1414277b024eSKalle Valo 	up(sem);
1415277b024eSKalle Valo 
1416277b024eSKalle Valo exit_sem_err:
1417277b024eSKalle Valo 	return -1;
1418277b024eSKalle Valo }
1419277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_add_card);
1420277b024eSKalle Valo 
1421277b024eSKalle Valo /*
1422277b024eSKalle Valo  * This function removes the card.
1423277b024eSKalle Valo  *
1424277b024eSKalle Valo  * This function follows the following major steps to remove the device -
1425277b024eSKalle Valo  *      - Stop data traffic
1426277b024eSKalle Valo  *      - Shutdown firmware
1427277b024eSKalle Valo  *      - Remove the logical interfaces
1428277b024eSKalle Valo  *      - Terminate the work queue
1429277b024eSKalle Valo  *      - Unregister the device
1430277b024eSKalle Valo  *      - Free the adapter structure
1431277b024eSKalle Valo  */
1432277b024eSKalle Valo int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem)
1433277b024eSKalle Valo {
1434277b024eSKalle Valo 	struct mwifiex_private *priv = NULL;
1435277b024eSKalle Valo 	int i;
1436277b024eSKalle Valo 
1437c3ec0ff6SXinming Hu 	if (down_trylock(sem))
1438277b024eSKalle Valo 		goto exit_sem_err;
1439277b024eSKalle Valo 
1440277b024eSKalle Valo 	if (!adapter)
1441277b024eSKalle Valo 		goto exit_remove;
1442277b024eSKalle Valo 
1443277b024eSKalle Valo 	/* We can no longer handle interrupts once we start doing the teardown
1444277b024eSKalle Valo 	 * below. */
1445277b024eSKalle Valo 	if (adapter->if_ops.disable_int)
1446277b024eSKalle Valo 		adapter->if_ops.disable_int(adapter);
1447277b024eSKalle Valo 
1448277b024eSKalle Valo 	adapter->surprise_removed = true;
1449277b024eSKalle Valo 
1450277b024eSKalle Valo 	mwifiex_terminate_workqueue(adapter);
1451277b024eSKalle Valo 
1452277b024eSKalle Valo 	/* Stop data */
1453277b024eSKalle Valo 	for (i = 0; i < adapter->priv_num; i++) {
1454277b024eSKalle Valo 		priv = adapter->priv[i];
1455277b024eSKalle Valo 		if (priv && priv->netdev) {
1456277b024eSKalle Valo 			mwifiex_stop_net_dev_queue(priv->netdev, adapter);
1457277b024eSKalle Valo 			if (netif_carrier_ok(priv->netdev))
1458277b024eSKalle Valo 				netif_carrier_off(priv->netdev);
1459277b024eSKalle Valo 		}
1460277b024eSKalle Valo 	}
1461277b024eSKalle Valo 
1462277b024eSKalle Valo 	mwifiex_dbg(adapter, CMD,
1463277b024eSKalle Valo 		    "cmd: calling mwifiex_shutdown_drv...\n");
1464277b024eSKalle Valo 	adapter->init_wait_q_woken = false;
1465277b024eSKalle Valo 
1466277b024eSKalle Valo 	if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
1467277b024eSKalle Valo 		wait_event_interruptible(adapter->init_wait_q,
1468277b024eSKalle Valo 					 adapter->init_wait_q_woken);
1469277b024eSKalle Valo 	mwifiex_dbg(adapter, CMD,
1470277b024eSKalle Valo 		    "cmd: mwifiex_shutdown_drv done\n");
1471277b024eSKalle Valo 	if (atomic_read(&adapter->rx_pending) ||
1472277b024eSKalle Valo 	    atomic_read(&adapter->tx_pending) ||
1473277b024eSKalle Valo 	    atomic_read(&adapter->cmd_pending)) {
1474277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR,
1475277b024eSKalle Valo 			    "rx_pending=%d, tx_pending=%d,\t"
1476277b024eSKalle Valo 			    "cmd_pending=%d\n",
1477277b024eSKalle Valo 			    atomic_read(&adapter->rx_pending),
1478277b024eSKalle Valo 			    atomic_read(&adapter->tx_pending),
1479277b024eSKalle Valo 			    atomic_read(&adapter->cmd_pending));
1480277b024eSKalle Valo 	}
1481277b024eSKalle Valo 
1482277b024eSKalle Valo 	for (i = 0; i < adapter->priv_num; i++) {
1483277b024eSKalle Valo 		priv = adapter->priv[i];
1484277b024eSKalle Valo 
1485277b024eSKalle Valo 		if (!priv)
1486277b024eSKalle Valo 			continue;
1487277b024eSKalle Valo 
1488277b024eSKalle Valo 		rtnl_lock();
1489277b024eSKalle Valo 		if (priv->netdev &&
1490277b024eSKalle Valo 		    priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED)
1491277b024eSKalle Valo 			mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev);
1492277b024eSKalle Valo 		rtnl_unlock();
1493277b024eSKalle Valo 	}
1494277b024eSKalle Valo 
1495277b024eSKalle Valo 	wiphy_unregister(adapter->wiphy);
1496277b024eSKalle Valo 	wiphy_free(adapter->wiphy);
1497277b024eSKalle Valo 
1498277b024eSKalle Valo 	/* Unregister device */
1499277b024eSKalle Valo 	mwifiex_dbg(adapter, INFO,
1500277b024eSKalle Valo 		    "info: unregister device\n");
1501277b024eSKalle Valo 	if (adapter->if_ops.unregister_dev)
1502277b024eSKalle Valo 		adapter->if_ops.unregister_dev(adapter);
1503277b024eSKalle Valo 	/* Free adapter structure */
1504277b024eSKalle Valo 	mwifiex_dbg(adapter, INFO,
1505277b024eSKalle Valo 		    "info: free adapter\n");
1506277b024eSKalle Valo 	mwifiex_free_adapter(adapter);
1507277b024eSKalle Valo 
1508277b024eSKalle Valo exit_remove:
1509277b024eSKalle Valo 	up(sem);
1510277b024eSKalle Valo exit_sem_err:
1511277b024eSKalle Valo 	return 0;
1512277b024eSKalle Valo }
1513277b024eSKalle Valo EXPORT_SYMBOL_GPL(mwifiex_remove_card);
1514277b024eSKalle Valo 
1515277b024eSKalle Valo void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask,
1516277b024eSKalle Valo 		  const char *fmt, ...)
1517277b024eSKalle Valo {
1518277b024eSKalle Valo 	struct va_format vaf;
1519277b024eSKalle Valo 	va_list args;
1520277b024eSKalle Valo 
1521277b024eSKalle Valo 	if (!adapter->dev || !(adapter->debug_mask & mask))
1522277b024eSKalle Valo 		return;
1523277b024eSKalle Valo 
1524277b024eSKalle Valo 	va_start(args, fmt);
1525277b024eSKalle Valo 
1526277b024eSKalle Valo 	vaf.fmt = fmt;
1527277b024eSKalle Valo 	vaf.va = &args;
1528277b024eSKalle Valo 
1529277b024eSKalle Valo 	dev_info(adapter->dev, "%pV", &vaf);
1530277b024eSKalle Valo 
1531277b024eSKalle Valo 	va_end(args);
1532277b024eSKalle Valo }
1533277b024eSKalle Valo EXPORT_SYMBOL_GPL(_mwifiex_dbg);
1534277b024eSKalle Valo 
1535277b024eSKalle Valo /*
1536277b024eSKalle Valo  * This function initializes the module.
1537277b024eSKalle Valo  *
1538277b024eSKalle Valo  * The debug FS is also initialized if configured.
1539277b024eSKalle Valo  */
1540277b024eSKalle Valo static int
1541277b024eSKalle Valo mwifiex_init_module(void)
1542277b024eSKalle Valo {
1543277b024eSKalle Valo #ifdef CONFIG_DEBUG_FS
1544277b024eSKalle Valo 	mwifiex_debugfs_init();
1545277b024eSKalle Valo #endif
1546277b024eSKalle Valo 	return 0;
1547277b024eSKalle Valo }
1548277b024eSKalle Valo 
1549277b024eSKalle Valo /*
1550277b024eSKalle Valo  * This function cleans up the module.
1551277b024eSKalle Valo  *
1552277b024eSKalle Valo  * The debug FS is removed if available.
1553277b024eSKalle Valo  */
1554277b024eSKalle Valo static void
1555277b024eSKalle Valo mwifiex_cleanup_module(void)
1556277b024eSKalle Valo {
1557277b024eSKalle Valo #ifdef CONFIG_DEBUG_FS
1558277b024eSKalle Valo 	mwifiex_debugfs_remove();
1559277b024eSKalle Valo #endif
1560277b024eSKalle Valo }
1561277b024eSKalle Valo 
1562277b024eSKalle Valo module_init(mwifiex_init_module);
1563277b024eSKalle Valo module_exit(mwifiex_cleanup_module);
1564277b024eSKalle Valo 
1565277b024eSKalle Valo MODULE_AUTHOR("Marvell International Ltd.");
1566277b024eSKalle Valo MODULE_DESCRIPTION("Marvell WiFi-Ex Driver version " VERSION);
1567277b024eSKalle Valo MODULE_VERSION(VERSION);
1568277b024eSKalle Valo MODULE_LICENSE("GPL v2");
1569