Controller's fiber is expected to read all enqueued events and data, hence wait on semaphore only after all events and data are read. Change-id: I0150f042b0ba91efa712b38903752b20198e5e6e Signed-off-by: Vinayak Chettimada <vinayak.kariappa.chettimada@nordicsemi.no>
394 lines
8.2 KiB
C
394 lines
8.2 KiB
C
/*
|
|
* 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 <errno.h>
|
|
#include <stddef.h>
|
|
|
|
#include <nanokernel.h>
|
|
#include <arch/cpu.h>
|
|
|
|
#include <board.h>
|
|
#include <init.h>
|
|
#include <uart.h>
|
|
#include <misc/util.h>
|
|
#include <misc/byteorder.h>
|
|
#include <string.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/log.h>
|
|
#include <bluetooth/hci.h>
|
|
#include <bluetooth/driver.h>
|
|
|
|
#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_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);
|
|
ASSERT(len);
|
|
|
|
retval = native_recv(len, buf);
|
|
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);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|