Skip to content

Commit

Permalink
wl1271: Multicast filtering configuration
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Juuso Oikarinen authored and John W. Linville committed Oct 27, 2009
1 parent 66497dc commit c87dec9
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 27 deletions.
7 changes: 5 additions & 2 deletions drivers/net/wireless/wl12xx/wl1271.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 | \
Expand All @@ -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,
Expand Down Expand Up @@ -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];
Expand Down
9 changes: 5 additions & 4 deletions drivers/net/wireless/wl12xx/wl1271_acx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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));
Expand Down
8 changes: 4 additions & 4 deletions drivers/net/wireless/wl12xx/wl1271_acx.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/wireless/wl12xx/wl1271_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
121 changes: 105 additions & 16 deletions drivers/net/wireless/wl12xx/wl1271_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit c87dec9

Please sign in to comment.