]> Git Repo - linux.git/blob - drivers/staging/wfx/hif_rx.c
efi/x86: add headroom to decompressor BSS to account for setup block
[linux.git] / drivers / staging / wfx / hif_rx.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Implementation of chip-to-host event (aka indications) of WFxxx Split Mac
4  * (WSM) API.
5  *
6  * Copyright (c) 2017-2019, Silicon Laboratories, Inc.
7  * Copyright (c) 2010, ST-Ericsson
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
21 static int hif_generic_confirm(struct wfx_dev *wdev,
22                                const struct hif_msg *hif, const void *buf)
23 {
24         // All confirm messages start with status
25         int status = le32_to_cpu(*((__le32 *) buf));
26         int cmd = hif->id;
27         int len = hif->len - 4; // drop header
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
62 static 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         struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
67
68         WARN_ON(!wvif);
69         if (!wvif)
70                 return -EFAULT;
71
72         wfx_tx_confirm_cb(wvif, body);
73         return 0;
74 }
75
76 static int hif_multi_tx_confirm(struct wfx_dev *wdev,
77                                 const struct hif_msg *hif, const void *buf)
78 {
79         const struct hif_cnf_multi_transmit *body = buf;
80         struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
81         int i;
82
83         WARN(body->num_tx_confs <= 0, "corrupted message");
84         WARN_ON(!wvif);
85         if (!wvif)
86                 return -EFAULT;
87
88         for (i = 0; i < body->num_tx_confs; i++)
89                 wfx_tx_confirm_cb(wvif, &body->tx_conf_payload[i]);
90         return 0;
91 }
92
93 static int hif_startup_indication(struct wfx_dev *wdev,
94                                   const struct hif_msg *hif, const void *buf)
95 {
96         const struct hif_ind_startup *body = buf;
97
98         if (body->status || body->firmware_type > 4) {
99                 dev_err(wdev->dev, "received invalid startup indication");
100                 return -EINVAL;
101         }
102         memcpy(&wdev->hw_caps, body, sizeof(struct hif_ind_startup));
103         le32_to_cpus(&wdev->hw_caps.status);
104         le16_to_cpus(&wdev->hw_caps.hardware_id);
105         le16_to_cpus(&wdev->hw_caps.num_inp_ch_bufs);
106         le16_to_cpus(&wdev->hw_caps.size_inp_ch_buf);
107
108         complete(&wdev->firmware_ready);
109         return 0;
110 }
111
112 static int hif_wakeup_indication(struct wfx_dev *wdev,
113                                  const struct hif_msg *hif, const void *buf)
114 {
115         if (!wdev->pdata.gpio_wakeup
116             || !gpiod_get_value(wdev->pdata.gpio_wakeup)) {
117                 dev_warn(wdev->dev, "unexpected wake-up indication\n");
118                 return -EIO;
119         }
120         return 0;
121 }
122
123 static int hif_keys_indication(struct wfx_dev *wdev,
124                                const struct hif_msg *hif, const void *buf)
125 {
126         const struct hif_ind_sl_exchange_pub_keys *body = buf;
127         u8 pubkey[API_NCP_PUB_KEY_SIZE];
128
129         // SL_PUB_KEY_EXCHANGE_STATUS_SUCCESS is used by legacy secure link
130         if (body->status && body->status != SL_PUB_KEY_EXCHANGE_STATUS_SUCCESS)
131                 dev_warn(wdev->dev, "secure link negociation error\n");
132         memcpy(pubkey, body->ncp_pub_key, sizeof(pubkey));
133         memreverse(pubkey, sizeof(pubkey));
134         wfx_sl_check_pubkey(wdev, pubkey, body->ncp_pub_key_mac);
135         return 0;
136 }
137
138 static int hif_receive_indication(struct wfx_dev *wdev,
139                                   const struct hif_msg *hif,
140                                   const void *buf, struct sk_buff *skb)
141 {
142         struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
143         const struct hif_ind_rx *body = buf;
144
145         if (!wvif) {
146                 dev_warn(wdev->dev, "ignore rx data for non-existent vif %d\n",
147                          hif->interface);
148                 return 0;
149         }
150         skb_pull(skb, sizeof(struct hif_msg) + sizeof(struct hif_ind_rx));
151         wfx_rx_cb(wvif, body, skb);
152
153         return 0;
154 }
155
156 static int hif_event_indication(struct wfx_dev *wdev,
157                                 const struct hif_msg *hif, const void *buf)
158 {
159         struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
160         const struct hif_ind_event *body = buf;
161         struct wfx_hif_event *event;
162         int first;
163
164         WARN_ON(!wvif);
165         if (!wvif)
166                 return 0;
167
168         event = kzalloc(sizeof(*event), GFP_KERNEL);
169         if (!event)
170                 return -ENOMEM;
171
172         memcpy(&event->evt, body, sizeof(struct hif_ind_event));
173         spin_lock(&wvif->event_queue_lock);
174         first = list_empty(&wvif->event_queue);
175         list_add_tail(&event->link, &wvif->event_queue);
176         spin_unlock(&wvif->event_queue_lock);
177
178         if (first)
179                 schedule_work(&wvif->event_handler_work);
180
181         return 0;
182 }
183
184 static int hif_pm_mode_complete_indication(struct wfx_dev *wdev,
185                                            const struct hif_msg *hif,
186                                            const void *buf)
187 {
188         struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
189
190         WARN_ON(!wvif);
191         complete(&wvif->set_pm_mode_complete);
192
193         return 0;
194 }
195
196 static int hif_scan_complete_indication(struct wfx_dev *wdev,
197                                         const struct hif_msg *hif,
198                                         const void *buf)
199 {
200         struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
201
202         WARN_ON(!wvif);
203         wfx_scan_complete(wvif);
204
205         return 0;
206 }
207
208 static int hif_join_complete_indication(struct wfx_dev *wdev,
209                                         const struct hif_msg *hif,
210                                         const void *buf)
211 {
212         struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
213
214         WARN_ON(!wvif);
215         dev_warn(wdev->dev, "unattended JoinCompleteInd\n");
216
217         return 0;
218 }
219
220 static int hif_suspend_resume_indication(struct wfx_dev *wdev,
221                                          const struct hif_msg *hif,
222                                          const void *buf)
223 {
224         struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
225         const struct hif_ind_suspend_resume_tx *body = buf;
226
227         WARN_ON(!wvif);
228         WARN(!body->suspend_resume_flags.bc_mc_only, "unsupported suspend/resume notification");
229         if (body->suspend_resume_flags.resume)
230                 wfx_suspend_resume_mc(wvif, STA_NOTIFY_AWAKE);
231         else
232                 wfx_suspend_resume_mc(wvif, STA_NOTIFY_SLEEP);
233
234         return 0;
235 }
236
237 static int hif_error_indication(struct wfx_dev *wdev,
238                                 const struct hif_msg *hif, const void *buf)
239 {
240         const struct hif_ind_error *body = buf;
241         u8 *pRollback = (u8 *) body->data;
242         u32 *pStatus = (u32 *) body->data;
243
244         switch (body->type) {
245         case HIF_ERROR_FIRMWARE_ROLLBACK:
246                 dev_err(wdev->dev,
247                         "asynchronous error: firmware rollback error %d\n",
248                         *pRollback);
249                 break;
250         case HIF_ERROR_FIRMWARE_DEBUG_ENABLED:
251                 dev_err(wdev->dev, "asynchronous error: firmware debug feature enabled\n");
252                 break;
253         case HIF_ERROR_OUTDATED_SESSION_KEY:
254                 dev_err(wdev->dev, "asynchronous error: secure link outdated key: %#.8x\n",
255                         *pStatus);
256                 break;
257         case HIF_ERROR_INVALID_SESSION_KEY:
258                 dev_err(wdev->dev, "asynchronous error: invalid session key\n");
259                 break;
260         case HIF_ERROR_OOR_VOLTAGE:
261                 dev_err(wdev->dev, "asynchronous error: out-of-range overvoltage: %#.8x\n",
262                         *pStatus);
263                 break;
264         case HIF_ERROR_PDS_VERSION:
265                 dev_err(wdev->dev,
266                         "asynchronous error: wrong PDS payload or version: %#.8x\n",
267                         *pStatus);
268                 break;
269         default:
270                 dev_err(wdev->dev, "asynchronous error: unknown (%d)\n",
271                         body->type);
272                 break;
273         }
274         return 0;
275 }
276
277 static int hif_generic_indication(struct wfx_dev *wdev,
278                                   const struct hif_msg *hif, const void *buf)
279 {
280         const struct hif_ind_generic *body = buf;
281
282         switch (body->indication_type) {
283         case HIF_GENERIC_INDICATION_TYPE_RAW:
284                 return 0;
285         case HIF_GENERIC_INDICATION_TYPE_STRING:
286                 dev_info(wdev->dev, "firmware says: %s\n",
287                          (char *) body->indication_data.raw_data);
288                 return 0;
289         case HIF_GENERIC_INDICATION_TYPE_RX_STATS:
290                 mutex_lock(&wdev->rx_stats_lock);
291                 // Older firmware send a generic indication beside RxStats
292                 if (!wfx_api_older_than(wdev, 1, 4))
293                         dev_info(wdev->dev, "Rx test ongoing. Temperature: %d°C\n",
294                                  body->indication_data.rx_stats.current_temp);
295                 memcpy(&wdev->rx_stats, &body->indication_data.rx_stats,
296                        sizeof(wdev->rx_stats));
297                 mutex_unlock(&wdev->rx_stats_lock);
298                 return 0;
299         default:
300                 dev_err(wdev->dev,
301                         "generic_indication: unknown indication type: %#.8x\n",
302                         body->indication_type);
303                 return -EIO;
304         }
305 }
306
307 static int hif_exception_indication(struct wfx_dev *wdev,
308                                     const struct hif_msg *hif, const void *buf)
309 {
310         size_t len = hif->len - 4; // drop header
311
312         dev_err(wdev->dev, "firmware exception\n");
313         print_hex_dump_bytes("Dump: ", DUMP_PREFIX_NONE, buf, len);
314         wdev->chip_frozen = 1;
315
316         return -1;
317 }
318
319 static const struct {
320         int msg_id;
321         int (*handler)(struct wfx_dev *wdev,
322                        const struct hif_msg *hif, const void *buf);
323 } hif_handlers[] = {
324         /* Confirmations */
325         { HIF_CNF_ID_TX,                   hif_tx_confirm },
326         { HIF_CNF_ID_MULTI_TRANSMIT,       hif_multi_tx_confirm },
327         /* Indications */
328         { HIF_IND_ID_STARTUP,              hif_startup_indication },
329         { HIF_IND_ID_WAKEUP,               hif_wakeup_indication },
330         { HIF_IND_ID_JOIN_COMPLETE,        hif_join_complete_indication },
331         { HIF_IND_ID_SET_PM_MODE_CMPL,     hif_pm_mode_complete_indication },
332         { HIF_IND_ID_SCAN_CMPL,            hif_scan_complete_indication },
333         { HIF_IND_ID_SUSPEND_RESUME_TX,    hif_suspend_resume_indication },
334         { HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication },
335         { HIF_IND_ID_EVENT,                hif_event_indication },
336         { HIF_IND_ID_GENERIC,              hif_generic_indication },
337         { HIF_IND_ID_ERROR,                hif_error_indication },
338         { HIF_IND_ID_EXCEPTION,            hif_exception_indication },
339         // FIXME: allocate skb_p from hif_receive_indication and make it generic
340         //{ HIF_IND_ID_RX,                 hif_receive_indication },
341 };
342
343 void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb)
344 {
345         int i;
346         const struct hif_msg *hif = (const struct hif_msg *)skb->data;
347         int hif_id = hif->id;
348
349         if (hif_id == HIF_IND_ID_RX) {
350                 // hif_receive_indication take care of skb lifetime
351                 hif_receive_indication(wdev, hif, hif->body, skb);
352                 return;
353         }
354         // Note: mutex_is_lock cause an implicit memory barrier that protect
355         // buf_send
356         if (mutex_is_locked(&wdev->hif_cmd.lock)
357             && wdev->hif_cmd.buf_send
358             && wdev->hif_cmd.buf_send->id == hif_id) {
359                 hif_generic_confirm(wdev, hif, hif->body);
360                 goto free;
361         }
362         for (i = 0; i < ARRAY_SIZE(hif_handlers); i++) {
363                 if (hif_handlers[i].msg_id == hif_id) {
364                         if (hif_handlers[i].handler)
365                                 hif_handlers[i].handler(wdev, hif, hif->body);
366                         goto free;
367                 }
368         }
369         if (hif_id & 0x80)
370                 dev_err(wdev->dev, "unsupported HIF indication: ID %02x\n",
371                         hif_id);
372         else
373                 dev_err(wdev->dev, "unexpected HIF confirmation: ID %02x\n",
374                         hif_id);
375 free:
376         dev_kfree_skb(skb);
377 }
This page took 0.054141 seconds and 4 git commands to generate.