2015-05-05 00:21:33 +02:00
|
|
|
|
In this section, we highlight the BTstack components that need to be
|
|
|
|
|
adjusted for different hardware platforms.
|
|
|
|
|
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
## Time Abstraction Layer {#sec:timeAbstractionPorting}
|
2015-05-08 15:09:18 +02:00
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
BTstack requires a way to learn about passing time.
|
2016-01-20 15:38:37 +01:00
|
|
|
|
*btstack_run_loop_embedded.c* supports two different modes: system ticks or a
|
2015-05-05 00:21:33 +02:00
|
|
|
|
system clock with millisecond resolution. BTstack’s timing requirements
|
|
|
|
|
are quite low as only Bluetooth timeouts in the second range need to be
|
|
|
|
|
handled.
|
|
|
|
|
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
### Tick Hardware Abstraction {#sec:tickAbstractionPorting}
|
2015-05-08 15:09:18 +02:00
|
|
|
|
|
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
If your platform doesn’t require a system clock or if you already have a
|
|
|
|
|
system tick (as it is the default with CMSIS on ARM Cortex devices), you
|
|
|
|
|
can use that to implement BTstack’s time abstraction in
|
|
|
|
|
*include/btstack/hal_tick.h\>*.
|
|
|
|
|
|
2016-01-21 15:41:16 +01:00
|
|
|
|
For this, you need to define *HAVE_TICK* in *btstack_config.h*:
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
#define HAVE_TICK
|
|
|
|
|
|
|
|
|
|
Then, you need to implement the functions *hal_tick_init* and
|
|
|
|
|
*hal_tick_set_handler*, which will be called during the
|
|
|
|
|
initialization of the run loop.
|
|
|
|
|
|
|
|
|
|
void hal_tick_init(void);
|
|
|
|
|
void hal_tick_set_handler(void (*tick_handler)(void));
|
|
|
|
|
int hal_tick_get_tick_period_in_ms(void);
|
|
|
|
|
|
|
|
|
|
After BTstack calls *hal_tick_init()* and
|
|
|
|
|
*hal_tick_set_handler(tick_handler)*, it expects that the
|
|
|
|
|
*tick_handler* gets called every
|
|
|
|
|
*hal_tick_get_tick_period_in_ms()* ms.
|
|
|
|
|
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
### Time MS Hardware Abstraction {#sec:timeMSAbstractionPorting}
|
2015-05-08 15:09:18 +02:00
|
|
|
|
|
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
If your platform already has a system clock or it is more convenient to
|
|
|
|
|
provide such a clock, you can use the Time MS Hardware Abstraction in
|
|
|
|
|
*include/btstack/hal_time_ms.h*.
|
|
|
|
|
|
2016-01-21 15:41:16 +01:00
|
|
|
|
For this, you need to define *HAVE_TIME_MS* in *btstack_config.h*:
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
#define HAVE_TIME_MS
|
|
|
|
|
|
|
|
|
|
Then, you need to implement the function *hal_time_ms()*, which will
|
|
|
|
|
be called from BTstack’s run loop and when setting a timer for the
|
|
|
|
|
future. It has to return the time in milliseconds.
|
|
|
|
|
|
|
|
|
|
uint32_t hal_time_ms(void);
|
|
|
|
|
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
## Bluetooth Hardware Control API {#sec:btHWControlPorting}
|
2015-05-08 15:09:18 +02:00
|
|
|
|
|
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
The Bluetooth hardware control API can provide the HCI layer with a
|
|
|
|
|
custom initialization script, a vendor-specific baud rate change
|
|
|
|
|
command, and system power notifications. It is also used to control the
|
|
|
|
|
power mode of the Bluetooth module, i.e., turning it on/off and putting
|
|
|
|
|
to sleep. In addition, it provides an error handler *hw_error* that is
|
|
|
|
|
called when a Hardware Error is reported by the Bluetooth module. The
|
|
|
|
|
callback allows for persistent logging or signaling of this failure.
|
|
|
|
|
|
2016-01-22 17:16:06 +01:00
|
|
|
|
Overall, the struct *btstack_control_t* encapsulates common functionality
|
2015-05-05 00:21:33 +02:00
|
|
|
|
that is not covered by the Bluetooth specification. As an example, the
|
2016-01-22 17:16:06 +01:00
|
|
|
|
*btstack_chipset_cc256x_in-stance* function returns a pointer to a control
|
2015-05-05 00:21:33 +02:00
|
|
|
|
struct suitable for the CC256x chipset.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
## HCI Transport Implementation {#sec:hciTransportPorting}
|
2015-05-08 15:09:18 +02:00
|
|
|
|
|
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
On embedded systems, a Bluetooth module can be connected via USB or an
|
|
|
|
|
UART port. BTstack implements two UART based protocols for carrying HCI
|
|
|
|
|
commands, events and data between a host and a Bluetooth module: HCI
|
|
|
|
|
UART Transport Layer (H4) and H4 with eHCILL support, a lightweight
|
|
|
|
|
low-power variant by Texas Instruments.
|
|
|
|
|
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
### HCI UART Transport Layer (H4) {#sec:hciUARTPorting}
|
2015-05-08 15:09:18 +02:00
|
|
|
|
|
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
Most embedded UART interfaces operate on the byte level and generate a
|
|
|
|
|
processor interrupt when a byte was received. In the interrupt handler,
|
|
|
|
|
common UART drivers then place the received data in a ring buffer and
|
|
|
|
|
set a flag for further processing or notify the higher-level code, i.e.,
|
|
|
|
|
in our case the Bluetooth stack.
|
|
|
|
|
|
|
|
|
|
Bluetooth communication is packet-based and a single packet may contain
|
|
|
|
|
up to 1021 bytes. Calling a data received handler of the Bluetooth stack
|
|
|
|
|
for every byte creates an unnecessary overhead. To avoid that, a
|
|
|
|
|
Bluetooth packet can be read as multiple blocks where the amount of
|
|
|
|
|
bytes to read is known in advance. Even better would be the use of
|
|
|
|
|
on-chip DMA modules for these block reads, if available.
|
|
|
|
|
|
|
|
|
|
The BTstack UART Hardware Abstraction Layer API reflects this design
|
|
|
|
|
approach and the underlying UART driver has to implement the following
|
|
|
|
|
API:
|
|
|
|
|
|
|
|
|
|
void hal_uart_dma_init(void);
|
|
|
|
|
void hal_uart_dma_set_block_received(void (*block_handler)(void));
|
|
|
|
|
void hal_uart_dma_set_block_sent(void (*block_handler)(void));
|
|
|
|
|
int hal_uart_dma_set_baud(uint32_t baud);
|
|
|
|
|
void hal_uart_dma_send_block(const uint8_t *buffer, uint16_t len);
|
|
|
|
|
void hal_uart_dma_receive_block(uint8_t *buffer, uint16_t len);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The main HCI H4 implementations for embedded system is
|
|
|
|
|
*hci_h4_transport-_dma* function. This function calls the following
|
|
|
|
|
sequence: *hal_uart_dma_init*, *hal_uart_dma_set_block_received*
|
|
|
|
|
and *hal_uart_dma_set_block_sent* functions. this sequence, the HCI
|
|
|
|
|
layer will start packet processing by calling
|
|
|
|
|
*hal_uart-_dma_receive_block* function. The HAL implementation is
|
|
|
|
|
responsible for reading the requested amount of bytes, stopping incoming
|
|
|
|
|
data via the RTS line when the requested amount of data was received and
|
|
|
|
|
has to call the handler. By this, the HAL implementation can stay
|
|
|
|
|
generic, while requiring only three callbacks per HCI packet.
|
|
|
|
|
|
|
|
|
|
### H4 with eHCILL support
|
|
|
|
|
|
|
|
|
|
With the standard H4 protocol interface, it is not possible for either
|
|
|
|
|
the host nor the baseband controller to enter a sleep mode. Besides the
|
|
|
|
|
official H5 protocol, various chip vendors came up with proprietary
|
|
|
|
|
solutions to this. The eHCILL support by Texas Instruments allows both
|
|
|
|
|
the host and the baseband controller to independently enter sleep mode
|
|
|
|
|
without loosing their synchronization with the HCI H4 Transport Layer.
|
|
|
|
|
In addition to the IRQ-driven block-wise RX and TX, eHCILL requires a
|
|
|
|
|
callback for CTS interrupts.
|
|
|
|
|
|
|
|
|
|
void hal_uart_dma_set_cts_irq_handler(void(*cts_irq_handler)(void));
|
|
|
|
|
void hal_uart_dma_set_sleep(uint8_t sleep);
|
|
|
|
|
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
## Persistent Storage API {#sec:persistentStoragePorting}
|
2015-05-08 15:09:18 +02:00
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
On embedded systems there is no generic way to persist data like link
|
|
|
|
|
keys or remote device names, as every type of a device has its own
|
|
|
|
|
capabilities, particularities and limitations. The persistent storage
|
|
|
|
|
API provides an interface to implement concrete drivers for a particular
|
|
|
|
|
system. As an example and for testing purposes, BTstack provides the
|
|
|
|
|
memory-only implementation *remote_device_db_memory*. An
|
|
|
|
|
implementation has to conform to the interface in Listing [below](#lst:persistentDB).
|
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~ {#lst:persistentDB .c caption="{Persistent storage interface.}"}
|
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
typedef struct {
|
|
|
|
|
// management
|
|
|
|
|
void (*open)();
|
|
|
|
|
void (*close)();
|
|
|
|
|
|
|
|
|
|
// link key
|
|
|
|
|
int (*get_link_key)(bd_addr_t bd_addr, link_key_t link_key);
|
|
|
|
|
void (*put_link_key)(bd_addr_t bd_addr, link_key_t key);
|
|
|
|
|
void (*delete_link_key)(bd_addr_t bd_addr);
|
|
|
|
|
|
|
|
|
|
// remote name
|
|
|
|
|
int (*get_name)(bd_addr_t bd_addr, device_name_t *device_name);
|
|
|
|
|
void(*put_name)(bd_addr_t bd_addr, device_name_t *device_name);
|
|
|
|
|
void(*delete_name)(bd_addr_t bd_addr);
|
|
|
|
|
} remote_device_db_t;
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~
|