1 // SPDX-License-Identifier: GPL-2.0-only
3 * Handling of the chip-to-host events (aka indications) of the hardware API.
5 * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
6 * Copyright (c) 2010, ST-Ericsson
8 #include <linux/skbuff.h>
9 #include <linux/etherdevice.h>
17 #include "hif_api_cmd.h"
19 static int wfx_hif_generic_confirm(struct wfx_dev
*wdev
,
20 const struct wfx_hif_msg
*hif
, const void *buf
)
22 /* All confirm messages start with status */
23 int status
= le32_to_cpup((__le32
*)buf
);
25 int len
= le16_to_cpu(hif
->len
) - 4; /* drop header */
27 WARN(!mutex_is_locked(&wdev
->hif_cmd
.lock
), "data locking error");
29 if (!wdev
->hif_cmd
.buf_send
) {
30 dev_warn(wdev
->dev
, "unexpected confirmation: 0x%.2x\n", cmd
);
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
);
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
);
46 wdev
->hif_cmd
.ret
= status
;
48 complete(&wdev
->hif_cmd
.done
);
52 static int wfx_hif_tx_confirm(struct wfx_dev
*wdev
,
53 const struct wfx_hif_msg
*hif
, const void *buf
)
55 const struct wfx_hif_cnf_tx
*body
= buf
;
57 wfx_tx_confirm_cb(wdev
, body
);
61 static int wfx_hif_multi_tx_confirm(struct wfx_dev
*wdev
,
62 const struct wfx_hif_msg
*hif
, const void *buf
)
64 const struct wfx_hif_cnf_multi_transmit
*body
= buf
;
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
]);
73 static int wfx_hif_startup_indication(struct wfx_dev
*wdev
,
74 const struct wfx_hif_msg
*hif
, const void *buf
)
76 const struct wfx_hif_ind_startup
*body
= buf
;
78 if (body
->status
|| body
->firmware_type
> 4) {
79 dev_err(wdev
->dev
, "received invalid startup indication");
82 memcpy(&wdev
->hw_caps
, body
, sizeof(struct wfx_hif_ind_startup
));
83 complete(&wdev
->firmware_ready
);
87 static int wfx_hif_wakeup_indication(struct wfx_dev
*wdev
,
88 const struct wfx_hif_msg
*hif
, const void *buf
)
90 if (!wdev
->pdata
.gpio_wakeup
|| gpiod_get_value(wdev
->pdata
.gpio_wakeup
) == 0) {
91 dev_warn(wdev
->dev
, "unexpected wake-up indication\n");
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
)
100 struct wfx_vif
*wvif
= wdev_to_wvif(wdev
, hif
->interface
);
101 const struct wfx_hif_ind_rx
*body
= buf
;
104 dev_warn(wdev
->dev
, "%s: received event for non-existent vif\n", __func__
);
107 skb_pull(skb
, sizeof(struct wfx_hif_msg
) + sizeof(struct wfx_hif_ind_rx
));
108 wfx_rx_cb(wvif
, body
, skb
);
113 static int wfx_hif_event_indication(struct wfx_dev
*wdev
,
114 const struct wfx_hif_msg
*hif
, const void *buf
)
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
);
121 dev_warn(wdev
->dev
, "%s: received event for non-existent vif\n", __func__
);
126 case HIF_EVENT_IND_RCPI_RSSI
:
127 wfx_event_report_rssi(wvif
, body
->event_data
.rcpi_rssi
);
129 case HIF_EVENT_IND_BSSLOST
:
130 schedule_delayed_work(&wvif
->beacon_loss_work
, 0);
132 case HIF_EVENT_IND_BSSREGAINED
:
133 cancel_delayed_work(&wvif
->beacon_loss_work
);
134 dev_dbg(wdev
->dev
, "ignore BSSREGAINED indication\n");
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
));
141 dev_warn(wdev
->dev
, "unhandled event indication: %.2x\n", type
);
147 static int wfx_hif_pm_mode_complete_indication(struct wfx_dev
*wdev
,
148 const struct wfx_hif_msg
*hif
, const void *buf
)
150 struct wfx_vif
*wvif
= wdev_to_wvif(wdev
, hif
->interface
);
153 dev_warn(wdev
->dev
, "%s: received event for non-existent vif\n", __func__
);
156 complete(&wvif
->set_pm_mode_complete
);
161 static int wfx_hif_scan_complete_indication(struct wfx_dev
*wdev
,
162 const struct wfx_hif_msg
*hif
, const void *buf
)
164 struct wfx_vif
*wvif
= wdev_to_wvif(wdev
, hif
->interface
);
165 const struct wfx_hif_ind_scan_cmpl
*body
= buf
;
168 dev_warn(wdev
->dev
, "%s: received event for non-existent vif\n", __func__
);
172 wfx_scan_complete(wvif
, body
->num_channels_completed
);
177 static int wfx_hif_join_complete_indication(struct wfx_dev
*wdev
,
178 const struct wfx_hif_msg
*hif
, const void *buf
)
180 struct wfx_vif
*wvif
= wdev_to_wvif(wdev
, hif
->interface
);
183 dev_warn(wdev
->dev
, "%s: received event for non-existent vif\n", __func__
);
186 dev_warn(wdev
->dev
, "unattended JoinCompleteInd\n");
191 static int wfx_hif_suspend_resume_indication(struct wfx_dev
*wdev
,
192 const struct wfx_hif_msg
*hif
, const void *buf
)
194 const struct wfx_hif_ind_suspend_resume_tx
*body
= buf
;
195 struct wfx_vif
*wvif
;
197 if (body
->bc_mc_only
) {
198 wvif
= wdev_to_wvif(wdev
, hif
->interface
);
200 dev_warn(wdev
->dev
, "%s: received event for non-existent vif\n", __func__
);
204 wfx_suspend_resume_mc(wvif
, STA_NOTIFY_AWAKE
);
206 wfx_suspend_resume_mc(wvif
, STA_NOTIFY_SLEEP
);
208 WARN(body
->peer_sta_set
, "misunderstood indication");
209 WARN(hif
->interface
!= 2, "misunderstood indication");
211 wfx_suspend_hot_dev(wdev
, STA_NOTIFY_AWAKE
);
213 wfx_suspend_hot_dev(wdev
, STA_NOTIFY_SLEEP
);
219 static int wfx_hif_generic_indication(struct wfx_dev
*wdev
,
220 const struct wfx_hif_msg
*hif
, const void *buf
)
222 const struct wfx_hif_ind_generic
*body
= buf
;
223 int type
= le32_to_cpu(body
->type
);
226 case HIF_GENERIC_INDICATION_TYPE_RAW
:
228 case HIF_GENERIC_INDICATION_TYPE_STRING
:
229 dev_info(wdev
->dev
, "firmware says: %s\n", (char *)&body
->data
);
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
);
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
);
247 dev_err(wdev
->dev
, "generic_indication: unknown indication type: %#.8x\n", type
);
252 static const struct {
257 { HIF_ERROR_FIRMWARE_ROLLBACK
,
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" },
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" },
296 static int wfx_hif_error_indication(struct wfx_dev
*wdev
,
297 const struct wfx_hif_msg
*hif
, const void *buf
)
299 const struct wfx_hif_ind_error
*body
= buf
;
300 int type
= le32_to_cpu(body
->type
);
301 int param
= (s8
)body
->data
[0];
304 for (i
= 0; i
< ARRAY_SIZE(hif_errors
); i
++)
305 if (type
== hif_errors
[i
].val
)
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
);
312 dev_err(wdev
->dev
, "asynchronous error: %s\n", hif_errors
[i
].str
);
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;
322 static int wfx_hif_exception_indication(struct wfx_dev
*wdev
,
323 const struct wfx_hif_msg
*hif
, const void *buf
)
325 const struct wfx_hif_ind_exception
*body
= buf
;
326 int type
= le32_to_cpu(body
->type
);
329 dev_err(wdev
->dev
, "firmware assert %d\n", le32_to_cpup((__le32
*)body
->data
));
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;
339 static const struct {
341 int (*handler
)(struct wfx_dev
*wdev
, const struct wfx_hif_msg
*hif
, const void *buf
);
344 { HIF_CNF_ID_TX
, wfx_hif_tx_confirm
},
345 { HIF_CNF_ID_MULTI_TRANSMIT
, wfx_hif_multi_tx_confirm
},
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 },
361 void wfx_handle_rx(struct wfx_dev
*wdev
, struct sk_buff
*skb
)
364 const struct wfx_hif_msg
*hif
= (const struct wfx_hif_msg
*)skb
->data
;
365 int hif_id
= hif
->id
;
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
);
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
);
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
);
385 if (hif_id
& HIF_ID_IS_INDICATION
)
386 dev_err(wdev
->dev
, "unsupported HIF indication: ID %02x\n", hif_id
);
388 dev_err(wdev
->dev
, "unexpected HIF confirmation: ID %02x\n", hif_id
);