This commit is contained in:
Milanka Ringwald 2016-01-20 16:11:10 +01:00
commit a9093a8aec
7 changed files with 276 additions and 81 deletions

View File

@ -5,8 +5,8 @@ It is well suited for small, resource-constraint devices
such as 8 or 16 bit embedded systems as it is highly configurable and comes with an ultra small memory footprint.
A minimal configuration for an SPP server on a MSP430 can run in 32 kB FLASH and only 4 kB of RAM.
It connects to the Bluetooth modules via different Bluetooth HCI transport layers (e.g., HCI H4 UART and
H5 the "Tree-Wire" protocol). The various platforms can be easily targeted by providing the necessary
It connects to the Bluetooth modules via a different Bluetooth HCI transport layers (e.g., HCI H4 UART and
H5 the "Tree-Wire" protocol, HCI H2 USB). Various platforms can be easily targeted by providing the necessary
UART, CPU, and CLOCK implementations.
On smaller embedded systems, a minimal run loop implementation allows to use BTstack without a Real Time OS (RTOS).
@ -15,17 +15,15 @@ If a RTOS is already provided, BTstack can be integrated and run as a single thr
On larger systems, BTstack provides a daemon that connects to a Bluetooth module.
Multiple applications can communicate with this daemon over different inter-process communication methods.
BTstack supports both, the Central and the Peripheral Role of Bluetooth 4.0 Low Energy specification.
BTstack supports both, the Central and the Peripheral Role of Bluetooth 4.2 Low Energy specification.
It can be configures as both a single mode or a dual mode stack.
BTstack is free for non-commercial use. For commercial use, <a href="mailto:contact@bluekitchen-gmbh.com">tell us</a>
a bit about your project to get a quote.
The Serial Port Profile (SPP) and the Bluetooth 4.0 Low Energy Peripheral role (LE Peripheral) have been qualified with
the Bluetooth SIG (QD ID 54558). This summer, we plan to qualify for Bluetooth Core 4.2,
together with LE Central, PAN/BNEP and HSP.
It has been qualified with the the Bluetooth SIG for GAP, IOP, HFP, HSP, SPP, PAN profiles and
GATT, SM of the Bluetooth 4.2 LE Central and Peripheral roles (QD ID 25340).
## Documentation
For starters, check the BTstack Manual
- [HTML](http://bluekitchen-gmbh.com/btstack/)
- [PDF](http://bluekitchen-gmbh.com/btstack.pdf)
@ -41,11 +39,13 @@ For starters, check the BTstack Manual
## Supported Profiles
* GAP
* IOP
* HFP
* HSP
* SPP
* PAN
* GATT
Coming soon: HSP, HFP, and more.
Coming next: HID, HOGP, A2DP, and more.
## Evaluation Platforms
@ -73,13 +73,13 @@ Status | Platform
## Supported Chipsets
Chipsets | Status
-------------- | ------
TI CC256x, WL183x | complete incl. eHCIll support (chipset-cc256x)
CSR 8811, 8510 | H4 only (chipset-csr)
TI CC256x, WL183x | complete incl. eHCIll support and SCO-over-HCI (chipset-cc256x)
CSR 8x10, 8x11 | H4 only (chipset-csr), SCO-over-HCI missing
STM STLC2500D | working, no support for custom deep sleep management (chipset-stlc2500d)
TC35661 | working, BLE patches missing (chipset-tc3566x)
EM 9301 | experimental use on Arduino Shield (chipset-em9301)
CSR USB Dongles | complete
Broadcom USB Dongles | complete
EM 9301 (LE-only) | working, used on Arduino Shield (chipset-em9301)
CSR USB Dongles | complete, incl. SCO-over-HCI
Broadcom USB Dongles | complete, SCO-over-HCI not working
## Discussion and Community Support
[BTstack Google Group](http://groups.google.com/group/btstack-dev)

View File

@ -441,6 +441,49 @@ def parseCharacteristicAggregateFormat(fout, parts):
fout.write("\n")
handle = handle + 1
def parseReportReference(fout, parts):
global handle
global total_size
property_read = property_flags['READ'];
size = 2 + 2 + 2 + 2 + 1 + 1
report_id = parts[1]
report_type = parts[2]
write_indent(fout)
fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
write_indent(fout)
write_16(fout, size)
write_16(fout, property_read)
write_16(fout, handle)
write_16(fout, 0x2908)
write_sequence(fout, report_id)
write_sequence(fout, report_type)
fout.write("\n")
handle = handle + 1
def parseNumberOfDigitals(fout, parts):
global handle
global total_size
property_read = property_flags['READ'];
size = 2 + 2 + 2 + 2 + 1
no_of_digitals = parts[1]
write_indent(fout)
fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
write_indent(fout)
write_16(fout, size)
write_16(fout, property_read)
write_16(fout, handle)
write_16(fout, 0x2909)
write_sequence(fout, no_of_digitals)
fout.write("\n")
handle = handle + 1
def parse(fname_in, fin, fname_out, fout):
global handle
@ -478,26 +521,81 @@ def parse(fname_in, fin, fname_out, fout):
parseIncludeService(fout, parts)
continue
# 2803
if parts[0] == 'CHARACTERISTIC':
parseCharacteristic(fout, parts)
continue
# 2900 Characteristic Extended Properties
# 2901
if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
parseCharacteristicUserDescription(fout, parts)
continue
# 2902 Client Characteristic Configuration - included in Characteristic if
# notification / indication is supported
# 2903
if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
parseServerCharacteristicConfiguration(fout, parts)
continue
# 2904
if parts[0] == 'CHARACTERISTIC_FORMAT':
parseCharacteristicFormat(fout, parts)
continue
# 2905
if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
parseCharacteristicAggregateFormat(fout, parts)
continue
# 2906
if parts[0] == 'VALID_RANGE':
print("WARNING: %s not implemented yet\n" % (parts[0]))
continue
# 2907
if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
print("WARNING: %s not implemented yet\n" % (parts[0]))
continue
# 2908
if parts[0] == 'REPORT_REFERENCE':
parseReportReference(fout, parts)
continue
# 2909
if parts[0] == 'NUMBER_OF_DIGITALS':
parseNumberOfDigitals(fout, parts)
continue
# 290A
if parts[0] == 'VALUE_TRIGGER_SETTING':
print("WARNING: %s not implemented yet\n" % (parts[0]))
continue
# 290B
if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
print("WARNING: %s not implemented yet\n" % (parts[0]))
continue
# 290C
if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
print("WARNING: %s not implemented yet\n" % (parts[0]))
continue
# 290D
if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
print("WARNING: %s not implemented yet\n" % (parts[0]))
continue
# 2906
if parts[0] == 'VALID_RANGE':
print("WARNING: %s not implemented yet\n" % (parts[0]))
continue
print("WARNING: unknown token: %s\n" % (parts[0]))
write_indent(fout)

View File

@ -43,12 +43,19 @@
//
// Tested working setups:
// - Ubuntu 14 64-bit, CC2564B connected via FTDI USB-2-UART adapter, 921600 baud
//
// Non working setups:
// - Ubuntu 14 64-bit, CSR dongle
// - OS X 10.11, CSR dongle
// Broken setups:
// - OS X 10.11, CC2564B connected via FDTI USB-2-UART adapter, 921600 baud
// - select(..) blocks > 400 ms -> num completed is received to late -> gaps between audio
// - looks like bug in select->FTDI driver as it works correct on Linux
// - OS X 10.11, USB Bluetooth Dongles...
//
// SCO not routed over HCI (yet)
// - CSR UART dongle
// - Broadcom USB dongle
// - Broadcom UART chipset
// - ..
// *****************************************************************************
#include "btstack-config.h"
@ -115,20 +122,24 @@ static void try_send_sco(void){
// printf("try_send_sco, cannot send now\n");
return;
}
const int frames_per_packet = 180;
const int sco_packet_length = hci_get_sco_packet_length();
const int sco_payload_length = sco_packet_length - 3;
const int frames_per_packet = sco_payload_length; // for 8-bit data. for 16-bit data it's /2
hci_reserve_packet_buffer();
uint8_t * sco_packet = hci_get_outgoing_packet_buffer();
// set handle + flags
bt_store_16(sco_packet, 0, sco_handle);
// set len
sco_packet[2] = frames_per_packet;
sco_packet[2] = sco_payload_length;
int i;
for (i=0;i<frames_per_packet;i++){
sco_packet[3+i] = sine[phase];
phase++;
if (phase >= sizeof(sine)) phase = 0;
}
hci_send_sco_packet_buffer(3 + frames_per_packet);
hci_send_sco_packet_buffer(sco_packet_length);
static int count = 0;
count++;
if ((count & 15) == 0) printf("Sent %u\n", count);
@ -216,6 +227,10 @@ int btstack_main(int argc, const char * argv[]){
compute_signal();
#endif
// 8-bit, 2's complement (== int8_t)
// 16-bit samples probably required for USB:
// hci_set_sco_voice_setting(0x0060);
hci_register_sco_packet_handler(&sco_packet_handler);
hci_discoverable_control(1);

View File

@ -21,6 +21,7 @@
#define HAVE_HCI_DUMP
#define SDP_DES_DUMP
// #define HAVE_SCO
#define HAVE_SCO
#define HAVE_SCO_OVER_HCI
#endif

View File

@ -65,16 +65,44 @@
#include "hci.h"
#include "hci_transport.h"
// #define HAVE_SCO
#if (USB_VENDOR_ID != 0) && (USB_PRODUCT_ID != 0)
#define HAVE_USB_VENDOR_ID_AND_PRODUCT_ID
#endif
#define ASYNC_BUFFERS 2
#define AYSNC_POLLING_INTERVAL_MS 1
//
// Bluetooth USB Transprot Alternate Settings:
//
// 0: No active voice channels (for USB compliance)
// 1: One 8 kHz voice channel with 8-bit encoding
// 2: Two 8 kHz voice channels with 8-bit encoding or one 8 kHz voice channel with 16-bit encoding
// 3: Three 8 kHz voice channels with 8-bit encoding
// 4: Two 8 kHz voice channels with 16-bit encoding or one 16 kHz voice channel with 16-bit encoding
// 5: Three 8 kHz voice channels with 16-bit encoding or one 8 kHz voice channel with 16-bit encoding and one 16 kHz voice channel with 16-bit encoding
// --> support only a single SCO connection
#define ALT_SETTING (2)
// for ALT_SETTING >= 1 and 8-bit channel, we need the following isochronous packets
// One complete SCO packet with 24 frames every 3 frames (== 3 ms)
#define NUM_ISO_PACKETS (3)
// results in 9 bytes per frame
#define ISO_PACKET_SIZE (9)
// 49 bytes is the max usb packet size for alternate setting 5 (Three 8 kHz 16-bit channels or one 8 kHz 16-bit channel and one 16 kHz 16-bit channel)
// note: alt setting 6 has max packet size of 63 every 7.5 ms = 472.5 bytes / HCI packet, while max SCO packet has 255 byte payload
#define SCO_PACKET_SIZE (49)
// Outgoing SCO packet queue
// simplified ring buffer implementation
#define SCO_RING_BUFFER_COUNT (8)
#define SCO_RING_BUFFER_SIZE (SCO_RING_BUFFER_COUNT * SCO_PACKET_SIZE)
// prototypes
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
static int usb_close(void *transport_config);
static int usb_close(void *transport_config);
typedef enum {
LIB_USB_CLOSED = 0,
LIB_USB_OPENED,
@ -103,28 +131,32 @@ static libusb_device * dev;
#endif
static libusb_device_handle * handle;
#define ASYNC_BUFFERS 2
#define AYSNC_POLLING_INTERVAL_MS 1
#define NUM_ISO_PACKETS 16
#define SCO_PACKET_SIZE 49
static struct libusb_transfer *command_out_transfer;
static struct libusb_transfer *acl_out_transfer;
static struct libusb_transfer *event_in_transfer[ASYNC_BUFFERS];
static struct libusb_transfer *acl_in_transfer[ASYNC_BUFFERS];
#ifdef HAVE_SCO
// incoming SCO
static H2_SCO_STATE sco_state;
static uint8_t sco_buffer[255+3 + SCO_PACKET_SIZE];
static uint16_t sco_read_pos;
static uint16_t sco_bytes_to_read;
#ifdef HAVE_SCO
static struct libusb_transfer *sco_out_transfer;
static struct libusb_transfer *sco_in_transfer[ASYNC_BUFFERS];
static uint8_t hci_sco_in_buffer[ASYNC_BUFFERS][NUM_ISO_PACKETS * SCO_PACKET_SIZE];
static uint8_t hci_sco_in_buffer[ASYNC_BUFFERS][SCO_PACKET_SIZE];
// outgoing SCO
static uint8_t sco_ring_buffer[SCO_RING_BUFFER_SIZE];
static int sco_ring_write; // packet idx
static int sco_ring_transfers_active;
static struct libusb_transfer *sco_ring_transfers[SCO_RING_BUFFER_COUNT];
#endif
// outgoing buffer for HCI Command packets
static uint8_t hci_cmd_buffer[3 + 256 + LIBUSB_CONTROL_SETUP_SIZE];
// incoming buffer for HCI Events and ACL Packets
static uint8_t hci_event_in_buffer[ASYNC_BUFFERS][HCI_ACL_BUFFER_SIZE]; // bigger than largest packet
static uint8_t hci_acl_in_buffer[ASYNC_BUFFERS][HCI_INCOMING_PRE_BUFFER_SIZE + HCI_ACL_BUFFER_SIZE];
@ -138,7 +170,6 @@ static timer_source_t usb_timer;
static int usb_timer_active;
static int usb_acl_out_active = 0;
static int usb_sco_out_active = 0;
static int usb_command_active = 0;
// endpoint addresses
@ -149,6 +180,17 @@ static int sco_in_addr;
static int sco_out_addr;
static void sco_ring_init(void){
sco_ring_write = 0;
sco_ring_transfers_active = 0;
}
static int sco_ring_have_space(void){
return sco_ring_transfers_active < SCO_RING_BUFFER_COUNT;
}
//
static void queue_transfer(struct libusb_transfer *transfer){
// log_info("queue_transfer %p, endpoint %x size %u", transfer, transfer->endpoint, transfer->actual_length);
@ -198,6 +240,48 @@ static void async_callback(struct libusb_transfer *transfer)
// log_info("end async_callback");
}
static int usb_send_sco_packet(uint8_t *packet, int size){
#ifdef HAVE_SCO
int r;
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return -1;
// log_info("usb_send_acl_packet enter, size %u", size);
// store packet in free slot
int tranfer_index = sco_ring_write;
uint8_t * data = &sco_ring_buffer[tranfer_index * SCO_PACKET_SIZE];
memcpy(data, packet, size);
// setup transfer
struct libusb_transfer * sco_transfer = sco_ring_transfers[tranfer_index];
libusb_fill_iso_transfer(sco_transfer, handle, sco_out_addr, data, size, NUM_ISO_PACKETS, async_callback, NULL, 0);
libusb_set_iso_packet_lengths(sco_transfer, ISO_PACKET_SIZE);
r = libusb_submit_transfer(sco_transfer);
if (r < 0) {
log_error("Error submitting sco transfer, %d", r);
return -1;
}
// mark slot as full
sco_ring_write++;
if (sco_ring_write == SCO_RING_BUFFER_COUNT){
sco_ring_write = 0;
}
sco_ring_transfers_active++;
// log_info("H2: queued packet at index %u, num active %u", tranfer_index, sco_ring_transfers_active);
// notify upper stack that packet processed and that it might be possible to send again
if (sco_ring_have_space()){
uint8_t event[] = { DAEMON_EVENT_HCI_PACKET_SENT, 0};
packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event));
}
#endif
return 0;
}
static void sco_state_machine_init(void){
sco_state = H2_W4_SCO_HEADER;
sco_read_pos = 0;
@ -247,7 +331,7 @@ static void handle_completed_transfer(struct libusb_transfer *transfer){
packet_handler(HCI_ACL_DATA_PACKET, transfer-> buffer, transfer->actual_length);
resubmit = 1;
} else if (transfer->endpoint == sco_in_addr) {
// log_info("handle_completed_transfer for SCO IN! num packets %u", transfer->num_iso_packets);
// log_info("handle_completed_transfer for SCO IN! num packets %u", transfer->NUM_ISO_PACKETS);
int i;
for (i = 0; i < transfer->num_iso_packets; i++) {
struct libusb_iso_packet_descriptor *pack = &transfer->iso_packet_desc[i];
@ -258,7 +342,7 @@ static void handle_completed_transfer(struct libusb_transfer *transfer){
if (!pack->actual_length) continue;
uint8_t * data = libusb_get_iso_packet_buffer_simple(transfer, i);
// printf_hexdump(data, pack->actual_length);
// log_debug("handle_isochronous_data,size %u/%u", pack->length, pack->actual_length);
// log_info("handle_isochronous_data,size %u/%u", pack->length, pack->actual_length);
handle_isochronous_data(data, pack->actual_length);
}
resubmit = 1;
@ -271,10 +355,17 @@ static void handle_completed_transfer(struct libusb_transfer *transfer){
usb_acl_out_active = 0;
signal_done = 1;
} else if (transfer->endpoint == sco_out_addr){
log_info("sco out done, size %u/%u - status %x", transfer->actual_length,
transfer->iso_packet_desc[0].actual_length, transfer->iso_packet_desc[0].status);
usb_sco_out_active = 0;
signal_done = 1;
log_info("sco out done, {{ %u/%u (%x)}, { %u/%u (%x)}, { %u/%u (%x)}}",
transfer->iso_packet_desc[0].actual_length, transfer->iso_packet_desc[0].length, transfer->iso_packet_desc[0].status,
transfer->iso_packet_desc[1].actual_length, transfer->iso_packet_desc[1].length, transfer->iso_packet_desc[1].status,
transfer->iso_packet_desc[2].actual_length, transfer->iso_packet_desc[2].length, transfer->iso_packet_desc[2].status);
if (!sco_ring_have_space()) {
// if there isn't space, the last SCO send didn't emit a packet sent event
signal_done = 1;
}
// decrease tab
sco_ring_transfers_active--;
// log_info("H2: sco out complete, num active num active %u", sco_ring_transfers_active);
} else {
log_info("usb_process_ds endpoint unknown %x", transfer->endpoint);
}
@ -527,9 +618,10 @@ static int prepare_device(libusb_device_handle * aHandle){
libusb_close(aHandle);
return r;
}
r = libusb_set_interface_alt_setting(aHandle, 1, 5); // 3 x 8 kHz voice channels
log_info("Switching to setting %u on interface 1..", ALT_SETTING);
r = libusb_set_interface_alt_setting(aHandle, 1, ALT_SETTING);
if (r < 0) {
fprintf(stderr, "Error setting alternative setting 5 for interface 1: %s\n", libusb_error_name(r));
fprintf(stderr, "Error setting alternative setting %u for interface 1: %s\n", ALT_SETTING, libusb_error_name(r));
libusb_close(aHandle);
return r;
}
@ -542,6 +634,7 @@ static int usb_open(void *transport_config){
int r;
sco_state_machine_init();
sco_ring_init();
handle_packet = NULL;
// default endpoint addresses
@ -666,6 +759,7 @@ static int usb_open(void *transport_config){
#ifdef HAVE_SCO
// incoming
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
sco_in_transfer[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // isochronous transfers SCO in
log_info("Alloc iso transfer");
@ -673,14 +767,10 @@ static int usb_open(void *transport_config){
usb_close(handle);
return LIBUSB_ERROR_NO_MEM;
}
// configure sco_in handlers
// configure sco_in handlers
libusb_fill_iso_transfer(sco_in_transfer[c], handle, sco_in_addr,
hci_sco_in_buffer[c], NUM_ISO_PACKETS * SCO_PACKET_SIZE, NUM_ISO_PACKETS, async_callback, NULL, 0) ;
libusb_set_iso_packet_lengths(sco_in_transfer[c], SCO_PACKET_SIZE);
// one of the following is relevant! find out which one
sco_in_transfer[c]->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
sco_in_transfer[c]->num_iso_packets = NUM_ISO_PACKETS;
// sco_in_transfer[c]->iso_packet_desc[0].length = 300;
hci_sco_in_buffer[c], SCO_PACKET_SIZE, NUM_ISO_PACKETS, async_callback, NULL, 0);
libusb_set_iso_packet_lengths(sco_in_transfer[c], ISO_PACKET_SIZE);
r = libusb_submit_transfer(sco_in_transfer[c]);
log_info("Submit iso transfer res = %d", r);
if (r) {
@ -689,7 +779,11 @@ static int usb_open(void *transport_config){
return r;
}
}
sco_out_transfer = libusb_alloc_transfer(1); // 1 isochronous transfers SCO out
// outgoing
for (c=0; c < SCO_RING_BUFFER_COUNT ; c++){
sco_ring_transfers[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // 1 isochronous transfers SCO out - up to 3 parts
}
#endif
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
@ -876,35 +970,6 @@ static int usb_send_acl_packet(uint8_t *packet, int size){
return 0;
}
static int usb_send_sco_packet(uint8_t *packet, int size){
#ifdef HAVE_SCO
int r;
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return -1;
// log_info("usb_send_acl_packet enter, size %u", size);
// prepare transfer
int completed = 0;
libusb_fill_iso_transfer(sco_out_transfer, handle, sco_out_addr, packet, size, 1,
async_callback, &completed, 0);
libusb_set_iso_packet_lengths(sco_out_transfer, size);
sco_out_transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
sco_out_transfer->iso_packet_desc[0].length = size;
// update state before submitting transfer
usb_sco_out_active = 1;
r = libusb_submit_transfer(sco_out_transfer);
if (r < 0) {
usb_sco_out_active = 0;
log_error("Error submitting sco transfer, %d", r);
return -1;
}
#endif
return 0;
}
static int usb_can_send_packet_now(uint8_t packet_type){
switch (packet_type){
case HCI_COMMAND_DATA_PACKET:
@ -912,7 +977,7 @@ static int usb_can_send_packet_now(uint8_t packet_type){
case HCI_ACL_DATA_PACKET:
return !usb_acl_out_active;
case HCI_SCO_DATA_PACKET:
return !usb_sco_out_active;
return sco_ring_have_space();
default:
return 0;
}

View File

@ -3458,6 +3458,16 @@ uint16_t hci_get_sco_voice_setting(){
return hci_stack->sco_voice_setting;
}
/** @brief Get SCO packet length for current SCO Voice setting
* @note Using SCO packets of the exact length is required for USB transfer
* @return Length of SCO packets in bytes (not audio frames)
*/
int hci_get_sco_packet_length(void){
// see Core Spec for H2 USB Transfer.
if (hci_stack->sco_voice_setting & 0x0020) return 51;
return 27;
}
/**
* @brief Set callback for Bluetooth Hardware Error
*/

View File

@ -970,6 +970,12 @@ void hci_set_sco_voice_setting(uint16_t voice_setting);
*/
uint16_t hci_get_sco_voice_setting(void);
/** @brief Get SCO packet length for current SCO Voice setting
* @note Using SCO packets of the exact length is required for USB transfer
* @return Length of SCO packets in bytes (not audio frames) incl. 3 byte header
*/
int hci_get_sco_packet_length(void);
/* API_END */
/**