mac80211: allow configure_filter callback to sleep

Over time, a whole bunch of drivers have come up
with their own scheme to delay the configure_filter
operation to a workqueue. To be able to simplify
things, allow configure_filter to sleep, and add
a new prepare_multicast callback that drivers that
need the multicast address list implement. This new
callback must be atomic, but most drivers either
don't care or just calculate a hash which can be
done atomically and then uploaded to the hardware
non-atomically.

A cursory look suggests that at76c50x-usb, ar9170,
mwl8k (which is actually very broken now), rt2x00,
wl1251, wl1271 and zd1211 should make use of this
new capability.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Johannes Berg
2009-08-17 16:16:53 +02:00
committed by John W. Linville
parent ea416a793d
commit 3ac64beecd
28 changed files with 298 additions and 170 deletions

View File

@@ -2100,10 +2100,29 @@ unlock:
mutex_unlock(&ar->mutex);
}
static u64 ar9170_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
struct dev_addr_list *mclist)
{
u64 mchash;
int i;
/* always get broadcast frames */
mchash = 1ULL << (0xff >> 2);
for (i = 0; i < mc_count; i++) {
if (WARN_ON(!mclist))
break;
mchash |= 1ULL << (mclist->dmi_addr[5] >> 2);
mclist = mclist->next;
}
return mchash;
}
static void ar9170_op_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *new_flags,
int mc_count, struct dev_mc_list *mclist)
u64 multicast)
{
struct ar9170 *ar = hw->priv;
@@ -2116,24 +2135,11 @@ static void ar9170_op_configure_filter(struct ieee80211_hw *hw,
* then checking the error flags, later.
*/
if (changed_flags & FIF_ALLMULTI) {
if (*new_flags & FIF_ALLMULTI) {
ar->want_mc_hash = ~0ULL;
} else {
u64 mchash;
int i;
if (changed_flags & FIF_ALLMULTI && *new_flags & FIF_ALLMULTI)
multicast = ~0ULL;
/* always get broadcast frames */
mchash = 1ULL << (0xff >> 2);
for (i = 0; i < mc_count; i++) {
if (WARN_ON(!mclist))
break;
mchash |= 1ULL << (mclist->dmi_addr[5] >> 2);
mclist = mclist->next;
}
ar->want_mc_hash = mchash;
}
if (multicast != ar->want_mc_hash) {
ar->want_mc_hash = multicast;
set_bit(AR9170_FILTER_CHANGED_MULTICAST, &ar->filter_changed);
}
@@ -2543,6 +2549,7 @@ static const struct ieee80211_ops ar9170_ops = {
.add_interface = ar9170_op_add_interface,
.remove_interface = ar9170_op_remove_interface,
.config = ar9170_op_config,
.prepare_multicast = ar9170_op_prepare_multicast,
.configure_filter = ar9170_op_configure_filter,
.conf_tx = ar9170_conf_tx,
.bss_info_changed = ar9170_op_bss_info_changed,

View File

@@ -229,10 +229,12 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
static void ath5k_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
static int ath5k_config(struct ieee80211_hw *hw, u32 changed);
static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw,
int mc_count, struct dev_addr_list *mc_list);
static void ath5k_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *new_flags,
int mc_count, struct dev_mc_list *mclist);
u64 multicast);
static int ath5k_set_key(struct ieee80211_hw *hw,
enum set_key_cmd cmd,
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
@@ -260,6 +262,7 @@ static const struct ieee80211_ops ath5k_hw_ops = {
.add_interface = ath5k_add_interface,
.remove_interface = ath5k_remove_interface,
.config = ath5k_config,
.prepare_multicast = ath5k_prepare_multicast,
.configure_filter = ath5k_configure_filter,
.set_key = ath5k_set_key,
.get_stats = ath5k_get_stats,
@@ -2853,6 +2856,37 @@ unlock:
return ret;
}
static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw,
int mc_count, struct dev_addr_list *mclist)
{
u32 mfilt[2], val;
int i;
u8 pos;
mfilt[0] = 0;
mfilt[1] = 1;
for (i = 0; i < mc_count; i++) {
if (!mclist)
break;
/* calculate XOR of eight 6-bit values */
val = get_unaligned_le32(mclist->dmi_addr + 0);
pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
val = get_unaligned_le32(mclist->dmi_addr + 3);
pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
pos &= 0x3f;
mfilt[pos / 32] |= (1 << (pos % 32));
/* XXX: we might be able to just do this instead,
* but not sure, needs testing, if we do use this we'd
* neet to inform below to not reset the mcast */
/* ath5k_hw_set_mcast_filterindex(ah,
* mclist->dmi_addr[5]); */
mclist = mclist->next;
}
return ((u64)(mfilt[1]) << 32) | mfilt[0];
}
#define SUPPORTED_FIF_FLAGS \
FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | \
FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \
@@ -2878,16 +2912,14 @@ unlock:
static void ath5k_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *new_flags,
int mc_count, struct dev_mc_list *mclist)
u64 multicast)
{
struct ath5k_softc *sc = hw->priv;
struct ath5k_hw *ah = sc->ah;
u32 mfilt[2], val, rfilt;
u8 pos;
int i;
u32 mfilt[2], rfilt;
mfilt[0] = 0;
mfilt[1] = 0;
mfilt[0] = multicast;
mfilt[1] = multicast >> 32;
/* Only deal with supported flags */
changed_flags &= SUPPORTED_FIF_FLAGS;
@@ -2913,24 +2945,6 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
if (*new_flags & FIF_ALLMULTI) {
mfilt[0] = ~0;
mfilt[1] = ~0;
} else {
for (i = 0; i < mc_count; i++) {
if (!mclist)
break;
/* calculate XOR of eight 6-bit values */
val = get_unaligned_le32(mclist->dmi_addr + 0);
pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
val = get_unaligned_le32(mclist->dmi_addr + 3);
pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
pos &= 0x3f;
mfilt[pos / 32] |= (1 << (pos % 32));
/* XXX: we might be able to just do this instead,
* but not sure, needs testing, if we do use this we'd
* neet to inform below to not reset the mcast */
/* ath5k_hw_set_mcast_filterindex(ah,
* mclist->dmi_addr[5]); */
mclist = mclist->next;
}
}
/* This is the best we can do */

View File

@@ -2394,8 +2394,7 @@ skip_chan_change:
static void ath9k_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *total_flags,
int mc_count,
struct dev_mc_list *mclist)
u64 multicast)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;