From 3faa160f75c1f2da2f28f5c07981475e5ed0ba4e Mon Sep 17 00:00:00 2001 From: Dirk Helbig Date: Thu, 10 Nov 2022 23:38:46 +0100 Subject: [PATCH] hci_transport_libusb: debugging version of which should keep more transfers in flight --- platform/libusb/hci_transport_h2_libusb.c | 663 +++++++++++++--------- 1 file changed, 389 insertions(+), 274 deletions(-) diff --git a/platform/libusb/hci_transport_h2_libusb.c b/platform/libusb/hci_transport_h2_libusb.c index a3ee9696f..e68db7946 100644 --- a/platform/libusb/hci_transport_h2_libusb.c +++ b/platform/libusb/hci_transport_h2_libusb.c @@ -60,6 +60,13 @@ #include +// bail out if seen libusb is apperently to old +#if !defined(LIBUSB_API_VERSION) || (LIBUSB_API_VERSION < 0x01000104) +#error libusb api version to old! +#endif + +#include + #include "btstack_config.h" #include "btstack_debug.h" @@ -81,6 +88,7 @@ #define ACL_IN_BUFFER_COUNT 3 #define EVENT_IN_BUFFER_COUNT 3 +#define EVENT_OUT_BUFFER_COUNT 4 #define SCO_IN_BUFFER_COUNT 10 #define ASYNC_POLLING_INTERVAL_MS 1 @@ -99,7 +107,7 @@ #ifdef ENABLE_SCO_OVER_HCI // alt setting for 1-3 connections and 8/16 bit -static const int alt_setting_8_bit[] = {1,2,3}; +static const int alt_setting_8_bit[] = {1,2,3}; static const int alt_setting_16_bit[] = {2,4,5}; // for ALT_SETTING >= 1 and 8-bit channel, we need the following isochronous packets @@ -130,8 +138,8 @@ static const uint16_t iso_packet_size_for_alt_setting[] = { #define USB_MAX_PATH_LEN 7 // prototypes -static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size); -static int usb_close(void); +static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size); +static int usb_close(void); typedef enum { LIB_USB_CLOSED = 0, @@ -160,10 +168,9 @@ static struct libusb_device_descriptor desc; #endif static libusb_device_handle * handle; -static struct libusb_transfer *command_out_transfer; -static struct libusb_transfer *acl_out_transfer; -static struct libusb_transfer *event_in_transfer[EVENT_IN_BUFFER_COUNT]; -static struct libusb_transfer *acl_in_transfer[ACL_IN_BUFFER_COUNT]; +//static struct libusb_transfer *acl_out_transfer; +//static struct libusb_transfer *event_in_transfer[EVENT_IN_BUFFER_COUNT]; +//static struct libusb_transfer *acl_in_transfer[ACL_IN_BUFFER_COUNT]; // known devices typedef struct { @@ -174,6 +181,165 @@ typedef struct { static btstack_linked_list_t usb_knwon_devices; +typedef struct list_head { + struct list_head *next, *prev; +} list_head_t; + +static inline void __list_add( list_head_t *new, list_head_t *prev, list_head_t *next ) { + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +static inline void init_list_head( list_head_t *list ) { + list->next = list; + list->prev = list; +} + +static inline void list_add( list_head_t *new, list_head_t *head ) { + __list_add( new, head, head->next ); +} + +static inline void list_add_tail( list_head_t *new, list_head_t *head ) { + __list_add( new, head->prev, head ); +} + +static inline void list_del( list_head_t *entry ) { + entry->next->prev = entry->prev; + entry->prev->next = entry->next; + + entry->prev = NULL; + entry->next = NULL; +} + +static inline bool list_empty( list_head_t *head ) { + return head->next == head; +} + +static inline list_head_t *list_pop_front( list_head_t *head ) { + list_head_t *front = head->next; + list_del( front ); + return front; +} + + +typedef struct { + list_head_t list; + struct libusb_transfer *t; + uint8_t *data; + bool in_flight; +} usb_transfer_list_entry_t; + +typedef struct { + list_head_t transfers; + int nbr; + usb_transfer_list_entry_t entries[0]; +} usb_transfer_list_t; + +static struct libusb_transfer *usb_transfer_list_acquire( usb_transfer_list_t *list ) { + usb_transfer_list_entry_t *current = (usb_transfer_list_entry_t*)list_pop_front( &list->transfers ); + struct libusb_transfer *transfer = current->t; + current->in_flight = true; + return transfer; +} + +static void usb_transfer_list_release( usb_transfer_list_t *list, struct libusb_transfer *transfer ) { + usb_transfer_list_entry_t *current = (usb_transfer_list_entry_t*)transfer->user_data; + assert( current != NULL ); + current->in_flight = false; + list_add( ¤t->list, &list->transfers ); +} + +static bool usb_transfer_list_empty( usb_transfer_list_t *list ) { + return list_empty( &list->transfers ); +} + +static usb_transfer_list_t *usb_transfer_list_alloc( int nbr, int iso_packets, int length ) { + usb_transfer_list_t *list = malloc( sizeof(usb_transfer_list_t) + nbr*sizeof(usb_transfer_list_entry_t) ); + init_list_head( &list->transfers ); + list->nbr = nbr; + for( int i=0; ientries[i]; + struct libusb_transfer *transfer = libusb_alloc_transfer(iso_packets); + entry->data = malloc( length ); + transfer->buffer = entry->data; +// transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER; + transfer->user_data = entry; + entry->t = transfer; + usb_transfer_list_release( list, transfer ); + assert( entry->t->user_data != NULL ); + } + return list; +} + +static void usb_transfer_list_cancel( usb_transfer_list_t *list ) { + for( int i=0; inbr; ++i ) { + usb_transfer_list_entry_t *current = &list->entries[i]; + if( current->in_flight ) { + libusb_cancel_transfer( current->t ); + } + } +} + +static int usb_transfer_list_in_flight( usb_transfer_list_t *list ) { + int cnt = 0; + for(int i=0; inbr; ++i) { + usb_transfer_list_entry_t *entry = &list->entries[i]; + if( entry->in_flight ) { + ++cnt; + } + } + return cnt; +} + + +static void usb_transfer_list_free_entry( struct libusb_transfer *transfer ) { + usb_transfer_list_entry_t *current = (usb_transfer_list_entry_t*)transfer->user_data; + free( current->data ); + libusb_free_transfer( transfer ); + current->in_flight = false; + current->t = NULL; + current->data = NULL; +} + +void usb_transfer_list_free( usb_transfer_list_t *list ) { + for( int i=0; inbr; ++i ) { + usb_transfer_list_entry_t *entry = &list->entries[i]; + assert( entry->in_flight == false ); + if( entry->t ) { + usb_transfer_list_free_entry( entry->t ); + } + } + free( list ); +} + +static usb_transfer_list_t *default_transfer_list = NULL; + +// For (ab)use as a linked list of received packets +static list_head_t handle_packet_list = LIST_HEAD_INIT(handle_packet_list); + +static void enqueue_transfer(struct libusb_transfer *transfer) { + usb_transfer_list_entry_t *current = (usb_transfer_list_entry_t*)transfer->user_data; + assert( current != NULL ); + list_add_tail( ¤t->list, &handle_packet_list ); +} + +static void signal_acknowledge(); +static void signal_sco_can_send_now(); + +// 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[EVENT_IN_BUFFER_COUNT][HCI_ACL_BUFFER_SIZE]; // bigger than largest packet +//static uint8_t hci_acl_in_buffer[ACL_IN_BUFFER_COUNT][HCI_INCOMING_PRE_BUFFER_SIZE + HCI_ACL_BUFFER_SIZE]; + + + #ifdef ENABLE_SCO_OVER_HCI #ifdef _WIN32 @@ -185,15 +351,15 @@ 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; -static struct libusb_transfer *sco_in_transfer[SCO_IN_BUFFER_COUNT]; -static uint8_t hci_sco_in_buffer[SCO_IN_BUFFER_COUNT][SCO_PACKET_SIZE]; +//static struct libusb_transfer *sco_in_transfer[SCO_IN_BUFFER_COUNT]; +//static uint8_t hci_sco_in_buffer[SCO_IN_BUFFER_COUNT][SCO_PACKET_SIZE]; // outgoing SCO -static uint8_t sco_out_ring_buffer[SCO_OUT_BUFFER_SIZE]; -static int sco_ring_write; // packet idx -static int sco_out_transfers_active; -static struct libusb_transfer *sco_out_transfers[SCO_OUT_BUFFER_COUNT]; -static int sco_out_transfers_in_flight[SCO_OUT_BUFFER_COUNT]; +//static uint8_t sco_out_ring_buffer[SCO_OUT_BUFFER_SIZE]; +//static int sco_ring_write; // packet idx +//static int sco_out_transfers_active; +//static struct libusb_transfer *sco_out_transfers[SCO_OUT_BUFFER_COUNT]; +//static int sco_out_transfers_in_flight[SCO_OUT_BUFFER_COUNT]; // pause/resume static uint16_t sco_voice_setting; @@ -204,27 +370,23 @@ static int sco_shutdown; static uint16_t iso_packet_size; static int sco_enabled; +usb_transfer_list_t *sco_transfer_list = NULL; + #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[EVENT_IN_BUFFER_COUNT][HCI_ACL_BUFFER_SIZE]; // bigger than largest packet -static uint8_t hci_acl_in_buffer[ACL_IN_BUFFER_COUNT][HCI_INCOMING_PRE_BUFFER_SIZE + HCI_ACL_BUFFER_SIZE]; -// For (ab)use as a linked list of received packets -static struct libusb_transfer *handle_packet; static int doing_pollfds; static int num_pollfds; static btstack_data_source_t * pollfd_data_sources; + +static void usb_transport_response_ds(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type); +static btstack_data_source_t transport_response; + static btstack_timer_source_t usb_timer; static int usb_timer_active; -static int usb_acl_out_active = 0; -static int usb_command_active = 0; - // endpoint addresses static int event_in_addr; static int acl_in_addr; @@ -256,16 +418,6 @@ static void hci_transport_h2_libusb_emit_usb_info(void) { (*packet_handler)(HCI_EVENT_PACKET, event, pos); } -#ifdef ENABLE_SCO_OVER_HCI -static void sco_ring_init(void){ - sco_ring_write = 0; - sco_out_transfers_active = 0; -} -static int sco_ring_have_space(void){ - return sco_out_transfers_active < SCO_OUT_BUFFER_COUNT; -} -#endif - void hci_transport_usb_add_device(uint16_t vendor_id, uint16_t product_id) { usb_known_device_t * device = malloc(sizeof(usb_known_device_t)); if (device != NULL) { @@ -279,40 +431,21 @@ void hci_transport_usb_set_path(int len, uint8_t * port_numbers){ if (len > USB_MAX_PATH_LEN || !port_numbers){ log_error("hci_transport_usb_set_path: len or port numbers invalid"); return; - } + } usb_path_len = len; memcpy(usb_path, port_numbers, len); } -// -static void queue_transfer(struct libusb_transfer *transfer){ - - // log_info("queue_transfer %p, endpoint %x size %u", transfer, transfer->endpoint, transfer->actual_length); - - transfer->user_data = NULL; - - // insert first element - if (handle_packet == NULL) { - handle_packet = transfer; - return; - } - - // Walk to end of list and add current packet there - struct libusb_transfer *temp = handle_packet; - while (temp->user_data) { - temp = (struct libusb_transfer*)temp->user_data; - } - temp->user_data = transfer; -} - LIBUSB_CALL static void async_callback(struct libusb_transfer *transfer){ int c; - if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED){ + if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) { log_info("shutdown, transfer %p", transfer); + usb_transfer_list_free_entry( transfer ); + return; } - +#if 0 // identify and free transfers as part of shutdown #ifdef ENABLE_SCO_OVER_HCI @@ -361,13 +494,15 @@ LIBUSB_CALL static void async_callback(struct libusb_transfer *transfer){ sco_out_transfers_in_flight[c] = 0; } } +#endif + #endif int r; // log_info("begin async_callback endpoint %x, status %x, actual length %u", transfer->endpoint, transfer->status, transfer->actual_length ); if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { - queue_transfer(transfer); + enqueue_transfer(transfer); } else if (transfer->status == LIBUSB_TRANSFER_STALL){ log_info("-> Transfer stalled, trying again"); r = libusb_clear_halt(handle, transfer->endpoint); @@ -396,43 +531,32 @@ static int usb_send_sco_packet(uint8_t *packet, int size){ if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return -1; + struct libusb_transfer *transfer = usb_transfer_list_acquire( sco_transfer_list ); + uint8_t *data = transfer->buffer; + void *user_data = transfer->user_data; + // 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_out_ring_buffer[tranfer_index * SCO_PACKET_SIZE]; memcpy(data, packet, size); // setup transfer // log_info("usb_send_sco_packet: size %u, max size %u, iso packet size %u", size, NUM_ISO_PACKETS * iso_packet_size, iso_packet_size); - struct libusb_transfer * sco_transfer = sco_out_transfers[tranfer_index]; - libusb_fill_iso_transfer(sco_transfer, handle, sco_out_addr, data, NUM_ISO_PACKETS * iso_packet_size, NUM_ISO_PACKETS, async_callback, NULL, 0); - libusb_set_iso_packet_lengths(sco_transfer, iso_packet_size); - r = libusb_submit_transfer(sco_transfer); + libusb_fill_iso_transfer(transfer, handle, sco_out_addr, data, NUM_ISO_PACKETS * iso_packet_size, NUM_ISO_PACKETS, async_callback, user_data, 0); + libusb_set_iso_packet_lengths(transfer, iso_packet_size); + r = libusb_submit_transfer(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_OUT_BUFFER_COUNT){ - sco_ring_write = 0; - } - sco_out_transfers_active++; - sco_out_transfers_in_flight[tranfer_index] = 1; - // log_info("H2: queued packet at index %u, num active %u", tranfer_index, sco_out_transfers_active); + signal_acknowledge(); - // notify upper stack that provided buffer can be used again - uint8_t event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0}; - packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event)); - - // and if we have more space for SCO packets - if (sco_ring_have_space()) { - uint8_t event_sco[] = { HCI_EVENT_SCO_CAN_SEND_NOW, 0}; - packet_handler(HCI_EVENT_PACKET, &event_sco[0], sizeof(event_sco)); + if( !usb_transfer_list_empty( sco_transfer_list ) ) { + signal_sco_can_send_now(); } + return 0; } @@ -479,8 +603,7 @@ static void handle_isochronous_data(uint8_t * buffer, uint16_t size){ static void handle_completed_transfer(struct libusb_transfer *transfer){ int resubmit = 0; - int signal_done = 0; - + assert(transfer->user_data != NULL ); if (transfer->endpoint == event_in_addr) { packet_handler(HCI_EVENT_PACKET, transfer->buffer, transfer->actual_length); resubmit = 1; @@ -490,15 +613,22 @@ static void handle_completed_transfer(struct libusb_transfer *transfer){ resubmit = 1; } else if (transfer->endpoint == 0){ // log_info("command done, size %u", transfer->actual_length); - usb_command_active = 0; - signal_done = 1; +// printf("%s cmd release\n", __FUNCTION__ ); + usb_transfer_list_release( default_transfer_list, transfer ); } else if (transfer->endpoint == acl_out_addr){ // log_info("acl out done, size %u", transfer->actual_length); - usb_acl_out_active = 0; - signal_done = 1; +// printf("%s acl release\n", __FUNCTION__ ); + usb_transfer_list_release( default_transfer_list, transfer ); #ifdef ENABLE_SCO_OVER_HCI } else if (transfer->endpoint == sco_in_addr) { // log_info("handle_completed_transfer for SCO IN! num packets %u", transfer->NUM_ISO_PACKETS); + + // give the transfer back to the pool, without resubmiting + if( sco_shutdown ) { + usb_transfer_list_release( sco_transfer_list, transfer ); + return; + } + int i; for (i = 0; i < transfer->num_iso_packets; i++) { struct libusb_iso_packet_descriptor *pack = &transfer->iso_packet_desc[i]; @@ -519,40 +649,40 @@ static void handle_completed_transfer(struct libusb_transfer *transfer){ log_error("Error: pack %u status %d\n", i, pack->status); } } + usb_transfer_list_release( sco_transfer_list, transfer ); + if( sco_shutdown ) { + return; + } // 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); // notify upper layer if there's space for new SCO packets - if (sco_ring_have_space()) { - uint8_t event[] = { HCI_EVENT_SCO_CAN_SEND_NOW, 0}; - packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event)); + if (!usb_transfer_list_empty(sco_transfer_list)) { + signal_sco_can_send_now(); } - // decrease tab - sco_out_transfers_active--; // log_info("H2: sco out complete, num active num active %u", sco_out_transfers_active); #endif } else { log_info("usb_process_ds endpoint unknown %x", transfer->endpoint); } - if (signal_done){ - // notify upper stack that provided buffer can be used again - uint8_t event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0}; - packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event)); - } - if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return; if (resubmit){ // Re-submit transfer - transfer->user_data = NULL; +// transfer->user_data = NULL; int r = libusb_submit_transfer(transfer); if (r) { log_error("Error re-submitting transfer %d", r); } - } + } +} + +void usb_handle_pending_events() { + struct timeval tv = { 0 }; + libusb_handle_events_timeout_completed(NULL, &tv, NULL); } static void usb_process_ds(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type) { @@ -560,24 +690,21 @@ static void usb_process_ds(btstack_data_source_t *ds, btstack_data_source_callba UNUSED(ds); UNUSED(callback_type); - if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return; + if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return; // log_info("begin usb_process_ds"); // always handling an event as we're called when data is ready - struct timeval tv; - memset(&tv, 0, sizeof(struct timeval)); - libusb_handle_events_timeout(NULL, &tv); + usb_handle_pending_events(); // Handle any packet in the order that they were received - while (handle_packet) { + while (!list_empty(&handle_packet_list)) { // log_info("handle packet %p, endpoint %x, status %x", handle_packet, handle_packet->endpoint, handle_packet->status); // pop next transfer - struct libusb_transfer * transfer = handle_packet; - handle_packet = (struct libusb_transfer*) handle_packet->user_data; + usb_transfer_list_entry_t *current = (usb_transfer_list_entry_t*)list_pop_front( &handle_packet_list ); // handle transfer - handle_completed_transfer(transfer); + handle_completed_transfer(current->t); // handle case where libusb_close might be called by hci packet handler if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return; @@ -721,12 +848,12 @@ static int scan_for_bt_device(libusb_device **devs, int start_index) { log_error("failed to get device descriptor"); return 0; } - + log_info("%04x:%04x (bus %d, device %d) - class %x subclass %x protocol %x ", desc.idVendor, desc.idProduct, libusb_get_bus_number(dev), libusb_get_device_address(dev), desc.bDeviceClass, desc.bDeviceSubClass, desc.bDeviceProtocol); - + // Detect USB Dongle based Class, Subclass, and Protocol // The class code (bDeviceClass) is 0xE0 – Wireless Controller. // The SubClass code (bDeviceSubClass) is 0x01 – RF Controller. @@ -855,13 +982,11 @@ static libusb_device_handle * try_open_device(libusb_device * device){ } #ifdef ENABLE_SCO_OVER_HCI - static int usb_sco_start(void){ log_info("usb_sco_start"); sco_state_machine_init(); - sco_ring_init(); int alt_setting; if (sco_voice_setting & 0x0020){ @@ -884,28 +1009,22 @@ static int usb_sco_start(void){ // incoming int c; for (c = 0 ; c < SCO_IN_BUFFER_COUNT ; c++) { - sco_in_transfer[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // isochronous transfers SCO in - if (!sco_in_transfer[c]) { - usb_close(); - return LIBUSB_ERROR_NO_MEM; - } + + struct libusb_transfer *transfer = usb_transfer_list_acquire( sco_transfer_list ); + uint8_t *data = transfer->buffer; + void *user_data = transfer->user_data; + // configure sco_in handlers - libusb_fill_iso_transfer(sco_in_transfer[c], handle, sco_in_addr, - hci_sco_in_buffer[c], NUM_ISO_PACKETS * iso_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]); + libusb_fill_iso_transfer(transfer, handle, sco_in_addr, + data, NUM_ISO_PACKETS * iso_packet_size, NUM_ISO_PACKETS, async_callback, user_data, 0); + libusb_set_iso_packet_lengths(transfer, iso_packet_size); + r = libusb_submit_transfer(transfer); if (r) { log_error("Error submitting isochronous in transfer %d", r); usb_close(); return r; } } - - // outgoing - for (c=0; c < SCO_OUT_BUFFER_COUNT ; c++){ - sco_out_transfers[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // 1 isochronous transfers SCO out - up to 3 parts - sco_out_transfers_in_flight[c] = 0; - } return 0; } @@ -913,7 +1032,7 @@ static void usb_sco_stop(void){ log_info("usb_sco_stop"); sco_shutdown = 1; - +#if 0 // Free SCO transfers already in queue struct libusb_transfer* transfer = handle_packet; struct libusb_transfer* prev_transfer = NULL; @@ -977,7 +1096,7 @@ static void usb_sco_stop(void){ while (!completed){ struct timeval tv; memset(&tv, 0, sizeof(struct timeval)); - libusb_handle_events_timeout(NULL, &tv); + libusb_handle_events_timeout_completed(NULL, &tv, NULL); // check if all done completed = 1; @@ -1000,7 +1119,7 @@ static void usb_sco_stop(void){ } sco_shutdown = 0; libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_WARNING); - +#endif log_info("Switching to setting %u on interface 1..", 0); int r = libusb_set_interface_alt_setting(handle, 1, 0); if (r < 0) { @@ -1015,13 +1134,24 @@ static void usb_sco_stop(void){ #endif +void pollfd_added_cb(int fd, short events, void *user_data) +{ + printf("add %d\n", fd); + assert(0); +} + +void pollfd_remove_cb(int fd, void *user_data) +{ + printf("remove %d\n", fd); + assert(0); +} + + static int usb_open(void){ int r; if (usb_transport_open) return 0; - handle_packet = NULL; - // default endpoint addresses event_in_addr = 0x81; // EP1, IN interrupt acl_in_addr = 0x82; // EP2, IN bulk @@ -1037,7 +1167,7 @@ static int usb_open(void){ // configure debug level libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_WARNING); - + libusb_device * dev = NULL; #ifdef HAVE_USB_VENDOR_ID_AND_PRODUCT_ID @@ -1152,36 +1282,35 @@ static int usb_open(void){ } #endif - + // allocate transfer handlers int c; - for (c = 0 ; c < EVENT_IN_BUFFER_COUNT ; c++) { - event_in_transfer[c] = libusb_alloc_transfer(0); // 0 isochronous transfers Events - if (!event_in_transfer[c]) { - usb_close(); - return LIBUSB_ERROR_NO_MEM; - } - } - for (c = 0 ; c < ACL_IN_BUFFER_COUNT ; c++) { - acl_in_transfer[c] = libusb_alloc_transfer(0); // 0 isochronous transfers ACL in - if (!acl_in_transfer[c]) { - usb_close(); - return LIBUSB_ERROR_NO_MEM; - } - } - command_out_transfer = libusb_alloc_transfer(0); - acl_out_transfer = libusb_alloc_transfer(0); + default_transfer_list = usb_transfer_list_alloc( + EVENT_OUT_BUFFER_COUNT+EVENT_IN_BUFFER_COUNT+ACL_IN_BUFFER_COUNT, + 0, + LIBUSB_CONTROL_SETUP_SIZE + HCI_INCOMING_PRE_BUFFER_SIZE + HCI_ACL_BUFFER_SIZE ); // biggest packet ever to expect + +#ifdef ENABLE_SCO_OVER_HCI + sco_transfer_list = usb_transfer_list_alloc( + SCO_OUT_BUFFER_COUNT+SCO_IN_BUFFER_COUNT, + NUM_ISO_PACKETS, + SCO_PACKET_SIZE + ); +#endif // TODO check for error libusb_state = LIB_USB_TRANSFERS_ALLOCATED; for (c = 0 ; c < EVENT_IN_BUFFER_COUNT ; c++) { - // configure event_in handlers - libusb_fill_interrupt_transfer(event_in_transfer[c], handle, event_in_addr, - hci_event_in_buffer[c], HCI_ACL_BUFFER_SIZE, async_callback, NULL, 0) ; - r = libusb_submit_transfer(event_in_transfer[c]); + struct libusb_transfer *transfer = usb_transfer_list_acquire( default_transfer_list ); + uint8_t *data = transfer->buffer; + void *user_data = transfer->user_data; + // configure event_in handlers + libusb_fill_interrupt_transfer(transfer, handle, event_in_addr, + data, HCI_ACL_BUFFER_SIZE, async_callback, user_data, 0); + r = libusb_submit_transfer(transfer); if (r) { log_error("Error submitting interrupt transfer %d", r); usb_close(); @@ -1190,25 +1319,26 @@ static int usb_open(void){ } for (c = 0 ; c < ACL_IN_BUFFER_COUNT ; c++) { + struct libusb_transfer *transfer = usb_transfer_list_acquire( default_transfer_list ); + usb_transfer_list_entry_t *transfer_meta_data = (usb_transfer_list_entry_t*)transfer->user_data; + uint8_t *data = transfer_meta_data->data; + void *user_data = transfer->user_data; // configure acl_in handlers - libusb_fill_bulk_transfer(acl_in_transfer[c], handle, acl_in_addr, - hci_acl_in_buffer[c] + HCI_INCOMING_PRE_BUFFER_SIZE, HCI_ACL_BUFFER_SIZE, async_callback, NULL, 0) ; - r = libusb_submit_transfer(acl_in_transfer[c]); + libusb_fill_bulk_transfer(transfer, handle, acl_in_addr, + data + HCI_INCOMING_PRE_BUFFER_SIZE, HCI_ACL_BUFFER_SIZE, async_callback, user_data, 0) ; + r = libusb_submit_transfer(transfer); if (r) { log_error("Error submitting bulk in transfer %d", r); usb_close(); return r; } - + } - #if 0 // Check for pollfds functionality doing_pollfds = libusb_pollfds_handle_timeouts(NULL); -#else - // NOTE: using pollfds doesn't work on Linux, so it is disable until further investigation - doing_pollfds = 0; -#endif + + libusb_set_pollfd_notifiers( NULL, pollfd_added_cb, pollfd_remove_cb, NULL ); if (doing_pollfds) { log_info("Async using pollfds:"); @@ -1219,18 +1349,21 @@ static int usb_open(void){ if (!pollfd_data_sources){ log_error("Cannot allocate data sources for pollfds"); usb_close(); - return 1; + return 1; } memset(pollfd_data_sources, 0, sizeof(btstack_data_source_t) * num_pollfds); for (r = 0 ; r < num_pollfds ; r++) { btstack_data_source_t *ds = &pollfd_data_sources[r]; btstack_run_loop_set_data_source_fd(ds, pollfd[r]->fd); btstack_run_loop_set_data_source_handler(ds, &usb_process_ds); - btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); + if( pollfd[r]->events & POLLIN ) + btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); + else + btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE); btstack_run_loop_add_data_source(ds); log_info("%u: %p fd: %u, events %x", r, pollfd[r], pollfd[r]->fd, pollfd[r]->events); } - free(pollfd); + libusb_free_pollfds(pollfd); } else { log_info("Async using timers:"); @@ -1244,6 +1377,10 @@ static int usb_open(void){ hci_transport_h2_libusb_emit_usb_info(); + btstack_data_source_t *ds = &transport_response; + btstack_run_loop_set_data_source_handler(ds, &usb_transport_response_ds); + btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_POLL); + btstack_run_loop_add_data_source(ds); return 0; } @@ -1282,98 +1419,32 @@ static int usb_close(void){ /* fall through */ case LIB_USB_INTERFACE_CLAIMED: - // Cancel all transfers, ignore warnings for this - libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_ERROR); - for (c = 0 ; c < EVENT_IN_BUFFER_COUNT ; c++) { - if (event_in_transfer[c]){ - log_info("cancel event_in_transfer[%u] = %p", c, event_in_transfer[c]); - libusb_cancel_transfer(event_in_transfer[c]); - } - } - for (c = 0 ; c < ACL_IN_BUFFER_COUNT ; c++) { - if (acl_in_transfer[c]){ - log_info("cancel acl_in_transfer[%u] = %p", c, acl_in_transfer[c]); - libusb_cancel_transfer(acl_in_transfer[c]); - } - } + libusb_set_pollfd_notifiers( NULL, NULL, NULL, NULL ); + usb_transfer_list_cancel( default_transfer_list ); #ifdef ENABLE_SCO_OVER_HCI - for (c = 0 ; c < SCO_IN_BUFFER_COUNT ; c++) { - if (sco_in_transfer[c]){ - log_info("cancel sco_in_transfer[%u] = %p", c, sco_in_transfer[c]); - libusb_cancel_transfer(sco_in_transfer[c]); - } - } - for (c = 0; c < SCO_OUT_BUFFER_COUNT ; c++){ - if (sco_out_transfers_in_flight[c]) { - log_info("cancel sco_out_transfers[%u] = %p", c, sco_out_transfers[c]); - libusb_cancel_transfer(sco_out_transfers[c]); - } else { - if (sco_out_transfers[c] != NULL){ - libusb_free_transfer(sco_out_transfers[c]); - sco_out_transfers[c] = 0; - } - } - } + usb_transfer_list_cancel( sco_transfer_list ); #endif - libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_WARNING); - // wait until all transfers are completed - or 20 iterations - int countdown = 20; - while (!completed){ - - if (--countdown == 0){ - log_info("Not all transfers cancelled, leaking a bit."); - break; - } - - struct timeval tv; - memset(&tv, 0, sizeof(struct timeval)); + int in_flight_transfers = usb_transfer_list_in_flight( default_transfer_list ); +#ifdef ENABLE_SCO_OVER_HCI + in_flight_transfers += usb_transfer_list_in_flight( sco_transfer_list ); +#endif + while( in_flight_transfers > 0 ) { + struct timeval tv = { 0 }; libusb_handle_events_timeout(NULL, &tv); - // check if all done - completed = 1; - for (c=0;c0; --acknowledge_count) { + static const uint8_t event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0 }; + packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event)); + } + + for(; sco_can_send_now_count>0; --sco_can_send_now_count) { + static const uint8_t event[] = { HCI_EVENT_SCO_CAN_SEND_NOW, 0 }; + packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event)); + } + +} + static int usb_send_cmd_packet(uint8_t *packet, int size){ int r; if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return -1; +// printf("%s( %p, %d )\n", __FUNCTION__, packet, size ); + + struct libusb_transfer *transfer = usb_transfer_list_acquire( default_transfer_list ); + uint8_t *data = transfer->buffer; + void *user_data = transfer->user_data; // async - libusb_fill_control_setup(hci_cmd_buffer, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, 0, 0, 0, size); - memcpy(hci_cmd_buffer + LIBUSB_CONTROL_SETUP_SIZE, packet, size); + libusb_fill_control_setup(data, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, 0, 0, 0, size); + memcpy(data + LIBUSB_CONTROL_SETUP_SIZE, packet, size); // prepare transfer - int completed = 0; - libusb_fill_control_transfer(command_out_transfer, handle, hci_cmd_buffer, async_callback, &completed, 0); - command_out_transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER; - - // update stata before submitting transfer - usb_command_active = 1; + libusb_fill_control_transfer(transfer, handle, data, async_callback, user_data, 0); // submit transfer - r = libusb_submit_transfer(command_out_transfer); + assert( transfer->user_data != NULL ); + r = libusb_submit_transfer(transfer); if (r < 0) { - usb_command_active = 0; log_error("Error submitting cmd transfer %d", r); return -1; } + signal_acknowledge(); + return 0; } @@ -1437,38 +1538,52 @@ static int usb_send_acl_packet(uint8_t *packet, int size){ int r; if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return -1; - +// printf("%s( %p, %d )\n", __FUNCTION__, packet, size ); // log_info("usb_send_acl_packet enter, size %u", size); + struct libusb_transfer *transfer = usb_transfer_list_acquire( default_transfer_list ); + uint8_t *data = transfer->buffer; + // prepare transfer - int completed = 0; - libusb_fill_bulk_transfer(acl_out_transfer, handle, acl_out_addr, packet, size, - async_callback, &completed, 0); - acl_out_transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + memcpy( data, packet, size ); + libusb_fill_bulk_transfer(transfer, handle, acl_out_addr, data, size, + async_callback, transfer->user_data, 0); - // update stata before submitting transfer - usb_acl_out_active = 1; + assert( transfer->user_data != NULL ); + r = libusb_submit_transfer(transfer); - r = libusb_submit_transfer(acl_out_transfer); if (r < 0) { - usb_acl_out_active = 0; log_error("Error submitting acl transfer, %d", r); return -1; } + signal_acknowledge(); + return 0; } static int usb_can_send_packet_now(uint8_t packet_type){ switch (packet_type){ - case HCI_COMMAND_DATA_PACKET: - return !usb_command_active; - case HCI_ACL_DATA_PACKET: - return !usb_acl_out_active; + case HCI_COMMAND_DATA_PACKET: { + int ret = !usb_transfer_list_empty( default_transfer_list ); + btstack_assert( ret == 1 ); +// printf("can send now: %d\n", ret ); + return ret; + } + case HCI_ACL_DATA_PACKET: { + int ret = !usb_transfer_list_empty( default_transfer_list ); + btstack_assert( ret == 1 ); +// printf("can send now: %d\n", ret ); + return ret; + } + #ifdef ENABLE_SCO_OVER_HCI - case HCI_SCO_DATA_PACKET: + case HCI_SCO_DATA_PACKET: { +// printf("%s sco\n", __FUNCTION__); if (!sco_enabled) return 0; - return sco_ring_have_space(); + int ret = !usb_transfer_list_empty( sco_transfer_list ); + return ret; + } #endif default: return 0;