aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuuso Oikarinen <juuso.oikarinen@nokia.com>2009-10-08 14:56:31 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-10-27 16:47:53 -0400
commitc87dec9f189b884df215756e285b9281cf065206 (patch)
treed8fd4ed6f27e28c0dcf5d77240e4f83cd2295129
parent66497dc3bd569e05a5bcb729d495eebad47aa46a (diff)
wl1271: Multicast filtering configuration
Enable multicast filtering. This way by default no multicast frames will reach the host, and when needed, only required multicast frames can be passed from the WLAN chipset to the host. Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com> Reviewed-by: Luciano Coelho <luciano.coelho@nokia.com> Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/wl12xx/wl1271.h7
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_acx.c9
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_acx.h8
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_init.c2
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_main.c121
5 files changed, 120 insertions, 27 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 0b4744db22ef..34a52b33bf5d 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -97,7 +97,8 @@ enum {
97 } while (0) 97 } while (0)
98 98
99#define WL1271_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \ 99#define WL1271_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \
100 CFG_BSSID_FILTER_EN) 100 CFG_BSSID_FILTER_EN | \
101 CFG_MC_FILTER_EN)
101 102
102#define WL1271_DEFAULT_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \ 103#define WL1271_DEFAULT_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \
103 CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \ 104 CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \
@@ -123,7 +124,7 @@ enum {
123#define WL1271_DEFAULT_BEACON_INT 100 124#define WL1271_DEFAULT_BEACON_INT 100
124#define WL1271_DEFAULT_DTIM_PERIOD 1 125#define WL1271_DEFAULT_DTIM_PERIOD 1
125 126
126#define ACX_TX_DESCRIPTORS 32 127#define ACX_TX_DESCRIPTORS 32
127 128
128enum wl1271_state { 129enum wl1271_state {
129 WL1271_STATE_OFF, 130 WL1271_STATE_OFF,
@@ -345,7 +346,9 @@ struct wl1271 {
345 bool tx_queue_stopped; 346 bool tx_queue_stopped;
346 347
347 struct work_struct tx_work; 348 struct work_struct tx_work;
349
348 struct work_struct filter_work; 350 struct work_struct filter_work;
351 struct wl1271_filter_params *filter_params;
349 352
350 /* Pending TX frames */ 353 /* Pending TX frames */
351 struct sk_buff *tx_frames[ACX_TX_DESCRIPTORS]; 354 struct sk_buff *tx_frames[ACX_TX_DESCRIPTORS];
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c
index 2ae1081ed627..a457123442a7 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.c
@@ -300,7 +300,8 @@ out:
300 return ret; 300 return ret;
301} 301}
302 302
303int wl1271_acx_group_address_tbl(struct wl1271 *wl) 303int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
304 void *mc_list, u32 mc_list_len)
304{ 305{
305 struct acx_dot11_grp_addr_tbl *acx; 306 struct acx_dot11_grp_addr_tbl *acx;
306 int ret; 307 int ret;
@@ -314,9 +315,9 @@ int wl1271_acx_group_address_tbl(struct wl1271 *wl)
314 } 315 }
315 316
316 /* MAC filtering */ 317 /* MAC filtering */
317 acx->enabled = 0; 318 acx->enabled = enable;
318 acx->num_groups = 0; 319 acx->num_groups = mc_list_len;
319 memset(acx->mac_table, 0, ADDRESS_GROUP_MAX_LEN); 320 memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN);
320 321
321 ret = wl1271_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL, 322 ret = wl1271_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL,
322 acx, sizeof(*acx)); 323 acx, sizeof(*acx));
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
index c1773459bf55..dae1fed66b30 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
@@ -301,8 +301,8 @@ struct acx_slot {
301} __attribute__ ((packed)); 301} __attribute__ ((packed));
302 302
303 303
304#define ADDRESS_GROUP_MAX (8) 304#define ACX_MC_ADDRESS_GROUP_MAX (8)
305#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ADDRESS_GROUP_MAX) 305#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ACX_MC_ADDRESS_GROUP_MAX)
306 306
307struct acx_dot11_grp_addr_tbl { 307struct acx_dot11_grp_addr_tbl {
308 struct acx_header header; 308 struct acx_header header;
@@ -313,7 +313,6 @@ struct acx_dot11_grp_addr_tbl {
313 u8 mac_table[ADDRESS_GROUP_MAX_LEN]; 313 u8 mac_table[ADDRESS_GROUP_MAX_LEN];
314} __attribute__ ((packed)); 314} __attribute__ ((packed));
315 315
316
317#define RX_TIMEOUT_PS_POLL_MIN 0 316#define RX_TIMEOUT_PS_POLL_MIN 0
318#define RX_TIMEOUT_PS_POLL_MAX (200000) 317#define RX_TIMEOUT_PS_POLL_MAX (200000)
319#define RX_TIMEOUT_PS_POLL_DEF (15) 318#define RX_TIMEOUT_PS_POLL_DEF (15)
@@ -1193,7 +1192,8 @@ int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time);
1193int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter); 1192int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter);
1194int wl1271_acx_pd_threshold(struct wl1271 *wl); 1193int wl1271_acx_pd_threshold(struct wl1271 *wl);
1195int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time); 1194int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time);
1196int wl1271_acx_group_address_tbl(struct wl1271 *wl); 1195int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
1196 void *mc_list, u32 mc_list_len);
1197int wl1271_acx_service_period_timeout(struct wl1271 *wl); 1197int wl1271_acx_service_period_timeout(struct wl1271 *wl);
1198int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold); 1198int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold);
1199int wl1271_acx_beacon_filter_opt(struct wl1271 *wl); 1199int wl1271_acx_beacon_filter_opt(struct wl1271 *wl);
diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c
index eb6b91ab968a..49ff4071c0bc 100644
--- a/drivers/net/wireless/wl12xx/wl1271_init.c
+++ b/drivers/net/wireless/wl12xx/wl1271_init.c
@@ -117,7 +117,7 @@ static int wl1271_init_phy_config(struct wl1271 *wl)
117 if (ret < 0) 117 if (ret < 0)
118 return ret; 118 return ret;
119 119
120 ret = wl1271_acx_group_address_tbl(wl); 120 ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0);
121 if (ret < 0) 121 if (ret < 0)
122 return ret; 122 return ret;
123 123
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index d1042305abcc..09fe9686977a 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -379,12 +379,39 @@ out:
379 return ret; 379 return ret;
380} 380}
381 381
382struct wl1271_filter_params {
383 unsigned int filters;
384 unsigned int changed;
385 int mc_list_length;
386 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
387};
388
389#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
390 FIF_ALLMULTI | \
391 FIF_FCSFAIL | \
392 FIF_BCN_PRBRESP_PROMISC | \
393 FIF_CONTROL | \
394 FIF_OTHER_BSS)
395
382static void wl1271_filter_work(struct work_struct *work) 396static void wl1271_filter_work(struct work_struct *work)
383{ 397{
384 struct wl1271 *wl = 398 struct wl1271 *wl =
385 container_of(work, struct wl1271, filter_work); 399 container_of(work, struct wl1271, filter_work);
400 struct wl1271_filter_params *fp;
401 unsigned long flags;
402 bool enabled = true;
386 int ret; 403 int ret;
387 404
405 /* first, get the filter parameters */
406 spin_lock_irqsave(&wl->wl_lock, flags);
407 fp = wl->filter_params;
408 wl->filter_params = NULL;
409 spin_unlock_irqrestore(&wl->wl_lock, flags);
410
411 if (!fp)
412 return;
413
414 /* then, lock the mutex without risk of lock-up */
388 mutex_lock(&wl->mutex); 415 mutex_lock(&wl->mutex);
389 416
390 if (wl->state == WL1271_STATE_OFF) 417 if (wl->state == WL1271_STATE_OFF)
@@ -394,6 +421,20 @@ static void wl1271_filter_work(struct work_struct *work)
394 if (ret < 0) 421 if (ret < 0)
395 goto out; 422 goto out;
396 423
424 /* configure the mc filter regardless of the changed flags */
425 if (fp->filters & FIF_ALLMULTI)
426 enabled = false;
427
428 ret = wl1271_acx_group_address_tbl(wl, enabled,
429 fp->mc_list, fp->mc_list_length);
430 if (ret < 0)
431 goto out_sleep;
432
433 /* determine, whether supported filter values have changed */
434 if (fp->changed == 0)
435 goto out;
436
437 /* apply configured filters */
397 ret = wl1271_cmd_join(wl); 438 ret = wl1271_cmd_join(wl);
398 if (ret < 0) 439 if (ret < 0)
399 goto out_sleep; 440 goto out_sleep;
@@ -403,6 +444,7 @@ out_sleep:
403 444
404out: 445out:
405 mutex_unlock(&wl->mutex); 446 mutex_unlock(&wl->mutex);
447 kfree(fp);
406} 448}
407 449
408int wl1271_plt_start(struct wl1271 *wl) 450int wl1271_plt_start(struct wl1271 *wl)
@@ -544,12 +586,20 @@ out:
544static void wl1271_op_stop(struct ieee80211_hw *hw) 586static void wl1271_op_stop(struct ieee80211_hw *hw)
545{ 587{
546 struct wl1271 *wl = hw->priv; 588 struct wl1271 *wl = hw->priv;
589 unsigned long flags;
547 int i; 590 int i;
548 591
549 wl1271_info("down"); 592 wl1271_info("down");
550 593
551 wl1271_debug(DEBUG_MAC80211, "mac80211 stop"); 594 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
552 595
596 /* complete/cancel ongoing work */
597 cancel_work_sync(&wl->filter_work);
598 spin_lock_irqsave(&wl->wl_lock, flags);
599 kfree(wl->filter_params);
600 wl->filter_params = NULL;
601 spin_unlock_irqrestore(&wl->wl_lock, flags);
602
553 mutex_lock(&wl->mutex); 603 mutex_lock(&wl->mutex);
554 604
555 WARN_ON(wl->state != WL1271_STATE_ON); 605 WARN_ON(wl->state != WL1271_STATE_ON);
@@ -784,16 +834,52 @@ out:
784 return ret; 834 return ret;
785} 835}
786 836
787#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ 837static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
788 FIF_ALLMULTI | \ 838 struct dev_addr_list *mc_list)
789 FIF_FCSFAIL | \ 839{
790 FIF_BCN_PRBRESP_PROMISC | \ 840 struct wl1271 *wl = hw->priv;
791 FIF_CONTROL | \ 841 struct wl1271_filter_params *fp;
792 FIF_OTHER_BSS) 842 unsigned long flags;
843 int i;
844
845 /*
846 * FIXME: we should return a hash that will be passed to
847 * configure_filter() instead of saving everything in the context.
848 */
849
850 fp = kzalloc(sizeof(*fp), GFP_KERNEL);
851 if (!fp) {
852 wl1271_error("Out of memory setting filters.");
853 return 0;
854 }
855
856 /* update multicast filtering parameters */
857 if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) {
858 mc_count = 0;
859 fp->filters |= FIF_ALLMULTI;
860 }
861
862 fp->mc_list_length = 0;
863 for (i = 0; i < mc_count; i++) {
864 if (mc_list->da_addrlen == ETH_ALEN) {
865 memcpy(fp->mc_list[fp->mc_list_length],
866 mc_list->da_addr, ETH_ALEN);
867 fp->mc_list_length++;
868 } else
869 wl1271_warning("Unknown mc address length.");
870 }
871
872 spin_lock_irqsave(&wl->wl_lock, flags);
873 kfree(wl->filter_params);
874 wl->filter_params = fp;
875 spin_unlock_irqrestore(&wl->wl_lock, flags);
876
877 return 1;
878}
793 879
794static void wl1271_op_configure_filter(struct ieee80211_hw *hw, 880static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
795 unsigned int changed, 881 unsigned int changed,
796 unsigned int *total,u64 multicast) 882 unsigned int *total, u64 multicast)
797{ 883{
798 struct wl1271 *wl = hw->priv; 884 struct wl1271 *wl = hw->priv;
799 885
@@ -802,19 +888,21 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
802 *total &= WL1271_SUPPORTED_FILTERS; 888 *total &= WL1271_SUPPORTED_FILTERS;
803 changed &= WL1271_SUPPORTED_FILTERS; 889 changed &= WL1271_SUPPORTED_FILTERS;
804 890
805 if (changed == 0) 891 if (!multicast)
806 return; 892 return;
807 893
808 /* FIXME: wl->rx_config and wl->rx_filter are not protected */
809 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
810 wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
811
812 /* 894 /*
813 * FIXME: workqueues need to be properly cancelled on stop(), for 895 * FIXME: for now we are still using a workqueue for filter
814 * now let's just disable changing the filter settings. They will 896 * configuration, but with the new mac80211, this is not needed,
815 * be updated any on config(). 897 * since configure_filter can now sleep. We now have
898 * prepare_multicast, which needs to be atomic instead.
816 */ 899 */
817 /* schedule_work(&wl->filter_work); */ 900
901 /* store current filter config */
902 wl->filter_params->filters = *total;
903 wl->filter_params->changed = changed;
904
905 ieee80211_queue_work(wl->hw, &wl->filter_work);
818} 906}
819 907
820static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 908static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
@@ -1177,6 +1265,7 @@ static const struct ieee80211_ops wl1271_ops = {
1177 .remove_interface = wl1271_op_remove_interface, 1265 .remove_interface = wl1271_op_remove_interface,
1178 .config = wl1271_op_config, 1266 .config = wl1271_op_config,
1179/* .config_interface = wl1271_op_config_interface, */ 1267/* .config_interface = wl1271_op_config_interface, */
1268 .prepare_multicast = wl1271_op_prepare_multicast,
1180 .configure_filter = wl1271_op_configure_filter, 1269 .configure_filter = wl1271_op_configure_filter,
1181 .tx = wl1271_op_tx, 1270 .tx = wl1271_op_tx,
1182 .set_key = wl1271_op_set_key, 1271 .set_key = wl1271_op_set_key,