Merge "Merge bluetooth branch to master"
This commit is contained in:
commit
ce52e3988f
113 changed files with 19980 additions and 861 deletions
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -20,3 +20,7 @@ CONFIG_UART_CONSOLE=y
|
|||
|
||||
# additional board options
|
||||
CONFIG_GPIO_AS_PINRESET=y
|
||||
|
||||
# bluetooth
|
||||
CONFIG_BLUETOOTH=y
|
||||
CONFIG_BLUETOOTH_CONTROLLER=y
|
||||
|
|
|
@ -18,3 +18,7 @@ CONFIG_UART_CONSOLE=y
|
|||
|
||||
# additional board options
|
||||
CONFIG_GPIO_AS_PINRESET=y
|
||||
|
||||
# bluetooth
|
||||
CONFIG_BLUETOOTH=y
|
||||
CONFIG_BLUETOOTH_CONTROLLER=y
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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/
|
||||
|
|
39
drivers/bluetooth/controller/Kconfig
Normal file
39
drivers/bluetooth/controller/Kconfig
Normal 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
|
18
drivers/bluetooth/controller/Makefile
Normal file
18
drivers/bluetooth/controller/Makefile
Normal 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
|
31
drivers/bluetooth/controller/hal/ccm.h
Normal file
31
drivers/bluetooth/controller/hal/ccm.h
Normal 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_ */
|
230
drivers/bluetooth/controller/hal/clock.c
Normal file
230
drivers/bluetooth/controller/hal/clock.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
26
drivers/bluetooth/controller/hal/clock.h
Normal file
26
drivers/bluetooth/controller/hal/clock.h
Normal 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
|
30
drivers/bluetooth/controller/hal/cpu.h
Normal file
30
drivers/bluetooth/controller/hal/cpu.h
Normal 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_ */
|
233
drivers/bluetooth/controller/hal/debug.h
Normal file
233
drivers/bluetooth/controller/hal/debug.h
Normal 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_ */
|
188
drivers/bluetooth/controller/hal/ecb.c
Normal file
188
drivers/bluetooth/controller/hal/ecb.c
Normal 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;
|
||||
}
|
45
drivers/bluetooth/controller/hal/ecb.h
Normal file
45
drivers/bluetooth/controller/hal/ecb.h
Normal 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_ */
|
27
drivers/bluetooth/controller/hal/hal_irq.h
Normal file
27
drivers/bluetooth/controller/hal/hal_irq.h
Normal 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_ */
|
27
drivers/bluetooth/controller/hal/hal_rtc.h
Normal file
27
drivers/bluetooth/controller/hal/hal_rtc.h
Normal 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_ */
|
33
drivers/bluetooth/controller/hal/hal_work.h
Normal file
33
drivers/bluetooth/controller/hal/hal_work.h
Normal 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_ */
|
53
drivers/bluetooth/controller/hal/irq.c
Normal file
53
drivers/bluetooth/controller/hal/irq.c
Normal 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);
|
||||
}
|
552
drivers/bluetooth/controller/hal/radio.c
Normal file
552
drivers/bluetooth/controller/hal/radio.c
Normal 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));
|
||||
}
|
108
drivers/bluetooth/controller/hal/radio.h
Normal file
108
drivers/bluetooth/controller/hal/radio.h
Normal 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
|
125
drivers/bluetooth/controller/hal/rand.c
Normal file
125
drivers/bluetooth/controller/hal/rand.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
25
drivers/bluetooth/controller/hal/rand.h
Normal file
25
drivers/bluetooth/controller/hal/rand.h
Normal 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_ */
|
68
drivers/bluetooth/controller/hal/rtc.c
Normal file
68
drivers/bluetooth/controller/hal/rtc.c
Normal 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;
|
||||
}
|
1979
drivers/bluetooth/controller/hci/hci.c
Normal file
1979
drivers/bluetooth/controller/hci/hci.c
Normal file
File diff suppressed because it is too large
Load diff
26
drivers/bluetooth/controller/hci/hci.h
Normal file
26
drivers/bluetooth/controller/hci/hci.h
Normal 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_ */
|
7407
drivers/bluetooth/controller/ll/ctrl.c
Normal file
7407
drivers/bluetooth/controller/ll/ctrl.c
Normal file
File diff suppressed because it is too large
Load diff
255
drivers/bluetooth/controller/ll/ctrl.h
Normal file
255
drivers/bluetooth/controller/ll/ctrl.h
Normal 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
|
239
drivers/bluetooth/controller/ll/ctrl_internal.h
Normal file
239
drivers/bluetooth/controller/ll/ctrl_internal.h
Normal 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)
|
||||
|
295
drivers/bluetooth/controller/ll/ll.c
Normal file
295
drivers/bluetooth/controller/ll/ll.c
Normal 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);
|
||||
}
|
39
drivers/bluetooth/controller/ll/ll.h
Normal file
39
drivers/bluetooth/controller/ll/ll.h
Normal 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_ */
|
260
drivers/bluetooth/controller/ll/pdu.h
Normal file
260
drivers/bluetooth/controller/ll/pdu.h
Normal 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_ */
|
1538
drivers/bluetooth/controller/ll/ticker.c
Normal file
1538
drivers/bluetooth/controller/ll/ticker.c
Normal file
File diff suppressed because it is too large
Load diff
138
drivers/bluetooth/controller/ll/ticker.h
Normal file
138
drivers/bluetooth/controller/ll/ticker.h
Normal 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
|
400
drivers/bluetooth/controller/main.c
Normal file
400
drivers/bluetooth/controller/main.c
Normal 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);
|
34
drivers/bluetooth/controller/util/defines.h
Normal file
34
drivers/bluetooth/controller/util/defines.h
Normal 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_ */
|
196
drivers/bluetooth/controller/util/mem.c
Normal file
196
drivers/bluetooth/controller/util/mem.c
Normal 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;
|
||||
}
|
35
drivers/bluetooth/controller/util/mem.h
Normal file
35
drivers/bluetooth/controller/util/mem.h
Normal 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_ */
|
96
drivers/bluetooth/controller/util/memq.c
Normal file
96
drivers/bluetooth/controller/util/memq.c
Normal 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;
|
||||
}
|
25
drivers/bluetooth/controller/util/memq.h
Normal file
25
drivers/bluetooth/controller/util/memq.h
Normal 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
|
134
drivers/bluetooth/controller/util/util.c
Normal file
134
drivers/bluetooth/controller/util/util.c
Normal 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;
|
||||
}
|
26
drivers/bluetooth/controller/util/util.h
Normal file
26
drivers/bluetooth/controller/util/util.h
Normal 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
|
168
drivers/bluetooth/controller/util/work.c
Normal file
168
drivers/bluetooth/controller/util/work.c
Normal 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();
|
||||
}
|
||||
}
|
38
drivers/bluetooth/controller/util/work.h
Normal file
38
drivers/bluetooth/controller/util/work.h
Normal 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_ */
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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("");
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
64
include/bluetooth/hci_raw.h
Normal file
64
include/bluetooth/hci_raw.h
Normal 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 */
|
|
@ -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
162
include/bluetooth/rfcomm.h
Normal 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 */
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 uint8_t att_handle_rsp(struct bt_att *att, void *pdu, uint16_t len,
|
||||
uint8_t err)
|
||||
static struct net_buf *att_req_clone(struct net_buf *buf)
|
||||
{
|
||||
struct bt_att_req req;
|
||||
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));
|
||||
|
||||
if (!att->req.func) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Release cloned buffer */
|
||||
if (att->req.buf) {
|
||||
net_buf_unref(att->req.buf);
|
||||
att->req.buf = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
/* Reset request before callback so another request can be queued */
|
||||
memcpy(&req, &att->req, sizeof(req));
|
||||
att->req.func = NULL;
|
||||
sys_slist_remove(&att->reqs, NULL, node);
|
||||
att_send_req(att, ATT_REQ(node));
|
||||
}
|
||||
|
||||
req.func(att->chan.chan.conn, err, pdu, len, req.user_data);
|
||||
static uint8_t att_handle_rsp(struct bt_att *att, void *pdu, uint16_t len,
|
||||
uint8_t err)
|
||||
{
|
||||
bt_att_func_t func;
|
||||
|
||||
att_req_destroy(&req);
|
||||
if (!att->req) {
|
||||
goto process;
|
||||
}
|
||||
|
||||
/* 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 func so it can be reused by the callback */
|
||||
func = att->req->func;
|
||||
att->req->func = NULL;
|
||||
|
||||
func(att->chan.chan.conn, err, pdu, len, att->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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
142
net/bluetooth/hci_raw.c
Normal 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;
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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
85
net/bluetooth/keys_br.c
Normal 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));
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
728
net/bluetooth/rfcomm.c
Normal 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);
|
||||
}
|
125
net/bluetooth/rfcomm_internal.h
Normal file
125
net/bluetooth/rfcomm_internal.h
Normal 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);
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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.
|
||||
|
|
5
samples/bluetooth/btusb/Makefile
Normal file
5
samples/bluetooth/btusb/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
KERNEL_TYPE = nano
|
||||
CONF_FILE = prj.conf
|
||||
BOARD ?= quark_se_devboard
|
||||
|
||||
include $(ZEPHYR_BASE)/Makefile.inc
|
10
samples/bluetooth/btusb/prj.conf
Normal file
10
samples/bluetooth/btusb/prj.conf
Normal 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
|
3
samples/bluetooth/btusb/src/Makefile
Normal file
3
samples/bluetooth/btusb/src/Makefile
Normal 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
|
725
samples/bluetooth/btusb/src/btusb.c
Normal file
725
samples/bluetooth/btusb/src/btusb.c
Normal 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);
|
||||
}
|
||||
}
|
5
samples/bluetooth/btusb/testcase.ini
Normal file
5
samples/bluetooth/btusb/testcase.ini
Normal file
|
@ -0,0 +1,5 @@
|
|||
[test_x86]
|
||||
tags = usb bluetooth
|
||||
build_only = true
|
||||
arch_whitelist = x86
|
||||
platform_whitelist = quark_se_devboard
|
|
@ -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) {
|
||||
|
|
178
samples/bluetooth/gatt/hog.c
Normal file
178
samples/bluetooth/gatt/hog.c
Normal 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));
|
||||
}
|
21
samples/bluetooth/gatt/hog.h
Normal file
21
samples/bluetooth/gatt/hog.h
Normal 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);
|
7
samples/bluetooth/peripheral_hids/Makefile
Normal file
7
samples/bluetooth/peripheral_hids/Makefile
Normal 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
|
7
samples/bluetooth/peripheral_hids/prj.conf
Normal file
7
samples/bluetooth/peripheral_hids/prj.conf
Normal 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
|
5
samples/bluetooth/peripheral_hids/prj.mdef
Normal file
5
samples/bluetooth/peripheral_hids/prj.mdef
Normal file
|
@ -0,0 +1,5 @@
|
|||
% Application : Bluetooth beacon sample
|
||||
|
||||
% TASK NAME PRIO ENTRY STACK GROUPS
|
||||
% ===================================================
|
||||
TASK MAIN 7 main 2048 [EXE]
|
5
samples/bluetooth/peripheral_hids/prj_nble.conf
Normal file
5
samples/bluetooth/peripheral_hids/prj_nble.conf
Normal 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
|
4
samples/bluetooth/peripheral_hids/src/Makefile
Normal file
4
samples/bluetooth/peripheral_hids/src/Makefile
Normal 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
|
155
samples/bluetooth/peripheral_hids/src/main.c
Normal file
155
samples/bluetooth/peripheral_hids/src/main.c
Normal 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);
|
||||
}
|
19
samples/bluetooth/peripheral_hids/testcase.ini
Normal file
19
samples/bluetooth/peripheral_hids/testcase.ini
Normal 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
|
13
tests/bluetooth/init/prj_controller.conf
Normal file
13
tests/bluetooth/init/prj_controller.conf
Normal 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
|
25
tests/bluetooth/init/prj_controller_dbg.conf
Normal file
25
tests/bluetooth/init/prj_controller_dbg.conf
Normal 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
Loading…
Add table
Add a link
Reference in a new issue