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