Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  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>
  8 */
  9
 10#include "wlcore.h"
 11#include "debug.h"
 12#include "io.h"
 13#include "event.h"
 14#include "ps.h"
 15#include "scan.h"
 16#include "wl12xx_80211.h"
 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
 25int wlcore_event_fw_logger(struct wl1271 *wl)
 26{
 27	int ret;
 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);
 93free_out:
 94	kfree(buffer);
 95out:
 96	return le32_to_cpu(fw_log.actual_buff_size);
 97}
 98EXPORT_SYMBOL_GPL(wlcore_event_fw_logger);
 99
100void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr)
101{
102	struct wl12xx_vif *wlvif;
103	struct ieee80211_vif *vif;
104	enum nl80211_cqm_rssi_threshold_event event;
105	s8 metric = metric_arr[0];
106
107	wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric);
108
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)
118			ieee80211_cqm_rssi_notify(vif, event, metric,
119						  GFP_KERNEL);
120		wlvif->last_rssi_event = event;
121	}
122}
123EXPORT_SYMBOL_GPL(wlcore_event_rssi_trigger);
124
125static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
126{
127	struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
128
129	if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
130		u8 hlid = wlvif->sta.hlid;
131		if (!wl->links[hlid].ba_bitmap)
132			return;
133		ieee80211_stop_rx_ba_session(vif, wl->links[hlid].ba_bitmap,
134					     vif->bss_conf.bssid);
135	} else {
136		u8 hlid;
137		struct wl1271_link *lnk;
138		for_each_set_bit(hlid, wlvif->ap.sta_hlid_map,
139				 wl->num_links) {
140			lnk = &wl->links[hlid];
141			if (!lnk->ba_bitmap)
142				continue;
143
144			ieee80211_stop_rx_ba_session(vif,
145						     lnk->ba_bitmap,
146						     lnk->addr);
147		}
148	}
149}
150
151void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable)
152{
153	struct wl12xx_vif *wlvif;
154
155	if (enable) {
156		set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
157	} else {
158		clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
159		wl12xx_for_each_wlvif_sta(wl, wlvif) {
160			wl1271_recalc_rx_streaming(wl, wlvif);
161		}
162	}
163}
164EXPORT_SYMBOL_GPL(wlcore_event_soft_gemini_sense);
165
166void wlcore_event_sched_scan_completed(struct wl1271 *wl,
167				       u8 status)
168{
169	wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)",
170		     status);
171
172	if (wl->sched_vif) {
173		ieee80211_sched_scan_stopped(wl->hw);
174		wl->sched_vif = NULL;
175	}
176}
177EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_completed);
178
179void wlcore_event_ba_rx_constraint(struct wl1271 *wl,
180				   unsigned long roles_bitmap,
181				   unsigned long allowed_bitmap)
182{
183	struct wl12xx_vif *wlvif;
184
185	wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx allowed=0x%lx",
186		     __func__, roles_bitmap, allowed_bitmap);
187
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;
192
193		wlvif->ba_allowed = !!test_bit(wlvif->role_id,
194					       &allowed_bitmap);
195		if (!wlvif->ba_allowed)
196			wl1271_stop_ba_event(wl, wlvif);
197	}
198}
199EXPORT_SYMBOL_GPL(wlcore_event_ba_rx_constraint);
200
201void 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;
207
208	wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d",
209		     __func__, roles_bitmap, success);
210
211	wl12xx_for_each_wlvif(wl, wlvif) {
212		if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
213		    !test_bit(wlvif->role_id , &roles_bitmap))
214			continue;
215
216		if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
217					&wlvif->flags))
218			continue;
219
220		vif = wl12xx_wlvif_to_vif(wlvif);
221
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 {
226			set_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags);
227			ieee80211_csa_finish(vif);
228		}
229	}
230}
231EXPORT_SYMBOL_GPL(wlcore_event_channel_switch);
232
233void wlcore_event_dummy_packet(struct wl1271 *wl)
234{
235	if (wl->plt) {
236		wl1271_info("Got DUMMY_PACKET event in PLT mode.  FW bug, ignoring.");
237		return;
238	}
239
240	wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
241	wl1271_tx_dummy_packet(wl);
242}
243EXPORT_SYMBOL_GPL(wlcore_event_dummy_packet);
244
245static 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
254	for_each_set_bit(h, &sta_bitmap, wl->num_links) {
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;
262		}
263		if (!found)
264			continue;
265
266		vif = wl12xx_wlvif_to_vif(wlvif);
267		addr = wl->links[h].addr;
268
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);
274		}
275		rcu_read_unlock();
276	}
277}
278
279void 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}
284EXPORT_SYMBOL_GPL(wlcore_event_max_tx_failure);
285
286void 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}
291EXPORT_SYMBOL_GPL(wlcore_event_inactive_sta);
292
293void 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}
299EXPORT_SYMBOL_GPL(wlcore_event_roc_complete);
300
301void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap)
302{
303	/*
304	 * We are HW_MONITOR device. On beacon loss - queue
305	 * connection loss work. Cancel it on REGAINED event.
306	 */
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;
311
312	wl1271_info("Beacon loss detected. roles:0x%lx", roles_bitmap);
313
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;
318
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
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));
335
336		ieee80211_cqm_beacon_loss_notify(vif, GFP_KERNEL);
337	}
338}
339EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss);
340
341int wl1271_event_unmask(struct wl1271 *wl)
342{
343	int ret;
344
345	wl1271_debug(DEBUG_EVENT, "unmasking event_mask 0x%x", wl->event_mask);
346	ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask));
347	if (ret < 0)
348		return ret;
349
350	return 0;
351}
352
353int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
354{
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 */
363	ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox,
364			  wl->mbox_size, false);
365	if (ret < 0)
366		return ret;
367
368	/* process the descriptor */
369	ret = wl->ops->process_mailbox_events(wl);
370	if (ret < 0)
371		return ret;
372
373	/*
374	 * TODO: we just need this because one bit is in a different
375	 * place.  Is there any better way?
376	 */
377	ret = wl->ops->ack_event(wl);
378
379	return ret;
380}