| /* |
| * Copyright (c) 2016 Nordic Semiconductor ASA |
| * Copyright (c) 2016 Vinayak Kariappa Chettimada |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <errno.h> |
| #include <stddef.h> |
| #include <string.h> |
| |
| #include <zephyr.h> |
| #include <soc.h> |
| #include <init.h> |
| #include <device.h> |
| #include <clock_control.h> |
| |
| #include <misc/util.h> |
| #include <misc/stack.h> |
| #include <misc/byteorder.h> |
| |
| #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLUETOOTH_DEBUG_HCI_DRIVER) |
| #include <bluetooth/log.h> |
| #include <bluetooth/bluetooth.h> |
| #include <bluetooth/hci.h> |
| #include <drivers/bluetooth/hci_driver.h> |
| |
| #ifdef CONFIG_CLOCK_CONTROL_NRF5 |
| #include <drivers/clock_control/nrf5_clock_control.h> |
| #endif |
| |
| #include <arch/arm/cortex_m/cmsis.h> |
| |
| #include "util/config.h" |
| #include "util/mayfly.h" |
| #include "util/util.h" |
| #include "util/mem.h" |
| #include "hal/rand.h" |
| #include "hal/ccm.h" |
| #include "hal/radio.h" |
| #include "hal/cntr.h" |
| #include "hal/cpu.h" |
| #include "ticker/ticker.h" |
| #include "ll/pdu.h" |
| #include "ll/ctrl.h" |
| #include "ll/ctrl_internal.h" |
| #include "hci_internal.h" |
| |
| #include "hal/debug.h" |
| |
| #define HCI_CMD 0x01 |
| #define HCI_ACL 0x02 |
| #define HCI_SCO 0x03 |
| #define HCI_EVT 0x04 |
| |
| static uint8_t MALIGN(4) _rand_context[3 + 4 + 1]; |
| static uint8_t MALIGN(4) _ticker_nodes[RADIO_TICKER_NODES][TICKER_NODE_T_SIZE]; |
| static uint8_t MALIGN(4) _ticker_users[MAYFLY_CALLER_COUNT] |
| [TICKER_USER_T_SIZE]; |
| static uint8_t MALIGN(4) _ticker_user_ops[RADIO_TICKER_USER_OPS] |
| [TICKER_USER_OP_T_SIZE]; |
| static uint8_t MALIGN(4) _radio[LL_MEM_TOTAL]; |
| |
| static K_SEM_DEFINE(sem_prio_recv, 0, UINT_MAX); |
| static K_FIFO_DEFINE(recv_fifo); |
| |
| static BT_STACK_NOINIT(prio_recv_thread_stack, |
| CONFIG_BLUETOOTH_CONTROLLER_RX_PRIO_STACK_SIZE); |
| static BT_STACK_NOINIT(recv_thread_stack, CONFIG_BLUETOOTH_RX_STACK_SIZE); |
| |
| K_MUTEX_DEFINE(mutex_rand); |
| |
| int bt_rand(void *buf, size_t len) |
| { |
| while (len) { |
| k_mutex_lock(&mutex_rand, K_FOREVER); |
| len = rand_get(len, buf); |
| k_mutex_unlock(&mutex_rand); |
| if (len) { |
| cpu_sleep(); |
| } |
| } |
| |
| return 0; |
| } |
| |
| void mayfly_enable(uint8_t caller_id, uint8_t callee_id, uint8_t enable) |
| { |
| (void)caller_id; |
| |
| LL_ASSERT(callee_id == MAYFLY_CALL_ID_1); |
| |
| if (enable) { |
| irq_enable(SWI4_IRQn); |
| } else { |
| irq_disable(SWI4_IRQn); |
| } |
| } |
| |
| uint32_t mayfly_is_enabled(uint8_t caller_id, uint8_t callee_id) |
| { |
| (void)caller_id; |
| |
| if (callee_id == MAYFLY_CALL_ID_0) { |
| return irq_is_enabled(RTC0_IRQn); |
| } else if (callee_id == MAYFLY_CALL_ID_1) { |
| return irq_is_enabled(SWI4_IRQn); |
| } |
| |
| LL_ASSERT(0); |
| |
| return 0; |
| } |
| |
| uint32_t mayfly_prio_is_equal(uint8_t caller_id, uint8_t callee_id) |
| { |
| return (caller_id == callee_id) || |
| ((caller_id == MAYFLY_CALL_ID_0) && |
| (callee_id == MAYFLY_CALL_ID_1)) || |
| ((caller_id == MAYFLY_CALL_ID_1) && |
| (callee_id == MAYFLY_CALL_ID_0)); |
| } |
| |
| void mayfly_pend(uint8_t caller_id, uint8_t callee_id) |
| { |
| (void)caller_id; |
| |
| switch (callee_id) { |
| case MAYFLY_CALL_ID_0: |
| NVIC_SetPendingIRQ(RTC0_IRQn); |
| break; |
| |
| case MAYFLY_CALL_ID_1: |
| NVIC_SetPendingIRQ(SWI4_IRQn); |
| break; |
| |
| case MAYFLY_CALL_ID_PROGRAM: |
| default: |
| LL_ASSERT(0); |
| break; |
| } |
| } |
| |
| void radio_active_callback(uint8_t active) |
| { |
| } |
| |
| void radio_event_callback(void) |
| { |
| k_sem_give(&sem_prio_recv); |
| } |
| |
| static void radio_nrf5_isr(void *arg) |
| { |
| isr_radio(arg); |
| } |
| |
| static void rtc0_nrf5_isr(void *arg) |
| { |
| uint32_t compare0, compare1; |
| |
| /* store interested events */ |
| compare0 = NRF_RTC0->EVENTS_COMPARE[0]; |
| compare1 = NRF_RTC0->EVENTS_COMPARE[1]; |
| |
| /* On compare0 run ticker worker instance0 */ |
| if (compare0) { |
| NRF_RTC0->EVENTS_COMPARE[0] = 0; |
| |
| ticker_trigger(0); |
| } |
| |
| /* On compare1 run ticker worker instance1 */ |
| if (compare1) { |
| NRF_RTC0->EVENTS_COMPARE[1] = 0; |
| |
| ticker_trigger(1); |
| } |
| |
| mayfly_run(MAYFLY_CALL_ID_0); |
| } |
| |
| static void rng_nrf5_isr(void *arg) |
| { |
| isr_rand(arg); |
| } |
| |
| static void swi4_nrf5_isr(void *arg) |
| { |
| mayfly_run(MAYFLY_CALL_ID_1); |
| } |
| |
| static void prio_recv_thread(void *p1, void *p2, void *p3) |
| { |
| while (1) { |
| struct radio_pdu_node_rx *node_rx; |
| struct net_buf *buf; |
| uint8_t num_cmplt; |
| uint16_t handle; |
| |
| while ((num_cmplt = radio_rx_get(&node_rx, &handle))) { |
| |
| buf = bt_buf_get_rx(K_FOREVER); |
| bt_buf_set_type(buf, BT_BUF_EVT); |
| hci_num_cmplt_encode(buf, handle, num_cmplt); |
| BT_DBG("Num Complete: 0x%04x:%u", handle, num_cmplt); |
| bt_recv_prio(buf); |
| |
| k_yield(); |
| } |
| |
| if (node_rx) { |
| |
| radio_rx_dequeue(); |
| |
| BT_DBG("RX node enqueue"); |
| k_fifo_put(&recv_fifo, node_rx); |
| |
| continue; |
| } |
| |
| BT_DBG("sem take..."); |
| k_sem_take(&sem_prio_recv, K_FOREVER); |
| BT_DBG("sem taken"); |
| |
| stack_analyze("prio recv thread stack", prio_recv_thread_stack, |
| sizeof(prio_recv_thread_stack)); |
| } |
| } |
| |
| static void recv_thread(void *p1, void *p2, void *p3) |
| { |
| while (1) { |
| struct radio_pdu_node_rx *node_rx; |
| struct pdu_data *pdu_data; |
| struct net_buf *buf; |
| |
| BT_DBG("RX node get"); |
| node_rx = k_fifo_get(&recv_fifo, K_FOREVER); |
| BT_DBG("RX node dequeued"); |
| |
| pdu_data = (void *)node_rx->pdu_data; |
| /* Check if we need to generate an HCI event or ACL |
| * data |
| */ |
| if (node_rx->hdr.type != NODE_RX_TYPE_DC_PDU || |
| pdu_data->ll_id == PDU_DATA_LLID_CTRL) { |
| /* generate a (non-priority) HCI event */ |
| if (hci_evt_is_discardable(node_rx)) { |
| buf = bt_buf_get_rx(K_NO_WAIT); |
| } else { |
| buf = bt_buf_get_rx(K_FOREVER); |
| } |
| |
| if (buf) { |
| bt_buf_set_type(buf, BT_BUF_EVT); |
| hci_evt_encode(node_rx, buf); |
| } |
| } else { |
| /* generate ACL data */ |
| buf = bt_buf_get_rx(K_FOREVER); |
| bt_buf_set_type(buf, BT_BUF_ACL_IN); |
| hci_acl_encode(node_rx, buf); |
| } |
| |
| radio_rx_fc_set(node_rx->hdr.handle, 0); |
| node_rx->hdr.onion.next = 0; |
| radio_rx_mem_release(&node_rx); |
| |
| if (buf) { |
| if (buf->len) { |
| BT_DBG("Packet in: type:%u len:%u", |
| bt_buf_get_type(buf), buf->len); |
| bt_recv(buf); |
| } else { |
| net_buf_unref(buf); |
| } |
| } |
| |
| k_yield(); |
| |
| stack_analyze("recv thread stack", recv_thread_stack, |
| sizeof(recv_thread_stack)); |
| } |
| } |
| |
| static int cmd_handle(struct net_buf *buf) |
| { |
| struct net_buf *evt; |
| |
| evt = hci_cmd_handle(buf); |
| if (!evt) { |
| return -EINVAL; |
| } |
| |
| BT_DBG("Replying with event of %u bytes", evt->len); |
| bt_recv_prio(evt); |
| |
| return 0; |
| } |
| |
| static int hci_driver_send(struct net_buf *buf) |
| { |
| uint8_t type; |
| int err; |
| |
| BT_DBG("enter"); |
| |
| if (!buf->len) { |
| BT_ERR("Empty HCI packet"); |
| return -EINVAL; |
| } |
| |
| type = bt_buf_get_type(buf); |
| switch (type) { |
| case BT_BUF_ACL_OUT: |
| err = hci_acl_handle(buf); |
| break; |
| case BT_BUF_CMD: |
| err = cmd_handle(buf); |
| break; |
| default: |
| BT_ERR("Unknown HCI type %u", type); |
| return -EINVAL; |
| } |
| |
| if (!err) { |
| net_buf_unref(buf); |
| } |
| |
| BT_DBG("exit: %d", err); |
| |
| return err; |
| } |
| |
| static int hci_driver_open(void) |
| { |
| struct device *clk_k32; |
| struct device *clk_m16; |
| uint32_t err; |
| |
| DEBUG_INIT(); |
| |
| /* TODO: bind and use RNG driver */ |
| rand_init(_rand_context, sizeof(_rand_context)); |
| |
| clk_k32 = device_get_binding(CONFIG_CLOCK_CONTROL_NRF5_K32SRC_DRV_NAME); |
| if (!clk_k32) { |
| return -ENODEV; |
| } |
| |
| clock_control_on(clk_k32, (void *)CLOCK_CONTROL_NRF5_K32SRC); |
| |
| /* TODO: bind and use counter driver */ |
| cntr_init(); |
| |
| mayfly_init(); |
| |
| _ticker_users[MAYFLY_CALL_ID_0][0] = RADIO_TICKER_USER_WORKER_OPS; |
| _ticker_users[MAYFLY_CALL_ID_1][0] = RADIO_TICKER_USER_JOB_OPS; |
| _ticker_users[MAYFLY_CALL_ID_2][0] = 0; |
| _ticker_users[MAYFLY_CALL_ID_PROGRAM][0] = RADIO_TICKER_USER_APP_OPS; |
| |
| ticker_init(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_NODES, |
| &_ticker_nodes[0], MAYFLY_CALLER_COUNT, &_ticker_users[0], |
| RADIO_TICKER_USER_OPS, &_ticker_user_ops[0]); |
| |
| clk_m16 = device_get_binding(CONFIG_CLOCK_CONTROL_NRF5_M16SRC_DRV_NAME); |
| if (!clk_m16) { |
| return -ENODEV; |
| } |
| |
| err = radio_init(clk_m16, CLOCK_CONTROL_NRF5_K32SRC_ACCURACY, |
| RADIO_CONNECTION_CONTEXT_MAX, |
| RADIO_PACKET_COUNT_RX_MAX, |
| RADIO_PACKET_COUNT_TX_MAX, |
| RADIO_LL_LENGTH_OCTETS_RX_MAX, |
| RADIO_PACKET_TX_DATA_SIZE, &_radio[0], sizeof(_radio)); |
| if (err) { |
| BT_ERR("Required RAM size: %d, supplied: %u.", err, |
| sizeof(_radio)); |
| return -ENOMEM; |
| } |
| |
| IRQ_CONNECT(NRF5_IRQ_RADIO_IRQn, 0, radio_nrf5_isr, 0, 0); |
| IRQ_CONNECT(NRF5_IRQ_RTC0_IRQn, 0, rtc0_nrf5_isr, 0, 0); |
| IRQ_CONNECT(NRF5_IRQ_RNG_IRQn, 1, rng_nrf5_isr, 0, 0); |
| IRQ_CONNECT(NRF5_IRQ_SWI4_IRQn, 0, swi4_nrf5_isr, 0, 0); |
| irq_enable(NRF5_IRQ_RADIO_IRQn); |
| irq_enable(NRF5_IRQ_RTC0_IRQn); |
| irq_enable(NRF5_IRQ_RNG_IRQn); |
| irq_enable(NRF5_IRQ_SWI4_IRQn); |
| |
| k_thread_spawn(prio_recv_thread_stack, sizeof(prio_recv_thread_stack), |
| prio_recv_thread, NULL, NULL, NULL, K_PRIO_COOP(6), 0, |
| K_NO_WAIT); |
| |
| k_thread_spawn(recv_thread_stack, sizeof(recv_thread_stack), |
| recv_thread, NULL, NULL, NULL, K_PRIO_COOP(7), 0, |
| K_NO_WAIT); |
| |
| BT_DBG("Success."); |
| |
| return 0; |
| } |
| |
| static struct bt_hci_driver drv = { |
| .name = "Controller", |
| .bus = BT_HCI_DRIVER_BUS_VIRTUAL, |
| .open = hci_driver_open, |
| .send = hci_driver_send, |
| }; |
| |
| static int _hci_driver_init(struct device *unused) |
| { |
| ARG_UNUSED(unused); |
| |
| bt_hci_driver_register(&drv); |
| |
| return 0; |
| } |
| |
| SYS_INIT(_hci_driver_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); |