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