From f258f41689dace7d26ba1f487f1962634a840e42 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 6 Jan 2017 16:59:51 +0100 Subject: [PATCH] tests to manually schedule transfers, try processing transfers in correct order --- platform/windows/hci_transport_h2_winusb.c | 446 +++++++++++++++++---- 1 file changed, 359 insertions(+), 87 deletions(-) diff --git a/platform/windows/hci_transport_h2_winusb.c b/platform/windows/hci_transport_h2_winusb.c index 65700dc9a..bd05c818d 100644 --- a/platform/windows/hci_transport_h2_winusb.c +++ b/platform/windows/hci_transport_h2_winusb.c @@ -56,6 +56,7 @@ #include #include /* UNIX standard function definitions */ #include +#include // to print long long int (aka 64 bit ints) #include "btstack_config.h" @@ -67,10 +68,15 @@ #include #include -#if 1 +#ifdef ENABLE_SCO_OVER_HCI // Isochronous Add-On +// Function signatures frome https://abi-laboratory.pro/compatibility/Windows_7.0_to_Windows_8.1/x86_64/info/winusb.dll/symbols.html +// MSDN documentation has multiple errors (Jan 2017), annotated below + +typedef PVOID WINUSB_ISOCH_BUFFER_HANDLE, *PWINUSB_ISOCH_BUFFER_HANDLE; + typedef struct _WINUSB_PIPE_INFORMATION_EX { USBD_PIPE_TYPE PipeType; UCHAR PipeId; @@ -79,8 +85,6 @@ typedef struct _WINUSB_PIPE_INFORMATION_EX { ULONG MaximumBytesPerInterval; } WINUSB_PIPE_INFORMATION_EX, *PWINUSB_PIPE_INFORMATION_EX; -typedef PVOID WINUSB_ISOCH_BUFFER_HANDLE, *PWINUSB_ISOCH_BUFFER_HANDLE; - typedef WINBOOL (WINAPI * WinUsb_QueryPipeEx_t) ( WINUSB_INTERFACE_HANDLE InterfaceHandle, UCHAR AlternateInterfaceNumber, @@ -99,8 +103,8 @@ typedef WINBOOL (WINAPI * WinUsb_ReadIsochPipe_t)( ULONG Offset, ULONG Length, PULONG FrameNumber, - PULONG NumberOfPackets, - PULONG IsoPacketDescriptors, + ULONG NumberOfPackets, // MSDN lists PULONG + PUSBD_ISO_PACKET_DESCRIPTOR IsoPacketDescriptors, // MSDN lists PULONG LPOVERLAPPED Overlapped ); typedef WINBOOL (WINAPI * WinUsb_ReadIsochPipeAsap_t)( @@ -108,7 +112,7 @@ typedef WINBOOL (WINAPI * WinUsb_ReadIsochPipeAsap_t)( ULONG Offset, ULONG Length, BOOL ContinueStream, - ULONG NumberOfPackets, + ULONG NumberOfPackets, // MSDN lists PULONG PUSBD_ISO_PACKET_DESCRIPTOR IsoPacketDescriptors, LPOVERLAPPED Overlapped ); @@ -129,6 +133,11 @@ typedef WINBOOL (WINAPI * WinUsb_WriteIsochPipeAsap_t)( typedef WINBOOL (WINAPI * WinUsb_UnregisterIsochBuffer_t)( PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle ); +typedef WINBOOL (WINAPI * WinUsb_GetCurrentFrameNumber_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, // MSDN lists 'Device handle returned from CreateFile' + PULONG CurrentFrameNumber, + LARGE_INTEGER *TimeStamp +); static WinUsb_QueryPipeEx_t WinUsb_QueryPipeEx; static WinUsb_RegisterIsochBuffer_t WinUsb_RegisterIsochBuffer; @@ -137,7 +146,7 @@ static WinUsb_ReadIsochPipeAsap_t WinUsb_ReadIsochPipeAsap; static WinUsb_WriteIsochPipe_t WinUsb_WriteIsochPipe; static WinUsb_WriteIsochPipeAsap_t WinUsb_WriteIsochPipeAsap; static WinUsb_UnregisterIsochBuffer_t WinUsb_UnregisterIsochBuffer; - +static WinUsb_GetCurrentFrameNumber_t WinUsb_GetCurrentFrameNumber; #endif // @@ -168,6 +177,11 @@ static WinUsb_UnregisterIsochBuffer_t WinUsb_UnregisterIsochBuffer; #define ISOC_BUFFERS 20 +// 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) + /** Request type bits of the "bmRequestType" field in control transfers. */ enum usb_request_type { USB_REQUEST_TYPE_STANDARD = (0x00 << 5), @@ -219,28 +233,44 @@ static int usb_acl_out_active; static uint8_t hci_event_in_buffer[2 + 255]; static uint8_t hci_acl_in_buffer[HCI_INCOMING_PRE_BUFFER_SIZE + HCI_ACL_BUFFER_SIZE]; + #ifdef ENABLE_SCO_OVER_HCI +typedef enum { + H2_W4_SCO_HEADER = 1, + H2_W4_PAYLOAD, +} H2_SCO_STATE; + // SCO Incoming Windows static uint8_t hci_sco_in_buffer[ISOC_BUFFERS * SCO_PACKET_SIZE]; static WINUSB_ISOCH_BUFFER_HANDLE hci_sco_in_buffer_handle; static USBD_ISO_PACKET_DESCRIPTOR hci_sco_packet_descriptors[ISOC_BUFFERS * NUM_ISO_PACKETS]; -// static OVERLAPPED canary_overlapped; static OVERLAPPED usb_overlapped_sco_in[ISOC_BUFFERS]; +static int usb_sco_in_expected_transfer; // SCO Incoming Run Loop static btstack_data_source_t usb_data_source_sco_in[ISOC_BUFFERS]; // SCO Incoming HCI -typedef enum { - H2_W4_SCO_HEADER = 1, - H2_W4_PAYLOAD, -} H2_SCO_STATE; static H2_SCO_STATE sco_state; static uint8_t sco_buffer[SCO_PACKET_SIZE]; static uint16_t sco_read_pos; static uint16_t sco_bytes_to_read; +// SCO Outgoing Windows +static OVERLAPPED usb_overlapped_sco_out[SCO_RING_BUFFER_COUNT]; +static int sco_ring_transfers_active; + +// next tranfer +static ULONG sco_next_transfer_at_frame; + +// SCO Outgoing Run Loop +static btstack_data_source_t usb_data_source_sco_out[SCO_RING_BUFFER_COUNT]; + +// SCO Outgoing HCI +static uint8_t sco_ring_buffer[SCO_RING_BUFFER_SIZE]; +static int sco_ring_write; // packet idx + #endif #if 0 @@ -265,6 +295,16 @@ static int usb_is_known_bluetooth_device(uint16_t vendor_id, uint16_t product_id } #endif +#ifdef ENABLE_SCO_OVER_HCI +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; +} +#endif + static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){ log_info("registering packet handler"); packet_handler = handler; @@ -330,11 +370,15 @@ exit_on_error: log_error("usb_submit_acl_in_transfer: winusb last error %lu", GetLastError()); } +#if 1 + static void usb_submit_sco_in_transfer(int i, int continue_stream){ - // log_info("WinUsb_ReadIsochPipeAsap #%u - handle, %u, %u, %u, %u, %p, %p", i, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE, (i == 0) ? FALSE : TRUE, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]); - // log_info_hexdump(&usb_overlapped_sco_in[i], sizeof(OVERLAPPED)); - // log_info_hexdump(&canary_overlapped, sizeof(OVERLAPPED)); + LARGE_INTEGER timestamp; + ULONG current_frame_number; + WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); + + // log_info("WinUsb_ReadIsochPipeAsap #%u - current frame %lu", i, current_frame_number); BOOL result = WinUsb_ReadIsochPipeAsap(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE, continue_stream, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]); @@ -343,24 +387,14 @@ static void usb_submit_sco_in_transfer(int i, int continue_stream){ if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error; } -#if 0 // check result +#if 1 + // check result DWORD bytes_transferred; - BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_in[i], &bytes_transferred, FALSE); - if (!ok){ - int err = (int) GetLastError(); - if (err == 87 && continue_stream == 1){ - // invalid parameters, try without continue - log_info("usb_submit_sco_in_transfer[%u] error 87, reset pipe", i); - result = WinUsb_ResetPipe(usb_interface_1_handle, sco_in_addr); - log_info("usb_submit_sco_in_transfer[%u] result %u, error %u - retry without continue flag", i, result, (int) GetLastError()); - WinUsb_ReadIsochPipeAsap(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE, - 0, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]); - } else { - log_info("WinUsb_GetOverlappedResult:[%u] error %u", i, (int) GetLastError()); - } + result = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_in[i], &bytes_transferred, FALSE); + if (!result){ + if (GetLastError() != ERROR_IO_INCOMPLETE) goto exit_on_error; } #endif - // IO_PENDING -> wait for completed btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[i], DATA_SOURCE_CALLBACK_READ); return; @@ -369,6 +403,61 @@ exit_on_error: log_error("usb_submit_sco_in_transfer: winusb last error %lu", GetLastError()); } +#else + +// frame number gets updated +static void usb_submit_sco_in_transfer_at_frame(int i, ULONG * frame_number){ + + // log_info_hexdump(&usb_overlapped_sco_in[i], sizeof(OVERLAPPED)); + // log_info_hexdump(&canary_overlapped, sizeof(OVERLAPPED)); + + LARGE_INTEGER timestamp; + ULONG current_frame_number; + WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); + + ULONG frame_before = *frame_number; + + BOOL result = WinUsb_ReadIsochPipe(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE, + frame_number, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]); + + log_info("WinUsb_ReadIsochPipe #%02u: current %lu, planned %lu - buffer %lu", i, current_frame_number, frame_before, frame_before - current_frame_number); + + if (!result) { + if (GetLastError() == ERROR_IO_PENDING) { + // log_info("ERROR_IO_PENDING"); + } else { + goto exit_on_error; + } + } + +#if 0 + // check result + DWORD bytes_transferred; + BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_in[i], &bytes_transferred, FALSE); + if (!ok){ + int err = (int) GetLastError(); + if (err == 87 && continue_stream == 1){ + // invalid parameters, try without continue + log_info("usb_submit_sco_in_transfer[%u] error 87, reset pipe", i); + result = WinUsb_ResetPipe(usb_interface_1_handle, sco_in_addr); + log_info("usb_submit_sco_in_transfer[%u] result %u, error %u - retry without continue flag", i, result, (int) GetLastError()); + WinUsb_ReadIsochPipeAsap(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE, + 0, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]); + } else { + log_info("WinUsb_GetOverlappedResult:[%u] error %u", i, (int) GetLastError()); + } + } +#endif + + // IO_PENDING -> wait for completed + btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[i], DATA_SOURCE_CALLBACK_READ); + return; + +exit_on_error: + log_error("usb_submit_sco_in_transfer: winusb last error %lu", GetLastError()); +} +#endif + static void usb_process_event_in(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type) { btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); @@ -458,50 +547,88 @@ static void sco_handle_data(uint8_t * buffer, uint16_t size){ } } -static void usb_process_sco_in(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){ - - btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); +static void usb_process_sco_out(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){ + btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE); // find index int i; - for (i=0;i wait for completed - btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[transfer_index], DATA_SOURCE_CALLBACK_READ); - return; + btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[transfer_index], DATA_SOURCE_CALLBACK_WRITE); + return; } else { - log_error("usb_process_sco_in_done[%u]: error reading %u, Internal %x", transfer_index, (int) err, (int) usb_overlapped_sco_in[i].Internal); - log_info_hexdump(&usb_overlapped_sco_in[transfer_index], sizeof(OVERLAPPED)); - // log_info_hexdump(&canary_overlapped, sizeof(OVERLAPPED)); - return; + log_error("usb_process_sco_out_done[%u]: error reading %u, Internal %x", transfer_index, (int) err, (int) usb_overlapped_sco_out[i].Internal); + log_info_hexdump(&usb_overlapped_sco_out[transfer_index], sizeof(OVERLAPPED)); + // log_info_hexdump(&canary_overlapped, sizeof(OVERLAPPED)); + return; } } - if (ok){ - for (i=0;iLength){ - uint8_t * iso_data = &hci_sco_in_buffer[transfer_index * SCO_PACKET_SIZE + packet_descriptor->Offset]; - uint16_t iso_len = packet_descriptor->Length; - // log_info_hexdump(iso_data, iso_len); - sco_handle_data(iso_data, iso_len); - } - } + // mark free + if (sco_ring_have_space()) { + uint8_t event[] = { HCI_EVENT_SCO_CAN_SEND_NOW, 0}; + packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event)); } + // decrease tab + sco_ring_transfers_active--; +} - if (!ok) return; +static void usb_process_sco_in(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){ - usb_submit_sco_in_transfer(transfer_index, 1); + // get current frame number + ULONG current_frame_number; + LARGE_INTEGER timestamp; + WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); + + // check all transfers, starting with usb_sco_in_expected_transfer + int i; + for (i=0;iLength){ + uint8_t * iso_data = &hci_sco_in_buffer[transfer_index * SCO_PACKET_SIZE + packet_descriptor->Offset]; + uint16_t iso_len = packet_descriptor->Length; + // log_info_hexdump(iso_data, iso_len); + sco_handle_data(iso_data, iso_len); + } + } + } + + // usb_submit_sco_in_transfer_at_frame(i, &sco_next_transfer_at_frame); + usb_submit_sco_in_transfer(transfer_index, 1); + + // update expected + usb_sco_in_expected_transfer = (i + 1) % ISOC_BUFFERS; + } } static void usb_process_command_out(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){ @@ -699,11 +826,6 @@ static int usb_try_open_device(const char * device_path){ #ifdef ENABLE_SCO_OVER_HCI - // AUTO_CLEAR_STALL, RAW_IO is not callable for ISO EP - // uint8_t value_on = 1; - // result = WinUsb_SetPipePolicy(usb_interface_1_handle, sco_in_addr, RAW_IO, sizeof(value_on), &value_on); - // if (!result) goto exit_on_error; - int i; memset(hci_sco_packet_descriptors, 0, sizeof(hci_sco_packet_descriptors)); @@ -717,25 +839,21 @@ static int usb_try_open_device(const char * device_path){ for (i=0;i= WAIT_OBJECT_0 && res < WAIT_OBJECT_0 + ISOC_BUFFERS){ + int transfer_index = res - WAIT_OBJECT_0; + // WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); + // log_info("Transfer #%u completed, current frame %lu", transfer_index, current_frame_number); + DWORD bytes_transferred; + BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_in[transfer_index], &bytes_transferred, FALSE); + if (!ok){ + log_error("WinUsb_GetOverlappedResult res %x", (int) GetLastError()); + } + usb_submit_sco_in_transfer_at_frame(transfer_index, &sco_next_transfer_at_frame); + } else { + log_error("Unexpected result %x", res); + } + } +#endif + +#if 0 + // stop until working + usb_free_resources(); + return 0; +#endif +#endif + +#endif + return 1; exit_on_error: @@ -777,9 +1002,9 @@ exit_on_error: #ifdef ENABLE_SCO_OVER_HCI -#define WinUSB_Lookup(fn) do { fn = (fn##_t) GetProcAddress(h, #fn); log_info("%-30s %p", #fn, fn); if (!fn) return; } while(0) +#define WinUSB_Lookup(fn) do { fn = (fn##_t) GetProcAddress(h, #fn); log_info("%-30s %p", #fn, fn); if (!fn) return FALSE; } while(0) -static void usb_lookup_symbols(void){ +static BOOL usb_lookup_symbols(void){ // lookup runtime symbols missing in current mingw64 distribution HMODULE h = GetModuleHandleA("WinUSB"); log_info("%-30s %p", "WinUSB", h); @@ -790,6 +1015,8 @@ static void usb_lookup_symbols(void){ WinUSB_Lookup(WinUsb_WriteIsochPipe); WinUSB_Lookup(WinUsb_WriteIsochPipeAsap); WinUSB_Lookup(WinUsb_UnregisterIsochBuffer); + WinUSB_Lookup(WinUsb_GetCurrentFrameNumber); + return TRUE; } #endif @@ -799,7 +1026,13 @@ static int usb_open(void){ int r = -1; #ifdef ENABLE_SCO_OVER_HCI - usb_lookup_symbols(); + BOOL ok = usb_lookup_symbols(); + if (!ok){ + log_error("usb_open: Failed to lookup WinSUB ISOCHRONOUS functions. Please disable ENABLE_SCO_OVER_HCI or use Windows 8.1 or higher"); + return r; + } + sco_state_machine_init(); + sco_ring_init(); #endif HDEVINFO hDevInfo; @@ -817,8 +1050,6 @@ static int usb_open(void){ sco_in_addr = 0x83; // EP3, IN isochronous sco_out_addr = 0x03; // EP3, OUT isochronous - sco_state_machine_init(); - // We will try to get device information set for all USB devices that have a // device interface and are currently present on the system (plugged in). hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); @@ -932,6 +1163,9 @@ static int usb_close(void){ for (i=0;i