Merge "Merge bluetooth branch to master"

This commit is contained in:
Anas Nashif 2016-09-08 02:26:59 +00:00
commit ce52e3988f
113 changed files with 19980 additions and 861 deletions

View file

@ -91,6 +91,12 @@ F: drivers/bluetooth/
F: samples/bluetooth/
F: tests/bluetooth/
BLUETOOTH CONTROLLER
M: Vinayak Chettimada <vinayak.kariappa.chettimada@nordicsemi.no>
M: Carles Cufi <carles.cufi@nordicsemi.no>
S: Supported
F: drivers/bluetooth/controller/
DOCUMENTATION
M: Kinder, David <david.b.kinder@intel.com>
M: Perez-Gonzalez, Inaky <inaky.perez-gonzalez@intel.com>

View file

@ -24,8 +24,7 @@
#ifndef _ASMLANGUAGE
#include "soc_irq.h"
#include <nrf52.h>
#include <nrf52_bitfields.h>
#include <nrf.h>
#include <device.h>
#include <misc/util.h>
#include <drivers/rand32.h>

View file

@ -20,3 +20,7 @@ CONFIG_UART_CONSOLE=y
# additional board options
CONFIG_GPIO_AS_PINRESET=y
# bluetooth
CONFIG_BLUETOOTH=y
CONFIG_BLUETOOTH_CONTROLLER=y

View file

@ -18,3 +18,7 @@ CONFIG_UART_CONSOLE=y
# additional board options
CONFIG_GPIO_AS_PINRESET=y
# bluetooth
CONFIG_BLUETOOTH=y
CONFIG_BLUETOOTH_CONTROLLER=y

View file

@ -50,6 +50,13 @@ Logical Link Control and Adaptation Protocol (L2CAP)
:project: Zephyr
:content-only:
Serial Port Emulation (RFCOMM)
******************************
.. doxygengroup:: bt_rfcomm
:project: Zephyr
:content-only:
Data Buffers
************
@ -70,3 +77,15 @@ HCI Drivers
.. doxygengroup:: bt_driver
:project: Zephyr
:content-only:
HCI RAW channel
***************
HCI RAW channel API is intended to expose HCI interface to the remote entity.
The local Bluetooth controller gets owned by the remote entity and host
Bluetooth stack is not used. RAW API provides direct access to packets which
are sent and received by the Bluetooth HCI driver.
.. doxygengroup:: hci_raw
:project: Zephyr
:content-only:

View file

@ -24,7 +24,7 @@ if BLUETOOTH
menu "Bluetooth Drivers"
if BLUETOOTH_STACK_HCI
if BLUETOOTH_STACK_HCI || BLUETOOTH_STACK_HCI_RAW
comment "Bluetooth HCI Driver Options"
@ -58,6 +58,12 @@ config BLUETOOTH_H5
Bluetooth three-wire (H:5) UART driver. Implementation of HCI
Three-Wire UART Transport Layer.
config BLUETOOTH_CONTROLLER
bool "Controller"
select BLUETOOTH_HOST_BUFFERS
help
Enables support for SoC native controller implementation.
config BLUETOOTH_NO_DRIVER
bool "No default HCI driver"
help
@ -80,7 +86,7 @@ config BLUETOOTH_HOST_BUFFERS
config BLUETOOTH_DEBUG_DRIVER
bool "Bluetooth driver debug"
depends on BLUETOOTH_DEBUG && BLUETOOTH_UART
depends on BLUETOOTH_DEBUG && (BLUETOOTH_UART || BLUETOOTH_CONTROLLER)
default n
help
This option enables debug support for the chosen
@ -119,6 +125,8 @@ endif # BLUETOOTH_STACK_HCI
source "drivers/bluetooth/nble/Kconfig"
source "drivers/bluetooth/controller/Kconfig"
config BLUETOOTH_NRF51_PM
bool "nRF51 Power Management [EXPERIMENTAL]"
depends on BLUETOOTH_H4 || NBLE
@ -128,6 +136,7 @@ config BLUETOOTH_NRF51_PM
config BLUETOOTH_WAIT_NOP
bool "Wait for \"NOP\" Command Complete event during init"
depends on !BLUETOOTH_CONTROLLER
help
Some controllers emit a Command Complete event for the NOP
opcode to indicate that they're ready to receive commands.

View file

@ -2,3 +2,4 @@ obj-$(CONFIG_BLUETOOTH_H4) += h4.o
obj-$(CONFIG_BLUETOOTH_H5) += h5.o
obj-$(CONFIG_NBLE) += nble/
obj-$(CONFIG_BLUETOOTH_NRF51_PM) += nrf51_pm.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += controller/

View file

@ -0,0 +1,39 @@
if BLUETOOTH_CONTROLLER
comment "BLE Controller configuration"
config BLUETOOTH_CONTROLLER_DATA_LENGTH
prompt "Maximum data length supported"
int
default 27
range 27 251
help
Set the maximum data length of PDU supported in the stack.
config BLUETOOTH_CONTROLLER_RX_BUFFERS
prompt "Number of Rx buffers"
int
default 1
range 1 10
help
Set the number of Rx PDUs to be buffered in the controller.
config BLUETOOTH_CONTROLLER_TX_BUFFERS
prompt "Number of Tx buffers"
int
default 1
range 1 10
help
Set the number of Tx PDUs to be queued for transmission
in the controller.
config BLUETOOTH_CONTROLLER_RX_STACK_SIZE
int "Size of the receiving fiber stack"
default 256
range 256 65536
help
Size of the receiving fiber stack. This is the context from
which all radio messages are encoded into HCI events or data
before passing it to Bluetooth receiving fiber.
endif # BLUETOOTH_CONTROLLER

View file

@ -0,0 +1,18 @@
ccflags-$(CONFIG_BLUETOOTH_CONTROLLER) += -I$(srctree)/drivers/bluetooth/controller/util
ccflags-$(CONFIG_BLUETOOTH_CONTROLLER) += -I$(srctree)/drivers/bluetooth/controller/hal
ccflags-$(CONFIG_BLUETOOTH_CONTROLLER) += -I$(srctree)/drivers/bluetooth/controller/ll
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += util/mem.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += util/memq.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += util/work.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += util/util.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/irq.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/clock.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/rtc.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/rand.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/ecb.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/radio.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += ll/ticker.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += ll/ctrl.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += ll/ll.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hci/hci.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += main.o

View file

@ -0,0 +1,31 @@
/*
* 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.
*/
#ifndef _CCM_H_
#define _CCM_H_
#include <toolchain.h>
struct __packed ccm {
uint8_t key[16];
uint64_t counter;
uint8_t direction:1;
uint8_t resv1:7;
uint8_t iv[8];
};
#endif /* _CCM_H_ */

View file

@ -0,0 +1,230 @@
/*
* 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 "nrf.h"
#include "clock.h"
#include "debug.h"
static inline void hal_nop(void)
{
__asm__ volatile ("mov r0,r0");
}
#if defined(__GNUC__)
#define NOP() hal_nop()
#else
#define NOP __nop
#endif
static struct {
uint8_t m16src_refcount;
} clock_instance;
uint32_t clock_m16src_start(uint32_t async)
{
/* if clock is already started then just increment refcount.
* refcount can handle 255 (uint8_t) requests, if the start
* and stop dont happen in pairs, a rollover will be caught
* and system should assert.
*/
if (clock_instance.m16src_refcount++) {
goto hf_start_return;
}
DEBUG_RADIO_XTAL(1);
NRF_CLOCK->TASKS_HFCLKSTOP = 1;
if (!async) {
uint32_t intenset;
NVIC_DisableIRQ(POWER_CLOCK_IRQn);
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
intenset = NRF_CLOCK->INTENSET;
NRF_CLOCK->INTENSET = CLOCK_INTENSET_HFCLKSTARTED_Msk;
NRF_CLOCK->TASKS_HFCLKSTART = 1;
NOP();
NOP();
NOP();
NOP();
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) {
__WFE();
}
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
if (!(intenset & CLOCK_INTENSET_HFCLKSTARTED_Msk)) {
NRF_CLOCK->INTENCLR = CLOCK_INTENCLR_HFCLKSTARTED_Msk;
}
NVIC_ClearPendingIRQ(POWER_CLOCK_IRQn);
NVIC_EnableIRQ(POWER_CLOCK_IRQn);
} else {
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
NRF_CLOCK->TASKS_HFCLKSTART = 1;
NOP();
NOP();
NOP();
NOP();
}
hf_start_return:
/* rollover should not happen as start and stop shall be
* called in pairs.
*/
ASSERT(clock_instance.m16src_refcount);
return (((NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_STATE_Msk)) ? 1 : 0);
}
void clock_m16src_stop(void)
{
ASSERT(clock_instance.m16src_refcount);
if (--clock_instance.m16src_refcount) {
return;
}
DEBUG_RADIO_XTAL(0);
NRF_CLOCK->TASKS_HFCLKSTOP = 1;
}
uint32_t clock_k32src_start(uint32_t src)
{
uint32_t intenset;
if ((NRF_CLOCK->LFCLKSTAT & CLOCK_LFCLKSTAT_STATE_Msk)) {
return 1;
}
NRF_CLOCK->TASKS_LFCLKSTOP = 1;
NVIC_DisableIRQ(POWER_CLOCK_IRQn);
NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
intenset = NRF_CLOCK->INTENSET;
NRF_CLOCK->INTENSET = CLOCK_INTENSET_LFCLKSTARTED_Msk;
NRF_CLOCK->LFCLKSRC = src;
NRF_CLOCK->TASKS_LFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0) {
__WFE();
}
NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
if (!(intenset & CLOCK_INTENSET_LFCLKSTARTED_Msk)) {
NRF_CLOCK->INTENCLR = CLOCK_INTENCLR_LFCLKSTARTED_Msk;
}
NVIC_ClearPendingIRQ(POWER_CLOCK_IRQn);
NVIC_EnableIRQ(POWER_CLOCK_IRQn);
/* Calibrate RC, and start timer for consecutive calibrations */
NRF_CLOCK->TASKS_CTSTOP = 1;
NRF_CLOCK->INTENCLR = CLOCK_INTENCLR_DONE_Msk | CLOCK_INTENCLR_CTTO_Msk;
NRF_CLOCK->EVENTS_DONE = 0;
NRF_CLOCK->EVENTS_CTTO = 0;
if (!src) {
/* Set the Calibration Timer initial value */
NRF_CLOCK->CTIV = 16; /* 4s in 0.25s units */
/* Enable DONE and CTTO IRQs */
NRF_CLOCK->INTENSET =
CLOCK_INTENSET_DONE_Msk | CLOCK_INTENSET_CTTO_Msk;
/* Start HF clock, if already started then explicitly
* assert IRQ
*/
NRF_CLOCK->INTENSET = CLOCK_INTENSET_HFCLKSTARTED_Msk;
if (clock_m16src_start(1)) {
NVIC_ClearPendingIRQ(POWER_CLOCK_IRQn);
}
}
return ((NRF_CLOCK->LFCLKSTAT & CLOCK_LFCLKSTAT_STATE_Msk)) ? 1 : 0;
}
void power_clock_isr(void)
{
uint8_t pof, hf_intenset, hf_stat, hf, lf, done, ctto;
pof = (NRF_POWER->EVENTS_POFWARN != 0);
hf_intenset =
((NRF_CLOCK->INTENSET & CLOCK_INTENSET_HFCLKSTARTED_Msk) != 0);
hf_stat = ((NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_STATE_Msk) != 0);
hf = (NRF_CLOCK->EVENTS_HFCLKSTARTED != 0);
lf = (NRF_CLOCK->EVENTS_LFCLKSTARTED != 0);
done = (NRF_CLOCK->EVENTS_DONE != 0);
ctto = (NRF_CLOCK->EVENTS_CTTO != 0);
ASSERT(pof || hf || lf || done || ctto);
if (pof) {
NRF_POWER->EVENTS_POFWARN = 0;
}
if (hf) {
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
}
if (hf_intenset && hf_stat) {
NRF_CLOCK->INTENCLR = CLOCK_INTENCLR_HFCLKSTARTED_Msk;
/* Start Calibration */
NRF_CLOCK->TASKS_CAL = 1;
}
if (lf) {
NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
ASSERT(0);
}
if (done) {
NRF_CLOCK->EVENTS_DONE = 0;
/* Calibration done, stop 16M Xtal. */
clock_m16src_stop();
/* Start timer for next calibration. */
NRF_CLOCK->TASKS_CTSTART = 1;
}
if (ctto) {
NRF_CLOCK->EVENTS_CTTO = 0;
/* Start HF clock, if already started
* then explicitly assert IRQ
*/
NRF_CLOCK->INTENSET = CLOCK_INTENSET_HFCLKSTARTED_Msk;
if (clock_m16src_start(1)) {
NVIC_ClearPendingIRQ(POWER_CLOCK_IRQn);
}
}
}

View file

@ -0,0 +1,26 @@
/*
* 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.
*/
#ifndef _CLOCK_H_
#define _CLOCK_H_
uint32_t clock_m16src_start(uint32_t async);
void clock_m16src_stop(void);
uint32_t clock_k32src_start(uint32_t src);
void power_clock_isr(void);
#endif

View file

@ -0,0 +1,30 @@
/*
* 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.
*/
#ifndef _CPU_H_
#define _CPU_H_
#include "nrf.h"
static inline void cpu_sleep(void)
{
__WFE();
__SEV();
__WFE();
}
#endif /* _CPU_H_ */

View file

@ -0,0 +1,233 @@
/*
* 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.
*/
#ifndef _DEBUG_H_
#define _DEBUG_H_
#ifdef __GNUC__
#define ASSERT(x) do { \
if (!(x)) { \
__asm__ volatile (".inst 0xde00\n"); \
} \
} while (0)
#else
static __asm void _udf(void)
{
UND #0;
}
#define ASSERT(x) do { \
if (!(x)) { \
_udf(); \
} \
} while (0)
#endif
#if (DEBUG == 1)
/*
* P2 is outgoing debug pins.
* Debug pins are initialized to low.
*/
#define DEBUG_INIT() do { \
NRF_GPIO->DIRSET = 0x03FF0000; \
NRF_GPIO->OUTCLR = 0x03FF0000; } \
while (0)
#define DEBUG_CPU_SLEEP(flag) do { \
if (flag) { \
NRF_GPIO->OUTSET = 0x00010000; \
NRF_GPIO->OUTCLR = 0x00010000; } \
else { \
NRF_GPIO->OUTCLR = 0x00010000; \
NRF_GPIO->OUTSET = 0x00010000; } \
} while (0)
#define DEBUG_TICKER_ISR(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00020000; \
NRF_GPIO->OUTSET = 0x00020000; } \
else { \
NRF_GPIO->OUTSET = 0x00020000; \
NRF_GPIO->OUTCLR = 0x00020000; } \
} while (0)
#define DEBUG_TICKER_TASK(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00020000; \
NRF_GPIO->OUTSET = 0x00020000; } \
else { \
NRF_GPIO->OUTSET = 0x00020000; \
NRF_GPIO->OUTCLR = 0x00020000; } \
} while (0)
#define DEBUG_TICKER_JOB(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00040000; \
NRF_GPIO->OUTSET = 0x00040000; } \
else { \
NRF_GPIO->OUTSET = 0x00040000; \
NRF_GPIO->OUTCLR = 0x00040000; } \
} while (0)
#define DEBUG_RADIO_ISR(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00800000; \
NRF_GPIO->OUTSET = 0x00800000; } \
else { \
NRF_GPIO->OUTSET = 0x00800000; \
NRF_GPIO->OUTCLR = 0x00800000; } \
} while (0)
#define DEBUG_RADIO_XTAL(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x01000000; \
NRF_GPIO->OUTSET = 0x01000000; } \
else { \
NRF_GPIO->OUTSET = 0x01000000; \
NRF_GPIO->OUTCLR = 0x01000000; } \
} while (0)
#define DEBUG_RADIO_ACTIVE(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x02000000; \
NRF_GPIO->OUTSET = 0x02000000; } \
else { \
NRF_GPIO->OUTSET = 0x02000000; \
NRF_GPIO->OUTCLR = 0x02000000; } \
} while (0)
#define DEBUG_RADIO_CLOSE(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00000000; \
NRF_GPIO->OUTSET = 0x00000000; } \
else { \
NRF_GPIO->OUTCLR = 0x00780000; } \
} while (0)
#define DEBUG_RADIO_PREPARE_A(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00080000; \
NRF_GPIO->OUTSET = 0x00080000; } \
else { \
NRF_GPIO->OUTCLR = 0x00080000; \
NRF_GPIO->OUTSET = 0x00080000; } \
} while (0)
#define DEBUG_RADIO_START_A(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00080000; \
NRF_GPIO->OUTSET = 0x00080000; } \
else { \
NRF_GPIO->OUTCLR = 0x00080000; \
NRF_GPIO->OUTSET = 0x00080000; } \
} while (0)
#define DEBUG_RADIO_PREPARE_S(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00100000; \
NRF_GPIO->OUTSET = 0x00100000; } \
else { \
NRF_GPIO->OUTCLR = 0x00100000; \
NRF_GPIO->OUTSET = 0x00100000; } \
} while (0)
#define DEBUG_RADIO_START_S(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00100000; \
NRF_GPIO->OUTSET = 0x00100000; } \
else { \
NRF_GPIO->OUTCLR = 0x00100000; \
NRF_GPIO->OUTSET = 0x00100000; } \
} while (0)
#define DEBUG_RADIO_PREPARE_O(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00200000; \
NRF_GPIO->OUTSET = 0x00200000; } \
else { \
NRF_GPIO->OUTCLR = 0x00200000; \
NRF_GPIO->OUTSET = 0x00200000; } \
} while (0)
#define DEBUG_RADIO_START_O(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00200000; \
NRF_GPIO->OUTSET = 0x00200000; } \
else { \
NRF_GPIO->OUTCLR = 0x00200000; \
NRF_GPIO->OUTSET = 0x00200000; } \
} while (0)
#define DEBUG_RADIO_PREPARE_M(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00400000; \
NRF_GPIO->OUTSET = 0x00400000; } \
else { \
NRF_GPIO->OUTCLR = 0x00400000; \
NRF_GPIO->OUTSET = 0x00400000; } \
} while (0)
#define DEBUG_RADIO_START_M(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00400000; \
NRF_GPIO->OUTSET = 0x00400000; } \
else { \
NRF_GPIO->OUTCLR = 0x00400000; \
NRF_GPIO->OUTSET = 0x00400000; } \
} while (0)
#else
#define DEBUG_INIT()
#define DEBUG_CPU_SLEEP(flag)
#define DEBUG_TICKER_ISR(flag)
#define DEBUG_TICKER_TASK(flag)
#define DEBUG_TICKER_JOB(flag)
#define DEBUG_RADIO_ISR(flag)
#define DEBUG_RADIO_HCTO(flag)
#define DEBUG_RADIO_XTAL(flag)
#define DEBUG_RADIO_ACTIVE(flag)
#define DEBUG_RADIO_CLOSE(flag)
#define DEBUG_RADIO_PREPARE_A(flag)
#define DEBUG_RADIO_START_A(flag)
#define DEBUG_RADIO_PREPARE_S(flag)
#define DEBUG_RADIO_START_S(flag)
#define DEBUG_RADIO_PREPARE_O(flag)
#define DEBUG_RADIO_START_O(flag)
#define DEBUG_RADIO_PREPARE_M(flag)
#define DEBUG_RADIO_START_M(flag)
#endif /* DEBUG */
#endif /* _DEBUG_H_ */

View file

@ -0,0 +1,188 @@
/*
* 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 <toolchain.h>
#include <string.h>
#include "nrf.h"
#include "mem.h"
#include "ecb.h"
#include "debug.h"
void ecb_encrypt(uint8_t const *const key_le,
uint8_t const *const clear_text_le,
uint8_t * const cipher_text_le,
uint8_t * const cipher_text_be)
{
struct __packed {
uint8_t key[16];
uint8_t clear_text[16];
uint8_t cipher_text[16];
} ecb;
mem_rcopy(&ecb.key[0], key_le, sizeof(ecb.key));
mem_rcopy(&ecb.clear_text[0], clear_text_le, sizeof(ecb.clear_text));
do {
NRF_ECB->TASKS_STOPECB = 1;
NRF_ECB->ECBDATAPTR = (uint32_t) &ecb;
NRF_ECB->EVENTS_ENDECB = 0;
NRF_ECB->EVENTS_ERRORECB = 0;
NRF_ECB->TASKS_STARTECB = 1;
while ((NRF_ECB->EVENTS_ENDECB == 0) &&
(NRF_ECB->EVENTS_ERRORECB == 0) &&
(NRF_ECB->ECBDATAPTR != 0)) {
/*__WFE();*/
}
NRF_ECB->TASKS_STOPECB = 1;
} while ((NRF_ECB->EVENTS_ERRORECB != 0) || (NRF_ECB->ECBDATAPTR == 0));
NRF_ECB->ECBDATAPTR = 0;
if (cipher_text_le) {
mem_rcopy(cipher_text_le, &ecb.cipher_text[0],
sizeof(ecb.cipher_text));
}
if (cipher_text_be) {
memcpy(cipher_text_be, &ecb.cipher_text[0],
sizeof(ecb.cipher_text));
}
}
uint32_t ecb_encrypt_nonblocking(struct ecb *ecb)
{
/* prepare to be used in a BE AES h/w */
if (ecb->in_key_le) {
mem_rcopy(&ecb->in_key_be[0], ecb->in_key_le,
sizeof(ecb->in_key_be));
}
if (ecb->in_clear_text_le) {
mem_rcopy(&ecb->in_clear_text_be[0],
ecb->in_clear_text_le,
sizeof(ecb->in_clear_text_be));
}
/* setup the encryption h/w */
NRF_ECB->ECBDATAPTR = (uint32_t)ecb;
NRF_ECB->EVENTS_ENDECB = 0;
NRF_ECB->EVENTS_ERRORECB = 0;
NRF_ECB->INTENSET = ECB_INTENSET_ERRORECB_Msk | ECB_INTENSET_ENDECB_Msk;
/* setup interrupt */
NVIC_SetPriority(ECB_IRQn, 2);
NVIC_ClearPendingIRQ(ECB_IRQn);
NVIC_EnableIRQ(ECB_IRQn);
/* start the encryption h/w */
NRF_ECB->TASKS_STARTECB = 1;
return 0;
}
static void ecb_cleanup(void)
{
/* stop h/w */
NRF_ECB->TASKS_STOPECB = 1;
/* cleanup interrupt */
NVIC_DisableIRQ(ECB_IRQn);
}
void ecb_isr(void)
{
if (NRF_ECB->EVENTS_ERRORECB) {
struct ecb *ecb = (struct ecb *)NRF_ECB->ECBDATAPTR;
ecb_cleanup();
ecb->fp_ecb(1, 0, ecb->context);
}
else if (NRF_ECB->EVENTS_ENDECB) {
struct ecb *ecb = (struct ecb *)NRF_ECB->ECBDATAPTR;
ecb_cleanup();
ecb->fp_ecb(0, &ecb->out_cipher_text_be[0],
ecb->context);
}
else {
ASSERT(0);
}
}
struct ecb_ut_context {
uint32_t volatile done;
uint32_t status;
uint8_t cipher_text[16];
};
static void ecb_cb(uint32_t status, uint8_t *cipher_be, void *context)
{
struct ecb_ut_context *ecb_ut_context =
(struct ecb_ut_context *)context;
ecb_ut_context->done = 1;
ecb_ut_context->status = status;
if (!status) {
mem_rcopy(ecb_ut_context->cipher_text, cipher_be,
sizeof(ecb_ut_context->cipher_text));
}
}
uint32_t ecb_ut(void)
{
uint8_t key[16] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
0x11, 0x22, 0x33, 0x44, 0x55 };
uint8_t clear_text[16] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
0x11, 0x22, 0x33, 0x44, 0x55 };
uint8_t cipher_text[16];
uint32_t status = 0;
struct ecb ecb;
struct ecb_ut_context context;
ecb_encrypt(key, clear_text, cipher_text, 0);
context.done = 0;
ecb.in_key_le = key;
ecb.in_clear_text_le = clear_text;
ecb.fp_ecb = ecb_cb;
ecb.context = &context;
status = ecb_encrypt_nonblocking(&ecb);
do {
__WFE();
__SEV();
__WFE();
} while (!context.done);
if (context.status != 0) {
return context.status;
}
status = memcmp(cipher_text, context.cipher_text, sizeof(cipher_text));
if (status) {
return status;
}
return status;
}

View file

@ -0,0 +1,45 @@
/*
* 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.
*/
#ifndef _ECB_H_
#define _ECB_H_
typedef void (*ecb_fp) (uint32_t status, uint8_t *cipher_be,
void *context);
struct ecb {
uint8_t in_key_be[16];
uint8_t in_clear_text_be[16];
uint8_t out_cipher_text_be[16];
/* if not null reverse copy into in_key_be */
uint8_t *in_key_le;
/* if not null reverse copy into in_clear_text_be */
uint8_t *in_clear_text_le;
ecb_fp fp_ecb;
void *context;
};
void ecb_encrypt(uint8_t const *const key_le,
uint8_t const *const clear_text_le,
uint8_t * const cipher_text_le,
uint8_t * const cipher_text_be);
uint32_t ecb_encrypt_nonblocking(struct ecb *ecb);
void ecb_isr(void);
uint32_t ecb_ut(void);
#endif /* _ECB_H_ */

View file

@ -0,0 +1,27 @@
/*
* 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.
*/
#ifndef _IRQ_H_
#define _IRQ_H_
void irq_enable(uint8_t irq);
void irq_disable(uint8_t irq);
void irq_pending_set(uint8_t irq);
uint8_t irq_enabled(uint8_t irq);
uint8_t irq_priority_equal(uint8_t irq);
#endif /* _IRQ_H_ */

View file

@ -0,0 +1,27 @@
/*
* 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.
*/
#ifndef _RTC_H_
#define _RTC_H_
void rtc_init(void);
uint32_t rtc_start(void);
uint32_t rtc_stop(void);
uint32_t rtc_tick_get(void);
uint32_t rtc_compare_set(uint8_t instance, uint32_t value);
#endif /* _RTC_H_ */

View file

@ -0,0 +1,33 @@
/*
* 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.
*/
#ifndef _HAL_WORK_H_
#define _HAL_WORK_H_
#include "nrf.h"
#define WORK_TICKER_WORKER0_IRQ RTC0_IRQn
#define WORK_TICKER_JOB0_IRQ SWI4_IRQn
#define WORK_TICKER_WORKER1_IRQ SWI5_IRQn
#define WORK_TICKER_JOB1_IRQ SWI5_IRQn
#define WORK_TICKER_WORKER0_IRQ_PRIORITY 0
#define WORK_TICKER_JOB0_IRQ_PRIORITY 0
#define WORK_TICKER_WORKER1_IRQ_PRIORITY 2
#define WORK_TICKER_JOB1_IRQ_PRIORITY 2
#endif /* _HAL_WORK_H_ */

View file

@ -0,0 +1,53 @@
/*
* 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 "nrf.h"
#include "hal_irq.h"
void irq_enable(uint8_t irq)
{
NVIC_EnableIRQ((IRQn_Type) irq);
}
void irq_disable(uint8_t irq)
{
NVIC_DisableIRQ((IRQn_Type) irq);
}
void irq_pending_set(uint8_t irq)
{
NVIC_SetPendingIRQ((IRQn_Type) irq);
}
uint8_t irq_enabled(uint8_t irq)
{
return ((NVIC->ISER[0] & (1 << ((uint32_t) (irq) & 0x1F))) != 0);
}
uint8_t irq_priority_equal(uint8_t irq)
{
uint32_t irq_current = SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk;
uint32_t irq_priority_current = 4;
if (irq_current > 16) {
irq_priority_current =
NVIC_GetPriority((IRQn_Type) (irq_current - 16)) & 0xFF;
}
return ((NVIC_GetPriority((IRQn_Type) irq) & 0xFF) ==
irq_priority_current);
}

View file

@ -0,0 +1,552 @@
/*
* 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 <misc/util.h>
#include "nrf.h"
#include "hal_work.h"
#include "defines.h"
#include "ccm.h"
#include "radio.h"
#if defined(NRF51)
#define RADIO_PDU_LEN_MAX (BIT(5) - 1)
#elif defined(NRF52)
#define RADIO_PDU_LEN_MAX (BIT(8) - 1)
#else
#error "Platform not defined."
#endif
static radio_isr_fp sfp_radio_isr;
void radio_isr(void)
{
if (sfp_radio_isr) {
sfp_radio_isr();
}
}
void radio_isr_set(radio_isr_fp fp_radio_isr)
{
sfp_radio_isr = fp_radio_isr; /* atomic assignment of 32-bit word */
NRF_RADIO->INTENSET = (0 |
/* RADIO_INTENSET_READY_Msk |
* RADIO_INTENSET_ADDRESS_Msk |
* RADIO_INTENSET_PAYLOAD_Msk |
* RADIO_INTENSET_END_Msk |
*/
RADIO_INTENSET_DISABLED_Msk
/* | RADIO_INTENSET_RSSIEND_Msk |
*/
);
NVIC_SetPriority(RADIO_IRQn, WORK_TICKER_WORKER0_IRQ_PRIORITY);
NVIC_ClearPendingIRQ(RADIO_IRQn);
NVIC_EnableIRQ(RADIO_IRQn);
}
void radio_reset(void)
{
NVIC_DisableIRQ(RADIO_IRQn);
NRF_RADIO->POWER =
((RADIO_POWER_POWER_Disabled << RADIO_POWER_POWER_Pos) &
RADIO_POWER_POWER_Msk);
NRF_RADIO->POWER =
((RADIO_POWER_POWER_Enabled << RADIO_POWER_POWER_Pos) &
RADIO_POWER_POWER_Msk);
}
void radio_phy_set(uint8_t phy)
{
NRF_RADIO->MODE =
(((phy) ? (uint32_t)phy : RADIO_MODE_MODE_Ble_1Mbit) <<
RADIO_MODE_MODE_Pos) & RADIO_MODE_MODE_Msk;
}
void radio_tx_power_set(uint32_t power)
{
/* TODO map power to h/w values. */
NRF_RADIO->TXPOWER = power;
}
void radio_freq_chnl_set(uint32_t chnl)
{
NRF_RADIO->FREQUENCY = chnl;
}
void radio_whiten_iv_set(uint32_t iv)
{
NRF_RADIO->DATAWHITEIV = iv;
}
void radio_aa_set(uint8_t *aa)
{
NRF_RADIO->TXADDRESS =
(((0UL) << RADIO_TXADDRESS_TXADDRESS_Pos) &
RADIO_TXADDRESS_TXADDRESS_Msk);
NRF_RADIO->RXADDRESSES =
((RADIO_RXADDRESSES_ADDR0_Enabled) << RADIO_RXADDRESSES_ADDR0_Pos);
NRF_RADIO->PREFIX0 = aa[3];
NRF_RADIO->BASE0 = (aa[2] << 24) | (aa[1] << 16) | (aa[0] << 8);
}
void radio_pkt_configure(uint8_t preamble16, uint8_t bits_len, uint8_t max_len)
{
#if defined(NRF51)
if (bits_len == 8) {
bits_len = 5;
}
#endif
NRF_RADIO->PCNF0 = ((((1UL) << RADIO_PCNF0_S0LEN_Pos) &
RADIO_PCNF0_S0LEN_Msk) |
((((uint32_t)bits_len) << RADIO_PCNF0_LFLEN_Pos) &
RADIO_PCNF0_LFLEN_Msk) |
((((uint32_t)8-bits_len) <<
RADIO_PCNF0_S1LEN_Pos) &
RADIO_PCNF0_S1LEN_Msk) |
(((RADIO_PCNF0_S1INCL_Include) <<
RADIO_PCNF0_S1INCL_Pos) &
RADIO_PCNF0_S1INCL_Msk) |
((((preamble16) ? RADIO_PCNF0_PLEN_16bit :
RADIO_PCNF0_PLEN_8bit) << RADIO_PCNF0_PLEN_Pos) &
RADIO_PCNF0_PLEN_Msk));
NRF_RADIO->PCNF1 = (((((uint32_t)max_len) << RADIO_PCNF1_MAXLEN_Pos) &
RADIO_PCNF1_MAXLEN_Msk) |
(((0UL) << RADIO_PCNF1_STATLEN_Pos) &
RADIO_PCNF1_STATLEN_Msk) |
(((3UL) << RADIO_PCNF1_BALEN_Pos) &
RADIO_PCNF1_BALEN_Msk) |
(((RADIO_PCNF1_ENDIAN_Little) <<
RADIO_PCNF1_ENDIAN_Pos) &
RADIO_PCNF1_ENDIAN_Msk) |
(((1UL) << RADIO_PCNF1_WHITEEN_Pos) &
RADIO_PCNF1_WHITEEN_Msk));
}
void radio_pkt_rx_set(void *rx_packet)
{
NRF_RADIO->PACKETPTR = (uint32_t)rx_packet;
}
void radio_pkt_tx_set(void *tx_packet)
{
NRF_RADIO->PACKETPTR = (uint32_t)tx_packet;
}
void radio_rx_enable(void)
{
NRF_RADIO->TASKS_RXEN = 1;
}
void radio_tx_enable(void)
{
NRF_RADIO->TASKS_TXEN = 1;
}
void radio_disable(void)
{
NRF_RADIO->SHORTS = 0;
NRF_RADIO->TASKS_DISABLE = 1;
}
void radio_status_reset(void)
{
NRF_RADIO->EVENTS_READY = 0;
NRF_RADIO->EVENTS_ADDRESS = 0;
NRF_RADIO->EVENTS_PAYLOAD = 0;
NRF_RADIO->EVENTS_END = 0;
NRF_RADIO->EVENTS_DISABLED = 0;
}
uint32_t radio_is_ready(void)
{
return NRF_RADIO->EVENTS_READY;
}
uint32_t radio_is_done(void)
{
return NRF_RADIO->EVENTS_END;
}
uint32_t radio_has_disabled(void)
{
return NRF_RADIO->EVENTS_DISABLED;
}
uint32_t radio_is_idle(void)
{
return (NRF_RADIO->STATE == 0);
}
void radio_crc_configure(uint32_t polynomial, uint32_t iv)
{
NRF_RADIO->CRCCNF =
(((RADIO_CRCCNF_SKIPADDR_Skip) << RADIO_CRCCNF_SKIPADDR_Pos) &
RADIO_CRCCNF_SKIPADDR_Msk) |
(((RADIO_CRCCNF_LEN_Three) << RADIO_CRCCNF_LEN_Pos) &
RADIO_CRCCNF_LEN_Msk);
NRF_RADIO->CRCPOLY = polynomial;
NRF_RADIO->CRCINIT = iv;
}
uint32_t radio_crc_is_valid(void)
{
return NRF_RADIO->CRCSTATUS;
}
static uint8_t ALIGNED(4) _pkt_empty[RADIO_EMPDU_SIZE_MAX];
static uint8_t ALIGNED(4) _pkt_scratch[
((RADIO_PDU_LEN_MAX + 3) > RADIO_ACPDU_SIZE_MAX) ?
(RADIO_PDU_LEN_MAX + 3) : RADIO_ACPDU_SIZE_MAX];
void *radio_pkt_empty_get(void)
{
return _pkt_empty;
}
void *radio_pkt_scratch_get(void)
{
return _pkt_scratch;
}
void radio_switch_complete_and_rx(void)
{
NRF_RADIO->SHORTS =
(RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk |
RADIO_SHORTS_DISABLED_RXEN_Msk);
}
void radio_switch_complete_and_tx(void)
{
NRF_RADIO->SHORTS =
(RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk |
RADIO_SHORTS_DISABLED_TXEN_Msk);
}
void radio_switch_complete_and_disable(void)
{
NRF_RADIO->SHORTS =
(RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk);
}
void radio_rssi_measure(void)
{
NRF_RADIO->SHORTS |=
(RADIO_SHORTS_ADDRESS_RSSISTART_Msk |
RADIO_SHORTS_DISABLED_RSSISTOP_Msk);
}
uint32_t radio_rssi_get(void)
{
return NRF_RADIO->RSSISAMPLE;
}
void radio_rssi_status_reset(void)
{
NRF_RADIO->EVENTS_RSSIEND = 0;
}
uint32_t radio_rssi_is_ready(void)
{
return NRF_RADIO->EVENTS_RSSIEND;
}
void radio_filter_configure(uint8_t bitmask_enable,
uint8_t bitmask_addr_type,
uint8_t *bdaddr)
{
uint8_t index = 8;
while (index--) {
NRF_RADIO->DAB[index] = ((uint32_t)bdaddr[3] << 24) |
((uint32_t)bdaddr[2] << 16) |
((uint32_t)bdaddr[1] << 8) |
bdaddr[0];
NRF_RADIO->DAP[index] = ((uint32_t)bdaddr[5] << 8) | bdaddr[4];
bdaddr += BDADDR_SIZE;
}
NRF_RADIO->DACNF = ((uint32_t)bitmask_addr_type << 8) | bitmask_enable;
}
void radio_filter_disable(void)
{
NRF_RADIO->DACNF &= ~(0x000000FF);
}
void radio_filter_status_reset(void)
{
NRF_RADIO->EVENTS_DEVMATCH = 0;
NRF_RADIO->EVENTS_DEVMISS = 0;
}
uint32_t radio_filter_has_match(void)
{
return NRF_RADIO->EVENTS_DEVMATCH;
}
void radio_bc_configure(uint32_t n)
{
NRF_RADIO->BCC = n;
NRF_RADIO->SHORTS |= RADIO_SHORTS_ADDRESS_BCSTART_Msk;
}
void radio_bc_status_reset(void)
{
NRF_RADIO->EVENTS_BCMATCH = 0;
}
uint32_t radio_bc_has_match(void)
{
return NRF_RADIO->EVENTS_BCMATCH;
}
void radio_tmr_status_reset(void)
{
NRF_RTC0->EVTENCLR = RTC_EVTENCLR_COMPARE2_Msk;
NRF_PPI->CHENCLR =
(PPI_CHEN_CH0_Msk | PPI_CHEN_CH1_Msk | PPI_CHEN_CH2_Msk |
PPI_CHEN_CH3_Msk | PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk |
PPI_CHEN_CH6_Msk);
}
void radio_tmr_tifs_set(uint32_t tifs)
{
NRF_RADIO->TIFS = tifs;
}
uint32_t radio_tmr_start(uint8_t trx, uint32_t ticks_start, uint32_t remainder)
{
if ((!(remainder / 1000000UL)) || (remainder & 0x80000000)) {
ticks_start--;
remainder += 30517578UL;
}
remainder /= 1000000UL;
NRF_TIMER0->TASKS_CLEAR = 1;
NRF_TIMER0->MODE = 0;
NRF_TIMER0->PRESCALER = 4;
NRF_TIMER0->BITMODE = 2; /* 24 - bit */
NRF_TIMER0->CC[0] = remainder;
NRF_TIMER0->EVENTS_COMPARE[0] = 0;
NRF_RTC0->CC[2] = ticks_start;
NRF_RTC0->EVTENSET = RTC_EVTENSET_COMPARE2_Msk;
NRF_RTC0->EVENTS_COMPARE[2] = 0;
NRF_PPI->CH[1].EEP = (uint32_t)&(NRF_RTC0->EVENTS_COMPARE[2]);
NRF_PPI->CH[1].TEP = (uint32_t)&(NRF_TIMER0->TASKS_START);
NRF_PPI->CHENSET = PPI_CHEN_CH1_Msk;
if (trx) {
NRF_PPI->CH[0].EEP =
(uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[0]);
NRF_PPI->CH[0].TEP =
(uint32_t)&(NRF_RADIO->TASKS_TXEN);
NRF_PPI->CHENSET = PPI_CHEN_CH0_Msk;
} else {
NRF_PPI->CH[0].EEP =
(uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[0]);
NRF_PPI->CH[0].TEP =
(uint32_t)&(NRF_RADIO->TASKS_RXEN);
NRF_PPI->CHENSET = PPI_CHEN_CH0_Msk;
}
return remainder;
}
void radio_tmr_stop(void)
{
NRF_TIMER0->TASKS_STOP = 1;
NRF_TIMER0->TASKS_SHUTDOWN = 1;
}
void radio_tmr_hcto_configure(uint32_t hcto)
{
NRF_TIMER0->CC[2] = hcto;
NRF_TIMER0->EVENTS_COMPARE[2] = 0;
NRF_PPI->CH[4].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS);
NRF_PPI->CH[4].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[2]);
NRF_PPI->CH[5].EEP = (uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[2]);
NRF_PPI->CH[5].TEP = (uint32_t)&(NRF_RADIO->TASKS_DISABLE);
NRF_PPI->CHENSET = (PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk);
}
void radio_tmr_aa_capture(void)
{
NRF_PPI->CH[2].EEP = (uint32_t)&(NRF_RADIO->EVENTS_READY);
NRF_PPI->CH[2].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[0]);
NRF_PPI->CH[3].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS);
NRF_PPI->CH[3].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[1]);
NRF_PPI->CHENSET = (PPI_CHEN_CH2_Msk | PPI_CHEN_CH3_Msk);
}
uint32_t radio_tmr_aa_get(void)
{
return (NRF_TIMER0->CC[1] - NRF_TIMER0->CC[0]);
}
void radio_tmr_end_capture(void)
{
NRF_PPI->CH[2].EEP = (uint32_t)&(NRF_RADIO->EVENTS_END);
NRF_PPI->CH[2].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[2]);
NRF_PPI->CHENSET = PPI_CHEN_CH2_Msk;
}
uint32_t radio_tmr_end_get(void)
{
return NRF_TIMER0->CC[2];
}
static uint8_t ALIGNED(4) _ccm_scratch[(RADIO_PDU_LEN_MAX - 4) + 16];
void *radio_ccm_rx_pkt_set(struct ccm *ccm, void *pkt)
{
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Disabled;
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled;
NRF_CCM->MODE =
((CCM_MODE_MODE_Decryption << CCM_MODE_MODE_Pos) &
CCM_MODE_MODE_Msk) |
((CCM_MODE_LENGTH_Extended << CCM_MODE_LENGTH_Pos) &
CCM_MODE_LENGTH_Msk);
NRF_CCM->CNFPTR = (uint32_t)ccm;
NRF_CCM->INPTR = (uint32_t)_pkt_scratch;
NRF_CCM->OUTPTR = (uint32_t)pkt;
NRF_CCM->SCRATCHPTR = (uint32_t)_ccm_scratch;
NRF_CCM->SHORTS = 0;
NRF_CCM->EVENTS_ENDKSGEN = 0;
NRF_CCM->EVENTS_ENDCRYPT = 0;
NRF_CCM->EVENTS_ERROR = 0;
NRF_PPI->CH[6].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS);
NRF_PPI->CH[6].TEP = (uint32_t)&(NRF_CCM->TASKS_CRYPT);
NRF_PPI->CHENSET = PPI_CHEN_CH6_Msk;
NRF_CCM->TASKS_KSGEN = 1;
return _pkt_scratch;
}
void *radio_ccm_tx_pkt_set(struct ccm *ccm, void *pkt)
{
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Disabled;
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled;
NRF_CCM->MODE =
((CCM_MODE_MODE_Encryption << CCM_MODE_MODE_Pos) &
CCM_MODE_MODE_Msk) |
((CCM_MODE_LENGTH_Extended << CCM_MODE_LENGTH_Pos) &
CCM_MODE_LENGTH_Msk);
NRF_CCM->CNFPTR = (uint32_t)ccm;
NRF_CCM->INPTR = (uint32_t)pkt;
NRF_CCM->OUTPTR = (uint32_t)_pkt_scratch;
NRF_CCM->SCRATCHPTR = (uint32_t)_ccm_scratch;
NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk;
NRF_CCM->EVENTS_ENDKSGEN = 0;
NRF_CCM->EVENTS_ENDCRYPT = 0;
NRF_CCM->EVENTS_ERROR = 0;
#if NRF51
/* set up PPI to enable CCM */
NRF_PPI->CH[6].EEP = (uint32_t)&(NRF_RADIO->EVENTS_READY);
NRF_PPI->CH[6].TEP = (uint32_t)&(NRF_CCM->TASKS_KSGEN);
NRF_PPI->CHENSET = PPI_CHEN_CH6_Msk;
#elif 0
/* encrypt tx packet */
NRF_CCM->INTENSET = CCM_INTENSET_ENDCRYPT_Msk;
NRF_CCM->TASKS_KSGEN = 1;
while (NRF_CCM->EVENTS_ENDCRYPT == 0) {
__WFE();
__SEV();
__WFE();
}
NRF_CCM->INTENCLR = CCM_INTENCLR_ENDCRYPT_Msk;
NVIC_ClearPendingIRQ(CCM_AAR_IRQn);
ASSERT(NRF_CCM->EVENTS_ERROR == 0);
#else
/* start KSGEN early, but dont wait for ENDCRYPT */
NRF_CCM->TASKS_KSGEN = 1;
#endif
return _pkt_scratch;
}
uint32_t radio_ccm_is_done(void)
{
NRF_CCM->INTENSET = CCM_INTENSET_ENDCRYPT_Msk;
while (NRF_CCM->EVENTS_ENDCRYPT == 0) {
__WFE();
__SEV();
__WFE();
}
NRF_CCM->INTENCLR = CCM_INTENCLR_ENDCRYPT_Msk;
NVIC_ClearPendingIRQ(CCM_AAR_IRQn);
return (NRF_CCM->EVENTS_ERROR == 0);
}
uint32_t radio_ccm_mic_is_valid(void)
{
return NRF_CCM->MICSTATUS;
}
static uint8_t ALIGNED(4) _aar_scratch[3];
void radio_ar_configure(uint32_t nirk, void *irk)
{
NRF_AAR->ENABLE = 1;
NRF_AAR->NIRK = nirk;
NRF_AAR->IRKPTR = (uint32_t)irk;
NRF_AAR->ADDRPTR = (uint32_t)NRF_RADIO->PACKETPTR;
NRF_AAR->SCRATCHPTR = (uint32_t)_aar_scratch[0];
radio_bc_configure(64);
NRF_PPI->CH[6].EEP = (uint32_t)&(NRF_RADIO->EVENTS_BCMATCH);
NRF_PPI->CH[6].TEP = (uint32_t)&(NRF_AAR->TASKS_START);
NRF_PPI->CHENSET = PPI_CHEN_CH6_Msk;
}
uint32_t radio_ar_match_get(void)
{
return NRF_AAR->STATUS;
}
void radio_ar_status_reset(void)
{
if (radio_bc_has_match()) {
NRF_AAR->EVENTS_END = 0;
NRF_AAR->EVENTS_RESOLVED = 0;
NRF_AAR->EVENTS_NOTRESOLVED = 0;
}
radio_bc_status_reset();
}
uint32_t radio_ar_has_match(void)
{
return (radio_bc_has_match() &&
(NRF_AAR->EVENTS_END) &&
(NRF_AAR->EVENTS_RESOLVED));
}

View file

@ -0,0 +1,108 @@
/*
* 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.
*/
#ifndef _RADIO_H_
#define _RADIO_H_
/* Ramp up times from OPS.
*/
#define RADIO_TX_READY_DELAY_US 140
#define RADIO_RX_READY_DELAY_US 138
/* Chain delays from OPS.
* nRF51: Tx= 1us, Rx= 3us;
* nRF52: Tx= 0.6us, Rx= 9.4us.
*/
#define RADIO_TX_CHAIN_DELAY_US 1
#define RADIO_RX_CHAIN_DELAY_US 10
/* Radio Pkt Size */
#define RADIO_EMPDU_SIZE_MAX 3
#define RADIO_ACPDU_SIZE_OVERHEAD 3
#define RADIO_ACPDU_SIZE_MAX (37 + RADIO_ACPDU_SIZE_OVERHEAD)
typedef void (*radio_isr_fp) (void);
void radio_isr(void);
void radio_isr_set(radio_isr_fp fp_radio_isr);
void radio_reset(void);
void radio_phy_set(uint8_t phy);
void radio_tx_power_set(uint32_t power);
void radio_freq_chnl_set(uint32_t chnl);
void radio_whiten_iv_set(uint32_t iv);
void radio_aa_set(uint8_t *aa);
void radio_pkt_configure(uint8_t preamble16, uint8_t bits_len, uint8_t max_len);
void radio_pkt_rx_set(void *rx_packet);
void radio_pkt_tx_set(void *tx_packet);
void radio_rx_enable(void);
void radio_tx_enable(void);
void radio_disable(void);
void radio_status_reset(void);
uint32_t radio_is_ready(void);
uint32_t radio_is_done(void);
uint32_t radio_has_disabled(void);
uint32_t radio_is_idle(void);
void radio_crc_configure(uint32_t polynomial, uint32_t iv);
uint32_t radio_crc_is_valid(void);
void *radio_pkt_empty_get(void);
void *radio_pkt_scratch_get(void);
void radio_switch_complete_and_rx(void);
void radio_switch_complete_and_tx(void);
void radio_switch_complete_and_disable(void);
void radio_rssi_measure(void);
uint32_t radio_rssi_get(void);
void radio_rssi_status_reset(void);
uint32_t radio_rssi_is_ready(void);
void radio_filter_configure(uint8_t bitmask_enable,
uint8_t bitmask_addr_type,
uint8_t *bdaddr);
void radio_filter_disable(void);
void radio_filter_status_reset(void);
uint32_t radio_filter_has_match(void);
void radio_bc_configure(uint32_t n);
void radio_bc_status_reset(void);
uint32_t radio_bc_has_match(void);
void radio_tmr_status_reset(void);
void radio_tmr_tifs_set(uint32_t tifs);
uint32_t radio_tmr_start(uint8_t trx, uint32_t ticks_start, uint32_t remainder);
void radio_tmr_stop(void);
void radio_tmr_hcto_configure(uint32_t hcto);
void radio_tmr_aa_capture(void);
uint32_t radio_tmr_aa_get(void);
void radio_tmr_end_capture(void);
uint32_t radio_tmr_end_get(void);
void *radio_ccm_rx_pkt_set(struct ccm *ccm, void *pkt);
void *radio_ccm_tx_pkt_set(struct ccm *ccm, void *pkt);
uint32_t radio_ccm_is_done(void);
uint32_t radio_ccm_mic_is_valid(void);
void radio_ar_configure(uint32_t nirk, void *irk);
uint32_t radio_ar_match_get(void);
void radio_ar_status_reset(void);
uint32_t radio_ar_has_match(void);
#endif

View file

@ -0,0 +1,125 @@
/*
* 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 "nrf.h"
#include "rand.h"
#include "debug.h"
#define RAND_RESERVED (4)
struct rand {
uint8_t count;
uint8_t first;
uint8_t last;
uint8_t rand[1];
};
static struct rand *_rand;
void rand_init(uint8_t *context, uint8_t context_len)
{
ASSERT(context_len > sizeof(struct rand));
_rand = (struct rand *)context;
_rand->count = context_len - sizeof(struct rand) + 1;
_rand->first = _rand->last = 0;
NRF_RNG->CONFIG = RNG_CONFIG_DERCEN_Msk;
NRF_RNG->EVENTS_VALRDY = 0;
NRF_RNG->INTENSET = RNG_INTENSET_VALRDY_Msk;
NVIC_SetPriority(RNG_IRQn, 2);
NVIC_EnableIRQ(RNG_IRQn);
NRF_RNG->TASKS_START = 1;
}
uint32_t rand_get(uint8_t octets, uint8_t *rand)
{
uint8_t reserved;
uint8_t first;
while (octets) {
if (_rand->first == _rand->last) {
break;
}
rand[--octets] = _rand->rand[_rand->first];
first = _rand->first + 1;
if (first == _rand->count) {
first = 0;
}
_rand->first = first;
}
reserved = RAND_RESERVED;
first = _rand->first;
while (reserved--) {
if (first == _rand->last) {
NRF_RNG->TASKS_START = 1;
break;
}
first++;
if (first == _rand->count) {
first = 0;
}
}
return octets;
}
void rng_isr(void)
{
if (NRF_RNG->EVENTS_VALRDY) {
uint8_t last;
last = _rand->last + 1;
if (last == _rand->count) {
last = 0;
}
if (last == _rand->first) {
/* this condition should not happen
* , but due to probable bug in HW
* , new value could be generated
* before task is stopped.
*/
NRF_RNG->TASKS_STOP = 1;
NRF_RNG->EVENTS_VALRDY = 0;
return;
}
_rand->rand[_rand->last] = NRF_RNG->VALUE;
_rand->last = last;
last = _rand->last + 1;
if (last == _rand->count) {
last = 0;
}
NRF_RNG->EVENTS_VALRDY = 0;
if (last == _rand->first) {
NRF_RNG->TASKS_STOP = 1;
}
}
}

View file

@ -0,0 +1,25 @@
/*
* 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.
*/
#ifndef _RAND_H_
#define _RAND_H_
void rand_init(uint8_t *context, uint8_t context_len);
uint32_t rand_get(uint8_t octets, uint8_t *rand);
void rng_isr(void);
#endif /* _RAND_H_ */

View file

@ -0,0 +1,68 @@
/*
* 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 "nrf.h"
#include "debug.h"
/* RTC instance to use */
#define NRF_RTC NRF_RTC0
static uint8_t _rtc_refcount;
void rtc_init(void)
{
NRF_RTC->PRESCALER = 0;
NRF_RTC->EVTENSET =
(RTC_EVTENSET_COMPARE0_Msk | RTC_EVTENSET_COMPARE1_Msk);
NRF_RTC->INTENSET =
(RTC_INTENSET_COMPARE0_Msk | RTC_INTENSET_COMPARE1_Msk);
}
uint32_t rtc_start(void)
{
if (_rtc_refcount++) {
return 1;
}
NRF_RTC->TASKS_START = 1;
return 0;
}
uint32_t rtc_stop(void)
{
ASSERT(_rtc_refcount);
if (--_rtc_refcount) {
return 1;
}
NRF_RTC->TASKS_STOP = 1;
return 0;
}
uint32_t rtc_tick_get(void)
{
return NRF_RTC->COUNTER;
}
void rtc_compare_set(uint8_t instance, uint32_t value)
{
NRF_RTC->CC[instance] = value;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,26 @@
/*
* 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.
*/
#ifndef _HCI_H_
#define _HCI_H_
void hci_handle(uint8_t x, uint8_t *len, uint8_t **out);
void hci_encode(uint8_t *buf, uint8_t *len, uint8_t **out);
void hci_encode_num_cmplt(uint16_t instance, uint8_t num, uint8_t *len,
uint8_t **out);
#endif /* _HCI_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,255 @@
/*
* 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.
*/
#ifndef _CTRL_H_
#define _CTRL_H_
/*****************************************************************************
* Zephyr Kconfig defined
****************************************************************************/
#ifdef CONFIG_BLUETOOTH_MAX_CONN
#define RADIO_CONNECTION_CONTEXT_MAX CONFIG_BLUETOOTH_MAX_CONN
#else
#define RADIO_CONNECTION_CONTEXT_MAX 0
#endif
#ifdef CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH
#define RADIO_LL_LENGTH_OCTETS_RX_MAX \
CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH
#endif
#ifdef CONFIG_BLUETOOTH_CONTROLLER_RX_BUFFERS
#define RADIO_PACKET_COUNT_RX_MAX \
CONFIG_BLUETOOTH_CONTROLLER_RX_BUFFERS
#endif
#ifdef CONFIG_BLUETOOTH_CONTROLLER_TX_BUFFERS
#define RADIO_PACKET_COUNT_TX_MAX \
CONFIG_BLUETOOTH_CONTROLLER_TX_BUFFERS
#endif
/*****************************************************************************
* Timer Resources (Controller defined)
****************************************************************************/
#define RADIO_TICKER_ID_EVENT 0
#define RADIO_TICKER_ID_MARKER_0 1
#define RADIO_TICKER_ID_PRE_EMPT 2
#define RADIO_TICKER_ID_ADV_STOP 3
#define RADIO_TICKER_ID_OBS_STOP 4
#define RADIO_TICKER_ID_ADV 5
#define RADIO_TICKER_ID_OBS 6
#define RADIO_TICKER_ID_FIRST_CONNECTION 7
#define RADIO_TICKER_INSTANCE_ID_RADIO 0
#define RADIO_TICKER_INSTANCE_ID_APP 1
#define RADIO_TICKER_USERS 3
#define RADIO_TICKER_USER_ID_WORKER 0
#define RADIO_TICKER_USER_ID_JOB 1
#define RADIO_TICKER_USER_ID_APP 2
#define RADIO_TICKER_USER_WORKER_OPS (7 + 1)
#define RADIO_TICKER_USER_JOB_OPS (2 + 1)
#define RADIO_TICKER_USER_APP_OPS (1 + 1)
#define RADIO_TICKER_USER_OPS (RADIO_TICKER_USER_WORKER_OPS \
+ RADIO_TICKER_USER_JOB_OPS \
+ RADIO_TICKER_USER_APP_OPS \
)
#define RADIO_TICKER_NODES (RADIO_TICKER_ID_FIRST_CONNECTION \
+ RADIO_CONNECTION_CONTEXT_MAX \
)
/*****************************************************************************
* Controller Interface Defines
****************************************************************************/
#define RADIO_BLE_VERSION_NUMBER (0x08)
#define RADIO_BLE_COMPANY_ID (0xFFFF)
#define RADIO_BLE_SUB_VERSION_NUMBER (0xFFFF)
#define RADIO_BLE_FEATURES (0x1F) /* LE Ping, Slave Initiated
* Feature request, Extended
* Reject Indication, Conn Param
* Req Procedure, LE encryption.
*/
/*****************************************************************************
* Controller Reference Defines (compile time override-able)
****************************************************************************/
/* Minimum LL Payload support (Dont change). */
#define RADIO_LL_LENGTH_OCTETS_RX_MIN 27
#define RADIO_LL_LENGTH_TIME_RX_MIN (((RADIO_LL_LENGTH_OCTETS_RX_MIN) \
+ 14) * 8 \
)
/* Maximum LL Payload support (27 to 251). */
#ifndef RADIO_LL_LENGTH_OCTETS_RX_MAX
#define RADIO_LL_LENGTH_OCTETS_RX_MAX 251
#endif
#define RADIO_LL_LENGTH_TIME_RX_MAX (((RADIO_LL_LENGTH_OCTETS_RX_MAX) \
+ 14) * 8 \
)
/* Implementation default L2CAP MTU */
#ifndef RADIO_L2CAP_MTU_MAX
#define RADIO_L2CAP_MTU_MAX (RADIO_LL_LENGTH_OCTETS_RX_MAX - 4)
#endif
/* Maximise L2CAP MTU to LL data PDU size */
#if (RADIO_L2CAP_MTU_MAX < (RADIO_LL_LENGTH_OCTETS_RX_MAX - 4))
#undef RADIO_L2CAP_MTU_MAX
#define RADIO_L2CAP_MTU_MAX (RADIO_LL_LENGTH_OCTETS_RX_MAX - 4)
#endif
/* Maximum LL PDU Receive pool size. */
#ifndef RADIO_PACKET_COUNT_RX_MAX
#define RADIO_PACKET_COUNT_RX ((RADIO_L2CAP_MTU_MAX + \
RADIO_LL_LENGTH_OCTETS_RX_MAX \
+ 3) \
/ \
RADIO_LL_LENGTH_OCTETS_RX_MAX \
)
#define RADIO_PACKET_COUNT_RX_MAX (RADIO_PACKET_COUNT_RX + \
((RADIO_CONNECTION_CONTEXT_MAX - 1) * \
(RADIO_PACKET_COUNT_RX - 1)) \
)
#endif /* RADIO_PACKET_COUNT_RX_MAX */
/* Maximum LL PDU Transmit pool size and application tx count. */
#ifndef RADIO_PACKET_COUNT_TX_MAX
#define RADIO_PACKET_COUNT_APP_TX_MAX (RADIO_CONNECTION_CONTEXT_MAX)
#define RADIO_PACKET_COUNT_TX_MAX (RADIO_PACKET_COUNT_RX_MAX + \
RADIO_PACKET_COUNT_APP_TX_MAX \
)
#else
#define RADIO_PACKET_COUNT_APP_TX_MAX (RADIO_PACKET_COUNT_TX_MAX)
#endif
/*****************************************************************************
* Controller Interface Structures
****************************************************************************/
struct radio_adv_data {
uint8_t data[DOUBLE_BUFFER_SIZE][RADIO_ACPDU_SIZE_MAX];
uint8_t first;
uint8_t last;
};
struct __packed radio_le_conn_cmplt {
uint8_t status;
uint8_t role;
uint8_t peer_addr_type;
uint8_t peer_addr[BDADDR_SIZE];
uint8_t own_addr_type;
uint8_t own_addr[BDADDR_SIZE];
uint8_t peer_irk_index;
uint16_t interval;
uint16_t latency;
uint16_t timeout;
uint8_t mca;
};
struct __packed radio_le_conn_update_cmplt {
uint8_t status;
uint16_t interval;
uint16_t latency;
uint16_t timeout;
};
struct radio_pdu_node_tx {
void *next;
uint8_t pdu_data[1];
};
enum radio_pdu_node_rx_type {
NODE_RX_TYPE_NONE,
NODE_RX_TYPE_DC_PDU,
NODE_RX_TYPE_PROFILE,
NODE_RX_TYPE_REPORT,
NODE_RX_TYPE_CONNECTION,
NODE_RX_TYPE_TERMINATE,
NODE_RX_TYPE_CONN_UPDATE,
NODE_RX_TYPE_ENC_REFRESH,
NODE_RX_TYPE_APTO,
NODE_RX_TYPE_RSSI,
};
struct radio_pdu_node_rx_hdr {
enum radio_pdu_node_rx_type type;
uint16_t handle;
union {
void *next;
void *link;
uint8_t packet_release_last;
} onion;
};
struct radio_pdu_node_rx {
struct radio_pdu_node_rx_hdr hdr;
uint8_t pdu_data[1];
};
/*****************************************************************************
* Controller Interface Functions
****************************************************************************/
uint32_t radio_init(uint8_t sca, uint8_t connection_count_max,
uint8_t rx_count_max, uint8_t tx_count_max,
uint16_t data_octets_max, uint8_t *mem_radio,
uint16_t mem_size);
void radio_ticks_active_to_start_set(uint32_t ticks_active_to_start);
struct radio_adv_data *radio_adv_data_get(void);
struct radio_adv_data *radio_scan_data_get(void);
void radio_filter_clear(void);
uint32_t radio_filter_add(uint8_t addr_type, uint8_t *addr);
void radio_irk_clear(void);
uint32_t radio_irk_add(uint8_t *irk);
uint32_t radio_adv_enable(uint16_t interval, uint8_t chl_map,
uint8_t filter_policy);
uint32_t radio_adv_disable(void);
uint32_t radio_scan_enable(uint8_t scan_type, uint8_t init_addr_type,
uint8_t *init_addr, uint16_t interval,
uint16_t window, uint8_t filter_policy);
uint32_t radio_scan_disable(void);
uint32_t radio_connect_enable(uint8_t adv_addr_type, uint8_t *adv_addr,
uint16_t interval, uint16_t latency,
uint16_t timeout);
uint32_t radio_connect_disable(void);
uint32_t radio_conn_update(uint16_t handle, uint8_t cmd, uint8_t status,
uint16_t interval, uint16_t latency,
uint16_t timeout);
uint32_t radio_chm_update(uint8_t *chm);
uint32_t radio_chm_get(uint16_t handle, uint8_t *chm);
uint32_t radio_enc_req_send(uint16_t handle, uint8_t *rand, uint8_t *ediv,
uint8_t *ltk);
uint32_t radio_start_enc_req_send(uint16_t handle, uint8_t err_code,
uint8_t const *const ltk);
uint32_t radio_feature_req_send(uint16_t handle);
uint32_t radio_version_ind_send(uint16_t handle);
uint32_t radio_terminate_ind_send(uint16_t handle, uint8_t reason);
uint32_t radio_length_req_send(uint16_t handle, uint16_t tx_octets);
uint8_t radio_rx_get(struct radio_pdu_node_rx **radio_pdu_node_rx,
uint16_t *handle);
void radio_rx_dequeue(void);
void radio_rx_mem_release(struct radio_pdu_node_rx **radio_pdu_node_rx);
uint8_t radio_rx_fc_set(uint16_t handle, uint8_t fc);
struct radio_pdu_node_tx *radio_tx_mem_acquire(void);
void radio_tx_mem_release(struct radio_pdu_node_tx *pdu_data_node_tx);
uint32_t radio_tx_mem_enqueue(uint16_t handle,
struct radio_pdu_node_tx *pdu_data_node_tx);
extern void radio_active_callback(uint8_t active);
extern void radio_event_callback(void);
#endif

View file

@ -0,0 +1,239 @@
/*
* 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 <stddef.h>
#include "util/defines.h"
#include "pdu.h"
#include "ctrl.h"
enum llcp {
LLCP_NONE,
LLCP_CONNECTION_UPDATE,
LLCP_CHANNEL_MAP,
LLCP_ENCRYPTION,
LLCP_FEATURE_EXCHANGE,
LLCP_VERSION_EXCHANGE,
/* LLCP_TERMINATE, */
LLCP_PING,
/* LLCP_LENGTH, */
};
struct shdr {
uint32_t ticks_xtal_to_start;
uint32_t ticks_active_to_start;
uint32_t ticks_preempt_to_start;
uint32_t ticks_slot;
};
struct connection {
struct shdr hdr;
uint8_t access_addr[4];
uint8_t crc_init[3];
uint8_t data_channel_map[5];
uint8_t data_channel_count;
uint8_t data_channel_hop;
uint8_t data_channel_use;
uint16_t handle;
uint16_t event_counter;
uint16_t conn_interval;
uint16_t latency;
uint16_t latency_prepare;
uint16_t latency_event;
uint16_t sug_tx_octets;
uint16_t max_tx_octets;
uint16_t max_rx_octets;
uint16_t supervision_reload;
uint16_t supervision_expire;
uint16_t procedure_reload;
uint16_t procedure_expire;
uint16_t appto_reload;
uint16_t appto_expire;
uint16_t apto_reload;
uint16_t apto_expire;
union {
struct {
uint8_t role:1;
uint8_t connect_expire;
} master;
struct {
uint8_t role:1;
uint8_t sca:3;
uint8_t latency_cancel:1;
uint32_t window_widening_periodic_us;
uint32_t window_widening_max_us;
uint32_t window_widening_prepare_us;
uint32_t window_widening_event_us;
uint32_t window_size_prepare_us;
uint32_t window_size_event_us;
uint32_t force;
uint32_t ticks_to_offset;
} slave;
} role;
uint8_t llcp_req;
uint8_t llcp_ack;
enum llcp llcp_type;
union {
struct {
uint16_t interval;
uint16_t latency;
uint16_t timeout;
uint8_t preferred_periodicity;
uint16_t instant;
uint16_t offset0;
uint16_t offset1;
uint16_t offset2;
uint16_t offset3;
uint16_t offset4;
uint16_t offset5;
uint32_t ticks_ref;
uint32_t ticks_to_offset_next;
uint32_t win_offset_us;
uint16_t *pdu_win_offset;
uint8_t win_size;
uint8_t state:3;
#define LLCP_CONN_STATE_INPROG 0 /* master + slave proc in progress
* until instant
*/
#define LLCP_CONN_STATE_INITIATE 1 /* master sends conn_update */
#define LLCP_CONN_STATE_REQ 2 /* master / slave send req */
#define LLCP_CONN_STATE_RSP 3 /* master rej / slave rej/rsp */
#define LLCP_CONN_STATE_APP_WAIT 4 /* app resp */
#define LLCP_CONN_STATE_RSP_WAIT 5 /* master rsp or slave conn_update
* or rej
*/
uint8_t is_internal:2;
} connection_update;
struct {
uint8_t initiate;
uint8_t chm[5];
uint16_t instant;
} channel_map;
struct {
uint8_t error_code;
uint8_t rand[8];
uint8_t ediv[2];
uint8_t ltk[16];
uint8_t skd[16];
} encryption;
} llcp;
uint8_t llcp_features;
struct {
uint8_t tx:1;
uint8_t rx:1;
uint8_t version_number;
uint16_t company_id;
uint16_t sub_version_number;
} llcp_version;
struct {
uint8_t req;
uint8_t ack;
uint8_t reason_own;
uint8_t reason_peer;
struct {
struct radio_pdu_node_rx_hdr hdr;
uint8_t reason;
} radio_pdu_node_rx;
} llcp_terminate;
struct {
uint8_t req;
uint8_t ack;
uint8_t state:2;
#define LLCP_LENGTH_STATE_REQ 0
#define LLCP_LENGTH_STATE_ACK_WAIT 1
#define LLCP_LENGTH_STATE_RSP_WAIT 2
#define LLCP_LENGTH_STATE_RESIZE 3
uint16_t rx_octets;
uint16_t tx_octets;
} llcp_length;
uint8_t sn:1;
uint8_t nesn:1;
uint8_t pause_rx:1;
uint8_t pause_tx:1;
uint8_t enc_rx:1;
uint8_t enc_tx:1;
uint8_t refresh:1;
uint8_t empty:1;
struct ccm ccm_rx;
struct ccm ccm_tx;
struct radio_pdu_node_tx *pkt_tx_head;
struct radio_pdu_node_tx *pkt_tx_ctrl;
struct radio_pdu_node_tx *pkt_tx_data;
struct radio_pdu_node_tx *pkt_tx_last;
uint8_t packet_tx_head_len;
uint8_t packet_tx_head_offset;
uint8_t rssi_latest;
uint8_t rssi_reported;
uint8_t rssi_sample_count;
};
#define CONNECTION_T_SIZE ALIGN4(sizeof(struct connection))
struct pdu_data_q_tx {
uint16_t handle;
struct radio_pdu_node_tx *node_tx;
};
/** @todo fix starvation when ctrl rx in radio ISR
* for multiple connections needs to tx back to peer.
*/
#define PACKET_MEM_COUNT_TX_CTRL 2
#define LL_MEM_CONN (sizeof(struct connection) * RADIO_CONNECTION_CONTEXT_MAX)
#define LL_MEM_RXQ (sizeof(void *) * (RADIO_PACKET_COUNT_RX_MAX + 4))
#define LL_MEM_TXQ (sizeof(struct pdu_data_q_tx) * \
(RADIO_PACKET_COUNT_TX_MAX + 2))
#define LL_MEM_RX_POOL_SZ (ALIGN4(offsetof(struct radio_pdu_node_rx,\
pdu_data) + ((\
(RADIO_ACPDU_SIZE_MAX + 1) < \
(offsetof(struct pdu_data, payload) + \
RADIO_LL_LENGTH_OCTETS_RX_MAX)) ? \
(offsetof(struct pdu_data, payload) + \
RADIO_LL_LENGTH_OCTETS_RX_MAX) \
: \
(RADIO_ACPDU_SIZE_MAX + 1))) * \
(RADIO_PACKET_COUNT_RX_MAX + 3))
#define LL_MEM_RX_LINK_POOL (sizeof(void *) * 2 * ((RADIO_PACKET_COUNT_RX_MAX +\
4) + RADIO_CONNECTION_CONTEXT_MAX))
#define LL_MEM_TX_CTRL_POOL ((ALIGN4(offsetof( \
struct radio_pdu_node_tx, pdu_data) + \
offsetof(struct pdu_data, payload) + 27)) * \
PACKET_MEM_COUNT_TX_CTRL)
#define LL_MEM_TX_DATA_POOL ((ALIGN4(offsetof( \
struct radio_pdu_node_tx, pdu_data) + \
offsetof(struct pdu_data, payload) + \
RADIO_LL_LENGTH_OCTETS_RX_MAX)) \
* (RADIO_PACKET_COUNT_TX_MAX + 1))
#define LL_MEM_TOTAL (LL_MEM_CONN + LL_MEM_RXQ + (LL_MEM_TXQ * 2) + \
LL_MEM_RX_POOL_SZ + \
LL_MEM_RX_LINK_POOL + LL_MEM_TX_CTRL_POOL + LL_MEM_TX_DATA_POOL)

View file

@ -0,0 +1,295 @@
/*
* 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 <stdint.h>
#include <string.h>
#include "defines.h"
#include "mem.h"
#include "ticker.h"
#include "ccm.h"
#include "radio.h"
#include "pdu.h"
#include "ctrl.h"
#include "ll.h"
#include "debug.h"
static struct {
uint8_t pub_addr[BDADDR_SIZE];
uint8_t rnd_addr[BDADDR_SIZE];
} _ll_context;
static struct {
uint16_t interval;
uint8_t adv_type:4;
uint8_t tx_addr:1;
uint8_t rx_addr:1;
uint8_t filter_policy:2;
uint8_t chl_map:3;
uint8_t adv_addr[BDADDR_SIZE];
uint8_t direct_addr[BDADDR_SIZE];
} _ll_adv_params;
static struct {
uint16_t interval;
uint16_t window;
uint8_t scan_type:1;
uint8_t tx_addr:1;
uint8_t filter_policy:1;
} _ll_scan_params;
void ll_address_get(uint8_t addr_type, uint8_t *bdaddr)
{
if (addr_type) {
memcpy(bdaddr, &_ll_context.rnd_addr[0], BDADDR_SIZE);
} else {
memcpy(bdaddr, &_ll_context.pub_addr[0], BDADDR_SIZE);
}
}
void ll_address_set(uint8_t addr_type, uint8_t const *const bdaddr)
{
if (addr_type) {
memcpy(&_ll_context.rnd_addr[0], bdaddr, BDADDR_SIZE);
} else {
memcpy(&_ll_context.pub_addr[0], bdaddr, BDADDR_SIZE);
}
}
void ll_adv_params_set(uint16_t interval, uint8_t adv_type,
uint8_t own_addr_type, uint8_t direct_addr_type,
uint8_t const *const direct_addr, uint8_t chl_map,
uint8_t filter_policy)
{
struct radio_adv_data *radio_adv_data;
struct pdu_adv *pdu;
/** @todo check and fail if adv role active else
* update (implemented below) current index elements for
* both adv and scan data.
*/
/* remember params so that set adv/scan data and adv enable
* interface can correctly update adv/scan data in the
* double buffer between caller and controller context.
*/
_ll_adv_params.interval = interval;
_ll_adv_params.chl_map = chl_map;
_ll_adv_params.filter_policy = filter_policy;
_ll_adv_params.adv_type = adv_type;
_ll_adv_params.tx_addr = own_addr_type;
_ll_adv_params.rx_addr = 0;
/* update the current adv data */
radio_adv_data = radio_adv_data_get();
pdu = (struct pdu_adv *)&radio_adv_data->data[radio_adv_data->last][0];
pdu->type = _ll_adv_params.adv_type;
pdu->tx_addr = _ll_adv_params.tx_addr;
if (adv_type == PDU_ADV_TYPE_DIRECT_IND) {
_ll_adv_params.rx_addr = direct_addr_type;
memcpy(&_ll_adv_params.direct_addr[0], direct_addr,
BDADDR_SIZE);
memcpy(&pdu->payload.direct_ind.init_addr[0],
direct_addr, BDADDR_SIZE);
pdu->len = sizeof(struct pdu_adv_payload_direct_ind);
} else if (pdu->len == 0) {
pdu->len = BDADDR_SIZE;
}
pdu->rx_addr = _ll_adv_params.rx_addr;
/* update the current scan data */
radio_adv_data = radio_scan_data_get();
pdu = (struct pdu_adv *)&radio_adv_data->data[radio_adv_data->last][0];
pdu->type = PDU_ADV_TYPE_SCAN_RESP;
pdu->tx_addr = _ll_adv_params.tx_addr;
pdu->rx_addr = 0;
if (pdu->len == 0) {
pdu->len = BDADDR_SIZE;
}
}
void ll_adv_data_set(uint8_t len, uint8_t const *const data)
{
struct radio_adv_data *radio_adv_data;
struct pdu_adv *pdu;
uint8_t last;
/** @todo dont update data if directed adv type. */
/* use the last index in double buffer, */
radio_adv_data = radio_adv_data_get();
if (radio_adv_data->first == radio_adv_data->last) {
last = radio_adv_data->last + 1;
if (last == DOUBLE_BUFFER_SIZE) {
last = 0;
}
} else {
last = radio_adv_data->last;
}
/* update adv pdu fields. */
pdu = (struct pdu_adv *)&radio_adv_data->data[last][0];
pdu->type = _ll_adv_params.adv_type;
pdu->tx_addr = _ll_adv_params.tx_addr;
pdu->rx_addr = _ll_adv_params.rx_addr;
memcpy(&pdu->payload.adv_ind.addr[0],
&_ll_adv_params.adv_addr[0], BDADDR_SIZE);
if (_ll_adv_params.adv_type == PDU_ADV_TYPE_DIRECT_IND) {
memcpy(&pdu->payload.direct_ind.init_addr[0],
&_ll_adv_params.direct_addr[0], BDADDR_SIZE);
pdu->len = sizeof(struct pdu_adv_payload_direct_ind);
} else {
memcpy(&pdu->payload.adv_ind.data[0], data, len);
pdu->len = BDADDR_SIZE + len;
}
/* commit the update so controller picks it. */
radio_adv_data->last = last;
}
void ll_scan_data_set(uint8_t len, uint8_t const *const data)
{
struct radio_adv_data *radio_scan_data;
struct pdu_adv *pdu;
uint8_t last;
/* use the last index in double buffer, */
radio_scan_data = radio_scan_data_get();
if (radio_scan_data->first == radio_scan_data->last) {
last = radio_scan_data->last + 1;
if (last == DOUBLE_BUFFER_SIZE) {
last = 0;
}
} else {
last = radio_scan_data->last;
}
/* update scan pdu fields. */
pdu = (struct pdu_adv *)&radio_scan_data->data[last][0];
pdu->type = PDU_ADV_TYPE_SCAN_RESP;
pdu->tx_addr = _ll_adv_params.tx_addr;
pdu->rx_addr = 0;
pdu->len = BDADDR_SIZE + len;
memcpy(&pdu->payload.scan_resp.addr[0],
&_ll_adv_params.adv_addr[0], BDADDR_SIZE);
memcpy(&pdu->payload.scan_resp.data[0], data, len);
/* commit the update so controller picks it. */
radio_scan_data->last = last;
}
uint32_t ll_adv_enable(uint8_t enable)
{
uint32_t status;
if (enable) {
struct radio_adv_data *radio_adv_data;
struct radio_adv_data *radio_scan_data;
struct pdu_adv *pdu_adv;
struct pdu_adv *pdu_scan;
/** @todo move the addr remembered into controller
* this way when implementing Privacy 1.2, generated
* new resolvable addresses can be used instantly.
*/
/* remember addr to use and also update the addr in
* both adv and scan PDUs.
*/
radio_adv_data = radio_adv_data_get();
radio_scan_data = radio_scan_data_get();
pdu_adv = (struct pdu_adv *)&radio_adv_data->data
[radio_adv_data->last][0];
pdu_scan = (struct pdu_adv *)&radio_scan_data->data
[radio_scan_data->last][0];
if (_ll_adv_params.tx_addr) {
memcpy(&_ll_adv_params.adv_addr[0],
&_ll_context.rnd_addr[0], BDADDR_SIZE);
memcpy(&pdu_adv->payload.adv_ind.addr[0],
&_ll_context.rnd_addr[0], BDADDR_SIZE);
memcpy(&pdu_scan->payload.scan_resp.addr[0],
&_ll_context.rnd_addr[0], BDADDR_SIZE);
} else {
memcpy(&_ll_adv_params.adv_addr[0],
&_ll_context.pub_addr[0], BDADDR_SIZE);
memcpy(&pdu_adv->payload.adv_ind.addr[0],
&_ll_context.pub_addr[0], BDADDR_SIZE);
memcpy(&pdu_scan->payload.scan_resp.addr[0],
&_ll_context.pub_addr[0], BDADDR_SIZE);
}
status = radio_adv_enable(_ll_adv_params.interval,
_ll_adv_params.chl_map,
_ll_adv_params.filter_policy);
} else {
status = radio_adv_disable();
}
return status;
}
void ll_scan_params_set(uint8_t scan_type, uint16_t interval, uint16_t window,
uint8_t own_addr_type, uint8_t filter_policy)
{
_ll_scan_params.scan_type = scan_type;
_ll_scan_params.interval = interval;
_ll_scan_params.window = window;
_ll_scan_params.tx_addr = own_addr_type;
_ll_scan_params.filter_policy = filter_policy;
}
uint32_t ll_scan_enable(uint8_t enable)
{
uint32_t status;
if (enable) {
status = radio_scan_enable(_ll_scan_params.scan_type,
_ll_scan_params.tx_addr,
(_ll_scan_params.tx_addr) ?
&_ll_context.rnd_addr[0] :
&_ll_context.pub_addr[0],
_ll_scan_params.interval,
_ll_scan_params.window,
_ll_scan_params.filter_policy);
} else {
status = radio_scan_disable();
}
return status;
}
uint32_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window,
uint8_t filter_policy, uint8_t peer_addr_type,
uint8_t *peer_addr, uint8_t own_addr_type,
uint16_t interval, uint16_t latency,
uint16_t timeout)
{
uint32_t status;
status = radio_connect_enable(peer_addr_type, peer_addr, interval,
latency, timeout);
if (status) {
return status;
}
return radio_scan_enable(0, own_addr_type, (own_addr_type) ?
&_ll_context.rnd_addr[0] :
&_ll_context.pub_addr[0],
scan_interval, scan_window, filter_policy);
}

View file

@ -0,0 +1,39 @@
/*
* 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.
*/
#ifndef _LL_H_
#define _LL_H_
void ll_address_get(uint8_t addr_type, uint8_t *p_bdaddr);
void ll_address_set(uint8_t addr_type, uint8_t const *const p_bdaddr);
void ll_adv_params_set(uint16_t interval, uint8_t adv_type,
uint8_t own_addr_type, uint8_t direct_addr_type,
uint8_t const *const p_direct_addr, uint8_t chl_map,
uint8_t filter_policy);
void ll_adv_data_set(uint8_t len, uint8_t const *const p_data);
void ll_scan_data_set(uint8_t len, uint8_t const *const p_data);
uint32_t ll_adv_enable(uint8_t enable);
void ll_scan_params_set(uint8_t scan_type, uint16_t interval, uint16_t window,
uint8_t own_addr_type, uint8_t filter_policy);
uint32_t ll_scan_enable(uint8_t enable);
uint32_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window,
uint8_t filter_policy, uint8_t peer_addr_type,
uint8_t *p_peer_addr, uint8_t own_addr_type,
uint16_t interval, uint16_t latency,
uint16_t timeout);
#endif /* _LL_H_ */

View file

@ -0,0 +1,260 @@
/*
* 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.
*/
#ifndef _PDU_H_
#define _PDU_H_
#include <toolchain.h>
struct __packed pdu_adv_payload_adv_ind {
uint8_t addr[BDADDR_SIZE];
uint8_t data[31];
};
struct __packed pdu_adv_payload_direct_ind {
uint8_t adv_addr[BDADDR_SIZE];
uint8_t init_addr[BDADDR_SIZE];
};
struct __packed pdu_adv_payload_scan_resp {
uint8_t addr[BDADDR_SIZE];
uint8_t data[31];
};
struct __packed pdu_adv_payload_scan_req {
uint8_t scan_addr[BDADDR_SIZE];
uint8_t adv_addr[BDADDR_SIZE];
};
struct __packed pdu_adv_payload_connect_req {
uint8_t init_addr[BDADDR_SIZE];
uint8_t adv_addr[BDADDR_SIZE];
struct __packed {
uint8_t access_addr[4];
uint8_t crc_init[3];
uint8_t win_size;
uint16_t win_offset;
uint16_t interval;
uint16_t latency;
uint16_t timeout;
uint8_t channel_map[5];
uint8_t hop:5;
uint8_t sca:3;
} lldata;
};
enum pdu_adv_type {
PDU_ADV_TYPE_ADV_IND = 0x00,
PDU_ADV_TYPE_DIRECT_IND = 0x01,
PDU_ADV_TYPE_NONCONN_IND = 0x02,
PDU_ADV_TYPE_SCAN_REQ = 0x03,
PDU_ADV_TYPE_SCAN_RESP = 0x04,
PDU_ADV_TYPE_CONNECT_REQ = 0x05,
PDU_ADV_TYPE_SCAN_IND = 0x06,
};
struct __packed pdu_adv {
uint8_t type:4;
uint8_t rfu0:2;
uint8_t tx_addr:1;
uint8_t rx_addr:1;
uint8_t len:6;
uint8_t rfu1:2;
uint8_t nrf_radio_s1;
union __packed {
struct pdu_adv_payload_adv_ind adv_ind;
struct pdu_adv_payload_direct_ind direct_ind;
struct pdu_adv_payload_scan_req scan_req;
struct pdu_adv_payload_scan_resp scan_resp;
struct pdu_adv_payload_connect_req connect_req;
} payload;
};
enum pdu_data_llid {
PDU_DATA_LLID_RESV = 0x00,
PDU_DATA_LLID_DATA_CONTINUE = 0x01,
PDU_DATA_LLID_DATA_START = 0x02,
PDU_DATA_LLID_CTRL = 0x03,
};
enum pdu_data_llctrl_type {
PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_REQ = 0x00,
PDU_DATA_LLCTRL_TYPE_CHANNEL_MAP_REQ = 0x01,
PDU_DATA_LLCTRL_TYPE_TERMINATE_IND = 0x02,
PDU_DATA_LLCTRL_TYPE_ENC_REQ = 0x03,
PDU_DATA_LLCTRL_TYPE_ENC_RSP = 0x04,
PDU_DATA_LLCTRL_TYPE_START_ENC_REQ = 0x05,
PDU_DATA_LLCTRL_TYPE_START_ENC_RSP = 0x06,
PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP = 0x07,
PDU_DATA_LLCTRL_TYPE_FEATURE_REQ = 0x08,
PDU_DATA_LLCTRL_TYPE_FEATURE_RSP = 0x09,
PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ = 0x0A,
PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP = 0x0B,
PDU_DATA_LLCTRL_TYPE_VERSION_IND = 0x0C,
PDU_DATA_LLCTRL_TYPE_REJECT_IND = 0x0D,
PDU_DATA_LLCTRL_TYPE_SLAVE_FEATURE_REQ = 0x0E,
PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ = 0x0F,
PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP = 0x10,
PDU_DATA_LLCTRL_TYPE_REJECT_IND_EXT = 0x11,
PDU_DATA_LLCTRL_TYPE_PING_REQ = 0x12,
PDU_DATA_LLCTRL_TYPE_PING_RSP = 0x13,
PDU_DATA_LLCTRL_TYPE_LENGTH_REQ = 0x14,
PDU_DATA_LLCTRL_TYPE_LENGTH_RSP = 0x15,
};
struct __packed pdu_data_llctrl_conn_update_req {
uint8_t win_size;
uint16_t win_offset;
uint16_t interval;
uint16_t latency;
uint16_t timeout;
uint16_t instant;
};
struct __packed pdu_data_llctrl_channel_map_req {
uint8_t chm[5];
uint16_t instant;
};
struct __packed pdu_data_llctrl_terminate_ind {
uint8_t error_code;
};
struct __packed pdu_data_llctrl_enc_req {
uint8_t rand[8];
uint8_t ediv[2];
uint8_t skdm[8];
uint8_t ivm[4];
};
struct __packed pdu_data_llctrl_enc_rsp {
uint8_t skds[8];
uint8_t ivs[4];
};
struct __packed pdu_data_llctrl_unknown_rsp {
uint8_t type;
};
struct __packed pdu_data_llctrl_feature_req {
uint8_t features[8];
};
struct __packed pdu_data_llctrl_feature_rsp {
uint8_t features[8];
};
struct __packed pdu_data_llctrl_version_ind {
uint8_t version_number;
uint16_t company_id;
uint16_t sub_version_number;
};
struct __packed pdu_data_llctrl_reject_ind {
uint8_t error_code;
};
struct __packed pdu_data_llctrl_conn_param_req {
uint16_t interval_min;
uint16_t interval_max;
uint16_t latency;
uint16_t timeout;
uint8_t preferred_periodicity;
uint16_t reference_conn_event_count;
uint16_t offset0;
uint16_t offset1;
uint16_t offset2;
uint16_t offset3;
uint16_t offset4;
uint16_t offset5;
};
struct __packed pdu_data_llctrl_conn_param_rsp {
uint16_t interval_min;
uint16_t interval_max;
uint16_t latency;
uint16_t timeout;
uint8_t preferred_periodicity;
uint16_t reference_conn_event_count;
uint16_t offset0;
uint16_t offset1;
uint16_t offset2;
uint16_t offset3;
uint16_t offset4;
uint16_t offset5;
};
struct __packed pdu_data_llctrl_reject_ind_ext {
uint8_t reject_opcode;
uint8_t error_code;
};
struct __packed pdu_data_llctrl_length_req_rsp {
uint16_t max_rx_octets;
uint16_t max_rx_time;
uint16_t max_tx_octets;
uint16_t max_tx_time;
};
struct __packed pdu_data_llctrl {
uint8_t opcode;
union __packed {
struct pdu_data_llctrl_conn_update_req conn_update_req;
struct pdu_data_llctrl_channel_map_req channel_map_req;
struct pdu_data_llctrl_terminate_ind terminate_ind;
struct pdu_data_llctrl_enc_req enc_req;
struct pdu_data_llctrl_enc_rsp enc_rsp;
struct pdu_data_llctrl_unknown_rsp unknown_rsp;
struct pdu_data_llctrl_feature_req feature_req;
struct pdu_data_llctrl_feature_rsp feature_rsp;
struct pdu_data_llctrl_version_ind version_ind;
struct pdu_data_llctrl_reject_ind reject_ind;
struct pdu_data_llctrl_feature_req slave_feature_req;
struct pdu_data_llctrl_conn_param_req conn_param_req;
struct pdu_data_llctrl_conn_param_rsp conn_param_rsp;
struct pdu_data_llctrl_reject_ind_ext reject_ind_ext;
struct pdu_data_llctrl_length_req_rsp length_req;
struct pdu_data_llctrl_length_req_rsp length_rsp;
} ctrldata;
};
struct __packed profile {
uint32_t min;
uint32_t avg;
uint32_t max;
};
struct __packed pdu_data {
uint8_t ll_id:2;
uint8_t nesn:1;
uint8_t sn:1;
uint8_t md:1;
uint8_t rfu0:3;
uint8_t len:8;
uint8_t resv:8;
union __packed {
uint8_t lldata[1];
struct pdu_data_llctrl llctrl;
uint8_t rssi;
struct profile profile;
} payload;
};
#endif /* _PDU_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,138 @@
/*
* 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.
*/
#ifndef _TICKER_H_
#define _TICKER_H_
/** \brief Macro to translate microseconds to tick units.
*
* \note This returns the floor value.
*/
#define TICKER_US_TO_TICKS(x) \
( \
((uint32_t)(((uint64_t) (x) * 1000000000UL) / 30517578125UL)) \
& 0x00FFFFFF \
)
/** \brief Macro returning remainder in nanoseconds over-and-above a tick unit.
*/
#define TICKER_REMAINDER(x) \
( \
( \
((uint64_t) (x) * 1000000000UL) \
- ((uint64_t) TICKER_US_TO_TICKS(x) * 30517578125UL) \
) \
/ 1000UL \
)
/** \brief Macro to translate tick units to microseconds.
*/
#define TICKER_TICKS_TO_US(x) \
((uint32_t)(((uint64_t) (x) * 30517578125UL) / 1000000000UL))
/** \defgroup Timer API return codes.
*
* @{
*/
#define TICKER_STATUS_SUCCESS 0 /**< Success. */
#define TICKER_STATUS_FAILURE 1 /**< Failure. */
#define TICKER_STATUS_BUSY 2 /**< Busy, requested feature will
* complete later in time as job is
* disabled or at lower execution
* priority than the caller.
*/
/**
* @}
*/
/** \defgroup Timer API common defaults parameter values.
*
* @{
*/
#define TICKER_NULL ((uint8_t)((uint8_t)0 - 1))
#define TICKER_NULL_REMAINDER 0
#define TICKER_NULL_PERIOD 0
#define TICKER_NULL_SLOT 0
#define TICKER_NULL_LAZY 0
/**
* @}
*/
/** \brief Timer node type size.
*/
#define TICKER_NODE_T_SIZE 36
/** \brief Timer user type size.
*/
#define TICKER_USER_T_SIZE 8
/** \brief Timer user operation type size.
*/
#define TICKER_USER_OP_T_SIZE 44
/** \brief Timer timeout function type.
*/
typedef void (*ticker_timeout_func) (uint32_t ticks_at_expire,
uint32_t remainder, uint16_t lazy,
void *context);
/** \brief Timer operation complete function type.
*/
typedef void (*ticker_op_func) (uint32_t status, void *op_context);
/** \brief Timer module initialization.
*
* \param[in] instance_index Timer mode instance 0 or 1 (uses RTC0 CMP0 or
* CMP1 respectively).
* \param[in] count_node Max. no. of ticker nodes to initialise.
* \param[in] node
* \param[in] count_user
* \param[in] user
* \param[in] count_op
* \param[in] user_op
*/
uint32_t ticker_init(uint8_t instance_index, uint8_t count_node, void *node,
uint8_t count_user, void *user, uint8_t count_op,
void *user_op);
void ticker_trigger(uint8_t instance_index);
uint32_t ticker_start(uint8_t instance_index, uint8_t user_id,
uint8_t ticker_id, uint32_t ticks_anchor,
uint32_t ticks_first, uint32_t ticks_periodic,
uint32_t remainder_periodic, uint16_t lazy,
uint16_t ticks_slot,
ticker_timeout_func ticker_timeout_func, void *context,
ticker_op_func fp_op_func, void *op_context);
uint32_t ticker_update(uint8_t instance_index, uint8_t user_id,
uint8_t ticker_id, uint16_t ticks_drift_plus,
uint16_t ticks_drift_minus, uint16_t ticks_slot_plus,
uint16_t ticks_slot_minus, uint16_t lazy, uint8_t force,
ticker_op_func fp_op_func, void *op_context);
uint32_t ticker_stop(uint8_t instance_index, uint8_t user_id,
uint8_t ticker_id, ticker_op_func fp_op_func,
void *op_context);
uint32_t ticker_next_slot_get(uint8_t instance_index, uint8_t user_id,
uint8_t *ticker_id_head,
uint32_t *ticks_current,
uint32_t *ticks_to_expire,
ticker_op_func fp_op_func, void *op_context);
uint32_t ticker_job_idle_get(uint8_t instance_index, uint8_t user_id,
ticker_op_func fp_op_func, void *op_context);
void ticker_job_sched(uint8_t instance_index);
uint32_t ticker_ticks_now_get(void);
uint32_t ticker_ticks_diff_get(uint32_t ticks_now, uint32_t ticks_old);
#endif

View file

@ -0,0 +1,400 @@
/*
* 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/stack.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_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);
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);
}
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);

View file

@ -0,0 +1,34 @@
/*
* 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.
*/
#ifndef _DEFINES_H_
#define _DEFINES_H_
#include <toolchain.h>
#if !defined(ALIGNED)
#define ALIGNED(x) __aligned(x)
#endif
#define ALIGN4(x) (((uint32_t)(x)+3) & (~((uint32_t)3)))
#define DOUBLE_BUFFER_SIZE 2
#define TRIPLE_BUFFER_SIZE 3
#define BDADDR_SIZE 6
#endif /* _DEFINES_H_ */

View file

@ -0,0 +1,196 @@
/*
* 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 <stdint.h>
#include <string.h>
#include "defines.h"
#include "mem.h"
void mem_init(void *mem_pool, uint16_t mem_size, uint16_t mem_count,
void **mem_head)
{
*mem_head = mem_pool;
/* Store free mem_count after the list's next pointer */
memcpy(((uint8_t *)mem_pool + sizeof(mem_pool)),
(uint8_t *)&mem_count, sizeof(mem_count));
/* Initialize next pointers to form a free list,
* next pointer is stored in the first 32-bit of each block
*/
memset(((uint8_t *)mem_pool + (mem_size * (--mem_count))), 0,
sizeof(mem_pool));
while (mem_count--) {
uint32_t next;
next = (uint32_t)((uint8_t *) mem_pool +
(mem_size * (mem_count + 1)));
memcpy(((uint8_t *)mem_pool + (mem_size * mem_count)),
(void *)&next, sizeof(next));
}
}
void *mem_acquire(void **mem_head)
{
if (*mem_head) {
uint16_t free_count;
void *mem;
/* Get the free count from the list and decrement it */
memcpy((void *)&free_count,
((uint8_t *)*mem_head + sizeof(mem_head)),
sizeof(free_count));
free_count--;
mem = *mem_head;
memcpy(mem_head, mem, sizeof(mem_head));
/* Store free mem_count after the list's next pointer */
if (*mem_head) {
memcpy(((uint8_t *)*mem_head + sizeof(mem_head)),
(uint8_t *)&free_count, sizeof(free_count));
}
return mem;
}
return NULL;
}
void mem_release(void *mem, void **mem_head)
{
uint16_t free_count = 0;
/* Get the free count from the list and increment it */
if (*mem_head) {
memcpy(&free_count, ((uint8_t *)*mem_head + sizeof(mem_head)),
sizeof(free_count));
}
free_count++;
memcpy(mem, mem_head, sizeof(mem));
*mem_head = mem;
/* Store free mem_count after the list's next pointer */
memcpy(((uint8_t *)*mem_head + sizeof(mem_head)),
(uint8_t *)&free_count, sizeof(free_count));
}
uint16_t mem_free_count_get(void *mem_head)
{
uint16_t free_count = 0;
/* Get the free count from the list */
if (mem_head) {
memcpy(&free_count, ((uint8_t *)mem_head + sizeof(mem_head)),
sizeof(free_count));
}
return free_count;
}
void *mem_get(void *mem_pool, uint16_t mem_size, uint16_t index)
{
return ((void *)((uint8_t *)mem_pool + (mem_size * index)));
}
uint16_t mem_index_get(void *mem, void *mem_pool, uint16_t mem_size)
{
return ((uint16_t)((uint8_t *)mem - (uint8_t *)mem_pool) /
mem_size);
}
void mem_rcopy(uint8_t *dst, uint8_t const *src, uint16_t len)
{
src += len;
while (len--) {
*dst++ = *--src;
}
}
uint8_t mem_is_zero(uint8_t *src, uint16_t len)
{
while (len--) {
if (*src++) {
return 1;
}
}
return 0;
}
uint32_t mem_ut(void)
{
#define BLOCK_SIZE ALIGN4(10)
#define BLOCK_COUNT 10
uint8_t ALIGNED(4) pool[BLOCK_COUNT][BLOCK_SIZE];
void *mem_free;
void *mem_used;
uint16_t mem_free_count;
void *mem;
mem_init(pool, BLOCK_SIZE, BLOCK_COUNT, &mem_free);
mem_free_count = mem_free_count_get(mem_free);
if (mem_free_count != BLOCK_COUNT) {
return 1;
}
mem_used = 0;
while (mem_free_count--) {
uint16_t mem_free_count_current;
mem = mem_acquire(&mem_free);
mem_free_count_current = mem_free_count_get(mem_free);
if (mem_free_count != mem_free_count_current) {
return 2;
}
memcpy(mem, &mem_used, sizeof(mem));
mem_used = mem;
}
mem = mem_acquire(&mem_free);
if (mem) {
return 3;
}
while (++mem_free_count < BLOCK_COUNT) {
uint16_t mem_free_count_current;
mem = mem_used;
memcpy(&mem_used, mem, sizeof(void *));
mem_release(mem, &mem_free);
mem_free_count_current = mem_free_count_get(mem_free);
if ((mem_free_count + 1) != mem_free_count_current) {
return 4;
}
}
if (mem != mem_free) {
return 5;
}
if (mem_free_count_get(mem_free) != BLOCK_COUNT) {
return 6;
}
return 0;
}

View file

@ -0,0 +1,35 @@
/*
* 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.
*/
#ifndef _MEM_H_
#define _MEM_H_
void mem_init(void *mem_pool, uint16_t mem_size, uint16_t mem_count,
void **mem_head);
void *mem_acquire(void **mem_head);
void mem_release(void *mem, void **mem_head);
uint16_t mem_free_count_get(void *mem_head);
void *mem_get(void *mem_pool, uint16_t mem_size, uint16_t index);
uint16_t mem_index_get(void *mem, void *mem_pool, uint16_t mem_size);
void mem_rcopy(uint8_t *dst, uint8_t const *src, uint16_t len);
uint8_t mem_is_zero(uint8_t *src, uint16_t len);
uint32_t mem_ut(void);
#endif /* _MEM_H_ */

View file

@ -0,0 +1,96 @@
/*
* 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 <stdint.h>
void *memq_init(void *link, void **head, void **tail)
{
/* head and tail pointer to the initial link node */
*head = *tail = link;
return link;
}
void *memq_enqueue(void *mem, void *link, void **tail)
{
/* make the current tail link node point to new link node */
*((void **)*tail) = link;
/* assign mem to current tail link node */
*((void **)*tail + 1) = mem;
/* increment the tail! */
*tail = link;
return link;
}
void *memq_dequeue(void *tail, void **head, void **mem)
{
void *link;
/* if head and tail are equal, then queue empty */
if (*head == tail) {
return 0;
}
/* pick the head link node */
link = *head;
/* extract the element node */
if (mem) {
*mem = *((void **)link + 1);
}
/* increment the head to next link node */
*head = *((void **)link);
return link;
}
uint32_t memq_ut(void)
{
void *head;
void *tail;
void *link;
void *link_0[2];
void *link_1[2];
link = memq_init(&link_0[0], &head, &tail);
if ((link != &link_0[0]) || (head != &link_0[0])
|| (tail != &link_0[0])) {
return 1;
}
link = memq_dequeue(tail, &head, 0);
if ((link) || (head != &link_0[0])) {
return 2;
}
link = memq_enqueue(0, &link_1[0], &tail);
if ((link != &link_1[0]) || (tail != &link_1[0])) {
return 3;
}
link = memq_dequeue(tail, &head, 0);
if ((link != &link_0[0]) || (tail != &link_1[0])
|| (head != &link_1[0])) {
return 4;
}
return 0;
}

View file

@ -0,0 +1,25 @@
/*
* 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.
*/
#ifndef _MEMQ_H_
#define _MEMQ_H_
void *memq_init(void *link, void **head, void **tail);
void *memq_enqueue(void *mem, void *link, void **tail);
void *memq_dequeue(void *tail, void **head, void **mem);
#endif

View file

@ -0,0 +1,134 @@
/*
* 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 <stdint.h>
#include "util.h"
/* Convert the integer D to a string and save the string in BUF. If
* BASE is equal to 'd', interpret that D is decimal, and if BASE is
* equal to 'x', interpret that D is hexadecimal.
*/
void util_itoa(char *buf, int base, int d)
{
char *p = buf;
char *p1, *p2;
unsigned long ud = d;
int divisor = 10;
/* If %d is specified and D is minus, put `-' in the head. */
if (base == 'd' && d < 0) {
*p++ = '-';
buf++;
ud = -d;
} else if (base == 'x')
divisor = 16;
/* Divide UD by DIVISOR until UD == 0. */
do {
int remainder = ud % divisor;
*p++ =
(remainder < 10) ? remainder + '0' : remainder + 'a' - 10;
} while (ud /= divisor);
/* Terminate BUF. */
*p = 0;
/* Reverse BUF. */
p1 = buf;
p2 = p - 1;
while (p1 < p2) {
char tmp = *p1;
*p1 = *p2;
*p2 = tmp;
p1++;
p2--;
}
}
int util_atoi(char *s)
{
int val = 0;
while (*s) {
if (*s < '0' || *s > '9')
return val;
val = (val * 10) + (*s - '0');
s++;
}
return val;
}
/* Format a string and print it on the screen, just like the libc
* function printf.
*/
void util_sprintf(char *str, const char *format, ...)
{
char **arg = (char **)&format;
int c;
char buf[20];
arg++;
while ((c = *format++)) {
if (c != '%')
*str++ = c;
else {
char *p;
c = *format++;
switch (c) {
case 'd':
case 'u':
case 'x':
util_itoa(buf, c, *((int *)arg++));
p = buf;
goto string;
case 's':
p = *arg++;
if (!p)
p = "(null)";
string:
while (*p)
*str++ = *p++;
break;
default:
*str++ = (*((int *)arg++));
break;
}
}
}
*str = 0;
}
uint8_t util_ones_count_get(uint8_t *octets, uint8_t octets_len)
{
uint8_t one_count = 0;
while (octets_len--) {
uint8_t bite;
bite = *octets;
while (bite) {
bite &= (bite - 1);
one_count++;
}
octets++;
}
return one_count;
}

View file

@ -0,0 +1,26 @@
/*
* 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.
*/
#ifndef _UTIL_H_
#define _UTIL_H_
void util_itoa(char *buf, int base, int d);
int util_atoi(char *s);
void util_sprintf(char *str, const char *format, ...);
uint8_t util_ones_count_get(uint8_t *octets, uint8_t octets_len);
#endif

View file

@ -0,0 +1,168 @@
/*
* 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 <stdint.h>
#include "hal_irq.h"
#include "work.h"
static struct work *_work_head;
#ifdef __GNUC__
static inline uint32_t __disable_irq(void)
{
uint32_t result;
__asm__ volatile ("MRS %0, PRIMASK\n\t CPSID i":"=r" (result));
return (result & 0x01);
}
static inline void __enable_irq(void)
{
__asm__ volatile ("CPSIE i");
}
#endif
void work_enable(uint8_t group)
{
irq_enable(group);
}
void work_disable(uint8_t group)
{
irq_disable(group);
}
uint8_t work_enabled(uint8_t group)
{
return irq_enabled(group);
}
uint32_t work_schedule(struct work *w, uint8_t chain)
{
int was_masked = __disable_irq();
struct work *prev;
struct work *curr;
/* Dequeue expired work at head */
while ((_work_head)
&& (_work_head->ack == _work_head->req)
) {
_work_head = _work_head->next;
}
/* Dequeue expired in between list and find last node */
curr = _work_head;
prev = curr;
while (curr) {
/* delete expired work */
if (curr->ack == curr->req) {
prev->next = curr->next;
} else {
prev = curr;
}
curr = curr->next;
}
/* chain, if explicitly requested, or if work not at current level */
chain = chain || (!irq_priority_equal(w->group))
|| (!irq_enabled(w->group));
/* Already in List */
curr = _work_head;
while (curr) {
if (curr == w) {
if (!chain) {
break;
}
if (!was_masked) {
__enable_irq();
}
return 1;
}
curr = curr->next;
}
/* handle work(s) that can be inline */
if (!chain) {
w->req = w->ack;
if (!was_masked) {
__enable_irq();
}
if (w->fp) {
w->fp(w->params);
}
return 0;
}
/* New, add to the list */
w->req = w->ack + 1;
w->next = 0;
if (prev == curr) {
_work_head = w;
} else {
prev->next = w;
}
irq_pending_set(w->group);
if (!was_masked) {
__enable_irq();
}
return 0;
}
void work_run(uint8_t group)
{
int was_masked = __disable_irq();
struct work *curr = _work_head;
while (curr) {
if ((curr->group == group) && (curr->ack != curr->req)) {
curr->ack = curr->req;
if (curr->fp) {
if (curr->next) {
irq_pending_set(group);
}
if (!was_masked) {
__enable_irq();
}
curr->fp(curr->params);
return;
}
}
curr = curr->next;
}
if (!was_masked) {
__enable_irq();
}
}

View file

@ -0,0 +1,38 @@
/*
* 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.
*/
#ifndef _WORK_H_
#define _WORK_H_
typedef void (*work_fp) (void *params);
struct work {
void *next;
uint8_t req;
uint8_t ack;
uint8_t group;
work_fp fp;
void *params;
};
void work_enable(uint8_t group);
void work_disable(uint8_t group);
uint8_t work_enabled(uint8_t group);
uint32_t work_schedule(struct work *w, uint8_t chain);
void work_run(uint8_t group);
#endif /* _WORK_H_ */

View file

@ -256,7 +256,7 @@ static void hexdump(const char *str, const uint8_t *packet, size_t length)
int n = 0;
if (!length) {
printf("%s zero-length signal packet\n");
printf("%s zero-length signal packet\n", str);
return;
}

View file

@ -687,7 +687,8 @@ void on_nble_gatts_indicate_rsp(const struct nble_gatts_indicate_rsp *rsp)
bt_conn_unref(conn);
}
int bt_gatt_exchange_mtu(struct bt_conn *conn, bt_gatt_rsp_func_t func)
int bt_gatt_exchange_mtu(struct bt_conn *conn,
struct bt_gatt_exchange_params *params)
{
return -ENOSYS;
}
@ -1393,7 +1394,7 @@ int bt_gatt_unsubscribe(struct bt_conn *conn,
return gatt_write_ccc(conn, params);
}
void bt_gatt_cancel(struct bt_conn *conn)
void bt_gatt_cancel(struct bt_conn *conn, void *params)
{
BT_DBG("");
}

View file

@ -245,12 +245,12 @@ void on_nble_gattc_read_multi_rsp(const struct nble_gattc_read_rsp *rsp,
struct nble_gattc_write_param;
typedef void (*bt_att_func_t)(struct bt_conn *conn, uint8_t err,
typedef void (*nble_att_func_t)(struct bt_conn *conn, uint8_t err,
const struct nble_gattc_write_param *par);
struct nble_gattc_write_param {
/* Function invoked upon write response */
bt_att_func_t func;
nble_att_func_t func;
/* User specific data */
void *user_data[2];
};

View file

@ -121,12 +121,20 @@ void rpc_transmit_cb(struct net_buf *buf)
hdr->len = buf->len - sizeof(*hdr);
hdr->channel = 0;
hdr->src_cpu_id = 0;
#if defined(CONFIG_BLUETOOTH_NRF51_PM)
/* Wake-up nble */
nrf51_wakeup();
#endif
while (buf->len) {
uart_poll_out(nble_dev, net_buf_pull_u8(buf));
}
net_buf_unref(buf);
#if defined(CONFIG_BLUETOOTH_NRF51_PM)
/* TODO check if FIFO is empty */
/* Allow nble to go to deep sleep */
nrf51_allow_sleep();
#endif
}
static size_t nble_discard(struct device *uart, size_t len)

View file

@ -28,12 +28,12 @@
static struct device *nrf51_gpio;
int nrf51_enable(void)
int nrf51_wakeup(void)
{
return gpio_pin_write(nrf51_gpio, NBLE_BTWAKE_PIN, 1);
}
int nrf51_disable(void)
int nrf51_allow_sleep(void)
{
return gpio_pin_write(nrf51_gpio, NBLE_BTWAKE_PIN, 0);
}
@ -112,5 +112,5 @@ int nrf51_init(struct device *dev)
return -ENODEV;
}
return nrf51_enable();
return nrf51_wakeup();
}

View file

@ -16,6 +16,6 @@
* limitations under the License.
*/
int nrf51_disable(void);
int nrf51_enable(void);
int nrf51_allow_sleep(void);
int nrf51_wakeup(void);
int nrf51_init(struct device *dev);

View file

@ -24,6 +24,8 @@
extern "C" {
#endif
#include <misc/slist.h>
/* Error codes for Error response PDU */
#define BT_ATT_ERR_INVALID_HANDLE 0x01
#define BT_ATT_ERR_READ_NOT_PERMITTED 0x02
@ -48,6 +50,22 @@ extern "C" {
#define BT_ATT_ERR_PROCEDURE_IN_PROGRESS 0xfe
#define BT_ATT_ERR_OUT_OF_RANGE 0xff
typedef void (*bt_att_func_t)(struct bt_conn *conn, uint8_t err,
const void *pdu, uint16_t length,
void *user_data);
typedef void (*bt_att_destroy_t)(void *user_data);
/* ATT request context */
struct bt_att_req {
sys_snode_t node;
bt_att_func_t func;
bt_att_destroy_t destroy;
struct net_buf *buf;
#if defined(CONFIG_BLUETOOTH_SMP)
bool retrying;
#endif /* CONFIG_BLUETOOTH_SMP */
};
#ifdef __cplusplus
}
#endif

View file

@ -707,6 +707,7 @@ typedef void (*bt_gatt_indicate_func_t)(struct bt_conn *conn,
/** @brief GATT Indicate Value parameters */
struct bt_gatt_indicate_params {
struct bt_att_req _req;
/** Indicate Attribute object*/
const struct bt_gatt_attr *attr;
/** Indicate Value callback */
@ -736,13 +737,13 @@ int bt_gatt_indicate(struct bt_conn *conn,
/* Client API */
/** @typedef bt_gatt_rsp_func_t
* @brief Response callback function
*
* @param conn Connection object.
* @param err ATT error code.
*/
typedef void (*bt_gatt_rsp_func_t)(struct bt_conn *conn, uint8_t err);
/** @brief GATT Exchange MTU parameters */
struct bt_gatt_exchange_params {
struct bt_att_req _req;
/** Response callback */
void (*func)(struct bt_conn *conn, uint8_t err,
struct bt_gatt_exchange_params *params);
};
/** @brief Exchange MTU
*
@ -752,11 +753,12 @@ typedef void (*bt_gatt_rsp_func_t)(struct bt_conn *conn, uint8_t err);
* NOTE: Shall only be used once per connection.
*
* @param conn Connection object.
* @param func Exchange MTU Response callback function.
* @param params Exchange MTU parameters.
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_gatt_exchange_mtu(struct bt_conn *conn, bt_gatt_rsp_func_t func);
int bt_gatt_exchange_mtu(struct bt_conn *conn,
struct bt_gatt_exchange_params *params);
struct bt_gatt_discover_params;
@ -788,6 +790,7 @@ enum {
/** @brief GATT Discover Attributes parameters */
struct bt_gatt_discover_params {
struct bt_att_req _req;
/** Discover UUID type */
struct bt_uuid *uuid;
/** Discover attribute callback */
@ -863,6 +866,7 @@ typedef uint8_t (*bt_gatt_read_func_t)(struct bt_conn *conn, uint8_t err,
* @param handles Handles to read in Read Multiple Characteristic Values
*/
struct bt_gatt_read_params {
struct bt_att_req _req;
bt_gatt_read_func_t func;
size_t handle_count;
union {
@ -902,6 +906,7 @@ typedef void (*bt_gatt_write_func_t)(struct bt_conn *conn, uint8_t err,
/** @brief GATT Write parameters */
struct bt_gatt_write_params {
struct bt_att_req _req;
/** Response callback */
bt_gatt_write_func_t func;
/** Attribute handle */
@ -962,6 +967,7 @@ typedef uint8_t (*bt_gatt_notify_func_t)(struct bt_conn *conn,
/** @brief GATT Subscribe parameters */
struct bt_gatt_subscribe_params {
struct bt_att_req _req;
bt_addr_le_t _peer;
/** Notification value callback */
bt_gatt_notify_func_t notify;
@ -1011,8 +1017,9 @@ int bt_gatt_unsubscribe(struct bt_conn *conn,
/** @brief Cancel GATT pending request
*
* @param conn Connection object.
* @param params Requested params address.
*/
void bt_gatt_cancel(struct bt_conn *conn);
void bt_gatt_cancel(struct bt_conn *conn, void *params);
#ifdef __cplusplus
}

View file

@ -21,6 +21,7 @@
#include <toolchain.h>
#include <stdint.h>
#include <string.h>
#include <misc/util.h>
#ifdef __cplusplus
extern "C" {
@ -28,6 +29,8 @@ extern "C" {
#define BT_ADDR_LE_PUBLIC 0x00
#define BT_ADDR_LE_RANDOM 0x01
#define BT_ADDR_LE_PUBLIC_ID 0x02
#define BT_ADDR_LE_RANDOM_ID 0x03
typedef struct {
uint8_t val[6];
@ -62,8 +65,12 @@ static inline void bt_addr_le_copy(bt_addr_le_t *dst, const bt_addr_le_t *src)
}
/* HCI Error Codes */
#define BT_HCI_ERR_UNKNOWN_CMD 0x01
#define BT_HCI_ERR_UNKNOWN_CONN_ID 0x02
#define BT_HCI_ERR_AUTHENTICATION_FAIL 0x05
#define BT_HCI_ERR_PIN_OR_KEY_MISSING 0x06
#define BT_HCI_ERR_MEM_CAPACITY_EXCEEDED 0x07
#define BT_HCI_ERR_CMD_DISALLOWED 0x0c
#define BT_HCI_ERR_INSUFFICIENT_RESOURCES 0x0d
#define BT_HCI_ERR_UNSUPP_FEATURE_PARAMS_VAL 0x11
#define BT_HCI_ERR_REMOTE_USER_TERM_CONN 0x13
@ -121,18 +128,25 @@ struct bt_hci_cmd_hdr {
uint8_t param_len;
} __packed;
/* LMP features */
#define BT_LMP_NO_BREDR 0x20
#define BT_LMP_LE 0x40
#define BT_LMP_REMOTE_EXT_FEATURES 0x80
/* Supported Commands */
#define BT_CMD_TEST(cmd, octet, bit) (cmd[octet] & BIT(bit))
#define BT_CMD_LE_STATES(cmd) BT_CMD_TEST(cmd, 28, 3)
/* Host features */
#define BT_LMP_HOST_SSP 0x01
#define BT_FEAT_TEST(feat, page, octet, bit) (feat[page][octet] & BIT(bit))
#define BT_FEAT_BREDR(feat) !BT_FEAT_TEST(feat, 0, 4, 5)
#define BT_FEAT_LE(feat) BT_FEAT_TEST(feat, 0, 4, 6)
#define BT_FEAT_EXT_FEATURES(feat) BT_FEAT_TEST(feat, 0, 7, 7)
#define BT_FEAT_HOST_SSP(feat) BT_FEAT_TEST(feat, 1, 0, 0)
#define BT_FEAT_SC(feat) BT_FEAT_TEST(feat, 2, 1, 0)
/* LE features */
#define BT_HCI_LE_ENCRYPTION 0x01
#define BT_HCI_LE_CONN_PARAM_REQ_PROC 0x02
#define BT_HCI_LE_SLAVE_FEATURES 0x08
#define BT_FEAT_LE_ENCR(feat) BT_FEAT_TEST(feat, 0, 0, 0)
#define BT_FEAT_LE_CONN_PARAM_REQ_PROC(feat) BT_FEAT_TEST(feat, 0, 0, 1)
#define BT_FEAT_LE_SLAVE_FEATURE_XCHG(feat) BT_FEAT_TEST(feat, 0, 0, 3)
/* LE States */
#define BT_LE_STATES_SLAVE_CONN_ADV(states) (states & 0x0000004000000000)
/* Bonding/authentication types */
#define BT_HCI_NO_BONDING 0x00
@ -180,10 +194,16 @@ struct bt_hci_cmd_hdr {
#define BT_OGF_BASEBAND 0x03
#define BT_OGF_INFO 0x04
#define BT_OGF_LE 0x08
#define BT_OGF_VS 0x3f
/* Construct OpCode from OGF and OCF */
#define BT_OP(ogf, ocf) ((ocf) | ((ogf) << 10))
/* Obtain OGF from OpCode */
#define BT_OGF(opcode) (((opcode) >> 10) & BIT_MASK(6))
/* Obtain OCF from OpCode */
#define BT_OCF(opcode) ((opcode) & BIT_MASK(10))
#define BT_HCI_OP_INQUIRY BT_OP(BT_OGF_LINK_CTRL, 0x0001)
struct bt_hci_op_inquiry {
uint8_t lap[3];
@ -300,6 +320,11 @@ struct bt_hci_cp_read_remote_ext_features {
uint8_t page;
} __packed;
#define BT_HCI_OP_READ_REMOTE_VERSION_INFO BT_OP(BT_OGF_LINK_CTRL, 0x001d)
struct bt_hci_cp_read_remote_version_info {
uint16_t handle;
} __packed;
#define BT_HCI_OP_IO_CAPABILITY_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x002b)
struct bt_hci_cp_io_capability_reply {
bt_addr_t bdaddr;
@ -342,6 +367,11 @@ struct bt_hci_cp_set_event_mask {
#define BT_HCI_OP_RESET BT_OP(BT_OGF_BASEBAND, 0x0003)
#define BT_HCI_OP_WRITE_LOCAL_NAME BT_OP(BT_OGF_BASEBAND, 0x0013)
struct bt_hci_write_local_name {
uint8_t local_name[248];
} __packed;
#define BT_HCI_OP_WRITE_SCAN_ENABLE BT_OP(BT_OGF_BASEBAND, 0x001a)
#define BT_BREDR_SCAN_DISABLED 0x00
#define BT_BREDR_SCAN_INQUIRY 0x01
@ -385,6 +415,11 @@ struct bt_hci_cp_write_le_host_supp {
uint8_t simul;
} __packed;
#define BT_HCI_OP_WRITE_SC_HOST_SUPP BT_OP(BT_OGF_BASEBAND, 0x007a)
struct bt_hci_cp_write_sc_host_supp {
uint8_t sc_support;
} __packed;
#define BT_HCI_OP_READ_LOCAL_VERSION_INFO BT_OP(BT_OGF_INFO, 0x0001)
struct bt_hci_rp_read_local_version_info {
uint8_t status;
@ -401,6 +436,17 @@ struct bt_hci_rp_read_supported_commands {
uint8_t commands[36];
} __packed;
#define BT_HCI_OP_READ_LOCAL_EXT_FEATURES BT_OP(BT_OGF_INFO, 0x0004)
struct bt_hci_cp_read_local_ext_features {
uint8_t page;
};
struct bt_hci_rp_read_local_ext_features {
uint8_t status;
uint8_t page;
uint8_t max_page;
uint8_t ext_features[8];
} __packed;
#define BT_HCI_OP_READ_LOCAL_FEATURES BT_OP(BT_OGF_INFO, 0x0003)
struct bt_hci_rp_read_local_features {
uint8_t status;
@ -422,13 +468,12 @@ struct bt_hci_rp_read_bd_addr {
bt_addr_t bdaddr;
} __packed;
/* BLE */
#define BT_HCI_OP_LE_SET_EVENT_MASK BT_OP(BT_OGF_LE, 0x0001)
struct bt_hci_cp_le_set_event_mask {
uint8_t events[8];
} __packed;
struct bt_hci_rp_le_set_event_mask {
uint8_t status;
} __packed;
#define BT_HCI_OP_LE_READ_BUFFER_SIZE BT_OP(BT_OGF_LE, 0x0002)
struct bt_hci_rp_le_read_buffer_size {
@ -444,6 +489,9 @@ struct bt_hci_rp_le_read_local_features {
} __packed;
#define BT_HCI_OP_LE_SET_RANDOM_ADDRESS BT_OP(BT_OGF_LE, 0x0005)
struct bt_hci_cp_le_set_random_address {
bt_addr_t bdaddr;
} __packed;
/* Advertising types */
#define BT_LE_ADV_IND 0x00
@ -465,6 +513,12 @@ struct bt_hci_cp_le_set_adv_param {
uint8_t filter_policy;
} __packed;
#define BT_HCI_OP_LE_READ_ADV_CH_TX_POWER BT_OP(BT_OGF_LE, 0x0007)
struct bt_hci_rp_le_read_ch_tx_power {
uint8_t status;
int8_t tx_power_level;
} __packed;
#define BT_HCI_OP_LE_SET_ADV_DATA BT_OP(BT_OGF_LE, 0x0008)
struct bt_hci_cp_le_set_adv_data {
uint8_t len;
@ -528,6 +582,24 @@ struct bt_hci_cp_le_create_conn {
#define BT_HCI_OP_LE_CREATE_CONN_CANCEL BT_OP(BT_OGF_LE, 0x000e)
#define BT_HCI_OP_LE_READ_WL_SIZE BT_OP(BT_OGF_LE, 0x000f)
struct bt_hci_rp_le_read_wl_size {
uint8_t status;
uint8_t wl_size;
} __packed;
#define BT_HCI_OP_LE_CLEAR_WL BT_OP(BT_OGF_LE, 0x0010)
#define BT_HCI_OP_LE_ADD_DEV_TO_WL BT_OP(BT_OGF_LE, 0x0011)
struct bt_hci_cp_le_add_dev_to_wl {
bt_addr_le_t addr;
} __packed;
#define BT_HCI_OP_LE_REM_DEV_FROM_WL BT_OP(BT_OGF_LE, 0x0012)
struct bt_hci_cp_le_rem_dev_from_wl {
bt_addr_le_t addr;
} __packed;
#define BT_HCI_OP_LE_CONN_UPDATE BT_OP(BT_OGF_LE, 0x0013)
struct hci_cp_le_conn_update {
uint16_t handle;
@ -539,6 +611,21 @@ struct hci_cp_le_conn_update {
uint16_t max_ce_len;
} __packed;
#define BT_HCI_OP_LE_SET_HOST_CH_CLASSIF BT_OP(BT_OGF_LE, 0x0014)
struct bt_hci_cp_le_set_host_ch_classif {
uint8_t ch_map[5];
} __packed;
#define BT_HCI_OP_LE_READ_CH_MAP BT_OP(BT_OGF_LE, 0x0015)
struct bt_hci_cp_le_read_ch_map {
uint16_t handle;
} __packed;
struct bt_hci_rp_le_read_ch_map {
uint8_t status;
uint16_t handle;
uint8_t ch_map[5];
} __packed;
#define BT_HCI_OP_LE_READ_REMOTE_FEATURES BT_OP(BT_OGF_LE, 0x0016)
struct bt_hci_cp_le_read_remote_features {
uint16_t handle;
@ -579,6 +666,30 @@ struct bt_hci_cp_le_ltk_req_neg_reply {
uint16_t handle;
} __packed;
#define BT_HCI_OP_LE_READ_SUPP_STATES BT_OP(BT_OGF_LE, 0x001c)
struct bt_hci_rp_le_read_supp_states {
uint8_t status;
uint8_t le_states[8];
} __packed;
#define BT_HCI_OP_LE_RX_TEST BT_OP(BT_OGF_LE, 0x001d)
struct bt_hci_cp_le_rx_test {
uint8_t rx_ch;
} __packed;
#define BT_HCI_OP_LE_TX_TEST BT_OP(BT_OGF_LE, 0x001e)
struct bt_hci_cp_le_tx_test {
uint8_t tx_ch;
uint8_t test_data_len;
uint8_t pkt_payload;
} __packed;
#define BT_HCI_OP_LE_TEST_END BT_OP(BT_OGF_LE, 0x001f)
struct bt_hci_rp_le_test_end {
uint8_t status;
uint16_t rx_pkt_count;
} __packed;
#define BT_HCI_OP_LE_CONN_PARAM_REQ_REPLY BT_OP(BT_OGF_LE, 0x0020)
struct bt_hci_cp_le_conn_param_req_reply {
uint16_t handle;
@ -596,6 +707,30 @@ struct bt_hci_cp_le_conn_param_req_neg_reply {
uint8_t reason;
} __packed;
#define BT_HCI_OP_LE_SET_DATA_LEN BT_OP(BT_OGF_LE, 0x0022)
struct bt_hci_cp_le_set_data_len {
uint16_t handle;
uint16_t tx_octets;
uint16_t tx_time;
} __packed;
struct bt_hci_rp_le_set_data_len {
uint8_t status;
uint16_t handle;
} __packed;
#define BT_HCI_OP_LE_READ_DEFAULT_DATA_LEN BT_OP(BT_OGF_LE, 0x0023)
struct bt_hci_rp_le_read_default_data_len {
uint8_t status;
uint16_t max_tx_octets;
uint16_t max_tx_time;
} __packed;
#define BT_HCI_OP_LE_WRITE_DEFAULT_DATA_LEN BT_OP(BT_OGF_LE, 0x0024)
struct bt_hci_cp_le_write_default_data_len {
uint16_t max_tx_octets;
uint16_t max_tx_time;
} __packed;
#define BT_HCI_OP_LE_P256_PUBLIC_KEY BT_OP(BT_OGF_LE, 0x0025)
#define BT_HCI_OP_LE_GENERATE_DHKEY BT_OP(BT_OGF_LE, 0x0026)
@ -603,6 +738,66 @@ struct bt_hci_cp_le_generate_dhkey {
uint8_t key[64];
} __packed;
#define BT_HCI_OP_LE_ADD_DEV_TO_RL BT_OP(BT_OGF_LE, 0x0027)
struct bt_hci_cp_le_add_dev_to_rl {
bt_addr_le_t peer_id_addr;
uint8_t peer_irk[16];
uint8_t local_irk[16];
} __packed;
#define BT_HCI_OP_LE_REM_DEV_FROM_RL BT_OP(BT_OGF_LE, 0x0028)
struct bt_hci_cp_le_rem_dev_from_rl {
bt_addr_le_t peer_id_addr;
} __packed;
#define BT_HCI_OP_LE_CLEAR_RL BT_OP(BT_OGF_LE, 0x0029)
#define BT_HCI_OP_LE_READ_RL_SIZE BT_OP(BT_OGF_LE, 0x002a)
struct bt_hci_rp_le_read_rl_size {
uint8_t status;
uint8_t rl_size;
} __packed;
#define BT_HCI_OP_LE_READ_PEER_RPA BT_OP(BT_OGF_LE, 0x002b)
struct bt_hci_cp_le_read_peer_rpa {
bt_addr_le_t peer_id_addr;
} __packed;
struct bt_hci_rp_le_read_peer_rpa {
uint8_t status;
bt_addr_t peer_rpa;
} __packed;
#define BT_HCI_OP_LE_READ_LOCAL_RPA BT_OP(BT_OGF_LE, 0x002c)
struct bt_hci_cp_le_read_local_rpa {
bt_addr_le_t peer_id_addr;
} __packed;
struct bt_hci_rp_le_read_local_rpa {
uint8_t status;
bt_addr_t local_rpa;
} __packed;
#define BT_HCI_ADDR_RES_DISABLE 0x00
#define BT_HCI_ADDR_RES_ENABLE 0x01
#define BT_HCI_OP_LE_SET_ADDR_RES_ENABLE BT_OP(BT_OGF_LE, 0x002d)
struct bt_hci_cp_le_set_addr_res_enable {
uint8_t enable;
} __packed;
#define BT_HCI_OP_LE_SET_RPA_TIMEOUT BT_OP(BT_OGF_LE, 0x002e)
struct bt_hci_cp_le_set_rpa_timeout {
uint8_t rpa_timeout;
} __packed;
#define BT_HCI_OP_LE_READ_MAX_DATA_LEN BT_OP(BT_OGF_LE, 0x002f)
struct bt_hci_rp_le_read_max_data_len {
uint8_t status;
uint16_t max_tx_octets;
uint16_t max_tx_time;
uint16_t max_rx_octets;
uint16_t max_rx_time;
} __packed;
/* Event definitions */
#define BT_HCI_EVT_VENDOR 0xff
@ -663,11 +858,15 @@ struct bt_hci_evt_remote_features {
} __packed;
#define BT_HCI_EVT_CMD_COMPLETE 0x0e
struct hci_evt_cmd_complete {
struct bt_hci_evt_cmd_complete {
uint8_t ncmd;
uint16_t opcode;
} __packed;
struct bt_hci_evt_cc_status {
uint8_t status;
} __packed;
#define BT_HCI_EVT_CMD_STATUS 0x0f
struct bt_hci_evt_cmd_status {
uint8_t status;
@ -842,6 +1041,15 @@ struct bt_hci_evt_le_conn_param_req {
uint16_t timeout;
} __packed;
#define BT_HCI_EVT_LE_DATA_LEN_CHANGE 0x07
struct bt_hci_evt_le_data_len_change {
uint16_t handle;
uint16_t max_tx_octets;
uint16_t max_tx_time;
uint16_t max_rx_octets;
uint16_t max_rx_time;
} __packed;
#define BT_HCI_EVT_LE_P256_PUBLIC_KEY_COMPLETE 0x08
struct bt_hci_evt_le_p256_public_key_complete {
uint8_t status;
@ -854,6 +1062,29 @@ struct bt_hci_evt_le_generate_dhkey_complete {
uint8_t dhkey[32];
} __packed;
#define BT_HCI_EVT_LE_ENH_CONN_COMPLETE 0x0a
struct bt_hci_evt_le_enh_conn_complete {
uint8_t status;
uint16_t handle;
uint8_t role;
bt_addr_le_t peer_addr;
bt_addr_t local_rpa;
bt_addr_t peer_rpa;
uint16_t interval;
uint16_t latency;
uint16_t supv_timeout;
uint8_t clock_accuracy;
} __packed;
#define BT_HCI_EVT_LE_DIRECTED_ADV_REPORT 0x02
struct bt_hci_ev_le_directed_adv_info {
uint8_t evt_type;
bt_addr_le_t dir_addr;
bt_addr_le_t addr;
int8_t rssi;
} __packed;
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,64 @@
/** @file
* @brief Bluetooth HCI RAW channel handling
*/
/*
* Copyright (c) 2016 Intel Corporation
*
* 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.
*/
#ifndef __BT_HCI_RAW_H
#define __BT_HCI_RAW_H
/**
* @brief HCI RAW channel
* @defgroup hci_raw HCI RAW channel
* @ingroup bluetooth
* @{
*/
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Send packet to the Bluetooth controller
*
* Send packet to the Bluetooth controller. Caller needs to
* implement netbuf pool.
*
* @param buf netbuf packet to be send
*
* @return Zero on success or (negative) error code otherwise.
*/
int bt_send(struct net_buf *buf);
/** @brief Enable Bluetooth RAW channel
*
* Enable Bluetooth RAW HCI channel.
*
* @param rx_queue netbuf queue where HCI packets received from the Bluetooth
* controller are to be queued. The queue is defined in the caller while
* the available buffers pools are handled in the stack.
*
* @return Zero on success or (negative) error code otherwise.
*/
int bt_enable_raw(struct nano_fifo *rx_queue);
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* __BT_HCI_RAW_H */

View file

@ -84,6 +84,8 @@ struct bt_l2cap_chan {
struct bt_l2cap_chan_ops *ops;
struct bt_l2cap_chan *_next;
bt_l2cap_chan_destroy_t destroy;
/* Response Timeout eXpired (RTX) timer */
struct nano_delayed_work rtx_work;
#if defined(CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL)
bt_l2cap_chan_state_t state;
/** Helps match request context during CoC */
@ -112,8 +114,6 @@ struct bt_l2cap_le_chan {
struct bt_l2cap_le_endpoint rx;
/** Channel Transmission Endpoint */
struct bt_l2cap_le_endpoint tx;
/* Response Timeout eXpired (RTX) timer */
struct nano_delayed_work rtx_work;
/** Segment SDU packet from upper layer */
struct net_buf *_sdu;
uint16_t _sdu_len;
@ -147,6 +147,8 @@ struct bt_l2cap_br_chan {
/** Channel Transmission Endpoint */
struct bt_l2cap_br_endpoint tx;
atomic_t flags[1];
/** Remote PSM to be connected */
uint16_t psm;
};
/** @brief L2CAP Channel operations structure. */

162
include/bluetooth/rfcomm.h Normal file
View file

@ -0,0 +1,162 @@
/** @file
* @brief Bluetooth RFCOMM handling
*/
/*
* Copyright (c) 2015-2016 Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1) Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2) Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3) Neither the name of Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __BT_RFCOMM_H
#define __BT_RFCOMM_H
/**
* @brief RFCOMM
* @defgroup bt_rfcomm RFCOMM
* @ingroup bluetooth
* @{
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <bluetooth/log.h>
#include <bluetooth/buf.h>
#include <bluetooth/conn.h>
struct bt_rfcomm_dlc;
/** @brief RFCOMM DLC operations structure. */
struct bt_rfcomm_dlc_ops {
/** DLC connected callback
*
* If this callback is provided it will be called whenever the
* connection completes.
*
* @param dlc The dlc that has been connected
*/
void (*connected)(struct bt_rfcomm_dlc *dlc);
/** DLC disconnected callback
*
* If this callback is provided it will be called whenever the
* dlc is disconnected, including when a connection gets
* rejected.
*
* @param dlc The dlc that has been Disconnected
*/
void (*disconnected)(struct bt_rfcomm_dlc *dlc);
/** DLC recv callback
*
* @param dlc The dlc receiving data.
* @param buf Buffer containing incoming data.
*/
void (*recv)(struct bt_rfcomm_dlc *dlc, struct net_buf *buf);
};
/** @brief RFCOMM DLC structure. */
struct bt_rfcomm_dlc {
/* Queue for outgoing data */
struct nano_fifo tx_queue;
/** TX credits */
struct nano_sem tx_credits;
atomic_t ref;
struct bt_rfcomm_session *session;
struct bt_rfcomm_dlc_ops *ops;
struct bt_rfcomm_dlc *_next;
uint16_t mtu;
uint8_t dlci;
uint8_t state;
uint8_t rx_credit;
bool initiator;
/* Stack for TX fiber */
BT_STACK(stack, 128);
};
struct bt_rfcomm_server {
/** Server Channel */
uint8_t channel;
/** Server accept callback
*
* This callback is called whenever a new incoming connection requires
* authorization.
*
* @param conn The connection that is requesting authorization
* @param dlc Pointer to received the allocated dlc
*
* @return 0 in case of success or negative value in case of error.
*/
int (*accept)(struct bt_conn *conn, struct bt_rfcomm_dlc **dlc);
struct bt_rfcomm_server *_next;
};
/** @brief Register RFCOMM server
*
* Register RFCOMM server for a channel, each new connection is authorized
* using the accept() callback which in case of success shall allocate the dlc
* structure to be used by the new connection.
*
* @param server Server structure.
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_rfcomm_server_register(struct bt_rfcomm_server *server);
/** @brief Send data to RFCOMM
*
* Send data from buffer to the dlc. Length should be less than or equal to
* mtu.
*
* @param dlc Dlc object.
* @param buf Data buffer.
*
* @return Bytes sent in case of success or negative value in case of error.
*/
int bt_rfcomm_dlc_send(struct bt_rfcomm_dlc *dlc, struct net_buf *buf);
/** @brief Get the buffer from fifo after reserving head room for RFCOMM, L2CAP
* and ACL headers.
*
* @param fifo Which FIFO to take the buffer from.
*
* @return New buffer.
*/
struct net_buf *bt_rfcomm_create_pdu(struct nano_fifo *fifo);
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* __BT_RFCOMM_H */

View file

@ -36,6 +36,7 @@ extern "C" {
/** @brief Bluetooth UUID types */
enum {
BT_UUID_TYPE_16,
BT_UUID_TYPE_32,
BT_UUID_TYPE_128,
};
@ -49,6 +50,11 @@ struct bt_uuid_16 {
uint16_t val;
};
struct bt_uuid_32 {
struct bt_uuid uuid;
uint32_t val;
};
struct bt_uuid_128 {
struct bt_uuid uuid;
uint8_t val[16];
@ -60,6 +66,12 @@ struct bt_uuid_128 {
.val = (value), \
}
#define BT_UUID_INIT_32(value) \
{ \
.uuid.type = BT_UUID_TYPE_32, \
.val = (value), \
}
#define BT_UUID_INIT_128(value...) \
{ \
.uuid.type = BT_UUID_TYPE_128, \
@ -68,10 +80,13 @@ struct bt_uuid_128 {
#define BT_UUID_DECLARE_16(value) \
((struct bt_uuid *) (&(struct bt_uuid_16) BT_UUID_INIT_16(value)))
#define BT_UUID_DECLARE_32(value) \
((struct bt_uuid *) (&(struct bt_uuid_32) BT_UUID_INIT_32(value)))
#define BT_UUID_DECLARE_128(value...) \
((struct bt_uuid *) (&(struct bt_uuid_128) BT_UUID_INIT_128(value)))
#define BT_UUID_16(__u) CONTAINER_OF(__u, struct bt_uuid_16, uuid)
#define BT_UUID_32(__u) CONTAINER_OF(__u, struct bt_uuid_32, uuid)
#define BT_UUID_128(__u) CONTAINER_OF(__u, struct bt_uuid_128, uuid)
/** @def BT_UUID_GAP
@ -104,6 +119,11 @@ struct bt_uuid_128 {
*/
#define BT_UUID_BAS BT_UUID_DECLARE_16(0x180f)
#define BT_UUID_BAS_VAL 0x180f
/** @def BT_UUID_HIDS
* @brief HID Service
*/
#define BT_UUID_HIDS BT_UUID_DECLARE_16(0x1812)
#define BT_UUID_HIDS_VAL 0x1812
/** @def BT_UUID_CSC
* @brief Cycling Speed and Cadence Service
*/
@ -169,6 +189,16 @@ struct bt_uuid_128 {
*/
#define BT_UUID_VALID_RANGE BT_UUID_DECLARE_16(0x2906)
#define BT_UUID_VALID_RANGE_VAL 0x2906
/** @def BT_UUID_HIDS_EXT_REPORT
* @brief HID External Report Descriptor
*/
#define BT_UUID_HIDS_EXT_REPORT BT_UUID_DECLARE_16(0x2907)
#define BT_UUID_HIDS_EXT_REPORT_VAL 0x2907
/** @def BT_UUID_HIDS_REPORT_REF
* @brief HID Report Reference Descriptor
*/
#define BT_UUID_HIDS_REPORT_REF BT_UUID_DECLARE_16(0x2908)
#define BT_UUID_HIDS_REPORT_REF_VAL 0x2908
/** @def BT_UUID_ES_CONFIGURATION
* @brief Environmental Sensing Configuration Descriptor
*/
@ -269,6 +299,26 @@ struct bt_uuid_128 {
*/
#define BT_UUID_HRS_CONTROL_POINT BT_UUID_DECLARE_16(0x2a39)
#define BT_UUID_HRS_CONTROL_POINT_VAL 0x2a39
/** @def BT_UUID_HIDS_INFO
* @brief HID Information Characteristic
*/
#define BT_UUID_HIDS_INFO BT_UUID_DECLARE_16(0x2a4a)
#define BT_UUID_HIDS_INFO_VAL 0x2a4a
/** @def BT_UUID_HIDS_REPORT_MAP
* @brief HID Report Map Characteristic
*/
#define BT_UUID_HIDS_REPORT_MAP BT_UUID_DECLARE_16(0x2a4b)
#define BT_UUID_HIDS_REPORT_MAP_VAL 0x2a4b
/** @def BT_UUID_HIDS_CTRL_POINT
* @brief HID Control Point Characteristic
*/
#define BT_UUID_HIDS_CTRL_POINT BT_UUID_DECLARE_16(0x2a4c)
#define BT_UUID_HIDS_CTRL_POINT_VAL 0x2a4c
/** @def BT_UUID_HIDS_REPORT
* @brief HID Report Characteristic
*/
#define BT_UUID_HIDS_REPORT BT_UUID_DECLARE_16(0x2a4d)
#define BT_UUID_HIDS_REPORT_VAL 0x2a4d
/** @def BT_UUID_CSC_MEASUREMENT
* @brief CSC Measurement Characteristic
*/

View file

@ -236,4 +236,19 @@ static inline uint32_t sys_get_le32(const uint8_t src[4])
return ((uint32_t)sys_get_le16(&src[2]) << 16) | sys_get_le16(&src[0]);
}
/**
* @brief Get a 64-bit integer stored in little-endian format.
*
* Get a 64-bit integer, stored in little-endian format in a potentially
* unaligned memory location, and convert it to the host endianness.
*
* @param src Location of the little-endian 64-bit integer to get.
*
* @return 64-bit integer in host endianness.
*/
static inline uint64_t sys_get_le64(const uint8_t src[8])
{
return ((uint64_t)sys_get_le32(&src[4]) << 32) | sys_get_le32(&src[0]);
}
#endif /* __BYTEORDER_H__ */

View file

@ -21,7 +21,6 @@ menuconfig BLUETOOTH
default n
select NANO_TIMEOUTS
select NET_BUF
select BLUETOOTH_LE if !BLUETOOTH_STACK_NBLE
help
This option enables Bluetooth support.
@ -43,6 +42,12 @@ config BLUETOOTH_STACK_NBLE
help
Select the Bluetooth stack to use with Nordic BLE drivers.
config BLUETOOTH_STACK_HCI_RAW
bool "Bluetooth RAW HCI access to the controller"
help
This option allows to access Bluetooth controller
from the application with the RAW HCI protocol.
endchoice
comment "HCI Stack Configurations"
@ -50,7 +55,7 @@ comment "HCI Stack Configurations"
config BLUETOOTH_LE
bool "Bluetooth Low Energy (LE) support"
default n
default y
select TINYCRYPT
select TINYCRYPT_AES if BLUETOOTH_SMP
select TINYCRYPT_AES_CMAC if BLUETOOTH_SMP
@ -63,7 +68,7 @@ config BLUETOOTH_LE
Currently it is mandatory whenever Bluetooth support
(CONFIG_BLUETOOTH) is enabled.
if BLUETOOTH_LE
if BLUETOOTH_LE || BLUETOOTH_STACK_HCI_RAW
config BLUETOOTH_HCI_CMD_COUNT
int "Number of HCI command buffers"
default 2
@ -74,14 +79,16 @@ config BLUETOOTH_HCI_CMD_COUNT
config BLUETOOTH_MAX_CMD_LEN
int "Maximum supported HCI command length"
default 64
default 255 if BLUETOOTH_BREDR
range 64 255
range 255 255 if BLUETOOTH_BREDR
help
Maximum length of each HCI command.
config BLUETOOTH_HCI_EVT_COUNT
int "Number of HCI event buffers"
default 4
default 8 if BLUETOOTH_CONN
default 4 if !BLUETOOTH_CONN
range 2 64
help
Number of buffers available for HCI events. This number should
@ -91,7 +98,7 @@ config BLUETOOTH_HCI_EVT_COUNT
config BLUETOOTH_MAX_EVT_LEN
int "Maximum supported HCI event length"
default 68 if !BLUETOOTH_BREDR
default 68
default 255 if BLUETOOTH_BREDR
range 68 255
help
@ -99,7 +106,28 @@ config BLUETOOTH_MAX_EVT_LEN
for LE is the Command Complete for Read Local Supported
Commands. It is a 3 byte Command Complete header + 65 byte
return parameters = 68 bytes in total.
endif # BLUETOOTH_LE || BLUETOOTH_STACK_HCI_RAW
if (BLUETOOTH_LE && BLUETOOTH_CONN) || BLUETOOTH_STACK_HCI_RAW
config BLUETOOTH_ACL_IN_COUNT
int "Number of incoming ACL data buffers"
default 5
range 2 64
help
Number of buffers available for incoming ACL data.
config BLUETOOTH_L2CAP_IN_MTU
int "Maximum supported L2CAP MTU for incoming data"
default 23
default 65 if BLUETOOTH_SMP
default 200 if BLUETOOTH_BREDR
range 23 1300
range 65 1300 if BLUETOOTH_SMP
help
Maximum size of each incoming L2CAP PDU.
endif # BLUETOOTH_LE && BLUETOOTH_CONN || BLUETOOTH_STACK_HCI_RAW
if BLUETOOTH_LE
config BLUETOOTH_RX_STACK_SIZE
int "Size of the receiving fiber stack"
default 1024
@ -133,27 +161,11 @@ config BLUETOOTH_CONN
default n
if BLUETOOTH_CONN
config BLUETOOTH_ACL_IN_COUNT
int "Number of incoming ACL data buffers"
default 5
range 2 64
help
Number of buffers available for incoming ACL data.
config BLUETOOTH_L2CAP_IN_MTU
int "Maximum supported L2CAP MTU for incoming data"
default 65 if BLUETOOTH_SMP
default 23 if !BLUETOOTH_SMP
range 65 1300 if BLUETOOTH_SMP
range 23 1300 if !BLUETOOTH_SMP
help
Maximum size of each incoming L2CAP PDU.
config BLUETOOTH_ATT_MTU
int "Attribute Protocol (ATT) channel MTU"
default 23
default 50 if BLUETOOTH_SMP # BLUETOOTH_L2CAP_IN_MTU is big enough
# for two complete ACL packets
default 23 if !BLUETOOTH_SMP
range 23 BLUETOOTH_L2CAP_IN_MTU
help
The MTU for the ATT channel. The minimum and default is 23,
@ -167,6 +179,14 @@ config BLUETOOTH_ATT_PREPARE_COUNT
Number of buffers available for ATT prepare write, setting
this to 0 disables GATT long/reliable writes.
config BLUETOOTH_ATT_REQ_COUNT
int "Number of ATT request buffers"
default 1
range 1 64
help
Number of outgoing buffers available for ATT requests per connection,
this controls how many requests can be queued without blocking.
config BLUETOOTH_SMP
bool "Security Manager Protocol support"
default n
@ -398,6 +418,14 @@ config BLUETOOTH_DEBUG_GATT
This option enables debug support for the Bluetooth
Generic Attribute Profile (GATT).
config BLUETOOTH_DEBUG_RFCOMM
bool "Bluetooth RFCOMM debug"
depends on BLUETOOTH_RFCOMM
default n
help
This option enables debug support for the Bluetooth
RFCOMM layer.
endif # BLUETOOTH_DEBUG
config BLUETOOTH_BREDR
@ -409,4 +437,27 @@ config BLUETOOTH_BREDR
help
This option enables Bluetooth BR/EDR support"
if BLUETOOTH_BREDR
config BLUETOOTH_RFCOMM
bool "Bluetooth RFCOMM protocol support [EXPERIMENTAL]"
default n
help
This option enables Bluetooth RFCOMM support
config BLUETOOTH_BREDR_NAME
string "Bluetooth BR/EDR device name"
default "Zephyr"
help
Bluetooth BR/EDR name. Name can be up to 248 bytes long (excluding
NULL termination). Can be empty string.
endif # BLUETOOTH_BREDR
if BLUETOOTH_RFCOMM
config BLUETOOTH_RFCOMM_L2CAP_MTU
int "L2CAP MTU for RFCOMM frames"
default BLUETOOTH_L2CAP_IN_MTU
range BLUETOOTH_L2CAP_IN_MTU 32767
help
Maximum size of L2CAP PDU for RFCOMM frames.
endif # BLUETOOTH_RFCOMM
endif # BLUETOOTH

View file

@ -1,8 +1,11 @@
ccflags-y +=-I$(srctree)/include/drivers
obj-$(CONFIG_BLUETOOTH_STACK_HCI) = \
hci_core.o \
uuid.o
obj-$(CONFIG_BLUETOOTH_STACK_HCI) += \
uuid.o \
hci_core.o
obj-$(CONFIG_BLUETOOTH_STACK_HCI_RAW) += \
hci_raw.o
obj-$(CONFIG_BLUETOOTH_DEBUG) += log.o
@ -11,17 +14,15 @@ obj-$(CONFIG_BLUETOOTH_DEBUG_MONITOR) += monitor.o
obj-$(CONFIG_BLUETOOTH_TINYCRYPT_ECC) += hci_ecc.o
ifeq ($(CONFIG_BLUETOOTH_CONN),y)
obj-y += conn.o \
l2cap.o \
att.o \
gatt.o
obj-y += conn.o l2cap.o att.o gatt.o
ifeq ($(CONFIG_BLUETOOTH_SMP),y)
obj-y += smp.o \
keys.o
obj-y += smp.o keys.o
else
obj-y += smp_null.o
endif
obj-$(CONFIG_BLUETOOTH_BREDR) += keys.o l2cap_br.o
obj-$(CONFIG_BLUETOOTH_BREDR) += keys_br.o l2cap_br.o
obj-$(CONFIG_BLUETOOTH_RFCOMM) += rfcomm.o
endif

View file

@ -47,6 +47,7 @@
#endif
#define ATT_CHAN(_ch) CONTAINER_OF(_ch, struct bt_att, chan.chan)
#define ATT_REQ(_node) CONTAINER_OF(_node, struct bt_att_req, node)
#define BT_GATT_PERM_READ_MASK (BT_GATT_PERM_READ | \
BT_GATT_PERM_READ_ENCRYPT | \
@ -75,54 +76,49 @@ static NET_BUF_POOL(prep_pool, CONFIG_BLUETOOTH_ATT_PREPARE_COUNT,
sizeof(struct bt_attr_data));
#endif /* CONFIG_BLUETOOTH_ATT_PREPARE_COUNT */
/* ATT request context */
struct bt_att_req {
bt_att_func_t func;
void *user_data;
bt_att_destroy_t destroy;
struct net_buf *buf;
#if defined(CONFIG_BLUETOOTH_SMP)
bool retrying;
#endif /* CONFIG_BLUETOOTH_SMP */
};
/* ATT channel specific context */
struct bt_att {
/* The channel this context is associated with */
struct bt_l2cap_le_chan chan;
struct bt_att_req req;
struct bt_att_req *req;
sys_slist_t reqs;
struct nano_delayed_work timeout_work;
#if CONFIG_BLUETOOTH_ATT_PREPARE_COUNT > 0
struct nano_fifo prep_queue;
#endif
/* TODO: Allow more than one pending request */
};
static struct bt_att bt_att_pool[CONFIG_BLUETOOTH_MAX_CONN];
static struct bt_att bt_req_pool[CONFIG_BLUETOOTH_MAX_CONN];
/*
* Pool for outgoing ATT packets. Reserve one buffer per connection plus
* one additional one in case cloning is needed.
* Pool for outgoing ATT requests packets.
*/
static struct nano_fifo att_buf;
static NET_BUF_POOL(att_pool, CONFIG_BLUETOOTH_MAX_CONN + 1,
static struct nano_fifo req_data;
static NET_BUF_POOL(req_pool,
CONFIG_BLUETOOTH_ATT_REQ_COUNT * CONFIG_BLUETOOTH_MAX_CONN,
BT_L2CAP_BUF_SIZE(CONFIG_BLUETOOTH_ATT_MTU),
&att_buf, NULL, BT_BUF_USER_DATA_MIN);
&req_data, NULL, BT_BUF_USER_DATA_MIN);
/*
* Pool for outstanding ATT request, this is required for resending in case
* there is a recoverable error since the original buffer is changed while
* sending.
*/
static struct nano_fifo clone_data;
static NET_BUF_POOL(clone_pool, 1 * CONFIG_BLUETOOTH_MAX_CONN,
BT_L2CAP_BUF_SIZE(CONFIG_BLUETOOTH_ATT_MTU),
&clone_data, NULL, BT_BUF_USER_DATA_MIN);
static void att_req_destroy(struct bt_att_req *req)
{
struct bt_att *att = CONTAINER_OF(req, struct bt_att, req);
if (req->buf) {
net_buf_unref(req->buf);
}
if (req->destroy) {
req->destroy(req->user_data);
req->destroy(req);
}
nano_delayed_work_cancel(&att->timeout_work);
memset(req, 0, sizeof(*req));
}
@ -195,28 +191,86 @@ static uint8_t att_mtu_req(struct bt_att *att, struct net_buf *buf)
return 0;
}
static struct net_buf *att_req_clone(struct net_buf *buf)
{
struct net_buf *clone;
clone = net_buf_get(&clone_data, net_buf_headroom(buf));
if (!clone) {
return NULL;
}
memcpy(net_buf_add(clone, buf->len), buf->data, buf->len);
return clone;
}
static int att_send_req(struct bt_att *att, struct bt_att_req *req)
{
BT_DBG("req %p", req);
att->req = req;
/* Start timeout work */
nano_delayed_work_submit(&att->timeout_work, ATT_TIMEOUT);
/* Send a clone to keep the original buffer intact */
bt_l2cap_send(att->chan.chan.conn, BT_L2CAP_CID_ATT,
att_req_clone(req->buf));
return 0;
}
static void att_process(struct bt_att *att)
{
sys_snode_t *node;
BT_DBG("");
/* Peek next request from the list */
node = sys_slist_peek_head(&att->reqs);
if (!node) {
return;
}
sys_slist_remove(&att->reqs, NULL, node);
att_send_req(att, ATT_REQ(node));
}
static uint8_t att_handle_rsp(struct bt_att *att, void *pdu, uint16_t len,
uint8_t err)
{
struct bt_att_req req;
bt_att_func_t func;
if (!att->req.func) {
return 0;
if (!att->req) {
goto process;
}
/* Release cloned buffer */
if (att->req.buf) {
net_buf_unref(att->req.buf);
att->req.buf = NULL;
/* Cancel timeout if ongoing */
nano_delayed_work_cancel(&att->timeout_work);
/* Release original buffer */
if (att->req->buf) {
net_buf_unref(att->req->buf);
att->req->buf = NULL;
}
/* Reset request before callback so another request can be queued */
memcpy(&req, &att->req, sizeof(req));
att->req.func = NULL;
/* Reset func so it can be reused by the callback */
func = att->req->func;
att->req->func = NULL;
req.func(att->chan.chan.conn, err, pdu, len, req.user_data);
func(att->chan.chan.conn, err, pdu, len, att->req);
att_req_destroy(&req);
/* Don't destroy if callback had reused the request */
if (!att->req->func) {
att_req_destroy(att->req);
}
att->req = NULL;
process:
/* Process pending requests */
att_process(att);
return 0;
}
@ -1416,7 +1470,6 @@ static int att_change_security(struct bt_conn *conn, uint8_t err)
static uint8_t att_error_rsp(struct bt_att *att, struct net_buf *buf)
{
struct bt_att_req *req = &att->req;
struct bt_att_error_rsp *rsp;
struct bt_att_hdr *hdr;
uint8_t err;
@ -1426,22 +1479,22 @@ static uint8_t att_error_rsp(struct bt_att *att, struct net_buf *buf)
BT_DBG("request 0x%02x handle 0x%04x error 0x%02x", rsp->request,
sys_le16_to_cpu(rsp->handle), rsp->error);
if (!req->buf) {
if (!att->req || att->req->buf) {
err = BT_ATT_ERR_UNLIKELY;
goto done;
}
hdr = (void *)req->buf->data;
hdr = (void *)att->req->buf->data;
err = rsp->request == hdr->code ? rsp->error : BT_ATT_ERR_UNLIKELY;
#if defined(CONFIG_BLUETOOTH_SMP)
if (req->retrying) {
if (att->req->retrying) {
goto done;
}
/* Check if security needs to be changed */
if (!att_change_security(att->chan.chan.conn, err)) {
req->retrying = true;
att->req->retrying = true;
/* Wait security_changed: TODO: Handle fail case */
return 0;
}
@ -1693,12 +1746,12 @@ struct net_buf *bt_att_create_pdu(struct bt_conn *conn, uint8_t op, size_t len)
}
if (len + sizeof(op) > att->chan.tx.mtu) {
BT_WARN("ATT MTU exceeded, max %u, wanted %zu",
BT_WARN("ATT MTU exceeded, max %u, wanted %u",
att->chan.tx.mtu, len + sizeof(op));
return NULL;
}
buf = bt_l2cap_create_pdu(&att_buf);
buf = bt_l2cap_create_pdu(&req_data);
if (!buf) {
return NULL;
}
@ -1711,6 +1764,7 @@ struct net_buf *bt_att_create_pdu(struct bt_conn *conn, uint8_t op, size_t len)
static void att_reset(struct bt_att *att)
{
sys_snode_t *node, *tmp;
#if CONFIG_BLUETOOTH_ATT_PREPARE_COUNT > 0
struct net_buf *buf;
@ -1720,7 +1774,25 @@ static void att_reset(struct bt_att *att)
}
#endif
/* Notify client if request is pending */
/* Notify pending requests */
SYS_SLIST_FOR_EACH_NODE_SAFE(&att->reqs, node, tmp) {
struct bt_att_req *req = ATT_REQ(node);
if (req->func) {
req->func(NULL, BT_ATT_ERR_UNLIKELY, NULL, 0, req);
}
att_req_destroy(req);
}
/* Reset list */
sys_slist_init(&att->reqs);
if (!att->req) {
return;
}
/* Notify outstanding request */
att_handle_rsp(att, NULL, 0, BT_ATT_ERR_UNLIKELY);
}
@ -1762,6 +1834,7 @@ static void bt_att_connected(struct bt_l2cap_chan *chan)
ch->rx.mtu = BT_ATT_DEFAULT_LE_MTU;
nano_delayed_work_init(&att->timeout_work, att_timeout);
sys_slist_init(&att->reqs);
bt_gatt_connected(ch->chan.conn);
}
@ -1784,9 +1857,7 @@ static void bt_att_encrypt_change(struct bt_l2cap_chan *chan)
{
struct bt_att *att = ATT_CHAN(chan);
struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan);
struct bt_conn *conn = ch->chan.conn;
struct bt_att_req *req;
BT_DBG("chan %p conn %p handle %u sec_level 0x%02x", ch, conn,
conn->handle, conn->sec_level);
@ -1795,16 +1866,15 @@ static void bt_att_encrypt_change(struct bt_l2cap_chan *chan)
return;
}
req = &att->req;
if (!req->retrying) {
if (!att->req || !att->req->retrying) {
return;
}
BT_DBG("Retrying");
/* Resend buffer */
bt_l2cap_send(conn, BT_L2CAP_CID_ATT, req->buf);
req->buf = NULL;
bt_l2cap_send(conn, BT_L2CAP_CID_ATT, att->req->buf);
att->req->buf = NULL;
}
#endif /* CONFIG_BLUETOOTH_SMP */
@ -1822,8 +1892,8 @@ static int bt_att_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
BT_DBG("conn %p handle %u", conn, conn->handle);
for (i = 0; i < ARRAY_SIZE(bt_att_pool); i++) {
struct bt_att *att = &bt_att_pool[i];
for (i = 0; i < ARRAY_SIZE(bt_req_pool); i++) {
struct bt_att *att = &bt_req_pool[i];
if (att->chan.chan.conn) {
continue;
@ -1848,7 +1918,8 @@ void bt_att_init(void)
.accept = bt_att_accept,
};
net_buf_pool_init(att_pool);
net_buf_pool_init(req_pool);
net_buf_pool_init(clone_pool);
#if CONFIG_BLUETOOTH_ATT_PREPARE_COUNT > 0
net_buf_pool_init(prep_pool);
#endif
@ -1869,80 +1940,12 @@ uint16_t bt_att_get_mtu(struct bt_conn *conn)
return att->chan.tx.mtu;
}
static struct bt_att_req *att_req_new(struct bt_att *att, struct net_buf *buf,
bt_att_func_t func, void *user_data,
bt_att_destroy_t destroy)
{
/* Check if there is a request pending */
if (att->req.func) {
/* TODO: Allow more than one pending request */
return NULL;
}
att->req.buf = net_buf_clone(buf);
#if defined(CONFIG_BLUETOOTH_SMP)
att->req.retrying = false;
#endif /* CONFIG_BLUETOOTH_SMP */
att->req.func = func;
att->req.user_data = user_data;
att->req.destroy = destroy;
return &att->req;
}
enum {
ATT_OP_TYPE_REQ,
ATT_OP_TYPE_RSP,
ATT_OP_TYPE_CMD,
ATT_OP_TYPE_UNKNOWN,
};
static uint8_t att_op_type(uint8_t op)
{
switch (op) {
case BT_ATT_OP_MTU_REQ:
case BT_ATT_OP_FIND_INFO_REQ:
case BT_ATT_OP_FIND_TYPE_REQ:
case BT_ATT_OP_READ_TYPE_REQ:
case BT_ATT_OP_READ_REQ:
case BT_ATT_OP_READ_BLOB_REQ:
case BT_ATT_OP_READ_MULT_REQ:
case BT_ATT_OP_READ_GROUP_REQ:
case BT_ATT_OP_WRITE_REQ:
case BT_ATT_OP_PREPARE_WRITE_REQ:
case BT_ATT_OP_EXEC_WRITE_REQ:
case BT_ATT_OP_INDICATE:
return ATT_OP_TYPE_REQ;
case BT_ATT_OP_ERROR_RSP:
case BT_ATT_OP_MTU_RSP:
case BT_ATT_OP_FIND_INFO_RSP:
case BT_ATT_OP_FIND_TYPE_RSP:
case BT_ATT_OP_READ_TYPE_RSP:
case BT_ATT_OP_READ_RSP:
case BT_ATT_OP_READ_BLOB_RSP:
case BT_ATT_OP_READ_MULT_RSP:
case BT_ATT_OP_READ_GROUP_RSP:
case BT_ATT_OP_WRITE_RSP:
case BT_ATT_OP_PREPARE_WRITE_RSP:
case BT_ATT_OP_EXEC_WRITE_RSP:
case BT_ATT_OP_CONFIRM:
return ATT_OP_TYPE_RSP;
case BT_ATT_OP_NOTIFY:
case BT_ATT_OP_WRITE_CMD:
case BT_ATT_OP_SIGNED_WRITE_CMD:
return ATT_OP_TYPE_CMD;
default:
return ATT_OP_TYPE_UNKNOWN;
}
}
int bt_att_send(struct bt_conn *conn, struct net_buf *buf, bt_att_func_t func,
void *user_data, bt_att_destroy_t destroy)
int bt_att_send(struct bt_conn *conn, struct net_buf *buf)
{
struct bt_att *att;
struct bt_att_hdr *hdr = (void *)buf->data;
struct bt_att_hdr *hdr;
if (!conn) {
if (!conn || !buf) {
return -EINVAL;
}
@ -1951,11 +1954,7 @@ int bt_att_send(struct bt_conn *conn, struct net_buf *buf, bt_att_func_t func,
return -ENOTCONN;
}
if (func) {
if (!att_req_new(att, buf, func, user_data, destroy)) {
return -EBUSY;
}
}
hdr = (void *)buf->data;
if (hdr->code == BT_ATT_OP_SIGNED_WRITE_CMD) {
int err;
@ -1965,8 +1964,6 @@ int bt_att_send(struct bt_conn *conn, struct net_buf *buf, bt_att_func_t func,
BT_ERR("Error signing data");
return err;
}
} else if (att_op_type(hdr->code) == ATT_OP_TYPE_REQ) {
nano_delayed_work_submit(&att->timeout_work, ATT_TIMEOUT);
}
bt_l2cap_send(conn, BT_L2CAP_CID_ATT, buf);
@ -1974,11 +1971,36 @@ int bt_att_send(struct bt_conn *conn, struct net_buf *buf, bt_att_func_t func,
return 0;
}
void bt_att_cancel(struct bt_conn *conn)
int bt_att_req_send(struct bt_conn *conn, struct bt_att_req *req)
{
struct bt_att *att;
if (!conn) {
BT_DBG("conn %p req %p", conn, req);
if (!conn || !req) {
return -EINVAL;
}
att = att_chan_get(conn);
if (!att) {
return -ENOTCONN;
}
/* Check if there is a request outstanding */
if (att->req) {
/* Queue the request to be send later */
sys_slist_append(&att->reqs, &req->node);
return 0;
}
return att_send_req(att, req);
}
void bt_att_req_cancel(struct bt_conn *conn, struct bt_att_req *req)
{
struct bt_att *att;
if (!conn || !req) {
return;
}
@ -1987,5 +2009,13 @@ void bt_att_cancel(struct bt_conn *conn)
return;
}
att_req_destroy(&att->req);
/* Check if request is outstanding */
if (att->req == req) {
att->req = NULL;
} else {
/* Remove request from the list */
sys_slist_find_and_remove(&att->reqs, &req->node);
}
att_req_destroy(req);
}

View file

@ -245,14 +245,11 @@ uint16_t bt_att_get_mtu(struct bt_conn *conn);
struct net_buf *bt_att_create_pdu(struct bt_conn *conn, uint8_t op,
size_t len);
typedef void (*bt_att_func_t)(struct bt_conn *conn, uint8_t err,
const void *pdu, uint16_t length,
void *user_data);
typedef void (*bt_att_destroy_t)(void *user_data);
/* Send ATT PDU over a connection */
int bt_att_send(struct bt_conn *conn, struct net_buf *buf, bt_att_func_t func,
void *user_data, bt_att_destroy_t destroy);
int bt_att_send(struct bt_conn *conn, struct net_buf *buf);
/* Send ATT Request over a connection */
int bt_att_req_send(struct bt_conn *conn, struct bt_att_req *req);
/* Cancel ATT request */
void bt_att_cancel(struct bt_conn *conn);
void bt_att_req_cancel(struct bt_conn *conn, struct bt_att_req *req);

View file

@ -32,6 +32,7 @@
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/driver.h>
#include <bluetooth/att.h>
#include "hci_core.h"
#include "conn_internal.h"
@ -618,7 +619,17 @@ int bt_conn_le_start_encryption(struct bt_conn *conn, uint64_t rand,
#if defined(CONFIG_BLUETOOTH_SMP) || defined(CONFIG_BLUETOOTH_BREDR)
uint8_t bt_conn_enc_key_size(struct bt_conn *conn)
{
return conn->keys ? conn->keys->enc_size : 0;
#if defined(CONFIG_BLUETOOTH_BREDR)
if (conn->type == BT_CONN_TYPE_BR) {
return conn->br.link_key ? 16 : 0;
}
#endif /* CONFIG_BLUETOOTH_BREDR */
#if defined(CONFIG_BLUETOOTH_SMP)
return conn->le.keys ? conn->le.keys->enc_size : 0;
#else
return 0;
#endif /* CONFIG_BLUETOOTH_SMP */
}
void bt_conn_security_changed(struct bt_conn *conn)
@ -657,38 +668,39 @@ static int start_security(struct bt_conn *conn)
#if defined(CONFIG_BLUETOOTH_CENTRAL) && defined(CONFIG_BLUETOOTH_SMP)
case BT_HCI_ROLE_MASTER:
{
if (!conn->keys) {
conn->keys = bt_keys_find(BT_KEYS_LTK_P256,
if (!conn->le.keys) {
conn->le.keys = bt_keys_find(BT_KEYS_LTK_P256,
&conn->le.dst);
if (!conn->keys) {
conn->keys = bt_keys_find(BT_KEYS_LTK,
if (!conn->le.keys) {
conn->le.keys = bt_keys_find(BT_KEYS_LTK,
&conn->le.dst);
}
}
if (!conn->keys ||
!(conn->keys->keys & (BT_KEYS_LTK | BT_KEYS_LTK_P256))) {
if (!conn->le.keys ||
!(conn->le.keys->keys & (BT_KEYS_LTK | BT_KEYS_LTK_P256))) {
return bt_smp_send_pairing_req(conn);
}
if (conn->required_sec_level > BT_SECURITY_MEDIUM &&
!atomic_test_bit(conn->keys->flags,
!atomic_test_bit(conn->le.keys->flags,
BT_KEYS_AUTHENTICATED)) {
return bt_smp_send_pairing_req(conn);
}
if (conn->required_sec_level > BT_SECURITY_HIGH &&
!atomic_test_bit(conn->keys->flags,
!atomic_test_bit(conn->le.keys->flags,
BT_KEYS_AUTHENTICATED) &&
!(conn->keys->keys & BT_KEYS_LTK_P256)) {
!(conn->le.keys->keys & BT_KEYS_LTK_P256)) {
return bt_smp_send_pairing_req(conn);
}
/* LE SC LTK and legacy master LTK are stored in same place */
return bt_conn_le_start_encryption(conn, conn->keys->ltk.rand,
conn->keys->ltk.ediv,
conn->keys->ltk.val,
conn->keys->enc_size);
return bt_conn_le_start_encryption(conn,
conn->le.keys->ltk.rand,
conn->le.keys->ltk.ediv,
conn->le.keys->ltk.val,
conn->le.keys->enc_size);
}
#endif /* CONFIG_BLUETOOTH_CENTRAL && CONFIG_BLUETOOTH_SMP */
#if defined(CONFIG_BLUETOOTH_PERIPHERAL) && defined(CONFIG_BLUETOOTH_SMP)
@ -1314,12 +1326,12 @@ int bt_conn_le_param_update(struct bt_conn *conn,
nano_delayed_work_cancel(&conn->le.update_work);
if ((conn->role == BT_HCI_ROLE_SLAVE) &&
!(bt_dev.le.features[0] & BT_HCI_LE_CONN_PARAM_REQ_PROC)) {
!BT_FEAT_LE_CONN_PARAM_REQ_PROC(bt_dev.le.features)) {
return bt_l2cap_update_conn_param(conn, param);
}
if ((conn->le.features[0] & BT_HCI_LE_CONN_PARAM_REQ_PROC) &&
(bt_dev.le.features[0] & BT_HCI_LE_CONN_PARAM_REQ_PROC)) {
if (BT_FEAT_LE_CONN_PARAM_REQ_PROC(conn->le.features) &&
BT_FEAT_LE_CONN_PARAM_REQ_PROC(bt_dev.le.features)) {
return bt_conn_le_conn_update(conn, param);
}

View file

@ -51,7 +51,9 @@ struct bt_conn_le {
uint16_t latency;
uint16_t timeout;
uint8_t features[8];
uint8_t features[1][8];
struct bt_keys *keys;
/* Delayed work for connection update handling */
struct nano_delayed_work update_work;
@ -61,14 +63,6 @@ struct bt_conn_le {
/* For now reserve space for 2 pages of LMP remote features */
#define LMP_MAX_PAGES 2
/* Helper to get remote extended features bit available at page 0 */
#define lmp_ext_feat_capable(conn) \
((conn)->br.features[0][7] & BT_LMP_REMOTE_EXT_FEATURES)
/* Helper to validate SSP host support within retrieved remote LMP features */
#define lmp_ssp_host_supported(conn) \
((conn)->br.features[1][0] & BT_LMP_HOST_SSP)
struct bt_conn_br {
bt_addr_t dst;
uint8_t remote_io_capa;
@ -76,6 +70,8 @@ struct bt_conn_br {
uint8_t pairing_method;
/* remote LMP features pages per 8 bytes each */
uint8_t features[LMP_MAX_PAGES][8];
struct bt_keys_link_key *link_key;
};
#endif
@ -100,8 +96,6 @@ struct bt_conn {
/* Queue for outgoing ACL data */
struct nano_fifo tx_queue;
struct bt_keys *keys;
/* L2CAP channels */
void *channels;

View file

@ -333,17 +333,19 @@ ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn,
uint16_t len, uint16_t offset, uint8_t flags)
{
struct _bt_gatt_ccc *ccc = attr->user_data;
const uint16_t *data = buf;
uint16_t value;
size_t i;
if (offset > sizeof(*data)) {
if (offset > sizeof(uint16_t)) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
if (offset + len > sizeof(*data)) {
if (offset + len > sizeof(uint16_t)) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
}
value = sys_get_le16(buf);
for (i = 0; i < ccc->cfg_len; i++) {
/* Check for existing configuration */
if (!bt_addr_le_cmp(&ccc->cfg[i].peer, &conn->le.dst)) {
@ -359,8 +361,11 @@ ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn,
}
bt_addr_le_copy(&ccc->cfg[i].peer, &conn->le.dst);
/* Only set valid if bonded */
ccc->cfg[i].valid = bt_addr_le_is_bonded(&conn->le.dst);
if (value) {
ccc->cfg[i].valid = true;
}
break;
}
@ -368,9 +373,12 @@ ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn,
BT_WARN("No space to store CCC cfg");
return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
}
} else if (!value) {
/* free existing configuration for default value */
ccc->cfg[i].valid = false;
}
ccc->cfg[i].value = sys_le16_to_cpu(*data);
ccc->cfg[i].value = value;
BT_DBG("handle 0x%04x value %u", attr->handle, ccc->cfg[i].value);
@ -455,12 +463,23 @@ static void gatt_indicate_rsp(struct bt_conn *conn, uint8_t err,
}
static int gatt_send(struct bt_conn *conn, struct net_buf *buf,
bt_att_func_t func, void *user_data,
bt_att_func_t func, void *params,
bt_att_destroy_t destroy)
{
int err;
err = bt_att_send(conn, buf, func, user_data, destroy);
if (params) {
struct bt_att_req *req = params;
req->buf = buf;
req->func = func;
req->destroy = destroy;
err = bt_att_req_send(conn, req);
} else {
err = bt_att_send(conn, buf);
}
if (err) {
BT_ERR("Error sending ATT PDU: %d", err);
net_buf_unref(buf);
@ -661,11 +680,13 @@ static uint8_t disconnected_cb(const struct bt_gatt_attr *attr, void *user_data)
}
} else {
/* Clear value if not paired */
if (!ccc->cfg[i].valid)
if (!bt_addr_le_is_bonded(&conn->le.dst)) {
ccc->cfg[i].valid = false;
memset(&ccc->cfg[i].value, 0,
sizeof(ccc->cfg[i].value));
}
}
}
/* Reset value while disconnected */
memset(&ccc->value, 0, sizeof(ccc->value));
@ -730,18 +751,19 @@ static void remove_subscriptions(struct bt_conn *conn)
static void gatt_mtu_rsp(struct bt_conn *conn, uint8_t err, const void *pdu,
uint16_t length, void *user_data)
{
bt_gatt_rsp_func_t func = user_data;
struct bt_gatt_exchange_params *params = user_data;
func(conn, err);
params->func(conn, err, params);
}
int bt_gatt_exchange_mtu(struct bt_conn *conn, bt_gatt_rsp_func_t func)
int bt_gatt_exchange_mtu(struct bt_conn *conn,
struct bt_gatt_exchange_params *params)
{
struct bt_att_exchange_mtu_req *req;
struct net_buf *buf;
uint16_t mtu;
if (!conn || !func) {
if (!conn || !params || !params->func) {
return -EINVAL;
}
@ -757,7 +779,7 @@ int bt_gatt_exchange_mtu(struct bt_conn *conn, bt_gatt_rsp_func_t func)
req = net_buf_add(buf, sizeof(*req));
req->mtu = sys_cpu_to_le16(mtu);
return gatt_send(conn, buf, gatt_mtu_rsp, func, NULL);
return gatt_send(conn, buf, gatt_mtu_rsp, params, NULL);
}
static void att_find_type_rsp(struct bt_conn *conn, uint8_t err,
@ -1164,7 +1186,6 @@ static int att_read_type(struct bt_conn *conn,
{
struct net_buf *buf;
struct bt_att_read_type_req *req;
uint16_t *value;
buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_TYPE_REQ, sizeof(*req));
if (!buf) {
@ -1175,11 +1196,10 @@ static int att_read_type(struct bt_conn *conn,
req->start_handle = sys_cpu_to_le16(params->start_handle);
req->end_handle = sys_cpu_to_le16(params->end_handle);
value = net_buf_add(buf, sizeof(*value));
if (params->type == BT_GATT_DISCOVER_INCLUDE)
*value = sys_cpu_to_le16(BT_UUID_GATT_INCLUDE_VAL);
net_buf_add_le16(buf, BT_UUID_GATT_INCLUDE_VAL);
else
*value = sys_cpu_to_le16(BT_UUID_GATT_CHRC_VAL);
net_buf_add_le16(buf, BT_UUID_GATT_CHRC_VAL);
BT_DBG("start_handle 0x%04x end_handle 0x%04x", params->start_handle,
params->end_handle);
@ -1760,9 +1780,9 @@ int bt_gatt_unsubscribe(struct bt_conn *conn,
return gatt_write_ccc(conn, params->ccc_handle, 0x0000, NULL, NULL);
}
void bt_gatt_cancel(struct bt_conn *conn)
void bt_gatt_cancel(struct bt_conn *conn, void *params)
{
bt_att_cancel(conn);
bt_att_req_cancel(conn, params);
}
static void add_subscriptions(struct bt_conn *conn)

View file

@ -358,51 +358,33 @@ static const bt_addr_le_t *find_id_addr(const bt_addr_le_t *addr)
return addr;
}
static int set_advertise_enable(void)
static int set_advertise_enable(bool enable)
{
struct net_buf *buf;
int err;
if (atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) {
return 0;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1);
if (!buf) {
return -ENOBUFS;
}
if (enable) {
net_buf_add_u8(buf, BT_HCI_LE_ADV_ENABLE);
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL);
if (err) {
return err;
}
atomic_set_bit(bt_dev.flags, BT_DEV_ADVERTISING);
return 0;
}
static int set_advertise_disable(void)
{
struct net_buf *buf;
int err;
if (!atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) {
return 0;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1);
if (!buf) {
return -ENOBUFS;
}
} else {
net_buf_add_u8(buf, BT_HCI_LE_ADV_DISABLE);
}
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL);
if (err) {
return err;
}
if (enable) {
atomic_set_bit(bt_dev.flags, BT_DEV_ADVERTISING);
} else {
atomic_clear_bit(bt_dev.flags, BT_DEV_ADVERTISING);
}
return 0;
}
@ -469,9 +451,9 @@ static void rpa_timeout(struct nano_work *work)
*/
if (atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) {
/* make sure new address is used */
set_advertise_disable();
set_advertise_enable(false);
le_set_rpa();
set_advertise_enable();
set_advertise_enable(true);
}
if (atomic_test_bit(bt_dev.flags, BT_DEV_ACTIVE_SCAN)) {
@ -635,7 +617,7 @@ static void hci_disconn_complete(struct net_buf *buf)
*/
if (conn->type == BT_CONN_TYPE_BR &&
atomic_test_and_clear_bit(conn->flags, BT_CONN_BR_NOBOND)) {
bt_keys_clear(conn->keys, BT_KEYS_LINK_KEY);
bt_keys_link_key_clear(conn->br.link_key);
}
#endif
bt_conn_unref(conn);
@ -650,11 +632,12 @@ static void hci_disconn_complete(struct net_buf *buf)
bt_conn_unref(conn);
advertise:
if (atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING)) {
if (atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING) &&
!atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) {
#if defined(CONFIG_BLUETOOTH_PRIVACY)
le_set_rpa();
#endif
set_advertise_enable();
set_advertise_enable(true);
}
}
@ -767,10 +750,29 @@ static void le_conn_complete(struct net_buf *buf)
bt_addr_le_copy(&conn->le.init_addr, &evt->peer_addr);
#if defined(CONFIG_BLUETOOTH_PRIVACY)
/* TODO Handle the probability that random address could have
* been updated by rpa_timeout or numerous other places it is
* called in this file before le_conn_complete is processed
* here.
*/
bt_addr_le_copy(&conn->le.resp_addr, &bt_dev.random_addr);
#else
bt_addr_le_copy(&conn->le.resp_addr, &bt_dev.id_addr);
#endif /* CONFIG_BLUETOOTH_PRIVACY */
/* if the controller supports, lets advertise for another
* slave connection.
* check for connectable advertising state is sufficient as
* this is how this le connection complete for slave occurred.
*/
if (atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING) &&
BT_LE_STATES_SLAVE_CONN_ADV(bt_dev.le.states)) {
#if defined(CONFIG_BLUETOOTH_PRIVACY)
le_set_rpa();
#endif
set_advertise_enable(true);
}
}
bt_conn_set_state(conn, BT_CONN_CONNECTED);
@ -785,7 +787,7 @@ static void le_conn_complete(struct net_buf *buf)
}
if ((evt->role == BT_HCI_ROLE_MASTER) ||
(bt_dev.le.features[0] & BT_HCI_LE_SLAVE_FEATURES)) {
BT_FEAT_LE_SLAVE_FEATURE_XCHG(bt_dev.le.features)) {
err = hci_le_read_remote_features(conn);
if (!err) {
goto done;
@ -1105,12 +1107,17 @@ static void update_sec_level_br(struct bt_conn *conn)
return;
}
if (conn->keys && (conn->keys->keys & BT_KEYS_LINK_KEY)) {
conn->sec_level = BT_SECURITY_MEDIUM;
if (atomic_test_bit(conn->keys->flags,
BT_KEYS_AUTHENTICATED)) {
if (conn->br.link_key) {
if (atomic_test_bit(conn->br.link_key->flags,
BT_LINK_KEY_AUTHENTICATED)) {
if (conn->encrypt == 0x02) {
conn->sec_level = BT_SECURITY_FIPS;
} else {
conn->sec_level = BT_SECURITY_HIGH;
}
} else {
conn->sec_level = BT_SECURITY_MEDIUM;
}
} else {
BT_WARN("No BR/EDR link key found");
conn->sec_level = BT_SECURITY_MEDIUM;
@ -1192,15 +1199,18 @@ static void link_key_notify(struct net_buf *buf)
BT_DBG("%s, link type 0x%02x", bt_addr_str(&evt->bdaddr), evt->key_type);
if (!conn->keys) {
conn->keys = bt_keys_get_link_key(&evt->bdaddr);
if (!conn->br.link_key) {
conn->br.link_key = bt_keys_get_link_key(&evt->bdaddr);
}
if (!conn->keys) {
if (!conn->br.link_key) {
BT_ERR("Can't update keys for %s", bt_addr_str(&evt->bdaddr));
bt_conn_unref(conn);
return;
}
/* clear any old Link Key flags */
atomic_set(conn->br.link_key->flags, 0);
switch (evt->key_type) {
case BT_LK_COMBINATION:
/*
@ -1209,30 +1219,41 @@ static void link_key_notify(struct net_buf *buf)
*/
if (atomic_test_and_clear_bit(conn->flags,
BT_CONN_BR_LEGACY_SECURE)) {
atomic_set_bit(conn->keys->flags,
BT_KEYS_AUTHENTICATED);
atomic_set_bit(conn->br.link_key->flags,
BT_LINK_KEY_AUTHENTICATED);
}
memcpy(conn->keys->link_key.val, evt->link_key, 16);
memcpy(conn->br.link_key->val, evt->link_key, 16);
break;
case BT_LK_UNAUTH_COMBINATION_P192:
case BT_LK_AUTH_COMBINATION_P192:
if (evt->key_type == BT_LK_AUTH_COMBINATION_P192) {
atomic_set_bit(conn->keys->flags,
BT_KEYS_AUTHENTICATED);
}
/*
* Update keys database if authentication bond is required to
* be persistent. Mark no-bond link key flag for connection on
* the contrary.
*/
if (bt_conn_ssp_get_auth(conn) > BT_HCI_NO_BONDING_MITM) {
memcpy(conn->keys->link_key.val, evt->link_key, 16);
} else {
atomic_set_bit(conn->br.link_key->flags,
BT_LINK_KEY_AUTHENTICATED);
/* fall through */
case BT_LK_UNAUTH_COMBINATION_P192:
/* Mark no-bond so that link-key is removed on disconnection */
if (bt_conn_ssp_get_auth(conn) < BT_HCI_DEDICATED_BONDING) {
atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND);
}
memcpy(conn->br.link_key->val, evt->link_key, 16);
break;
case BT_LK_AUTH_COMBINATION_P256:
atomic_set_bit(conn->br.link_key->flags,
BT_LINK_KEY_AUTHENTICATED);
/* fall through */
case BT_LK_UNAUTH_COMBINATION_P256:
atomic_set_bit(conn->br.link_key->flags, BT_LINK_KEY_SC);
/* Mark no-bond so that link-key is removed on disconnection */
if (bt_conn_ssp_get_auth(conn) < BT_HCI_DEDICATED_BONDING) {
atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND);
}
memcpy(conn->br.link_key->val, evt->link_key, 16);
break;
default:
BT_WARN("Link key type unsupported/unimplemented");
BT_WARN("Unsupported Link Key type %u", evt->key_type);
memset(conn->br.link_key->val, 0,
sizeof(conn->br.link_key->val));
break;
}
@ -1290,11 +1311,11 @@ static void link_key_req(struct net_buf *buf)
return;
}
if (!conn->keys) {
conn->keys = bt_keys_find_link_key(&evt->bdaddr);
if (!conn->br.link_key) {
conn->br.link_key = bt_keys_find_link_key(&evt->bdaddr);
}
if (!conn->keys) {
if (!conn->br.link_key) {
link_key_neg_reply(&evt->bdaddr);
bt_conn_unref(conn);
return;
@ -1304,14 +1325,15 @@ static void link_key_req(struct net_buf *buf)
* Enforce regenerate by controller stronger link key since found one
* in database not covers requested security level.
*/
if (!atomic_test_bit(conn->keys->flags, BT_KEYS_AUTHENTICATED) &&
if (!atomic_test_bit(conn->br.link_key->flags,
BT_LINK_KEY_AUTHENTICATED) &&
conn->required_sec_level > BT_SECURITY_MEDIUM) {
link_key_neg_reply(&evt->bdaddr);
bt_conn_unref(conn);
return;
}
link_key_reply(&evt->bdaddr, conn->keys->link_key.val);
link_key_reply(&evt->bdaddr, conn->br.link_key->val);
bt_conn_unref(conn);
}
@ -1841,7 +1863,7 @@ static void read_remote_features_complete(struct net_buf *buf)
memcpy(conn->br.features[0], evt->features, sizeof(evt->features));
if (!lmp_ext_feat_capable(conn)) {
if (!BT_FEAT_EXT_FEATURES(conn->br.features)) {
goto done;
}
@ -1886,7 +1908,7 @@ static void read_remote_ext_features_complete(struct net_buf *buf)
#endif /* CONFIG_BLUETOOTH_BREDR */
#if defined(CONFIG_BLUETOOTH_SMP) || defined(CONFIG_BLUETOOTH_BREDR)
#if defined(CONFIG_BLUETOOTH_SMP)
static void update_sec_level(struct bt_conn *conn)
{
if (!conn->encrypt) {
@ -1894,9 +1916,9 @@ static void update_sec_level(struct bt_conn *conn)
return;
}
if (conn->keys && atomic_test_bit(conn->keys->flags,
if (conn->le.keys && atomic_test_bit(conn->le.keys->flags,
BT_KEYS_AUTHENTICATED)) {
if (conn->keys->keys & BT_KEYS_LTK_P256) {
if (conn->le.keys->keys & BT_KEYS_LTK_P256) {
conn->sec_level = BT_SECURITY_FIPS;
} else {
conn->sec_level = BT_SECURITY_HIGH;
@ -1910,7 +1932,9 @@ static void update_sec_level(struct bt_conn *conn)
bt_conn_disconnect(conn, BT_HCI_ERR_AUTHENTICATION_FAIL);
}
}
#endif /* CONFIG_BLUETOOTH_SMP */
#if defined(CONFIG_BLUETOOTH_SMP) || defined(CONFIG_BLUETOOTH_BREDR)
static void hci_encrypt_change(struct net_buf *buf)
{
struct bt_hci_evt_encrypt_change *evt = (void *)buf->data;
@ -1942,26 +1966,28 @@ static void hci_encrypt_change(struct net_buf *buf)
conn->encrypt = evt->encrypt;
#if defined(CONFIG_BLUETOOTH_SMP)
if (conn->type == BT_CONN_TYPE_LE) {
/*
* we update keys properties only on successful encryption to avoid
* losing valid keys if encryption was not successful
* we update keys properties only on successful encryption to
* avoid losing valid keys if encryption was not successful.
*
* Update keys with last pairing info for proper sec level update.
* This is done only for LE transport, for BR/EDR keys are updated on
* HCI 'Link Key Notification Event'
* Update keys with last pairing info for proper sec level
* update. This is done only for LE transport, for BR/EDR keys
* are updated on HCI 'Link Key Notification Event'
*/
if (conn->encrypt && conn->type == BT_CONN_TYPE_LE) {
if (conn->encrypt) {
bt_smp_update_keys(conn);
}
if (conn->type == BT_CONN_TYPE_LE) {
update_sec_level(conn);
}
#endif /* CONFIG_BLUETOOTH_SMP */
#if defined(CONFIG_BLUETOOTH_BREDR)
} else {
if (conn->type == BT_CONN_TYPE_BR) {
update_sec_level_br(conn);
reset_pairing(conn);
#endif /* CONFIG_BLUETOOTH_BREDR */
}
#endif /* CONFIG_BLUETOOTH_BREDR */
bt_l2cap_encrypt_change(conn);
bt_conn_security_changed(conn);
@ -1995,14 +2021,17 @@ static void hci_encrypt_key_refresh_complete(struct net_buf *buf)
* updated on HCI 'Link Key Notification Event', therefore update here
* only security level based on available keys and encryption state.
*/
#if defined(CONFIG_BLUETOOTH_SMP)
if (conn->type == BT_CONN_TYPE_LE) {
bt_smp_update_keys(conn);
update_sec_level(conn);
#if defined(CONFIG_BLUETOOTH_BREDR)
} else {
update_sec_level_br(conn);
#endif /* CONFIG_BLUETOOTH_BREDR */
}
#endif /* CONFIG_BLUETOOTH_SMP */
#if defined(CONFIG_BLUETOOTH_BREDR)
if (conn->type == BT_CONN_TYPE_BR) {
update_sec_level_br(conn);
}
#endif /* CONFIG_BLUETOOTH_BREDR */
bt_l2cap_encrypt_change(conn);
bt_conn_security_changed(conn);
@ -2053,15 +2082,15 @@ static void le_ltk_request(struct net_buf *buf)
goto done;
}
if (!conn->keys) {
conn->keys = bt_keys_find(BT_KEYS_LTK_P256, &conn->le.dst);
if (!conn->keys) {
conn->keys = bt_keys_find(BT_KEYS_SLAVE_LTK,
if (!conn->le.keys) {
conn->le.keys = bt_keys_find(BT_KEYS_LTK_P256, &conn->le.dst);
if (!conn->le.keys) {
conn->le.keys = bt_keys_find(BT_KEYS_SLAVE_LTK,
&conn->le.dst);
}
}
if (conn->keys && (conn->keys->keys & BT_KEYS_LTK_P256) &&
if (conn->le.keys && (conn->le.keys->keys & BT_KEYS_LTK_P256) &&
evt->rand == 0 && evt->ediv == 0) {
struct bt_hci_cp_le_ltk_req_reply *cp;
@ -2076,10 +2105,11 @@ static void le_ltk_request(struct net_buf *buf)
cp->handle = evt->handle;
/* use only enc_size bytes of key for encryption */
memcpy(cp->ltk, conn->keys->ltk.val, conn->keys->enc_size);
if (conn->keys->enc_size < sizeof(cp->ltk)) {
memset(cp->ltk + conn->keys->enc_size, 0,
sizeof(cp->ltk) - conn->keys->enc_size);
memcpy(cp->ltk, conn->le.keys->ltk.val,
conn->le.keys->enc_size);
if (conn->le.keys->enc_size < sizeof(cp->ltk)) {
memset(cp->ltk + conn->le.keys->enc_size, 0,
sizeof(cp->ltk) - conn->le.keys->enc_size);
}
bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_REPLY, buf);
@ -2087,9 +2117,9 @@ static void le_ltk_request(struct net_buf *buf)
}
#if !defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
if (conn->keys && (conn->keys->keys & BT_KEYS_SLAVE_LTK) &&
conn->keys->slave_ltk.rand == evt->rand &&
conn->keys->slave_ltk.ediv == evt->ediv) {
if (conn->le.keys && (conn->le.keys->keys & BT_KEYS_SLAVE_LTK) &&
conn->le.keys->slave_ltk.rand == evt->rand &&
conn->le.keys->slave_ltk.ediv == evt->ediv) {
struct bt_hci_cp_le_ltk_req_reply *cp;
struct net_buf *buf;
@ -2104,11 +2134,11 @@ static void le_ltk_request(struct net_buf *buf)
cp->handle = evt->handle;
/* use only enc_size bytes of key for encryption */
memcpy(cp->ltk, conn->keys->slave_ltk.val,
conn->keys->enc_size);
if (conn->keys->enc_size < sizeof(cp->ltk)) {
memset(cp->ltk + conn->keys->enc_size, 0,
sizeof(cp->ltk) - conn->keys->enc_size);
memcpy(cp->ltk, conn->le.keys->slave_ltk.val,
conn->le.keys->enc_size);
if (conn->le.keys->enc_size < sizeof(cp->ltk)) {
memset(cp->ltk + conn->le.keys->enc_size, 0,
sizeof(cp->ltk) - conn->le.keys->enc_size);
}
bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_REPLY, buf);
@ -2219,7 +2249,7 @@ static void hci_cmd_done(uint16_t opcode, uint8_t status, struct net_buf *buf)
static void hci_cmd_complete(struct net_buf *buf)
{
struct hci_evt_cmd_complete *evt = (void *)buf->data;
struct bt_hci_evt_cmd_complete *evt = (void *)buf->data;
uint16_t opcode = sys_le16_to_cpu(evt->opcode);
uint8_t status;
@ -2397,7 +2427,7 @@ static int start_le_scan(uint8_t scan_type, uint16_t interval, uint16_t window,
}
#else
/* only set NRPA if there is no advertising ongoing */
if (!atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING)) {
if (!atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) {
err = le_set_nrpa();
if (err) {
net_buf_unref(buf);
@ -2700,15 +2730,6 @@ static void hci_cmd_tx_fiber(void)
}
}
static void read_local_features_complete(struct net_buf *buf)
{
struct bt_hci_rp_read_local_features *rp = (void *)buf->data;
BT_DBG("status %u", rp->status);
memcpy(bt_dev.features, rp->features, sizeof(bt_dev.features));
}
static void read_local_ver_complete(struct net_buf *buf)
{
struct bt_hci_rp_read_local_version_info *rp = (void *)buf->data;
@ -2821,6 +2842,24 @@ static void read_supported_commands_complete(struct net_buf *buf)
#endif /* CONFIG_BLUETOOTH_TINYCRYPT_ECC */
}
static void read_local_features_complete(struct net_buf *buf)
{
struct bt_hci_rp_read_local_features *rp = (void *)buf->data;
BT_DBG("status %u", rp->status);
memcpy(bt_dev.features[0], rp->features, sizeof(bt_dev.features[0]));
}
static void le_read_supp_states_complete(struct net_buf *buf)
{
struct bt_hci_rp_le_read_supp_states *rp = (void *)buf->data;
BT_DBG("status %u", rp->status);
bt_dev.le.states = sys_get_le64(rp->le_states);
}
static int common_init(void)
{
struct net_buf *rsp;
@ -2896,7 +2935,7 @@ static int le_init(void)
int err;
/* For now we only support LE capable controllers */
if (!lmp_le_capable(bt_dev)) {
if (!BT_FEAT_LE(bt_dev.features)) {
BT_ERR("Non-LE capable controller detected!");
return -ENODEV;
}
@ -2918,7 +2957,7 @@ static int le_init(void)
le_read_buffer_size_complete(rsp);
net_buf_unref(rsp);
if (lmp_bredr_capable(bt_dev)) {
if (BT_FEAT_BREDR(bt_dev.features)) {
buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP,
sizeof(*cp_le));
if (!buf) {
@ -2927,7 +2966,7 @@ static int le_init(void)
cp_le = net_buf_add(buf, sizeof(*cp_le));
/* Excplicitly enable LE for dual-mode controllers */
/* Explicitly enable LE for dual-mode controllers */
cp_le->le = 0x01;
cp_le->simul = 0x00;
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, buf,
@ -2937,6 +2976,18 @@ static int le_init(void)
}
}
/* Read LE Supported States */
if (BT_CMD_LE_STATES(bt_dev.supported_commands)) {
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_SUPP_STATES, NULL,
&rsp);
if (err) {
return err;
}
le_read_supp_states_complete(rsp);
net_buf_unref(rsp);
}
/* Set LE event mask */
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_EVENT_MASK, sizeof(*cp_mask));
if (!buf) {
return -ENOBUFS;
@ -2977,13 +3028,64 @@ static int le_init(void)
}
#if defined(CONFIG_BLUETOOTH_BREDR)
static int read_ext_features(void)
{
int i;
/* Read Local Supported Extended Features */
for (i = 1; i < LMP_FEAT_PAGES_COUNT; i++) {
struct bt_hci_cp_read_local_ext_features *cp;
struct bt_hci_rp_read_local_ext_features *rp;
struct net_buf *buf, *rsp;
int err;
buf = bt_hci_cmd_create(BT_HCI_OP_READ_LOCAL_EXT_FEATURES,
sizeof(*cp));
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
cp->page = i;
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_EXT_FEATURES,
buf, &rsp);
if (err) {
return err;
}
rp = (void *)rsp->data;
memcpy(&bt_dev.features[i], rp->ext_features,
sizeof(bt_dev.features[i]));
if (rp->max_page <= i) {
net_buf_unref(rsp);
break;
}
net_buf_unref(rsp);
}
return 0;
}
static int br_init(void)
{
struct net_buf *buf;
struct bt_hci_cp_write_ssp_mode *ssp_cp;
struct bt_hci_cp_write_inquiry_mode *inq_cp;
struct bt_hci_write_local_name *name_cp;
int err;
/* Read extended local features */
if (BT_FEAT_EXT_FEATURES(bt_dev.features)) {
err = read_ext_features();
if (err) {
return err;
}
}
/* Get BR/EDR buffer size */
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BUFFER_SIZE, NULL, &buf);
if (err) {
@ -3019,6 +3121,41 @@ static int br_init(void)
return err;
}
/* Set local name */
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_LOCAL_NAME, sizeof(*name_cp));
if (!buf) {
return -ENOBUFS;
}
name_cp = net_buf_add(buf, sizeof(*name_cp));
strncpy(name_cp->local_name, CONFIG_BLUETOOTH_BREDR_NAME,
sizeof(name_cp->local_name));
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_LOCAL_NAME, buf, NULL);
if (err) {
return err;
}
/* Enable BR/EDR SC if supported */
if (BT_FEAT_SC(bt_dev.features)) {
struct bt_hci_cp_write_sc_host_supp *sc_cp;
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SC_HOST_SUPP,
sizeof(*sc_cp));
if (!buf) {
return -ENOBUFS;
}
sc_cp = net_buf_add(buf, sizeof(*sc_cp));
sc_cp->sc_support = 0x01;
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SC_HOST_SUPP, buf,
NULL);
if (err) {
return err;
}
}
return 0;
}
#else
@ -3091,7 +3228,7 @@ static int set_event_mask(void)
#endif /* CONFIG_BLUETOOTH_CONN */
#if defined(CONFIG_BLUETOOTH_SMP)
if (bt_dev.le.features[0] & BT_HCI_LE_ENCRYPTION) {
if (BT_FEAT_LE_ENCR(bt_dev.le.features)) {
ev->events[0] |= 0x80; /* Encryption Change */
ev->events[5] |= 0x80; /* Encryption Key Refresh Complete */
}
@ -3169,13 +3306,18 @@ static int hci_init(void)
return err;
}
if (lmp_bredr_capable(bt_dev)) {
if (BT_FEAT_BREDR(bt_dev.features)) {
err = br_init();
if (err) {
return err;
}
} else {
#if defined(CONFIG_BLUETOOTH_BREDR)
BT_ERR("Non-BR/EDR controller detected");
return -EIO;
#else
BT_DBG("Non-BR/EDR controller detected! Skipping BR init.");
#endif
}
err = set_event_mask();
@ -3544,15 +3686,10 @@ int bt_le_adv_start(const struct bt_le_adv_param *param,
return -EINVAL;
}
if (atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING)) {
if (atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) {
return -EALREADY;
}
err = set_advertise_disable();
if (err) {
return err;
}
err = set_ad(BT_HCI_OP_LE_SET_ADV_DATA, ad, ad_len);
if (err) {
return err;
@ -3633,7 +3770,7 @@ int bt_le_adv_start(const struct bt_le_adv_param *param,
return err;
}
err = set_advertise_enable();
err = set_advertise_enable(true);
if (err) {
return err;
}
@ -3647,17 +3784,25 @@ int bt_le_adv_stop(void)
{
int err;
if (!atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING)) {
/* Advertise disable may fail if slave connections are established,
* and advertising is not kept ON as the controller does not support
* simultaneous slave connections and connectable advertising state.
* Hence, we test and clear BT_DEV_KEEP_ADVERTISING flag before trying
* to disable advertising if BT_DEV_ADVERTISING is set.
*/
if (!atomic_test_and_clear_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING)) {
return -EALREADY;
}
err = set_advertise_disable();
if (!atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) {
return 0;
}
err = set_advertise_enable(false);
if (err) {
return err;
}
atomic_clear_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING);
#if !defined(CONFIG_BLUETOOTH_PRIVACY)
/* If active scan is ongoing set NRPA */
if (atomic_test_bit(bt_dev.flags, BT_DEV_ACTIVE_SCAN)) {

View file

@ -16,14 +16,16 @@
* limitations under the License.
*/
/* LMP feature helpers */
#define lmp_bredr_capable(dev) (!((dev).features[4] & BT_LMP_NO_BREDR))
#define lmp_le_capable(dev) ((dev).features[4] & BT_LMP_LE)
/* LL connection parameters */
#define LE_CONN_LATENCY 0x0000
#define LE_CONN_TIMEOUT 0x002a
#if defined(CONFIG_BLUETOOTH_BREDR)
#define LMP_FEAT_PAGES_COUNT 3
#else
#define LMP_FEAT_PAGES_COUNT 1
#endif
/* bt_dev flags: the flags defined here represent BT controller state */
enum {
BT_DEV_ENABLE,
@ -50,7 +52,9 @@ enum {
struct bt_dev_le {
/* LE features */
uint8_t features[8];
uint8_t features[1][8];
/* LE states */
uint64_t states;
/* Controller buffer information */
uint16_t mtu;
@ -78,8 +82,8 @@ struct bt_dev {
uint16_t hci_revision;
uint16_t manufacturer;
/* BR/EDR features page 0 */
uint8_t features[8];
/* LMP features (pages 0, 1, 2) */
uint8_t features[LMP_FEAT_PAGES_COUNT][8];
/* Supported commands */
uint8_t supported_commands[36];

View file

@ -46,6 +46,7 @@ static const uint32_t debug_private_key[8] = {
};
static struct nano_fifo ecc_queue;
static bool ecc_queue_ready;
static int (*drv_send)(struct net_buf *buf);
static uint32_t private_key[8];
@ -184,9 +185,23 @@ static void emulate_le_generate_dhkey(struct net_buf *buf)
bt_recv(buf);
}
static void ecc_queue_init(void)
{
unsigned int mask;
mask = irq_lock();
if (!ecc_queue_ready) {
nano_fifo_init(&ecc_queue);
ecc_queue_ready = true;
}
irq_unlock(mask);
}
static void ecc_task(void)
{
nano_fifo_init(&ecc_queue);
ecc_queue_init();
while (true) {
struct net_buf *buf;
@ -247,6 +262,8 @@ static int ecc_send(struct net_buf *buf)
void bt_hci_ecc_init(void)
{
ecc_queue_init();
/* set wrapper for driver send function */
drv_send = bt_dev.drv->send;
bt_dev.drv->send = ecc_send;

142
net/bluetooth/hci_raw.c Normal file
View file

@ -0,0 +1,142 @@
/* hci_userchan.c - HCI user channel Bluetooth handling */
/*
* Copyright (c) 2015-2016 Intel Corporation
*
* 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 <atomic.h>
#include <bluetooth/driver.h>
#include <bluetooth/log.h>
#include "monitor.h"
static struct nano_fifo *raw_rx;
/* ACL incoming buffers */
static struct nano_fifo avail_acl_in;
static NET_BUF_POOL(acl_in_pool, CONFIG_BLUETOOTH_ACL_IN_COUNT,
BT_BUF_ACL_IN_SIZE, &avail_acl_in, NULL,
sizeof(uint8_t));
/* HCI event buffers */
static struct nano_fifo avail_hci_evt;
static NET_BUF_POOL(hci_evt_pool, CONFIG_BLUETOOTH_HCI_EVT_COUNT,
BT_BUF_EVT_SIZE, &avail_hci_evt, NULL,
sizeof(uint8_t));
static struct bt_dev {
/* Registered HCI driver */
struct bt_driver *drv;
} bt_dev;
int bt_driver_register(struct bt_driver *drv)
{
if (bt_dev.drv) {
return -EALREADY;
}
if (!drv->open || !drv->send) {
return -EINVAL;
}
bt_dev.drv = drv;
BT_DBG("Registered %s", drv->name ? drv->name : "");
bt_monitor_new_index(BT_MONITOR_TYPE_PRIMARY, drv->bus,
BT_ADDR_ANY, drv->name ? drv->name : "bt0");
return 0;
}
void bt_driver_unregister(struct bt_driver *drv)
{
bt_dev.drv = NULL;
}
struct net_buf *bt_buf_get_evt(uint8_t opcode)
{
struct net_buf *buf;
buf = net_buf_get(&avail_hci_evt, 0);
if (buf) {
bt_buf_set_type(buf, BT_BUF_EVT);
}
return buf;
}
struct net_buf *bt_buf_get_acl(void)
{
struct net_buf *buf;
buf = net_buf_get(&avail_acl_in, 0);
if (buf) {
bt_buf_set_type(buf, BT_BUF_ACL_IN);
}
return buf;
}
int bt_recv(struct net_buf *buf)
{
BT_DBG("buf %p len %u", buf, buf->len);
bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len);
/* Queue to RAW rx queue */
net_buf_put(raw_rx, buf);
return 0;
}
int bt_send(struct net_buf *buf)
{
BT_DBG("buf %p len %u", buf, buf->len);
bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len);
return bt_dev.drv->send(buf);
}
int bt_enable_raw(struct nano_fifo *rx_queue)
{
struct bt_driver *drv = bt_dev.drv;
int err;
BT_DBG("");
net_buf_pool_init(hci_evt_pool);
net_buf_pool_init(acl_in_pool);
raw_rx = rx_queue;
if (!bt_dev.drv) {
BT_ERR("No HCI driver registered");
return -ENODEV;
}
err = drv->open();
if (err) {
BT_ERR("HCI driver open failed (%d)", err);
return err;
}
BT_INFO("Bluetooth enabled in RAW mode");
return 0;
}

View file

@ -38,7 +38,6 @@
static struct bt_keys key_pool[CONFIG_BLUETOOTH_MAX_PAIRED];
#if defined(CONFIG_BLUETOOTH_SMP)
struct bt_keys *bt_keys_get_addr(const bt_addr_le_t *addr)
{
struct bt_keys *keys;
@ -159,105 +158,16 @@ struct bt_keys *bt_keys_find_addr(const bt_addr_le_t *addr)
return NULL;
}
#endif /* CONFIG_BLUETOOTH_SMP */
void bt_keys_add_type(struct bt_keys *keys, int type)
{
keys->keys |= type;
}
void bt_keys_clear(struct bt_keys *keys, int type)
void bt_keys_clear(struct bt_keys *keys)
{
BT_DBG("keys for %s type %d", bt_addr_le_str(&keys->addr), type);
BT_DBG("keys for %s", bt_addr_le_str(&keys->addr));
keys->keys &= ~type;
if (!keys->keys) {
memset(keys, 0, sizeof(*keys));
}
}
#if defined(CONFIG_BLUETOOTH_BREDR)
static struct bt_keys *bt_keys_get_addr_br(const bt_addr_t *addr)
{
struct bt_keys *keys;
int i;
BT_DBG("%s", bt_addr_str(addr));
for (i = 0; i < ARRAY_SIZE(key_pool); i++) {
keys = &key_pool[i];
/*
* When both LE and BR/EDR keys are for the same device,
* the bt_addr_le_t is the public address, i.e. the same
* as the BR/EDR address.
*/
if (keys->addr.type == BT_ADDR_LE_PUBLIC &&
!bt_addr_cmp(&keys->addr.a, addr)) {
return keys;
}
/*
* BT_ADDR_LE_ANY has the same type of as BT_ADDR_LE_PUBLIC
* value. No need to make redundant comparison against
* BT_ADDR_LE_PUBLIC.
*/
if (!bt_addr_le_cmp(&keys->addr, BT_ADDR_LE_ANY)) {
bt_addr_copy(&keys->addr.a, addr);
BT_DBG("created %p for %s", keys, bt_addr_str(addr));
return keys;
}
}
BT_DBG("unable to create keys for %s", bt_addr_str(addr));
return NULL;
}
struct bt_keys *bt_keys_find_link_key(const bt_addr_t *addr)
{
struct bt_keys *keys;
int i;
BT_DBG("%s", bt_addr_str(addr));
for (i = 0; i < ARRAY_SIZE(key_pool); i++) {
keys = &key_pool[i];
/*
* When both LE and BR/EDR keys are for the same device,
* the bt_addr_le_t is the public address, i.e. the same
* as the BR/EDR address.
*/
if (keys->addr.type == BT_ADDR_LE_PUBLIC &&
(keys->keys & BT_KEYS_LINK_KEY) &&
!bt_addr_cmp(&keys->addr.a, addr)) {
return keys;
}
}
return NULL;
}
struct bt_keys *bt_keys_get_link_key(const bt_addr_t *addr)
{
struct bt_keys *keys;
BT_DBG("%s", bt_addr_str(addr));
keys = bt_keys_find_link_key(addr);
if (keys) {
return keys;
}
keys = bt_keys_get_addr_br(addr);
if (!keys) {
return NULL;
}
bt_keys_add_type(keys, BT_KEYS_LINK_KEY);
return keys;
}
#endif /* CONFIG_BLUETOOTH_BREDR */

View file

@ -16,7 +16,7 @@
* limitations under the License.
*/
#if defined(CONFIG_BLUETOOTH_SMP) || defined(CONFIG_BLUETOOTH_BREDR)
#if defined(CONFIG_BLUETOOTH_SMP)
enum {
BT_KEYS_SLAVE_LTK = BIT(0),
BT_KEYS_IRK = BIT(1),
@ -24,12 +24,10 @@ enum {
BT_KEYS_LOCAL_CSRK = BIT(3),
BT_KEYS_REMOTE_CSRK = BIT(4),
BT_KEYS_LTK_P256 = BIT(5),
BT_KEYS_LINK_KEY = BIT(6),
BT_KEYS_ALL = (BT_KEYS_SLAVE_LTK | BT_KEYS_IRK | \
BT_KEYS_LTK | BT_KEYS_LOCAL_CSRK | \
BT_KEYS_REMOTE_CSRK | BT_KEYS_LTK_P256 | \
BT_KEYS_LINK_KEY),
BT_KEYS_REMOTE_CSRK | BT_KEYS_LTK_P256),
};
enum {
@ -56,46 +54,49 @@ struct bt_csrk {
uint32_t cnt;
};
struct bt_link_key {
uint8_t val[16];
};
struct bt_keys {
bt_addr_le_t addr;
uint8_t enc_size;
ATOMIC_DEFINE(flags, BT_KEYS_NUM_FLAGS);
uint16_t keys;
#if defined(CONFIG_BLUETOOTH_SMP)
#if !defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
struct bt_ltk slave_ltk;
#endif /* CONFIG_BLUETOOTH_SMP_SC_ONLY */
struct bt_ltk ltk;
struct bt_irk irk;
#if defined(CONFIG_BLUETOOTH_SIGNING)
struct bt_csrk local_csrk;
struct bt_csrk remote_csrk;
#endif /* BLUETOOTH_SIGNING */
#endif /* CONFIG_BLUETOOTH_SMP */
#if defined(CONFIG_BLUETOOTH_BREDR)
struct bt_link_key link_key;
#endif /* CONFIG_BLUETOOTH_BREDR */
#if !defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
struct bt_ltk slave_ltk;
#endif /* CONFIG_BLUETOOTH_SMP_SC_ONLY */
};
#if defined(CONFIG_BLUETOOTH_BREDR)
struct bt_keys *bt_keys_get_link_key(const bt_addr_t *addr);
struct bt_keys *bt_keys_find_link_key(const bt_addr_t *addr);
#endif /* CONFIG_BLUETOOTH_BREDR */
#if defined(CONFIG_BLUETOOTH_SMP)
struct bt_keys *bt_keys_get_addr(const bt_addr_le_t *addr);
struct bt_keys *bt_keys_get_type(int type, const bt_addr_le_t *addr);
struct bt_keys *bt_keys_find(int type, const bt_addr_le_t *addr);
struct bt_keys *bt_keys_find_irk(const bt_addr_le_t *addr);
struct bt_keys *bt_keys_find_addr(const bt_addr_le_t *addr);
#endif /* CONFIG_BLUETOOTH_SMP */
void bt_keys_add_type(struct bt_keys *keys, int type);
void bt_keys_clear(struct bt_keys *keys, int type);
#endif /* CONFIG_BLUETOOTH_SMP || CONFIG_BLUETOOTH_BREDR */
void bt_keys_clear(struct bt_keys *keys);
#endif /* CONFIG_BLUETOOTH_SMP */
#if defined(CONFIG_BLUETOOTH_BREDR)
enum {
BT_LINK_KEY_AUTHENTICATED,
BT_LINK_KEY_DEBUG,
BT_LINK_KEY_SC,
/* Total number of flags - must be at the end of the enum */
BT_LINK_KEY_NUM_FLAGS,
};
struct bt_keys_link_key {
bt_addr_t addr;
ATOMIC_DEFINE(flags, BT_LINK_KEY_NUM_FLAGS);
uint8_t val[16];
};
struct bt_keys_link_key *bt_keys_get_link_key(const bt_addr_t *addr);
struct bt_keys_link_key *bt_keys_find_link_key(const bt_addr_t *addr);
void bt_keys_link_key_clear(struct bt_keys_link_key *link_key);
#endif /* CONFIG_BLUETOOTH_BREDR */

85
net/bluetooth/keys_br.c Normal file
View file

@ -0,0 +1,85 @@
/* keys_br.c - Bluetooth BR/EDR key handling */
/*
* Copyright (c) 2015-2016 Intel Corporation
*
* 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 <string.h>
#include <nanokernel.h>
#include <atomic.h>
#include <misc/util.h>
#include <misc/nano_work.h>
#include <bluetooth/log.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/hci.h>
#include "hci_core.h"
#include "keys.h"
#if !defined(CONFIG_BLUETOOTH_DEBUG_KEYS)
#undef BT_DBG
#define BT_DBG(fmt, ...)
#endif
static struct bt_keys_link_key key_pool[CONFIG_BLUETOOTH_MAX_PAIRED];
struct bt_keys_link_key *bt_keys_find_link_key(const bt_addr_t *addr)
{
struct bt_keys_link_key *key;
int i;
BT_DBG("%s", bt_addr_str(addr));
for (i = 0; i < ARRAY_SIZE(key_pool); i++) {
key = &key_pool[i];
if (!bt_addr_cmp(&key->addr, addr)) {
return key;
}
}
return NULL;
}
struct bt_keys_link_key *bt_keys_get_link_key(const bt_addr_t *addr)
{
struct bt_keys_link_key *key;
key = bt_keys_find_link_key(addr);
if (key) {
return key;
}
key = bt_keys_find_link_key(BT_ADDR_ANY);
if (key) {
bt_addr_copy(&key->addr, addr);
BT_DBG("created %p for %s", key, bt_addr_str(addr));
return key;
}
BT_DBG("unable to create link key for %s", bt_addr_str(addr));
return NULL;
}
void bt_keys_link_key_clear(struct bt_keys_link_key *link_key)
{
BT_DBG("%s", bt_addr_str(&link_key->addr));
memset(link_key, 0, sizeof(*link_key));
}

View file

@ -41,7 +41,7 @@
#define BT_DBG(fmt, ...)
#endif
#define LE_CHAN_RTX(_w) CONTAINER_OF(_w, struct bt_l2cap_le_chan, rtx_work)
#define LE_CHAN_RTX(_w) CONTAINER_OF(_w, struct bt_l2cap_le_chan, chan.rtx_work)
#define L2CAP_LE_MIN_MTU 23
#define L2CAP_LE_MAX_CREDITS (CONFIG_BLUETOOTH_ACL_IN_COUNT - 1)
@ -68,16 +68,11 @@
#define l2cap_remove_ident(conn, ident) __l2cap_lookup_ident(conn, ident, true)
#endif /* CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL */
enum l2cap_conn_list_action {
L2CAP_LOOKUP_CHAN,
L2CAP_DETACH_CHAN,
};
/* Wrapper macros making action on channel's list assigned to connection */
#define l2cap_lookup_chan(conn, chan) \
__l2cap_chan(conn, chan, L2CAP_LOOKUP_CHAN)
__l2cap_chan(conn, chan, BT_L2CAP_CHAN_LOOKUP)
#define l2cap_detach_chan(conn, chan) \
__l2cap_chan(conn, chan, L2CAP_DETACH_CHAN)
__l2cap_chan(conn, chan, BT_L2CAP_CHAN_DETACH)
static struct bt_l2cap_fixed_chan *le_channels;
#if defined(CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL)
@ -193,7 +188,7 @@ static struct bt_l2cap_le_chan *__l2cap_chan(struct bt_conn *conn,
}
switch (action) {
case L2CAP_DETACH_CHAN:
case BT_L2CAP_CHAN_DETACH:
if (!prev) {
conn->channels = chan->_next;
} else {
@ -201,7 +196,7 @@ static struct bt_l2cap_le_chan *__l2cap_chan(struct bt_conn *conn,
}
return BT_L2CAP_LE_CHAN(chan);
case L2CAP_LOOKUP_CHAN:
case BT_L2CAP_CHAN_LOOKUP:
default:
return BT_L2CAP_LE_CHAN(chan);
}
@ -228,10 +223,6 @@ destroy:
if (chan->destroy) {
chan->destroy(chan);
}
#if defined(CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL)
chan->state = BT_L2CAP_DISCONNECTED;
#endif /* CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL */
}
static void l2cap_rtx_timeout(struct nano_work *work)
@ -266,7 +257,7 @@ static bool l2cap_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan,
return false;
}
nano_delayed_work_init(&ch->rtx_work, l2cap_rtx_timeout);
nano_delayed_work_init(&chan->rtx_work, l2cap_rtx_timeout);
bt_l2cap_chan_add(conn, chan, destroy);
@ -545,7 +536,7 @@ static void l2cap_chan_destroy(struct bt_l2cap_chan *chan)
BT_DBG("chan %p cid 0x%04x", ch, ch->rx.cid);
/* Cancel ongoing work */
nano_delayed_work_cancel(&ch->rtx_work);
nano_delayed_work_cancel(&chan->rtx_work);
/* There could be a writer waiting for credits so return a dummy credit
* to wake it up.
@ -793,7 +784,7 @@ static void le_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
l2cap_chan_rx_give_credits(chan, L2CAP_LE_MAX_CREDITS);
/* Cancel RTX work */
nano_delayed_work_cancel(&chan->rtx_work);
nano_delayed_work_cancel(&chan->chan.rtx_work);
break;
/* TODO: Retry on Authentication and Encryption errors */
@ -1258,9 +1249,9 @@ static void l2cap_chan_send_req(struct bt_l2cap_le_chan *chan,
* link is lost.
*/
if (ticks) {
nano_delayed_work_submit(&chan->rtx_work, ticks);
nano_delayed_work_submit(&chan->chan.rtx_work, ticks);
} else {
nano_delayed_work_cancel(&chan->rtx_work);
nano_delayed_work_cancel(&chan->chan.rtx_work);
}
bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_LE_SIG, buf);

View file

@ -35,6 +35,9 @@
#include "hci_core.h"
#include "conn_internal.h"
#include "l2cap_internal.h"
#if defined(CONFIG_BLUETOOTH_RFCOMM)
#include "rfcomm_internal.h"
#endif
#if !defined(CONFIG_BLUETOOTH_DEBUG_L2CAP)
#undef BT_DBG
@ -42,6 +45,7 @@
#endif
#define BR_CHAN(_ch) CONTAINER_OF(_ch, struct bt_l2cap_br_chan, chan)
#define BR_CHAN_RTX(_w) CONTAINER_OF(_w, struct bt_l2cap_br_chan, chan.rtx_work)
#define L2CAP_BR_PSM_START 0x0001
#define L2CAP_BR_PSM_END 0xffff
@ -54,17 +58,34 @@
#define L2CAP_BR_PSM_SDP 0x0001
#define L2CAP_BR_INFO_TIMEOUT SECONDS(4)
#define L2CAP_BR_CFG_TIMEOUT SECONDS(4)
#define L2CAP_BR_DISCONN_TIMEOUT SECONDS(1)
#define L2CAP_BR_CONN_TIMEOUT SECONDS(40)
/* Size of MTU is based on the maximum amount of data the buffer can hold
* excluding ACL and driver headers.
*/
#define L2CAP_BR_MAX_MTU CONFIG_BLUETOOTH_L2CAP_IN_MTU
/*
* L2CAP extended feature mask:
* BR/EDR fixed channel support enabled
*/
#define L2CAP_FEAT_FIXED_CHAN_MASK 0x00000080
/* Wrapper macros making action on channel's list assigned to connection */
#define l2cap_br_lookup_chan(conn, chan) \
__l2cap_chan(conn, chan, BT_L2CAP_CHAN_LOOKUP)
#define l2cap_br_detach_chan(conn, chan) \
__l2cap_chan(conn, chan, BT_L2CAP_CHAN_DETACH)
/* Auxiliary L2CAP CoC flags making channel context */
enum {
L2CAP_FLAG_LCONF_DONE, /* local config accepted by remote */
L2CAP_FLAG_RCONF_DONE, /* remote config accepted by local */
L2CAP_FLAG_ACCEPTOR, /* getting incoming connection req on PSM */
L2CAP_FLAG_CONN_PENDING,/* remote sent pending result in response */
};
static struct bt_l2cap_server *br_servers;
@ -226,7 +247,83 @@ l2cap_br_chan_alloc_cid(struct bt_conn *conn, struct bt_l2cap_chan *chan)
return NULL;
}
static bool l2cap_br_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan)
static struct bt_l2cap_br_chan *__l2cap_chan(struct bt_conn *conn,
struct bt_l2cap_chan *ch,
enum l2cap_conn_list_action action)
{
struct bt_l2cap_chan *chan, *prev;
for (chan = conn->channels, prev = NULL; chan;
prev = chan, chan = chan->_next) {
if (chan != ch) {
continue;
}
switch (action) {
case BT_L2CAP_CHAN_DETACH:
if (!prev) {
conn->channels = chan->_next;
} else {
prev->_next = chan->_next;
}
return BR_CHAN(chan);
case BT_L2CAP_CHAN_LOOKUP:
default:
return BR_CHAN(chan);
}
}
return NULL;
}
static void l2cap_br_chan_destroy(struct bt_l2cap_chan *chan)
{
BT_DBG("chan %p cid 0x%04x", BR_CHAN(chan), BR_CHAN(chan)->rx.cid);
/* Cancel ongoing work */
nano_delayed_work_cancel(&chan->rtx_work);
/* Reset _ONLY_ internal members of common channel */
chan->state = BT_L2CAP_DISCONNECTED;
/* Reset _ONLY_ BR/EDR specific members on L2CAP app channel */
BR_CHAN(chan)->psm = 0;
atomic_clear(BR_CHAN(chan)->flags);
}
static void l2cap_br_rtx_timeout(struct nano_work *work)
{
struct bt_l2cap_br_chan *chan = BR_CHAN_RTX(work);
struct bt_l2cap_br *l2cap = CONTAINER_OF(chan, struct bt_l2cap_br,
chan.chan);
BT_WARN("chan %p timeout", chan);
if (chan->rx.cid == BT_L2CAP_CID_BR_SIG) {
BT_DBG("Skip BR/EDR signalling channel ");
atomic_clear_bit(l2cap->flags, BT_L2CAP_FLAG_INFO_PENDING);
return;
}
BT_DBG("chan %p %s scid 0x%04x", chan, state2str(chan->chan.state),
chan->rx.cid);
switch (chan->chan.state) {
case BT_L2CAP_CONFIG:
bt_l2cap_br_chan_disconnect(&chan->chan);
break;
case BT_L2CAP_DISCONNECT:
case BT_L2CAP_CONNECT:
l2cap_br_detach_chan(chan->chan.conn, &chan->chan);
bt_l2cap_chan_del(&chan->chan);
break;
default:
break;
}
}
static bool l2cap_br_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan,
bt_l2cap_chan_destroy_t destroy)
{
struct bt_l2cap_br_chan *ch = l2cap_br_chan_alloc_cid(conn, chan);
@ -235,7 +332,8 @@ static bool l2cap_br_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan)
return false;
}
bt_l2cap_chan_add(conn, chan, NULL);
nano_delayed_work_init(&chan->rtx_work, l2cap_br_rtx_timeout);
bt_l2cap_chan_add(conn, chan, destroy);
return true;
}
@ -253,6 +351,27 @@ static uint8_t l2cap_br_get_ident(void)
return ident;
}
static void l2cap_br_chan_send_req(struct bt_l2cap_br_chan *chan,
struct net_buf *buf, uint32_t ticks)
{
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part A] page 126:
*
* The value of this timer is implementation-dependent but the minimum
* initial value is 1 second and the maximum initial value is 60
* seconds. One RTX timer shall exist for each outstanding signaling
* request, including each Echo Request. The timer disappears on the
* final expiration, when the response is received, or the physical
* link is lost.
*/
if (ticks) {
nano_delayed_work_submit(&chan->chan.rtx_work, ticks);
} else {
nano_delayed_work_cancel(&chan->chan.rtx_work);
}
bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_BR_SIG, buf);
}
static void l2cap_br_get_info(struct bt_l2cap_br *l2cap, uint16_t info_type)
{
struct bt_l2cap_info_req *info;
@ -291,8 +410,7 @@ static void l2cap_br_get_info(struct bt_l2cap_br *l2cap, uint16_t info_type)
info = net_buf_add(buf, sizeof(*info));
info->type = sys_cpu_to_le16(info_type);
/* TODO: add command timeout guard */
bt_l2cap_send(l2cap->chan.chan.conn, BT_L2CAP_CID_BR_SIG, buf);
l2cap_br_chan_send_req(&l2cap->chan, buf, L2CAP_BR_INFO_TIMEOUT);
}
static int l2cap_br_info_rsp(struct bt_l2cap_br *l2cap, uint8_t ident,
@ -306,7 +424,14 @@ static int l2cap_br_info_rsp(struct bt_l2cap_br *l2cap, uint8_t ident,
return 0;
}
atomic_clear_bit(l2cap->flags, BT_L2CAP_FLAG_INFO_PENDING);
if (atomic_test_and_clear_bit(l2cap->flags,
BT_L2CAP_FLAG_INFO_PENDING)) {
/*
* Release RTX timer since got the response & there's pending
* command request.
*/
nano_delayed_work_cancel(&l2cap->chan.chan.rtx_work);
}
if (buf->len < sizeof(*rsp)) {
BT_ERR("Too small info rsp packet size");
@ -400,7 +525,7 @@ static int l2cap_br_info_req(struct bt_l2cap_br *l2cap, uint8_t ident,
/* fixed channel mask protocol data is 8 octets wide */
memset(net_buf_add(rsp_buf, 8), 0, 8);
/* signaling channel is mandatory on BR/EDR transport */
rsp->data[0] = BT_L2CAP_MASK_BR_SIG;
rsp->data[0] = BIT(BT_L2CAP_CID_BR_SIG);
hdr_info->len = sys_cpu_to_le16(sizeof(*rsp) + 8);
break;
default:
@ -439,7 +564,7 @@ void bt_l2cap_br_connected(struct bt_conn *conn)
ch->rx.cid = fchan->cid;
ch->tx.cid = fchan->cid;
if (!l2cap_br_chan_add(conn, chan)) {
if (!l2cap_br_chan_add(conn, chan, NULL)) {
return;
}
@ -474,7 +599,6 @@ static void l2cap_br_conf_add_mtu(struct net_buf *buf, const uint16_t mtu)
static void l2cap_br_conf(struct bt_l2cap_chan *chan)
{
struct bt_conn *conn = chan->conn;
struct bt_l2cap_sig_hdr *hdr;
struct bt_l2cap_conf_req *conf;
struct net_buf *buf;
@ -504,44 +628,95 @@ static void l2cap_br_conf(struct bt_l2cap_chan *chan)
/*
* TODO:
* 1. start tracking number of configuration iterations on
* might be needed to start tracking number of configuration iterations
* on both directions
* 2. add individual command timeout guard
* 3. might be the option to add overall configuration phase
* timeout (max 120sec)
*/
bt_l2cap_send(conn, BT_L2CAP_CID_BR_SIG, buf);
l2cap_br_chan_send_req(BR_CHAN(chan), buf, L2CAP_BR_CFG_TIMEOUT);
}
static bool l2cap_br_security_check(struct bt_l2cap_chan *chan,
const uint16_t psm)
enum l2cap_br_conn_security_result {
L2CAP_CONN_SECURITY_PASSED,
L2CAP_CONN_SECURITY_REJECT,
L2CAP_CONN_SECURITY_PENDING
};
/*
* Security helper against channel connection.
* Returns L2CAP_CONN_SECURITY_PASSED if:
* - existing security on link is applicable for requested PSM in connection,
* - legacy (non SSP) devices connecting with low security requirements,
* Returns L2CAP_CONN_SECURITY_PENDING if:
* - channel connection process is on hold since there were valid security
* conditions triggering authentication indirectly in subcall.
* Returns L2CAP_CONN_SECURITY_REJECT if:
* - bt_conn_security API returns < 0.
*/
static enum l2cap_br_conn_security_result
l2cap_br_conn_security(struct bt_l2cap_chan *chan, const uint16_t psm)
{
int check;
/* For SDP PSM there's no need to change existing security on link */
if (psm == L2CAP_BR_PSM_SDP) {
return false;
return L2CAP_CONN_SECURITY_PASSED;
};
/* No link key needed and legacy devices */
/*
* No link key needed for legacy devices (pre 2.1) and when low security
* level is required.
*/
if (chan->required_sec_level == BT_SECURITY_LOW &&
!lmp_ssp_host_supported(chan->conn)) {
return false;
!BT_FEAT_HOST_SSP(chan->conn->br.features)) {
return L2CAP_CONN_SECURITY_PASSED;
}
switch (chan->required_sec_level) {
case BT_SECURITY_FIPS:
case BT_SECURITY_HIGH:
case BT_SECURITY_MEDIUM:
break;
default:
/*
* For non SDP PSM connections GAP's Security Mode 4 requires at
* least unauthenticated link key and enabled encryption if
* remote supports SSP before any L2CAP CoC traffic. So preset
* local to MEDIUM security to trigger it if needed.
*/
if (BT_FEAT_HOST_SSP(chan->conn->br.features)) {
chan->required_sec_level = BT_SECURITY_MEDIUM;
}
break;
}
check = bt_conn_security(chan->conn, chan->required_sec_level);
/*
* Get case when connection security level already covers channel
* demands and bt_conn_security returns 0 and differentiate it to
* the case when HCI authentication request in internal subcall was send
* and bt_conn_security returns as well 0.
* Check case when on existing connection security level already covers
* channel (service) security requirements against link security and
* bt_conn_security API returns 0 what implies also there was no need to
* trigger authentication.
*/
if (check == 0 &&
chan->conn->sec_level >= chan->required_sec_level) {
return false;
return L2CAP_CONN_SECURITY_PASSED;
}
return (check == 0) ? true : false;
/*
* If 'check' still holds 0, it means local host just sent HCI
* authentication command to start procedure to increase link security
* since service/profile requires that.
*/
if (check == 0) {
return L2CAP_CONN_SECURITY_PENDING;
};
/*
* For any other values in 'check' it means there was internal
* validation condition forbidding to start authentication at this
* moment.
*/
return L2CAP_CONN_SECURITY_REJECT;
}
static void l2cap_br_conn_req(struct bt_l2cap_br *l2cap, uint8_t ident,
@ -590,7 +765,7 @@ static void l2cap_br_conn_req(struct bt_l2cap_br *l2cap, uint8_t ident,
* Report security violation for non SDP channel without encryption when
* remote supports SSP.
*/
if (psm != L2CAP_BR_PSM_SDP && lmp_ssp_host_supported(conn) &&
if (psm != L2CAP_BR_PSM_SDP && BT_FEAT_HOST_SSP(conn->br.features) &&
!conn->encrypt) {
result = BT_L2CAP_ERR_SEC_BLOCK;
goto done;
@ -616,20 +791,30 @@ static void l2cap_br_conn_req(struct bt_l2cap_br *l2cap, uint8_t ident,
goto done;
}
l2cap_br_chan_add(conn, chan);
l2cap_br_chan_add(conn, chan, l2cap_br_chan_destroy);
BR_CHAN(chan)->tx.cid = scid;
dcid = BR_CHAN(chan)->rx.cid;
l2cap_br_state_set(chan, BT_L2CAP_CONNECT);
atomic_set_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_ACCEPTOR);
if (l2cap_br_security_check(chan, psm)) {
/* Disable fragmentation of l2cap rx pdu */
BR_CHAN(chan)->rx.mtu = min(BR_CHAN(chan)->rx.mtu, L2CAP_BR_MAX_MTU);
switch (l2cap_br_conn_security(chan, psm)) {
case L2CAP_CONN_SECURITY_PENDING:
result = BT_L2CAP_PENDING;
status = BT_L2CAP_CS_AUTHEN_PEND;
/* store ident for connection response after GAP done */
chan->ident = ident;
/* TODO: auth timeout */
} else {
break;
case L2CAP_CONN_SECURITY_PASSED:
result = BT_L2CAP_SUCCESS;
break;
case L2CAP_CONN_SECURITY_REJECT:
default:
result = BT_L2CAP_ERR_SEC_BLOCK;
break;
}
done:
rsp->dcid = sys_cpu_to_le16(dcid);
@ -680,6 +865,9 @@ static void l2cap_br_conf_rsp(struct bt_l2cap_br *l2cap, uint8_t ident,
return;
}
/* Release RTX work since got the response */
nano_delayed_work_cancel(&chan->rtx_work);
/*
* TODO: handle other results than success and parse response data if
* available
@ -1004,7 +1192,16 @@ static void l2cap_br_connected(struct bt_l2cap_chan *chan)
static void l2cap_br_disconnected(struct bt_l2cap_chan *chan)
{
struct bt_l2cap_br *l2cap = CONTAINER_OF(BR_CHAN(chan),
struct bt_l2cap_br, chan.chan);
BT_DBG("ch %p cid 0x%04x", BR_CHAN(chan), BR_CHAN(chan)->rx.cid);
if (atomic_test_and_clear_bit(l2cap->flags,
BT_L2CAP_FLAG_INFO_PENDING)) {
/* Cancel RTX work on signal channel */
nano_delayed_work_cancel(&chan->rtx_work);
}
}
int bt_l2cap_br_chan_disconnect(struct bt_l2cap_chan *chan)
@ -1043,7 +1240,7 @@ int bt_l2cap_br_chan_disconnect(struct bt_l2cap_chan *chan)
req->dcid = sys_cpu_to_le16(ch->tx.cid);
req->scid = sys_cpu_to_le16(ch->rx.cid);
bt_l2cap_send(chan->conn, BT_L2CAP_CID_BR_SIG, buf);
l2cap_br_chan_send_req(ch, buf, L2CAP_BR_DISCONN_TIMEOUT);
l2cap_br_state_set(chan, BT_L2CAP_DISCONNECT);
return 0;
@ -1079,12 +1276,147 @@ static void l2cap_br_disconn_rsp(struct bt_l2cap_br *l2cap, uint8_t ident,
int bt_l2cap_br_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
uint16_t psm)
{
return -ENOTSUP;
struct net_buf *buf;
struct bt_l2cap_sig_hdr *hdr;
struct bt_l2cap_conn_req *req;
if (!psm) {
return -EINVAL;
}
if (BR_CHAN(chan)->psm) {
return -EEXIST;
}
/* PSM must be odd and lsb of upper byte must be 0 */
if ((psm & 0x0101) != 0x0001) {
return -EINVAL;
}
switch (chan->state) {
case BT_L2CAP_CONNECTED:
/* Already connected */
return -EISCONN;
case BT_L2CAP_DISCONNECTED:
/* Can connect */
break;
case BT_L2CAP_CONFIG:
case BT_L2CAP_DISCONNECT:
default:
/* Bad context */
return -EBUSY;
}
if (!l2cap_br_chan_add(conn, chan, l2cap_br_chan_destroy)) {
return -ENOMEM;
}
BR_CHAN(chan)->psm = psm;
l2cap_br_state_set(chan, BT_L2CAP_CONNECT);
switch (l2cap_br_conn_security(chan, psm)) {
case L2CAP_CONN_SECURITY_PENDING:
/*
* Authentication was triggered, wait with sending request on
* connection security changed callback context.
*/
return 0;
case L2CAP_CONN_SECURITY_PASSED:
break;
case L2CAP_CONN_SECURITY_REJECT:
default:
l2cap_br_detach_chan(chan->conn, chan);
bt_l2cap_chan_del(chan);
return -EIO;
}
buf = bt_l2cap_create_pdu(&br_sig);
if (!buf) {
BT_ERR("Unable to send L2CAP connection request");
return -ENOMEM;
}
hdr = net_buf_add(buf, sizeof(*hdr));
hdr->code = BT_L2CAP_CONN_REQ;
hdr->ident = l2cap_br_get_ident();
hdr->len = sys_cpu_to_le16(sizeof(*req));
req = net_buf_add(buf, sizeof(*req));
req->psm = sys_cpu_to_le16(psm);
req->scid = sys_cpu_to_le16(BR_CHAN(chan)->rx.cid);
l2cap_br_chan_send_req(BR_CHAN(chan), buf, L2CAP_BR_CONN_TIMEOUT);
return 0;
}
static void l2cap_br_conn_rsp(struct bt_l2cap_br *l2cap, uint8_t ident,
struct net_buf *buf)
{
struct bt_conn *conn = l2cap->chan.chan.conn;
struct bt_l2cap_chan *chan;
struct bt_l2cap_conn_rsp *rsp = (void *)buf->data;
uint16_t dcid, scid, result, status;
if (buf->len < sizeof(*rsp)) {
BT_ERR("Too small L2CAP conn rsp packet size");
return;
}
dcid = sys_le16_to_cpu(rsp->dcid);
scid = sys_le16_to_cpu(rsp->scid);
result = sys_le16_to_cpu(rsp->result);
status = sys_le16_to_cpu(rsp->status);
BT_DBG("dcid 0x%04x scid 0x%04x result %u status %u", dcid, scid,
result, status);
chan = bt_l2cap_br_lookup_rx_cid(conn, scid);
if (!chan) {
BT_ERR("No scid 0x%04x channel found", scid);
return;
}
/* Release RTX work since got the response */
nano_delayed_work_cancel(&chan->rtx_work);
if (chan->state != BT_L2CAP_CONNECT) {
BT_DBG("Invalid channel %p state %s", chan,
state2str(chan->state));
return;
}
switch (result) {
case BT_L2CAP_SUCCESS:
chan->ident = 0;
BR_CHAN(chan)->tx.cid = dcid;
l2cap_br_conf(chan);
l2cap_br_state_set(chan, BT_L2CAP_CONFIG);
atomic_clear_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_PENDING);
break;
case BT_L2CAP_PENDING:
atomic_set_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_PENDING);
nano_delayed_work_submit(&chan->rtx_work,
L2CAP_BR_CONN_TIMEOUT);
break;
default:
l2cap_br_detach_chan(chan->conn, chan);
bt_l2cap_chan_del(chan);
break;
}
}
int bt_l2cap_br_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
return -ENOTSUP;
struct bt_l2cap_br_chan *ch = BR_CHAN(chan);
if (buf->len > ch->tx.mtu) {
return -EMSGSIZE;
}
bt_l2cap_send(ch->chan.conn, ch->tx.cid, buf);
return buf->len;
}
static void l2cap_br_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
@ -1136,6 +1468,9 @@ static void l2cap_br_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
case BT_L2CAP_DISCONN_RSP:
l2cap_br_disconn_rsp(l2cap, hdr->ident, buf);
break;
case BT_L2CAP_CONN_RSP:
l2cap_br_conn_rsp(l2cap, hdr->ident, buf);
break;
default:
BT_WARN("Unknown/Unsupported L2CAP PDU code 0x%02x", hdr->code);
l2cap_br_send_reject(chan->conn, hdr->ident,
@ -1149,11 +1484,16 @@ static void l2cap_br_conn_pend(struct bt_l2cap_chan *chan)
struct net_buf *buf;
struct bt_l2cap_conn_rsp *rsp;
struct bt_l2cap_sig_hdr *hdr;
struct bt_l2cap_conn_req *req;
if (chan->state != BT_L2CAP_CONNECT) {
return;
}
if (!chan->conn->encrypt) {
return;
}
/*
* For incoming connection state send confirming outstanding
* response and initiate configuration request.
@ -1186,6 +1526,25 @@ static void l2cap_br_conn_pend(struct bt_l2cap_chan *chan)
* local MTU segmentation.
*/
l2cap_br_conf(chan);
} else if (!atomic_test_bit(BR_CHAN(chan)->flags,
L2CAP_FLAG_CONN_PENDING)) {
buf = bt_l2cap_create_pdu(&br_sig);
if (!buf) {
BT_ERR("Unable to send L2CAP connection request");
return;
}
hdr = net_buf_add(buf, sizeof(*hdr));
hdr->code = BT_L2CAP_CONN_REQ;
hdr->ident = l2cap_br_get_ident();
hdr->len = sys_cpu_to_le16(sizeof(*req));
req = net_buf_add(buf, sizeof(*req));
req->psm = sys_cpu_to_le16(BR_CHAN(chan)->psm);
req->scid = sys_cpu_to_le16(BR_CHAN(chan)->rx.cid);
l2cap_br_chan_send_req(BR_CHAN(chan), buf,
L2CAP_BR_CONN_TIMEOUT);
}
}
@ -1231,7 +1590,7 @@ static int l2cap_br_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
return -ENOMEM;
}
static void bt_l2cap_br_fixed_chan_register(struct bt_l2cap_fixed_chan *chan)
void bt_l2cap_br_fixed_chan_register(struct bt_l2cap_fixed_chan *chan)
{
BT_DBG("CID 0x%04x", chan->cid);
@ -1243,10 +1602,12 @@ void bt_l2cap_br_init(void)
{
static struct bt_l2cap_fixed_chan chan_br = {
.cid = BT_L2CAP_CID_BR_SIG,
.mask = BT_L2CAP_MASK_BR_SIG,
.accept = l2cap_br_accept,
};
net_buf_pool_init(br_sig_pool);
bt_l2cap_br_fixed_chan_register(&chan_br);
#if defined(CONFIG_BLUETOOTH_RFCOMM)
bt_rfcomm_init();
#endif /* CONFIG_BLUETOOTH_RFCOMM */
}

View file

@ -20,14 +20,17 @@
#include <bluetooth/l2cap.h>
enum l2cap_conn_list_action {
BT_L2CAP_CHAN_LOOKUP,
BT_L2CAP_CHAN_DETACH,
};
#define BT_L2CAP_CID_BR_SIG 0x0001
#define BT_L2CAP_CID_ATT 0x0004
#define BT_L2CAP_CID_LE_SIG 0x0005
#define BT_L2CAP_CID_SMP 0x0006
/* Supported BR/EDR fixed channels mask (first octet) */
#define BT_L2CAP_MASK_BR_SIG 0x02
#define BT_L2CAP_MASK_SMP 0x80
#define BT_L2CAP_PSM_RFCOMM 0x0003
struct bt_l2cap_hdr {
uint16_t len;
@ -196,12 +199,6 @@ struct bt_l2cap_le_credits {
struct bt_l2cap_fixed_chan {
uint16_t cid;
#if defined(CONFIG_BLUETOOTH_BREDR)
/* Supported channels mask (first octet). Only for BR/EDR. */
uint8_t mask;
#endif
int (*accept)(struct bt_conn *conn, struct bt_l2cap_chan **chan);
struct bt_l2cap_fixed_chan *_next;
@ -254,6 +251,9 @@ struct bt_l2cap_chan *bt_l2cap_le_lookup_rx_cid(struct bt_conn *conn,
/* Initialize BR/EDR L2CAP signal layer */
void bt_l2cap_br_init(void);
/* Register fixed channel */
void bt_l2cap_br_fixed_chan_register(struct bt_l2cap_fixed_chan *chan);
/* Notify BR/EDR L2CAP channels about established new ACL connection */
void bt_l2cap_br_connected(struct bt_conn *conn);

View file

@ -177,8 +177,10 @@ static int bt_monitor_init(struct device *d)
monitor_dev = device_get_binding(CONFIG_BLUETOOTH_MONITOR_ON_DEV_NAME);
#if defined(CONFIG_UART_INTERRUPT_DRIVEN)
uart_irq_rx_disable(monitor_dev);
uart_irq_tx_disable(monitor_dev);
#endif
#if !defined(CONFIG_UART_CONSOLE)
__printk_hook_install(monitor_console_out);

728
net/bluetooth/rfcomm.c Normal file
View file

@ -0,0 +1,728 @@
/* rfcomm.c - RFCOMM handling */
/*
* Copyright (c) 2016 Intel Corporation
*
* 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 <nanokernel.h>
#include <arch/cpu.h>
#include <toolchain.h>
#include <string.h>
#include <errno.h>
#include <atomic.h>
#include <misc/byteorder.h>
#include <misc/util.h>
#include <bluetooth/log.h>
#include <bluetooth/hci.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/driver.h>
#include <bluetooth/l2cap.h>
#include <bluetooth/rfcomm.h>
#include "hci_core.h"
#include "conn_internal.h"
#include "l2cap_internal.h"
#include "rfcomm_internal.h"
#if !defined(CONFIG_BLUETOOTH_DEBUG_RFCOMM)
#undef BT_DBG
#define BT_DBG(fmt, ...)
#endif
#define RFCOMM_CHANNEL_START 0x01
#define RFCOMM_CHANNEL_END 0x1e
#define RFCOMM_MIN_MTU BT_RFCOMM_SIG_MIN_MTU
#define RFCOMM_DEFAULT_MTU 127
#define RFCOMM_DEFAULT_CREDIT 7
static struct bt_rfcomm_server *servers;
/* Pool for outgoing RFCOMM control packets, min MTU is 23 */
static struct nano_fifo rfcomm_session;
static NET_BUF_POOL(rfcomm_session_pool, CONFIG_BLUETOOTH_MAX_CONN,
BT_RFCOMM_BUF_SIZE(RFCOMM_MIN_MTU), &rfcomm_session, NULL,
BT_BUF_USER_DATA_MIN);
#define RFCOMM_SESSION(_ch) CONTAINER_OF(_ch, \
struct bt_rfcomm_session, br_chan.chan)
static struct bt_rfcomm_session bt_rfcomm_pool[CONFIG_BLUETOOTH_MAX_CONN];
/* reversed, 8-bit, poly=0x07 */
static const uint8_t rfcomm_crc_table[256] = {
0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
};
static uint8_t rfcomm_calc_fcs(uint16_t len, const uint8_t *data)
{
uint8_t fcs = 0xff;
while (len--) {
fcs = rfcomm_crc_table[fcs ^ *data++];
}
/* Ones compliment */
return (0xff - fcs);
}
static bool rfcomm_check_fcs(uint16_t len, const uint8_t *data,
uint8_t recvd_fcs)
{
uint8_t fcs = 0xff;
while (len--) {
fcs = rfcomm_crc_table[fcs ^ *data++];
}
/* Ones compliment */
fcs = rfcomm_crc_table[fcs ^ recvd_fcs];
/*0xCF is the reversed order of 11110011.*/
return (fcs == 0xcf);
}
static struct bt_rfcomm_dlc *rfcomm_dlcs_lookup_dlci(struct bt_rfcomm_dlc *dlcs,
uint8_t dlci)
{
for (; dlcs; dlcs = dlcs->_next) {
if (dlcs->dlci == dlci) {
return dlcs;
}
}
return NULL;
}
static struct bt_rfcomm_server *rfcomm_server_lookup_channel(uint8_t channel)
{
struct bt_rfcomm_server *server;
for (server = servers; server; server = server->_next) {
if (server->channel == channel) {
return server;
}
}
return NULL;
}
int bt_rfcomm_server_register(struct bt_rfcomm_server *server)
{
if (server->channel < RFCOMM_CHANNEL_START ||
server->channel > RFCOMM_CHANNEL_END || !server->accept) {
return -EINVAL;
}
/* Check if given channel is already in use */
if (rfcomm_server_lookup_channel(server->channel)) {
BT_DBG("Channel already registered");
return -EADDRINUSE;
}
BT_DBG("Channel 0x%02x", server->channel);
server->_next = servers;
servers = server;
return 0;
}
static void rfcomm_dlc_tx_give_credits(struct bt_rfcomm_dlc *dlc,
uint8_t credits)
{
BT_DBG("dlc %p credits %u", dlc, credits);
while (credits--) {
nano_sem_give(&dlc->tx_credits);
}
BT_DBG("dlc %p updated credits %u", dlc, dlc->tx_credits.nsig);
}
static void rfcomm_dlc_unref(struct bt_rfcomm_dlc *dlc)
{
atomic_dec(&dlc->ref);
BT_DBG("dlc %p ref %u", dlc, atomic_get(&dlc->ref));
/* TODO: Destroy dlc if ref is 0 */
}
static struct bt_rfcomm_dlc *rfcomm_dlc_ref(struct bt_rfcomm_dlc *dlc)
{
atomic_inc(&dlc->ref);
BT_DBG("dlc %p ref %u", dlc, atomic_get(&dlc->ref));
return dlc;
}
struct net_buf *bt_rfcomm_create_pdu(struct nano_fifo *fifo)
{
/* Length in RFCOMM header can be 2 bytes depending on length of user
* data
*/
return bt_conn_create_pdu(fifo,
sizeof(struct bt_l2cap_hdr) +
sizeof(struct bt_rfcomm_hdr) + 1);
}
static struct net_buf *rfcomm_make_uih_msg(struct bt_rfcomm_dlc *dlc,
uint8_t cr, uint8_t type,
uint8_t len)
{
struct bt_rfcomm_hdr *hdr;
struct bt_rfcomm_msg_hdr *msg_hdr;
struct net_buf *buf;
uint8_t hdr_cr;
buf = bt_l2cap_create_pdu(&rfcomm_session);
if (!buf) {
BT_ERR("No buffers");
return NULL;
}
hdr = net_buf_add(buf, sizeof(*hdr));
hdr_cr = dlc->session->initiator ? 1 : 0;
hdr->address = BT_RFCOMM_SET_ADDR(0, hdr_cr);
hdr->control = BT_RFCOMM_SET_CTRL(BT_RFCOMM_UIH, 0);
hdr->length = BT_RFCOMM_SET_LEN_8(sizeof(*msg_hdr) + len);
msg_hdr = net_buf_add(buf, sizeof(*msg_hdr));
msg_hdr->type = BT_RFCOMM_SET_MSG_TYPE(type, cr);
msg_hdr->len = BT_RFCOMM_SET_LEN_8(len);
return buf;
}
static void rfcomm_connected(struct bt_l2cap_chan *chan)
{
struct bt_rfcomm_session *session = RFCOMM_SESSION(chan);
BT_DBG("Session %p", session);
/* Need to include UIH header and FCS*/
session->mtu = min(session->br_chan.rx.mtu,
session->br_chan.tx.mtu) -
BT_RFCOMM_HDR_SIZE + BT_RFCOMM_FCS_SIZE;
}
static void rfcomm_disconnected(struct bt_l2cap_chan *chan)
{
BT_DBG("Session %p", RFCOMM_SESSION(chan));
}
static struct bt_rfcomm_dlc *rfcomm_dlc_accept(struct bt_rfcomm_session *session,
uint8_t dlci)
{
struct bt_rfcomm_server *server;
struct bt_rfcomm_dlc *dlc;
uint8_t channel;
channel = BT_RFCOMM_GET_CHANNEL(dlci);
server = rfcomm_server_lookup_channel(channel);
if (!server) {
BT_ERR("Server Channel not registered");
return NULL;
}
if (server->accept(session->br_chan.chan.conn, &dlc) < 0) {
BT_DBG("Incoming connection rejected");
return NULL;
}
if (!BT_RFCOMM_CHECK_MTU(dlc->mtu)) {
/* Destroy dlc */
return NULL;
}
BT_DBG("Dlc %p initialized", dlc);
dlc->_next = session->dlcs;
session->dlcs = dlc;
dlc->dlci = dlci;
dlc->session = session;
dlc->initiator = false;
dlc->rx_credit = RFCOMM_DEFAULT_CREDIT;
dlc->state = BT_RFCOMM_STATE_INIT;
dlc->mtu = min(dlc->mtu, session->mtu);
atomic_set(&dlc->ref, 1);
nano_sem_init(&dlc->tx_credits);
return dlc;
}
static void rfcomm_dlc_tx_fiber(int arg1, int arg2)
{
struct bt_rfcomm_dlc *dlc = (struct bt_rfcomm_dlc *)arg1;
struct net_buf *buf;
BT_DBG("Started for dlc %p", dlc);
while (dlc->state == BT_RFCOMM_STATE_CONNECTED) {
/* Get next packet for dlc */
BT_DBG("Wait for buf %p", dlc);
buf = net_buf_get_timeout(&dlc->tx_queue, 0, TICKS_UNLIMITED);
if (dlc->state != BT_RFCOMM_STATE_CONNECTED) {
net_buf_unref(buf);
break;
}
BT_DBG("Wait for credits %p", dlc);
/* Wait for credits */
nano_sem_take(&dlc->tx_credits, TICKS_UNLIMITED);
if (dlc->state != BT_RFCOMM_STATE_CONNECTED) {
net_buf_unref(buf);
break;
}
if (bt_l2cap_chan_send(&dlc->session->br_chan.chan, buf) < 0) {
/* This fails only if channel is disconnected */
net_buf_unref(buf);
break;
}
}
BT_DBG("dlc %p disconnected - cleaning up", dlc);
/* Give back any allocated buffers */
while ((buf = net_buf_get_timeout(&dlc->tx_queue, 0, TICKS_NONE))) {
net_buf_unref(buf);
}
rfcomm_dlc_unref(dlc);
BT_DBG("dlc %p exiting", dlc);
}
static int rfcomm_send_ua(struct bt_rfcomm_session *session, uint8_t dlci)
{
struct bt_rfcomm_hdr *hdr;
struct net_buf *buf;
uint8_t cr, fcs;
buf = bt_l2cap_create_pdu(&rfcomm_session);
if (!buf) {
BT_ERR("No buffers");
return -ENOMEM;
}
hdr = net_buf_add(buf, sizeof(*hdr));
cr = session->initiator ? 0 : 1;
hdr->address = BT_RFCOMM_SET_ADDR(dlci, cr);
hdr->control = BT_RFCOMM_SET_CTRL(BT_RFCOMM_UA, 1);
hdr->length = BT_RFCOMM_SET_LEN_8(0);
fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_NON_UIH, buf->data);
net_buf_add_u8(buf, fcs);
return bt_l2cap_chan_send(&session->br_chan.chan, buf);
}
static int rfcomm_send_msc(struct bt_rfcomm_dlc *dlc, uint8_t cr)
{
struct bt_rfcomm_msc *msc;
struct net_buf *buf;
uint8_t fcs;
buf = rfcomm_make_uih_msg(dlc, cr, BT_RFCOMM_MSC, sizeof(*msc));
if (!buf) {
return -ENOMEM;
}
msc = net_buf_add(buf, sizeof(*msc));
/* cr bit should be always 1 in MSC */
msc->dlci = BT_RFCOMM_SET_ADDR(dlc->dlci, 1);
msc->v24_signal = BT_RFCOMM_DEFAULT_V24_SIG;
fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data);
net_buf_add_u8(buf, fcs);
return bt_l2cap_chan_send(&dlc->session->br_chan.chan, buf);
}
static void rfcomm_dlc_connected(struct bt_rfcomm_dlc *dlc)
{
dlc->state = BT_RFCOMM_STATE_CONNECTED;
rfcomm_send_msc(dlc, BT_RFCOMM_MSG_CMD);
nano_fifo_init(&dlc->tx_queue);
fiber_start(dlc->stack, sizeof(dlc->stack), rfcomm_dlc_tx_fiber,
(int)rfcomm_dlc_ref(dlc), 0, 7, 0);
if (dlc->ops && dlc->ops->connected) {
dlc->ops->connected(dlc);
}
}
static void rfcomm_handle_sabm(struct bt_rfcomm_session *session, uint8_t dlci)
{
if (!dlci) {
session->initiator = false;
if (rfcomm_send_ua(session, dlci) < 0) {
return;
}
session->state = BT_RFCOMM_STATE_CONNECTED;
} else {
struct bt_rfcomm_dlc *dlc;
dlc = rfcomm_dlcs_lookup_dlci(session->dlcs, dlci);
if (!dlc) {
dlc = rfcomm_dlc_accept(session, dlci);
if (!dlc) {
return;
}
}
if (rfcomm_send_ua(session, dlci) < 0) {
return;
}
rfcomm_dlc_connected(dlc);
}
}
static int rfcomm_send_pn(struct bt_rfcomm_dlc *dlc, uint8_t cr)
{
struct bt_rfcomm_pn *pn;
struct net_buf *buf;
uint8_t fcs;
buf = rfcomm_make_uih_msg(dlc, cr, BT_RFCOMM_PN, sizeof(*pn));
if (!buf) {
return -ENOMEM;
}
BT_DBG("mtu %x", dlc->mtu);
pn = net_buf_add(buf, sizeof(*pn));
pn->dlci = dlc->dlci;
pn->mtu = sys_cpu_to_le16(dlc->mtu);
if (dlc->state == BT_RFCOMM_STATE_CONFIG) {
pn->credits = dlc->rx_credit;
pn->flow_ctrl = cr ? 0xf0 : 0xe0;
} else {
/* If PN comes in already opened dlc these should be 0*/
pn->credits = 0;
pn->flow_ctrl = 0;
}
pn->max_retrans = 0;
pn->ack_timer = 0;
pn->priority = 0;
fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data);
net_buf_add_u8(buf, fcs);
return bt_l2cap_chan_send(&dlc->session->br_chan.chan, buf);
}
static void rfcomm_handle_msc(struct bt_rfcomm_session *session,
struct net_buf *buf, uint8_t cr)
{
struct bt_rfcomm_msc *msc = (void *)buf->data;
struct bt_rfcomm_dlc *dlc;
uint8_t dlci = BT_RFCOMM_GET_DLCI(msc->dlci);
BT_DBG("dlci %d", dlci);
dlc = rfcomm_dlcs_lookup_dlci(session->dlcs, dlci);
if (!dlc) {
return;
}
if (cr == BT_RFCOMM_MSG_CMD) {
rfcomm_send_msc(dlc, BT_RFCOMM_MSG_RESP);
}
}
static void rfcomm_handle_pn(struct bt_rfcomm_session *session,
struct net_buf *buf, uint8_t cr)
{
struct bt_rfcomm_pn *pn = (void *)buf->data;
struct bt_rfcomm_dlc *dlc;
if (!BT_RFCOMM_CHECK_MTU(pn->mtu)) {
/* TODO: Send DM */
BT_ERR("Invalid mtu %d", pn->mtu);
return;
}
dlc = rfcomm_dlcs_lookup_dlci(session->dlcs, pn->dlci);
if (!dlc) {
dlc = rfcomm_dlc_accept(session, pn->dlci);
if (!dlc) {
return;
}
BT_DBG("Incoming connection accepted dlc %p", dlc);
dlc->mtu = min(dlc->mtu, sys_le16_to_cpu(pn->mtu));
rfcomm_dlc_tx_give_credits(dlc, pn->credits);
dlc->state = BT_RFCOMM_STATE_CONFIG;
}
rfcomm_send_pn(dlc, BT_RFCOMM_MSG_RESP);
}
static void rfcomm_handle_msg(struct bt_rfcomm_session *session,
struct net_buf *buf)
{
struct bt_rfcomm_msg_hdr *hdr = (void *)buf->data;
uint8_t msg_type, len, cr;
msg_type = BT_RFCOMM_GET_MSG_TYPE(hdr->type);
cr = BT_RFCOMM_GET_MSG_CR(hdr->type);
len = BT_RFCOMM_GET_LEN(hdr->len);
BT_DBG("msg type %x cr %x", msg_type, cr);
net_buf_pull(buf, sizeof(*hdr));
switch (msg_type) {
case BT_RFCOMM_PN:
rfcomm_handle_pn(session, buf, cr);
break;
case BT_RFCOMM_MSC:
rfcomm_handle_msc(session, buf, cr);
break;
default:
BT_WARN("Unknown/Unsupported RFCOMM Msg type 0x%02x", msg_type);
break;
}
}
static void rfcomm_handle_data(struct bt_rfcomm_session *session,
struct net_buf *buf, uint8_t dlci, uint8_t pf)
{
struct bt_rfcomm_dlc *dlc;
BT_DBG("dlci %d, pf %d", dlci, pf);
dlc = rfcomm_dlcs_lookup_dlci(session->dlcs, dlci);
if (!dlc) {
BT_ERR("Data recvd in non existing DLC");
return;
}
BT_DBG("dlc %p rx credit %d", dlc, dlc->rx_credit);
if (dlc->state != BT_RFCOMM_STATE_CONNECTED) {
return;
}
if (pf == BT_RFCOMM_PF_CREDIT) {
rfcomm_dlc_tx_give_credits(dlc, net_buf_pull_u8(buf));
}
if (buf->len > BT_RFCOMM_FCS_SIZE) {
if (!dlc->rx_credit) {
BT_ERR("Data recvd when rx credit is 0");
/* Disconnect */
return;
}
/* Remove FCS */
buf->len -= BT_RFCOMM_FCS_SIZE;
if (dlc->ops && dlc->ops->recv) {
dlc->ops->recv(dlc, buf);
}
dlc->rx_credit--;
}
}
int bt_rfcomm_dlc_send(struct bt_rfcomm_dlc *dlc, struct net_buf *buf)
{
struct bt_rfcomm_hdr *hdr;
uint8_t fcs;
if (!buf) {
return -EINVAL;
}
BT_DBG("dlc %p tx credit %d", dlc, dlc->tx_credits.nsig);
if (dlc->state != BT_RFCOMM_STATE_CONNECTED) {
return -ENOTCONN;
}
if (buf->len > dlc->mtu) {
return -EMSGSIZE;
}
if (buf->len > BT_RFCOMM_MAX_LEN_8) {
uint16_t *len;
/* Length is 2 byte */
hdr = net_buf_push(buf, sizeof(*hdr) + 1);
len = (uint16_t *)&hdr->length;
*len = BT_RFCOMM_SET_LEN_16(sys_cpu_to_le16(buf->len -
sizeof(*hdr) + 1));
} else {
hdr = net_buf_push(buf, sizeof(*hdr));
hdr->length = BT_RFCOMM_SET_LEN_8(buf->len - sizeof(*hdr));
}
hdr->address = BT_RFCOMM_SET_ADDR(dlc->dlci, dlc->session->initiator);
hdr->control = BT_RFCOMM_SET_CTRL(BT_RFCOMM_UIH,
BT_RFCOMM_PF_NO_CREDIT);
fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data);
net_buf_add_u8(buf, fcs);
net_buf_put(&dlc->tx_queue, buf);
return buf->len;
}
static void rfcomm_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
struct bt_rfcomm_session *session = RFCOMM_SESSION(chan);
struct bt_rfcomm_hdr *hdr = (void *)buf->data;
uint8_t dlci, frame_type, fcs, fcs_len;
/* Need to consider FCS also*/
if (buf->len < (sizeof(*hdr) + 1)) {
BT_ERR("Too small RFCOMM Frame");
return;
}
dlci = BT_RFCOMM_GET_DLCI(hdr->address);
frame_type = BT_RFCOMM_GET_FRAME_TYPE(hdr->control);
BT_DBG("session %p dlci %x type %x", session, dlci, frame_type);
fcs_len = (frame_type == BT_RFCOMM_UIH) ? BT_RFCOMM_FCS_LEN_UIH :
BT_RFCOMM_FCS_LEN_NON_UIH;
fcs = *(net_buf_tail(buf) - 1);
if (!rfcomm_check_fcs(fcs_len, buf->data, fcs)) {
BT_ERR("FCS check failed");
return;
}
if (BT_RFCOMM_LEN_EXTENDED(hdr->length)) {
net_buf_pull(buf, sizeof(*hdr) + 1);
} else {
net_buf_pull(buf, sizeof(*hdr));
}
switch (frame_type) {
case BT_RFCOMM_SABM:
rfcomm_handle_sabm(session, dlci);
break;
case BT_RFCOMM_UIH:
if (!dlci) {
rfcomm_handle_msg(session, buf);
} else {
rfcomm_handle_data(session, buf, dlci,
BT_RFCOMM_GET_PF(hdr->control));
}
break;
default:
BT_WARN("Unknown/Unsupported RFCOMM Frame type 0x%02x",
frame_type);
break;
}
}
static int rfcomm_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
{
int i;
static struct bt_l2cap_chan_ops ops = {
.connected = rfcomm_connected,
.disconnected = rfcomm_disconnected,
.recv = rfcomm_recv,
};
BT_DBG("conn %p handle %u", conn, conn->handle);
for (i = 0; i < ARRAY_SIZE(bt_rfcomm_pool); i++) {
struct bt_rfcomm_session *session = &bt_rfcomm_pool[i];
if (session->br_chan.chan.conn) {
continue;
}
BT_DBG("session %p initialized", session);
session->br_chan.chan.ops = &ops;
session->br_chan.rx.mtu = CONFIG_BLUETOOTH_RFCOMM_L2CAP_MTU;
session->state = BT_RFCOMM_STATE_INIT;
*chan = &session->br_chan.chan;
return 0;
}
BT_ERR("No available RFCOMM context for conn %p", conn);
return -ENOMEM;
}
void bt_rfcomm_init(void)
{
static struct bt_l2cap_server server = {
.psm = BT_L2CAP_PSM_RFCOMM,
.accept = rfcomm_accept,
};
net_buf_pool_init(rfcomm_session_pool);
bt_l2cap_br_server_register(&server);
}

View file

@ -0,0 +1,125 @@
/** @file
* @brief Internal APIs for Bluetooth RFCOMM handling.
*/
/*
* Copyright (c) 2015-2016 Intel Corporation
*
* 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 <bluetooth/rfcomm.h>
/* RFCOMM signalling connection specific context */
struct bt_rfcomm_session {
/* L2CAP channel this context is associated with */
struct bt_l2cap_br_chan br_chan;
struct bt_rfcomm_dlc *dlcs;
uint16_t mtu;
uint8_t state;
bool initiator;
};
enum {
BT_RFCOMM_STATE_IDLE,
BT_RFCOMM_STATE_INIT,
BT_RFCOMM_STATE_CONNECTED,
BT_RFCOMM_STATE_CONFIG,
};
struct bt_rfcomm_hdr {
uint8_t address;
uint8_t control;
uint8_t length;
} __packed;
#define BT_RFCOMM_SABM 0x2f
#define BT_RFCOMM_UA 0x63
#define BT_RFCOMM_UIH 0xef
struct bt_rfcomm_msg_hdr {
uint8_t type;
uint8_t len;
} __packed;
#define BT_RFCOMM_PN 0x20
struct bt_rfcomm_pn {
uint8_t dlci;
uint8_t flow_ctrl;
uint8_t priority;
uint8_t ack_timer;
uint16_t mtu;
uint8_t max_retrans;
uint8_t credits;
} __packed;
#define BT_RFCOMM_MSC 0x38
struct bt_rfcomm_msc {
uint8_t dlci;
uint8_t v24_signal;
} __packed;
/* DV = 1 IC = 0 RTR = 1 RTC = 1 FC = 0 EXT = 0 */
#define BT_RFCOMM_DEFAULT_V24_SIG 0x8d
#define BT_RFCOMM_SIG_MIN_MTU 23
#define BT_RFCOMM_SIG_MAX_MTU 32767
#define BT_RFCOMM_CHECK_MTU(mtu) (!!((mtu) >= BT_RFCOMM_SIG_MIN_MTU && \
(mtu) <= BT_RFCOMM_SIG_MAX_MTU))
/* Helper to calculate needed outgoing buffer size.
* Length in rfcomm header can be two bytes depending on user data length.
* One byte in the tail should be reserved for FCS.
*/
#define BT_RFCOMM_BUF_SIZE(mtu) (CONFIG_BLUETOOTH_HCI_SEND_RESERVE + \
sizeof(struct bt_hci_acl_hdr) + \
sizeof(struct bt_l2cap_hdr) + \
sizeof(struct bt_rfcomm_hdr) + 1 + (mtu) + \
BT_RFCOMM_FCS_SIZE)
#define BT_RFCOMM_GET_DLCI(addr) (((addr) & 0xfc) >> 2)
#define BT_RFCOMM_GET_FRAME_TYPE(ctrl) ((ctrl) & 0xef)
#define BT_RFCOMM_GET_MSG_TYPE(type) (((type) & 0xfc) >> 2)
#define BT_RFCOMM_GET_MSG_CR(type) (((type) & 0x02) >> 1)
#define BT_RFCOMM_GET_LEN(len) (((len) & 0xfe) >> 1)
#define BT_RFCOMM_GET_CHANNEL(dlci) ((dlci) >> 1)
#define BT_RFCOMM_GET_PF(ctrl) (((ctrl) & 0x10) >> 4)
#define BT_RFCOMM_SET_ADDR(dlci, cr) ((((dlci) & 0x3f) << 2) | \
((cr) << 1) | 0x01)
#define BT_RFCOMM_SET_CTRL(type, pf) (((type) & 0xef) | ((pf) << 4))
#define BT_RFCOMM_SET_LEN_8(len) (((len) << 1) | 1)
#define BT_RFCOMM_SET_LEN_16(len) ((len) << 1)
#define BT_RFCOMM_SET_MSG_TYPE(type, cr) (((type) << 2) | (cr << 1) | 0x01)
#define BT_RFCOMM_LEN_EXTENDED(len) (!((len) & 0x01))
#define BT_RFCOMM_MSG_CMD 1
#define BT_RFCOMM_MSG_RESP 0
/* Excluding ext bit */
#define BT_RFCOMM_MAX_LEN_8 127
/* Length can be 2 bytes depending on data size */
#define BT_RFCOMM_HDR_SIZE (sizeof(struct bt_rfcomm_hdr) + 1)
#define BT_RFCOMM_FCS_SIZE 1
#define BT_RFCOMM_FCS_LEN_UIH 2
#define BT_RFCOMM_FCS_LEN_NON_UIH 3
#define BT_RFCOMM_PF_CREDIT 1
#define BT_RFCOMM_PF_NO_CREDIT 0
/* Initialize RFCOMM signal layer */
void bt_rfcomm_init(void);

View file

@ -62,16 +62,24 @@
#define SIGN_DIST 0
#endif
#define RECV_KEYS (BT_SMP_DIST_ID_KEY | BT_SMP_DIST_ENC_KEY | SIGN_DIST)
#if defined(CONFIG_BLUETOOTH_PRIVACY)
#define SEND_KEYS (BT_SMP_DIST_ENC_KEY | BT_SMP_DIST_ID_KEY | SIGN_DIST)
#define ID_DIST BT_SMP_DIST_ID_KEY
#else
#define SEND_KEYS (BT_SMP_DIST_ENC_KEY | SIGN_DIST)
#define ID_DIST 0
#endif
#define RECV_KEYS_SC (RECV_KEYS & ~(BT_SMP_DIST_ENC_KEY | BT_SMP_DIST_LINK_KEY))
#define SEND_KEYS_SC (SEND_KEYS & ~(BT_SMP_DIST_ENC_KEY | BT_SMP_DIST_LINK_KEY))
#if defined(CONFIG_BLUETOOTH_BREDR)
#define LINK_DIST BT_SMP_DIST_LINK_KEY
#else
#define LINK_DIST 0
#endif
#define RECV_KEYS (BT_SMP_DIST_ENC_KEY | BT_SMP_DIST_ID_KEY | SIGN_DIST |\
LINK_DIST)
#define SEND_KEYS (BT_SMP_DIST_ENC_KEY | ID_DIST | SIGN_DIST | LINK_DIST)
#define RECV_KEYS_SC (RECV_KEYS & ~(BT_SMP_DIST_ENC_KEY))
#define SEND_KEYS_SC (SEND_KEYS & ~(BT_SMP_DIST_ENC_KEY))
#define BT_SMP_AUTH_MASK 0x07
#define BT_SMP_AUTH_MASK_SC 0x0f
@ -99,6 +107,7 @@ enum {
SMP_FLAG_SC_DEBUG_KEY, /* if Secure Connection are using debug key */
SMP_FLAG_SEC_REQ, /* if Security Request was sent/received */
SMP_FLAG_DHCHECK_WAIT, /* if waiting for remote DHCheck (as slave) */
SMP_FLAG_DERIVE_LK, /* if Link Key should be derived */
/* Total number of flags - must be at the end */
SMP_NUM_FLAGS,
@ -535,6 +544,72 @@ static int smp_g2(const uint8_t u[32], const uint8_t v[32],
return 0;
}
#if defined(CONFIG_BLUETOOTH_BREDR)
static int smp_h6(const uint8_t w[16], const uint8_t key_id[4], uint8_t res[16])
{
uint8_t ws[16];
uint8_t key_id_s[4];
int err;
BT_DBG("w %s", bt_hex(w, 16));
BT_DBG("key_id %s", bt_hex(key_id, 4));
swap_buf(ws, w, 16);
swap_buf(key_id_s, key_id, 4);
err = bt_smp_aes_cmac(ws, key_id_s, 4, res);
if (err) {
return err;
}
BT_DBG("res %s", bt_hex(res, 16));
swap_in_place(res, 16);
return 0;
}
static void sc_derive_link_key(struct bt_smp *smp)
{
/* constants as specified in Core Spec Vol.3 Part H 2.4.2.4 */
static const uint8_t tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 };
static const uint8_t lebr[4] = { 0x72, 0x62, 0x65, 0x6c };
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_keys_link_key *link_key;
uint8_t ilk[16];
BT_DBG("");
/* TODO handle errors? */
/*
* At this point remote device identity is known so we can use
* destination address here
*/
link_key = bt_keys_get_link_key(&conn->le.dst.a);
if (!link_key) {
return;
}
if (smp_h6(conn->le.keys->ltk.val, tmp1, ilk)) {
bt_keys_link_key_clear(link_key);
return;
}
if (smp_h6(ilk, lebr, link_key->val)) {
bt_keys_link_key_clear(link_key);
}
atomic_set_bit(link_key->flags, BT_LINK_KEY_SC);
if (atomic_test_bit(conn->le.keys->flags, BT_KEYS_AUTHENTICATED)) {
atomic_set_bit(link_key->flags, BT_LINK_KEY_AUTHENTICATED);
} else {
atomic_clear_bit(link_key->flags, BT_LINK_KEY_AUTHENTICATED);
}
}
#endif /* CONFIG_BLUETOOTH_BREDR */
static void smp_reset(struct bt_smp *smp)
{
struct bt_conn *conn = smp->chan.chan.conn;
@ -563,6 +638,26 @@ static void smp_reset(struct bt_smp *smp)
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
}
static void smp_pairing_complete(struct bt_smp *smp, uint8_t status)
{
BT_DBG("status 0x%x", status);
#if defined(CONFIG_BLUETOOTH_BREDR)
if (!status) {
/*
* Don't derive if Debug Keys are used.
* TODO should we allow this if BR/EDR is already connected?
*/
if (atomic_test_bit(smp->flags, SMP_FLAG_DERIVE_LK) &&
!atomic_test_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY)) {
sc_derive_link_key(smp);
}
}
#endif /* CONFIG_BLUETOOTH_BREDR */
smp_reset(smp);
}
static void smp_timeout(struct nano_work *work)
{
struct bt_smp *smp = CONTAINER_OF(work, struct bt_smp, work);
@ -574,11 +669,11 @@ static void smp_timeout(struct nano_work *work)
* pairing failed and don't store any keys from this pairing.
*/
if (atomic_test_bit(smp->flags, SMP_FLAG_KEYS_DISTR) &&
smp->chan.chan.conn->keys) {
bt_keys_clear(smp->chan.chan.conn->keys, BT_KEYS_ALL);
smp->chan.chan.conn->le.keys) {
bt_keys_clear(smp->chan.chan.conn->le.keys);
}
smp_reset(smp);
smp_pairing_complete(smp, BT_SMP_ERR_UNSPECIFIED);
atomic_set_bit(smp->flags, SMP_FLAG_TIMEOUT);
}
@ -611,8 +706,8 @@ static int smp_error(struct bt_smp *smp, uint8_t reason)
struct bt_smp_pairing_fail *rsp;
struct net_buf *buf;
/* reset context */
smp_reset(smp);
/* reset context and report */
smp_pairing_complete(smp, reason);
buf = smp_create_pdu(smp->chan.chan.conn, BT_SMP_CMD_PAIRING_FAIL,
sizeof(*rsp));
@ -767,7 +862,7 @@ static uint8_t smp_send_pairing_confirm(struct bt_smp *smp)
static void legacy_distribute_keys(struct bt_smp *smp)
{
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_keys *keys = conn->keys;
struct bt_keys *keys = conn->le.keys;
if (smp->local_dist & BT_SMP_DIST_ENC_KEY) {
struct bt_smp_encrypt_info *info;
@ -829,7 +924,7 @@ static void legacy_distribute_keys(struct bt_smp *smp)
static void bt_smp_distribute_keys(struct bt_smp *smp)
{
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_keys *keys = conn->keys;
struct bt_keys *keys = conn->le.keys;
if (!keys) {
BT_ERR("No keys space for %s", bt_addr_le_str(&conn->le.dst));
@ -1249,7 +1344,7 @@ static uint8_t smp_master_ident(struct bt_smp *smp, struct net_buf *buf)
/* if all keys were distributed, pairing is done */
if (!smp->local_dist && !smp->remote_dist) {
smp_reset(smp);
smp_pairing_complete(smp, 0);
}
return 0;
@ -2054,11 +2149,11 @@ static uint8_t smp_pairing_failed(struct bt_smp *smp, struct net_buf *buf)
* so if there are any keys distributed, shall be cleared.
*/
if (atomic_test_bit(smp->flags, SMP_FLAG_KEYS_DISTR) &&
smp->chan.chan.conn->keys) {
bt_keys_clear(smp->chan.chan.conn->keys, BT_KEYS_ALL);
smp->chan.chan.conn->le.keys) {
bt_keys_clear(smp->chan.chan.conn->le.keys);
}
smp_reset(smp);
smp_pairing_complete(smp, req->reason);
/* return no error to avoid sending Pairing Failed in response */
return 0;
@ -2159,7 +2254,7 @@ static uint8_t smp_ident_addr_info(struct bt_smp *smp, struct net_buf *buf)
/* if all keys were distributed, pairing is done */
if (!smp->local_dist && !smp->remote_dist) {
smp_reset(smp);
smp_pairing_complete(smp, 0);
}
return 0;
@ -2197,7 +2292,7 @@ static uint8_t smp_signing_info(struct bt_smp *smp, struct net_buf *buf)
/* if all keys were distributed, pairing is done */
if (!smp->local_dist && !smp->remote_dist) {
smp_reset(smp);
smp_pairing_complete(smp, 0);
}
return 0;
@ -2224,20 +2319,21 @@ static uint8_t smp_security_request(struct bt_smp *smp, struct net_buf *buf)
auth = req->auth_req & BT_SMP_AUTH_MASK;
}
if (!conn->keys) {
conn->keys = bt_keys_find(BT_KEYS_LTK_P256, &conn->le.dst);
if (!conn->keys) {
conn->keys = bt_keys_find(BT_KEYS_LTK, &conn->le.dst);
if (!conn->le.keys) {
conn->le.keys = bt_keys_find(BT_KEYS_LTK_P256, &conn->le.dst);
if (!conn->le.keys) {
conn->le.keys = bt_keys_find(BT_KEYS_LTK,
&conn->le.dst);
}
}
if (!conn->keys) {
if (!conn->le.keys) {
goto pair;
}
/* if MITM required key must be authenticated */
if ((auth & BT_SMP_AUTH_MITM) &&
!atomic_test_bit(conn->keys->flags, BT_KEYS_AUTHENTICATED)) {
!atomic_test_bit(conn->le.keys->flags, BT_KEYS_AUTHENTICATED)) {
if (get_io_capa() != BT_SMP_IO_NO_INPUT_OUTPUT) {
BT_INFO("New auth requirements: 0x%x, repairing",
auth);
@ -2249,16 +2345,17 @@ static uint8_t smp_security_request(struct bt_smp *smp, struct net_buf *buf)
goto pair;
}
/* if LE SC required and no p256 key present reapair */
if ((auth & BT_SMP_AUTH_SC) && !(conn->keys->keys & BT_KEYS_LTK_P256)) {
/* if LE SC required and no p256 key present repair */
if ((auth & BT_SMP_AUTH_SC) &&
!(conn->le.keys->keys & BT_KEYS_LTK_P256)) {
BT_INFO("New auth requirements: 0x%x, repairing", auth);
goto pair;
}
if (bt_conn_le_start_encryption(conn, conn->keys->ltk.rand,
conn->keys->ltk.ediv,
conn->keys->ltk.val,
conn->keys->enc_size) < 0) {
if (bt_conn_le_start_encryption(conn, conn->le.keys->ltk.rand,
conn->le.keys->ltk.ediv,
conn->le.keys->ltk.val,
conn->le.keys->enc_size) < 0) {
return BT_SMP_ERR_UNSPECIFIED;
}
@ -2608,7 +2705,7 @@ static void bt_smp_connected(struct bt_l2cap_chan *chan)
static void bt_smp_disconnected(struct bt_l2cap_chan *chan)
{
struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan);
struct bt_keys *keys = chan->conn->keys;
struct bt_keys *keys = chan->conn->le.keys;
BT_DBG("chan %p cid 0x%04x", chan,
CONTAINER_OF(chan, struct bt_l2cap_le_chan, chan)->tx.cid);
@ -2622,7 +2719,7 @@ static void bt_smp_disconnected(struct bt_l2cap_chan *chan)
*/
if (!keys->keys ||
atomic_test_bit(keys->flags, BT_KEYS_DEBUG)) {
bt_keys_clear(keys, BT_KEYS_ALL);
bt_keys_clear(keys);
}
}
@ -2657,6 +2754,24 @@ static void bt_smp_encrypt_change(struct bt_l2cap_chan *chan)
return;
}
/* derive BR/EDR LinkKey if supported by both sides */
if (atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
if ((smp->local_dist & BT_SMP_DIST_LINK_KEY) &&
(smp->remote_dist & BT_SMP_DIST_LINK_KEY)) {
/*
* Link Key will be derived after key distribution to
* make sure remote device identity is known
*/
atomic_set_bit(smp->flags, SMP_FLAG_DERIVE_LK);
}
/*
* Those are used as pairing finished indicator so generated
* but not distributed keys must be cleared here.
*/
smp->local_dist &= ~BT_SMP_DIST_LINK_KEY;
smp->remote_dist &= ~BT_SMP_DIST_LINK_KEY;
}
if (smp->remote_dist & BT_SMP_DIST_ENC_KEY) {
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_ENCRYPT_INFO);
} else if (smp->remote_dist & BT_SMP_DIST_ID_KEY) {
@ -2678,7 +2793,7 @@ static void bt_smp_encrypt_change(struct bt_l2cap_chan *chan)
/* if all keys were distributed, pairing is done */
if (!smp->local_dist && !smp->remote_dist) {
smp_reset(smp);
smp_pairing_complete(smp, 0);
}
}
@ -3151,6 +3266,30 @@ static int smp_g2_test(void)
return 0;
}
#if defined(CONFIG_BLUETOOTH_BREDR)
static int smp_h6_test(void)
{
uint8_t w[16] = { 0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
uint8_t key_id[8] = { 0x72, 0x62, 0x65, 0x6c };
uint8_t exp_res[16] = { 0x99, 0x63, 0xb1, 0x80, 0xe2, 0xa9, 0xd3, 0xe8,
0x1c, 0xc9, 0x6d, 0xe7, 0x02, 0xe1, 0x9a, 0x2d};
uint8_t res[16];
int err;
err = smp_h6(w, key_id, res);
if (err) {
return err;
}
if (memcmp(res, exp_res, 16)) {
return -EINVAL;
}
return 0;
}
#endif /* CONFIG_BLUETOOTH_BREDR */
static int smp_self_test(void)
{
int err;
@ -3191,6 +3330,14 @@ static int smp_self_test(void)
return err;
}
#if defined(CONFIG_BLUETOOTH_BREDR)
err = smp_h6_test();
if (err) {
BT_ERR("SMP h6 self test failed");
return err;
}
#endif /* CONFIG_BLUETOOTH_BREDR */
return 0;
}
#else
@ -3388,12 +3535,12 @@ void bt_smp_update_keys(struct bt_conn *conn)
* If link was successfully encrypted cleanup old keys as from now on
* only keys distributed in this pairing or LTK from LE SC will be used.
*/
if (conn->keys) {
bt_keys_clear(conn->keys, BT_KEYS_ALL);
if (conn->le.keys) {
bt_keys_clear(conn->le.keys);
}
conn->keys = bt_keys_get_addr(&conn->le.dst);
if (!conn->keys) {
conn->le.keys = bt_keys_get_addr(&conn->le.dst);
if (!conn->le.keys) {
BT_ERR("Unable to get keys for %s",
bt_addr_le_str(&conn->le.dst));
return;
@ -3401,7 +3548,7 @@ void bt_smp_update_keys(struct bt_conn *conn)
/* mark keys as debug */
if (atomic_test_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY)) {
atomic_set_bit(conn->keys->flags, BT_KEYS_DEBUG);
atomic_set_bit(conn->le.keys->flags, BT_KEYS_DEBUG);
}
/*
@ -3413,16 +3560,16 @@ void bt_smp_update_keys(struct bt_conn *conn)
case PASSKEY_DISPLAY:
case PASSKEY_INPUT:
case PASSKEY_CONFIRM:
atomic_set_bit(conn->keys->flags, BT_KEYS_AUTHENTICATED);
atomic_set_bit(conn->le.keys->flags, BT_KEYS_AUTHENTICATED);
break;
case JUST_WORKS:
default:
/* unauthenticated key, clear it */
atomic_clear_bit(conn->keys->flags, BT_KEYS_AUTHENTICATED);
atomic_clear_bit(conn->le.keys->flags, BT_KEYS_AUTHENTICATED);
break;
}
conn->keys->enc_size = get_encryption_key_size(smp);
conn->le.keys->enc_size = get_encryption_key_size(smp);
/*
* Store LTK if LE SC is used, this is safe since LE SC is mutually
@ -3431,11 +3578,11 @@ void bt_smp_update_keys(struct bt_conn *conn)
*/
if (atomic_test_bit(smp->flags, SMP_FLAG_SC) &&
atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
bt_keys_add_type(conn->keys, BT_KEYS_LTK_P256);
memcpy(conn->keys->ltk.val, smp->tk,
sizeof(conn->keys->ltk.val));
conn->keys->ltk.rand = 0;
conn->keys->ltk.ediv = 0;
bt_keys_add_type(conn->le.keys, BT_KEYS_LTK_P256);
memcpy(conn->le.keys->ltk.val, smp->tk,
sizeof(conn->le.keys->ltk.val));
conn->le.keys->ltk.rand = 0;
conn->le.keys->ltk.ediv = 0;
}
}

View file

@ -107,15 +107,6 @@ static int bt_smp_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
return -ENOMEM;
}
void bt_smp_update_keys(struct bt_conn *conn)
{
}
int bt_smp_create_rpa(const uint8_t irk[16], bt_addr_t *rpa)
{
return -ENOTSUP;
}
int bt_smp_init(void)
{
static struct bt_l2cap_fixed_chan chan = {

View file

@ -55,6 +55,11 @@ static void uuid_to_uuid128(const struct bt_uuid *src, struct bt_uuid_128 *dst)
sys_put_le16(BT_UUID_16(src)->val,
&dst->val[UUID_16_BASE_OFFSET]);
return;
case BT_UUID_TYPE_32:
*dst = uuid128_base;
sys_put_le32(BT_UUID_32(src)->val,
&dst->val[UUID_16_BASE_OFFSET]);
return;
case BT_UUID_TYPE_128:
memcpy(dst, src, sizeof(*dst));
return;
@ -80,6 +85,8 @@ int bt_uuid_cmp(const struct bt_uuid *u1, const struct bt_uuid *u2)
switch (u1->type) {
case BT_UUID_TYPE_16:
return (int)BT_UUID_16(u1)->val - (int)BT_UUID_16(u2)->val;
case BT_UUID_TYPE_32:
return (int)BT_UUID_32(u1)->val - (int)BT_UUID_32(u2)->val;
case BT_UUID_TYPE_128:
return memcmp(BT_UUID_128(u1)->val, BT_UUID_128(u2)->val, 16);
}
@ -97,6 +104,9 @@ void bt_uuid_to_str(const struct bt_uuid *uuid, char *str, size_t len)
case BT_UUID_TYPE_16:
snprintf(str, len, "%.4x", BT_UUID_16(uuid)->val);
break;
case BT_UUID_TYPE_32:
snprintf(str, len, "%.4x", BT_UUID_32(uuid)->val);
break;
case BT_UUID_TYPE_128:
memcpy(&tmp0, &BT_UUID_128(uuid)->val[0], sizeof(tmp0));
memcpy(&tmp1, &BT_UUID_128(uuid)->val[2], sizeof(tmp1));

View file

@ -93,3 +93,8 @@ peripheral_hr:
Similar to 'peripheral', except that this application
specifically exposes the HR (Heart Rate) GATT Service. Once a
device connects it will generate dummy heart-rate values.
peripheral_hids:
Similar to 'peripheral', except that this application
specifically exposes the HID GATT Service. The report map used
is for a generic mouse.

View file

@ -0,0 +1,5 @@
KERNEL_TYPE = nano
CONF_FILE = prj.conf
BOARD ?= quark_se_devboard
include $(ZEPHYR_BASE)/Makefile.inc

View file

@ -0,0 +1,10 @@
CONFIG_STDOUT_CONSOLE=y
CONFIG_GPIO=y
CONFIG_ARC_INIT=n
CONFIG_USB=y
CONFIG_USB_DW=y
CONFIG_USB_DEVICE_STACK=y
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_BLUETOOTH=y
CONFIG_BLUETOOTH_STACK_HCI_RAW=y

View file

@ -0,0 +1,3 @@
ccflags-y += -I${ZEPHYR_BASE}/usb/include -I${ZEPHYR_BASE}/include/drivers/usb -I${ZEPHYR_BASE}/include/usb/
obj-y += btusb.o

View file

@ -0,0 +1,725 @@
/*
* Copyright (c) 2016 Intel Corporation
*
* 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 <stdio.h>
#include <device.h>
#include <uart.h>
#include <misc/sys_log.h>
#include <misc/util.h>
#include <net/buf.h>
#include <bluetooth/buf.h>
#include <bluetooth/hci_raw.h>
#include "usb_device.h"
#include "usb_common.h"
#define BTUSB_BUFFER_SIZE 64
/* Max packet size for endpoints */
#define BTUSB_BULK_EP_MPS 64
#define BTUSB_ISO1_EP_MPS 0
#define BTUSB_ISO2_EP_MPS 9
#define BTUSB_ISO3_EP_MPS 17
#define BTUSB_ISO4_EP_MPS 25
#define BTUSB_ISO5_EP_MPS 33
#define BTUSB_ISO6_EP_MPS 49
/* Max packet size for Interrupt endpoints */
#define BTUSB_INTERRUPT_EP_MPS 16
/* Max Bluetooth command data size */
#define BTUSB_CLASS_MAX_DATA_SIZE 100
#define INTEL_VENDOR_ID 0x8086
#define PRODUCT_ID 0xFF02 /* TODO: Find better ID */
#define DEVICE_RELNUM 0x0100
#define BTUSB_NUM_CONF 1
#define BTUSB_NUM_ITF 2
/* Add also alternat interfaces */
#define BTUSB_NUM_ITF_TOTAL (BTUSB_NUM_ITF + 5)
#define BTUSB_IF1_NUM_EP 3
#define BTUSB_IF2_NUM_EP 2
/* Include all endpoints, also alternate configurations */
#define BTUSB_NUM_EP (BTUSB_IF1_NUM_EP + BTUSB_IF2_NUM_EP * 6)
#define BTUSB_ENDP_INT 0x81
#define BTUSB_ENDP_BULK_OUT 0x02
#define BTUSB_ENDP_BULK_IN 0x82
#define BTUSB_ENDP_ISO_OUT 0x03
#define BTUSB_ENDP_ISO_IN 0x83
#define BTUSB_CONF_SIZE (USB_CONFIGURATION_DESC_SIZE + \
(BTUSB_NUM_ITF_TOTAL * USB_INTERFACE_DESC_SIZE) + \
(BTUSB_NUM_EP * USB_ENDPOINT_DESC_SIZE))
/* Misc. macros */
#define LOW_BYTE(x) ((x) & 0xFF)
#define HIGH_BYTE(x) ((x) >> 8)
static struct device *btusb_dev;
#define DEV_DATA(dev) \
((struct btusb_dev_data_t * const)(dev)->driver_data)
static struct nano_fifo rx_queue;
/* HCI command buffers */
#define CMD_BUF_SIZE (CONFIG_BLUETOOTH_HCI_SEND_RESERVE + \
sizeof(struct bt_hci_cmd_hdr) + \
CONFIG_BLUETOOTH_MAX_CMD_LEN)
static struct nano_fifo avail_tx;
static NET_BUF_POOL(tx_pool, CONFIG_BLUETOOTH_HCI_CMD_COUNT, CMD_BUF_SIZE,
&avail_tx, NULL, sizeof(uint8_t));
#define BT_L2CAP_MTU 64
/** Data size needed for ACL buffers */
#define BT_BUF_ACL_SIZE (CONFIG_BLUETOOTH_HCI_RECV_RESERVE + \
sizeof(struct bt_hci_acl_hdr) + \
4 /* L2CAP header size */ + \
BT_L2CAP_MTU)
static struct nano_fifo avail_acl_tx;
static NET_BUF_POOL(acl_tx_pool, 2, BT_BUF_ACL_SIZE, &avail_acl_tx, NULL,
sizeof(uint8_t));
/* Device data structure */
struct btusb_dev_data_t {
/* USB device status code */
enum usb_dc_status_code usb_status;
uint8_t interface_data[BTUSB_CLASS_MAX_DATA_SIZE];
uint8_t notification_sent;
};
/**
* Bluetooth USB descriptors configuration
*/
static const uint8_t btusb_desc[] = {
/* Device descriptor */
USB_DEVICE_DESC_SIZE, /* Descriptor size */
USB_DEVICE_DESC, /* Descriptor type */
LOW_BYTE(USB_1_1),
HIGH_BYTE(USB_1_1), /* USB version in BCD format */
WIRELESS_DEVICE_CLASS, /* Class */
RF_SUBCLASS, /* SubClass - Interface specific */
BLUETOOTH_PROTOCOL, /* Protocol - Interface specific */
MAX_PACKET_SIZE0, /* Max Packet Size */
LOW_BYTE(INTEL_VENDOR_ID),
HIGH_BYTE(INTEL_VENDOR_ID), /* Vendor Id */
LOW_BYTE(PRODUCT_ID),
HIGH_BYTE(PRODUCT_ID), /* Product Id */
LOW_BYTE(DEVICE_RELNUM),
HIGH_BYTE(DEVICE_RELNUM), /* Device Release Number */
/* Index of Manufacturer String Descriptor */
0x01,
/* Index of Product String Descriptor */
0x02,
/* Index of Serial Number String Descriptor */
0x03,
BTUSB_NUM_CONF, /* Number of Possible Configuration */
/* Configuration descriptor */
USB_CONFIGURATION_DESC_SIZE, /* Descriptor size */
USB_CONFIGURATION_DESC, /* Descriptor type */
/* Total length in bytes of data returned */
LOW_BYTE(BTUSB_CONF_SIZE),
HIGH_BYTE(BTUSB_CONF_SIZE),
BTUSB_NUM_ITF, /* Number of interfaces */
0x01, /* Configuration value */
0x00, /* Index of the Configuration string */
USB_CONFIGURATION_ATTRIBUTES, /* Attributes */
MAX_LOW_POWER, /* Max power consumption */
/* Interface descriptor 0 */
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
USB_INTERFACE_DESC, /* Descriptor type */
0x00, /* Interface index */
0x00, /* Alternate setting */
BTUSB_IF1_NUM_EP, /* Number of Endpoints */
WIRELESS_DEVICE_CLASS, /* Class */
RF_SUBCLASS, /* SubClass */
BLUETOOTH_PROTOCOL, /* Protocol */
/* Index of the Interface String Descriptor */
0x00,
/* Endpoint INT */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_INT, /* Endpoint address */
USB_DC_EP_INTERRUPT, /* Attributes */
LOW_BYTE(BTUSB_INTERRUPT_EP_MPS),
HIGH_BYTE(BTUSB_INTERRUPT_EP_MPS),/* Max packet size */
0x01, /* Interval */
/* Endpoint OUT */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_BULK_OUT, /* Endpoint address */
USB_DC_EP_BULK, /* Attributes */
LOW_BYTE(BTUSB_BULK_EP_MPS),
HIGH_BYTE(BTUSB_BULK_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* Endpoint IN */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_BULK_IN, /* Endpoint address */
USB_DC_EP_BULK, /* Attributes */
LOW_BYTE(BTUSB_BULK_EP_MPS),
HIGH_BYTE(BTUSB_BULK_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* Interface descriptor 1:0 */
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
USB_INTERFACE_DESC, /* Descriptor type */
0x01, /* Interface index */
0x00, /* Alternate setting */
BTUSB_IF2_NUM_EP, /* Number of Endpoints */
WIRELESS_DEVICE_CLASS, /* Class */
RF_SUBCLASS, /* SubClass */
BLUETOOTH_PROTOCOL, /* Protocol */
/* Index of the Interface String Descriptor */
0x00,
/* Endpoint OUT */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_ISO_OUT, /* Endpoint address */
USB_DC_EP_ISOCHRONOUS, /* Attributes */
LOW_BYTE(BTUSB_ISO1_EP_MPS),
HIGH_BYTE(BTUSB_ISO1_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* Endpoint IN */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_ISO_IN, /* Endpoint address */
USB_DC_EP_ISOCHRONOUS, /* Attributes */
LOW_BYTE(BTUSB_ISO1_EP_MPS),
HIGH_BYTE(BTUSB_ISO1_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* Interface descriptor 1:1 */
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
USB_INTERFACE_DESC, /* Descriptor type */
0x01, /* Interface index */
0x01, /* Alternate setting */
BTUSB_IF2_NUM_EP, /* Number of Endpoints */
WIRELESS_DEVICE_CLASS, /* Class */
RF_SUBCLASS, /* SubClass */
BLUETOOTH_PROTOCOL, /* Protocol */
/* Index of the Interface String Descriptor */
0x00,
/* Endpoint OUT */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_ISO_OUT, /* Endpoint address */
USB_DC_EP_ISOCHRONOUS, /* Attributes */
LOW_BYTE(BTUSB_ISO2_EP_MPS),
HIGH_BYTE(BTUSB_ISO2_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* Endpoint IN */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_ISO_IN, /* Endpoint address */
USB_DC_EP_ISOCHRONOUS, /* Attributes */
LOW_BYTE(BTUSB_ISO2_EP_MPS),
HIGH_BYTE(BTUSB_ISO2_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* Interface descriptor 1:2 */
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
USB_INTERFACE_DESC, /* Descriptor type */
0x01, /* Interface index */
0x02, /* Alternate setting */
BTUSB_IF2_NUM_EP, /* Number of Endpoints */
WIRELESS_DEVICE_CLASS, /* Class */
RF_SUBCLASS, /* SubClass */
BLUETOOTH_PROTOCOL, /* Protocol */
/* Index of the Interface String Descriptor */
0x00,
/* Endpoint OUT */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_ISO_OUT, /* Endpoint address */
USB_DC_EP_ISOCHRONOUS, /* Attributes */
LOW_BYTE(BTUSB_ISO3_EP_MPS),
HIGH_BYTE(BTUSB_ISO3_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* Endpoint IN */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_ISO_IN, /* Endpoint address */
USB_DC_EP_ISOCHRONOUS, /* Attributes */
LOW_BYTE(BTUSB_ISO3_EP_MPS),
HIGH_BYTE(BTUSB_ISO3_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* Interface descriptor 1:3 */
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
USB_INTERFACE_DESC, /* Descriptor type */
0x01, /* Interface index */
0x03, /* Alternate setting */
BTUSB_IF2_NUM_EP, /* Number of Endpoints */
WIRELESS_DEVICE_CLASS, /* Class */
RF_SUBCLASS, /* SubClass */
BLUETOOTH_PROTOCOL, /* Protocol */
/* Index of the Interface String Descriptor */
0x00,
/* Endpoint OUT */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_ISO_OUT, /* Endpoint address */
USB_DC_EP_ISOCHRONOUS, /* Attributes */
LOW_BYTE(BTUSB_ISO4_EP_MPS),
HIGH_BYTE(BTUSB_ISO4_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* Endpoint IN */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_ISO_IN, /* Endpoint address */
USB_DC_EP_ISOCHRONOUS, /* Attributes */
LOW_BYTE(BTUSB_ISO4_EP_MPS),
HIGH_BYTE(BTUSB_ISO4_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* Interface descriptor 1:4 */
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
USB_INTERFACE_DESC, /* Descriptor type */
0x01, /* Interface index */
0x04, /* Alternate setting */
BTUSB_IF2_NUM_EP, /* Number of Endpoints */
WIRELESS_DEVICE_CLASS, /* Class */
RF_SUBCLASS, /* SubClass */
BLUETOOTH_PROTOCOL, /* Protocol */
/* Index of the Interface String Descriptor */
0x00,
/* Endpoint OUT */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_ISO_OUT, /* Endpoint address */
USB_DC_EP_ISOCHRONOUS, /* Attributes */
LOW_BYTE(BTUSB_ISO5_EP_MPS),
HIGH_BYTE(BTUSB_ISO5_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* Endpoint IN */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_ISO_IN, /* Endpoint address */
USB_DC_EP_ISOCHRONOUS, /* Attributes */
LOW_BYTE(BTUSB_ISO5_EP_MPS),
HIGH_BYTE(BTUSB_ISO5_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* Interface descriptor 1:5 */
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
USB_INTERFACE_DESC, /* Descriptor type */
0x01, /* Interface index */
0x05, /* Alternate setting */
BTUSB_IF2_NUM_EP, /* Number of Endpoints */
WIRELESS_DEVICE_CLASS, /* Class */
RF_SUBCLASS, /* SubClass */
BLUETOOTH_PROTOCOL, /* Protocol */
/* Index of the Interface String Descriptor */
0x00,
/* Endpoint OUT */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_ISO_OUT, /* Endpoint address */
USB_DC_EP_ISOCHRONOUS, /* Attributes */
LOW_BYTE(BTUSB_ISO6_EP_MPS),
HIGH_BYTE(BTUSB_ISO6_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* Endpoint IN */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
BTUSB_ENDP_ISO_IN, /* Endpoint address */
USB_DC_EP_ISOCHRONOUS, /* Attributes */
LOW_BYTE(BTUSB_ISO6_EP_MPS),
HIGH_BYTE(BTUSB_ISO6_EP_MPS), /* Max packet size */
0x01, /* Interval */
/* String descriptor language, only one, so min size 4 bytes.
* 0x0409 English(US) language code used
*/
USB_STRING_DESC_SIZE, /* Descriptor size */
USB_STRING_DESC, /* Descriptor type */
0x09,
0x04,
/* Manufacturer String Descriptor "Intel" */
0x0C,
USB_STRING_DESC,
'I', 0, 'n', 0, 't', 0, 'e', 0, 'l', 0,
/* Product String Descriptor */
0x10,
USB_STRING_DESC,
'Q', 0, 'U', 0, 'A', 0, 'R', 0, 'Q', 0, 'B', 0, 'T', 0,
/* Serial Number String Descriptor "00.01" */
0x0C,
USB_STRING_DESC,
'0', 0, '0', 0, '.', 0, '0', 0, '1', 0,
};
/* TODO: move to standard utils */
static void hexdump(const char *str, const uint8_t *packet, size_t length)
{
int n = 0;
if (!length) {
printf("%s zero-length signal packet\n", str);
return;
}
while (length--) {
if (n % 16 == 0) {
printf("%s %08X ", str, n);
}
printf("%02X ", *packet++);
n++;
if (n % 8 == 0) {
if (n % 16 == 0) {
printf("\n");
} else {
printf(" ");
}
}
}
if (n % 16) {
printf("\n");
}
}
static void btusb_int_in(uint8_t ep, enum usb_dc_ep_cb_status_code ep_status)
{
SYS_LOG_DBG("ep %x status %d", ep, ep_status);
}
/* EP Bulk OUT handler, used to read the data received from the Host */
static void btusb_bulk_out(uint8_t ep, enum usb_dc_ep_cb_status_code ep_status)
{
struct net_buf *buf;
uint32_t len, read = 0;
uint8_t tmp[4];
SYS_LOG_DBG("ep %x status %d", ep, ep_status);
/* Read number of butes to read */
usb_read(ep, NULL, 0, &len);
if (!len || len > BT_BUF_ACL_SIZE) {
SYS_LOG_ERR("Incorrect length: %u\n", len);
return;
}
buf = net_buf_get(&avail_acl_tx, 0);
if (!buf) {
SYS_LOG_ERR("Cannot get free buffer\n");
return;
}
/**
* Quark SE USB controller is always storing data
* in the FIFOs per 32-bit words.
*/
while (len > buf->len) {
usb_read(ep, tmp, 4, NULL);
read += 4;
if (len > read) {
memcpy(net_buf_add(buf, 4), tmp, 4);
} else {
uint8_t remains = 4 - (read - len);
memcpy(net_buf_add(buf, remains), tmp, remains);
}
}
hexdump(">", buf->data, buf->len);
bt_buf_set_type(buf, BT_BUF_ACL_OUT);
bt_send(buf);
}
/* EP Bulk IN handler, used to send data to the Host */
static void btusb_bulk_in(uint8_t ep, enum usb_dc_ep_cb_status_code ep_status)
{
SYS_LOG_ERR("Not implemented");
}
/* EP ISO OUT handler, used to read the data received from the Host */
static void btusb_iso_out(uint8_t ep, enum usb_dc_ep_cb_status_code ep_status)
{
SYS_LOG_ERR("Not implemented");
}
/* EP ISO IN handler, used to send data to the Host */
static void btusb_iso_in(uint8_t ep, enum usb_dc_ep_cb_status_code ep_status)
{
SYS_LOG_ERR("Not implemented");
}
/* Describe EndPoints configuration */
static struct usb_ep_cfg_data btusb_ep[] = {
{
.ep_cb = btusb_int_in,
.ep_addr = BTUSB_ENDP_INT
},
{
.ep_cb = btusb_bulk_out,
.ep_addr = BTUSB_ENDP_BULK_OUT
},
{
.ep_cb = btusb_bulk_in,
.ep_addr = BTUSB_ENDP_BULK_IN
},
{
.ep_cb = btusb_iso_out,
.ep_addr = BTUSB_ENDP_ISO_OUT
},
{
.ep_cb = btusb_iso_in,
.ep_addr = BTUSB_ENDP_ISO_IN
},
#if 0
/* FIXME: check endpoints */
/* Alt 1 */
{
.ep_cb = btusb_iso_out,
.ep_addr = BTUSB_ENDP_ISO_OUT
},
{
.ep_cb = btusb_iso_in,
.ep_addr = BTUSB_ENDP_ISO_IN
},
/* Alt 2 */
{
.ep_cb = btusb_iso_out,
.ep_addr = BTUSB_ENDP_ISO_OUT
},
{
.ep_cb = btusb_iso_in,
.ep_addr = BTUSB_ENDP_ISO_IN
},
/* Alt 3 */
{
.ep_cb = btusb_iso_out,
.ep_addr = BTUSB_ENDP_ISO_OUT
},
{
.ep_cb = btusb_iso_in,
.ep_addr = BTUSB_ENDP_ISO_IN
},
/* Alt 4 */
{
.ep_cb = btusb_iso_out,
.ep_addr = BTUSB_ENDP_ISO_OUT
},
{
.ep_cb = btusb_iso_in,
.ep_addr = BTUSB_ENDP_ISO_IN
},
/* Alt 5 */
{
.ep_cb = btusb_iso_out,
.ep_addr = BTUSB_ENDP_ISO_OUT
},
{
.ep_cb = btusb_iso_in,
.ep_addr = BTUSB_ENDP_ISO_IN
},
#endif
};
static void btusb_status_cb(enum usb_dc_status_code status)
{
struct btusb_dev_data_t * const dev_data = DEV_DATA(btusb_dev);
/* Store the new status */
dev_data->usb_status = status;
/* Check the USB status and do needed action if required */
switch (status) {
case USB_DC_ERROR:
SYS_LOG_DBG("USB device error");
break;
case USB_DC_RESET:
SYS_LOG_DBG("USB device reset detected");
break;
case USB_DC_CONNECTED:
SYS_LOG_DBG("USB device connected");
break;
case USB_DC_CONFIGURED:
SYS_LOG_DBG("USB device configured");
break;
case USB_DC_DISCONNECTED:
SYS_LOG_DBG("USB device disconnected");
break;
case USB_DC_SUSPEND:
SYS_LOG_DBG("USB device supended");
break;
case USB_DC_RESUME:
SYS_LOG_DBG("USB device resumed");
break;
case USB_DC_UNKNOWN:
default:
SYS_LOG_DBG("USB unknown state");
break;
}
}
static int btusb_class_handler(struct usb_setup_packet *setup,
int32_t *len, uint8_t **data)
{
struct net_buf *buf;
if (!*len || *len > CMD_BUF_SIZE) {
SYS_LOG_ERR("Incorrect length: %u\n", len);
return -EINVAL;
}
hexdump(">", *data, *len);
buf = net_buf_get(&avail_tx, 0);
if (!buf) {
SYS_LOG_ERR("Cannot get free buffer\n");
return -ENOMEM;
}
bt_buf_set_type(buf, BT_BUF_CMD);
memcpy(net_buf_add(buf, *len), *data, *len);
bt_send(buf);
return 0;
}
static struct usb_cfg_data btusb_config = {
.usb_device_description = btusb_desc,
.cb_usb_status = btusb_status_cb,
.interface = {
.class_handler = btusb_class_handler,
.custom_handler = NULL,
},
.num_endpoints = ARRAY_SIZE(btusb_ep),
.endpoint = btusb_ep,
};
static int btusb_init(struct device *dev)
{
struct btusb_dev_data_t * const dev_data = DEV_DATA(dev);
int ret;
btusb_config.interface.payload_data = dev_data->interface_data;
btusb_dev = dev;
/* Initialize the USB driver with the right configuration */
ret = usb_set_config(&btusb_config);
if (ret < 0) {
SYS_LOG_ERR("Failed to config USB");
return ret;
}
/* Enable USB driver */
ret = usb_enable(&btusb_config);
if (ret < 0) {
SYS_LOG_ERR("Failed to enable USB");
return ret;
}
return 0;
}
static struct btusb_dev_data_t btusb_dev_data = {
.usb_status = USB_DC_UNKNOWN,
};
DEVICE_INIT(btusb, "btusb", &btusb_init,
&btusb_dev_data, NULL,
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
static int try_write(uint8_t ep, struct net_buf *buf)
{
while (1) {
int ret = usb_write(ep, buf->data, buf->len, NULL);
switch (ret) {
case -EAGAIN:
break;
/* TODO: Handle other error codes */
default:
return ret;
}
}
}
void main(void)
{
SYS_LOG_DBG("Start");
/* Initialize the buffer pools */
net_buf_pool_init(tx_pool);
net_buf_pool_init(acl_tx_pool);
nano_fifo_init(&rx_queue);
bt_enable_raw(&rx_queue);
while (1) {
struct net_buf *buf;
buf = net_buf_get_timeout(&rx_queue, 0, TICKS_UNLIMITED);
hexdump("<", buf->data, buf->len);
switch (bt_buf_get_type(buf)) {
case BT_BUF_EVT:
try_write(BTUSB_ENDP_INT, buf);
break;
case BT_BUF_ACL_IN:
try_write(BTUSB_ENDP_BULK_IN, buf);
break;
default:
SYS_LOG_ERR("Unknown type %u", bt_buf_get_type(buf));
break;
}
net_buf_unref(buf);
}
}

View file

@ -0,0 +1,5 @@
[test_x86]
tags = usb bluetooth
build_only = true
arch_whitelist = x86
platform_whitelist = quark_se_devboard

View file

@ -24,6 +24,7 @@
#include <misc/util.h>
#include <misc/byteorder.h>
#include <zephyr.h>
#include <misc/nano_work.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
@ -37,6 +38,10 @@
#define EDS_VERSION 0x00
#define EDS_URL_READ_OFFSET 2
#define EDS_URL_WRITE_OFFSET 4
#define EDS_IDLE_TIMEOUT SECONDS(30)
/* Idle timer */
struct nano_delayed_work idle_work;
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
@ -603,13 +608,46 @@ static void bt_ready(int err)
return;
}
printk("Beacon started\n");
nano_delayed_work_submit(&idle_work, EDS_IDLE_TIMEOUT);
printk("Configuration mode: waiting connections...\n");
}
static void idle_timeout(struct nano_work *work)
{
if (eds_slots[eds_active_slot].type == EDS_TYPE_NONE) {
printk("Switching to Beacon mode.\n");
eds_slot_restart(&eds_slots[eds_active_slot], EDS_TYPE_URL);
}
}
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
printk("Connection failed (err %u)\n", err);
} else {
printk("Connected\n");
nano_delayed_work_cancel(&idle_work);
}
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
printk("Disconnected (reason %u)\n", reason);
}
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
};
void main(void)
{
int err;
bt_conn_cb_register(&conn_callbacks);
nano_delayed_work_init(&idle_work, idle_timeout);
/* Initialize the Bluetooth Subsystem */
err = bt_enable(bt_ready);
if (err) {

View file

@ -0,0 +1,178 @@
/** @file
* @brief HoG Service sample
*/
/*
* Copyright (c) 2016 Intel Corporation
*
* 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 <stdint.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <misc/printk.h>
#include <misc/byteorder.h>
#include <zephyr.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/conn.h>
#include <bluetooth/uuid.h>
#include <bluetooth/gatt.h>
enum {
HIDS_REMOTE_WAKE = BIT(0),
HIDS_NORMALLY_CONNECTABLE = BIT(1),
};
struct hids_info {
uint16_t version; /* version number of base USB HID Specification */
uint8_t code; /* country HID Device hardware is localized for. */
uint8_t flags;
} __packed;
struct hids_report {
uint8_t id; /* report id */
uint8_t type; /* report type */
} __packed;
static struct hids_info info = {
.version = 0x0000,
.code = 0x00,
.flags = HIDS_NORMALLY_CONNECTABLE,
};
enum {
HIDS_INPUT = 0x01,
HIDS_OUTPUT = 0x02,
HIDS_FEATURE = 0x03,
};
static struct hids_report input = {
.id = 0x01,
.type = HIDS_INPUT,
};
static struct bt_gatt_ccc_cfg input_ccc_cfg[CONFIG_BLUETOOTH_MAX_PAIRED] = {};
static uint8_t simulate_input;
static uint8_t ctrl_point;
static uint8_t report_map[] = {
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x02, /* Usage (Mouse) */
0xA1, 0x01, /* Collection (Application) */
0x09, 0x01, /* Usage (Pointer) */
0xA1, 0x00, /* Collection (Physical) */
0x05, 0x09, /* Usage Page (Button) */
0x19, 0x01, /* Usage Minimum (0x01) */
0x29, 0x03, /* Usage Maximum (0x03) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x95, 0x03, /* Report Count (3) */
0x75, 0x01, /* Report Size (1) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,...) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x05, /* Report Size (5) */
0x81, 0x03, /* Input (Const,Var,Abs,No Wrap,Linear,...) */
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x15, 0x81, /* Logical Minimum (129) */
0x25, 0x7F, /* Logical Maximum (127) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x02, /* Report Count (2) */
0x81, 0x06, /* Input (Data,Var,Rel,No Wrap,Linear,...) */
0xC0, /* End Collection */
0xC0, /* End Collection */
};
static ssize_t read_info(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data,
sizeof(struct hids_info));
}
static ssize_t read_report_map(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
return bt_gatt_attr_read(conn, attr, buf, len, offset, report_map,
sizeof(report_map));
}
static ssize_t read_report(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data,
sizeof(struct hids_report));
}
static void input_ccc_changed(uint16_t value)
{
simulate_input = (value == BT_GATT_CCC_NOTIFY) ? 1 : 0;
}
static ssize_t read_input_report(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
return bt_gatt_attr_read(conn, attr, buf, len, offset, NULL, 0);
}
static ssize_t write_ctrl_point(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *buf, uint16_t len, uint16_t offset,
uint8_t flags)
{
uint8_t *value = attr->user_data;
if (offset + len > sizeof(ctrl_point)) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
memcpy(value + offset, buf, len);
return len;
}
/* HID Service Declaration */
static struct bt_gatt_attr attrs[] = {
BT_GATT_PRIMARY_SERVICE(BT_UUID_HIDS),
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_INFO, BT_GATT_CHRC_READ),
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_INFO, BT_GATT_PERM_READ,
read_info, NULL, &info),
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT_MAP, BT_GATT_CHRC_READ),
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_MAP, BT_GATT_PERM_READ,
read_report_map, NULL, NULL),
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT, BT_GATT_PERM_READ_AUTHEN,
read_input_report, NULL, NULL),
BT_GATT_CCC(input_ccc_cfg, input_ccc_changed),
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ,
read_report, NULL, &input),
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT,
BT_GATT_CHRC_WRITE_WITHOUT_RESP),
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_CTRL_POINT, BT_GATT_PERM_WRITE,
NULL, write_ctrl_point, &ctrl_point),
};
void hog_init(void)
{
bt_gatt_register(attrs, ARRAY_SIZE(attrs));
}

View file

@ -0,0 +1,21 @@
/** @file
* @brief HoG Service sample
*/
/*
* Copyright (c) 2016 Intel Corporation
*
* 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.
*/
void hog_init(void);

View file

@ -0,0 +1,7 @@
BOARD ?= qemu_x86
MDEF_FILE = prj.mdef
KERNEL_TYPE = micro
CONF_FILE = prj.conf
QEMU_EXTRA_FLAGS = -serial unix:/tmp/bt-server-bredr
include $(ZEPHYR_BASE)/Makefile.inc

View file

@ -0,0 +1,7 @@
CONFIG_BLUETOOTH=y
CONFIG_BLUETOOTH_LE=y
CONFIG_BLUETOOTH_DEBUG_LOG=y
CONFIG_BLUETOOTH_SMP=y
CONFIG_BLUETOOTH_PERIPHERAL=y
CONFIG_BLUETOOTH_GATT_DYNAMIC_DB=y
CONFIG_ARC_INIT=n

View file

@ -0,0 +1,5 @@
% Application : Bluetooth beacon sample
% TASK NAME PRIO ENTRY STACK GROUPS
% ===================================================
TASK MAIN 7 main 2048 [EXE]

View file

@ -0,0 +1,5 @@
CONFIG_BLUETOOTH=y
CONFIG_BLUETOOTH_STACK_NBLE=y
CONFIG_NBLE=y
CONFIG_ARC_INIT=n
CONFIG_BLUETOOTH_DEBUG_LOG=y

View file

@ -0,0 +1,4 @@
ccflags-y +=-I${ZEPHYR_BASE}/samples/bluetooth
obj-y = main.o ../../gatt/gap.o ../../gatt/dis.o ../../gatt/bas.o \
../../gatt/hog.o

View file

@ -0,0 +1,155 @@
/* main.c - Application main entry point */
/*
* Copyright (c) 2016 Intel Corporation
*
* 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 <stdint.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <misc/printk.h>
#include <misc/byteorder.h>
#include <zephyr.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/conn.h>
#include <bluetooth/uuid.h>
#include <bluetooth/gatt.h>
#include <gatt/gap.h>
#include <gatt/dis.h>
#include <gatt/bas.h>
#include <gatt/hog.h>
#define DEVICE_NAME "Test HoG mouse"
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
#define HOG_APPEARANCE 0x03c2
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID16_ALL,
0x12, 0x18, /* HID Service */
0x0f, 0x18), /* Battery Service */
};
static const struct bt_data sd[] = {
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};
static void connected(struct bt_conn *conn, uint8_t err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (err) {
printk("Failed to connect to %s (%u)\n", addr, err);
return;
}
printk("Connected %s\n", addr);
if (bt_conn_security(conn, BT_SECURITY_MEDIUM)) {
printk("Failed to set security\n");
}
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Disconnected from %s (reason %u)\n", addr, reason);
}
static void security_changed(struct bt_conn *conn, bt_security_t level)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Security changed: %s level %u\n", addr, level);
}
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
.security_changed = security_changed,
};
static void bt_ready(int err)
{
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
gap_init(DEVICE_NAME, HOG_APPEARANCE);
bas_init();
dis_init(CONFIG_SOC, "Manufacturer");
hog_init();
err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad),
sd, ARRAY_SIZE(sd));
if (err) {
printk("Advertising failed to start (err %d)\n", err);
return;
}
printk("Advertising successfully started\n");
}
static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Passkey for %s: %u\n", addr, passkey);
}
static void auth_cancel(struct bt_conn *conn)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Pairing cancelled: %s\n", addr);
}
static struct bt_conn_auth_cb auth_cb_display = {
.passkey_display = auth_passkey_display,
.passkey_entry = NULL,
.cancel = auth_cancel,
};
void main(void)
{
int err;
err = bt_enable(bt_ready);
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
bt_conn_cb_register(&conn_callbacks);
bt_conn_auth_cb_register(&auth_cb_display);
}

View file

@ -0,0 +1,19 @@
[test_x86]
tags = bluetooth
build_only = true
arch_whitelist = x86
# FIXME Doesn't work for ia32_pci
filter = CONFIG_SOC == "ia32"
[test_arm]
tags = bluetooth
build_only = true
arch_whitelist = arm
platform_exclude = arduino_due
[test_nble]
tags = bluetooth
build_only = true
extra_args = CONF_FILE="prj_nble.conf"
arch_whitelist = x86
platform_whitelist = arduino_101

View file

@ -0,0 +1,13 @@
CONFIG_BLUETOOTH=y
CONFIG_BLUETOOTH_CONTROLLER=y
CONFIG_BLUETOOTH_LE=y
CONFIG_BLUETOOTH_PERIPHERAL=y
CONFIG_BLUETOOTH_CENTRAL=y
CONFIG_BLUETOOTH_SMP=y
CONFIG_BLUETOOTH_SIGNING=y
CONFIG_BLUETOOTH_SMP_SC_ONLY=y
CONFIG_BLUETOOTH_TINYCRYPT_ECC=y
CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL=y
CONFIG_BLUETOOTH_GATT_DYNAMIC_DB=y
CONFIG_BLUETOOTH_GATT_CLIENT=y
CONFIG_BLUETOOTH_BREDR=n

View file

@ -0,0 +1,25 @@
CONFIG_BLUETOOTH=y
CONFIG_BLUETOOTH_CONTROLLER=y
CONFIG_BLUETOOTH_LE=y
CONFIG_BLUETOOTH_PERIPHERAL=y
CONFIG_BLUETOOTH_CENTRAL=y
CONFIG_BLUETOOTH_SMP=y
CONFIG_BLUETOOTH_SIGNING=y
CONFIG_BLUETOOTH_SMP_SC_ONLY=y
CONFIG_BLUETOOTH_TINYCRYPT_ECC=y
CONFIG_BLUETOOTH_USE_DEBUG_KEYS=y
CONFIG_BLUETOOTH_L2CAP_DYNAMIC_CHANNEL=y
CONFIG_BLUETOOTH_GATT_DYNAMIC_DB=y
CONFIG_BLUETOOTH_GATT_CLIENT=y
CONFIG_BLUETOOTH_DEBUG_MONITOR=y
CONFIG_BLUETOOTH_DEBUG_HCI_CORE=y
CONFIG_BLUETOOTH_DEBUG_CONN=y
CONFIG_BLUETOOTH_DEBUG_KEYS=y
CONFIG_BLUETOOTH_DEBUG_L2CAP=y
CONFIG_BLUETOOTH_DEBUG_SMP=y
CONFIG_BLUETOOTH_DEBUG_DRIVER=y
CONFIG_BLUETOOTH_SMP_SELFTEST=y
CONFIG_BLUETOOTH_DEBUG_ATT=y
CONFIG_BLUETOOTH_DEBUG_GATT=y
CONFIG_BLUETOOTH_BREDR=n
CONFIG_DEBUG=y

Some files were not shown because too many files have changed in this diff Show more