|
94 | 94 | #include "common/log.h"
|
95 | 95 | #include "hal/debug.h"
|
96 | 96 |
|
| 97 | +#define STR_NULL_TERMINATOR 0x00 |
| 98 | + |
97 | 99 | /* opcode of the HCI command currently being processed. The opcode is stored
|
98 | 100 | * by hci_cmd_handle() and then used during the creation of cmd complete and
|
99 | 101 | * 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)
|
4801 | 4803 | rp->handle = sys_cpu_to_le16(handle);
|
4802 | 4804 | }
|
4803 | 4805 | #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 | + |
4804 | 4955 | #endif /* CONFIG_BT_HCI_VS_EXT */
|
4805 | 4956 |
|
4806 | 4957 | #if defined(CONFIG_BT_HCI_MESH_EXT)
|
|
0 commit comments