1
2
3
4
5
6
7
8
9#include <linux/skbuff.h>
10#include <linux/etherdevice.h>
11
12#include "hif_rx.h"
13#include "wfx.h"
14#include "scan.h"
15#include "bh.h"
16#include "sta.h"
17#include "data_rx.h"
18#include "secure_link.h"
19#include "hif_api_cmd.h"
20
21static int hif_generic_confirm(struct wfx_dev *wdev,
22 const struct hif_msg *hif, const void *buf)
23{
24
25 int status = le32_to_cpup((__le32 *)buf);
26 int cmd = hif->id;
27 int len = le16_to_cpu(hif->len) - 4;
28
29 WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error");
30
31 if (!wdev->hif_cmd.buf_send) {
32 dev_warn(wdev->dev, "unexpected confirmation: 0x%.2x\n", cmd);
33 return -EINVAL;
34 }
35
36 if (cmd != wdev->hif_cmd.buf_send->id) {
37 dev_warn(wdev->dev,
38 "chip response mismatch request: 0x%.2x vs 0x%.2x\n",
39 cmd, wdev->hif_cmd.buf_send->id);
40 return -EINVAL;
41 }
42
43 if (wdev->hif_cmd.buf_recv) {
44 if (wdev->hif_cmd.len_recv >= len)
45 memcpy(wdev->hif_cmd.buf_recv, buf, len);
46 else
47 status = -ENOMEM;
48 }
49 wdev->hif_cmd.ret = status;
50
51 if (!wdev->hif_cmd.async) {
52 complete(&wdev->hif_cmd.done);
53 } else {
54 wdev->hif_cmd.buf_send = NULL;
55 mutex_unlock(&wdev->hif_cmd.lock);
56 if (cmd != HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS)
57 mutex_unlock(&wdev->hif_cmd.key_renew_lock);
58 }
59 return status;
60}
61
62static int hif_tx_confirm(struct wfx_dev *wdev,
63 const struct hif_msg *hif, const void *buf)
64{
65 const struct hif_cnf_tx *body = buf;
66
67 wfx_tx_confirm_cb(wdev, body);
68 return 0;
69}
70
71static int hif_multi_tx_confirm(struct wfx_dev *wdev,
72 const struct hif_msg *hif, const void *buf)
73{
74 const struct hif_cnf_multi_transmit *body = buf;
75 int i;
76
77 WARN(body->num_tx_confs <= 0, "corrupted message");
78 for (i = 0; i < body->num_tx_confs; i++)
79 wfx_tx_confirm_cb(wdev, &body->tx_conf_payload[i]);
80 return 0;
81}
82
83static int hif_startup_indication(struct wfx_dev *wdev,
84 const struct hif_msg *hif, const void *buf)
85{
86 const struct hif_ind_startup *body = buf;
87
88 if (body->status || body->firmware_type > 4) {
89 dev_err(wdev->dev, "received invalid startup indication");
90 return -EINVAL;
91 }
92 memcpy(&wdev->hw_caps, body, sizeof(struct hif_ind_startup));
93 le16_to_cpus((__le16 *)&wdev->hw_caps.hardware_id);
94 le16_to_cpus((__le16 *)&wdev->hw_caps.num_inp_ch_bufs);
95 le16_to_cpus((__le16 *)&wdev->hw_caps.size_inp_ch_buf);
96 le32_to_cpus((__le32 *)&wdev->hw_caps.supported_rate_mask);
97
98 complete(&wdev->firmware_ready);
99 return 0;
100}
101
102static int hif_wakeup_indication(struct wfx_dev *wdev,
103 const struct hif_msg *hif, const void *buf)
104{
105 if (!wdev->pdata.gpio_wakeup
106 || !gpiod_get_value(wdev->pdata.gpio_wakeup)) {
107 dev_warn(wdev->dev, "unexpected wake-up indication\n");
108 return -EIO;
109 }
110 return 0;
111}
112
113static int hif_keys_indication(struct wfx_dev *wdev,
114 const struct hif_msg *hif, const void *buf)
115{
116 const struct hif_ind_sl_exchange_pub_keys *body = buf;
117 u8 pubkey[API_NCP_PUB_KEY_SIZE];
118
119
120 if (body->status && body->status != HIF_STATUS_SLK_NEGO_SUCCESS)
121 dev_warn(wdev->dev, "secure link negociation error\n");
122 memcpy(pubkey, body->ncp_pub_key, sizeof(pubkey));
123 memreverse(pubkey, sizeof(pubkey));
124 wfx_sl_check_pubkey(wdev, pubkey, body->ncp_pub_key_mac);
125 return 0;
126}
127
128static int hif_receive_indication(struct wfx_dev *wdev,
129 const struct hif_msg *hif,
130 const void *buf, struct sk_buff *skb)
131{
132 struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
133 const struct hif_ind_rx *body = buf;
134
135 if (!wvif) {
136 dev_warn(wdev->dev, "ignore rx data for non-existent vif %d\n",
137 hif->interface);
138 return 0;
139 }
140 skb_pull(skb, sizeof(struct hif_msg) + sizeof(struct hif_ind_rx));
141 wfx_rx_cb(wvif, body, skb);
142
143 return 0;
144}
145
146static int hif_event_indication(struct wfx_dev *wdev,
147 const struct hif_msg *hif, const void *buf)
148{
149 struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
150 const struct hif_ind_event *body = buf;
151 int type = le32_to_cpu(body->event_id);
152
153 if (!wvif) {
154 dev_warn(wdev->dev, "received event for non-existent vif\n");
155 return 0;
156 }
157
158 switch (type) {
159 case HIF_EVENT_IND_RCPI_RSSI:
160 wfx_event_report_rssi(wvif, body->event_data.rcpi_rssi);
161 break;
162 case HIF_EVENT_IND_BSSLOST:
163 schedule_delayed_work(&wvif->beacon_loss_work, 0);
164 break;
165 case HIF_EVENT_IND_BSSREGAINED:
166 cancel_delayed_work(&wvif->beacon_loss_work);
167 dev_dbg(wdev->dev, "ignore BSSREGAINED indication\n");
168 break;
169 case HIF_EVENT_IND_PS_MODE_ERROR:
170 dev_warn(wdev->dev, "error while processing power save request: %d\n",
171 le32_to_cpu(body->event_data.ps_mode_error));
172 break;
173 default:
174 dev_warn(wdev->dev, "unhandled event indication: %.2x\n",
175 type);
176 break;
177 }
178 return 0;
179}
180
181static int hif_pm_mode_complete_indication(struct wfx_dev *wdev,
182 const struct hif_msg *hif,
183 const void *buf)
184{
185 struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
186
187 WARN_ON(!wvif);
188 complete(&wvif->set_pm_mode_complete);
189
190 return 0;
191}
192
193static int hif_scan_complete_indication(struct wfx_dev *wdev,
194 const struct hif_msg *hif,
195 const void *buf)
196{
197 struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
198
199 WARN_ON(!wvif);
200 wfx_scan_complete(wvif);
201
202 return 0;
203}
204
205static int hif_join_complete_indication(struct wfx_dev *wdev,
206 const struct hif_msg *hif,
207 const void *buf)
208{
209 struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
210
211 WARN_ON(!wvif);
212 dev_warn(wdev->dev, "unattended JoinCompleteInd\n");
213
214 return 0;
215}
216
217static int hif_suspend_resume_indication(struct wfx_dev *wdev,
218 const struct hif_msg *hif,
219 const void *buf)
220{
221 struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
222 const struct hif_ind_suspend_resume_tx *body = buf;
223
224 if (body->suspend_resume_flags.bc_mc_only) {
225 WARN_ON(!wvif);
226 if (body->suspend_resume_flags.resume)
227 wfx_suspend_resume_mc(wvif, STA_NOTIFY_AWAKE);
228 else
229 wfx_suspend_resume_mc(wvif, STA_NOTIFY_SLEEP);
230 } else {
231 WARN(body->peer_sta_set, "misunderstood indication");
232 WARN(hif->interface != 2, "misunderstood indication");
233 if (body->suspend_resume_flags.resume)
234 wfx_suspend_hot_dev(wdev, STA_NOTIFY_AWAKE);
235 else
236 wfx_suspend_hot_dev(wdev, STA_NOTIFY_SLEEP);
237 }
238
239 return 0;
240}
241
242static int hif_generic_indication(struct wfx_dev *wdev,
243 const struct hif_msg *hif, const void *buf)
244{
245 const struct hif_ind_generic *body = buf;
246 int type = le32_to_cpu(body->indication_type);
247
248 switch (type) {
249 case HIF_GENERIC_INDICATION_TYPE_RAW:
250 return 0;
251 case HIF_GENERIC_INDICATION_TYPE_STRING:
252 dev_info(wdev->dev, "firmware says: %s\n",
253 (char *)body->indication_data.raw_data);
254 return 0;
255 case HIF_GENERIC_INDICATION_TYPE_RX_STATS:
256 mutex_lock(&wdev->rx_stats_lock);
257
258 if (!wfx_api_older_than(wdev, 1, 4))
259 dev_info(wdev->dev, "Rx test ongoing. Temperature: %d°C\n",
260 body->indication_data.rx_stats.current_temp);
261 memcpy(&wdev->rx_stats, &body->indication_data.rx_stats,
262 sizeof(wdev->rx_stats));
263 mutex_unlock(&wdev->rx_stats_lock);
264 return 0;
265 case HIF_GENERIC_INDICATION_TYPE_TX_POWER_LOOP_INFO:
266 mutex_lock(&wdev->tx_power_loop_info_lock);
267 memcpy(&wdev->tx_power_loop_info,
268 &body->indication_data.tx_power_loop_info,
269 sizeof(wdev->tx_power_loop_info));
270 mutex_unlock(&wdev->tx_power_loop_info_lock);
271 return 0;
272 default:
273 dev_err(wdev->dev, "generic_indication: unknown indication type: %#.8x\n",
274 type);
275 return -EIO;
276 }
277}
278
279static const struct {
280 int val;
281 const char *str;
282 bool has_param;
283} hif_errors[] = {
284 { HIF_ERROR_FIRMWARE_ROLLBACK,
285 "rollback status" },
286 { HIF_ERROR_FIRMWARE_DEBUG_ENABLED,
287 "debug feature enabled" },
288 { HIF_ERROR_PDS_PAYLOAD,
289 "PDS version is not supported" },
290 { HIF_ERROR_PDS_TESTFEATURE,
291 "PDS ask for an unknown test mode" },
292 { HIF_ERROR_OOR_VOLTAGE,
293 "out-of-range power supply voltage", true },
294 { HIF_ERROR_OOR_TEMPERATURE,
295 "out-of-range temperature", true },
296 { HIF_ERROR_SLK_REQ_DURING_KEY_EXCHANGE,
297 "secure link does not expect request during key exchange" },
298 { HIF_ERROR_SLK_SESSION_KEY,
299 "secure link session key is invalid" },
300 { HIF_ERROR_SLK_OVERFLOW,
301 "secure link overflow" },
302 { HIF_ERROR_SLK_WRONG_ENCRYPTION_STATE,
303 "secure link messages list does not match message encryption" },
304 { HIF_ERROR_HIF_BUS_FREQUENCY_TOO_LOW,
305 "bus clock is too slow (<1kHz)" },
306 { HIF_ERROR_HIF_RX_DATA_TOO_LARGE,
307 "HIF message too large" },
308
309 { HIF_ERROR_HIF_TX_QUEUE_FULL,
310 "HIF messages queue is full" },
311 { HIF_ERROR_HIF_BUS,
312 "HIF bus" },
313 { HIF_ERROR_SLK_MULTI_TX_UNSUPPORTED,
314 "secure link does not support multi-tx confirmations" },
315 { HIF_ERROR_SLK_OUTDATED_SESSION_KEY,
316 "secure link session key is outdated" },
317 { HIF_ERROR_SLK_DECRYPTION,
318 "secure link params (nonce or tag) mismatch" },
319};
320
321static int hif_error_indication(struct wfx_dev *wdev,
322 const struct hif_msg *hif, const void *buf)
323{
324 const struct hif_ind_error *body = buf;
325 int type = le32_to_cpu(body->type);
326 int param = (s8)body->data[0];
327 int i;
328
329 for (i = 0; i < ARRAY_SIZE(hif_errors); i++)
330 if (type == hif_errors[i].val)
331 break;
332 if (i < ARRAY_SIZE(hif_errors))
333 if (hif_errors[i].has_param)
334 dev_err(wdev->dev, "asynchronous error: %s: %d\n",
335 hif_errors[i].str, param);
336 else
337 dev_err(wdev->dev, "asynchronous error: %s\n",
338 hif_errors[i].str);
339 else
340 dev_err(wdev->dev, "asynchronous error: unknown: %08x\n", type);
341 print_hex_dump(KERN_INFO, "hif: ", DUMP_PREFIX_OFFSET,
342 16, 1, hif, le16_to_cpu(hif->len), false);
343 wdev->chip_frozen = true;
344
345 return 0;
346};
347
348static int hif_exception_indication(struct wfx_dev *wdev,
349 const struct hif_msg *hif, const void *buf)
350{
351 const struct hif_ind_exception *body = buf;
352 int type = le32_to_cpu(body->type);
353
354 if (type == 4)
355 dev_err(wdev->dev, "firmware assert %d\n",
356 le32_to_cpup((__le32 *)body->data));
357 else
358 dev_err(wdev->dev, "firmware exception\n");
359 print_hex_dump(KERN_INFO, "hif: ", DUMP_PREFIX_OFFSET,
360 16, 1, hif, le16_to_cpu(hif->len), false);
361 wdev->chip_frozen = true;
362
363 return -1;
364}
365
366static const struct {
367 int msg_id;
368 int (*handler)(struct wfx_dev *wdev,
369 const struct hif_msg *hif, const void *buf);
370} hif_handlers[] = {
371
372 { HIF_CNF_ID_TX, hif_tx_confirm },
373 { HIF_CNF_ID_MULTI_TRANSMIT, hif_multi_tx_confirm },
374
375 { HIF_IND_ID_STARTUP, hif_startup_indication },
376 { HIF_IND_ID_WAKEUP, hif_wakeup_indication },
377 { HIF_IND_ID_JOIN_COMPLETE, hif_join_complete_indication },
378 { HIF_IND_ID_SET_PM_MODE_CMPL, hif_pm_mode_complete_indication },
379 { HIF_IND_ID_SCAN_CMPL, hif_scan_complete_indication },
380 { HIF_IND_ID_SUSPEND_RESUME_TX, hif_suspend_resume_indication },
381 { HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication },
382 { HIF_IND_ID_EVENT, hif_event_indication },
383 { HIF_IND_ID_GENERIC, hif_generic_indication },
384 { HIF_IND_ID_ERROR, hif_error_indication },
385 { HIF_IND_ID_EXCEPTION, hif_exception_indication },
386
387
388};
389
390void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb)
391{
392 int i;
393 const struct hif_msg *hif = (const struct hif_msg *)skb->data;
394 int hif_id = hif->id;
395
396 if (hif_id == HIF_IND_ID_RX) {
397
398 hif_receive_indication(wdev, hif, hif->body, skb);
399 return;
400 }
401
402
403 if (mutex_is_locked(&wdev->hif_cmd.lock)
404 && wdev->hif_cmd.buf_send
405 && wdev->hif_cmd.buf_send->id == hif_id) {
406 hif_generic_confirm(wdev, hif, hif->body);
407 goto free;
408 }
409 for (i = 0; i < ARRAY_SIZE(hif_handlers); i++) {
410 if (hif_handlers[i].msg_id == hif_id) {
411 if (hif_handlers[i].handler)
412 hif_handlers[i].handler(wdev, hif, hif->body);
413 goto free;
414 }
415 }
416 if (hif_id & 0x80)
417 dev_err(wdev->dev, "unsupported HIF indication: ID %02x\n",
418 hif_id);
419 else
420 dev_err(wdev->dev, "unexpected HIF confirmation: ID %02x\n",
421 hif_id);
422free:
423 dev_kfree_skb(skb);
424}
425