diff options
author | Juuso Oikarinen <juuso.oikarinen@nokia.com> | 2009-10-08 21:56:31 +0300 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-10-27 16:47:53 -0400 |
commit | c87dec9f189b884df215756e285b9281cf065206 (patch) | |
tree | d8fd4ed6f27e28c0dcf5d77240e4f83cd2295129 /drivers/net/wireless/wl12xx/wl1271_main.c | |
parent | 66497dc3bd569e05a5bcb729d495eebad47aa46a (diff) | |
download | kernel_samsung_smdk4412-c87dec9f189b884df215756e285b9281cf065206.tar.gz kernel_samsung_smdk4412-c87dec9f189b884df215756e285b9281cf065206.tar.bz2 kernel_samsung_smdk4412-c87dec9f189b884df215756e285b9281cf065206.zip |
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>
Diffstat (limited to 'drivers/net/wireless/wl12xx/wl1271_main.c')
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_main.c | 121 |
1 files changed, 105 insertions, 16 deletions
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, |