Commit c87dec9f authored by Juuso Oikarinen's avatar Juuso Oikarinen Committed by John W. Linville

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: default avatarJuuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: default avatarLuciano Coelho <luciano.coelho@nokia.com>
Signed-off-by: default avatarLuciano Coelho <luciano.coelho@nokia.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 66497dc3
......@@ -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 | \
......@@ -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];
......
......@@ -300,7 +300,8 @@ int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time)
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));
......
......@@ -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);
......
......@@ -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;
......
......@@ -379,12 +379,39 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
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 @@ static void wl1271_filter_work(struct work_struct *work)
out:
mutex_unlock(&wl->mutex);
kfree(fp);
}
int wl1271_plt_start(struct wl1271 *wl)
......@@ -544,12 +586,20 @@ static int wl1271_op_start(struct ieee80211_hw *hw)
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 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
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,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment