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