xref: /linux/drivers/net/wireless/silabs/wfx/hif_rx.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Handling of the chip-to-host events (aka indications) of the hardware API.
4  *
5  * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
6  * Copyright (c) 2010, ST-Ericsson
7  */
8 #include <linux/skbuff.h>
9 #include <linux/etherdevice.h>
10 
11 #include "hif_rx.h"
12 #include "wfx.h"
13 #include "scan.h"
14 #include "bh.h"
15 #include "sta.h"
16 #include "data_rx.h"
17 #include "hif_api_cmd.h"
18 
wfx_hif_generic_confirm(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf)19 static int wfx_hif_generic_confirm(struct wfx_dev *wdev,
20 				   const struct wfx_hif_msg *hif, const void *buf)
21 {
22 	/* All confirm messages start with status */
23 	int status = le32_to_cpup((__le32 *)buf);
24 	int cmd = hif->id;
25 	int len = le16_to_cpu(hif->len) - 4; /* drop header */
26 
27 	WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error");
28 
29 	if (!wdev->hif_cmd.buf_send) {
30 		dev_warn(wdev->dev, "unexpected confirmation: 0x%.2x\n", cmd);
31 		return -EINVAL;
32 	}
33 
34 	if (cmd != wdev->hif_cmd.buf_send->id) {
35 		dev_warn(wdev->dev, "chip response mismatch request: 0x%.2x vs 0x%.2x\n",
36 			 cmd, wdev->hif_cmd.buf_send->id);
37 		return -EINVAL;
38 	}
39 
40 	if (wdev->hif_cmd.buf_recv) {
41 		if (wdev->hif_cmd.len_recv >= len && len > 0)
42 			memcpy(wdev->hif_cmd.buf_recv, buf, len);
43 		else
44 			status = -EIO;
45 	}
46 	wdev->hif_cmd.ret = status;
47 
48 	complete(&wdev->hif_cmd.done);
49 	return status;
50 }
51 
wfx_hif_tx_confirm(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf)52 static int wfx_hif_tx_confirm(struct wfx_dev *wdev,
53 			      const struct wfx_hif_msg *hif, const void *buf)
54 {
55 	const struct wfx_hif_cnf_tx *body = buf;
56 
57 	wfx_tx_confirm_cb(wdev, body);
58 	return 0;
59 }
60 
wfx_hif_multi_tx_confirm(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf)61 static int wfx_hif_multi_tx_confirm(struct wfx_dev *wdev,
62 				    const struct wfx_hif_msg *hif, const void *buf)
63 {
64 	const struct wfx_hif_cnf_multi_transmit *body = buf;
65 	int i;
66 
67 	WARN(body->num_tx_confs <= 0, "corrupted message");
68 	for (i = 0; i < body->num_tx_confs; i++)
69 		wfx_tx_confirm_cb(wdev, &body->tx_conf_payload[i]);
70 	return 0;
71 }
72 
wfx_hif_startup_indication(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf)73 static int wfx_hif_startup_indication(struct wfx_dev *wdev,
74 				      const struct wfx_hif_msg *hif, const void *buf)
75 {
76 	const struct wfx_hif_ind_startup *body = buf;
77 
78 	if (body->status || body->firmware_type > 4) {
79 		dev_err(wdev->dev, "received invalid startup indication");
80 		return -EINVAL;
81 	}
82 	memcpy(&wdev->hw_caps, body, sizeof(struct wfx_hif_ind_startup));
83 	complete(&wdev->firmware_ready);
84 	return 0;
85 }
86 
wfx_hif_wakeup_indication(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf)87 static int wfx_hif_wakeup_indication(struct wfx_dev *wdev,
88 				     const struct wfx_hif_msg *hif, const void *buf)
89 {
90 	if (!wdev->pdata.gpio_wakeup || gpiod_get_value(wdev->pdata.gpio_wakeup) == 0) {
91 		dev_warn(wdev->dev, "unexpected wake-up indication\n");
92 		return -EIO;
93 	}
94 	return 0;
95 }
96 
wfx_hif_receive_indication(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf,struct sk_buff * skb)97 static int wfx_hif_receive_indication(struct wfx_dev *wdev, const struct wfx_hif_msg *hif,
98 				      const void *buf, struct sk_buff *skb)
99 {
100 	struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
101 	const struct wfx_hif_ind_rx *body = buf;
102 
103 	if (!wvif) {
104 		dev_warn(wdev->dev, "%s: received event for non-existent vif\n", __func__);
105 		return -EIO;
106 	}
107 	skb_pull(skb, sizeof(struct wfx_hif_msg) + sizeof(struct wfx_hif_ind_rx));
108 	wfx_rx_cb(wvif, body, skb);
109 
110 	return 0;
111 }
112 
wfx_hif_event_indication(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf)113 static int wfx_hif_event_indication(struct wfx_dev *wdev,
114 				    const struct wfx_hif_msg *hif, const void *buf)
115 {
116 	struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
117 	const struct wfx_hif_ind_event *body = buf;
118 	int type = le32_to_cpu(body->event_id);
119 
120 	if (!wvif) {
121 		dev_warn(wdev->dev, "%s: received event for non-existent vif\n", __func__);
122 		return -EIO;
123 	}
124 
125 	switch (type) {
126 	case HIF_EVENT_IND_RCPI_RSSI:
127 		wfx_event_report_rssi(wvif, body->event_data.rcpi_rssi);
128 		break;
129 	case HIF_EVENT_IND_BSSLOST:
130 		schedule_delayed_work(&wvif->beacon_loss_work, 0);
131 		break;
132 	case HIF_EVENT_IND_BSSREGAINED:
133 		cancel_delayed_work(&wvif->beacon_loss_work);
134 		dev_dbg(wdev->dev, "ignore BSSREGAINED indication\n");
135 		break;
136 	case HIF_EVENT_IND_PS_MODE_ERROR:
137 		dev_warn(wdev->dev, "error while processing power save request: %d\n",
138 			 le32_to_cpu(body->event_data.ps_mode_error));
139 		break;
140 	default:
141 		dev_warn(wdev->dev, "unhandled event indication: %.2x\n", type);
142 		break;
143 	}
144 	return 0;
145 }
146 
wfx_hif_pm_mode_complete_indication(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf)147 static int wfx_hif_pm_mode_complete_indication(struct wfx_dev *wdev,
148 					       const struct wfx_hif_msg *hif, const void *buf)
149 {
150 	struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
151 
152 	if (!wvif) {
153 		dev_warn(wdev->dev, "%s: received event for non-existent vif\n", __func__);
154 		return -EIO;
155 	}
156 	complete(&wvif->set_pm_mode_complete);
157 
158 	return 0;
159 }
160 
wfx_hif_scan_complete_indication(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf)161 static int wfx_hif_scan_complete_indication(struct wfx_dev *wdev,
162 					    const struct wfx_hif_msg *hif, const void *buf)
163 {
164 	struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
165 	const struct wfx_hif_ind_scan_cmpl *body = buf;
166 
167 	if (!wvif) {
168 		dev_warn(wdev->dev, "%s: received event for non-existent vif\n", __func__);
169 		return -EIO;
170 	}
171 
172 	wfx_scan_complete(wvif, body->num_channels_completed);
173 
174 	return 0;
175 }
176 
wfx_hif_join_complete_indication(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf)177 static int wfx_hif_join_complete_indication(struct wfx_dev *wdev,
178 					    const struct wfx_hif_msg *hif, const void *buf)
179 {
180 	struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
181 
182 	if (!wvif) {
183 		dev_warn(wdev->dev, "%s: received event for non-existent vif\n", __func__);
184 		return -EIO;
185 	}
186 	dev_warn(wdev->dev, "unattended JoinCompleteInd\n");
187 
188 	return 0;
189 }
190 
wfx_hif_suspend_resume_indication(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf)191 static int wfx_hif_suspend_resume_indication(struct wfx_dev *wdev,
192 					     const struct wfx_hif_msg *hif, const void *buf)
193 {
194 	const struct wfx_hif_ind_suspend_resume_tx *body = buf;
195 	struct wfx_vif *wvif;
196 
197 	if (body->bc_mc_only) {
198 		wvif = wdev_to_wvif(wdev, hif->interface);
199 		if (!wvif) {
200 			dev_warn(wdev->dev, "%s: received event for non-existent vif\n", __func__);
201 			return -EIO;
202 		}
203 		if (body->resume)
204 			wfx_suspend_resume_mc(wvif, STA_NOTIFY_AWAKE);
205 		else
206 			wfx_suspend_resume_mc(wvif, STA_NOTIFY_SLEEP);
207 	} else {
208 		WARN(body->peer_sta_set, "misunderstood indication");
209 		WARN(hif->interface != 2, "misunderstood indication");
210 		if (body->resume)
211 			wfx_suspend_hot_dev(wdev, STA_NOTIFY_AWAKE);
212 		else
213 			wfx_suspend_hot_dev(wdev, STA_NOTIFY_SLEEP);
214 	}
215 
216 	return 0;
217 }
218 
wfx_hif_generic_indication(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf)219 static int wfx_hif_generic_indication(struct wfx_dev *wdev,
220 				      const struct wfx_hif_msg *hif, const void *buf)
221 {
222 	const struct wfx_hif_ind_generic *body = buf;
223 	int type = le32_to_cpu(body->type);
224 
225 	switch (type) {
226 	case HIF_GENERIC_INDICATION_TYPE_RAW:
227 		return 0;
228 	case HIF_GENERIC_INDICATION_TYPE_STRING:
229 		dev_info(wdev->dev, "firmware says: %s\n", (char *)&body->data);
230 		return 0;
231 	case HIF_GENERIC_INDICATION_TYPE_RX_STATS:
232 		mutex_lock(&wdev->rx_stats_lock);
233 		/* Older firmware send a generic indication beside RxStats */
234 		if (!wfx_api_older_than(wdev, 1, 4))
235 			dev_info(wdev->dev, "Rx test ongoing. Temperature: %d degrees C\n",
236 				 body->data.rx_stats.current_temp);
237 		memcpy(&wdev->rx_stats, &body->data.rx_stats, sizeof(wdev->rx_stats));
238 		mutex_unlock(&wdev->rx_stats_lock);
239 		return 0;
240 	case HIF_GENERIC_INDICATION_TYPE_TX_POWER_LOOP_INFO:
241 		mutex_lock(&wdev->tx_power_loop_info_lock);
242 		memcpy(&wdev->tx_power_loop_info, &body->data.tx_power_loop_info,
243 		       sizeof(wdev->tx_power_loop_info));
244 		mutex_unlock(&wdev->tx_power_loop_info_lock);
245 		return 0;
246 	default:
247 		dev_err(wdev->dev, "generic_indication: unknown indication type: %#.8x\n", type);
248 		return -EIO;
249 	}
250 }
251 
252 static const struct {
253 	int val;
254 	const char *str;
255 	bool has_param;
256 } hif_errors[] = {
257 	{ HIF_ERROR_FIRMWARE_ROLLBACK,
258 		"rollback status" },
259 	{ HIF_ERROR_FIRMWARE_DEBUG_ENABLED,
260 		"debug feature enabled" },
261 	{ HIF_ERROR_PDS_PAYLOAD,
262 		"PDS version is not supported" },
263 	{ HIF_ERROR_PDS_TESTFEATURE,
264 		"PDS ask for an unknown test mode" },
265 	{ HIF_ERROR_OOR_VOLTAGE,
266 		"out-of-range power supply voltage", true },
267 	{ HIF_ERROR_OOR_TEMPERATURE,
268 		"out-of-range temperature", true },
269 	{ HIF_ERROR_SLK_REQ_DURING_KEY_EXCHANGE,
270 		"secure link does not expect request during key exchange" },
271 	{ HIF_ERROR_SLK_SESSION_KEY,
272 		"secure link session key is invalid" },
273 	{ HIF_ERROR_SLK_OVERFLOW,
274 		"secure link overflow" },
275 	{ HIF_ERROR_SLK_WRONG_ENCRYPTION_STATE,
276 		"secure link messages list does not match message encryption" },
277 	{ HIF_ERROR_SLK_UNCONFIGURED,
278 		"secure link not yet configured" },
279 	{ HIF_ERROR_HIF_BUS_FREQUENCY_TOO_LOW,
280 		"bus clock is too slow (<1kHz)" },
281 	{ HIF_ERROR_HIF_RX_DATA_TOO_LARGE,
282 		"HIF message too large" },
283 	/* Following errors only exists in old firmware versions: */
284 	{ HIF_ERROR_HIF_TX_QUEUE_FULL,
285 		"HIF messages queue is full" },
286 	{ HIF_ERROR_HIF_BUS,
287 		"HIF bus" },
288 	{ HIF_ERROR_SLK_MULTI_TX_UNSUPPORTED,
289 		"secure link does not support multi-tx confirmations" },
290 	{ HIF_ERROR_SLK_OUTDATED_SESSION_KEY,
291 		"secure link session key is outdated" },
292 	{ HIF_ERROR_SLK_DECRYPTION,
293 		"secure link params (nonce or tag) mismatch" },
294 };
295 
wfx_hif_error_indication(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf)296 static int wfx_hif_error_indication(struct wfx_dev *wdev,
297 				    const struct wfx_hif_msg *hif, const void *buf)
298 {
299 	const struct wfx_hif_ind_error *body = buf;
300 	int type = le32_to_cpu(body->type);
301 	int param = (s8)body->data[0];
302 	int i;
303 
304 	for (i = 0; i < ARRAY_SIZE(hif_errors); i++)
305 		if (type == hif_errors[i].val)
306 			break;
307 	if (i < ARRAY_SIZE(hif_errors))
308 		if (hif_errors[i].has_param)
309 			dev_err(wdev->dev, "asynchronous error: %s: %d\n",
310 				hif_errors[i].str, param);
311 		else
312 			dev_err(wdev->dev, "asynchronous error: %s\n", hif_errors[i].str);
313 	else
314 		dev_err(wdev->dev, "asynchronous error: unknown: %08x\n", type);
315 	print_hex_dump(KERN_INFO, "hif: ", DUMP_PREFIX_OFFSET,
316 		       16, 1, hif, le16_to_cpu(hif->len), false);
317 	wdev->chip_frozen = true;
318 
319 	return 0;
320 };
321 
wfx_hif_exception_indication(struct wfx_dev * wdev,const struct wfx_hif_msg * hif,const void * buf)322 static int wfx_hif_exception_indication(struct wfx_dev *wdev,
323 					const struct wfx_hif_msg *hif, const void *buf)
324 {
325 	const struct wfx_hif_ind_exception *body = buf;
326 	int type = le32_to_cpu(body->type);
327 
328 	if (type == 4)
329 		dev_err(wdev->dev, "firmware assert %d\n", le32_to_cpup((__le32 *)body->data));
330 	else
331 		dev_err(wdev->dev, "firmware exception\n");
332 	print_hex_dump(KERN_INFO, "hif: ", DUMP_PREFIX_OFFSET,
333 		       16, 1, hif, le16_to_cpu(hif->len), false);
334 	wdev->chip_frozen = true;
335 
336 	return -1;
337 }
338 
339 static const struct {
340 	int msg_id;
341 	int (*handler)(struct wfx_dev *wdev, const struct wfx_hif_msg *hif, const void *buf);
342 } hif_handlers[] = {
343 	/* Confirmations */
344 	{ HIF_CNF_ID_TX,                wfx_hif_tx_confirm },
345 	{ HIF_CNF_ID_MULTI_TRANSMIT,    wfx_hif_multi_tx_confirm },
346 	/* Indications */
347 	{ HIF_IND_ID_STARTUP,           wfx_hif_startup_indication },
348 	{ HIF_IND_ID_WAKEUP,            wfx_hif_wakeup_indication },
349 	{ HIF_IND_ID_JOIN_COMPLETE,     wfx_hif_join_complete_indication },
350 	{ HIF_IND_ID_SET_PM_MODE_CMPL,  wfx_hif_pm_mode_complete_indication },
351 	{ HIF_IND_ID_SCAN_CMPL,         wfx_hif_scan_complete_indication },
352 	{ HIF_IND_ID_SUSPEND_RESUME_TX, wfx_hif_suspend_resume_indication },
353 	{ HIF_IND_ID_EVENT,             wfx_hif_event_indication },
354 	{ HIF_IND_ID_GENERIC,           wfx_hif_generic_indication },
355 	{ HIF_IND_ID_ERROR,             wfx_hif_error_indication },
356 	{ HIF_IND_ID_EXCEPTION,         wfx_hif_exception_indication },
357 	/* FIXME: allocate skb_p from wfx_hif_receive_indication and make it generic */
358 	//{ HIF_IND_ID_RX,              wfx_hif_receive_indication },
359 };
360 
wfx_handle_rx(struct wfx_dev * wdev,struct sk_buff * skb)361 void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb)
362 {
363 	int i;
364 	const struct wfx_hif_msg *hif = (const struct wfx_hif_msg *)skb->data;
365 	int hif_id = hif->id;
366 
367 	if (hif_id == HIF_IND_ID_RX) {
368 		/* wfx_hif_receive_indication take care of skb lifetime */
369 		wfx_hif_receive_indication(wdev, hif, hif->body, skb);
370 		return;
371 	}
372 	/* Note: mutex_is_lock cause an implicit memory barrier that protect buf_send */
373 	if (mutex_is_locked(&wdev->hif_cmd.lock) &&
374 	    wdev->hif_cmd.buf_send && wdev->hif_cmd.buf_send->id == hif_id) {
375 		wfx_hif_generic_confirm(wdev, hif, hif->body);
376 		goto free;
377 	}
378 	for (i = 0; i < ARRAY_SIZE(hif_handlers); i++) {
379 		if (hif_handlers[i].msg_id == hif_id) {
380 			if (hif_handlers[i].handler)
381 				hif_handlers[i].handler(wdev, hif, hif->body);
382 			goto free;
383 		}
384 	}
385 	if (hif_id & HIF_ID_IS_INDICATION)
386 		dev_err(wdev->dev, "unsupported HIF indication: ID %02x\n", hif_id);
387 	else
388 		dev_err(wdev->dev, "unexpected HIF confirmation: ID %02x\n", hif_id);
389 free:
390 	dev_kfree_skb(skb);
391 }
392