diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-08-17 10:16:53 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-08-20 11:35:58 -0400 |
commit | 3ac64beecd27400d12cc7afb4108eef26c499f6a (patch) | |
tree | da0220085f68e30fe61ba9b8833dc6311d6dc25e /net/mac80211/main.c | |
parent | ea416a793d2b611f22b42ba094fd2e5bd30fff43 (diff) |
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>
Diffstat (limited to 'net/mac80211/main.c')
-rw-r--r-- | net/mac80211/main.c | 24 |
1 files changed, 19 insertions, 5 deletions
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b03fd84777fa..05f923575fee 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -50,9 +50,9 @@ struct ieee80211_tx_status_rtap_hdr { | |||
50 | } __attribute__ ((packed)); | 50 | } __attribute__ ((packed)); |
51 | 51 | ||
52 | 52 | ||
53 | /* must be called under mdev tx lock */ | ||
54 | void ieee80211_configure_filter(struct ieee80211_local *local) | 53 | void ieee80211_configure_filter(struct ieee80211_local *local) |
55 | { | 54 | { |
55 | u64 mc; | ||
56 | unsigned int changed_flags; | 56 | unsigned int changed_flags; |
57 | unsigned int new_flags = 0; | 57 | unsigned int new_flags = 0; |
58 | 58 | ||
@@ -62,7 +62,7 @@ void ieee80211_configure_filter(struct ieee80211_local *local) | |||
62 | if (atomic_read(&local->iff_allmultis)) | 62 | if (atomic_read(&local->iff_allmultis)) |
63 | new_flags |= FIF_ALLMULTI; | 63 | new_flags |= FIF_ALLMULTI; |
64 | 64 | ||
65 | if (local->monitors) | 65 | if (local->monitors || local->scanning) |
66 | new_flags |= FIF_BCN_PRBRESP_PROMISC; | 66 | new_flags |= FIF_BCN_PRBRESP_PROMISC; |
67 | 67 | ||
68 | if (local->fif_fcsfail) | 68 | if (local->fif_fcsfail) |
@@ -80,20 +80,30 @@ void ieee80211_configure_filter(struct ieee80211_local *local) | |||
80 | if (local->fif_pspoll) | 80 | if (local->fif_pspoll) |
81 | new_flags |= FIF_PSPOLL; | 81 | new_flags |= FIF_PSPOLL; |
82 | 82 | ||
83 | spin_lock_bh(&local->filter_lock); | ||
83 | changed_flags = local->filter_flags ^ new_flags; | 84 | changed_flags = local->filter_flags ^ new_flags; |
84 | 85 | ||
86 | mc = drv_prepare_multicast(local, local->mc_count, local->mc_list); | ||
87 | spin_unlock_bh(&local->filter_lock); | ||
88 | |||
85 | /* be a bit nasty */ | 89 | /* be a bit nasty */ |
86 | new_flags |= (1<<31); | 90 | new_flags |= (1<<31); |
87 | 91 | ||
88 | drv_configure_filter(local, changed_flags, &new_flags, | 92 | drv_configure_filter(local, changed_flags, &new_flags, mc); |
89 | local->mc_count, | ||
90 | local->mc_list); | ||
91 | 93 | ||
92 | WARN_ON(new_flags & (1<<31)); | 94 | WARN_ON(new_flags & (1<<31)); |
93 | 95 | ||
94 | local->filter_flags = new_flags & ~(1<<31); | 96 | local->filter_flags = new_flags & ~(1<<31); |
95 | } | 97 | } |
96 | 98 | ||
99 | static void ieee80211_reconfig_filter(struct work_struct *work) | ||
100 | { | ||
101 | struct ieee80211_local *local = | ||
102 | container_of(work, struct ieee80211_local, reconfig_filter); | ||
103 | |||
104 | ieee80211_configure_filter(local); | ||
105 | } | ||
106 | |||
97 | int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) | 107 | int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) |
98 | { | 108 | { |
99 | struct ieee80211_channel *chan, *scan_chan; | 109 | struct ieee80211_channel *chan, *scan_chan; |
@@ -692,6 +702,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
692 | 702 | ||
693 | INIT_WORK(&local->restart_work, ieee80211_restart_work); | 703 | INIT_WORK(&local->restart_work, ieee80211_restart_work); |
694 | 704 | ||
705 | INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); | ||
706 | |||
695 | INIT_WORK(&local->dynamic_ps_enable_work, | 707 | INIT_WORK(&local->dynamic_ps_enable_work, |
696 | ieee80211_dynamic_ps_enable_work); | 708 | ieee80211_dynamic_ps_enable_work); |
697 | INIT_WORK(&local->dynamic_ps_disable_work, | 709 | INIT_WORK(&local->dynamic_ps_disable_work, |
@@ -946,6 +958,8 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) | |||
946 | 958 | ||
947 | rtnl_unlock(); | 959 | rtnl_unlock(); |
948 | 960 | ||
961 | cancel_work_sync(&local->reconfig_filter); | ||
962 | |||
949 | ieee80211_clear_tx_pending(local); | 963 | ieee80211_clear_tx_pending(local); |
950 | sta_info_stop(local); | 964 | sta_info_stop(local); |
951 | rate_control_deinitialize(local); | 965 | rate_control_deinitialize(local); |