Skip to content

Commit a0a8a12

Browse files
ppryga-nordiccarlescufi
authored andcommitted
Bluetooth: hci: Use extended VS fatal error in hci and hci_rpmsg sample
Provide common helper functions to create extended extended Zephyr Fatal Error functionality in HCI common code. Use the implementation in hci_rpmsg sample. The sample didn't provide an information about Controllers assert or system fatal error to an application code while run with nRF5340 SoC. The goal for hci_rpmsg sample change is to enhance user experience for conformance testing of the Bluetooth Controller while executed with nRF5340. Signed-off-by: Piotr Pryga <[email protected]>
1 parent c6e9b3c commit a0a8a12

File tree

3 files changed

+252
-4
lines changed

3 files changed

+252
-4
lines changed

samples/bluetooth/hci_rpmsg/src/main.c

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
#include <zephyr/bluetooth/hci.h>
2323
#include <zephyr/bluetooth/buf.h>
2424
#include <zephyr/bluetooth/hci_raw.h>
25+
#include <zephyr/bluetooth/hci_vs.h>
26+
27+
#if defined(CONFIG_BT_HCI_VS_FATAL_ERROR)
28+
#include <zephyr/logging/log_ctrl.h>
29+
#endif /* CONFIG_BT_HCI_VS_FATAL_ERROR */
2530

2631
#define BT_DBG_ENABLED 0
2732
#define LOG_MODULE_NAME hci_rpmsg
@@ -33,13 +38,22 @@ static K_THREAD_STACK_DEFINE(tx_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE);
3338
static struct k_thread tx_thread_data;
3439
static K_FIFO_DEFINE(tx_queue);
3540
static K_SEM_DEFINE(ipc_bound_sem, 0, 1);
41+
#if defined(CONFIG_BT_CTLR_ASSERT_HANDLER) || defined(CONFIG_BT_HCI_VS_FATAL_ERROR)
42+
/* A flag used to store information if the IPC endpoint has already been bound. The end point can't
43+
* be used before that happens.
44+
*/
45+
static bool ipc_ept_ready;
46+
#endif /* CONFIG_BT_CTLR_ASSERT_HANDLER || CONFIG_BT_HCI_VS_FATAL_ERROR */
3647

3748
#define HCI_RPMSG_CMD 0x01
3849
#define HCI_RPMSG_ACL 0x02
3950
#define HCI_RPMSG_SCO 0x03
4051
#define HCI_RPMSG_EVT 0x04
4152
#define HCI_RPMSG_ISO 0x05
4253

54+
#define HCI_FATAL_ERR_MSG true
55+
#define HCI_REGULAR_MSG false
56+
4357
static struct net_buf *hci_rpmsg_cmd_recv(uint8_t *data, size_t remaining)
4458
{
4559
struct bt_hci_cmd_hdr *hdr = (void *)data;
@@ -191,7 +205,7 @@ static void tx_thread(void *p1, void *p2, void *p3)
191205
}
192206
}
193207

194-
static void hci_rpmsg_send(struct net_buf *buf)
208+
static void hci_rpmsg_send(struct net_buf *buf, bool is_fatal_err)
195209
{
196210
uint8_t pkt_indicator;
197211
uint8_t retries = 0;
@@ -230,7 +244,21 @@ static void hci_rpmsg_send(struct net_buf *buf)
230244
LOG_WRN("IPC send has been blocked for 1.5 seconds.");
231245
retries = 0;
232246
}
233-
k_yield();
247+
248+
/* The function can be called by the application main thread,
249+
* bt_ctlr_assert_handle and k_sys_fatal_error_handler. In case of a call by
250+
* Bluetooth Controller assert handler or system fatal error handler the
251+
* call can be from ISR context, hence there is no thread to yield. Besides
252+
* that both handlers implement a policy to provide error information and
253+
* stop the system in an infinite loop. The goal is to prevent any other
254+
* damage to the system if one of such exeptional situations occur, hence
255+
* call to k_yield is against it.
256+
*/
257+
if (is_fatal_err) {
258+
LOG_ERR("IPC service send error: %d", ret);
259+
} else {
260+
k_yield();
261+
}
234262
}
235263
} while (ret < 0);
236264

@@ -242,13 +270,74 @@ static void hci_rpmsg_send(struct net_buf *buf)
242270
#if defined(CONFIG_BT_CTLR_ASSERT_HANDLER)
243271
void bt_ctlr_assert_handle(char *file, uint32_t line)
244272
{
245-
BT_ASSERT_MSG(false, "Controller assert in: %s at %d", file, line);
273+
#if defined(CONFIG_BT_HCI_VS_FATAL_ERROR)
274+
/* Disable interrupts, this is unrecoverable */
275+
(void)irq_lock();
276+
277+
/* Generate an error event only when IPC service endpoint is already bound. */
278+
if (ipc_ept_ready) {
279+
/* Prepare vendor specific HCI debug event */
280+
struct net_buf *buf;
281+
282+
buf = hci_vs_err_assert(file, line);
283+
if (buf == NULL) {
284+
/* Send the event over rpmsg */
285+
hci_rpmsg_send(buf, HCI_FATAL_ERR_MSG);
286+
} else {
287+
LOG_ERR("Can't create Fatal Error HCI event: %s at %d", __FILE__, __LINE__);
288+
}
289+
} else {
290+
LOG_ERR("IPC endpoint is not redy yet: %s at %d", __FILE__, __LINE__);
291+
}
292+
293+
LOG_ERR("Halting system");
294+
295+
while (true) {
296+
};
297+
#else
298+
LOG_ERR("Controller assert in: %s at %d", file, line);
299+
#endif /* CONFIG_BT_HCI_VS_FATAL_ERROR */
246300
}
247301
#endif /* CONFIG_BT_CTLR_ASSERT_HANDLER */
248302

303+
#if defined(CONFIG_BT_HCI_VS_FATAL_ERROR)
304+
void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf)
305+
{
306+
LOG_PANIC();
307+
308+
/* Disable interrupts, this is unrecoverable */
309+
(void)irq_lock();
310+
311+
/* Generate an error event only when there is a stack frame and IPC service endpoint is
312+
* already bound.
313+
*/
314+
if (esf != NULL && ipc_ept_ready) {
315+
/* Prepare vendor specific HCI debug event */
316+
struct net_buf *buf;
317+
318+
buf = hci_vs_err_stack_frame(reason, esf);
319+
if (buf != NULL) {
320+
hci_rpmsg_send(buf, HCI_FATAL_ERR_MSG);
321+
} else {
322+
LOG_ERR("Can't create Fatal Error HCI event.\n");
323+
}
324+
}
325+
326+
LOG_ERR("Halting system");
327+
328+
while (true) {
329+
};
330+
331+
CODE_UNREACHABLE;
332+
}
333+
#endif /* CONFIG_BT_HCI_VS_FATAL_ERROR */
334+
249335
static void hci_ept_bound(void *priv)
250336
{
251337
k_sem_give(&ipc_bound_sem);
338+
#if defined(CONFIG_BT_CTLR_ASSERT_HANDLER) || defined(CONFIG_BT_HCI_VS_FATAL_ERROR)
339+
ipc_ept_ready = true;
340+
#endif /* CONFIG_BT_CTLR_ASSERT_HANDLER || CONFIG_BT_HCI_VS_FATAL_ERROR */
252341
}
253342

254343
static void hci_ept_recv(const void *data, size_t len, void *priv)
@@ -304,6 +393,6 @@ void main(void)
304393
struct net_buf *buf;
305394

306395
buf = net_buf_get(&rx_queue, K_FOREVER);
307-
hci_rpmsg_send(buf);
396+
hci_rpmsg_send(buf, HCI_REGULAR_MSG);
308397
}
309398
}

subsys/bluetooth/common/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,14 @@ config BT_HCI_VS_EXT
214214
Host and/or Controller. This enables Write BD_ADDR, Read Build Info,
215215
Read Static Addresses and Read Key Hierarchy Roots vendor commands.
216216

217+
config BT_HCI_VS_FATAL_ERROR
218+
bool "Enable vendor specific HCI event Zephyr Fatal Error"
219+
depends on BT_HCI_VS_EXT
220+
default n
221+
help
222+
Enable emiting HCI Vendor-Specific events for system and Controller error that are
223+
unrecoverable.
224+
217225
config BT_HCI_VS_EXT_DETECT
218226
bool "Use heuristics to guess HCI vendor extensions support in advance"
219227
depends on BT_HCI_VS_EXT && !BT_CTLR

subsys/bluetooth/controller/hci/hci.c

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@
9494
#include "common/log.h"
9595
#include "hal/debug.h"
9696

97+
#define STR_NULL_TERMINATOR 0x00
98+
9799
/* opcode of the HCI command currently being processed. The opcode is stored
98100
* by hci_cmd_handle() and then used during the creation of cmd complete and
99101
* cmd status events to avoid passing it up the call chain.
@@ -4801,6 +4803,155 @@ static void vs_read_tx_power_level(struct net_buf *buf, struct net_buf **evt)
48014803
rp->handle = sys_cpu_to_le16(handle);
48024804
}
48034805
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
4806+
4807+
#if defined(CONFIG_BT_HCI_VS_FATAL_ERROR)
4808+
/* A memory pool for vandor specific events for fatal error reporting purposes. */
4809+
NET_BUF_POOL_FIXED_DEFINE(vs_err_tx_pool, 1, BT_BUF_EVT_RX_SIZE, 8, NULL);
4810+
4811+
/* The alias for convenience of Controller HCI implementation. Controller is build for
4812+
* a particular architecture hence the alias will allow to avoid conditional compilation.
4813+
* Host may be not aware of hardware architecture the Controller is working on, hence
4814+
* all CPU data types for supported architectures should be available during build, hence
4815+
* the alias is defined here.
4816+
*/
4817+
#if defined(CONFIG_CPU_CORTEX_M)
4818+
typedef struct bt_hci_vs_fata_error_cpu_data_cortex_m bt_hci_vs_fatal_error_cpu_data;
4819+
4820+
static void vs_err_fatal_cpu_data_fill(bt_hci_vs_fatal_error_cpu_data *cpu_data,
4821+
const z_arch_esf_t *esf)
4822+
{
4823+
cpu_data->a1 = sys_cpu_to_le32(esf->basic.a1);
4824+
cpu_data->a2 = sys_cpu_to_le32(esf->basic.a2);
4825+
cpu_data->a3 = sys_cpu_to_le32(esf->basic.a3);
4826+
cpu_data->a4 = sys_cpu_to_le32(esf->basic.a4);
4827+
cpu_data->ip = sys_cpu_to_le32(esf->basic.ip);
4828+
cpu_data->lr = sys_cpu_to_le32(esf->basic.lr);
4829+
cpu_data->xpsr = sys_cpu_to_le32(esf->basic.xpsr);
4830+
}
4831+
#endif /* CONFIG_CPU_CORTEX_M */
4832+
4833+
static struct net_buf *vs_err_evt_create(uint8_t subevt, uint8_t len)
4834+
{
4835+
struct net_buf *buf;
4836+
4837+
buf = net_buf_alloc(&vs_err_tx_pool, K_FOREVER);
4838+
if (buf) {
4839+
struct bt_hci_evt_le_meta_event *me;
4840+
struct bt_hci_evt_hdr *hdr;
4841+
4842+
net_buf_reserve(buf, BT_BUF_RESERVE);
4843+
bt_buf_set_type(buf, BT_BUF_EVT);
4844+
4845+
hdr = net_buf_add(buf, sizeof(*hdr));
4846+
hdr->evt = BT_HCI_EVT_VENDOR;
4847+
hdr->len = len + sizeof(*me);
4848+
4849+
me = net_buf_add(buf, sizeof(*me));
4850+
me->subevent = subevt;
4851+
}
4852+
4853+
return buf;
4854+
}
4855+
4856+
struct net_buf *hci_vs_err_stack_frame(unsigned int reason, const z_arch_esf_t *esf)
4857+
{
4858+
/* Prepare vendor specific HCI Fatal Error event */
4859+
struct bt_hci_vs_fatal_error_stack_frame *sf;
4860+
bt_hci_vs_fatal_error_cpu_data *cpu_data;
4861+
struct net_buf *buf;
4862+
4863+
buf = vs_err_evt_create(BT_HCI_EVT_VS_ERROR_DATA_TYPE_STACK_FRAME,
4864+
sizeof(*sf) + sizeof(*cpu_data));
4865+
if (buf != NULL) {
4866+
sf = net_buf_add(buf, (sizeof(*sf) + sizeof(*cpu_data)));
4867+
sf->reason = sys_cpu_to_le32(reason);
4868+
sf->cpu_type = BT_HCI_EVT_VS_ERROR_CPU_TYPE_CORTEX_M;
4869+
4870+
vs_err_fatal_cpu_data_fill(
4871+
(bt_hci_vs_fatal_error_cpu_data *)sf->cpu_data, esf);
4872+
} else {
4873+
BT_ERR("Can't create HCI Fatal Error event");
4874+
}
4875+
4876+
return buf;
4877+
}
4878+
4879+
static struct net_buf *hci_vs_err_trace_create(uint8_t data_type,
4880+
const char *file_path,
4881+
uint32_t line, uint64_t pc)
4882+
{
4883+
uint32_t file_name_len = 0U, pos = 0U;
4884+
struct net_buf *buf = NULL;
4885+
4886+
if (file_path) {
4887+
/* Extract file name from a path */
4888+
while (file_path[file_name_len] != '\0') {
4889+
if (file_path[file_name_len] == '/') {
4890+
pos = file_name_len + 1;
4891+
}
4892+
file_name_len++;
4893+
}
4894+
file_path += pos;
4895+
file_name_len -= pos;
4896+
4897+
/* If file name was found in file_path, in other words: file_path is not empty
4898+
* string and is not `foo/bar/`.
4899+
*/
4900+
if (file_name_len) {
4901+
/* Total data length: len = file name strlen + \0 + sizeof(line number)
4902+
* Maximum length of an HCI event data is BT_BUF_EVT_RX_SIZE. If total data
4903+
* length exceeds this maximum, truncate file name.
4904+
*/
4905+
uint32_t data_len = 1 + sizeof(line);
4906+
4907+
/* If a buffer is created for a TRACE data, include sizeof(pc) in total
4908+
* length.
4909+
*/
4910+
if (data_type == BT_HCI_EVT_VS_ERROR_DATA_TYPE_TRACE) {
4911+
data_len += sizeof(pc);
4912+
}
4913+
4914+
if (data_len + file_name_len > BT_BUF_EVT_RX_SIZE) {
4915+
uint32_t overflow_len =
4916+
file_name_len + data_len - BT_BUF_EVT_RX_SIZE;
4917+
4918+
/* Truncate the file name length by number of overflow bytes */
4919+
file_name_len -= overflow_len;
4920+
}
4921+
4922+
/* Get total event data length including file name length */
4923+
data_len += file_name_len;
4924+
4925+
/* Prepare vendor specific HCI Fatal Error event */
4926+
buf = vs_err_evt_create(data_type, data_len);
4927+
if (buf != NULL) {
4928+
if (data_type == BT_HCI_EVT_VS_ERROR_DATA_TYPE_TRACE) {
4929+
net_buf_add_le64(buf, pc);
4930+
}
4931+
net_buf_add_mem(buf, file_path, file_name_len);
4932+
net_buf_add_u8(buf, STR_NULL_TERMINATOR);
4933+
net_buf_add_le32(buf, line);
4934+
} else {
4935+
BT_ERR("Can't create HCI Fatal Error event");
4936+
}
4937+
}
4938+
}
4939+
4940+
return buf;
4941+
}
4942+
4943+
struct net_buf *hci_vs_err_trace(const char *file, uint32_t line, uint64_t pc)
4944+
{
4945+
return hci_vs_err_trace_create(BT_HCI_EVT_VS_ERROR_DATA_TYPE_TRACE, file, line, pc);
4946+
}
4947+
4948+
struct net_buf *hci_vs_err_assert(const char *file, uint32_t line)
4949+
{
4950+
/* ASSERT data does not contain PC counter, because of that zero constant is used */
4951+
return hci_vs_err_trace_create(BT_HCI_EVT_VS_ERROR_DATA_TYPE_CTRL_ASSERT, file, line, 0U);
4952+
}
4953+
#endif /* CONFIG_BT_HCI_VS_FATAL_ERROR */
4954+
48044955
#endif /* CONFIG_BT_HCI_VS_EXT */
48054956

48064957
#if defined(CONFIG_BT_HCI_MESH_EXT)

0 commit comments

Comments
 (0)