mwifiex: wait for firmware
[cascardo/linux.git] / drivers / net / wireless / mwifiex / main.c
index 9d1b3ca..dad3dd0 100644 (file)
@@ -186,7 +186,8 @@ process_start:
                            adapter->tx_lock_flag)
                                break;
 
-                       if (adapter->scan_processing || adapter->data_sent ||
+                       if ((adapter->scan_processing &&
+                            !adapter->scan_delay_cnt) || adapter->data_sent ||
                            mwifiex_wmm_lists_empty(adapter)) {
                                if (adapter->cmd_sent || adapter->curr_cmd ||
                                    (!is_command_pending(adapter)))
@@ -235,8 +236,8 @@ process_start:
                        }
                }
 
-               if (!adapter->scan_processing && !adapter->data_sent &&
-                   !mwifiex_wmm_lists_empty(adapter)) {
+               if ((!adapter->scan_processing || adapter->scan_delay_cnt) &&
+                   !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter)) {
                        mwifiex_wmm_process_tx(adapter);
                        if (adapter->hs_activated) {
                                adapter->is_hs_configured = false;
@@ -273,6 +274,7 @@ exit_main_proc:
                mwifiex_shutdown_drv(adapter);
        return ret;
 }
+EXPORT_SYMBOL_GPL(mwifiex_main_process);
 
 /*
  * This function frees the adapter structure.
@@ -292,29 +294,28 @@ static void mwifiex_free_adapter(struct mwifiex_adapter *adapter)
 }
 
 /*
- * This function initializes the hardware and firmware.
+ * This function gets firmware and initializes it.
  *
  * The main initialization steps followed are -
  *      - Download the correct firmware to card
- *      - Allocate and initialize the adapter structure
- *      - Initialize the private structures
  *      - Issue the init commands to firmware
  */
-static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter)
+static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
 {
-       int ret, err;
+       int ret;
+       char fmt[64];
+       struct mwifiex_private *priv;
+       struct mwifiex_adapter *adapter = context;
        struct mwifiex_fw_image fw;
 
-       memset(&fw, 0, sizeof(struct mwifiex_fw_image));
-
-       err = request_firmware(&adapter->firmware, adapter->fw_name,
-                              adapter->dev);
-       if (err < 0) {
-               dev_err(adapter->dev, "request_firmware() returned"
-                               " error code %#x\n", err);
-               ret = -1;
+       if (!firmware) {
+               dev_err(adapter->dev,
+                       "Failed to get firmware %s\n", adapter->fw_name);
                goto done;
        }
+
+       memset(&fw, 0, sizeof(struct mwifiex_fw_image));
+       adapter->firmware = firmware;
        fw.fw_buf = (u8 *) adapter->firmware->data;
        fw.fw_len = adapter->firmware->size;
 
@@ -335,61 +336,57 @@ static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter)
        /* Wait for mwifiex_init to complete */
        wait_event_interruptible(adapter->init_wait_q,
                                 adapter->init_wait_q_woken);
-       if (adapter->hw_status != MWIFIEX_HW_STATUS_READY) {
-               ret = -1;
+       if (adapter->hw_status != MWIFIEX_HW_STATUS_READY)
                goto done;
+
+       priv = adapter->priv[0];
+       if (mwifiex_register_cfg80211(priv) != 0) {
+               dev_err(adapter->dev, "cannot register with cfg80211\n");
+               goto err_init_fw;
+       }
+
+       rtnl_lock();
+       /* Create station interface by default */
+       if (!mwifiex_add_virtual_intf(priv->wdev->wiphy, "mlan%d",
+                                     NL80211_IFTYPE_STATION, NULL, NULL)) {
+               dev_err(adapter->dev, "cannot create default STA interface\n");
+               goto err_add_intf;
        }
-       ret = 0;
+       rtnl_unlock();
 
+       mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
+       dev_notice(adapter->dev, "driver_version = %s\n", fmt);
+       goto done;
+
+err_add_intf:
+       mwifiex_del_virtual_intf(priv->wdev->wiphy, priv->netdev);
+       rtnl_unlock();
+err_init_fw:
+       pr_debug("info: %s: unregister device\n", __func__);
+       adapter->if_ops.unregister_dev(adapter);
 done:
-       if (adapter->firmware)
-               release_firmware(adapter->firmware);
-       if (ret)
-               ret = -1;
-       return ret;
+       release_firmware(adapter->firmware);
+       complete(&adapter->fw_load);
+       return;
 }
 
 /*
- * This function fills a driver buffer.
- *
- * The function associates a given SKB with the provided driver buffer
- * and also updates some of the SKB parameters, including IP header,
- * priority and timestamp.
+ * This function initializes the hardware and gets firmware.
  */
-static void
-mwifiex_fill_buffer(struct sk_buff *skb)
+static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter)
 {
-       struct ethhdr *eth;
-       struct iphdr *iph;
-       struct timeval tv;
-       u8 tid = 0;
-
-       eth = (struct ethhdr *) skb->data;
-       switch (eth->h_proto) {
-       case __constant_htons(ETH_P_IP):
-               iph = ip_hdr(skb);
-               tid = IPTOS_PREC(iph->tos);
-               pr_debug("data: packet type ETH_P_IP: %04x, tid=%#x prio=%#x\n",
-                        eth->h_proto, tid, skb->priority);
-               break;
-       case __constant_htons(ETH_P_ARP):
-               pr_debug("data: ARP packet: %04x\n", eth->h_proto);
-       default:
-               break;
-       }
-/* Offset for TOS field in the IP header */
-#define IPTOS_OFFSET 5
-       tid = (tid >> IPTOS_OFFSET);
-       skb->priority = tid;
-       /* Record the current time the packet was queued; used to
-          determine the amount of time the packet was queued in
-          the driver before it was sent to the firmware.
-          The delay is then sent along with the packet to the
-          firmware for aggregate delay calculation for stats and
-          MSDU lifetime expiry.
-        */
-       do_gettimeofday(&tv);
-       skb->tstamp = timeval_to_ktime(tv);
+       int ret;
+
+       const struct firmware *fw;
+
+       init_completion(&adapter->fw_load);
+       ret = request_firmware(&fw, adapter->fw_name, adapter->dev);
+       if (ret < 0)
+               dev_err(adapter->dev,
+                       "request_firmware() returned error %d\n", ret);
+       else
+               mwifiex_fw_dpc(fw, adapter);
+       return ret;
 }
 
 /*
@@ -410,6 +407,42 @@ mwifiex_open(struct net_device *dev)
 static int
 mwifiex_close(struct net_device *dev)
 {
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+       if (priv->scan_request) {
+               dev_dbg(priv->adapter->dev, "aborting scan on ndo_stop\n");
+               cfg80211_scan_done(priv->scan_request, 1);
+               priv->scan_request = NULL;
+               priv->scan_aborting = true;
+       }
+
+       return 0;
+}
+
+/*
+ * Add buffer into wmm tx queue and queue work to transmit it.
+ */
+int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb)
+{
+       struct netdev_queue *txq;
+       int index = mwifiex_1d_to_wmm_queue[skb->priority];
+
+       if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) {
+               txq = netdev_get_tx_queue(priv->netdev, index);
+               if (!netif_tx_queue_stopped(txq)) {
+                       netif_tx_stop_queue(txq);
+                       dev_dbg(priv->adapter->dev, "stop queue: %d\n", index);
+               }
+       }
+
+       atomic_inc(&priv->adapter->tx_pending);
+       mwifiex_wmm_add_buf_txqueue(priv, skb);
+
+       if (priv->adapter->scan_delay_cnt)
+               atomic_set(&priv->adapter->is_tx_received, true);
+
+       queue_work(priv->adapter->workqueue, &priv->adapter->main_work);
+
        return 0;
 }
 
@@ -422,6 +455,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
        struct sk_buff *new_skb;
        struct mwifiex_txinfo *tx_info;
+       struct timeval tv;
 
        dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n",
                jiffies, priv->bss_type, priv->bss_num);
@@ -459,17 +493,18 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        tx_info = MWIFIEX_SKB_TXCB(skb);
        tx_info->bss_num = priv->bss_num;
        tx_info->bss_type = priv->bss_type;
-       mwifiex_fill_buffer(skb);
-
-       mwifiex_wmm_add_buf_txqueue(priv, skb);
-       atomic_inc(&priv->adapter->tx_pending);
 
-       if (atomic_read(&priv->adapter->tx_pending) >= MAX_TX_PENDING) {
-               mwifiex_set_trans_start(dev);
-               mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter);
-       }
+       /* Record the current time the packet was queued; used to
+        * determine the amount of time the packet was queued in
+        * the driver before it was sent to the firmware.
+        * The delay is then sent along with the packet to the
+        * firmware for aggregate delay calculation for stats and
+        * MSDU lifetime expiry.
+        */
+       do_gettimeofday(&tv);
+       skb->tstamp = timeval_to_ktime(tv);
 
-       queue_work(priv->adapter->workqueue, &priv->adapter->main_work);
+       mwifiex_queue_tx_pkt(priv, skb);
 
        return 0;
 }
@@ -531,10 +566,22 @@ mwifiex_tx_timeout(struct net_device *dev)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
 
-       dev_err(priv->adapter->dev, "%lu : Tx timeout, bss_type-num = %d-%d\n",
-               jiffies, priv->bss_type, priv->bss_num);
-       mwifiex_set_trans_start(dev);
        priv->num_tx_timeout++;
+       priv->tx_timeout_cnt++;
+       dev_err(priv->adapter->dev,
+               "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n",
+               jiffies, priv->tx_timeout_cnt, priv->bss_type, priv->bss_num);
+       mwifiex_set_trans_start(dev);
+
+       if (priv->adapter->if_ops.reg_dbg)
+               priv->adapter->if_ops.reg_dbg(priv->adapter);
+
+       if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD &&
+           priv->adapter->if_ops.card_reset) {
+               dev_err(priv->adapter->dev, "tx_timeout_cnt exceeds threshold. "
+                       "Triggering card reset!\n");
+               priv->adapter->if_ops.card_reset(priv->adapter);
+       }
 }
 
 /*
@@ -547,6 +594,13 @@ static struct net_device_stats *mwifiex_get_stats(struct net_device *dev)
        return &priv->stats;
 }
 
+static u16
+mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb)
+{
+       skb->priority = cfg80211_classify8021d(skb);
+       return mwifiex_1d_to_wmm_queue[skb->priority];
+}
+
 /* Network device handlers */
 static const struct net_device_ops mwifiex_netdev_ops = {
        .ndo_open = mwifiex_open,
@@ -556,6 +610,7 @@ static const struct net_device_ops mwifiex_netdev_ops = {
        .ndo_tx_timeout = mwifiex_tx_timeout,
        .ndo_get_stats = mwifiex_get_stats,
        .ndo_set_rx_mode = mwifiex_set_multicast_list,
+       .ndo_select_queue = mwifiex_netdev_select_wmm_queue,
 };
 
 /*
@@ -581,6 +636,7 @@ void mwifiex_init_priv_params(struct mwifiex_private *priv,
                                                struct net_device *dev)
 {
        dev->netdev_ops = &mwifiex_netdev_ops;
+       dev->destructor = free_netdev;
        /* Initialize private structure */
        priv->current_key_index = 0;
        priv->media_connected = false;
@@ -650,8 +706,6 @@ mwifiex_add_card(void *card, struct semaphore *sem,
                 struct mwifiex_if_ops *if_ops, u8 iface_type)
 {
        struct mwifiex_adapter *adapter;
-       char fmt[64];
-       struct mwifiex_private *priv;
 
        if (down_interruptible(sem))
                goto exit_sem_err;
@@ -692,37 +746,9 @@ mwifiex_add_card(void *card, struct semaphore *sem,
                goto err_init_fw;
        }
 
-       priv = adapter->priv[0];
-
-       if (mwifiex_register_cfg80211(priv) != 0) {
-               dev_err(adapter->dev, "cannot register netdevice"
-                              " with cfg80211\n");
-                       goto err_init_fw;
-       }
-
-       rtnl_lock();
-       /* Create station interface by default */
-       if (!mwifiex_add_virtual_intf(priv->wdev->wiphy, "mlan%d",
-                                     NL80211_IFTYPE_STATION, NULL, NULL)) {
-               rtnl_unlock();
-               dev_err(adapter->dev, "cannot create default station"
-                               " interface\n");
-               goto err_add_intf;
-       }
-
-       rtnl_unlock();
-
        up(sem);
-
-       mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
-       dev_notice(adapter->dev, "driver_version = %s\n", fmt);
-
        return 0;
 
-err_add_intf:
-       rtnl_lock();
-       mwifiex_del_virtual_intf(priv->wdev->wiphy, priv->netdev);
-       rtnl_unlock();
 err_init_fw:
        pr_debug("info: %s: unregister device\n", __func__);
        adapter->if_ops.unregister_dev(adapter);
@@ -778,9 +804,7 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem)
        for (i = 0; i < adapter->priv_num; i++) {
                priv = adapter->priv[i];
                if (priv && priv->netdev) {
-                       if (!netif_queue_stopped(priv->netdev))
-                               mwifiex_stop_net_dev_queue(priv->netdev,
-                                                          adapter);
+                       mwifiex_stop_net_dev_queue(priv->netdev, adapter);
                        if (netif_carrier_ok(priv->netdev))
                                netif_carrier_off(priv->netdev);
                }