Thread (14 messages) 14 messages, 3 authors, 2014-06-05

Re: [RFC] mac80211: at76x50x_usb driver broken by commit 3afc216.. and RX path involved in scan

From: Andrea Merello <hidden>
Date: 2014-06-04 21:02:43

Hi Johannes,
Yeah, and it would do much more than what you want. You could use
cfg80211_find_ie() though to simplify the code.
Excellent! I missed it..
So the loop can be simplified to

        el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, el_len);
        if (el && el[1] > 0)
                channel = el[2];
I guess el_len should be len, but I got it :)
You might also invoke this only when you know you're scanning, to avoid
the overhead otherwise, but with this driver it probably doesn't matter
much.
Probably it doesn't, but you are right that is useless to waste time
parsing those mgmt frames when not scanning.

I modified the function (and I changed also its name) in a way that
when scanning it avoid parsing and it does return priv->channel, and I
made it also inline.

So this is the updated patch.
If you agree, we could push it for merge :)

Thank you
Andrea

 drivers/net/wireless/at76c50x-usb.c | 53 +++++++++++++++++++++++++++++++++++++
 drivers/net/wireless/at76c50x-usb.h |  1 +
 2 files changed, 54 insertions(+)
diff --git a/drivers/net/wireless/at76c50x-usb.c
b/drivers/net/wireless/at76c50x-usb.c
index 10fd12e..6f52633 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -1429,6 +1429,8 @@ static int at76_startup_device(struct at76_priv *priv)
     /* remove BSSID from previous run */
     memset(priv->bssid, 0, ETH_ALEN);

+    priv->scanning = false;
+
     if (at76_set_radio(priv, 1) == 1)
         at76_wait_completion(priv, CMD_RADIO_ON);
@@ -1502,6 +1504,52 @@ static void at76_work_submit_rx(struct work_struct *work)
     mutex_unlock(&priv->mtx);
 }

+/* This is a workaround to make scan working:
+ * currently mac80211 does not process frames with no frequency
+ * information.
+ * However during scan the HW performs a sweep by itself, and we
+ * are unable to know where the radio is actually tuned.
+ * This function tries to do its best to guess this information..
+ * During scan, If the current frame is a beacon or a probe response,
+ * the channel information is extracted from it.
+ * When not scanning, for other frames, or if it happens that for
+ * whatever reason we fail to parse beacons and probe responses, this
+ * function returns the priv->channel information, that should be correct
+ * at least when we are not scanning.
+ */
+static inline int at76_guess_freq(struct at76_priv *priv)
+{
+    size_t el_off;
+    const u8 *el;
+    int channel = priv->channel;
+    int len = priv->rx_skb->len;
+    struct ieee80211_hdr *hdr = (void *)priv->rx_skb->data;
+
+    if (!priv->scanning)
+        goto exit;
+
+    if (len < 24)
+        goto exit;
+
+    if (ieee80211_is_probe_resp(hdr->frame_control)) {
+        el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+        el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable;
+    } else if (ieee80211_is_beacon(hdr->frame_control)) {
+        el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+        el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable;
+    } else
+        goto exit;
+
+    len -= el_off;
+
+    el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len);
+    if (el && el[1] > 0)
+        channel = el[2];
+
+exit:
+    return ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ);
+}
+
 static void at76_rx_tasklet(unsigned long param)
 {
     struct urb *urb = (struct urb *)param;
@@ -1542,6 +1590,8 @@ static void at76_rx_tasklet(unsigned long param)
     rx_status.signal = buf->rssi;
     rx_status.flag |= RX_FLAG_DECRYPTED;
     rx_status.flag |= RX_FLAG_IV_STRIPPED;
+    rx_status.band = IEEE80211_BAND_2GHZ;
+    rx_status.freq = at76_guess_freq(priv);

     at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d",
          priv->rx_skb->len, priv->rx_skb->data_len);
@@ -1894,6 +1944,8 @@ static void at76_dwork_hw_scan(struct work_struct *work)
     if (is_valid_ether_addr(priv->bssid))
         at76_join(priv);

+    priv->scanning = false;
+
     mutex_unlock(&priv->mtx);

     ieee80211_scan_completed(priv->hw, false);
@@ -1948,6 +2000,7 @@ static int at76_hw_scan(struct ieee80211_hw *hw,
         goto exit;
     }

+    priv->scanning = true;
     ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan,
                      SCAN_POLL_INTERVAL);
diff --git a/drivers/net/wireless/at76c50x-usb.h
b/drivers/net/wireless/at76c50x-usb.h
index 4718aa5..55090a3 100644
--- a/drivers/net/wireless/at76c50x-usb.h
+++ b/drivers/net/wireless/at76c50x-usb.h
@@ -418,6 +418,7 @@ struct at76_priv {
     int scan_max_time;    /* scan max channel time */
     int scan_mode;        /* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */
     int scan_need_any;    /* if set, need to scan for any ESSID */
+    bool scanning;        /* if set, the scan is running */

     u16 assoc_id;        /* current association ID, if associated */
-- 
1.9.1
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help