Commit | Line | Data |
---|---|---|
f5fc0f86 LC |
1 | /* |
2 | * This file is part of wl1271 | |
3 | * | |
4 | * Copyright (C) 2008-2009 Nokia Corporation | |
5 | * | |
6 | * Contact: Luciano Coelho <luciano.coelho@nokia.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License | |
10 | * version 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
20 | * 02110-1301 USA | |
21 | * | |
22 | */ | |
23 | ||
c31be25a | 24 | #include "wlcore.h" |
0f4e3122 | 25 | #include "debug.h" |
00d20100 SL |
26 | #include "io.h" |
27 | #include "event.h" | |
28 | #include "ps.h" | |
29 | #include "scan.h" | |
66497dc3 | 30 | #include "wl12xx_80211.h" |
f5fc0f86 | 31 | |
c50a2825 | 32 | void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr) |
00236aed | 33 | { |
c50a2825 EP |
34 | struct wl12xx_vif *wlvif; |
35 | struct ieee80211_vif *vif; | |
00236aed | 36 | enum nl80211_cqm_rssi_threshold_event event; |
c50a2825 | 37 | s8 metric = metric_arr[0]; |
00236aed JO |
38 | |
39 | wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric); | |
40 | ||
c50a2825 EP |
41 | /* TODO: check actual multi-role support */ |
42 | wl12xx_for_each_wlvif_sta(wl, wlvif) { | |
43 | if (metric <= wlvif->rssi_thold) | |
44 | event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; | |
45 | else | |
46 | event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; | |
47 | ||
48 | vif = wl12xx_wlvif_to_vif(wlvif); | |
49 | if (event != wlvif->last_rssi_event) | |
50 | ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL); | |
51 | wlvif->last_rssi_event = event; | |
52 | } | |
00236aed | 53 | } |
c50a2825 | 54 | EXPORT_SYMBOL_GPL(wlcore_event_rssi_trigger); |
00236aed | 55 | |
536129c8 | 56 | static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
70559a06 | 57 | { |
4b730b6a EP |
58 | struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); |
59 | ||
536129c8 | 60 | if (wlvif->bss_type != BSS_TYPE_AP_BSS) { |
9ae5d8d4 AN |
61 | u8 hlid = wlvif->sta.hlid; |
62 | if (!wl->links[hlid].ba_bitmap) | |
f4d3b6ab | 63 | return; |
9ae5d8d4 | 64 | ieee80211_stop_rx_ba_session(vif, wl->links[hlid].ba_bitmap, |
4b730b6a | 65 | vif->bss_conf.bssid); |
f4d3b6ab | 66 | } else { |
c7ffb902 | 67 | u8 hlid; |
f4d3b6ab | 68 | struct wl1271_link *lnk; |
c7ffb902 EP |
69 | for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, |
70 | WL12XX_MAX_LINKS) { | |
71 | lnk = &wl->links[hlid]; | |
72 | if (!lnk->ba_bitmap) | |
f4d3b6ab AN |
73 | continue; |
74 | ||
4b730b6a | 75 | ieee80211_stop_rx_ba_session(vif, |
f4d3b6ab AN |
76 | lnk->ba_bitmap, |
77 | lnk->addr); | |
78 | } | |
79 | } | |
70559a06 SL |
80 | } |
81 | ||
c50a2825 | 82 | void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable) |
77ddaa10 | 83 | { |
4b730b6a EP |
84 | struct wl12xx_vif *wlvif; |
85 | ||
77ddaa10 | 86 | if (enable) { |
77ddaa10 EP |
87 | set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); |
88 | } else { | |
9eb599e9 | 89 | clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); |
4b730b6a | 90 | wl12xx_for_each_wlvif_sta(wl, wlvif) { |
9eb599e9 | 91 | wl1271_recalc_rx_streaming(wl, wlvif); |
4b730b6a | 92 | } |
77ddaa10 | 93 | } |
77ddaa10 | 94 | } |
c50a2825 | 95 | EXPORT_SYMBOL_GPL(wlcore_event_soft_gemini_sense); |
77ddaa10 | 96 | |
c50a2825 EP |
97 | void wlcore_event_sched_scan_completed(struct wl1271 *wl, |
98 | u8 status) | |
f5fc0f86 | 99 | { |
c50a2825 EP |
100 | wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)", |
101 | status); | |
f5fc0f86 | 102 | |
10199756 | 103 | if (wl->sched_vif) { |
c50a2825 | 104 | ieee80211_sched_scan_stopped(wl->hw); |
10199756 | 105 | wl->sched_vif = NULL; |
c50a2825 EP |
106 | } |
107 | } | |
108 | EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_completed); | |
f5fc0f86 | 109 | |
c50a2825 EP |
110 | void wlcore_event_ba_rx_constraint(struct wl1271 *wl, |
111 | unsigned long roles_bitmap, | |
112 | unsigned long allowed_bitmap) | |
113 | { | |
114 | struct wl12xx_vif *wlvif; | |
f5fc0f86 | 115 | |
c50a2825 EP |
116 | wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx allowed=0x%lx", |
117 | __func__, roles_bitmap, allowed_bitmap); | |
34dd2aaa | 118 | |
c50a2825 EP |
119 | wl12xx_for_each_wlvif(wl, wlvif) { |
120 | if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || | |
121 | !test_bit(wlvif->role_id , &roles_bitmap)) | |
122 | continue; | |
f5fc0f86 | 123 | |
c50a2825 EP |
124 | wlvif->ba_allowed = !!test_bit(wlvif->role_id, |
125 | &allowed_bitmap); | |
126 | if (!wlvif->ba_allowed) | |
127 | wl1271_stop_ba_event(wl, wlvif); | |
6394c01b | 128 | } |
c50a2825 EP |
129 | } |
130 | EXPORT_SYMBOL_GPL(wlcore_event_ba_rx_constraint); | |
6394c01b | 131 | |
c50a2825 EP |
132 | void wlcore_event_channel_switch(struct wl1271 *wl, |
133 | unsigned long roles_bitmap, | |
134 | bool success) | |
135 | { | |
136 | struct wl12xx_vif *wlvif; | |
137 | struct ieee80211_vif *vif; | |
6394c01b | 138 | |
c50a2825 EP |
139 | wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d", |
140 | __func__, roles_bitmap, success); | |
8d2ef7bd | 141 | |
c50a2825 EP |
142 | wl12xx_for_each_wlvif_sta(wl, wlvif) { |
143 | if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || | |
144 | !test_bit(wlvif->role_id , &roles_bitmap)) | |
145 | continue; | |
6b8bf5bc | 146 | |
c50a2825 EP |
147 | if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, |
148 | &wlvif->flags)) | |
149 | continue; | |
9f5b424d | 150 | |
c50a2825 | 151 | vif = wl12xx_wlvif_to_vif(wlvif); |
9f5b424d | 152 | |
c50a2825 EP |
153 | ieee80211_chswitch_done(vif, success); |
154 | cancel_delayed_work(&wlvif->channel_switch_work); | |
5f561f68 | 155 | } |
c50a2825 EP |
156 | } |
157 | EXPORT_SYMBOL_GPL(wlcore_event_channel_switch); | |
f5fc0f86 | 158 | |
c50a2825 EP |
159 | void wlcore_event_dummy_packet(struct wl1271 *wl) |
160 | { | |
161 | wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); | |
162 | wl1271_tx_dummy_packet(wl); | |
163 | } | |
164 | EXPORT_SYMBOL_GPL(wlcore_event_dummy_packet); | |
19ad0715 | 165 | |
c50a2825 EP |
166 | static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap) |
167 | { | |
168 | u32 num_packets = wl->conf.tx.max_tx_retries; | |
169 | struct wl12xx_vif *wlvif; | |
170 | struct ieee80211_vif *vif; | |
171 | struct ieee80211_sta *sta; | |
172 | const u8 *addr; | |
173 | int h; | |
174 | ||
175 | for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) { | |
176 | bool found = false; | |
177 | /* find the ap vif connected to this sta */ | |
178 | wl12xx_for_each_wlvif_ap(wl, wlvif) { | |
179 | if (!test_bit(h, wlvif->ap.sta_hlid_map)) | |
180 | continue; | |
181 | found = true; | |
182 | break; | |
4b730b6a | 183 | } |
c50a2825 EP |
184 | if (!found) |
185 | continue; | |
70559a06 | 186 | |
c50a2825 EP |
187 | vif = wl12xx_wlvif_to_vif(wlvif); |
188 | addr = wl->links[h].addr; | |
f4d3b6ab | 189 | |
c50a2825 EP |
190 | rcu_read_lock(); |
191 | sta = ieee80211_find_sta(vif, addr); | |
192 | if (sta) { | |
193 | wl1271_debug(DEBUG_EVENT, "remove sta %d", h); | |
194 | ieee80211_report_low_ack(sta, num_packets); | |
4b730b6a | 195 | } |
c50a2825 | 196 | rcu_read_unlock(); |
70559a06 | 197 | } |
c50a2825 | 198 | } |
70559a06 | 199 | |
c50a2825 EP |
200 | void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap) |
201 | { | |
202 | wl1271_debug(DEBUG_EVENT, "MAX_TX_FAILURE_EVENT_ID"); | |
203 | wlcore_disconnect_sta(wl, sta_bitmap); | |
204 | } | |
205 | EXPORT_SYMBOL_GPL(wlcore_event_max_tx_failure); | |
775e1a4b | 206 | |
c50a2825 EP |
207 | void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap) |
208 | { | |
209 | wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID"); | |
210 | wlcore_disconnect_sta(wl, sta_bitmap); | |
211 | } | |
212 | EXPORT_SYMBOL_GPL(wlcore_event_inactive_sta); | |
6d158ff3 | 213 | |
c50a2825 EP |
214 | void wlcore_event_roc_complete(struct wl1271 *wl) |
215 | { | |
216 | wl1271_debug(DEBUG_EVENT, "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID"); | |
217 | if (wl->roc_vif) | |
218 | ieee80211_ready_on_channel(wl->hw); | |
219 | } | |
220 | EXPORT_SYMBOL_GPL(wlcore_event_roc_complete); | |
ae47c45f | 221 | |
c50a2825 EP |
222 | void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap) |
223 | { | |
3618f30f | 224 | /* |
c50a2825 EP |
225 | * We are HW_MONITOR device. On beacon loss - queue |
226 | * connection loss work. Cancel it on REGAINED event. | |
3618f30f | 227 | */ |
c50a2825 EP |
228 | struct wl12xx_vif *wlvif; |
229 | struct ieee80211_vif *vif; | |
230 | int delay = wl->conf.conn.synch_fail_thold * | |
231 | wl->conf.conn.bss_lose_timeout; | |
3618f30f | 232 | |
c50a2825 | 233 | wl1271_info("Beacon loss detected. roles:0x%lx", roles_bitmap); |
dabf37db | 234 | |
c50a2825 EP |
235 | wl12xx_for_each_wlvif_sta(wl, wlvif) { |
236 | if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || | |
237 | !test_bit(wlvif->role_id , &roles_bitmap)) | |
238 | continue; | |
3618f30f | 239 | |
c0ad2f2e ES |
240 | vif = wl12xx_wlvif_to_vif(wlvif); |
241 | ||
242 | /* don't attempt roaming in case of p2p */ | |
243 | if (wlvif->p2p) { | |
244 | ieee80211_connection_loss(vif); | |
245 | continue; | |
246 | } | |
247 | ||
c50a2825 EP |
248 | /* |
249 | * if the work is already queued, it should take place. | |
250 | * We don't want to delay the connection loss | |
251 | * indication any more. | |
252 | */ | |
253 | ieee80211_queue_delayed_work(wl->hw, | |
254 | &wlvif->connection_loss_work, | |
255 | msecs_to_jiffies(delay)); | |
3618f30f | 256 | |
c50a2825 EP |
257 | ieee80211_cqm_rssi_notify( |
258 | vif, | |
259 | NL80211_CQM_RSSI_BEACON_LOSS_EVENT, | |
260 | GFP_KERNEL); | |
3618f30f | 261 | } |
f5fc0f86 | 262 | } |
c50a2825 | 263 | EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss); |
f5fc0f86 LC |
264 | |
265 | int wl1271_event_unmask(struct wl1271 *wl) | |
266 | { | |
267 | int ret; | |
268 | ||
269 | ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask)); | |
270 | if (ret < 0) | |
271 | return ret; | |
272 | ||
273 | return 0; | |
274 | } | |
275 | ||
13f2dc52 | 276 | int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) |
f5fc0f86 | 277 | { |
f5fc0f86 LC |
278 | int ret; |
279 | ||
280 | wl1271_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num); | |
281 | ||
282 | if (mbox_num > 1) | |
283 | return -EINVAL; | |
284 | ||
285 | /* first we read the mbox descriptor */ | |
045b9b5f | 286 | ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox, |
c50a2825 | 287 | wl->mbox_size, false); |
045b9b5f IY |
288 | if (ret < 0) |
289 | return ret; | |
f5fc0f86 LC |
290 | |
291 | /* process the descriptor */ | |
c50a2825 | 292 | ret = wl->ops->process_mailbox_events(wl); |
f5fc0f86 LC |
293 | if (ret < 0) |
294 | return ret; | |
295 | ||
f16ff758 LC |
296 | /* |
297 | * TODO: we just need this because one bit is in a different | |
298 | * place. Is there any better way? | |
299 | */ | |
b0f0ad39 | 300 | ret = wl->ops->ack_event(wl); |
f5fc0f86 | 301 | |
b0f0ad39 | 302 | return ret; |
f5fc0f86 | 303 | } |