From 39e7ee9f5df7b74450032df357daa94e932e3106 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Sun, 30 Apr 2017 00:13:30 +0200 Subject: [PATCH] cc256x: ENABLE_CC256X_BAUDRATE_CHANGE_FLOWCONTROL_BUG_WORKAROUND avoids lost bytes during baud rate change, updated docu --- chipset/README.md | 103 ++------------------------------------ doc/manual/docs/how_to.md | 2 +- src/hci_transport_h4.c | 45 +++++++++++++++++ 3 files changed, 49 insertions(+), 101 deletions(-) diff --git a/chipset/README.md b/chipset/README.md index 2d35ec087..deb5f82d9 100644 --- a/chipset/README.md +++ b/chipset/README.md @@ -218,7 +218,9 @@ CC256xC | 0x9a1a | 6.12.26 **SCO data** is routed to the I2S/PCM interface but can be configured with the [HCI_VS_Write_SCO_Configuration](http://processors.wiki.ti.com/index.php/CC256x_VS_HCI_Commands#HCI_VS_Write_SCO_Configuration_.280xFE10.29) command. -**Baud rate** can be set with [HCI_VS_Update_UART_HCI_Baudrate](http://processors.wiki.ti.com/index.php/CC256x_VS_HCI_Commands#HCI_VS_Update_UART_HCI_Baudrate_.280xFF36.29). The chipset confirms the change with a command complete event after which the local UART is set to the new speed. Oddly enough, the CC256x chipsets ignore the incoming CTS line during this particular command complete response. See next paragraph for a workaround. +**Baud rate** can be set with [HCI_VS_Update_UART_HCI_Baudrate](http://processors.wiki.ti.com/index.php/CC256x_VS_HCI_Commands#HCI_VS_Update_UART_HCI_Baudrate_.280xFF36.29). The chipset confirms the change with a command complete event after which the local UART is set to the new speed. Oddly enough, the CC256x chipsets ignore the incoming CTS line during this particular command complete response. + +If you've implemented the hal_uart_dma.h without an additional ring buffer (as recommended!) and you have a bit of delay, e.g. because of thread switching on a RTOS, this could cause a UART overrun. If this happens, BTstack provides a workaround in the HCI H4 transport implementation by adding #define ENABLE_CC256X_BAUDRATE_CHANGE_FLOWCONTROL_BUG_WORKAROUND to your btstack_config.h. If this is enabled, the H4 transport layer will resort to "deep packet inspection" to first check if its a TI controller and then wait for the HCI_VS_Update_UART_HCI_Baudrate. When detected, it will tweak the next UART read to expect the HCI Command Complete event. **BD Addr** can be set with [HCI_VS_Write_BD_Addr](2.2.1 HCI_VS_Write_BD_Addr (0xFC06)) although all chipsets have an official address stored. @@ -237,105 +239,6 @@ The Makefile at *chipset/cc256x/Makefile.inc* is able to automatically download SCO Data can be routed over HCI, so HFP Wide-Band Speech is supported. -**Work around for CTS bug while HCI Command Complete Event of HCI VS Update Baudrate with H4/eHCILL** - -If you're using a RTOS port (e.g. FreeRTOS) and have implemented the *hal_uart_dma.h* API directly, chances are, you're getting a UART overrun errors in this situation, and the bootup will get stuck at this point. - -BTstack currently cannot deal with this situation since it would need to request a read for the complete HCI Command Complete before sending the HCI VS Baudrate Command. - -We recommend to add the following workaround in the *hal_uart_dma.h* implementation to deal with this bug in the lowest layer. - -The idea is to detect the HCI VS Baudrate Command and then to ignore all received data for 100 ms. After that, send the appropriate HCI Command Complete Event to the HCI transport. - -Let's assume that your hal_uart_dma.h implementation contains *hal_uart_dma_rx_complete()* and *hal_uart_dma_tx_complete* that are called when the corresponding asynchronous request has been completed, and that you've stored the registered callbacks in *rx_done_handler* and *tx_done_handler*. Please provide new functions *hal_uart_dma_sleep_ms* that just blocks (you might have needed a similar to implement the CC256x power cycle before) and *hci_uart_dma_rx_flush* that clear all errors and flushes all incoming buffers (might not be needed on most platforms). - - // types & declaration - static enum { - CC256X_HACK_IDLE, - CC256X_HACK_W4_SENT, - CC256X_HACK_W4_READ_HEADER, - CC256X_HACK_W4_READ_PAYLOAD, - } cc256x_hack_state; - - ... - - void hal_uart_dma_send_block(const uint8_t *buffer, uint16_t length){ - // detect CC256x set baudrate command - const uint8_t baud_rate_command_prefix[] = { 0x01, 0x36, 0xff, 0x04}; - if (memcmp(data, baud_rate_command_prefix, sizeof(baud_rate_command_prefix)) == 0) { - log_info("CC256x baud rate command detected"); - cc256x_hack_state = CC256X_HACK_W4_SENT; - } - - // your code to trigger buffer send follow - ... - } - - void hal_uart_dma_receive_block(uint8_t *buffer, uint16_t len){ - switch (cc256x_hack_state){ - case CC256X_HACK_W4_READ_HEADER: - cc256x_hack_state = CC256X_HACK_W4_READ_PAYLOAD; - // HCI Command Complete - *buffer++ = 0x0e; - *buffer++ = 0x04; - (*rx_done_handler)(); - return; - case CC256X_HACK_W4_READ_PAYLOAD: - cc256x_hack_state = CC256X_HACK_IDLE; - // commands 0, opcode 0xff36, status 0 - *buffer++ = 0x01; - *buffer++ = 0x36; - *buffer++ = 0xff; - *buffer++ = 0x00; - (*rx_done_handler)(); - return; - default: - break; - } - - // your code to trigger buffer read follows - ... - } - - static void hal_uart_dma_rx_complete(void){ - switch (cc256x_hack_state){ - case CC256X_HACK_W4_SENT: - // ignore until sent & event received; - return; - default: - break; - } - - // your code to call complete handler, probably looks like - if(rx_done_handler){ - (*rx_done_handler)(); - } - } - - static void tx_done_handler(void){ - // your code to call complete handler, probably looks like - if(tx_done_handler){ - (*tx_done_handler)(); - - // cc256x hack - switch (cc256x_hack_state){ - case CC256X_HACK_W4_SENT: - log_info("cc256x hack: sleep"); - hal_uart_dma_sleep_ms(100); - log_info("cc256x hack: flush"); - hal_uart_block_reset_rx(); - log_info("cc256x hack: simulate command complete"); - *read_bytes_data++ = 0x04; - read_bytes_len--; - cc256x_hack_state = CC256X_HACK_W4_READ_HEADER; - (*rx_done_handler)(); - break; - default: - break; - } - } - } - ## Toshiba The Toshiba TC35661 Dual-Mode chipset is available in three variants: standalone incl. binary Bluetooth stack, as a module with embedded stack or with a regular HCI interface. The HCI variant has the model number TC35661–007. diff --git a/doc/manual/docs/how_to.md b/doc/manual/docs/how_to.md index 2bf270f8e..f8abcb989 100644 --- a/doc/manual/docs/how_to.md +++ b/doc/manual/docs/how_to.md @@ -81,7 +81,7 @@ ENABLE_LE_SECURE_CONNECTIONS | Enable LE Secure Connections using [mbed TLS libr ENABLE_LE_DATA_CHANNELS | Enable LE Data Channels in credit-based flow control mode ENABLE_LE_SIGNED_WRITE | Enable LE Signed Writes in ATT/GATT ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL | Enable HCI Controller to Host Flow Control, see below - +ENABLE_CC256X_BAUDRATE_CHANGE_FLOWCONTROL_BUG_WORKAROUND | Enable workaround for bug in CC256x Flow Control during baud rate change, see chipset docs. ### HCI Controller to Host Flow Control In general, BTstack relies on flow control of the HCI transport, either via Hardware CTS/RTS flow control for UART or regular USB flow control. If this is not possible, e.g on an SoC, BTstack can use HCI Controller to Host Flow Control by defining ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL. If enabled, the HCI Transport implementation must be able to buffer the specified packets. In addition, it also need to be able to buffer a few HCI Events. Using a low number of host buffers might result in less throughput. diff --git a/src/hci_transport_h4.c b/src/hci_transport_h4.c index 24e939738..422f5e198 100644 --- a/src/hci_transport_h4.c +++ b/src/hci_transport_h4.c @@ -50,6 +50,7 @@ #include "btstack_debug.h" #include "hci.h" #include "hci_transport.h" +#include "bluetooth_company_id.h" #include "btstack_uart_block.h" #define ENABLE_LOG_EHCILL @@ -139,6 +140,17 @@ static int read_pos; static uint8_t hci_packet_with_pre_buffer[HCI_INCOMING_PRE_BUFFER_SIZE + 1 + HCI_PACKET_BUFFER_SIZE]; // packet type + max(acl header + acl payload, event header + event data) static uint8_t * hci_packet = &hci_packet_with_pre_buffer[HCI_INCOMING_PRE_BUFFER_SIZE]; +#ifdef ENABLE_CC256X_BAUDRATE_CHANGE_FLOWCONTROL_BUG_WORKAROUND +static const uint8_t local_version_event_prefix[] = { 0x04, 0x0e, 0x0c, 0x01, 0x01, 0x10}; +static const uint8_t baud_rate_command_prefix[] = { 0x01, 0x36, 0xff, 0x04}; +static enum { + CC256X_WORKAROUND_IDLE, + CC256X_WORKAROUND_CHIPSET_DETECTED, + CC256X_WORKAROUND_BAUDRATE_COMMAND_SENT, + CC256X_WORKAROUND_DONE +} cc256x_workaround_state; +#endif + static int hci_transport_h4_set_baudrate(uint32_t baudrate){ log_info("hci_transport_h4_set_baudrate %u", baudrate); return btstack_uart->set_baudrate(baudrate); @@ -212,12 +224,36 @@ static void hci_transport_h4_block_read(void){ break; case H4_W4_PAYLOAD: +#ifdef ENABLE_CC256X_BAUDRATE_CHANGE_FLOWCONTROL_BUG_WORKAROUND + if (cc256x_workaround_state == CC256X_WORKAROUND_IDLE + && memcmp(hci_packet, local_version_event_prefix, sizeof(local_version_event_prefix)) == 0){ + if (little_endian_read_16(hci_packet, 11) == BLUETOOTH_COMPANY_ID_TEXAS_INSTRUMENTS_INC){ + // detect TI CC256x controller based on manufacturer + log_info("Detected CC256x controller"); + cc256x_workaround_state = CC256X_WORKAROUND_CHIPSET_DETECTED; + } else { + // work around not needed + log_info("Bluetooth controller not by TI"); + cc256x_workaround_state = CC256X_WORKAROUND_DONE; + } + } +#endif packet_handler(hci_packet[0], &hci_packet[1], read_pos-1); hci_transport_h4_reset_statemachine(); break; default: break; } + +#ifdef ENABLE_CC256X_BAUDRATE_CHANGE_FLOWCONTROL_BUG_WORKAROUND + if (cc256x_workaround_state == CC256X_WORKAROUND_BAUDRATE_COMMAND_SENT){ + cc256x_workaround_state = CC256X_WORKAROUND_IDLE; + // avoid flowcontrol problem by reading expected hci command complete event of 7 bytes in a single block read + h4_state = H4_W4_PAYLOAD; + bytes_to_read = 7; + } +#endif + hci_transport_h4_trigger_next_read(); } @@ -252,11 +288,20 @@ static int hci_transport_h4_can_send_now(uint8_t packet_type){ } static int hci_transport_h4_send_packet(uint8_t packet_type, uint8_t * packet, int size){ + // store packet type before actual data and increase size size++; packet--; *packet = packet_type; +#ifdef ENABLE_CC256X_BAUDRATE_CHANGE_FLOWCONTROL_BUG_WORKAROUND + if ((cc256x_workaround_state == CC256X_WORKAROUND_CHIPSET_DETECTED) + && (memcmp(packet, baud_rate_command_prefix, sizeof(baud_rate_command_prefix)) == 0)) { + log_info("CC256x baud rate command detected, expect command complete event next"); + cc256x_workaround_state = CC256X_WORKAROUND_BAUDRATE_COMMAND_SENT; + } +#endif + // store request tx_len = size; tx_data = packet;