/* * Copyright (c) 2016 Nordic Semiconductor ASA * Copyright (c) 2016 Vinayak Kariappa Chettimada * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "util/defines.h" #include "util/work.h" #include "hal/clock.h" #include "hal/rand.h" #include "hal/ccm.h" #include "hal/radio.h" #include "ll/ticker.h" #include "ll/ctrl_internal.h" #include "hci/hci.h" #include "hal/debug.h" #if !defined(CONFIG_BLUETOOTH_DEBUG_DRIVER) #undef BT_DBG #define BT_DBG(fmt, ...) #endif #define HCI_CMD 0x01 #define HCI_ACL 0x02 #define HCI_SCO 0x03 #define HCI_EVT 0x04 static uint8_t ALIGNED(4) _rand_context[3 + 4 + 1]; static uint8_t ALIGNED(4) _ticker_nodes[RADIO_TICKER_NODES][TICKER_NODE_T_SIZE]; static uint8_t ALIGNED(4) _ticker_users[RADIO_TICKER_USERS][TICKER_USER_T_SIZE]; static uint8_t ALIGNED(4) _ticker_user_ops[RADIO_TICKER_USER_OPS] [TICKER_USER_OP_T_SIZE]; static uint8_t ALIGNED(4) _radio[LL_MEM_TOTAL]; static struct nano_sem nano_sem_native_recv; static BT_STACK_NOINIT(native_recv_fiber_stack, CONFIG_BLUETOOTH_CONTROLLER_RX_STACK_SIZE); void radio_active_callback(uint8_t active) { } void radio_event_callback(void) { nano_isr_sem_give(&nano_sem_native_recv); } static void power_clock_nrf5_isr(void *arg) { power_clock_isr(); } static void radio_nrf5_isr(void *arg) { radio_isr(); } 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); } work_run(RTC0_IRQn); } static void rng_nrf5_isr(void *arg) { rng_isr(); } static void swi4_nrf5_isr(void *arg) { work_run(NRF52_IRQ_SWI4_EGU4_IRQn); } static void swi5_nrf5_isr(void *arg) { work_run(NRF52_IRQ_SWI5_EGU5_IRQn); } static struct net_buf *native_evt_recv(uint8_t *remaining, uint8_t **in) { struct bt_hci_evt_hdr hdr; struct net_buf *buf; /* TODO: check available length */ memcpy(&hdr, *in, sizeof(hdr)); *in += sizeof(hdr); *remaining = hdr.len; buf = bt_buf_get_evt(0); if (buf) { memcpy(net_buf_add(buf, sizeof(hdr)), &hdr, sizeof(hdr)); } else { BT_ERR("No available event buffers!"); } BT_DBG("len %u", hdr.len); return buf; } static struct net_buf *native_acl_recv(uint8_t *remaining, uint8_t **in) { struct bt_hci_acl_hdr hdr; struct net_buf *buf; /* TODO: check available length */ memcpy(&hdr, *in, sizeof(hdr)); *in += sizeof(hdr); buf = bt_buf_get_acl(); if (buf) { memcpy(net_buf_add(buf, sizeof(hdr)), &hdr, sizeof(hdr)); } else { BT_ERR("No available ACL buffers!"); } *remaining = sys_le16_to_cpu(hdr.len); BT_DBG("len %u", *remaining); return buf; } static int native_recv(uint8_t remaining, uint8_t *in) { struct net_buf *buf; uint8_t type; type = *in++; switch (type) { case HCI_EVT: buf = native_evt_recv(&remaining, &in); break; case HCI_ACL: buf = native_acl_recv(&remaining, &in); break; default: BT_ERR("Unknown HCI type %u", type); return -EINVAL; } BT_DBG("remaining %u bytes", remaining); if (buf && remaining > net_buf_tailroom(buf)) { BT_ERR("Not enough space in buffer"); net_buf_unref(buf); buf = NULL; } if (buf) { memcpy(net_buf_tail(buf), in, remaining); buf->len += remaining; BT_DBG("bt_recv"); bt_recv(buf); } return 0; } static void native_recv_fiber(int unused0, int unused1) { while (1) { uint16_t handle; uint8_t num_cmplt; struct radio_pdu_node_rx *radio_pdu_node_rx; while ((num_cmplt = radio_rx_get(&radio_pdu_node_rx, &handle))) { uint8_t len; uint8_t *buf; int retval; hci_encode_num_cmplt(handle, num_cmplt, &len, &buf); BT_ASSERT(len); retval = native_recv(len, buf); BT_ASSERT(!retval); fiber_yield(); } if (radio_pdu_node_rx) { uint8_t len; uint8_t *buf; int retval; hci_encode((uint8_t *)radio_pdu_node_rx, &len, &buf); /* Not all radio_rx_get are translated to HCI!, * hence just dequeue. */ if (len) { retval = native_recv(len, buf); BT_ASSERT(!retval); } radio_rx_dequeue(); radio_rx_fc_set(radio_pdu_node_rx->hdr.handle, 0); radio_pdu_node_rx->hdr.onion.next = 0; radio_rx_mem_release(&radio_pdu_node_rx); fiber_yield(); } else { nano_fiber_sem_take(&nano_sem_native_recv, TICKS_UNLIMITED); } stack_analyze("native recv fiber stack", native_recv_fiber_stack, sizeof(native_recv_fiber_stack)); } } static int native_send(struct net_buf *buf) { extern void hci_handle(uint8_t x, uint8_t *len, uint8_t **out); uint8_t type; uint8_t remaining; uint8_t *in; BT_DBG("enter"); remaining = 0; type = bt_buf_get_type(buf); switch (type) { case BT_BUF_ACL_OUT: hci_handle(HCI_ACL, &remaining, &in); break; case BT_BUF_CMD: hci_handle(HCI_CMD, &remaining, &in); break; default: BT_ERR("Unknown HCI type %u", type); return -EINVAL; } if (remaining || !buf->len) { BT_ERR("Empty or Len greater than expected"); return -EINVAL; } if (buf->len) { while (buf->len - 1) { hci_handle(net_buf_pull_u8(buf), &remaining, &in); } if (remaining) { BT_ERR("Len greater than expected"); return -EINVAL; } hci_handle(net_buf_pull_u8(buf), &remaining, &in); BT_DBG("hci_handle returned %u bytes", remaining); if (remaining) { int retval; retval = native_recv(remaining, in); if (retval) { return retval; } } } net_buf_unref(buf); BT_DBG("exit"); return 0; } static int native_open(void) { uint32_t retval; clock_k32src_start(1); _ticker_users[RADIO_TICKER_USER_ID_WORKER][0] = RADIO_TICKER_USER_WORKER_OPS; _ticker_users[RADIO_TICKER_USER_ID_JOB][0] = RADIO_TICKER_USER_JOB_OPS; _ticker_users[RADIO_TICKER_USER_ID_APP][0] = RADIO_TICKER_USER_APP_OPS; ticker_init(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_NODES, &_ticker_nodes[0] , RADIO_TICKER_USERS, &_ticker_users[0] , RADIO_TICKER_USER_OPS, &_ticker_user_ops[0] ); rand_init(_rand_context, sizeof(_rand_context)); retval = radio_init(7, /* 20ppm = 7 ... 250ppm = 1, 500ppm = 0 */ RADIO_CONNECTION_CONTEXT_MAX, RADIO_PACKET_COUNT_RX_MAX, RADIO_PACKET_COUNT_TX_MAX, RADIO_LL_LENGTH_OCTETS_RX_MAX, &_radio[0], sizeof(_radio) ); if (retval) { BT_ERR("Required RAM size: %d, supplied: %u.", retval, sizeof(_radio)); return -ENOMEM; } IRQ_CONNECT(NRF52_IRQ_POWER_CLOCK_IRQn, 2, power_clock_nrf5_isr, 0, 0); IRQ_CONNECT(NRF52_IRQ_RADIO_IRQn, 0, radio_nrf5_isr, 0, 0); IRQ_CONNECT(NRF52_IRQ_RTC0_IRQn, 0, rtc0_nrf5_isr, 0, 0); IRQ_CONNECT(NRF52_IRQ_RNG_IRQn, 2, rng_nrf5_isr, 0, 0); IRQ_CONNECT(NRF52_IRQ_SWI4_EGU4_IRQn, 0, swi4_nrf5_isr, 0, 0); IRQ_CONNECT(NRF52_IRQ_SWI5_EGU5_IRQn, 2, swi5_nrf5_isr, 0, 0); irq_enable(NRF52_IRQ_POWER_CLOCK_IRQn); irq_enable(NRF52_IRQ_RADIO_IRQn); irq_enable(NRF52_IRQ_RTC0_IRQn); irq_enable(NRF52_IRQ_RNG_IRQn); irq_enable(NRF52_IRQ_SWI4_EGU4_IRQn); irq_enable(NRF52_IRQ_SWI5_EGU5_IRQn); nano_sem_init(&nano_sem_native_recv); fiber_start(native_recv_fiber_stack, sizeof(native_recv_fiber_stack), (nano_fiber_entry_t)native_recv_fiber, 0, 0, 7, 0); BT_DBG("Success."); return 0; } static struct bt_driver drv = { .name = "Controller", .bus = BT_DRIVER_BUS_VIRTUAL, .open = native_open, .send = native_send, }; static int _native_init(struct device *unused) { ARG_UNUSED(unused); bt_driver_register(&drv); return 0; } SYS_INIT(_native_init, NANOKERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);