From df630c49fcbcfe9b6ef11e74809232c969e2ec34 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 15 Jan 2016 11:39:01 +0100 Subject: [PATCH 01/12] libusb: enable sco --- platforms/libusb/btstack-config.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platforms/libusb/btstack-config.h b/platforms/libusb/btstack-config.h index 05498edb5..b0436c300 100644 --- a/platforms/libusb/btstack-config.h +++ b/platforms/libusb/btstack-config.h @@ -21,6 +21,7 @@ #define HAVE_HCI_DUMP #define SDP_DES_DUMP -// #define HAVE_SCO +#define HAVE_SCO +#define HAVE_SCO_OVER_HCI #endif From 0bddbee1baadc5b9c53becd4607b5bf1fd55e535 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 15 Jan 2016 22:11:56 +0100 Subject: [PATCH 02/12] libusb: rewrite SCO sending to keep mutiple USB transfers queued --- platforms/posix/src/hci_transport_h2_libusb.c | 130 ++++++++++++------ 1 file changed, 90 insertions(+), 40 deletions(-) diff --git a/platforms/posix/src/hci_transport_h2_libusb.c b/platforms/posix/src/hci_transport_h2_libusb.c index c236e0295..87903323e 100644 --- a/platforms/posix/src/hci_transport_h2_libusb.c +++ b/platforms/posix/src/hci_transport_h2_libusb.c @@ -106,20 +106,23 @@ 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 +#define SCO_PACKET_SIZE 9 +// does not work with size >= 50 - why? Module reports 8 x 64 SCO packets +// alternate setting "recommends" 49 bytes 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]; +// incoming SCO packets 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]; #endif @@ -138,7 +141,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 @@ -148,7 +150,28 @@ static int acl_out_addr; static int sco_in_addr; static int sco_out_addr; +// outgoing SCO packets (4 > 2, 64 is size of SCO buffer in PTS Dongle) +// simplified ring buffer implementation +#define SCO_RING_PACKET_SIZE (64) +#define SCO_RING_BUFFER_COUNT (8) +#define SCO_RING_BUFFER_SIZE (SCO_RING_BUFFER_COUNT * SCO_RING_PACKET_SIZE) +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]; + +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 +221,52 @@ 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_RING_PACKET_SIZE]; + memcpy(&sco_ring_buffer[sco_ring_write * SCO_RING_PACKET_SIZE], data, size); + + // setup transfer + struct libusb_transfer * sco_transfer = sco_ring_transfers[tranfer_index]; + + // hard-coded for now: num_iso_packets = 3, packet len = 9 + int num_iso_packets = 3; + int iso_packet_size = 9; + 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; @@ -258,7 +327,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 +340,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,7 +603,7 @@ 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 + r = libusb_set_interface_alt_setting(aHandle, 1, 1); // 1 x 8 kHz voice channels, 8 bit if (r < 0) { fprintf(stderr, "Error setting alternative setting 5 for interface 1: %s\n", libusb_error_name(r)); libusb_close(aHandle); @@ -542,6 +618,7 @@ static int usb_open(void *transport_config){ int r; sco_state_machine_init(); + sco_ring_init(); handle_packet = NULL; // default endpoint addresses @@ -689,7 +766,9 @@ static int usb_open(void *transport_config){ return r; } } - sco_out_transfer = libusb_alloc_transfer(1); // 1 isochronous transfers SCO out + for (c=0; c < SCO_RING_BUFFER_COUNT ; c++){ + sco_ring_transfers[c] = libusb_alloc_transfer(3); // 1 isochronous transfers SCO out - up to 3 parts + } #endif for (c = 0 ; c < ASYNC_BUFFERS ; c++) { @@ -876,35 +955,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 +962,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; } From 0aa6c1da660648af30c696738b013a93bc819c3e Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 15 Jan 2016 22:41:42 +0100 Subject: [PATCH 03/12] libusb: document alternate settings for interface 1 --- platforms/posix/src/hci_transport_h2_libusb.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/platforms/posix/src/hci_transport_h2_libusb.c b/platforms/posix/src/hci_transport_h2_libusb.c index 87903323e..102d45042 100644 --- a/platforms/posix/src/hci_transport_h2_libusb.c +++ b/platforms/posix/src/hci_transport_h2_libusb.c @@ -603,9 +603,18 @@ static int prepare_device(libusb_device_handle * aHandle){ libusb_close(aHandle); return r; } - r = libusb_set_interface_alt_setting(aHandle, 1, 1); // 1 x 8 kHz voice channels, 8 bit + // 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 + int alt_setting = 1; + 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; } From 8961322356d37c03f7c77e53940b2f96ed94ba61 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 15 Jan 2016 23:14:15 +0100 Subject: [PATCH 04/12] libusb: use passed in SCO packet instead of empty buffer --- platforms/posix/src/hci_transport_h2_libusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/posix/src/hci_transport_h2_libusb.c b/platforms/posix/src/hci_transport_h2_libusb.c index 102d45042..14b247ed3 100644 --- a/platforms/posix/src/hci_transport_h2_libusb.c +++ b/platforms/posix/src/hci_transport_h2_libusb.c @@ -233,7 +233,7 @@ static int usb_send_sco_packet(uint8_t *packet, int size){ // store packet in free slot int tranfer_index = sco_ring_write; uint8_t * data = &sco_ring_buffer[tranfer_index * SCO_RING_PACKET_SIZE]; - memcpy(&sco_ring_buffer[sco_ring_write * SCO_RING_PACKET_SIZE], data, size); + memcpy(data, packet, size); // setup transfer struct libusb_transfer * sco_transfer = sco_ring_transfers[tranfer_index]; From 800f1352ca078ff76abb61cc10523763986a518c Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 15 Jan 2016 23:15:52 +0100 Subject: [PATCH 05/12] use recommended SCO packet size of 24 frames for single channel --- example/embedded/hsp_hs_test.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/example/embedded/hsp_hs_test.c b/example/embedded/hsp_hs_test.c index 567e4b906..2a653e782 100644 --- a/example/embedded/hsp_hs_test.c +++ b/example/embedded/hsp_hs_test.c @@ -115,7 +115,7 @@ static void try_send_sco(void){ // printf("try_send_sco, cannot send now\n"); return; } - const int frames_per_packet = 180; + const int frames_per_packet = 24; hci_reserve_packet_buffer(); uint8_t * sco_packet = hci_get_outgoing_packet_buffer(); // set handle + flags @@ -216,6 +216,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); From d5422c82dcdf4733904ac6ba98b439707c24f45f Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Sat, 16 Jan 2016 21:49:21 +0100 Subject: [PATCH 06/12] libusb: use alt setting 2 for one 8-bit or 16-bit voice channel --- platforms/posix/src/hci_transport_h2_libusb.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/platforms/posix/src/hci_transport_h2_libusb.c b/platforms/posix/src/hci_transport_h2_libusb.c index 14b247ed3..4983a5e3b 100644 --- a/platforms/posix/src/hci_transport_h2_libusb.c +++ b/platforms/posix/src/hci_transport_h2_libusb.c @@ -65,8 +65,6 @@ #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 @@ -150,9 +148,10 @@ static int acl_out_addr; static int sco_in_addr; static int sco_out_addr; -// outgoing SCO packets (4 > 2, 64 is size of SCO buffer in PTS Dongle) +// Outgoing SCO packet queue +// 49 bytes is the max usb packet size for alternate setting 5 (3 8-bit channels or 1 8-bit and 1 16-bit channel) // simplified ring buffer implementation -#define SCO_RING_PACKET_SIZE (64) +#define SCO_RING_PACKET_SIZE (49) #define SCO_RING_BUFFER_COUNT (8) #define SCO_RING_BUFFER_SIZE (SCO_RING_BUFFER_COUNT * SCO_RING_PACKET_SIZE) @@ -603,14 +602,16 @@ static int prepare_device(libusb_device_handle * aHandle){ libusb_close(aHandle); return r; } + // // 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 - int alt_setting = 1; + int alt_setting = 2; // <-- we support only a single SCO connection log_info("Switching to setting %u on interface 1..", alt_setting); r = libusb_set_interface_alt_setting(aHandle, 1, alt_setting); if (r < 0) { From b3aad8da607fb2d110206e6b1b3950dae8f50ca1 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Sat, 16 Jan 2016 21:57:34 +0100 Subject: [PATCH 07/12] SCO: add hci_get_sco_packet_length to query SCO packet size needed for USB and use in hsp_hs_test --- example/embedded/hsp_hs_test.c | 23 +++++++++++++++++------ src/hci.c | 10 ++++++++++ src/hci.h | 6 ++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/example/embedded/hsp_hs_test.c b/example/embedded/hsp_hs_test.c index 2a653e782..8abc5b948 100644 --- a/example/embedded/hsp_hs_test.c +++ b/example/embedded/hsp_hs_test.c @@ -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 = 24; + + 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= 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); diff --git a/src/hci.c b/src/hci.c index c0d3ef118..5d4241047 100644 --- a/src/hci.c +++ b/src/hci.c @@ -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 */ diff --git a/src/hci.h b/src/hci.h index 99cbf7bba..48b3cab05 100644 --- a/src/hci.h +++ b/src/hci.h @@ -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 */ /** From 6d9dc928851542a4ec486e0d14a942b617e22182 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Mon, 18 Jan 2016 11:05:00 +0100 Subject: [PATCH 08/12] add 2015 qualifcation --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 0f5a58448..d0909a9b8 100644 --- a/README.md +++ b/README.md @@ -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, tell us 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) From cf35e31f00b1eac3c970ced7108907f9658c09d3 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Mon, 18 Jan 2016 13:58:34 +0100 Subject: [PATCH 09/12] libusb: cleanup SCO code --- platforms/posix/src/hci_transport_h2_libusb.c | 113 +++++++++--------- 1 file changed, 59 insertions(+), 54 deletions(-) diff --git a/platforms/posix/src/hci_transport_h2_libusb.c b/platforms/posix/src/hci_transport_h2_libusb.c index 4983a5e3b..5a117004e 100644 --- a/platforms/posix/src/hci_transport_h2_libusb.c +++ b/platforms/posix/src/hci_transport_h2_libusb.c @@ -69,10 +69,40 @@ #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, @@ -101,31 +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 9 -// does not work with size >= 50 - why? Module reports 8 x 64 SCO packets -// alternate setting "recommends" 49 bytes - 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]; -// incoming SCO packets +#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_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]; @@ -148,17 +179,6 @@ static int acl_out_addr; static int sco_in_addr; static int sco_out_addr; -// Outgoing SCO packet queue -// 49 bytes is the max usb packet size for alternate setting 5 (3 8-bit channels or 1 8-bit and 1 16-bit channel) -// simplified ring buffer implementation -#define SCO_RING_PACKET_SIZE (49) -#define SCO_RING_BUFFER_COUNT (8) -#define SCO_RING_BUFFER_SIZE (SCO_RING_BUFFER_COUNT * SCO_RING_PACKET_SIZE) - -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]; static void sco_ring_init(void){ sco_ring_write = 0; @@ -231,17 +251,13 @@ static int usb_send_sco_packet(uint8_t *packet, int size){ // store packet in free slot int tranfer_index = sco_ring_write; - uint8_t * data = &sco_ring_buffer[tranfer_index * SCO_RING_PACKET_SIZE]; + 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]; - - // hard-coded for now: num_iso_packets = 3, packet len = 9 - int num_iso_packets = 3; - int iso_packet_size = 9; - 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); + 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); @@ -315,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]; @@ -602,20 +618,10 @@ static int prepare_device(libusb_device_handle * aHandle){ libusb_close(aHandle); return r; } - // - // 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 - int alt_setting = 2; // <-- we support only a single SCO connection - log_info("Switching to setting %u on interface 1..", alt_setting); - r = libusb_set_interface_alt_setting(aHandle, 1, alt_setting); + 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 %u for interface 1: %s\n", alt_setting, 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; } @@ -753,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"); @@ -760,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) { @@ -776,8 +779,10 @@ static int usb_open(void *transport_config){ return r; } } + + // outgoing for (c=0; c < SCO_RING_BUFFER_COUNT ; c++){ - sco_ring_transfers[c] = libusb_alloc_transfer(3); // 1 isochronous transfers SCO out - up to 3 parts + sco_ring_transfers[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // 1 isochronous transfers SCO out - up to 3 parts } #endif From 917d42d5b9046d4507b996b38eb5f1b628ed3bf3 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Mon, 18 Jan 2016 21:21:32 +0100 Subject: [PATCH 10/12] gatt compiler: parse report reference --- ble/compile-gatt.py | 78 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/ble/compile-gatt.py b/ble/compile-gatt.py index 3b81b4391..3ae4b30d7 100755 --- a/ble/compile-gatt.py +++ b/ble/compile-gatt.py @@ -441,6 +441,27 @@ 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 parse(fname_in, fin, fname_out, fout): global handle @@ -478,26 +499,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': + print("WARNING: %s not implemented yet\n" % (parts[0])) + 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) From 1c18ad7f10ef858f13ffd98e1345054bfb0e1735 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Mon, 18 Jan 2016 21:31:45 +0100 Subject: [PATCH 11/12] gatt parser: parse number of digitals --- ble/compile-gatt.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ble/compile-gatt.py b/ble/compile-gatt.py index 3ae4b30d7..85fca7313 100755 --- a/ble/compile-gatt.py +++ b/ble/compile-gatt.py @@ -463,7 +463,26 @@ def parseReportReference(fout, parts): fout.write("\n") handle = handle + 1 -def parse(fname_in, fin, fname_out, fout): + +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 + 1def parse(fname_in, fin, fname_out, fout): global handle global total_size @@ -546,7 +565,7 @@ def parse(fname_in, fin, fname_out, fout): # 2909 if parts[0] == 'NUMBER_OF_DIGITALS': - print("WARNING: %s not implemented yet\n" % (parts[0])) + parseNumberOfDigitals(fout, parts) continue # 290A From 1eaab45cfe3aa7fd396ff3e2f3b8da04d5d59f73 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Tue, 19 Jan 2016 10:00:42 +0100 Subject: [PATCH 12/12] gatt compiler: typo --- ble/compile-gatt.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ble/compile-gatt.py b/ble/compile-gatt.py index 85fca7313..f33b1f614 100755 --- a/ble/compile-gatt.py +++ b/ble/compile-gatt.py @@ -482,7 +482,10 @@ def parseNumberOfDigitals(fout, parts): write_16(fout, 0x2909) write_sequence(fout, no_of_digitals) fout.write("\n") - handle = handle + 1def parse(fname_in, fin, fname_out, fout): + handle = handle + 1 + + +def parse(fname_in, fin, fname_out, fout): global handle global total_size