2015-05-20 22:03:17 +02:00
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
BTstack is a modular dual-mode Bluetooth stack, supporting both
|
|
|
|
|
Bluetooth Basic Rate/Enhanced Date Rate (BR/EDR) as well as Bluetooth
|
|
|
|
|
Low Energy (LE). The BR/EDR technology, also known as Classic Bluetooth,
|
|
|
|
|
provides a robust wireless connection between devices designed for high
|
|
|
|
|
data rates. In contrast, the LE technology has a lower throughput but
|
|
|
|
|
also lower energy consumption, faster connection setup, and the ability
|
|
|
|
|
to connect to more devices in parallel.
|
|
|
|
|
|
|
|
|
|
Whether Classic or LE, a Bluetooth device implements one or more
|
|
|
|
|
Bluetooth profiles. A Bluetooth profile specifies how one or more
|
|
|
|
|
Bluetooth protocols are used to achieve its goals. For example, every
|
|
|
|
|
Bluetooth device must implement the Generic Access Profile (GAP), which
|
|
|
|
|
defines how devices find each other and how they establish a connection.
|
|
|
|
|
This profile mainly make use of the Host Controller Interface (HCI)
|
|
|
|
|
protocol, the lowest protocol in the stack hierarchy which implements a
|
|
|
|
|
command interface to the Bluetooth chipset.
|
|
|
|
|
|
|
|
|
|
In addition to GAP, a popular Classic Bluetooth example would be a
|
|
|
|
|
peripheral devices that can be connected via the Serial Port Profile
|
|
|
|
|
(SPP). SPP basically specifies that a compatible device should provide a
|
|
|
|
|
Service Discovery Protocol (SDP) record containing an RFCOMM channel
|
|
|
|
|
number, which will be used for the actual communication.
|
|
|
|
|
|
|
|
|
|
Similarly, for every LE device, the Generic Attribute Profile (GATT)
|
|
|
|
|
profile must be implemented in addition to GAP. GATT is built on top of
|
|
|
|
|
the Attribute Protocol (ATT), and defines how one device can interact
|
|
|
|
|
with GATT Services on a remote device.
|
|
|
|
|
|
|
|
|
|
So far, the most popular use of BTstack is in peripheral devices that
|
|
|
|
|
can be connected via SPP (Android 2.0 or higher) and GATT (Android 4.3
|
|
|
|
|
or higher, and iOS 5 or higher). If higher data rates are required
|
|
|
|
|
between a peripheral and iOS device, the iAP1 and iAP2 protocols of the
|
|
|
|
|
Made for iPhone program can be used instead of GATT. Please contact us
|
|
|
|
|
directly for information on BTstack and MFi.
|
|
|
|
|
|
2015-06-18 12:44:40 +02:00
|
|
|
|
Figure {@fig:BTstackProtocolArchitecture} depicts Bluetooth protocols
|
|
|
|
|
and profiles that are currently implemented by BTstack.
|
2015-05-05 00:21:33 +02:00
|
|
|
|
In the following, we first explain how the various Bluetooth protocols
|
2015-06-18 12:44:40 +02:00
|
|
|
|
are used in BTstack. In the next chapter, we go over the profiles.
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2015-06-18 12:44:40 +02:00
|
|
|
|
![Architecture of a BTstack-based application.](picts/btstack-protocols.png) {#fig:BTstackProtocolArchitecture}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## HCI - Host Controller Interface
|
|
|
|
|
|
|
|
|
|
The HCI protocol provides a command interface to the Bluetooth chipset.
|
|
|
|
|
In BTstack, the HCI implementation also keeps track of all active
|
|
|
|
|
connections and handles the fragmentation and re-assembly of higher
|
|
|
|
|
layer (L2CAP) packets.
|
|
|
|
|
|
|
|
|
|
Please note, that an application rarely has to send HCI commands on its
|
|
|
|
|
own. Instead, BTstack provides convenience functions in GAP and higher
|
2016-03-31 22:30:50 +02:00
|
|
|
|
level protocols that use HCI automatically. E.g. to set the name, you
|
2015-05-05 00:21:33 +02:00
|
|
|
|
call *gap_set_local_name()* before powering up. The main use of HCI
|
|
|
|
|
commands in application is during the startup phase to configure special
|
2016-03-31 22:30:50 +02:00
|
|
|
|
features that are not available via the GAP API yet. How to send a
|
|
|
|
|
custom HCI command is explained in the following section.
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
### Defining custom HCI command templates
|
|
|
|
|
|
|
|
|
|
Each HCI command is assigned a 2-byte OpCode used to uniquely identify
|
|
|
|
|
different types of commands. The OpCode parameter is divided into two
|
|
|
|
|
fields, called the OpCode Group Field (OGF) and OpCode Command Field
|
|
|
|
|
(OCF), see [Bluetooth Specification](https://www.bluetooth.org/Technical/Specifications/adopted.htm) -
|
|
|
|
|
Core Version 4.0, Volume 2, Part E, Chapter 5.4.
|
|
|
|
|
|
2015-05-08 15:09:18 +02:00
|
|
|
|
Listing [below](#lst:hciOGFs) shows the OGFs provided by BTstack in file [src/hci.h]():
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~ {#lst:hciOGFs .c caption="{HCI OGFs provided by BTstack.}"}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
#define OGF_LINK_CONTROL 0x01
|
|
|
|
|
#define OGF_LINK_POLICY 0x02
|
|
|
|
|
#define OGF_CONTROLLER_BASEBAND 0x03
|
|
|
|
|
#define OGF_INFORMATIONAL_PARAMETERS 0x04
|
|
|
|
|
#define OGF_LE_CONTROLLER 0x08
|
|
|
|
|
#define OGF_BTSTACK 0x3d
|
|
|
|
|
#define OGF_VENDOR 0x3f
|
2016-03-31 15:48:41 +02:00
|
|
|
|
~~~~
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
For all existing Bluetooth
|
|
|
|
|
commands and their OCFs see [Bluetooth Specification](https://www.bluetooth.org/Technical/Specifications/adopted.htm) -
|
|
|
|
|
Core Version 4.0, Volume 2, Part E, Chapter 7.
|
|
|
|
|
|
|
|
|
|
In a HCI command packet, the OpCode is followed by parameter total
|
|
|
|
|
length, and the actual parameters. The OpCode of a command can be
|
|
|
|
|
calculated using the OPCODE macro. BTstack provides the *hci_cmd_t*
|
|
|
|
|
struct as a compact format to define HCI command packets, see
|
2016-01-21 11:33:17 +01:00
|
|
|
|
Listing [below](#lst:HCIcmdTemplate), and [include/btstack/hci_cmd.h]()
|
2015-05-08 15:09:18 +02:00
|
|
|
|
file in the source code.
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~ {#lst:HCIcmdTemplate .c caption="{HCI command struct.}"}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
// Calculate combined ogf/ocf value.
|
|
|
|
|
#define OPCODE(ogf, ocf) (ocf | ogf << 10)
|
|
|
|
|
|
|
|
|
|
// Compact HCI Command packet description.
|
|
|
|
|
typedef struct {
|
|
|
|
|
uint16_t opcode;
|
|
|
|
|
const char *format;
|
|
|
|
|
} hci_cmd_t;
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
Listing [below](#lst:HCIcmdExample) illustrates the *hci_write_local_name* HCI
|
|
|
|
|
command template from library:
|
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~ {#lst:HCIcmdExample .c caption="{HCI command example.}"}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
// Sets local Bluetooth name
|
|
|
|
|
const hci_cmd_t hci_write_local_name = {
|
|
|
|
|
OPCODE(OGF_CONTROLLER_BASEBAND, 0x13), "N"
|
|
|
|
|
// Local name (UTF-8, Null Terminated, max 248 octets)
|
|
|
|
|
};
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
It uses OGF_CONTROLLER_BASEBAND as OGF,
|
|
|
|
|
0x13 as OCF, and has one parameter with format “N” indicating a null
|
2015-06-19 15:41:29 +02:00
|
|
|
|
terminated UTF-8 string. Table {@tbl:hciCmdParamSpecifier} lists the format
|
2015-05-05 00:21:33 +02:00
|
|
|
|
specifiers supported by BTstack. Check for other predefined HCI commands
|
|
|
|
|
and info on their parameters.
|
|
|
|
|
|
|
|
|
|
------------------- ----------------------------------------------------
|
|
|
|
|
Format Specifier Description
|
|
|
|
|
1,2,3,4 one to four byte value
|
|
|
|
|
A 31 bytes advertising data
|
|
|
|
|
B Bluetooth Baseband Address
|
|
|
|
|
D 8 byte data block
|
|
|
|
|
E Extended Inquiry Information 240 octets
|
|
|
|
|
H HCI connection handle
|
|
|
|
|
N Name up to 248 chars, UTF8 string, null terminated
|
|
|
|
|
P 16 byte Pairing code, e.g. PIN code or link key
|
|
|
|
|
S Service Record (Data Element Sequence)
|
|
|
|
|
------------------- ----------------------------------------------------
|
|
|
|
|
|
2015-06-19 15:41:29 +02:00
|
|
|
|
Table: Supported Format Specifiers of HCI Command Parameter. {#tbl:hciCmdParamSpecifier}
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
|
|
|
|
|
### Sending HCI command based on a template {#sec:sendingHCIProtocols}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
You can use the *hci_send_cmd* function to send HCI command based on a
|
|
|
|
|
template and a list of parameters. However, it is necessary to check
|
|
|
|
|
that the outgoing packet buffer is empty and that the Bluetooth module
|
|
|
|
|
is ready to receive the next command - most modern Bluetooth modules
|
|
|
|
|
only allow to send a single HCI command. This can be done by calling
|
|
|
|
|
*hci_can_send_command_packet_now()* function, which returns true,
|
|
|
|
|
if it is ok to send.
|
|
|
|
|
|
|
|
|
|
Listing [below](#lst:HCIcmdExampleLocalName) illustrates how to manually set the
|
|
|
|
|
device name with the HCI Write Local Name command.
|
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~ {#lst:HCIcmdExampleLocalName .c caption="{Sending HCI command example.}"}
|
2015-06-05 11:57:37 +02:00
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
if (hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)){
|
|
|
|
|
hci_send_cmd(&hci_write_local_name, "BTstack Demo");
|
|
|
|
|
}
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
Please note, that an application rarely has to send HCI commands on its
|
|
|
|
|
own. Instead, BTstack provides convenience functions in GAP and higher
|
2016-03-31 22:30:50 +02:00
|
|
|
|
level protocols that use HCI automatically.
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
|
|
|
|
|
## L2CAP - Logical Link Control and Adaptation Protocol
|
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
The L2CAP protocol supports higher level protocol multiplexing and
|
|
|
|
|
packet fragmentation. It provides the base for the RFCOMM and BNEP
|
|
|
|
|
protocols. For all profiles that are officially supported by BTstack,
|
|
|
|
|
L2CAP does not need to be used directly. For testing or the development
|
|
|
|
|
of custom protocols, it’s helpful to be able to access and provide L2CAP
|
|
|
|
|
services however.
|
|
|
|
|
|
|
|
|
|
### Access an L2CAP service on a remote device
|
|
|
|
|
|
|
|
|
|
L2CAP is based around the concept of channels. A channel is a logical
|
|
|
|
|
connection on top of a baseband connection. Each channel is bound to a
|
|
|
|
|
single protocol in a many-to-one fashion. Multiple channels can be bound
|
|
|
|
|
to the same protocol, but a channel cannot be bound to multiple
|
|
|
|
|
protocols. Multiple channels can share the same baseband connection.
|
|
|
|
|
|
|
|
|
|
To communicate with an L2CAP service on a remote device, the application
|
|
|
|
|
on a local Bluetooth device initiates the L2CAP layer using the
|
|
|
|
|
*l2cap_init* function, and then creates an outgoing L2CAP channel to
|
2016-01-21 12:21:03 +01:00
|
|
|
|
the PSM of a remote device using the *l2cap_create_channel*
|
|
|
|
|
function. The *l2cap_create_channel* function will initiate
|
2015-05-05 00:21:33 +02:00
|
|
|
|
a new baseband connection if it does not already exist. The packet
|
|
|
|
|
handler that is given as an input parameter of the L2CAP create channel
|
|
|
|
|
function will be assigned to the new outgoing L2CAP channel. This
|
|
|
|
|
handler receives the L2CAP_EVENT_CHANNEL_OPENED and
|
|
|
|
|
L2CAP_EVENT_CHANNEL_CLOSED events and L2CAP data packets, as shown
|
|
|
|
|
in Listing [below](#lst:L2CAPremoteService).
|
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
|
|
|
|
|
~~~~ {#lst:L2CAPremoteService .c caption="{Accessing an L2CAP service on a remote device.}"}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
btstack_packet_handler_t l2cap_packet_handler;
|
|
|
|
|
|
2016-03-31 21:49:07 +02:00
|
|
|
|
void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
|
|
|
|
bd_addr_t event_addr;
|
|
|
|
|
switch (packet_type){
|
|
|
|
|
case HCI_EVENT_PACKET:
|
|
|
|
|
switch (hci_event_packet_get_type(packet)){
|
|
|
|
|
case L2CAP_EVENT_CHANNEL_OPENED:
|
|
|
|
|
l2cap_event_channel_opened_get_address(packet, &event_addr);
|
|
|
|
|
psm = l2cap_event_channel_opened_get_psm(packet);
|
|
|
|
|
local_cid = l2cap_event_channel_opened_get_local_cid(packet);
|
|
|
|
|
handle = l2cap_event_channel_opened_get_handle(packet);
|
|
|
|
|
if (l2cap_event_channel_opened_get_status(packet)) {
|
|
|
|
|
printf("Connection failed\n\r");
|
|
|
|
|
} else
|
|
|
|
|
printf("Connected\n\r");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case L2CAP_EVENT_CHANNEL_CLOSED:
|
|
|
|
|
break;
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
case L2CAP_DATA_PACKET:
|
|
|
|
|
// handle L2CAP data packet
|
|
|
|
|
break;
|
|
|
|
|
...
|
|
|
|
|
}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void create_outgoing_l2cap_channel(bd_addr_t address, uint16_t psm, uint16_t mtu){
|
2016-01-21 12:21:03 +01:00
|
|
|
|
l2cap_create_channel(NULL, l2cap_packet_handler, remote_bd_addr, psm, mtu);
|
2015-05-05 00:21:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 21:49:07 +02:00
|
|
|
|
void btstack_setup(){
|
|
|
|
|
...
|
|
|
|
|
l2cap_init();
|
2015-05-05 00:21:33 +02:00
|
|
|
|
}
|
2016-03-31 21:49:07 +02:00
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
### Provide an L2CAP service
|
|
|
|
|
|
|
|
|
|
To provide an L2CAP service, the application on a local Bluetooth device
|
|
|
|
|
must init the L2CAP layer and register the service with
|
2016-01-21 12:21:03 +01:00
|
|
|
|
*l2cap_register_service*. From there on, it can wait for
|
2015-05-05 00:21:33 +02:00
|
|
|
|
incoming L2CAP connections. The application can accept or deny an
|
2016-01-21 12:03:39 +01:00
|
|
|
|
incoming connection by calling the *l2cap_accept_connection*
|
2016-03-31 21:49:07 +02:00
|
|
|
|
and *l2cap_deny_connection* functions respectively.
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2016-03-31 21:49:07 +02:00
|
|
|
|
If a connection is accepted and the incoming L2CAP channel gets successfully
|
|
|
|
|
opened, the L2CAP service can send and receive L2CAP data packets to the connected
|
|
|
|
|
device with *l2cap_send*.
|
2016-03-31 11:04:47 +02:00
|
|
|
|
|
|
|
|
|
Listing [below](#lst:L2CAPService)
|
2015-05-05 00:21:33 +02:00
|
|
|
|
provides L2CAP service example code.
|
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~ {#lst:L2CAPService .c caption="{Providing an L2CAP service.}"}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2016-03-31 21:49:07 +02:00
|
|
|
|
void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
|
|
|
|
bd_addr_t event_addr;
|
|
|
|
|
switch (packet_type){
|
|
|
|
|
case HCI_EVENT_PACKET:
|
|
|
|
|
switch (hci_event_packet_get_type(packet)){
|
|
|
|
|
case L2CAP_EVENT_INCOMING_CONNECTION:
|
|
|
|
|
local_cid = l2cap_event_incoming_connection_get_local_cid(packet);
|
|
|
|
|
l2cap_accept_connection(local_cid);
|
|
|
|
|
break;
|
|
|
|
|
case L2CAP_EVENT_CHANNEL_OPENED:
|
|
|
|
|
l2cap_event_channel_opened_get_address(packet, &event_addr);
|
|
|
|
|
psm = l2cap_event_channel_opened_get_psm(packet);
|
|
|
|
|
local_cid = l2cap_event_channel_opened_get_local_cid(packet);
|
|
|
|
|
handle = l2cap_event_channel_opened_get_handle(packet);
|
|
|
|
|
if (l2cap_event_channel_opened_get_status(packet)) {
|
|
|
|
|
printf("Connection failed\n\r");
|
|
|
|
|
} else
|
|
|
|
|
printf("Connected\n\r");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case L2CAP_EVENT_CHANNEL_CLOSED:
|
|
|
|
|
break;
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
case L2CAP_DATA_PACKET:
|
|
|
|
|
// handle L2CAP data packet
|
|
|
|
|
break;
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
void btstack_setup(){
|
|
|
|
|
...
|
|
|
|
|
l2cap_init();
|
2016-01-21 12:21:03 +01:00
|
|
|
|
l2cap_register_service(NULL, packet_handler, 0x11,100);
|
2015-05-05 00:21:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2016-03-31 21:49:07 +02:00
|
|
|
|
### Sending L2CAP Data {#sec:l2capSendProtocols}
|
|
|
|
|
|
|
|
|
|
Sending of L2CAP data packets may fail due to a full internal BTstack
|
|
|
|
|
outgoing packet buffer, or if the ACL buffers in the Bluetooth module
|
|
|
|
|
become full, i.e., if the application is sending faster than the packets
|
|
|
|
|
can be transferred over the air.
|
2016-03-31 15:56:16 +02:00
|
|
|
|
|
|
|
|
|
Instead of directly calling *l2cap_send*, it is recommended to call
|
2016-03-31 23:17:00 +02:00
|
|
|
|
*l2cap_request_can_send_now_event(cahnnel_id)* which will trigger an L2CAP_EVENT_CAN_SEND_NOW
|
|
|
|
|
as soon as possible. It might happen that the event is received via
|
|
|
|
|
packet handler before the *l2cap_request_can_send_now_event* function returns.
|
|
|
|
|
The L2CAP_EVENT_CAN_SEND_NOW indicates a channel ID on which sending is possible.
|
2016-03-31 15:56:16 +02:00
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
## RFCOMM - Radio Frequency Communication Protocol
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
The Radio frequency communication (RFCOMM) protocol provides emulation
|
|
|
|
|
of serial ports over the L2CAP protocol. and reassembly. It is the base
|
|
|
|
|
for the Serial Port Profile and other profiles used for
|
|
|
|
|
telecommunication like Head-Set Profile, Hands-Free Profile, Object
|
|
|
|
|
Exchange (OBEX) etc.
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
|
|
|
|
|
### RFCOMM flow control {#sec:flowControlProtocols}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
RFCOMM has a mandatory credit-based flow-control. This means that two
|
|
|
|
|
devices that established RFCOMM connection, use credits to keep track of
|
|
|
|
|
how many more RFCOMM data packets can be sent to each. If a device has
|
|
|
|
|
no (outgoing) credits left, it cannot send another RFCOMM packet, the
|
|
|
|
|
transmission must be paused. During the connection establishment,
|
|
|
|
|
initial credits are provided. BTstack tracks the number of credits in
|
|
|
|
|
both directions. If no outgoing credits are available, the RFCOMM send
|
|
|
|
|
function will return an error, and you can try later. For incoming data,
|
|
|
|
|
BTstack provides channels and services with and without automatic credit
|
|
|
|
|
management via different functions to create/register them respectively.
|
|
|
|
|
If the management of credits is automatic, the new credits are provided
|
|
|
|
|
when needed relying on ACL flow control - this is only useful if there
|
|
|
|
|
is not much data transmitted and/or only one physical connection is
|
|
|
|
|
used. If the management of credits is manual, credits are provided by
|
|
|
|
|
the application such that it can manage its receive buffers explicitly.
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
|
|
|
|
|
### Access an RFCOMM service on a remote device {#sec:rfcommClientProtocols}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
To communicate with an RFCOMM service on a remote device, the
|
|
|
|
|
application on a local Bluetooth device initiates the RFCOMM layer using
|
|
|
|
|
the *rfcomm_init* function, and then creates an outgoing RFCOMM channel
|
|
|
|
|
to a given server channel on a remote device using the
|
2016-01-21 12:21:03 +01:00
|
|
|
|
*rfcomm_create_channel* function. The
|
|
|
|
|
*rfcomm_create_channel* function will initiate a new L2CAP
|
2015-05-05 00:21:33 +02:00
|
|
|
|
connection for the RFCOMM multiplexer, if it does not already exist. The
|
|
|
|
|
channel will automatically provide enough credits to the remote side. To
|
|
|
|
|
provide credits manually, you have to create the RFCOMM connection by
|
2016-01-21 12:21:03 +01:00
|
|
|
|
calling *rfcomm_create_channel_with_initial_credits* -
|
2015-06-18 16:33:34 +02:00
|
|
|
|
see Section [on manual credit assignement](#sec:manualCreditsProtocols).
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
The packet handler that is given as an input parameter of the RFCOMM
|
|
|
|
|
create channel function will be assigned to the new outgoing channel.
|
2016-03-31 23:37:33 +02:00
|
|
|
|
This handler receives the RFCOMM_EVENT_CHANNEL_OPENED and
|
2015-05-05 00:21:33 +02:00
|
|
|
|
RFCOMM_EVENT_CHANNEL_CLOSED events, and RFCOMM data packets, as shown in
|
|
|
|
|
Listing [below](#lst:RFCOMMremoteService).
|
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
|
|
|
|
|
~~~~ {#lst:RFCOMMremoteService .c caption="{RFCOMM handler for outgoing RFCOMM channel.}"}
|
|
|
|
|
|
2016-03-31 21:49:07 +02:00
|
|
|
|
void rfcomm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
|
|
|
|
switch (packet_type){
|
|
|
|
|
case HCI_EVENT_PACKET:
|
|
|
|
|
switch (hci_event_packet_get_type(packet)){
|
2016-03-31 23:37:33 +02:00
|
|
|
|
case RFCOMM_EVENT_CHANNEL_OPENED:
|
2016-03-31 21:49:07 +02:00
|
|
|
|
if (rfcomm_event_open_channel_complete_get_status(packet)) {
|
|
|
|
|
printf("Connection failed\n\r");
|
|
|
|
|
} else {
|
|
|
|
|
printf("Connected\n\r");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case RFCOMM_EVENT_CHANNEL_CLOSED:
|
|
|
|
|
break;
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case RFCOMM_DATA_PACKET:
|
|
|
|
|
// handle RFCOMM data packets
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void create_rfcomm_channel(uint8_t packet_type, uint8_t *packet, uint16_t size){
|
2016-03-31 21:49:07 +02:00
|
|
|
|
rfcomm_create_channel(rfcomm_packet_handler, addr, rfcomm_channel);
|
2015-05-05 00:21:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 21:49:07 +02:00
|
|
|
|
void btstack_setup(){
|
|
|
|
|
...
|
|
|
|
|
l2cap_init();
|
|
|
|
|
rfcomm_init();
|
2015-05-05 00:21:33 +02:00
|
|
|
|
}
|
2016-03-31 21:49:07 +02:00
|
|
|
|
|
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
### Provide an RFCOMM service {#sec:rfcommServiceProtocols}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
To provide an RFCOMM service, the application on a local Bluetooth
|
|
|
|
|
device must first init the L2CAP and RFCOMM layers and then register the
|
2016-01-21 12:21:03 +01:00
|
|
|
|
service with *rfcomm_register_service*. From there on, it
|
2015-05-05 00:21:33 +02:00
|
|
|
|
can wait for incoming RFCOMM connections. The application can accept or
|
|
|
|
|
deny an incoming connection by calling the
|
2016-03-31 21:49:07 +02:00
|
|
|
|
*rfcomm_accept_connection* and *rfcomm_deny_connection* functions respectively.
|
|
|
|
|
If a connection is accepted and the incoming RFCOMM channel gets successfully
|
2015-05-05 00:21:33 +02:00
|
|
|
|
opened, the RFCOMM service can send RFCOMM data packets to the connected
|
2016-01-21 12:09:19 +01:00
|
|
|
|
device with *rfcomm_send* and receive data packets by the
|
2016-01-21 12:21:03 +01:00
|
|
|
|
packet handler provided by the *rfcomm_register_service*
|
2015-05-05 00:21:33 +02:00
|
|
|
|
call.
|
|
|
|
|
|
2016-03-31 21:49:07 +02:00
|
|
|
|
Listing [below](#lst:RFCOMMService) provides the RFCOMM service example code.
|
2015-06-24 16:47:05 +02:00
|
|
|
|
|
|
|
|
|
~~~~ {#lst:RFCOMMService .c caption="{Providing an RFCOMM service.}"}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2016-03-31 21:49:07 +02:00
|
|
|
|
void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
|
|
|
|
switch (packet_type){
|
|
|
|
|
case HCI_EVENT_PACKET:
|
|
|
|
|
switch (hci_event_packet_get_type(packet)){
|
|
|
|
|
case RFCOMM_EVENT_INCOMING_CONNECTION:
|
|
|
|
|
rfcomm_channel_id = rfcomm_event_incoming_connection_get_rfcomm_cid(packet);
|
|
|
|
|
rfcomm_accept_connection(rfcomm_channel_id);
|
|
|
|
|
break;
|
2016-03-31 23:37:33 +02:00
|
|
|
|
case RFCOMM_EVENT_CHANNEL_OPENED:
|
2016-03-31 21:49:07 +02:00
|
|
|
|
if (rfcomm_event_open_channel_complete_get_status(packet)){
|
|
|
|
|
printf("RFCOMM channel open failed.");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
rfcomm_channel_id = rfcomm_event_open_channel_complete_get_rfcomm_cid(packet);
|
|
|
|
|
mtu = rfcomm_event_open_channel_complete_get_max_frame_size(packet);
|
|
|
|
|
printf("RFCOMM channel open succeeded, max frame size %u.", mtu);
|
|
|
|
|
break;
|
|
|
|
|
case RFCOMM_EVENT_CHANNEL_CLOSED:
|
|
|
|
|
printf("Channel closed.");
|
|
|
|
|
break;
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case RFCOMM_DATA_PACKET:
|
|
|
|
|
// handle RFCOMM data packets
|
|
|
|
|
return;
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
void btstack_setup(){
|
|
|
|
|
...
|
2016-03-31 21:49:07 +02:00
|
|
|
|
l2cap_init();
|
2015-05-05 00:21:33 +02:00
|
|
|
|
rfcomm_init();
|
2016-03-31 21:49:07 +02:00
|
|
|
|
rfcomm_register_service(packet_handler, rfcomm_channel_nr, mtu);
|
2015-05-05 00:21:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~
|
2015-06-18 16:33:34 +02:00
|
|
|
|
|
2016-03-31 15:56:16 +02:00
|
|
|
|
### Slowing down RFCOMM data reception {#sec:manualCreditsProtocols}
|
2015-06-24 16:47:05 +02:00
|
|
|
|
|
2016-03-31 15:56:16 +02:00
|
|
|
|
RFCOMM’s credit-based flow-control can be used to adapt, i.e., slow down
|
|
|
|
|
the RFCOMM data to your processing speed. For incoming data, BTstack
|
|
|
|
|
provides channels and services with and without automatic credit
|
|
|
|
|
management. If the management of credits is automatic, new credits
|
|
|
|
|
are provided when needed relying on ACL flow control. This is only
|
|
|
|
|
useful if there is not much data transmitted and/or only one physical
|
|
|
|
|
connection is used. See Listing [below](#lst:automaticFlowControl).
|
|
|
|
|
|
|
|
|
|
~~~~ {#lst:automaticFlowControl .c caption="{RFCOMM service with automatic credit management.}"}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
void btstack_setup(void){
|
|
|
|
|
...
|
|
|
|
|
// init RFCOMM
|
|
|
|
|
rfcomm_init();
|
2016-03-31 21:49:07 +02:00
|
|
|
|
rfcomm_register_service(packet_handler, rfcomm_channel_nr, 100);
|
2015-05-05 00:21:33 +02:00
|
|
|
|
}
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2016-03-31 15:56:16 +02:00
|
|
|
|
If the management of credits is manual, credits are provided by the
|
|
|
|
|
application such that it can manage its receive buffers explicitly, see
|
|
|
|
|
Listing [below](#lst:explicitFlowControl).
|
|
|
|
|
|
2015-05-05 00:21:33 +02:00
|
|
|
|
Manual credit management is recommended when received RFCOMM data cannot
|
2015-06-18 16:33:34 +02:00
|
|
|
|
be processed immediately. In the [SPP flow control example](examples/generated/#sec:sppflowcontrolExample),
|
2015-05-05 00:21:33 +02:00
|
|
|
|
delayed processing of received data is
|
|
|
|
|
simulated with the help of a periodic timer. To provide new credits, you
|
|
|
|
|
call the *rfcomm_grant_credits* function with the RFCOMM channel ID
|
|
|
|
|
and the number of credits as shown in Listing [below](#lst:NewCredits).
|
|
|
|
|
|
2016-03-31 15:56:16 +02:00
|
|
|
|
~~~~ {#lst:explicitFlowControl .c caption="{RFCOMM service with manual credit management.}"}
|
|
|
|
|
void btstack_setup(void){
|
|
|
|
|
...
|
|
|
|
|
// init RFCOMM
|
|
|
|
|
rfcomm_init();
|
|
|
|
|
// reserved channel, mtu=100, 1 credit
|
2016-03-31 21:49:07 +02:00
|
|
|
|
rfcomm_register_service_with_initial_credits(packet_handler, rfcomm_channel_nr, 100, 1);
|
2016-03-31 15:56:16 +02:00
|
|
|
|
}
|
|
|
|
|
~~~~
|
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~ {#lst:NewCredits .c caption="{Granting RFCOMM credits.}"}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
void processing(){
|
|
|
|
|
// process incoming data packet
|
|
|
|
|
...
|
|
|
|
|
// provide new credit
|
|
|
|
|
rfcomm_grant_credits(rfcomm_channel_id, 1);
|
|
|
|
|
}
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
Please note that providing single credits effectively reduces the credit-based
|
|
|
|
|
(sliding window) flow control to a stop-and-wait flow-control that
|
|
|
|
|
limits the data throughput substantially. On the plus side, it allows
|
|
|
|
|
for a minimal memory footprint. If possible, multiple RFCOMM buffers
|
|
|
|
|
should be used to avoid pauses while the sender has to wait for a new
|
|
|
|
|
credit.
|
|
|
|
|
|
2016-03-31 22:44:42 +02:00
|
|
|
|
### Sending RFCOMM data {#sec:rfcommSendProtocols}
|
|
|
|
|
|
|
|
|
|
Outgoing packets, both commands and data, are not queued in BTstack.
|
|
|
|
|
This section explains the consequences of this design decision for
|
|
|
|
|
sending data and why it is not as bad as it sounds.
|
|
|
|
|
|
|
|
|
|
Independent from the number of output buffers, packet generation has to
|
|
|
|
|
be adapted to the remote receiver and/or maximal link speed. Therefore,
|
|
|
|
|
a packet can only be generated when it can get sent. With this
|
|
|
|
|
assumption, the single output buffer design does not impose additional
|
|
|
|
|
restrictions. In the following, we show how this is used for adapting
|
|
|
|
|
the RFCOMM send rate.
|
|
|
|
|
|
2016-03-31 23:17:00 +02:00
|
|
|
|
When there is a need to send a packet, call *rcomm_request_can_send_now*
|
|
|
|
|
and wait for the reception of the RFCOMM_EVENT_CAN_SEND_NOW event
|
|
|
|
|
to send the packet, as shown in Listing [below](#lst:rfcommRequestCanSendNow).
|
2016-03-31 22:44:42 +02:00
|
|
|
|
|
|
|
|
|
~~~~ {#lst:rfcommRequestCanSendNow .c caption="{Preparing and sending data.}"}
|
2016-03-31 23:17:00 +02:00
|
|
|
|
void prepare_data(uint16_t rfcomm_channel_id){
|
2016-03-31 22:44:42 +02:00
|
|
|
|
...
|
|
|
|
|
// prepare data in data_buffer
|
2016-03-31 23:17:00 +02:00
|
|
|
|
rfcomm_request_can_send_now_event(rfcomm_channel_id);
|
2016-03-31 22:44:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 23:17:00 +02:00
|
|
|
|
void send_data(uint16_t rfcomm_channel_id){
|
2016-03-31 22:44:42 +02:00
|
|
|
|
rfcomm_send(rfcomm_channel_id, data_buffer, data_len);
|
|
|
|
|
// packet is handed over to BTstack, we can prepare the next one
|
2016-03-31 23:17:00 +02:00
|
|
|
|
prepare_data(rfcomm_channel_id);
|
2016-03-31 22:44:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
|
|
|
|
switch (packet_type){
|
|
|
|
|
case HCI_EVENT_PACKET:
|
|
|
|
|
switch (hci_event_packet_get_type(packet)){
|
|
|
|
|
...
|
2016-03-31 23:17:00 +02:00
|
|
|
|
case RFCOMM_EVENT_CAN_SEND_NOW:
|
|
|
|
|
rfcomm_channel_id = rfcomm_event_can_send_now_get_rfcomm_cid(packet);
|
|
|
|
|
send_data(rfcomm_channel_id);
|
2016-03-31 22:44:42 +02:00
|
|
|
|
break;
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~~~~
|
|
|
|
|
|
|
|
|
|
### Optimized sending of RFCOMM data
|
|
|
|
|
|
|
|
|
|
When sending RFCOMM data via *rfcomm_send*, BTstack needs to copy the data
|
|
|
|
|
from the user provided buffer into the outgoing buffer. This requires both
|
|
|
|
|
an additional buffer for the user data as well requires a copy operation.
|
|
|
|
|
|
|
|
|
|
To avoid this, it is possible to directly write the user data into the outgoing buffer.
|
|
|
|
|
|
|
|
|
|
When get the RFCOMM_CAN_SEND_NOW event, you call *rfcomm_reserve_packet_buffer* to
|
|
|
|
|
lock the buffer for your send operation. Then, you can ask how many bytes you can send
|
|
|
|
|
with *rfcomm_get_max_frame_size* and get a pointer to BTstack's buffer with
|
|
|
|
|
*rfcomm_get_outgoing_buffer*. Now, you can fill that buffer and finally send the
|
|
|
|
|
data with *rfcomm_send_prepared*.
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
|
|
|
|
|
## SDP - Service Discovery Protocol
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
The SDP protocol allows to announce services and discover services
|
|
|
|
|
provided by a remote Bluetooth device.
|
|
|
|
|
|
|
|
|
|
### Create and announce SDP records
|
|
|
|
|
|
|
|
|
|
BTstack contains a complete SDP server and allows to register SDP
|
|
|
|
|
records. An SDP record is a list of SDP Attribute *{ID, Value}* pairs
|
|
|
|
|
that are stored in a Data Element Sequence (DES). The Attribute ID is a
|
|
|
|
|
16-bit number, the value can be of other simple types like integers or
|
|
|
|
|
strings or can itselff contain other DES.
|
|
|
|
|
|
|
|
|
|
To create an SDP record for an SPP service, you can call
|
2016-03-17 11:24:38 +01:00
|
|
|
|
*spp_create_sdp_record* from with a pointer to a buffer to store the
|
2015-05-05 00:21:33 +02:00
|
|
|
|
record, the server channel number, and a record name.
|
|
|
|
|
|
|
|
|
|
For other types of records, you can use the other functions in , using
|
|
|
|
|
the data element *de_* functions. Listing [sdpCreate] shows how an SDP
|
|
|
|
|
record containing two SDP attributes can be created. First, a DES is
|
|
|
|
|
created and then the Service Record Handle and Service Class ID List
|
|
|
|
|
attributes are added to it. The Service Record Handle attribute is added
|
|
|
|
|
by calling the *de_add_number* function twice: the first time to add
|
|
|
|
|
0x0000 as attribute ID, and the second time to add the actual record
|
|
|
|
|
handle (here 0x1000) as attribute value. The Service Class ID List
|
|
|
|
|
attribute has ID 0x0001, and it requires a list of UUIDs as attribute
|
|
|
|
|
value. To create the list, *de_push_sequence* is called, which “opens”
|
|
|
|
|
a sub-DES. The returned pointer is used to add elements to this sub-DES.
|
|
|
|
|
After adding all UUIDs, the sub-DES is “closed” with
|
|
|
|
|
*de_pop_sequence*.
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
|
|
|
|
|
### Query remote SDP service {#sec:querySDPProtocols}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
BTstack provides an SDP client to query SDP services of a remote device.
|
2015-06-18 16:33:34 +02:00
|
|
|
|
The SDP Client API is shown in [here](appendix/apis/#sec:sdpAPIAppendix). The
|
2015-05-05 00:21:33 +02:00
|
|
|
|
*sdp_client_query* function initiates an L2CAP connection to the
|
|
|
|
|
remote SDP server. Upon connect, a *Service Search Attribute* request
|
|
|
|
|
with a *Service Search Pattern* and a *Attribute ID List* is sent. The
|
|
|
|
|
result of the *Service Search Attribute* query contains a list of
|
|
|
|
|
*Service Records*, and each of them contains the requested attributes.
|
|
|
|
|
These records are handled by the SDP parser. The parser delivers
|
|
|
|
|
SDP_PARSER_ATTRIBUTE_VALUE and SDP_PARSER_COMPLETE events via a
|
|
|
|
|
registered callback. The SDP_PARSER_ATTRIBUTE_VALUE event delivers
|
|
|
|
|
the attribute value byte by byte.
|
|
|
|
|
|
|
|
|
|
On top of this, you can implement specific SDP queries. For example,
|
|
|
|
|
BTstack provides a query for RFCOMM service name and channel number.
|
|
|
|
|
This information is needed, e.g., if you want to connect to a remote SPP
|
|
|
|
|
service. The query delivers all matching RFCOMM services, including its
|
|
|
|
|
name and the channel number, as well as a query complete event via a
|
|
|
|
|
registered callback, as shown in Listing [below](#lst:SDPClientRFCOMM).
|
|
|
|
|
|
2015-06-24 16:47:05 +02:00
|
|
|
|
|
|
|
|
|
~~~~ {#lst:SDPClientRFCOMM .c caption="{Searching RFCOMM services on a remote device.}"}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
bd_addr_t remote = {0x04,0x0C,0xCE,0xE4,0x85,0xD3};
|
|
|
|
|
|
|
|
|
|
void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
|
|
|
|
if (packet_type != HCI_EVENT_PACKET) return;
|
|
|
|
|
|
|
|
|
|
uint8_t event = packet[0];
|
|
|
|
|
switch (event) {
|
|
|
|
|
case BTSTACK_EVENT_STATE:
|
|
|
|
|
// bt stack activated, get started
|
|
|
|
|
if (packet[2] == HCI_STATE_WORKING){
|
2016-03-17 10:42:14 +01:00
|
|
|
|
sdp_client_query_rfcomm_channel_and_name_for_uuid(remote, 0x0003);
|
2015-05-05 00:21:33 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void btstack_setup(){
|
|
|
|
|
...
|
|
|
|
|
// init L2CAP
|
|
|
|
|
l2cap_init();
|
|
|
|
|
l2cap_register_packet_handler(packet_handler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void handle_query_rfcomm_event(sdp_query_event_t * event, void * context){
|
2016-03-17 10:42:14 +01:00
|
|
|
|
sdp_client_query_rfcomm_service_event_t * ve;
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
switch (event->type){
|
2016-01-30 23:58:36 +01:00
|
|
|
|
case SDP_EVENT_QUERY_RFCOMM_SERVICE:
|
2016-03-17 10:42:14 +01:00
|
|
|
|
ve = (sdp_client_query_rfcomm_service_event_t*) event;
|
2015-05-05 00:21:33 +02:00
|
|
|
|
printf("Service name: '%s', RFCOMM port %u\n", ve->service_name, ve->channel_nr);
|
|
|
|
|
break;
|
2016-01-30 23:58:36 +01:00
|
|
|
|
case SDP_EVENT_QUERY_COMPLETE:
|
2015-05-05 00:21:33 +02:00
|
|
|
|
report_found_services();
|
|
|
|
|
printf("Client query response done with status %d. \n", ce->status);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(void){
|
|
|
|
|
hw_setup();
|
|
|
|
|
btstack_setup();
|
|
|
|
|
|
|
|
|
|
// register callback to receive matching RFCOMM Services and
|
|
|
|
|
// query complete event
|
2016-03-17 10:42:14 +01:00
|
|
|
|
sdp_client_query_rfcomm_register_callback(handle_query_rfcomm_event, NULL);
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
// turn on!
|
|
|
|
|
hci_power_control(HCI_POWER_ON);
|
|
|
|
|
// go!
|
2016-01-20 15:58:46 +01:00
|
|
|
|
btstack_run_loop_execute();
|
2015-05-05 00:21:33 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2015-06-24 16:47:05 +02:00
|
|
|
|
~~~~
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2015-06-05 14:38:52 +02:00
|
|
|
|
## BNEP - Bluetooth Network Encapsulation Protocol
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
The BNEP protocol is used to transport control and data packets over
|
|
|
|
|
standard network protocols such as TCP, IPv4 or IPv6. It is built on top
|
|
|
|
|
of L2CAP, and it specifies a minimum L2CAP MTU of 1691 bytes.
|
|
|
|
|
|
|
|
|
|
### Receive BNEP events
|
|
|
|
|
|
|
|
|
|
To receive BNEP events, please register a packet handler with
|
|
|
|
|
*bnep_register_packet_handler*.
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
### Access a BNEP service on a remote device {#sec:bnepClientProtocols}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
To connect to a remote BNEP service, you need to know its UUID. The set
|
|
|
|
|
of available UUIDs can be queried by a SDP query for the PAN profile.
|
2015-06-18 16:33:34 +02:00
|
|
|
|
Please see section on [PAN profile](profiles/#sec:panProfiles) for details.
|
2015-06-05 11:57:37 +02:00
|
|
|
|
With the remote UUID, you can create a connection using the *bnep_connect*
|
|
|
|
|
function. You’ll receive a *BNEP_EVENT_OPEN_CHANNEL_COMPLETE* on success or
|
2015-05-05 00:21:33 +02:00
|
|
|
|
failure.
|
|
|
|
|
|
|
|
|
|
After the connection was opened successfully, you can send and receive
|
|
|
|
|
Ethernet packets. Before sending an Ethernet frame with *bnep_send*,
|
|
|
|
|
*bnep_can_send_packet_now* needs to return true. Ethernet frames
|
|
|
|
|
are received via the registered packet handler with packet type
|
|
|
|
|
*BNEP_DATA_PACKET*.
|
|
|
|
|
|
|
|
|
|
BTstack BNEP implementation supports both network protocol filter and
|
|
|
|
|
multicast filters with *bnep_set_net_type_filter* and
|
|
|
|
|
*bnep_set_multicast_filter* respectively.
|
|
|
|
|
|
|
|
|
|
Finally, to close a BNEP connection, you can call *bnep_disconnect*.
|
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
### Provide BNEP service {#sec:bnepServiceProtocols}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
To provide a BNEP service, call *bnep_register_service* with the
|
|
|
|
|
provided service UUID and a max frame size.
|
|
|
|
|
|
|
|
|
|
A *BNEP_EVENT_INCOMING_CONNECTION* event will mark that an incoming
|
|
|
|
|
connection is established. At this point you can start sending and
|
|
|
|
|
receiving Ethernet packets as described in the previous section.
|
|
|
|
|
|
2016-03-31 22:30:50 +02:00
|
|
|
|
### Sending Ethernet packets
|
|
|
|
|
|
|
|
|
|
Similar to L2CAP and RFOMM, directly sending an Ethernet packet via BNEP might fail,
|
|
|
|
|
if the outgoing packet buffer or the ACL buffers in the Bluetooth module are full.
|
|
|
|
|
|
|
|
|
|
When there's a need to send an Ethernet packet, call *bnep_request_can_send_now*
|
|
|
|
|
and send the packet when the BNEP_EVENT_CAN_SEND_NOW event
|
|
|
|
|
gets received.
|
|
|
|
|
|
|
|
|
|
|
2015-06-05 14:38:52 +02:00
|
|
|
|
## ATT - Attribute Protocol
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
The ATT protocol is used by an ATT client to read and write attribute
|
|
|
|
|
values stored on an ATT server. In addition, the ATT server can notify
|
|
|
|
|
the client about attribute value changes. An attribute has a handle, a
|
2016-03-31 22:30:50 +02:00
|
|
|
|
type, and a set of properties.
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
The Generic Attribute (GATT) profile is built upon ATT and provides
|
|
|
|
|
higher level organization of the ATT attributes into GATT Services and
|
|
|
|
|
GATT Characteristics. In BTstack, the complete ATT client functionality
|
2016-03-31 22:30:50 +02:00
|
|
|
|
is included within the GATT Client. See [GATT client](profiles/#sec:GATTClientProfiles) for more.
|
|
|
|
|
|
|
|
|
|
On the server side, one ore more GATT profiles are converted ahead of time
|
|
|
|
|
into the corresponding ATT attribute database and provided by the *att_server*
|
|
|
|
|
implementation. The constant data are automatically served by the ATT server upon client
|
2015-05-05 00:21:33 +02:00
|
|
|
|
request. To receive the dynamic data, such is characteristic value, the
|
|
|
|
|
application needs to register read and/or write callback. In addition,
|
2015-06-18 16:33:34 +02:00
|
|
|
|
notifications and indications can be sent. Please see Section on
|
2016-03-31 22:30:50 +02:00
|
|
|
|
[GATT server](section:profiles/#sec:GATTServerProfile) for more.
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2015-06-18 16:33:34 +02:00
|
|
|
|
## SMP - Security Manager Protocol {#sec:smpProtocols}
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
The SMP protocol allows to setup authenticated and encrypted LE
|
|
|
|
|
connection. After initialization and configuration, SMP handles security
|
|
|
|
|
related functions on it’s own but emits events when feedback from the
|
|
|
|
|
main app or the user is required. The two main tasks of the SMP protocol
|
|
|
|
|
are: bonding and identity resolving.
|
|
|
|
|
|
|
|
|
|
### Initialization
|
|
|
|
|
|
|
|
|
|
To activate the security manager, call *sm_init()*.
|
|
|
|
|
|
|
|
|
|
If you’re creating a product, you should also call *sm_set_ir()* and
|
|
|
|
|
*sm_set_er()* with a fixed random 16 byte number to create the IR and
|
|
|
|
|
ER key seeds. If possible use a unique random number per device instead
|
|
|
|
|
of deriving it from the product serial number or something similar. The
|
|
|
|
|
encryption key generated by the BLE peripheral will be ultimately
|
2015-06-18 16:33:34 +02:00
|
|
|
|
derived from the ER key seed. See
|
|
|
|
|
[Bluetooth Specification](https://www.bluetooth.org/Technical/Specifications/adopted.htm) -
|
2015-05-05 00:21:33 +02:00
|
|
|
|
Bluetooth Core V4.0, Vol 3, Part G, 5.2.2 for more details on deriving
|
|
|
|
|
the different keys. The IR key is used to identify a device if private,
|
|
|
|
|
resolvable Bluetooth addresses are used.
|
|
|
|
|
|
|
|
|
|
### Configuration
|
|
|
|
|
|
|
|
|
|
To receive events from the Security Manager, a callback is necessary.
|
|
|
|
|
How to register this packet handler depends on your application
|
|
|
|
|
configuration.
|
|
|
|
|
|
|
|
|
|
When *att_server* is used to provide a GATT/ATT service, *att_server*
|
|
|
|
|
registers itself as the Security Manager packet handler. Security
|
|
|
|
|
Manager events are then received by the application via the
|
|
|
|
|
*att_server* packet handler.
|
|
|
|
|
|
|
|
|
|
If *att_server* is not used, you can directly register your packet
|
|
|
|
|
handler with the security manager by calling
|
|
|
|
|
*sm_register_packet_handler*.
|
|
|
|
|
|
|
|
|
|
The default SMP configuration in BTstack is to be as open as possible:
|
|
|
|
|
|
|
|
|
|
- accept all Short Term Key (STK) Generation methods,
|
|
|
|
|
|
|
|
|
|
- accept encryption key size from 7..16 bytes,
|
|
|
|
|
|
|
|
|
|
- expect no authentication requirements, and
|
|
|
|
|
|
|
|
|
|
- IO Capabilities set to *IO_CAPABILITY_NO_INPUT_NO_OUTPUT*.
|
|
|
|
|
|
|
|
|
|
You can configure these items by calling following functions
|
|
|
|
|
respectively:
|
|
|
|
|
|
|
|
|
|
- *sm_set_accepted_stk_generation_methods*
|
|
|
|
|
|
|
|
|
|
- *sm_set_encryption_key_size_range*
|
|
|
|
|
|
|
|
|
|
- *sm_set_authentication_requirements*
|
|
|
|
|
|
|
|
|
|
- *sm_set_io_capabilities*
|
|
|
|
|
|
|
|
|
|
### Identity Resolving
|
|
|
|
|
|
|
|
|
|
Identity resolving is the process of matching a private, resolvable
|
|
|
|
|
Bluetooth address to a previously paired device using its Identity
|
|
|
|
|
Resolving (IR) key. After an LE connection gets established, BTstack
|
|
|
|
|
automatically tries to resolve the address of this device. During this
|
|
|
|
|
lookup, BTstack will emit the following events:
|
|
|
|
|
|
2016-01-30 23:58:36 +01:00
|
|
|
|
- *SM_EVENT_IDENTITY_RESOLVING_STARTED* to mark the start of a lookup,
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
and later:
|
|
|
|
|
|
2016-01-30 23:58:36 +01:00
|
|
|
|
- *SM_EVENT_IDENTITY_RESOLVING_SUCCEEDED* on lookup success, or
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2016-01-30 23:58:36 +01:00
|
|
|
|
- *SM_EVENT_IDENTITY_RESOLVING_FAILED* on lookup failure.
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
|
|
|
|
### Bonding process
|
|
|
|
|
|
|
|
|
|
In Bluetooth LE, there are three main methods of establishing an
|
|
|
|
|
encrypted connection. From the most to the least secure, these are:
|
|
|
|
|
Out-of-Band (OOB) Data , Passkey, and Just Works.
|
|
|
|
|
|
|
|
|
|
With OOB data, there needs to be a pre-shared secret 16 byte key. In
|
|
|
|
|
most cases, this is not an option, especially since popular OS like iOS
|
|
|
|
|
don’t provide a way to specify it. It some applications, where both
|
|
|
|
|
sides of a Bluetooth link are developed together, this could provide a
|
|
|
|
|
viable option.
|
|
|
|
|
|
|
|
|
|
To provide OOB data, you can register an OOB data callback with
|
|
|
|
|
*sm_register_oob_data_callback*.
|
|
|
|
|
|
|
|
|
|
Depending on the authentication requirements, available OOB data, and
|
|
|
|
|
the enabled STK generation methods, BTstack will request feedback from
|
|
|
|
|
the app in the form of an event:
|
|
|
|
|
|
2016-01-30 23:58:36 +01:00
|
|
|
|
- *SM_EVENT_PASSKEY_INPUT_NUMBER*: request user to input a passkey
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2016-01-30 23:58:36 +01:00
|
|
|
|
- *SM_EVENT_PASSKEY_DISPLAY_NUMBER*: show a passkey to the user
|
2015-05-05 00:21:33 +02:00
|
|
|
|
|
2016-01-30 23:58:36 +01:00
|
|
|
|
- *SM_EVENT_JUST_WORKS_REQUEST*: request a user to accept a Just Works
|
2015-05-05 00:21:33 +02:00
|
|
|
|
pairing
|
|
|
|
|
|
|
|
|
|
To stop the bonding process, *sm_bonding_decline* should be called.
|
|
|
|
|
Otherwise, *sm_just_works_confirm* or *sm_passkey_input* can be
|
|
|
|
|
called.
|
|
|
|
|
|
2016-01-30 23:58:36 +01:00
|
|
|
|
After the bonding process, *SM_EVENT_PASSKEY_DISPLAY_CANCEL* is emitted to
|
2015-05-05 00:21:33 +02:00
|
|
|
|
update the user interface.
|