wl1251: implement filtering
authorGrazvydas Ignotas <notasas@gmail.com>
Tue, 26 Oct 2010 15:51:49 +0000 (18:51 +0300)
committerGrazvydas Ignotas <notasas@gmail.com>
Sat, 5 Mar 2011 21:18:00 +0000 (23:18 +0200)
Finish up filtering code and enable it. mac80211 no longer requires
to be atomic in .configure_filter callback, so remove filter_work.
The new filtering code makes sure it doesn't enable BSSID filtering
while BSSID is not set so that association works properly. This
allows removing hack in wl1251_cmd_join.

Signed-off-by: Grazvydas Ignotas <notasas@gmail.com>
drivers/net/wireless/wl12xx/wl1251.h
drivers/net/wireless/wl12xx/wl1251_cmd.c
drivers/net/wireless/wl12xx/wl1251_main.c

index 4a3f195..46dd160 100644 (file)
@@ -49,6 +49,7 @@ enum {
        DEBUG_MAC80211  = BIT(11),
        DEBUG_CMD       = BIT(12),
        DEBUG_ACX       = BIT(13),
        DEBUG_MAC80211  = BIT(11),
        DEBUG_CMD       = BIT(12),
        DEBUG_ACX       = BIT(13),
+       DEBUG_FILTERS   = BIT(14),
        DEBUG_ALL       = ~0,
 };
 
        DEBUG_ALL       = ~0,
 };
 
@@ -310,7 +311,6 @@ struct wl1251 {
        bool tx_queue_stopped;
 
        struct work_struct tx_work;
        bool tx_queue_stopped;
 
        struct work_struct tx_work;
-       struct work_struct filter_work;
 
        /* Pending TX frames */
        struct sk_buff *tx_frames[16];
 
        /* Pending TX frames */
        struct sk_buff *tx_frames[16];
@@ -351,6 +351,7 @@ struct wl1251 {
        unsigned int tx_mgmt_frm_rate;
        unsigned int tx_mgmt_frm_mod;
 
        unsigned int tx_mgmt_frm_rate;
        unsigned int tx_mgmt_frm_mod;
 
+       unsigned int mac80211_filters;
        unsigned int rx_config;
        unsigned int rx_filter;
 
        unsigned int rx_config;
        unsigned int rx_filter;
 
index a64dbd3..c0cdcd0 100644 (file)
@@ -300,15 +300,6 @@ int wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel,
        join->rx_config_options = wl->rx_config;
        join->rx_filter_options = wl->rx_filter;
 
        join->rx_config_options = wl->rx_config;
        join->rx_filter_options = wl->rx_filter;
 
-       /*
-        * FIXME: disable temporarily all filters because after commit
-        * 9cef8737 "mac80211: fix managed mode BSSID handling" broke
-        * association. The filter logic needs to be implemented properly
-        * and once that is done, this hack can be removed.
-        */
-       join->rx_config_options = 0;
-       join->rx_filter_options = WL1251_DEFAULT_RX_FILTER;
-
        join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS |
                RATE_MASK_5_5MBPS | RATE_MASK_11MBPS;
 
        join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS |
                RATE_MASK_5_5MBPS | RATE_MASK_11MBPS;
 
index 5e09161..8b3e78e 100644 (file)
@@ -323,6 +323,46 @@ out:
        mutex_unlock(&wl->mutex);
 }
 
        mutex_unlock(&wl->mutex);
 }
 
+static void wl1251_convert_filters(struct wl1251 *wl,
+                                 unsigned int mac80211_filters)
+{
+       wl->rx_config = WL1251_DEFAULT_RX_CONFIG;
+       wl->rx_filter = WL1251_DEFAULT_RX_FILTER;
+
+       if (mac80211_filters & FIF_PROMISC_IN_BSS) {
+               wl->rx_config |= CFG_BSSID_FILTER_EN;
+               wl->rx_config &= ~CFG_UNI_FILTER_EN;
+       }
+       if (mac80211_filters & FIF_ALLMULTI)
+               /*
+                * CFG_MC_FILTER_EN in rx_config needs to be 0 to receive
+                * all multicast frames
+                */
+               wl->rx_config &= ~CFG_MC_FILTER_EN;
+       if (mac80211_filters & FIF_FCSFAIL)
+               wl->rx_filter |= CFG_RX_FCS_ERROR;
+       if (mac80211_filters & FIF_BCN_PRBRESP_PROMISC) {
+               wl->rx_config &= ~CFG_BSSID_FILTER_EN;
+               wl->rx_config &= ~CFG_SSID_FILTER_EN;
+       }
+       if (mac80211_filters & FIF_CONTROL)
+               wl->rx_filter |= CFG_RX_CTL_EN;
+       if (mac80211_filters & FIF_OTHER_BSS)
+               wl->rx_config &= ~CFG_BSSID_FILTER_EN;
+       if (mac80211_filters & FIF_PROBE_REQ)
+               wl->rx_filter |= CFG_RX_PREQ_EN;
+
+       /*
+        * If we have no associated BSSID, the filter must be disabled
+        * or else association will break
+        */
+       if (is_zero_ether_addr(wl->bssid))
+               wl->rx_config &= ~CFG_BSSID_FILTER_EN;
+
+       wl1251_debug(DEBUG_FILTERS, "rx_config %04x, rx_filter %04x",
+               wl->rx_config, wl->rx_filter);
+}
+
 static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel,
                       u16 beacon_interval, u8 dtim_period)
 {
 static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel,
                       u16 beacon_interval, u8 dtim_period)
 {
@@ -335,6 +375,8 @@ static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel,
        if (ret < 0)
                goto out;
 
        if (ret < 0)
                goto out;
 
+       /* Join uses filters, update them */
+       wl1251_convert_filters(wl, wl->mac80211_filters);
 
        ret = wl1251_cmd_join(wl, bss_type, channel, beacon_interval,
                              dtim_period);
 
        ret = wl1251_cmd_join(wl, bss_type, channel, beacon_interval,
                              dtim_period);
@@ -349,33 +391,6 @@ out:
        return ret;
 }
 
        return ret;
 }
 
-static void wl1251_filter_work(struct work_struct *work)
-{
-       struct wl1251 *wl =
-               container_of(work, struct wl1251, filter_work);
-       int ret;
-
-       mutex_lock(&wl->mutex);
-
-       if (wl->state == WL1251_STATE_OFF)
-               goto out;
-
-       ret = wl1251_ps_elp_wakeup(wl);
-       if (ret < 0)
-               goto out;
-
-       ret = wl1251_join(wl, wl->bss_type, wl->channel, wl->beacon_int,
-                         wl->dtim_period);
-       if (ret < 0)
-               goto out_sleep;
-
-out_sleep:
-       wl1251_ps_elp_sleep(wl);
-
-out:
-       mutex_unlock(&wl->mutex);
-}
-
 static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct wl1251 *wl = hw->priv;
 static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct wl1251 *wl = hw->priv;
@@ -483,7 +498,6 @@ static void wl1251_op_stop(struct ieee80211_hw *hw)
 
        cancel_work_sync(&wl->irq_work);
        cancel_work_sync(&wl->tx_work);
 
        cancel_work_sync(&wl->irq_work);
        cancel_work_sync(&wl->tx_work);
-       cancel_work_sync(&wl->filter_work);
 
        mutex_lock(&wl->mutex);
 
 
        mutex_lock(&wl->mutex);
 
@@ -676,15 +690,18 @@ out:
                                  FIF_FCSFAIL | \
                                  FIF_BCN_PRBRESP_PROMISC | \
                                  FIF_CONTROL | \
                                  FIF_FCSFAIL | \
                                  FIF_BCN_PRBRESP_PROMISC | \
                                  FIF_CONTROL | \
-                                 FIF_OTHER_BSS)
+                                 FIF_OTHER_BSS | \
+                                 FIF_PROBE_REQ)
 
 static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
                                       unsigned int changed,
                                       unsigned int *total,u64 multicast)
 {
        struct wl1251 *wl = hw->priv;
 
 static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
                                       unsigned int changed,
                                       unsigned int *total,u64 multicast)
 {
        struct wl1251 *wl = hw->priv;
+       int ret;
 
        wl1251_debug(DEBUG_MAC80211, "mac80211 configure filter");
 
        wl1251_debug(DEBUG_MAC80211, "mac80211 configure filter");
+       wl1251_debug(DEBUG_FILTERS, "configure filter %x %x", *total, changed);
 
        *total &= WL1251_SUPPORTED_FILTERS;
        changed &= WL1251_SUPPORTED_FILTERS;
 
        *total &= WL1251_SUPPORTED_FILTERS;
        changed &= WL1251_SUPPORTED_FILTERS;
@@ -693,38 +710,25 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
                /* no filters which we support changed */
                return;
 
                /* no filters which we support changed */
                return;
 
-       /* FIXME: wl->rx_config and wl->rx_filter are not protected */
+       mutex_lock(&wl->mutex);
 
 
-       wl->rx_config = WL1251_DEFAULT_RX_CONFIG;
-       wl->rx_filter = WL1251_DEFAULT_RX_FILTER;
+       wl->mac80211_filters = *total;
+       wl1251_convert_filters(wl, *total);
 
 
-       if (*total & FIF_PROMISC_IN_BSS) {
-               wl->rx_config |= CFG_BSSID_FILTER_EN;
-               wl->rx_config |= CFG_RX_ALL_GOOD;
-       }
-       if (*total & FIF_ALLMULTI)
-               /*
-                * CFG_MC_FILTER_EN in rx_config needs to be 0 to receive
-                * all multicast frames
-                */
-               wl->rx_config &= ~CFG_MC_FILTER_EN;
-       if (*total & FIF_FCSFAIL)
-               wl->rx_filter |= CFG_RX_FCS_ERROR;
-       if (*total & FIF_BCN_PRBRESP_PROMISC) {
-               wl->rx_config &= ~CFG_BSSID_FILTER_EN;
-               wl->rx_config &= ~CFG_SSID_FILTER_EN;
-       }
-       if (*total & FIF_CONTROL)
-               wl->rx_filter |= CFG_RX_CTL_EN;
-       if (*total & FIF_OTHER_BSS)
-               wl->rx_filter &= ~CFG_BSSID_FILTER_EN;
+       if (wl->state == WL1251_STATE_OFF)
+               goto out;
 
 
-       /*
-        * FIXME: workqueues need to be properly cancelled on stop(), for
-        * now let's just disable changing the filter settings. They will
-        * be updated any on config().
-        */
-       /* schedule_work(&wl->filter_work); */
+       ret = wl1251_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       /* apply configured filters */
+       wl1251_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
+
+       wl1251_ps_elp_sleep(wl);
+
+out:
+       mutex_unlock(&wl->mutex);
 }
 
 /* HW encryption */
 }
 
 /* HW encryption */
@@ -1358,7 +1362,6 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
 
        skb_queue_head_init(&wl->tx_queue);
 
 
        skb_queue_head_init(&wl->tx_queue);
 
-       INIT_WORK(&wl->filter_work, wl1251_filter_work);
        INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work);
        wl->channel = WL1251_DEFAULT_CHANNEL;
        wl->scanning = false;
        INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work);
        wl->channel = WL1251_DEFAULT_CHANNEL;
        wl->scanning = false;