Bluetooth: Controller: Add BLE controller driver
The main.c source file in drivers/bluetooth/controller acts as the necessary glue between the Link Layer and Zephyr's Bluetooth subsystem. It instantiates the required RX fiber and marshalls the control and data traffic between the BLE radio and the BLE stack. Jira: ZEP-702 Origin: Original Change-Id: Ia62baedcd6e3ea83bd16306779357930f9a6c5f7 Signed-off-by: Vinayak Chettimada <vinayak.kariappa.chettimada@nordicsemi.no> Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
This commit is contained in:
parent
48c48711e6
commit
5c01a7e3b3
2 changed files with 392 additions and 0 deletions
391
drivers/bluetooth/controller/main.c
Normal file
391
drivers/bluetooth/controller/main.c
Normal file
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
(void)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.", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
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);
|
Loading…
Add table
Add a link
Reference in a new issue