wil6210: fix race condition between BACK event and Rx data
[deliverable/linux.git] / drivers / net / wireless / ath / wil6210 / wmi.c
index 1d1d0afdd2e195c6856372299e3fc635b213df23..c3682c3ae89651b331bf570cf2a0688b983288c6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -14,6 +14,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <linux/moduleparam.h>
 #include <linux/etherdevice.h>
 #include <linux/if_arp.h>
 
 #include "wmi.h"
 #include "trace.h"
 
+static uint max_assoc_sta = 1;
+module_param(max_assoc_sta, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_assoc_sta, " Max number of stations associated to the AP");
+
 /**
  * WMI event receiving - theory of operations
  *
@@ -152,6 +157,7 @@ int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
                 struct wil6210_mbox_hdr *hdr)
 {
        void __iomem *src = wmi_buffer(wil, ptr);
+
        if (!src)
                return -EINVAL;
 
@@ -273,6 +279,7 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
        struct net_device *ndev = wil_to_ndev(wil);
        struct wireless_dev *wdev = wil->wdev;
        struct wmi_ready_event *evt = d;
+
        wil->fw_version = le32_to_cpu(evt->sw_version);
        wil->n_mids = evt->numof_additional_mids;
 
@@ -346,11 +353,11 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
                                    rx_mgmt_frame->bssid);
                        cfg80211_put_bss(wiphy, bss);
                } else {
-                       wil_err(wil, "cfg80211_inform_bss() failed\n");
+                       wil_err(wil, "cfg80211_inform_bss_frame() failed\n");
                }
        } else {
                cfg80211_rx_mgmt(wil->wdev, freq, signal,
-                                (void *)rx_mgmt_frame, d_len, 0, GFP_KERNEL);
+                                (void *)rx_mgmt_frame, d_len, 0);
        }
 }
 
@@ -482,33 +489,6 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
        mutex_unlock(&wil->mutex);
 }
 
-static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)
-{
-       struct wmi_notify_req_done_event *evt = d;
-
-       if (len < sizeof(*evt)) {
-               wil_err(wil, "Short NOTIFY event\n");
-               return;
-       }
-
-       wil->stats.tsf = le64_to_cpu(evt->tsf);
-       wil->stats.snr = le32_to_cpu(evt->snr_val);
-       wil->stats.bf_mcs = le16_to_cpu(evt->bf_mcs);
-       wil->stats.my_rx_sector = le16_to_cpu(evt->my_rx_sector);
-       wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector);
-       wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector);
-       wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector);
-       wil_dbg_wmi(wil, "Link status, MCS %d TSF 0x%016llx\n"
-                   "BF status 0x%08x SNR 0x%08x SQI %d%%\n"
-                   "Tx Tpt %d goodput %d Rx goodput %d\n"
-                   "Sectors(rx:tx) my %d:%d peer %d:%d\n",
-                   wil->stats.bf_mcs, wil->stats.tsf, evt->status,
-                   wil->stats.snr, evt->sqi, le32_to_cpu(evt->tx_tpt),
-                   le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput),
-                   wil->stats.my_rx_sector, wil->stats.my_tx_sector,
-                   wil->stats.peer_rx_sector, wil->stats.peer_tx_sector);
-}
-
 /*
  * Firmware reports EAPOL frame using WME event.
  * Reconstruct Ethernet frame and deliver it via normal Rx
@@ -617,27 +597,40 @@ static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d,
                return;
        }
 
+       mutex_lock(&wil->mutex);
+
        cid = wil->vring2cid_tid[evt->ringid][0];
        if (cid >= WIL6210_MAX_CID) {
                wil_err(wil, "invalid CID %d for vring %d\n", cid, evt->ringid);
-               return;
+               goto out;
        }
 
        sta = &wil->sta[cid];
        if (sta->status == wil_sta_unused) {
                wil_err(wil, "CID %d unused\n", cid);
-               return;
+               goto out;
        }
 
        wil_dbg_wmi(wil, "BACK for CID %d %pM\n", cid, sta->addr);
        for (i = 0; i < WIL_STA_TID_NUM; i++) {
-               struct wil_tid_ampdu_rx *r = sta->tid_rx[i];
+               struct wil_tid_ampdu_rx *r;
+               unsigned long flags;
+
+               spin_lock_irqsave(&sta->tid_rx_lock, flags);
+
+               r = sta->tid_rx[i];
                sta->tid_rx[i] = NULL;
                wil_tid_ampdu_rx_free(wil, r);
+
+               spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
+
                if ((evt->status == WMI_BA_AGREED) && evt->agg_wsize)
                        sta->tid_rx[i] = wil_tid_ampdu_rx_alloc(wil,
                                                evt->agg_wsize, 0);
        }
+
+out:
+       mutex_unlock(&wil->mutex);
 }
 
 static const struct {
@@ -651,7 +644,6 @@ static const struct {
        {WMI_SCAN_COMPLETE_EVENTID,     wmi_evt_scan_complete},
        {WMI_CONNECT_EVENTID,           wmi_evt_connect},
        {WMI_DISCONNECT_EVENTID,        wmi_evt_disconnect},
-       {WMI_NOTIFY_REQ_DONE_EVENTID,   wmi_evt_notify},
        {WMI_EAPOL_RX_EVENTID,          wmi_evt_eapol_rx},
        {WMI_DATA_PORT_OPEN_EVENTID,    wmi_evt_linkup},
        {WMI_WBE_LINKDOWN_EVENTID,      wmi_evt_linkdown},
@@ -676,7 +668,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
        unsigned n;
 
        if (!test_bit(wil_status_reset_done, &wil->status)) {
-               wil_err(wil, "Reset not completed\n");
+               wil_err(wil, "Reset in progress. Cannot handle WMI event\n");
                return;
        }
 
@@ -731,6 +723,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                        struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi;
                        u16 id = le16_to_cpu(wmi->id);
                        u32 tstamp = le32_to_cpu(wmi->timestamp);
+
                        wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n",
                                    id, wmi->mid, tstamp);
                        trace_wil6210_wmi_event(wmi, &wmi[1],
@@ -822,7 +815,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
                .network_type = wmi_nettype,
                .disable_sec_offload = 1,
                .channel = chan - 1,
-               .pcp_max_assoc_sta = WIL6210_MAX_CID,
+               .pcp_max_assoc_sta = max_assoc_sta,
        };
        struct {
                struct wil6210_mbox_hdr_wmi wmi;
@@ -832,6 +825,14 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
        if (!wil->secure_pcp)
                cmd.disable_sec = 1;
 
+       if ((cmd.pcp_max_assoc_sta > WIL6210_MAX_CID) ||
+           (cmd.pcp_max_assoc_sta <= 0)) {
+               wil_info(wil,
+                        "Requested connection limit %u, valid values are 1 - %d. Setting to %d\n",
+                        max_assoc_sta, WIL6210_MAX_CID, WIL6210_MAX_CID);
+               cmd.pcp_max_assoc_sta = WIL6210_MAX_CID;
+       }
+
        /*
         * Processing time may be huge, in case of secure AP it takes about
         * 3500ms for FW to start AP
@@ -968,6 +969,7 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
        int rc;
        u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len;
        struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL);
+
        if (!cmd)
                return -ENOMEM;
 
@@ -1143,6 +1145,9 @@ static void wmi_event_handle(struct wil6210_priv *wil,
                struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]);
                void *evt_data = (void *)(&wmi[1]);
                u16 id = le16_to_cpu(wmi->id);
+
+               wil_dbg_wmi(wil, "Handle WMI 0x%04x (reply_id 0x%04x)\n",
+                           id, wil->reply_id);
                /* check if someone waits for this event */
                if (wil->reply_id && wil->reply_id == id) {
                        if (wil->reply_buf) {
@@ -1199,9 +1204,11 @@ void wmi_event_worker(struct work_struct *work)
        struct pending_wmi_event *evt;
        struct list_head *lh;
 
+       wil_dbg_wmi(wil, "Start %s\n", __func__);
        while ((lh = next_wmi_ev(wil)) != NULL) {
                evt = list_entry(lh, struct pending_wmi_event, list);
                wmi_event_handle(wil, &evt->event.hdr);
                kfree(evt);
        }
+       wil_dbg_wmi(wil, "Finished %s\n", __func__);
 }
This page took 0.027055 seconds and 5 git commands to generate.