diff options
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271.h | 7 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_acx.c | 9 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_acx.h | 8 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_init.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_main.c | 121 |
5 files changed, 120 insertions, 27 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 0b4744db22e..34a52b33bf5 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -97,7 +97,8 @@ enum { } while (0) #define WL1271_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \ - CFG_BSSID_FILTER_EN) + CFG_BSSID_FILTER_EN | \ + CFG_MC_FILTER_EN) #define WL1271_DEFAULT_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \ CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \ @@ -123,7 +124,7 @@ enum { #define WL1271_DEFAULT_BEACON_INT 100 #define WL1271_DEFAULT_DTIM_PERIOD 1 -#define ACX_TX_DESCRIPTORS 32 +#define ACX_TX_DESCRIPTORS 32 enum wl1271_state { WL1271_STATE_OFF, @@ -345,7 +346,9 @@ struct wl1271 { bool tx_queue_stopped; struct work_struct tx_work; + struct work_struct filter_work; + struct wl1271_filter_params *filter_params; /* Pending TX frames */ 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 2ae1081ed62..a457123442a 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -300,7 +300,8 @@ out: return ret; } -int wl1271_acx_group_address_tbl(struct wl1271 *wl) +int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable, + void *mc_list, u32 mc_list_len) { struct acx_dot11_grp_addr_tbl *acx; int ret; @@ -314,9 +315,9 @@ int wl1271_acx_group_address_tbl(struct wl1271 *wl) } /* MAC filtering */ - acx->enabled = 0; - acx->num_groups = 0; - memset(acx->mac_table, 0, ADDRESS_GROUP_MAX_LEN); + acx->enabled = enable; + acx->num_groups = mc_list_len; + memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN); ret = wl1271_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL, acx, sizeof(*acx)); diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h index c1773459bf5..dae1fed66b3 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.h +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -301,8 +301,8 @@ struct acx_slot { } __attribute__ ((packed)); -#define ADDRESS_GROUP_MAX (8) -#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ADDRESS_GROUP_MAX) +#define ACX_MC_ADDRESS_GROUP_MAX (8) +#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ACX_MC_ADDRESS_GROUP_MAX) struct acx_dot11_grp_addr_tbl { struct acx_header header; @@ -313,7 +313,6 @@ struct acx_dot11_grp_addr_tbl { u8 mac_table[ADDRESS_GROUP_MAX_LEN]; } __attribute__ ((packed)); - #define RX_TIMEOUT_PS_POLL_MIN 0 #define RX_TIMEOUT_PS_POLL_MAX (200000) #define RX_TIMEOUT_PS_POLL_DEF (15) @@ -1193,7 +1192,8 @@ int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time); int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter); int wl1271_acx_pd_threshold(struct wl1271 *wl); int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time); -int wl1271_acx_group_address_tbl(struct wl1271 *wl); +int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable, + void *mc_list, u32 mc_list_len); int wl1271_acx_service_period_timeout(struct wl1271 *wl); int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold); int 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 eb6b91ab968..49ff4071c0b 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) if (ret < 0) return ret; - ret = wl1271_acx_group_address_tbl(wl); + ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index d1042305abc..09fe9686977 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -379,12 +379,39 @@ out: return ret; } +struct wl1271_filter_params { + unsigned int filters; + unsigned int changed; + int mc_list_length; + u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN]; +}; + +#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ + FIF_ALLMULTI | \ + FIF_FCSFAIL | \ + FIF_BCN_PRBRESP_PROMISC | \ + FIF_CONTROL | \ + FIF_OTHER_BSS) + static void wl1271_filter_work(struct work_struct *work) { struct wl1271 *wl = container_of(work, struct wl1271, filter_work); + struct wl1271_filter_params *fp; + unsigned long flags; + bool enabled = true; int ret; + /* first, get the filter parameters */ + spin_lock_irqsave(&wl->wl_lock, flags); + fp = wl->filter_params; + wl->filter_params = NULL; + spin_unlock_irqrestore(&wl->wl_lock, flags); + + if (!fp) + return; + + /* then, lock the mutex without risk of lock-up */ mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) @@ -394,6 +421,20 @@ static void wl1271_filter_work(struct work_struct *work) if (ret < 0) goto out; + /* configure the mc filter regardless of the changed flags */ + if (fp->filters & FIF_ALLMULTI) + enabled = false; + + ret = wl1271_acx_group_address_tbl(wl, enabled, + fp->mc_list, fp->mc_list_length); + if (ret < 0) + goto out_sleep; + + /* determine, whether supported filter values have changed */ + if (fp->changed == 0) + goto out; + + /* apply configured filters */ ret = wl1271_cmd_join(wl); if (ret < 0) goto out_sleep; @@ -403,6 +444,7 @@ out_sleep: out: mutex_unlock(&wl->mutex); + kfree(fp); } int wl1271_plt_start(struct wl1271 *wl) @@ -544,12 +586,20 @@ out: static void wl1271_op_stop(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; + unsigned long flags; int i; wl1271_info("down"); wl1271_debug(DEBUG_MAC80211, "mac80211 stop"); + /* complete/cancel ongoing work */ + cancel_work_sync(&wl->filter_work); + spin_lock_irqsave(&wl->wl_lock, flags); + kfree(wl->filter_params); + wl->filter_params = NULL; + spin_unlock_irqrestore(&wl->wl_lock, flags); + mutex_lock(&wl->mutex); WARN_ON(wl->state != WL1271_STATE_ON); @@ -784,16 +834,52 @@ out: return ret; } -#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ - FIF_ALLMULTI | \ - FIF_FCSFAIL | \ - FIF_BCN_PRBRESP_PROMISC | \ - FIF_CONTROL | \ - FIF_OTHER_BSS) +static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, + struct dev_addr_list *mc_list) +{ + struct wl1271 *wl = hw->priv; + struct wl1271_filter_params *fp; + unsigned long flags; + int i; + + /* + * FIXME: we should return a hash that will be passed to + * configure_filter() instead of saving everything in the context. + */ + + fp = kzalloc(sizeof(*fp), GFP_KERNEL); + if (!fp) { + wl1271_error("Out of memory setting filters."); + return 0; + } + + /* update multicast filtering parameters */ + if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) { + mc_count = 0; + fp->filters |= FIF_ALLMULTI; + } + + fp->mc_list_length = 0; + for (i = 0; i < mc_count; i++) { + if (mc_list->da_addrlen == ETH_ALEN) { + memcpy(fp->mc_list[fp->mc_list_length], + mc_list->da_addr, ETH_ALEN); + fp->mc_list_length++; + } else + wl1271_warning("Unknown mc address length."); + } + + spin_lock_irqsave(&wl->wl_lock, flags); + kfree(wl->filter_params); + wl->filter_params = fp; + spin_unlock_irqrestore(&wl->wl_lock, flags); + + return 1; +} static void wl1271_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, - unsigned int *total,u64 multicast) + unsigned int *total, u64 multicast) { struct wl1271 *wl = hw->priv; @@ -802,19 +888,21 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw, *total &= WL1271_SUPPORTED_FILTERS; changed &= WL1271_SUPPORTED_FILTERS; - if (changed == 0) + if (!multicast) return; - /* FIXME: wl->rx_config and wl->rx_filter are not protected */ - wl->rx_config = WL1271_DEFAULT_RX_CONFIG; - wl->rx_filter = WL1271_DEFAULT_RX_FILTER; - /* - * 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(). + * FIXME: for now we are still using a workqueue for filter + * configuration, but with the new mac80211, this is not needed, + * since configure_filter can now sleep. We now have + * prepare_multicast, which needs to be atomic instead. */ - /* schedule_work(&wl->filter_work); */ + + /* store current filter config */ + wl->filter_params->filters = *total; + wl->filter_params->changed = changed; + + ieee80211_queue_work(wl->hw, &wl->filter_work); } static 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 = { .remove_interface = wl1271_op_remove_interface, .config = wl1271_op_config, /* .config_interface = wl1271_op_config_interface, */ + .prepare_multicast = wl1271_op_prepare_multicast, .configure_filter = wl1271_op_configure_filter, .tx = wl1271_op_tx, .set_key = wl1271_op_set_key, |