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" |
3719c17e SP |
31 | #include "hw_ops.h" |
32 | ||
33 | #define WL18XX_LOGGER_SDIO_BUFF_MAX (0x1020) | |
34 | #define WL18XX_DATA_RAM_BASE_ADDRESS (0x20000000) | |
35 | #define WL18XX_LOGGER_SDIO_BUFF_ADDR (0x40159c) | |
36 | #define WL18XX_LOGGER_BUFF_OFFSET (sizeof(struct fw_logger_information)) | |
37 | #define WL18XX_LOGGER_READ_POINT_OFFSET (12) | |
38 | ||
39 | int wlcore_event_fw_logger(struct wl1271 *wl) | |
40 | { | |
41 | u32 ret; | |
42 | struct fw_logger_information fw_log; | |
43 | u8 *buffer; | |
44 | u32 internal_fw_addrbase = WL18XX_DATA_RAM_BASE_ADDRESS; | |
45 | u32 addr = WL18XX_LOGGER_SDIO_BUFF_ADDR; | |
46 | u32 end_buff_addr = WL18XX_LOGGER_SDIO_BUFF_ADDR + | |
47 | WL18XX_LOGGER_BUFF_OFFSET; | |
48 | u32 available_len; | |
49 | u32 actual_len; | |
50 | u32 clear_addr; | |
51 | size_t len; | |
52 | u32 start_loc; | |
53 | ||
54 | buffer = kzalloc(WL18XX_LOGGER_SDIO_BUFF_MAX, GFP_KERNEL); | |
55 | if (!buffer) { | |
56 | wl1271_error("Fail to allocate fw logger memory"); | |
57 | fw_log.actual_buff_size = cpu_to_le32(0); | |
58 | goto out; | |
59 | } | |
60 | ||
61 | ret = wlcore_read(wl, addr, buffer, WL18XX_LOGGER_SDIO_BUFF_MAX, | |
62 | false); | |
63 | if (ret < 0) { | |
64 | wl1271_error("Fail to read logger buffer, error_id = %d", | |
65 | ret); | |
66 | fw_log.actual_buff_size = cpu_to_le32(0); | |
67 | goto free_out; | |
68 | } | |
69 | ||
70 | memcpy(&fw_log, buffer, sizeof(fw_log)); | |
71 | ||
72 | if (le32_to_cpu(fw_log.actual_buff_size) == 0) | |
73 | goto free_out; | |
74 | ||
75 | actual_len = le32_to_cpu(fw_log.actual_buff_size); | |
76 | start_loc = (le32_to_cpu(fw_log.buff_read_ptr) - | |
77 | internal_fw_addrbase) - addr; | |
78 | end_buff_addr += le32_to_cpu(fw_log.max_buff_size); | |
79 | available_len = end_buff_addr - | |
80 | (le32_to_cpu(fw_log.buff_read_ptr) - | |
81 | internal_fw_addrbase); | |
82 | actual_len = min(actual_len, available_len); | |
83 | len = actual_len; | |
84 | ||
85 | wl12xx_copy_fwlog(wl, &buffer[start_loc], len); | |
86 | clear_addr = addr + start_loc + le32_to_cpu(fw_log.actual_buff_size) + | |
87 | internal_fw_addrbase; | |
88 | ||
89 | len = le32_to_cpu(fw_log.actual_buff_size) - len; | |
90 | if (len) { | |
91 | wl12xx_copy_fwlog(wl, | |
92 | &buffer[WL18XX_LOGGER_BUFF_OFFSET], | |
93 | len); | |
94 | clear_addr = addr + WL18XX_LOGGER_BUFF_OFFSET + len + | |
95 | internal_fw_addrbase; | |
96 | } | |
97 | ||
98 | /* double check that clear address and write pointer are the same */ | |
99 | if (clear_addr != le32_to_cpu(fw_log.buff_write_ptr)) { | |
100 | wl1271_error("Calculate of clear addr Clear = %x, write = %x", | |
101 | clear_addr, le32_to_cpu(fw_log.buff_write_ptr)); | |
102 | } | |
103 | ||
104 | /* indicate FW about Clear buffer */ | |
105 | ret = wlcore_write32(wl, addr + WL18XX_LOGGER_READ_POINT_OFFSET, | |
106 | fw_log.buff_write_ptr); | |
107 | free_out: | |
108 | kfree(buffer); | |
109 | out: | |
110 | return le32_to_cpu(fw_log.actual_buff_size); | |
111 | } | |
112 | EXPORT_SYMBOL_GPL(wlcore_event_fw_logger); | |
f5fc0f86 | 113 | |
c50a2825 | 114 | void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr) |
00236aed | 115 | { |
c50a2825 EP |
116 | struct wl12xx_vif *wlvif; |
117 | struct ieee80211_vif *vif; | |
00236aed | 118 | enum nl80211_cqm_rssi_threshold_event event; |
c50a2825 | 119 | s8 metric = metric_arr[0]; |
00236aed JO |
120 | |
121 | wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric); | |
122 | ||
c50a2825 EP |
123 | /* TODO: check actual multi-role support */ |
124 | wl12xx_for_each_wlvif_sta(wl, wlvif) { | |
125 | if (metric <= wlvif->rssi_thold) | |
126 | event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; | |
127 | else | |
128 | event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; | |
129 | ||
130 | vif = wl12xx_wlvif_to_vif(wlvif); | |
131 | if (event != wlvif->last_rssi_event) | |
132 | ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL); | |
133 | wlvif->last_rssi_event = event; | |
134 | } | |
00236aed | 135 | } |
c50a2825 | 136 | EXPORT_SYMBOL_GPL(wlcore_event_rssi_trigger); |
00236aed | 137 | |
536129c8 | 138 | static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
70559a06 | 139 | { |
4b730b6a EP |
140 | struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); |
141 | ||
536129c8 | 142 | if (wlvif->bss_type != BSS_TYPE_AP_BSS) { |
9ae5d8d4 AN |
143 | u8 hlid = wlvif->sta.hlid; |
144 | if (!wl->links[hlid].ba_bitmap) | |
f4d3b6ab | 145 | return; |
9ae5d8d4 | 146 | ieee80211_stop_rx_ba_session(vif, wl->links[hlid].ba_bitmap, |
4b730b6a | 147 | vif->bss_conf.bssid); |
f4d3b6ab | 148 | } else { |
c7ffb902 | 149 | u8 hlid; |
f4d3b6ab | 150 | struct wl1271_link *lnk; |
c7ffb902 | 151 | for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, |
da08fdfa | 152 | wl->num_links) { |
c7ffb902 EP |
153 | lnk = &wl->links[hlid]; |
154 | if (!lnk->ba_bitmap) | |
f4d3b6ab AN |
155 | continue; |
156 | ||
4b730b6a | 157 | ieee80211_stop_rx_ba_session(vif, |
f4d3b6ab AN |
158 | lnk->ba_bitmap, |
159 | lnk->addr); | |
160 | } | |
161 | } | |
70559a06 SL |
162 | } |
163 | ||
c50a2825 | 164 | void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable) |
77ddaa10 | 165 | { |
4b730b6a EP |
166 | struct wl12xx_vif *wlvif; |
167 | ||
77ddaa10 | 168 | if (enable) { |
77ddaa10 EP |
169 | set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); |
170 | } else { | |
9eb599e9 | 171 | clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); |
4b730b6a | 172 | wl12xx_for_each_wlvif_sta(wl, wlvif) { |
9eb599e9 | 173 | wl1271_recalc_rx_streaming(wl, wlvif); |
4b730b6a | 174 | } |
77ddaa10 | 175 | } |
77ddaa10 | 176 | } |
c50a2825 | 177 | EXPORT_SYMBOL_GPL(wlcore_event_soft_gemini_sense); |
77ddaa10 | 178 | |
c50a2825 EP |
179 | void wlcore_event_sched_scan_completed(struct wl1271 *wl, |
180 | u8 status) | |
f5fc0f86 | 181 | { |
c50a2825 EP |
182 | wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)", |
183 | status); | |
f5fc0f86 | 184 | |
10199756 | 185 | if (wl->sched_vif) { |
c50a2825 | 186 | ieee80211_sched_scan_stopped(wl->hw); |
10199756 | 187 | wl->sched_vif = NULL; |
c50a2825 EP |
188 | } |
189 | } | |
190 | EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_completed); | |
f5fc0f86 | 191 | |
c50a2825 EP |
192 | void wlcore_event_ba_rx_constraint(struct wl1271 *wl, |
193 | unsigned long roles_bitmap, | |
194 | unsigned long allowed_bitmap) | |
195 | { | |
196 | struct wl12xx_vif *wlvif; | |
f5fc0f86 | 197 | |
c50a2825 EP |
198 | wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx allowed=0x%lx", |
199 | __func__, roles_bitmap, allowed_bitmap); | |
34dd2aaa | 200 | |
c50a2825 EP |
201 | wl12xx_for_each_wlvif(wl, wlvif) { |
202 | if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || | |
203 | !test_bit(wlvif->role_id , &roles_bitmap)) | |
204 | continue; | |
f5fc0f86 | 205 | |
c50a2825 EP |
206 | wlvif->ba_allowed = !!test_bit(wlvif->role_id, |
207 | &allowed_bitmap); | |
208 | if (!wlvif->ba_allowed) | |
209 | wl1271_stop_ba_event(wl, wlvif); | |
6394c01b | 210 | } |
c50a2825 EP |
211 | } |
212 | EXPORT_SYMBOL_GPL(wlcore_event_ba_rx_constraint); | |
6394c01b | 213 | |
c50a2825 EP |
214 | void wlcore_event_channel_switch(struct wl1271 *wl, |
215 | unsigned long roles_bitmap, | |
216 | bool success) | |
217 | { | |
218 | struct wl12xx_vif *wlvif; | |
219 | struct ieee80211_vif *vif; | |
6394c01b | 220 | |
c50a2825 EP |
221 | wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d", |
222 | __func__, roles_bitmap, success); | |
8d2ef7bd | 223 | |
534719f4 | 224 | wl12xx_for_each_wlvif(wl, wlvif) { |
c50a2825 EP |
225 | if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || |
226 | !test_bit(wlvif->role_id , &roles_bitmap)) | |
227 | continue; | |
6b8bf5bc | 228 | |
c50a2825 EP |
229 | if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, |
230 | &wlvif->flags)) | |
231 | continue; | |
9f5b424d | 232 | |
c50a2825 | 233 | vif = wl12xx_wlvif_to_vif(wlvif); |
9f5b424d | 234 | |
534719f4 EP |
235 | if (wlvif->bss_type == BSS_TYPE_STA_BSS) { |
236 | ieee80211_chswitch_done(vif, success); | |
237 | cancel_delayed_work(&wlvif->channel_switch_work); | |
238 | } else { | |
830513ab | 239 | set_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags); |
534719f4 EP |
240 | ieee80211_csa_finish(vif); |
241 | } | |
5f561f68 | 242 | } |
c50a2825 EP |
243 | } |
244 | EXPORT_SYMBOL_GPL(wlcore_event_channel_switch); | |
f5fc0f86 | 245 | |
c50a2825 EP |
246 | void wlcore_event_dummy_packet(struct wl1271 *wl) |
247 | { | |
69aa1675 LC |
248 | if (wl->plt) { |
249 | wl1271_info("Got DUMMY_PACKET event in PLT mode. FW bug, ignoring."); | |
250 | return; | |
251 | } | |
252 | ||
c50a2825 EP |
253 | wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); |
254 | wl1271_tx_dummy_packet(wl); | |
255 | } | |
256 | EXPORT_SYMBOL_GPL(wlcore_event_dummy_packet); | |
19ad0715 | 257 | |
c50a2825 EP |
258 | static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap) |
259 | { | |
260 | u32 num_packets = wl->conf.tx.max_tx_retries; | |
261 | struct wl12xx_vif *wlvif; | |
262 | struct ieee80211_vif *vif; | |
263 | struct ieee80211_sta *sta; | |
264 | const u8 *addr; | |
265 | int h; | |
266 | ||
da08fdfa | 267 | for_each_set_bit(h, &sta_bitmap, wl->num_links) { |
c50a2825 EP |
268 | bool found = false; |
269 | /* find the ap vif connected to this sta */ | |
270 | wl12xx_for_each_wlvif_ap(wl, wlvif) { | |
271 | if (!test_bit(h, wlvif->ap.sta_hlid_map)) | |
272 | continue; | |
273 | found = true; | |
274 | break; | |
4b730b6a | 275 | } |
c50a2825 EP |
276 | if (!found) |
277 | continue; | |
70559a06 | 278 | |
c50a2825 EP |
279 | vif = wl12xx_wlvif_to_vif(wlvif); |
280 | addr = wl->links[h].addr; | |
f4d3b6ab | 281 | |
c50a2825 EP |
282 | rcu_read_lock(); |
283 | sta = ieee80211_find_sta(vif, addr); | |
284 | if (sta) { | |
285 | wl1271_debug(DEBUG_EVENT, "remove sta %d", h); | |
286 | ieee80211_report_low_ack(sta, num_packets); | |
4b730b6a | 287 | } |
c50a2825 | 288 | rcu_read_unlock(); |
70559a06 | 289 | } |
c50a2825 | 290 | } |
70559a06 | 291 | |
c50a2825 EP |
292 | void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap) |
293 | { | |
294 | wl1271_debug(DEBUG_EVENT, "MAX_TX_FAILURE_EVENT_ID"); | |
295 | wlcore_disconnect_sta(wl, sta_bitmap); | |
296 | } | |
297 | EXPORT_SYMBOL_GPL(wlcore_event_max_tx_failure); | |
775e1a4b | 298 | |
c50a2825 EP |
299 | void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap) |
300 | { | |
301 | wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID"); | |
302 | wlcore_disconnect_sta(wl, sta_bitmap); | |
303 | } | |
304 | EXPORT_SYMBOL_GPL(wlcore_event_inactive_sta); | |
6d158ff3 | 305 | |
c50a2825 EP |
306 | void wlcore_event_roc_complete(struct wl1271 *wl) |
307 | { | |
308 | wl1271_debug(DEBUG_EVENT, "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID"); | |
309 | if (wl->roc_vif) | |
310 | ieee80211_ready_on_channel(wl->hw); | |
311 | } | |
312 | EXPORT_SYMBOL_GPL(wlcore_event_roc_complete); | |
ae47c45f | 313 | |
c50a2825 EP |
314 | void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap) |
315 | { | |
3618f30f | 316 | /* |
c50a2825 EP |
317 | * We are HW_MONITOR device. On beacon loss - queue |
318 | * connection loss work. Cancel it on REGAINED event. | |
3618f30f | 319 | */ |
c50a2825 EP |
320 | struct wl12xx_vif *wlvif; |
321 | struct ieee80211_vif *vif; | |
322 | int delay = wl->conf.conn.synch_fail_thold * | |
323 | wl->conf.conn.bss_lose_timeout; | |
3618f30f | 324 | |
c50a2825 | 325 | wl1271_info("Beacon loss detected. roles:0x%lx", roles_bitmap); |
dabf37db | 326 | |
c50a2825 EP |
327 | wl12xx_for_each_wlvif_sta(wl, wlvif) { |
328 | if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || | |
329 | !test_bit(wlvif->role_id , &roles_bitmap)) | |
330 | continue; | |
3618f30f | 331 | |
c0ad2f2e ES |
332 | vif = wl12xx_wlvif_to_vif(wlvif); |
333 | ||
334 | /* don't attempt roaming in case of p2p */ | |
335 | if (wlvif->p2p) { | |
336 | ieee80211_connection_loss(vif); | |
337 | continue; | |
338 | } | |
339 | ||
c50a2825 EP |
340 | /* |
341 | * if the work is already queued, it should take place. | |
342 | * We don't want to delay the connection loss | |
343 | * indication any more. | |
344 | */ | |
345 | ieee80211_queue_delayed_work(wl->hw, | |
346 | &wlvif->connection_loss_work, | |
347 | msecs_to_jiffies(delay)); | |
3618f30f | 348 | |
98f03342 | 349 | ieee80211_cqm_beacon_loss_notify(vif, GFP_KERNEL); |
3618f30f | 350 | } |
f5fc0f86 | 351 | } |
c50a2825 | 352 | EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss); |
f5fc0f86 LC |
353 | |
354 | int wl1271_event_unmask(struct wl1271 *wl) | |
355 | { | |
356 | int ret; | |
357 | ||
71e996be | 358 | wl1271_debug(DEBUG_EVENT, "unmasking event_mask 0x%x", wl->event_mask); |
f5fc0f86 LC |
359 | ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask)); |
360 | if (ret < 0) | |
361 | return ret; | |
362 | ||
363 | return 0; | |
364 | } | |
365 | ||
13f2dc52 | 366 | int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) |
f5fc0f86 | 367 | { |
f5fc0f86 LC |
368 | int ret; |
369 | ||
370 | wl1271_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num); | |
371 | ||
372 | if (mbox_num > 1) | |
373 | return -EINVAL; | |
374 | ||
375 | /* first we read the mbox descriptor */ | |
045b9b5f | 376 | ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox, |
c50a2825 | 377 | wl->mbox_size, false); |
045b9b5f IY |
378 | if (ret < 0) |
379 | return ret; | |
f5fc0f86 LC |
380 | |
381 | /* process the descriptor */ | |
c50a2825 | 382 | ret = wl->ops->process_mailbox_events(wl); |
f5fc0f86 LC |
383 | if (ret < 0) |
384 | return ret; | |
385 | ||
f16ff758 LC |
386 | /* |
387 | * TODO: we just need this because one bit is in a different | |
388 | * place. Is there any better way? | |
389 | */ | |
b0f0ad39 | 390 | ret = wl->ops->ack_event(wl); |
f5fc0f86 | 391 | |
b0f0ad39 | 392 | return ret; |
f5fc0f86 | 393 | } |