mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-01-27 06:35:20 +00:00
Merge branch 'master' of https://github.com/bluekitchen/btstack
This commit is contained in:
commit
a9093a8aec
26
README.md
26
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.
|
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.
|
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
|
It connects to the Bluetooth modules via a 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
|
H5 the "Tree-Wire" protocol, HCI H2 USB). Various platforms can be easily targeted by providing the necessary
|
||||||
UART, CPU, and CLOCK implementations.
|
UART, CPU, and CLOCK implementations.
|
||||||
|
|
||||||
On smaller embedded systems, a minimal run loop implementation allows to use BTstack without a Real Time OS (RTOS).
|
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.
|
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.
|
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.
|
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>
|
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.
|
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
|
It has been qualified with the the Bluetooth SIG for GAP, IOP, HFP, HSP, SPP, PAN profiles and
|
||||||
the Bluetooth SIG (QD ID 54558). This summer, we plan to qualify for Bluetooth Core 4.2,
|
GATT, SM of the Bluetooth 4.2 LE Central and Peripheral roles (QD ID 25340).
|
||||||
together with LE Central, PAN/BNEP and HSP.
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
For starters, check the BTstack Manual
|
|
||||||
- [HTML](http://bluekitchen-gmbh.com/btstack/)
|
- [HTML](http://bluekitchen-gmbh.com/btstack/)
|
||||||
- [PDF](http://bluekitchen-gmbh.com/btstack.pdf)
|
- [PDF](http://bluekitchen-gmbh.com/btstack.pdf)
|
||||||
|
|
||||||
@ -41,11 +39,13 @@ For starters, check the BTstack Manual
|
|||||||
## Supported Profiles
|
## Supported Profiles
|
||||||
* GAP
|
* GAP
|
||||||
* IOP
|
* IOP
|
||||||
|
* HFP
|
||||||
|
* HSP
|
||||||
* SPP
|
* SPP
|
||||||
* PAN
|
* PAN
|
||||||
* GATT
|
* GATT
|
||||||
|
|
||||||
Coming soon: HSP, HFP, and more.
|
Coming next: HID, HOGP, A2DP, and more.
|
||||||
|
|
||||||
## Evaluation Platforms
|
## Evaluation Platforms
|
||||||
|
|
||||||
@ -73,13 +73,13 @@ Status | Platform
|
|||||||
## Supported Chipsets
|
## Supported Chipsets
|
||||||
Chipsets | Status
|
Chipsets | Status
|
||||||
-------------- | ------
|
-------------- | ------
|
||||||
TI CC256x, WL183x | complete incl. eHCIll support (chipset-cc256x)
|
TI CC256x, WL183x | complete incl. eHCIll support and SCO-over-HCI (chipset-cc256x)
|
||||||
CSR 8811, 8510 | H4 only (chipset-csr)
|
CSR 8x10, 8x11 | H4 only (chipset-csr), SCO-over-HCI missing
|
||||||
STM STLC2500D | working, no support for custom deep sleep management (chipset-stlc2500d)
|
STM STLC2500D | working, no support for custom deep sleep management (chipset-stlc2500d)
|
||||||
TC35661 | working, BLE patches missing (chipset-tc3566x)
|
TC35661 | working, BLE patches missing (chipset-tc3566x)
|
||||||
EM 9301 | experimental use on Arduino Shield (chipset-em9301)
|
EM 9301 (LE-only) | working, used on Arduino Shield (chipset-em9301)
|
||||||
CSR USB Dongles | complete
|
CSR USB Dongles | complete, incl. SCO-over-HCI
|
||||||
Broadcom USB Dongles | complete
|
Broadcom USB Dongles | complete, SCO-over-HCI not working
|
||||||
|
|
||||||
## Discussion and Community Support
|
## Discussion and Community Support
|
||||||
[BTstack Google Group](http://groups.google.com/group/btstack-dev)
|
[BTstack Google Group](http://groups.google.com/group/btstack-dev)
|
||||||
|
@ -441,6 +441,49 @@ def parseCharacteristicAggregateFormat(fout, parts):
|
|||||||
fout.write("\n")
|
fout.write("\n")
|
||||||
handle = handle + 1
|
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):
|
def parse(fname_in, fin, fname_out, fout):
|
||||||
global handle
|
global handle
|
||||||
@ -478,26 +521,81 @@ def parse(fname_in, fin, fname_out, fout):
|
|||||||
parseIncludeService(fout, parts)
|
parseIncludeService(fout, parts)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 2803
|
||||||
if parts[0] == 'CHARACTERISTIC':
|
if parts[0] == 'CHARACTERISTIC':
|
||||||
parseCharacteristic(fout, parts)
|
parseCharacteristic(fout, parts)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 2900 Characteristic Extended Properties
|
||||||
|
|
||||||
|
# 2901
|
||||||
if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
|
if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
|
||||||
parseCharacteristicUserDescription(fout, parts)
|
parseCharacteristicUserDescription(fout, parts)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 2902 Client Characteristic Configuration - included in Characteristic if
|
||||||
|
# notification / indication is supported
|
||||||
|
|
||||||
|
# 2903
|
||||||
if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
|
if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
|
||||||
parseServerCharacteristicConfiguration(fout, parts)
|
parseServerCharacteristicConfiguration(fout, parts)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 2904
|
||||||
if parts[0] == 'CHARACTERISTIC_FORMAT':
|
if parts[0] == 'CHARACTERISTIC_FORMAT':
|
||||||
parseCharacteristicFormat(fout, parts)
|
parseCharacteristicFormat(fout, parts)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 2905
|
||||||
if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
|
if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
|
||||||
parseCharacteristicAggregateFormat(fout, parts)
|
parseCharacteristicAggregateFormat(fout, parts)
|
||||||
continue
|
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]))
|
print("WARNING: unknown token: %s\n" % (parts[0]))
|
||||||
|
|
||||||
write_indent(fout)
|
write_indent(fout)
|
||||||
|
@ -43,12 +43,19 @@
|
|||||||
//
|
//
|
||||||
// Tested working setups:
|
// Tested working setups:
|
||||||
// - Ubuntu 14 64-bit, CC2564B connected via FTDI USB-2-UART adapter, 921600 baud
|
// - Ubuntu 14 64-bit, CC2564B connected via FTDI USB-2-UART adapter, 921600 baud
|
||||||
//
|
// - Ubuntu 14 64-bit, CSR dongle
|
||||||
// Non working setups:
|
// - OS X 10.11, CSR dongle
|
||||||
|
|
||||||
|
// Broken setups:
|
||||||
// - OS X 10.11, CC2564B connected via FDTI USB-2-UART adapter, 921600 baud
|
// - 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
|
// - 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
|
// - 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"
|
#include "btstack-config.h"
|
||||||
@ -115,20 +122,24 @@ static void try_send_sco(void){
|
|||||||
// printf("try_send_sco, cannot send now\n");
|
// printf("try_send_sco, cannot send now\n");
|
||||||
return;
|
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();
|
hci_reserve_packet_buffer();
|
||||||
uint8_t * sco_packet = hci_get_outgoing_packet_buffer();
|
uint8_t * sco_packet = hci_get_outgoing_packet_buffer();
|
||||||
// set handle + flags
|
// set handle + flags
|
||||||
bt_store_16(sco_packet, 0, sco_handle);
|
bt_store_16(sco_packet, 0, sco_handle);
|
||||||
// set len
|
// set len
|
||||||
sco_packet[2] = frames_per_packet;
|
sco_packet[2] = sco_payload_length;
|
||||||
int i;
|
int i;
|
||||||
for (i=0;i<frames_per_packet;i++){
|
for (i=0;i<frames_per_packet;i++){
|
||||||
sco_packet[3+i] = sine[phase];
|
sco_packet[3+i] = sine[phase];
|
||||||
phase++;
|
phase++;
|
||||||
if (phase >= sizeof(sine)) phase = 0;
|
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;
|
static int count = 0;
|
||||||
count++;
|
count++;
|
||||||
if ((count & 15) == 0) printf("Sent %u\n", count);
|
if ((count & 15) == 0) printf("Sent %u\n", count);
|
||||||
@ -216,6 +227,10 @@ int btstack_main(int argc, const char * argv[]){
|
|||||||
compute_signal();
|
compute_signal();
|
||||||
#endif
|
#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_register_sco_packet_handler(&sco_packet_handler);
|
||||||
|
|
||||||
hci_discoverable_control(1);
|
hci_discoverable_control(1);
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#define HAVE_HCI_DUMP
|
#define HAVE_HCI_DUMP
|
||||||
#define SDP_DES_DUMP
|
#define SDP_DES_DUMP
|
||||||
|
|
||||||
// #define HAVE_SCO
|
#define HAVE_SCO
|
||||||
|
#define HAVE_SCO_OVER_HCI
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -65,16 +65,44 @@
|
|||||||
#include "hci.h"
|
#include "hci.h"
|
||||||
#include "hci_transport.h"
|
#include "hci_transport.h"
|
||||||
|
|
||||||
// #define HAVE_SCO
|
|
||||||
|
|
||||||
#if (USB_VENDOR_ID != 0) && (USB_PRODUCT_ID != 0)
|
#if (USB_VENDOR_ID != 0) && (USB_PRODUCT_ID != 0)
|
||||||
#define HAVE_USB_VENDOR_ID_AND_PRODUCT_ID
|
#define HAVE_USB_VENDOR_ID_AND_PRODUCT_ID
|
||||||
#endif
|
#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
|
// prototypes
|
||||||
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
|
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 {
|
typedef enum {
|
||||||
LIB_USB_CLOSED = 0,
|
LIB_USB_CLOSED = 0,
|
||||||
LIB_USB_OPENED,
|
LIB_USB_OPENED,
|
||||||
@ -103,28 +131,32 @@ static libusb_device * dev;
|
|||||||
#endif
|
#endif
|
||||||
static libusb_device_handle * handle;
|
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 *command_out_transfer;
|
||||||
static struct libusb_transfer *acl_out_transfer;
|
static struct libusb_transfer *acl_out_transfer;
|
||||||
static struct libusb_transfer *event_in_transfer[ASYNC_BUFFERS];
|
static struct libusb_transfer *event_in_transfer[ASYNC_BUFFERS];
|
||||||
static struct libusb_transfer *acl_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 H2_SCO_STATE sco_state;
|
||||||
static uint8_t sco_buffer[255+3 + SCO_PACKET_SIZE];
|
static uint8_t sco_buffer[255+3 + SCO_PACKET_SIZE];
|
||||||
static uint16_t sco_read_pos;
|
static uint16_t sco_read_pos;
|
||||||
static uint16_t sco_bytes_to_read;
|
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 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
|
#endif
|
||||||
|
|
||||||
|
// outgoing buffer for HCI Command packets
|
||||||
static uint8_t hci_cmd_buffer[3 + 256 + LIBUSB_CONTROL_SETUP_SIZE];
|
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_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];
|
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_timer_active;
|
||||||
|
|
||||||
static int usb_acl_out_active = 0;
|
static int usb_acl_out_active = 0;
|
||||||
static int usb_sco_out_active = 0;
|
|
||||||
static int usb_command_active = 0;
|
static int usb_command_active = 0;
|
||||||
|
|
||||||
// endpoint addresses
|
// endpoint addresses
|
||||||
@ -149,6 +180,17 @@ static int sco_in_addr;
|
|||||||
static int sco_out_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){
|
static void queue_transfer(struct libusb_transfer *transfer){
|
||||||
|
|
||||||
// log_info("queue_transfer %p, endpoint %x size %u", transfer, transfer->endpoint, transfer->actual_length);
|
// 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");
|
// 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){
|
static void sco_state_machine_init(void){
|
||||||
sco_state = H2_W4_SCO_HEADER;
|
sco_state = H2_W4_SCO_HEADER;
|
||||||
sco_read_pos = 0;
|
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);
|
packet_handler(HCI_ACL_DATA_PACKET, transfer-> buffer, transfer->actual_length);
|
||||||
resubmit = 1;
|
resubmit = 1;
|
||||||
} else if (transfer->endpoint == sco_in_addr) {
|
} 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;
|
int i;
|
||||||
for (i = 0; i < transfer->num_iso_packets; i++) {
|
for (i = 0; i < transfer->num_iso_packets; i++) {
|
||||||
struct libusb_iso_packet_descriptor *pack = &transfer->iso_packet_desc[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;
|
if (!pack->actual_length) continue;
|
||||||
uint8_t * data = libusb_get_iso_packet_buffer_simple(transfer, i);
|
uint8_t * data = libusb_get_iso_packet_buffer_simple(transfer, i);
|
||||||
// printf_hexdump(data, pack->actual_length);
|
// 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);
|
handle_isochronous_data(data, pack->actual_length);
|
||||||
}
|
}
|
||||||
resubmit = 1;
|
resubmit = 1;
|
||||||
@ -271,10 +355,17 @@ static void handle_completed_transfer(struct libusb_transfer *transfer){
|
|||||||
usb_acl_out_active = 0;
|
usb_acl_out_active = 0;
|
||||||
signal_done = 1;
|
signal_done = 1;
|
||||||
} else if (transfer->endpoint == sco_out_addr){
|
} else if (transfer->endpoint == sco_out_addr){
|
||||||
log_info("sco out done, size %u/%u - status %x", transfer->actual_length,
|
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].status);
|
transfer->iso_packet_desc[0].actual_length, transfer->iso_packet_desc[0].length, transfer->iso_packet_desc[0].status,
|
||||||
usb_sco_out_active = 0;
|
transfer->iso_packet_desc[1].actual_length, transfer->iso_packet_desc[1].length, transfer->iso_packet_desc[1].status,
|
||||||
signal_done = 1;
|
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 {
|
} else {
|
||||||
log_info("usb_process_ds endpoint unknown %x", transfer->endpoint);
|
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);
|
libusb_close(aHandle);
|
||||||
return r;
|
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) {
|
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);
|
libusb_close(aHandle);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -542,6 +634,7 @@ static int usb_open(void *transport_config){
|
|||||||
int r;
|
int r;
|
||||||
|
|
||||||
sco_state_machine_init();
|
sco_state_machine_init();
|
||||||
|
sco_ring_init();
|
||||||
handle_packet = NULL;
|
handle_packet = NULL;
|
||||||
|
|
||||||
// default endpoint addresses
|
// default endpoint addresses
|
||||||
@ -666,6 +759,7 @@ static int usb_open(void *transport_config){
|
|||||||
|
|
||||||
#ifdef HAVE_SCO
|
#ifdef HAVE_SCO
|
||||||
|
|
||||||
|
// incoming
|
||||||
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
|
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
|
||||||
sco_in_transfer[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // isochronous transfers SCO in
|
sco_in_transfer[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // isochronous transfers SCO in
|
||||||
log_info("Alloc iso transfer");
|
log_info("Alloc iso transfer");
|
||||||
@ -673,14 +767,10 @@ static int usb_open(void *transport_config){
|
|||||||
usb_close(handle);
|
usb_close(handle);
|
||||||
return LIBUSB_ERROR_NO_MEM;
|
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,
|
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) ;
|
hci_sco_in_buffer[c], SCO_PACKET_SIZE, NUM_ISO_PACKETS, async_callback, NULL, 0);
|
||||||
libusb_set_iso_packet_lengths(sco_in_transfer[c], SCO_PACKET_SIZE);
|
libusb_set_iso_packet_lengths(sco_in_transfer[c], ISO_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;
|
|
||||||
r = libusb_submit_transfer(sco_in_transfer[c]);
|
r = libusb_submit_transfer(sco_in_transfer[c]);
|
||||||
log_info("Submit iso transfer res = %d", r);
|
log_info("Submit iso transfer res = %d", r);
|
||||||
if (r) {
|
if (r) {
|
||||||
@ -689,7 +779,11 @@ static int usb_open(void *transport_config){
|
|||||||
return r;
|
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
|
#endif
|
||||||
|
|
||||||
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
|
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
|
||||||
@ -876,35 +970,6 @@ static int usb_send_acl_packet(uint8_t *packet, int size){
|
|||||||
return 0;
|
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){
|
static int usb_can_send_packet_now(uint8_t packet_type){
|
||||||
switch (packet_type){
|
switch (packet_type){
|
||||||
case HCI_COMMAND_DATA_PACKET:
|
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:
|
case HCI_ACL_DATA_PACKET:
|
||||||
return !usb_acl_out_active;
|
return !usb_acl_out_active;
|
||||||
case HCI_SCO_DATA_PACKET:
|
case HCI_SCO_DATA_PACKET:
|
||||||
return !usb_sco_out_active;
|
return sco_ring_have_space();
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
10
src/hci.c
10
src/hci.c
@ -3458,6 +3458,16 @@ uint16_t hci_get_sco_voice_setting(){
|
|||||||
return hci_stack->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
|
* @brief Set callback for Bluetooth Hardware Error
|
||||||
*/
|
*/
|
||||||
|
@ -970,6 +970,12 @@ void hci_set_sco_voice_setting(uint16_t voice_setting);
|
|||||||
*/
|
*/
|
||||||
uint16_t hci_get_sco_voice_setting(void);
|
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 */
|
/* API_END */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user