|  | /* | 
|  | * 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 <atomic.h> | 
|  |  | 
|  | #include <misc/util.h> | 
|  | #include <misc/stack.h> | 
|  | #include <misc/byteorder.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 | 
|  |  | 
|  | #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) | 
|  | #include "common/log.h" | 
|  |  | 
|  | #include "util/util.h" | 
|  | #include "hal/ccm.h" | 
|  | #include "hal/radio.h" | 
|  | #include "ll_sw/pdu.h" | 
|  | #include "ll_sw/ctrl.h" | 
|  | #include "ll.h" | 
|  | #include "hci_internal.h" | 
|  |  | 
|  | #include "hal/debug.h" | 
|  |  | 
|  | #define NODE_RX(_node) CONTAINER_OF(_node, struct radio_pdu_node_rx, \ | 
|  | hdr.onion.node) | 
|  |  | 
|  | static K_SEM_DEFINE(sem_prio_recv, 0, UINT_MAX); | 
|  | static K_FIFO_DEFINE(recv_fifo); | 
|  |  | 
|  | struct k_thread prio_recv_thread_data; | 
|  | static BT_STACK_NOINIT(prio_recv_thread_stack, | 
|  | CONFIG_BT_CTLR_RX_PRIO_STACK_SIZE); | 
|  | struct k_thread recv_thread_data; | 
|  | static BT_STACK_NOINIT(recv_thread_stack, CONFIG_BT_RX_STACK_SIZE); | 
|  |  | 
|  | #if defined(CONFIG_INIT_STACKS) | 
|  | static u32_t prio_ts; | 
|  | static u32_t rx_ts; | 
|  | #endif | 
|  |  | 
|  | #if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) | 
|  | static struct k_poll_signal hbuf_signal = | 
|  | K_POLL_SIGNAL_INITIALIZER(hbuf_signal); | 
|  | static sys_slist_t hbuf_pend; | 
|  | static s32_t hbuf_count; | 
|  | #endif | 
|  |  | 
|  | static void prio_recv_thread(void *p1, void *p2, void *p3) | 
|  | { | 
|  | while (1) { | 
|  | void *node_rx; | 
|  | u8_t num_cmplt; | 
|  | u16_t handle; | 
|  |  | 
|  | while ((num_cmplt = ll_rx_get(&node_rx, &handle))) { | 
|  | #if defined(CONFIG_BT_CONN) | 
|  | struct net_buf *buf; | 
|  |  | 
|  | buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER); | 
|  | hci_num_cmplt_encode(buf, handle, num_cmplt); | 
|  | BT_DBG("Num Complete: 0x%04x:%u", handle, num_cmplt); | 
|  | bt_recv_prio(buf); | 
|  | k_yield(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | if (node_rx) { | 
|  |  | 
|  | ll_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"); | 
|  |  | 
|  | #if defined(CONFIG_INIT_STACKS) | 
|  | if (k_uptime_get_32() - prio_ts > K_SECONDS(5)) { | 
|  | STACK_ANALYZE("prio recv thread stack", | 
|  | prio_recv_thread_stack); | 
|  | prio_ts = k_uptime_get_32(); | 
|  | } | 
|  | #endif | 
|  | } | 
|  | } | 
|  |  | 
|  | static inline struct net_buf *encode_node(struct radio_pdu_node_rx *node_rx, | 
|  | s8_t class) | 
|  | { | 
|  | struct net_buf *buf = NULL; | 
|  |  | 
|  | /* Check if we need to generate an HCI event or ACL data */ | 
|  | switch (class) { | 
|  | case HCI_CLASS_EVT_DISCARDABLE: | 
|  | case HCI_CLASS_EVT_REQUIRED: | 
|  | case HCI_CLASS_EVT_CONNECTION: | 
|  | if (class == HCI_CLASS_EVT_DISCARDABLE) { | 
|  | buf = bt_buf_get_rx(BT_BUF_EVT, K_NO_WAIT); | 
|  | } else { | 
|  | buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER); | 
|  | } | 
|  | if (buf) { | 
|  | hci_evt_encode(node_rx, buf); | 
|  | } | 
|  | break; | 
|  | #if defined(CONFIG_BT_CONN) | 
|  | case HCI_CLASS_ACL_DATA: | 
|  | /* generate ACL data */ | 
|  | buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_FOREVER); | 
|  | hci_acl_encode(node_rx, buf); | 
|  | break; | 
|  | #endif | 
|  | default: | 
|  | LL_ASSERT(0); | 
|  | break; | 
|  | } | 
|  |  | 
|  | #if defined(CONFIG_BT_LL_SW) | 
|  | radio_rx_fc_set(node_rx->hdr.handle, 0); | 
|  | #endif /* CONFIG_BT_LL_SW */ | 
|  |  | 
|  | node_rx->hdr.onion.next = 0; | 
|  | ll_rx_mem_release((void **)&node_rx); | 
|  |  | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | static inline struct net_buf *process_node(struct radio_pdu_node_rx *node_rx) | 
|  | { | 
|  | s8_t class = hci_get_class(node_rx); | 
|  | struct net_buf *buf = NULL; | 
|  |  | 
|  | #if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) | 
|  | if (hbuf_count != -1) { | 
|  | bool pend = !sys_slist_is_empty(&hbuf_pend); | 
|  |  | 
|  | /* controller to host flow control enabled */ | 
|  | switch (class) { | 
|  | case HCI_CLASS_EVT_DISCARDABLE: | 
|  | case HCI_CLASS_EVT_REQUIRED: | 
|  | break; | 
|  | case HCI_CLASS_EVT_CONNECTION: | 
|  | /* for conn-related events, only pend is relevant */ | 
|  | hbuf_count = 1; | 
|  | /* fallthrough */ | 
|  | case HCI_CLASS_ACL_DATA: | 
|  | if (pend || !hbuf_count) { | 
|  | sys_slist_append(&hbuf_pend, | 
|  | &node_rx->hdr.onion.node); | 
|  | BT_DBG("FC: Queuing item: %d", class); | 
|  | return NULL; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | LL_ASSERT(0); | 
|  | break; | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* process regular node from radio */ | 
|  | buf = encode_node(node_rx, class); | 
|  |  | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | #if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) | 
|  | static inline struct net_buf *process_hbuf(struct radio_pdu_node_rx *n) | 
|  | { | 
|  | /* shadow total count in case of preemption */ | 
|  | struct radio_pdu_node_rx *node_rx = NULL; | 
|  | s32_t hbuf_total = hci_hbuf_total; | 
|  | struct net_buf *buf = NULL; | 
|  | sys_snode_t *node = NULL; | 
|  | s8_t class; | 
|  | int reset; | 
|  |  | 
|  | reset = atomic_test_and_clear_bit(&hci_state_mask, HCI_STATE_BIT_RESET); | 
|  | if (reset) { | 
|  | /* flush queue, no need to free, the LL has already done it */ | 
|  | sys_slist_init(&hbuf_pend); | 
|  | } | 
|  |  | 
|  | if (hbuf_total <= 0) { | 
|  | hbuf_count = -1; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* available host buffers */ | 
|  | hbuf_count = hbuf_total - (hci_hbuf_sent - hci_hbuf_acked); | 
|  |  | 
|  | /* host acked ACL packets, try to dequeue from hbuf */ | 
|  | node = sys_slist_peek_head(&hbuf_pend); | 
|  | if (!node) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Return early if this iteration already has a node to process */ | 
|  | node_rx = NODE_RX(node); | 
|  | class = hci_get_class(node_rx); | 
|  | if (n) { | 
|  | if (class == HCI_CLASS_EVT_CONNECTION || | 
|  | (class == HCI_CLASS_ACL_DATA && hbuf_count)) { | 
|  | /* node to process later, schedule an iteration */ | 
|  | BT_DBG("FC: signalling"); | 
|  | k_poll_signal(&hbuf_signal, 0x0); | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | switch (class) { | 
|  | case HCI_CLASS_EVT_CONNECTION: | 
|  | BT_DBG("FC: dequeueing event"); | 
|  | (void) sys_slist_get(&hbuf_pend); | 
|  | break; | 
|  | case HCI_CLASS_ACL_DATA: | 
|  | if (hbuf_count) { | 
|  | BT_DBG("FC: dequeueing ACL data"); | 
|  | (void) sys_slist_get(&hbuf_pend); | 
|  | } else { | 
|  | /* no buffers, HCI will signal */ | 
|  | node = NULL; | 
|  | } | 
|  | break; | 
|  | case HCI_CLASS_EVT_DISCARDABLE: | 
|  | case HCI_CLASS_EVT_REQUIRED: | 
|  | default: | 
|  | LL_ASSERT(0); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (node) { | 
|  | buf = encode_node(node_rx, class); | 
|  | /* Update host buffers after encoding */ | 
|  | hbuf_count = hbuf_total - (hci_hbuf_sent - hci_hbuf_acked); | 
|  | /* next node */ | 
|  | node = sys_slist_peek_head(&hbuf_pend); | 
|  | if (node) { | 
|  | node_rx = NODE_RX(node); | 
|  | class = hci_get_class(node_rx); | 
|  |  | 
|  | if (class == HCI_CLASS_EVT_CONNECTION || | 
|  | (class == HCI_CLASS_ACL_DATA && hbuf_count)) { | 
|  | /* more to process, schedule an | 
|  | * iteration | 
|  | */ | 
|  | BT_DBG("FC: signalling"); | 
|  | k_poll_signal(&hbuf_signal, 0x0); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return buf; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void recv_thread(void *p1, void *p2, void *p3) | 
|  | { | 
|  | #if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) | 
|  | /* @todo: check if the events structure really needs to be static */ | 
|  | static struct k_poll_event events[2] = { | 
|  | K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_SIGNAL, | 
|  | K_POLL_MODE_NOTIFY_ONLY, | 
|  | &hbuf_signal, 0), | 
|  | K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE, | 
|  | K_POLL_MODE_NOTIFY_ONLY, | 
|  | &recv_fifo, 0), | 
|  | }; | 
|  | #endif | 
|  |  | 
|  | while (1) { | 
|  | struct radio_pdu_node_rx *node_rx = NULL; | 
|  | struct net_buf *buf = NULL; | 
|  |  | 
|  | BT_DBG("blocking"); | 
|  | #if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) | 
|  | int err; | 
|  |  | 
|  | err = k_poll(events, 2, K_FOREVER); | 
|  | LL_ASSERT(err == 0); | 
|  | if (events[0].state == K_POLL_STATE_SIGNALED) { | 
|  | events[0].signal->signaled = 0; | 
|  | } else if (events[1].state == | 
|  | K_POLL_STATE_FIFO_DATA_AVAILABLE) { | 
|  | node_rx = k_fifo_get(events[1].fifo, 0); | 
|  | } | 
|  |  | 
|  | events[0].state = K_POLL_STATE_NOT_READY; | 
|  | events[1].state = K_POLL_STATE_NOT_READY; | 
|  |  | 
|  | /* process host buffers first if any */ | 
|  | buf = process_hbuf(node_rx); | 
|  |  | 
|  | #else | 
|  | node_rx = k_fifo_get(&recv_fifo, K_FOREVER); | 
|  | #endif | 
|  | BT_DBG("unblocked"); | 
|  |  | 
|  | if (node_rx && !buf) { | 
|  | /* process regular node from radio */ | 
|  | buf = process_node(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(); | 
|  |  | 
|  | #if defined(CONFIG_INIT_STACKS) | 
|  | if (k_uptime_get_32() - rx_ts > K_SECONDS(5)) { | 
|  | STACK_ANALYZE("recv thread stack", recv_thread_stack); | 
|  | rx_ts = k_uptime_get_32(); | 
|  | } | 
|  | #endif | 
|  | } | 
|  | } | 
|  |  | 
|  | static int cmd_handle(struct net_buf *buf) | 
|  | { | 
|  | struct net_buf *evt; | 
|  |  | 
|  | evt = hci_cmd_handle(buf); | 
|  | if (evt) { | 
|  | BT_DBG("Replying with event of %u bytes", evt->len); | 
|  | bt_recv_prio(evt); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #if defined(CONFIG_BT_CONN) | 
|  | static int acl_handle(struct net_buf *buf) | 
|  | { | 
|  | struct net_buf *evt; | 
|  | int err; | 
|  |  | 
|  | err = hci_acl_handle(buf, &evt); | 
|  | if (evt) { | 
|  | BT_DBG("Replying with event of %u bytes", evt->len); | 
|  | bt_recv_prio(evt); | 
|  | } | 
|  |  | 
|  | return err; | 
|  | } | 
|  | #endif /* CONFIG_BT_CONN */ | 
|  |  | 
|  | static int hci_driver_send(struct net_buf *buf) | 
|  | { | 
|  | u8_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) { | 
|  | #if defined(CONFIG_BT_CONN) | 
|  | case BT_BUF_ACL_OUT: | 
|  | err = acl_handle(buf); | 
|  | break; | 
|  | #endif /* CONFIG_BT_CONN */ | 
|  | 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) | 
|  | { | 
|  | u32_t err; | 
|  |  | 
|  | DEBUG_INIT(); | 
|  |  | 
|  | err = ll_init(&sem_prio_recv); | 
|  | if (err) { | 
|  | BT_ERR("LL initialization failed: %u", err); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | #if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) | 
|  | hci_init(&hbuf_signal); | 
|  | #else | 
|  | hci_init(NULL); | 
|  | #endif | 
|  |  | 
|  | k_thread_create(&prio_recv_thread_data, prio_recv_thread_stack, | 
|  | K_THREAD_STACK_SIZEOF(prio_recv_thread_stack), | 
|  | prio_recv_thread, NULL, NULL, NULL, | 
|  | K_PRIO_COOP(CONFIG_BT_CTLR_RX_PRIO), 0, K_NO_WAIT); | 
|  |  | 
|  | k_thread_create(&recv_thread_data, recv_thread_stack, | 
|  | K_THREAD_STACK_SIZEOF(recv_thread_stack), | 
|  | recv_thread, NULL, NULL, NULL, | 
|  | K_PRIO_COOP(CONFIG_BT_RX_PRIO), 0, K_NO_WAIT); | 
|  |  | 
|  | BT_DBG("Success."); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const 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); |