From 19423ce79f7c9bd2e27f45a96cffa36a514342c0 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Mon, 16 Jan 2017 14:44:17 +0100 Subject: [PATCH] winusb: fix compile without enable_sco_over_hci --- platform/windows/hci_transport_h2_winusb.c | 2603 ++++++++++---------- 1 file changed, 1299 insertions(+), 1304 deletions(-) diff --git a/platform/windows/hci_transport_h2_winusb.c b/platform/windows/hci_transport_h2_winusb.c index 150b570fa..5bd4897dd 100644 --- a/platform/windows/hci_transport_h2_winusb.c +++ b/platform/windows/hci_transport_h2_winusb.c @@ -1,1304 +1,1299 @@ -/* - * Copyright (C) 2014 BlueKitchen GmbH - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * 4. Any redistribution, use, or modification is done solely for - * personal benefit and not for any commercial purpose or for - * monetary gain. - * - * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS - * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Please inquire about commercial licensing options at - * contact@bluekitchen-gmbh.com - * - */ - -/* - * hci_transport_usb.c - * - * HCI Transport API implementation for USB - * - * Created by Matthias Ringwald on 7/5/09. - */ - -// Interface Number - Alternate Setting - suggested Endpoint Address - Endpoint Type - Suggested Max Packet Size -// HCI Commands 0 0 0x00 Control 8/16/32/64 -// HCI Events 0 0 0x81 Interrupt (IN) 16 -// ACL Data 0 0 0x82 Bulk (IN) 32/64 -// ACL Data 0 0 0x02 Bulk (OUT) 32/64 -// SCO Data 0 0 0x83 Isochronous (IN) -// SCO Data 0 0 0x03 Isochronous (Out) - -#include -#include -#include -#include /* UNIX standard function definitions */ -#include -#include // to print long long int (aka 64 bit ints) - -#include "btstack_config.h" - -#include "btstack_debug.h" -#include "hci.h" -#include "hci_transport.h" - -#include -#include -#include - -#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; - USHORT MaximumPacketSize; - UCHAR Interval; - ULONG MaximumBytesPerInterval; -} WINUSB_PIPE_INFORMATION_EX, *PWINUSB_PIPE_INFORMATION_EX; - -typedef WINBOOL (WINAPI * WinUsb_QueryPipeEx_t) ( - WINUSB_INTERFACE_HANDLE InterfaceHandle, - UCHAR AlternateInterfaceNumber, - UCHAR PipeIndex, - PWINUSB_PIPE_INFORMATION_EX PipeInformationEx -); -typedef WINBOOL (WINAPI * WinUsb_RegisterIsochBuffer_t)( - WINUSB_INTERFACE_HANDLE InterfaceHandle, - UCHAR PipeID, - PVOID Buffer, - ULONG BufferLength, - PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle -); -typedef WINBOOL (WINAPI * WinUsb_ReadIsochPipe_t)( - PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle, - ULONG Offset, - ULONG Length, - PULONG FrameNumber, - ULONG NumberOfPackets, // MSDN lists PULONG - PUSBD_ISO_PACKET_DESCRIPTOR IsoPacketDescriptors, // MSDN lists PULONG - LPOVERLAPPED Overlapped -); -typedef WINBOOL (WINAPI * WinUsb_ReadIsochPipeAsap_t)( - PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle, - ULONG Offset, - ULONG Length, - BOOL ContinueStream, - ULONG NumberOfPackets, // MSDN lists PULONG - PUSBD_ISO_PACKET_DESCRIPTOR IsoPacketDescriptors, - LPOVERLAPPED Overlapped -); -typedef WINBOOL (WINAPI * WinUsb_WriteIsochPipe_t)( - PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle, - ULONG Offset, - ULONG Length, - PULONG FrameNumber, - LPOVERLAPPED Overlapped -); -typedef WINBOOL (WINAPI * WinUsb_WriteIsochPipeAsap_t)( - PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle, - ULONG Offset, - ULONG Length, - BOOL ContinueStream, - LPOVERLAPPED Overlapped -); -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; -static WinUsb_ReadIsochPipe_t WinUsb_ReadIsochPipe; -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 - -// Doesn't work as expected -// #define SCHEDULE_SCO_IN_TRANSFERS_MANUALLY - -// Not tested yet -// #define SCHEDULE_SCO_OUT_TRANSFERS_MANUALLY - -// -// Bluetooth USB Transport 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 (1) - -// 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 (NUM_ISO_PACKETS * ISO_PACKET_SIZE) - -#define ISOC_BUFFERS 8 - -// Outgoing SCO packet queue -// simplified ring buffer implementation -#define SCO_RING_BUFFER_COUNT (20) -#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), - USB_REQUEST_TYPE_CLASS = (0x01 << 5), - USB_REQUEST_TYPE_VENDOR = (0x02 << 5), -}; - -/** Recipient bits of the "bmRequestType" field in control transfers. Values 4 through 31 are reserved. */ -enum usb_request_recipient { - USB_RECIPIENT_DEVICE = 0x00, - USB_RECIPIENT_INTERFACE = 0x01, - USB_RECIPIENT_ENDPOINT = 0x02, - USB_RECIPIENT_OTHER = 0x03, -}; - -// This is the GUID for the USB device class -static GUID GUID_DEVINTERFACE_USB_DEVICE = -{ 0xA5DCBF10L, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } }; - -static void usb_dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size); - -static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = &usb_dummy_handler; - -// endpoint addresses -static int event_in_addr; -static int acl_in_addr; -static int acl_out_addr; -static int sco_in_addr; -static int sco_out_addr; - -// -static HANDLE usb_device_handle; -static WINUSB_INTERFACE_HANDLE usb_interface_0_handle; -static WINUSB_INTERFACE_HANDLE usb_interface_1_handle; -static OVERLAPPED usb_overlapped_event_in; -static OVERLAPPED usb_overlapped_command_out; -static OVERLAPPED usb_overlapped_acl_in; -static OVERLAPPED usb_overlapped_acl_out; -static btstack_data_source_t usb_data_source_event_in; -static btstack_data_source_t usb_data_source_command_out; -static btstack_data_source_t usb_data_source_acl_in; -static btstack_data_source_t usb_data_source_acl_out; - -// -static int usb_command_out_active; -static int usb_acl_out_active; - -// buffer for HCI Events and ACL Packets -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 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 -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 WINUSB_ISOCH_BUFFER_HANDLE hci_sco_out_buffer_handle; -static OVERLAPPED usb_overlapped_sco_out[SCO_RING_BUFFER_COUNT]; -static int sco_ring_transfers_active; -static int usb_sco_out_expected_transfer; - -#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY -// next tranfer -static ULONG sco_next_transfer_at_frame; -#endif - -// 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 -// list of known devices, using VendorID/ProductID tuples -static const uint16_t known_bluetooth_devices[] = { - // DeLOCK Bluetooth 4.0 - 0x0a5c, 0x21e8, - // Asus BT400 - 0x0b05, 0x17cb, -}; - -static int num_known_devices = sizeof(known_bluetooth_devices) / sizeof(uint16_t) / 2; - -static int usb_is_known_bluetooth_device(uint16_t vendor_id, uint16_t product_id){ - int i; - for (i=0; i wait for completed - btstack_run_loop_enable_data_source_callbacks(&usb_data_source_event_in, DATA_SOURCE_CALLBACK_READ); - return; - -exit_on_error: - log_error("usb_submit_event_in_transfer: winusb last error %lu", GetLastError()); -} - -static void usb_submit_acl_in_transfer(void){ - // submit transfer - BOOL result = WinUsb_ReadPipe(usb_interface_0_handle, acl_in_addr, hci_acl_in_buffer, sizeof(hci_acl_in_buffer), NULL, &usb_overlapped_acl_in); - if (!result) { - if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error; - } - - // IO_PENDING -> wait for completed - btstack_run_loop_enable_data_source_callbacks(&usb_data_source_acl_in, DATA_SOURCE_CALLBACK_READ); - return; - -exit_on_error: - log_error("usb_submit_acl_in_transfer: winusb last error %lu", GetLastError()); -} - -#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY - -// frame number gets updated -static void usb_submit_sco_in_transfer_at_frame(int i, ULONG * frame_number){ - - 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) { - } else { - goto exit_on_error; - } - } - - return; - -exit_on_error: - log_error("usb_submit_sco_in_transfer: winusb last error %lu", GetLastError()); -} - -#else - -static void usb_submit_sco_in_transfer_asap(int i, int continue_stream){ - - LARGE_INTEGER timestamp; - ULONG current_frame_number; - WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); - - // log_info("usb_submit_sco_in_transfer[%02u]: 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]); - - if (!result) { - if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error; - } - - 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); - - DWORD bytes_read; - BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_event_in, &bytes_read, FALSE); - if(!ok){ - DWORD err = GetLastError(); - if (err == ERROR_IO_INCOMPLETE){ - // IO_INCOMPLETE -> wait for completed - btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); - } else { - log_error("usb_process_event_in: error reading"); - } - return; - } - - // notify uppper - packet_handler(HCI_EVENT_PACKET, hci_event_in_buffer, bytes_read); - - // re-submit transfer - usb_submit_event_in_transfer(); -} - -static void usb_process_acl_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); - - DWORD bytes_read; - BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_acl_in, &bytes_read, FALSE); - if(!ok){ - DWORD err = GetLastError(); - if (err == ERROR_IO_INCOMPLETE){ - // IO_INCOMPLETE -> wait for completed - btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); - } else { - log_error("usb_process_acl_in: error writing"); - } - return; - } - - // notify uppper - packet_handler(HCI_ACL_DATA_PACKET, hci_acl_in_buffer, bytes_read); - - // re-submit transfer - usb_submit_acl_in_transfer(); -} - -static void sco_state_machine_init(void){ - sco_state = H2_W4_SCO_HEADER; - sco_read_pos = 0; - sco_bytes_to_read = 3; -} - -static void sco_handle_data(uint8_t * buffer, uint16_t size){ - // printf("sco_handle_data: state %u, pos %u, to read %u, size %u", sco_state, sco_read_pos, sco_bytes_to_read, size); - while (size){ - if (size < sco_bytes_to_read){ - // just store incomplete data - memcpy(&sco_buffer[sco_read_pos], buffer, size); - sco_read_pos += size; - sco_bytes_to_read -= size; - return; - } - // copy requested data - memcpy(&sco_buffer[sco_read_pos], buffer, sco_bytes_to_read); - sco_read_pos += sco_bytes_to_read; - buffer += sco_bytes_to_read; - size -= sco_bytes_to_read; - - // chunk read successfully, next action - switch (sco_state){ - case H2_W4_SCO_HEADER: - sco_state = H2_W4_PAYLOAD; - sco_bytes_to_read = sco_buffer[2]; - if (sco_bytes_to_read > (sizeof(sco_buffer)-3)){ - log_error("sco_handle_data: sco packet len > packet size"); - sco_state_machine_init(); - } - break; - case H2_W4_PAYLOAD: - // packet complete - packet_handler(HCI_SCO_DATA_PACKET, sco_buffer, sco_read_pos); - sco_state_machine_init(); - break; - } - } -} - -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); - - // get current frame number - ULONG current_frame_number; - LARGE_INTEGER timestamp; - WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); - - // find index - int transfer_index; - for (transfer_index=0;transfer_index wait for completed - btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_out[transfer_index], DATA_SOURCE_CALLBACK_WRITE); - return; - } - log_error("usb_process_sco_out_done[%02u]: error writing %u, Internal %x", transfer_index, (int) err, (int) usb_overlapped_sco_out[transfer_index].Internal); - } - - // decrease tab - sco_ring_transfers_active--; - - // enable next data source callback - if (sco_ring_transfers_active){ - // update expected and wait for completion - usb_sco_out_expected_transfer = (transfer_index+ 1) % SCO_RING_BUFFER_COUNT; - log_info("usb_process_sco_out_done[%02u]: wait for transfer %02u", transfer_index, usb_sco_out_expected_transfer); - btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_out[usb_sco_out_expected_transfer], DATA_SOURCE_CALLBACK_WRITE); - } - - log_info("usb_process_sco_out_done: transfers active %u", sco_ring_transfers_active); - - // 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)); - } -} - -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); - - - // find index - int i; - for (i=0;i wait for completed - btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); - return; - } - log_error("usb_process_sco_in[%02u]: error reading %u, Internal %x", transfer_index, (int) err, (int) usb_overlapped_sco_out[i].Internal); - } - - 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; - sco_handle_data(iso_data, iso_len); - } - } - } - -#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY - usb_submit_sco_in_transfer_at_frame(i, &sco_next_transfer_at_frame); -#else - usb_submit_sco_in_transfer_asap(transfer_index, 1); -#endif - // update expected and wait for completion - usb_sco_in_expected_transfer = (transfer_index+ 1) % ISOC_BUFFERS; - - // log_info("usb_process_sco_in[%02u]: enable data source %02u", transfer_index, usb_sco_in_expected_transfer); - btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[usb_sco_in_expected_transfer], DATA_SOURCE_CALLBACK_READ); -} - -static void usb_process_command_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); - - // update stata before submitting transfer - usb_command_out_active = 0; - - // 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)); -} - -static void usb_process_acl_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); - - // update stata before submitting transfer - usb_acl_out_active = 0; - - // 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)); -} - -static BOOL usb_scan_for_bluetooth_endpoints(void) { - int i; - USB_INTERFACE_DESCRIPTOR usb_interface_descriptor; - - // reset - event_in_addr = 0; - acl_in_addr = 0; - acl_out_addr = 0; - - log_info("Scanning USB Entpoints:"); - - // look for Event and ACL pipes on Interface #0 - BOOL result = WinUsb_QueryInterfaceSettings(usb_interface_0_handle, 0, &usb_interface_descriptor); - if (!result) goto exit_on_error; - for (i=0;i using 0x%2.2X for HCI Events", event_in_addr); - break; - case USB_ENDPOINT_TYPE_BULK: - if (pipe.PipeId & 0x80) { - if (acl_in_addr) continue; - acl_in_addr = pipe.PipeId; - log_info("-> using 0x%2.2X for ACL Data In", acl_in_addr); - } else { - if (acl_out_addr) continue; - acl_out_addr = pipe.PipeId; - log_info("-> using 0x%2.2X for ACL Data Out", acl_out_addr); - } - break; - default: - break; - } - } - -#ifdef ENABLE_SCO_OVER_HCI - sco_out_addr = 0; - sco_in_addr = 0; - - // look for SCO pipes on Interface #1, Alt Setting ALT_SETTING - result = WinUsb_QueryInterfaceSettings(usb_interface_1_handle, ALT_SETTING, &usb_interface_descriptor); - if (!result) goto exit_on_error; - for (i=0;i using 0x%2.2X for SCO Data In", sco_in_addr); - } else { - if (sco_out_addr) continue; - sco_out_addr = pipe.PipeId; - log_info("-> using 0x%2.2X for SCO Data Out", sco_out_addr); - } - break; - default: - break; - } - } - if (!sco_in_addr){ - log_error("Couldn't find pipe for SCO IN!"); - return FALSE; - } - if (!sco_out_addr){ - log_error("Couldn't find pipe for SCO IN!"); - return FALSE; - } -#endif - - // check if all found - if (!event_in_addr){ - log_error("Couldn't find pipe for Event IN!"); - return FALSE; - } - if (!acl_in_addr){ - log_error("Couldn't find pipe for ACL IN!"); - return FALSE; - } - if (!acl_out_addr){ - log_error("Couldn't find pipe for ACL OUT!"); - return FALSE; - } - - // all clear - return TRUE; - -exit_on_error: - log_error("usb_scan_for_bluetooth_endpoints: last error %lu", GetLastError()); - return FALSE; -} - -// returns 0 if successful, -1 otherwise -static int usb_try_open_device(const char * device_path){ - - // open file - usb_device_handle = CreateFile(device_path, - GENERIC_WRITE | GENERIC_READ, - FILE_SHARE_WRITE | FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, - NULL); - log_info("Opening USB device: %p", usb_device_handle); - if (!usb_device_handle) goto exit_on_error; - - // WinUsb_Initialize returns TRUE if the operation succeed - BOOL result = WinUsb_Initialize(usb_device_handle, &usb_interface_0_handle); - if (!result) goto exit_on_error; - - // Detect USB Dongle based Class, Subclass, and Protocol - // The class code (bDeviceClass) is 0xE0 – Wireless Controller. - // The SubClass code (bDeviceSubClass) is 0x01 – RF Controller. - // The Protocol code (bDeviceProtocol) is 0x01 – Bluetooth programming. - USB_INTERFACE_DESCRIPTOR usb_interface_descriptor; - result = WinUsb_QueryInterfaceSettings(usb_interface_0_handle, 0, &usb_interface_descriptor); - if (!result) goto exit_on_error; - // - if (usb_interface_descriptor.bInterfaceClass != 0xe0 || - usb_interface_descriptor.bInterfaceSubClass != 0x01 || - usb_interface_descriptor.bInterfaceProtocol != 0x01){ - - // TODO: fallback to whitelist - log_info("Class, Subclass, Protocol does not match Bluetooth device"); - usb_free_resources(); - return 0; - } - -#ifdef ENABLE_SCO_OVER_HCI - log_info("Claiming interface 1..."); - // WinUsb_GetAssociatedInterface returns TRUE if the operation succeeds. - // We use index 1 - assuming it refers to interface #1 with libusb - // A value of 0 indicates the first associated interface, a value of 1 indicates the second associated interface, and so on. - result = WinUsb_GetAssociatedInterface(usb_interface_0_handle, 0, &usb_interface_1_handle); - if (!result) goto exit_on_error; - log_info("Claiming interface 1: success"); - - log_info("Switching to setting %u on interface 1..", ALT_SETTING); - // WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds. - result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, ALT_SETTING); - if (!result) goto exit_on_error; -#endif - - result = usb_scan_for_bluetooth_endpoints(); - if (!result) { - log_error("Could not find all Bluetooth Endpoints!"); - usb_free_resources(); - return 0; - } - -#ifdef ENABLE_SCO_OVER_HCI - - int i; - - memset(hci_sco_packet_descriptors, 0, sizeof(hci_sco_packet_descriptors)); - log_info("Size of packet descriptors for SCO IN%u", (int) sizeof(hci_sco_packet_descriptors)); - - result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_in_addr, hci_sco_in_buffer, ISOC_BUFFERS * SCO_PACKET_SIZE, &hci_sco_in_buffer_handle); - if (!result) goto exit_on_error; - log_info("hci_sco_in_buffer_handle %p", hci_sco_in_buffer_handle); - - result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_out_addr, sco_ring_buffer, sizeof(sco_ring_buffer), &hci_sco_out_buffer_handle); - if (!result) goto exit_on_error; - log_info("hci_sco_out_buffer_handle %p", hci_sco_out_buffer_handle); - - // setup async io && btstack handler - memset(&usb_overlapped_sco_in, 0, sizeof(usb_overlapped_sco_in)); - for (i=0;i %p", hDevInfo); - if (hDevInfo == INVALID_HANDLE_VALUE) return -1; - - // Prepare to enumerate all device interfaces for the device information - // set that we retrieved with SetupDiGetClassDevs(..) - DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - dwMemberIdx = 0; - - // Next, we will keep calling this SetupDiEnumDeviceInterfaces(..) until this - // function causes GetLastError() to return ERROR_NO_MORE_ITEMS. With each - // call the dwMemberIdx value needs to be incremented to retrieve the next - // device interface information. - - SetupDiEnumDeviceInterfaces(hDevInfo, NULL, (LPGUID) &GUID_DEVINTERFACE_USB_DEVICE, - dwMemberIdx, &DevIntfData); - - while(GetLastError() != ERROR_NO_MORE_ITEMS){ - - // As a last step we will need to get some more details for each - // of device interface information we are able to retrieve. This - // device interface detail gives us the information we need to identify - // the device (VID/PID), and decide if it's useful to us. It will also - // provide a DEVINFO_DATA structure which we can use to know the serial - // port name for a virtual com port. - - DevData.cbSize = sizeof(DevData); - - // Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail with - // a NULL DevIntfDetailData pointer, a DevIntfDetailDataSize - // of zero, and a valid RequiredSize variable. In response to such a call, - // this function returns the required buffer size at dwSize. - - SetupDiGetDeviceInterfaceDetail( - hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL); - - // Allocate memory for the DeviceInterfaceDetail struct. Don't forget to - // deallocate it later! - DevIntfDetailData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize); - DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, - DevIntfDetailData, dwSize, &dwSize, &DevData)) - { - // Finally we can start checking if we've found a useable device, - // by inspecting the DevIntfDetailData->DevicePath variable. - // The DevicePath looks something like this: - // - // \\?\usb#vid_04d8&pid_0033#5&19f2438f&0&2#{a5dcbf10-6530-11d2-901f-00c04fb951ed} - // - - log_info("usb_open: Device Path: %s", DevIntfDetailData->DevicePath); - -#if 0 - // check for hard-coded vendor/product ids - char vid_pid_match[30]; - uint16_t vid = 0x0a12; - uint16_t pid = 0x0001; - sprintf(vid_pid_match, "\\\\?\\usb#vid_%04x&pid_%04x", vid, pid); - if (strncmp(DevIntfDetailData->DevicePath, &vid_pid_match[0], strlen(vid_pid_match)) == 0 ){ - log_info("Matched search string %s", vid_pid_match); - - BOOL result = usb_try_open_device(DevIntfDetailData->DevicePath); - if (result){ - log_info("usb_open: Device opened, stop scanning"); - r = 0; - } else { - log_error("usb_open: Device open failed"); - } - } -#endif - - // try all devices - BOOL result = usb_try_open_device(DevIntfDetailData->DevicePath); - if (result){ - log_info("usb_open: Device opened, stop scanning"); - r = 0; - } else { - log_error("usb_open: Device open failed"); - } - } - HeapFree(GetProcessHeap(), 0, DevIntfDetailData); - - if (r == 0) break; - - // Continue looping - SetupDiEnumDeviceInterfaces( - hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE, ++dwMemberIdx, &DevIntfData); - } - - SetupDiDestroyDeviceInfoList(hDevInfo); - - log_info("usb_open: done"); - - return r; -} - -static int usb_close(void){ - - // remove data sources - btstack_run_loop_remove_data_source(&usb_data_source_command_out); - btstack_run_loop_remove_data_source(&usb_data_source_event_in); - btstack_run_loop_remove_data_source(&usb_data_source_acl_in); - btstack_run_loop_remove_data_source(&usb_data_source_acl_out); - -#ifdef ENABLE_SCO_OVER_HCI - int i; - for (i=0;i wait for completed - btstack_run_loop_enable_data_source_callbacks(&usb_data_source_command_out, DATA_SOURCE_CALLBACK_WRITE); - - return 0; - -exit_on_error: - log_error("winusb: last error %lu", GetLastError()); - return -1; -} - -static int usb_send_acl_packet(uint8_t *packet, int size){ - - // update stata before submitting transfer - usb_acl_out_active = 1; - - // Start trasnsfer - BOOL ok = WinUsb_WritePipe(usb_interface_0_handle, acl_out_addr, packet, size, NULL, &usb_overlapped_acl_out); - if (!ok) { - if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error; - } - - // IO_PENDING -> wait for completed - btstack_run_loop_enable_data_source_callbacks(&usb_data_source_acl_out, DATA_SOURCE_CALLBACK_WRITE); - return 0; - -exit_on_error: - log_error("winusb: last error %lu", GetLastError()); - return -1; -} - -#ifdef ENABLE_SCO_OVER_HCI -static int usb_send_sco_packet(uint8_t *packet, int size){ - - if (size > SCO_PACKET_SIZE){ - log_error("usb_send_sco_packet: size %u > SCO_PACKET_SIZE %u", size, SCO_PACKET_SIZE); - return -1; - } - - // get current frame number - ULONG current_frame_number; - LARGE_INTEGER timestamp; - WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); - - // store packet in free slot - int transfer_index = sco_ring_write; - uint8_t * data = &sco_ring_buffer[transfer_index * SCO_PACKET_SIZE]; - memcpy(data, packet, size); - - - // setup transfer - int continue_stream = sco_ring_transfers_active > 0; - BOOL ok = WinUsb_WriteIsochPipeAsap(hci_sco_out_buffer_handle, transfer_index * SCO_PACKET_SIZE, size, continue_stream, &usb_overlapped_sco_out[transfer_index]); - log_info("usb_send_sco_packet: using slot #%02u, current frame %lu, continue stream %u, ok %u", transfer_index, current_frame_number, continue_stream, ok); - if (!ok) { - if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error; - } - - // successful started transfer, enable data source callback if first active transfer - if (sco_ring_transfers_active == 0){ - usb_sco_out_expected_transfer = transfer_index; - btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_out[transfer_index], DATA_SOURCE_CALLBACK_WRITE); - } - - // mark slot as full - sco_ring_write = (sco_ring_write + 1) % SCO_RING_BUFFER_COUNT; - sco_ring_transfers_active++; - - // 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)); - - log_info("usb_send_sco_packet: transfers active %u", sco_ring_transfers_active); - - // 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)); - } - return 0; - -exit_on_error: - log_error("usb_send_sco_packet: last error %lu", GetLastError()); - return -1; -} -#endif - -static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){ - switch (packet_type){ - case HCI_COMMAND_DATA_PACKET: - return usb_send_cmd_packet(packet, size); - case HCI_ACL_DATA_PACKET: - return usb_send_acl_packet(packet, size); -#ifdef ENABLE_SCO_OVER_HCI - case HCI_SCO_DATA_PACKET: - return usb_send_sco_packet(packet, size); -#endif - default: - return -1; - } -} - -// get usb singleton -static const hci_transport_t hci_transport_usb = { - /* const char * name; */ "H2_WINUSB", - /* void (*init) (const void *transport_config); */ &usb_init, - /* int (*open)(void); */ &usb_open, - /* int (*close)(void); */ &usb_close, - /* void (*register_packet_handler)(void (*handler)(...); */ &usb_register_packet_handler, - /* int (*can_send_packet_now)(uint8_t packet_type); */ &usb_can_send_packet_now, - /* int (*send_packet)(...); */ &usb_send_packet, - /* int (*set_baudrate)(uint32_t baudrate); */ NULL, - /* void (*reset_link)(void); */ NULL, -}; - -const hci_transport_t * hci_transport_usb_instance(void) { - return &hci_transport_usb; -} +/* + * Copyright (C) 2014 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * hci_transport_usb.c + * + * HCI Transport API implementation for USB + * + * Created by Matthias Ringwald on 7/5/09. + */ + +// Interface Number - Alternate Setting - suggested Endpoint Address - Endpoint Type - Suggested Max Packet Size +// HCI Commands 0 0 0x00 Control 8/16/32/64 +// HCI Events 0 0 0x81 Interrupt (IN) 16 +// ACL Data 0 0 0x82 Bulk (IN) 32/64 +// ACL Data 0 0 0x02 Bulk (OUT) 32/64 +// SCO Data 0 0 0x83 Isochronous (IN) +// SCO Data 0 0 0x03 Isochronous (Out) + +#include +#include +#include +#include /* UNIX standard function definitions */ +#include +#include // to print long long int (aka 64 bit ints) + +#include "btstack_config.h" + +#include "btstack_debug.h" +#include "hci.h" +#include "hci_transport.h" + +#include +#include +#include + +#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; + USHORT MaximumPacketSize; + UCHAR Interval; + ULONG MaximumBytesPerInterval; +} WINUSB_PIPE_INFORMATION_EX, *PWINUSB_PIPE_INFORMATION_EX; + +typedef WINBOOL (WINAPI * WinUsb_QueryPipeEx_t) ( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AlternateInterfaceNumber, + UCHAR PipeIndex, + PWINUSB_PIPE_INFORMATION_EX PipeInformationEx +); +typedef WINBOOL (WINAPI * WinUsb_RegisterIsochBuffer_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + PVOID Buffer, + ULONG BufferLength, + PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle +); +typedef WINBOOL (WINAPI * WinUsb_ReadIsochPipe_t)( + PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle, + ULONG Offset, + ULONG Length, + PULONG FrameNumber, + ULONG NumberOfPackets, // MSDN lists PULONG + PUSBD_ISO_PACKET_DESCRIPTOR IsoPacketDescriptors, // MSDN lists PULONG + LPOVERLAPPED Overlapped +); +typedef WINBOOL (WINAPI * WinUsb_ReadIsochPipeAsap_t)( + PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle, + ULONG Offset, + ULONG Length, + BOOL ContinueStream, + ULONG NumberOfPackets, // MSDN lists PULONG + PUSBD_ISO_PACKET_DESCRIPTOR IsoPacketDescriptors, + LPOVERLAPPED Overlapped +); +typedef WINBOOL (WINAPI * WinUsb_WriteIsochPipe_t)( + PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle, + ULONG Offset, + ULONG Length, + PULONG FrameNumber, + LPOVERLAPPED Overlapped +); +typedef WINBOOL (WINAPI * WinUsb_WriteIsochPipeAsap_t)( + PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle, + ULONG Offset, + ULONG Length, + BOOL ContinueStream, + LPOVERLAPPED Overlapped +); +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; +static WinUsb_ReadIsochPipe_t WinUsb_ReadIsochPipe; +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 + +// Doesn't work as expected +// #define SCHEDULE_SCO_IN_TRANSFERS_MANUALLY + +// Not tested yet +// #define SCHEDULE_SCO_OUT_TRANSFERS_MANUALLY + +// +// Bluetooth USB Transport 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 (1) + +// 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 (NUM_ISO_PACKETS * ISO_PACKET_SIZE) + +#define ISOC_BUFFERS 8 + +// Outgoing SCO packet queue +// simplified ring buffer implementation +#define SCO_RING_BUFFER_COUNT (20) +#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), + USB_REQUEST_TYPE_CLASS = (0x01 << 5), + USB_REQUEST_TYPE_VENDOR = (0x02 << 5), +}; + +/** Recipient bits of the "bmRequestType" field in control transfers. Values 4 through 31 are reserved. */ +enum usb_request_recipient { + USB_RECIPIENT_DEVICE = 0x00, + USB_RECIPIENT_INTERFACE = 0x01, + USB_RECIPIENT_ENDPOINT = 0x02, + USB_RECIPIENT_OTHER = 0x03, +}; + +// This is the GUID for the USB device class +static GUID GUID_DEVINTERFACE_USB_DEVICE = +{ 0xA5DCBF10L, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } }; + +static void usb_dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size); + +static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = &usb_dummy_handler; + +// endpoint addresses +static int event_in_addr; +static int acl_in_addr; +static int acl_out_addr; +static int sco_in_addr; +static int sco_out_addr; + +// +static HANDLE usb_device_handle; +static WINUSB_INTERFACE_HANDLE usb_interface_0_handle; +static WINUSB_INTERFACE_HANDLE usb_interface_1_handle; +static OVERLAPPED usb_overlapped_event_in; +static OVERLAPPED usb_overlapped_command_out; +static OVERLAPPED usb_overlapped_acl_in; +static OVERLAPPED usb_overlapped_acl_out; +static btstack_data_source_t usb_data_source_event_in; +static btstack_data_source_t usb_data_source_command_out; +static btstack_data_source_t usb_data_source_acl_in; +static btstack_data_source_t usb_data_source_acl_out; + +// +static int usb_command_out_active; +static int usb_acl_out_active; + +// buffer for HCI Events and ACL Packets +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 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 +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 WINUSB_ISOCH_BUFFER_HANDLE hci_sco_out_buffer_handle; +static OVERLAPPED usb_overlapped_sco_out[SCO_RING_BUFFER_COUNT]; +static int sco_ring_transfers_active; +static int usb_sco_out_expected_transfer; + +#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY +// next tranfer +static ULONG sco_next_transfer_at_frame; +#endif + +// 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 +// list of known devices, using VendorID/ProductID tuples +static const uint16_t known_bluetooth_devices[] = { + // DeLOCK Bluetooth 4.0 + 0x0a5c, 0x21e8, + // Asus BT400 + 0x0b05, 0x17cb, +}; + +static int num_known_devices = sizeof(known_bluetooth_devices) / sizeof(uint16_t) / 2; + +static int usb_is_known_bluetooth_device(uint16_t vendor_id, uint16_t product_id){ + int i; + for (i=0; i wait for completed + btstack_run_loop_enable_data_source_callbacks(&usb_data_source_event_in, DATA_SOURCE_CALLBACK_READ); + return; + +exit_on_error: + log_error("usb_submit_event_in_transfer: winusb last error %lu", GetLastError()); +} + +static void usb_submit_acl_in_transfer(void){ + // submit transfer + BOOL result = WinUsb_ReadPipe(usb_interface_0_handle, acl_in_addr, hci_acl_in_buffer, sizeof(hci_acl_in_buffer), NULL, &usb_overlapped_acl_in); + if (!result) { + if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error; + } + + // IO_PENDING -> wait for completed + btstack_run_loop_enable_data_source_callbacks(&usb_data_source_acl_in, DATA_SOURCE_CALLBACK_READ); + return; + +exit_on_error: + log_error("usb_submit_acl_in_transfer: winusb last error %lu", GetLastError()); +} + +#ifdef ENABLE_SCO_OVER_HCI +#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY + +// frame number gets updated +static void usb_submit_sco_in_transfer_at_frame(int i, ULONG * frame_number){ + + 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) { + } else { + goto exit_on_error; + } + } + + return; + +exit_on_error: + log_error("usb_submit_sco_in_transfer: winusb last error %lu", GetLastError()); +} + +#else + +static void usb_submit_sco_in_transfer_asap(int i, int continue_stream){ + + LARGE_INTEGER timestamp; + ULONG current_frame_number; + WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); + + // log_info("usb_submit_sco_in_transfer[%02u]: 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]); + + if (!result) { + if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error; + } + + return; + +exit_on_error: + log_error("usb_submit_sco_in_transfer: winusb last error %lu", GetLastError()); +} +#endif +#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); + + DWORD bytes_read; + BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_event_in, &bytes_read, FALSE); + if(!ok){ + DWORD err = GetLastError(); + if (err == ERROR_IO_INCOMPLETE){ + // IO_INCOMPLETE -> wait for completed + btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); + } else { + log_error("usb_process_event_in: error reading"); + } + return; + } + + // notify uppper + packet_handler(HCI_EVENT_PACKET, hci_event_in_buffer, bytes_read); + + // re-submit transfer + usb_submit_event_in_transfer(); +} + +static void usb_process_acl_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); + + DWORD bytes_read; + BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_acl_in, &bytes_read, FALSE); + if(!ok){ + DWORD err = GetLastError(); + if (err == ERROR_IO_INCOMPLETE){ + // IO_INCOMPLETE -> wait for completed + btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); + } else { + log_error("usb_process_acl_in: error writing"); + } + return; + } + + // notify uppper + packet_handler(HCI_ACL_DATA_PACKET, hci_acl_in_buffer, bytes_read); + + // re-submit transfer + usb_submit_acl_in_transfer(); +} + +#ifdef ENABLE_SCO_OVER_HCI +static void sco_state_machine_init(void){ + sco_state = H2_W4_SCO_HEADER; + sco_read_pos = 0; + sco_bytes_to_read = 3; +} + +static void sco_handle_data(uint8_t * buffer, uint16_t size){ + // printf("sco_handle_data: state %u, pos %u, to read %u, size %u", sco_state, sco_read_pos, sco_bytes_to_read, size); + while (size){ + if (size < sco_bytes_to_read){ + // just store incomplete data + memcpy(&sco_buffer[sco_read_pos], buffer, size); + sco_read_pos += size; + sco_bytes_to_read -= size; + return; + } + // copy requested data + memcpy(&sco_buffer[sco_read_pos], buffer, sco_bytes_to_read); + sco_read_pos += sco_bytes_to_read; + buffer += sco_bytes_to_read; + size -= sco_bytes_to_read; + + // chunk read successfully, next action + switch (sco_state){ + case H2_W4_SCO_HEADER: + sco_state = H2_W4_PAYLOAD; + sco_bytes_to_read = sco_buffer[2]; + if (sco_bytes_to_read > (sizeof(sco_buffer)-3)){ + log_error("sco_handle_data: sco packet len > packet size"); + sco_state_machine_init(); + } + break; + case H2_W4_PAYLOAD: + // packet complete + packet_handler(HCI_SCO_DATA_PACKET, sco_buffer, sco_read_pos); + sco_state_machine_init(); + break; + } + } +} + +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); + + // get current frame number + ULONG current_frame_number; + LARGE_INTEGER timestamp; + WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); + + // find index + int transfer_index; + for (transfer_index=0;transfer_index wait for completed + btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_out[transfer_index], DATA_SOURCE_CALLBACK_WRITE); + return; + } + log_error("usb_process_sco_out_done[%02u]: error writing %u, Internal %x", transfer_index, (int) err, (int) usb_overlapped_sco_out[transfer_index].Internal); + } + + // decrease tab + sco_ring_transfers_active--; + + // enable next data source callback + if (sco_ring_transfers_active){ + // update expected and wait for completion + usb_sco_out_expected_transfer = (transfer_index+ 1) % SCO_RING_BUFFER_COUNT; + log_info("usb_process_sco_out_done[%02u]: wait for transfer %02u", transfer_index, usb_sco_out_expected_transfer); + btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_out[usb_sco_out_expected_transfer], DATA_SOURCE_CALLBACK_WRITE); + } + + log_info("usb_process_sco_out_done: transfers active %u", sco_ring_transfers_active); + + // 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)); + } +} + +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); + + + // find index + int i; + for (i=0;i wait for completed + btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); + return; + } + log_error("usb_process_sco_in[%02u]: error reading %u, Internal %x", transfer_index, (int) err, (int) usb_overlapped_sco_out[i].Internal); + } + + 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; + sco_handle_data(iso_data, iso_len); + } + } + } + +#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY + usb_submit_sco_in_transfer_at_frame(i, &sco_next_transfer_at_frame); +#else + usb_submit_sco_in_transfer_asap(transfer_index, 1); +#endif + // update expected and wait for completion + usb_sco_in_expected_transfer = (transfer_index+ 1) % ISOC_BUFFERS; + + // log_info("usb_process_sco_in[%02u]: enable data source %02u", transfer_index, usb_sco_in_expected_transfer); + btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[usb_sco_in_expected_transfer], DATA_SOURCE_CALLBACK_READ); +} +#endif + +static void usb_process_command_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); + + // update stata before submitting transfer + usb_command_out_active = 0; + + // 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)); +} + +static void usb_process_acl_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); + + // update stata before submitting transfer + usb_acl_out_active = 0; + + // 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)); +} + +static BOOL usb_scan_for_bluetooth_endpoints(void) { + int i; + USB_INTERFACE_DESCRIPTOR usb_interface_descriptor; + + // reset + event_in_addr = 0; + acl_in_addr = 0; + acl_out_addr = 0; + + log_info("Scanning USB Entpoints:"); + + // look for Event and ACL pipes on Interface #0 + BOOL result = WinUsb_QueryInterfaceSettings(usb_interface_0_handle, 0, &usb_interface_descriptor); + if (!result) goto exit_on_error; + for (i=0;i using 0x%2.2X for HCI Events", event_in_addr); + break; + case USB_ENDPOINT_TYPE_BULK: + if (pipe.PipeId & 0x80) { + if (acl_in_addr) continue; + acl_in_addr = pipe.PipeId; + log_info("-> using 0x%2.2X for ACL Data In", acl_in_addr); + } else { + if (acl_out_addr) continue; + acl_out_addr = pipe.PipeId; + log_info("-> using 0x%2.2X for ACL Data Out", acl_out_addr); + } + break; + default: + break; + } + } + +#ifdef ENABLE_SCO_OVER_HCI + sco_out_addr = 0; + sco_in_addr = 0; + + // look for SCO pipes on Interface #1, Alt Setting ALT_SETTING + result = WinUsb_QueryInterfaceSettings(usb_interface_1_handle, ALT_SETTING, &usb_interface_descriptor); + if (!result) goto exit_on_error; + for (i=0;i using 0x%2.2X for SCO Data In", sco_in_addr); + } else { + if (sco_out_addr) continue; + sco_out_addr = pipe.PipeId; + log_info("-> using 0x%2.2X for SCO Data Out", sco_out_addr); + } + break; + default: + break; + } + } + if (!sco_in_addr){ + log_error("Couldn't find pipe for SCO IN!"); + return FALSE; + } + if (!sco_out_addr){ + log_error("Couldn't find pipe for SCO IN!"); + return FALSE; + } +#endif + + // check if all found + if (!event_in_addr){ + log_error("Couldn't find pipe for Event IN!"); + return FALSE; + } + if (!acl_in_addr){ + log_error("Couldn't find pipe for ACL IN!"); + return FALSE; + } + if (!acl_out_addr){ + log_error("Couldn't find pipe for ACL OUT!"); + return FALSE; + } + + // all clear + return TRUE; + +exit_on_error: + log_error("usb_scan_for_bluetooth_endpoints: last error %lu", GetLastError()); + return FALSE; +} + +// returns 0 if successful, -1 otherwise +static int usb_try_open_device(const char * device_path){ + + // open file + usb_device_handle = CreateFile(device_path, + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL); + log_info("Opening USB device: %p", usb_device_handle); + if (!usb_device_handle) goto exit_on_error; + + // WinUsb_Initialize returns TRUE if the operation succeed + BOOL result = WinUsb_Initialize(usb_device_handle, &usb_interface_0_handle); + if (!result) goto exit_on_error; + + // Detect USB Dongle based Class, Subclass, and Protocol + // The class code (bDeviceClass) is 0xE0 – Wireless Controller. + // The SubClass code (bDeviceSubClass) is 0x01 – RF Controller. + // The Protocol code (bDeviceProtocol) is 0x01 – Bluetooth programming. + USB_INTERFACE_DESCRIPTOR usb_interface_descriptor; + result = WinUsb_QueryInterfaceSettings(usb_interface_0_handle, 0, &usb_interface_descriptor); + if (!result) goto exit_on_error; + // + if (usb_interface_descriptor.bInterfaceClass != 0xe0 || + usb_interface_descriptor.bInterfaceSubClass != 0x01 || + usb_interface_descriptor.bInterfaceProtocol != 0x01){ + + // TODO: fallback to whitelist + log_info("Class, Subclass, Protocol does not match Bluetooth device"); + usb_free_resources(); + return 0; + } + +#ifdef ENABLE_SCO_OVER_HCI + log_info("Claiming interface 1..."); + // WinUsb_GetAssociatedInterface returns TRUE if the operation succeeds. + // We use index 1 - assuming it refers to interface #1 with libusb + // A value of 0 indicates the first associated interface, a value of 1 indicates the second associated interface, and so on. + result = WinUsb_GetAssociatedInterface(usb_interface_0_handle, 0, &usb_interface_1_handle); + if (!result) goto exit_on_error; + log_info("Claiming interface 1: success"); + + log_info("Switching to setting %u on interface 1..", ALT_SETTING); + // WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds. + result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, ALT_SETTING); + if (!result) goto exit_on_error; +#endif + + result = usb_scan_for_bluetooth_endpoints(); + if (!result) { + log_error("Could not find all Bluetooth Endpoints!"); + usb_free_resources(); + return 0; + } + +#ifdef ENABLE_SCO_OVER_HCI + int i; + + memset(hci_sco_packet_descriptors, 0, sizeof(hci_sco_packet_descriptors)); + log_info("Size of packet descriptors for SCO IN%u", (int) sizeof(hci_sco_packet_descriptors)); + + result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_in_addr, hci_sco_in_buffer, ISOC_BUFFERS * SCO_PACKET_SIZE, &hci_sco_in_buffer_handle); + if (!result) goto exit_on_error; + log_info("hci_sco_in_buffer_handle %p", hci_sco_in_buffer_handle); + + result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_out_addr, sco_ring_buffer, sizeof(sco_ring_buffer), &hci_sco_out_buffer_handle); + if (!result) goto exit_on_error; + log_info("hci_sco_out_buffer_handle %p", hci_sco_out_buffer_handle); + + // setup async io && btstack handler + memset(&usb_overlapped_sco_in, 0, sizeof(usb_overlapped_sco_in)); + for (i=0;i %p", hDevInfo); + if (hDevInfo == INVALID_HANDLE_VALUE) return -1; + + // Prepare to enumerate all device interfaces for the device information + // set that we retrieved with SetupDiGetClassDevs(..) + DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + dwMemberIdx = 0; + + // Next, we will keep calling this SetupDiEnumDeviceInterfaces(..) until this + // function causes GetLastError() to return ERROR_NO_MORE_ITEMS. With each + // call the dwMemberIdx value needs to be incremented to retrieve the next + // device interface information. + + SetupDiEnumDeviceInterfaces(hDevInfo, NULL, (LPGUID) &GUID_DEVINTERFACE_USB_DEVICE, + dwMemberIdx, &DevIntfData); + + while(GetLastError() != ERROR_NO_MORE_ITEMS){ + + // As a last step we will need to get some more details for each + // of device interface information we are able to retrieve. This + // device interface detail gives us the information we need to identify + // the device (VID/PID), and decide if it's useful to us. It will also + // provide a DEVINFO_DATA structure which we can use to know the serial + // port name for a virtual com port. + + DevData.cbSize = sizeof(DevData); + + // Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail with + // a NULL DevIntfDetailData pointer, a DevIntfDetailDataSize + // of zero, and a valid RequiredSize variable. In response to such a call, + // this function returns the required buffer size at dwSize. + + SetupDiGetDeviceInterfaceDetail( + hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL); + + // Allocate memory for the DeviceInterfaceDetail struct. Don't forget to + // deallocate it later! + DevIntfDetailData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize); + DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, + DevIntfDetailData, dwSize, &dwSize, &DevData)) + { + // Finally we can start checking if we've found a useable device, + // by inspecting the DevIntfDetailData->DevicePath variable. + // The DevicePath looks something like this: + // + // \\?\usb#vid_04d8&pid_0033#5&19f2438f&0&2#{a5dcbf10-6530-11d2-901f-00c04fb951ed} + // + + log_info("usb_open: Device Path: %s", DevIntfDetailData->DevicePath); + +#if 0 + // check for hard-coded vendor/product ids + char vid_pid_match[30]; + uint16_t vid = 0x0a12; + uint16_t pid = 0x0001; + sprintf(vid_pid_match, "\\\\?\\usb#vid_%04x&pid_%04x", vid, pid); + if (strncmp(DevIntfDetailData->DevicePath, &vid_pid_match[0], strlen(vid_pid_match)) == 0 ){ + log_info("Matched search string %s", vid_pid_match); + + BOOL result = usb_try_open_device(DevIntfDetailData->DevicePath); + if (result){ + log_info("usb_open: Device opened, stop scanning"); + r = 0; + } else { + log_error("usb_open: Device open failed"); + } + } +#endif + + // try all devices + BOOL result = usb_try_open_device(DevIntfDetailData->DevicePath); + if (result){ + log_info("usb_open: Device opened, stop scanning"); + r = 0; + } else { + log_error("usb_open: Device open failed"); + } + } + HeapFree(GetProcessHeap(), 0, DevIntfDetailData); + + if (r == 0) break; + + // Continue looping + SetupDiEnumDeviceInterfaces( + hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE, ++dwMemberIdx, &DevIntfData); + } + + SetupDiDestroyDeviceInfoList(hDevInfo); + + log_info("usb_open: done"); + + return r; +} + +static int usb_close(void){ + + // remove data sources + btstack_run_loop_remove_data_source(&usb_data_source_command_out); + btstack_run_loop_remove_data_source(&usb_data_source_event_in); + btstack_run_loop_remove_data_source(&usb_data_source_acl_in); + btstack_run_loop_remove_data_source(&usb_data_source_acl_out); + +#ifdef ENABLE_SCO_OVER_HCI + int i; + for (i=0;i wait for completed + btstack_run_loop_enable_data_source_callbacks(&usb_data_source_command_out, DATA_SOURCE_CALLBACK_WRITE); + + return 0; + +exit_on_error: + log_error("winusb: last error %lu", GetLastError()); + return -1; +} + +static int usb_send_acl_packet(uint8_t *packet, int size){ + + // update stata before submitting transfer + usb_acl_out_active = 1; + + // Start trasnsfer + BOOL ok = WinUsb_WritePipe(usb_interface_0_handle, acl_out_addr, packet, size, NULL, &usb_overlapped_acl_out); + if (!ok) { + if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error; + } + + // IO_PENDING -> wait for completed + btstack_run_loop_enable_data_source_callbacks(&usb_data_source_acl_out, DATA_SOURCE_CALLBACK_WRITE); + return 0; + +exit_on_error: + log_error("winusb: last error %lu", GetLastError()); + return -1; +} + +#ifdef ENABLE_SCO_OVER_HCI +static int usb_send_sco_packet(uint8_t *packet, int size){ + + if (size > SCO_PACKET_SIZE){ + log_error("usb_send_sco_packet: size %u > SCO_PACKET_SIZE %u", size, SCO_PACKET_SIZE); + return -1; + } + + // get current frame number + ULONG current_frame_number; + LARGE_INTEGER timestamp; + WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); + + // store packet in free slot + int transfer_index = sco_ring_write; + uint8_t * data = &sco_ring_buffer[transfer_index * SCO_PACKET_SIZE]; + memcpy(data, packet, size); + + + // setup transfer + int continue_stream = sco_ring_transfers_active > 0; + BOOL ok = WinUsb_WriteIsochPipeAsap(hci_sco_out_buffer_handle, transfer_index * SCO_PACKET_SIZE, size, continue_stream, &usb_overlapped_sco_out[transfer_index]); + log_info("usb_send_sco_packet: using slot #%02u, current frame %lu, continue stream %u, ok %u", transfer_index, current_frame_number, continue_stream, ok); + if (!ok) { + if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error; + } + + // successful started transfer, enable data source callback if first active transfer + if (sco_ring_transfers_active == 0){ + usb_sco_out_expected_transfer = transfer_index; + btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_out[transfer_index], DATA_SOURCE_CALLBACK_WRITE); + } + + // mark slot as full + sco_ring_write = (sco_ring_write + 1) % SCO_RING_BUFFER_COUNT; + sco_ring_transfers_active++; + + // 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)); + + log_info("usb_send_sco_packet: transfers active %u", sco_ring_transfers_active); + + // 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)); + } + return 0; + +exit_on_error: + log_error("usb_send_sco_packet: last error %lu", GetLastError()); + return -1; +} +#endif + +static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){ + switch (packet_type){ + case HCI_COMMAND_DATA_PACKET: + return usb_send_cmd_packet(packet, size); + case HCI_ACL_DATA_PACKET: + return usb_send_acl_packet(packet, size); +#ifdef ENABLE_SCO_OVER_HCI + case HCI_SCO_DATA_PACKET: + return usb_send_sco_packet(packet, size); +#endif + default: + return -1; + } +} + +// get usb singleton +static const hci_transport_t hci_transport_usb = { + /* const char * name; */ "H2_WINUSB", + /* void (*init) (const void *transport_config); */ &usb_init, + /* int (*open)(void); */ &usb_open, + /* int (*close)(void); */ &usb_close, + /* void (*register_packet_handler)(void (*handler)(...); */ &usb_register_packet_handler, + /* int (*can_send_packet_now)(uint8_t packet_type); */ &usb_can_send_packet_now, + /* int (*send_packet)(...); */ &usb_send_packet, + /* int (*set_baudrate)(uint32_t baudrate); */ NULL, + /* void (*reset_link)(void); */ NULL, +}; + +const hci_transport_t * hci_transport_usb_instance(void) { + return &hci_transport_usb; +}