xref: /btstack/platform/windows/hci_transport_h2_winusb.c (revision b795dcd07cafa39590ef7b7e12c7051fdb8ae36a)
15da69017SMatthias Ringwald /*
25da69017SMatthias Ringwald  * Copyright (C) 2014 BlueKitchen GmbH
35da69017SMatthias Ringwald  *
45da69017SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
55da69017SMatthias Ringwald  * modification, are permitted provided that the following conditions
65da69017SMatthias Ringwald  * are met:
75da69017SMatthias Ringwald  *
85da69017SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
95da69017SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
105da69017SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
115da69017SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
125da69017SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
135da69017SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
145da69017SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
155da69017SMatthias Ringwald  *    from this software without specific prior written permission.
165da69017SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
175da69017SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
185da69017SMatthias Ringwald  *    monetary gain.
195da69017SMatthias Ringwald  *
205da69017SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
215da69017SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
225da69017SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
255da69017SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
265da69017SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
275da69017SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
285da69017SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
295da69017SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
305da69017SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
315da69017SMatthias Ringwald  * SUCH DAMAGE.
325da69017SMatthias Ringwald  *
335da69017SMatthias Ringwald  * Please inquire about commercial licensing options at
345da69017SMatthias Ringwald  * [email protected]
355da69017SMatthias Ringwald  *
365da69017SMatthias Ringwald  */
37ab2c6ae4SMatthias Ringwald 
38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "hci_transport_h2_winusb.c"
395da69017SMatthias Ringwald 
405da69017SMatthias Ringwald /*
415da69017SMatthias Ringwald  *  hci_transport_usb.c
425da69017SMatthias Ringwald  *
435da69017SMatthias Ringwald  *  HCI Transport API implementation for USB
445da69017SMatthias Ringwald  *
455da69017SMatthias Ringwald  *  Created by Matthias Ringwald on 7/5/09.
465da69017SMatthias Ringwald  */
475da69017SMatthias Ringwald 
485da69017SMatthias Ringwald // Interface Number - Alternate Setting - suggested Endpoint Address - Endpoint Type - Suggested Max Packet Size
495da69017SMatthias Ringwald // HCI Commands 0 0 0x00 Control 8/16/32/64
505da69017SMatthias Ringwald // HCI Events   0 0 0x81 Interrupt (IN) 16
515da69017SMatthias Ringwald // ACL Data     0 0 0x82 Bulk (IN) 32/64
525da69017SMatthias Ringwald // ACL Data     0 0 0x02 Bulk (OUT) 32/64
535da69017SMatthias Ringwald // SCO Data     0 0 0x83 Isochronous (IN)
545da69017SMatthias Ringwald // SCO Data     0 0 0x03 Isochronous (Out)
555da69017SMatthias Ringwald 
56*b795dcd0SMarcel Wappler #include <Windows.h>
57*b795dcd0SMarcel Wappler 
585da69017SMatthias Ringwald #include <stdio.h>
595da69017SMatthias Ringwald #include <string.h>
605da69017SMatthias Ringwald #include <sys/types.h>
61f258f416SMatthias Ringwald #include <inttypes.h>   // to print long long int (aka 64 bit ints)
625da69017SMatthias Ringwald 
635da69017SMatthias Ringwald #include "btstack_config.h"
645da69017SMatthias Ringwald 
655da69017SMatthias Ringwald #include "btstack_debug.h"
665da69017SMatthias Ringwald #include "hci.h"
675da69017SMatthias Ringwald #include "hci_transport.h"
68c8dfe071SMatthias Ringwald #include "hci_transport_usb.h"
695da69017SMatthias Ringwald 
705da69017SMatthias Ringwald #include <SetupAPI.h>
715da69017SMatthias Ringwald #include <Winusb.h>
725da69017SMatthias Ringwald 
73f258f416SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
745da69017SMatthias Ringwald 
755da69017SMatthias Ringwald // Isochronous Add-On
765da69017SMatthias Ringwald 
77f258f416SMatthias Ringwald // Function signatures frome https://abi-laboratory.pro/compatibility/Windows_7.0_to_Windows_8.1/x86_64/info/winusb.dll/symbols.html
78f258f416SMatthias Ringwald // MSDN documentation has multiple errors (Jan 2017), annotated below
79f258f416SMatthias Ringwald 
80ecf76c8dSMatthias Ringwald // As Isochochronous functions are provided by newer versions of ming64, we use a BTstack/BTSTACK prefix to prevent name collisions
81f258f416SMatthias Ringwald 
82ecf76c8dSMatthias Ringwald typedef PVOID BTSTACK_WINUSB_ISOCH_BUFFER_HANDLE, *BTSTACK_PWINUSB_ISOCH_BUFFER_HANDLE;
83ecf76c8dSMatthias Ringwald 
84ecf76c8dSMatthias Ringwald typedef struct _BTSTACK_WINUSB_PIPE_INFORMATION_EX {
855da69017SMatthias Ringwald   USBD_PIPE_TYPE PipeType;
865da69017SMatthias Ringwald   UCHAR          PipeId;
875da69017SMatthias Ringwald   USHORT         MaximumPacketSize;
885da69017SMatthias Ringwald   UCHAR          Interval;
895da69017SMatthias Ringwald   ULONG          MaximumBytesPerInterval;
90ecf76c8dSMatthias Ringwald } BTSTACK_WINUSB_PIPE_INFORMATION_EX, *BTSTACK_PWINUSB_PIPE_INFORMATION_EX;
915da69017SMatthias Ringwald 
92b115538eSMatthias Ringwald typedef BOOL (WINAPI * BTstack_WinUsb_QueryPipeEx_t) (
935da69017SMatthias Ringwald 	WINUSB_INTERFACE_HANDLE 	InterfaceHandle,
945da69017SMatthias Ringwald 	UCHAR						AlternateInterfaceNumber,
955da69017SMatthias Ringwald 	UCHAR 						PipeIndex,
96ecf76c8dSMatthias Ringwald 	BTSTACK_PWINUSB_PIPE_INFORMATION_EX PipeInformationEx
975da69017SMatthias Ringwald );
98b115538eSMatthias Ringwald typedef BOOL (WINAPI * BTstack_WinUsb_RegisterIsochBuffer_t)(
995da69017SMatthias Ringwald 	WINUSB_INTERFACE_HANDLE     InterfaceHandle,
1005da69017SMatthias Ringwald 	UCHAR                       PipeID,
1015da69017SMatthias Ringwald 	PVOID                       Buffer,
1025da69017SMatthias Ringwald 	ULONG                       BufferLength,
103ecf76c8dSMatthias Ringwald 	BTSTACK_PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle
1045da69017SMatthias Ringwald );
105b115538eSMatthias Ringwald typedef BOOL (WINAPI * BTstack_WinUsb_ReadIsochPipe_t)(
106ecf76c8dSMatthias Ringwald     BTSTACK_PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle,
1075da69017SMatthias Ringwald     ULONG                       Offset,
1085da69017SMatthias Ringwald     ULONG                       Length,
1095da69017SMatthias Ringwald     PULONG                      FrameNumber,
110f258f416SMatthias Ringwald     ULONG                       NumberOfPackets,        // MSDN lists PULONG
111f258f416SMatthias Ringwald     PUSBD_ISO_PACKET_DESCRIPTOR IsoPacketDescriptors,   // MSDN lists PULONG
1125da69017SMatthias Ringwald     LPOVERLAPPED                Overlapped
1135da69017SMatthias Ringwald );
114b115538eSMatthias Ringwald typedef BOOL (WINAPI * BTstack_WinUsb_ReadIsochPipeAsap_t)(
115ecf76c8dSMatthias Ringwald     BTSTACK_PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle,
1165da69017SMatthias Ringwald     ULONG                       Offset,
1175da69017SMatthias Ringwald     ULONG                       Length,
1185da69017SMatthias Ringwald     BOOL                        ContinueStream,
119f258f416SMatthias Ringwald     ULONG                      	NumberOfPackets,        // MSDN lists PULONG
1205da69017SMatthias Ringwald     PUSBD_ISO_PACKET_DESCRIPTOR IsoPacketDescriptors,
1215da69017SMatthias Ringwald  	LPOVERLAPPED                Overlapped
1225da69017SMatthias Ringwald );
123b115538eSMatthias Ringwald typedef BOOL (WINAPI * BTstack_WinUsb_WriteIsochPipe_t)(
124ecf76c8dSMatthias Ringwald     BTSTACK_PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle,
1255da69017SMatthias Ringwald     ULONG                       Offset,
1265da69017SMatthias Ringwald     ULONG                       Length,
1275da69017SMatthias Ringwald     PULONG                      FrameNumber,
1285da69017SMatthias Ringwald 	LPOVERLAPPED                Overlapped
1295da69017SMatthias Ringwald );
130b115538eSMatthias Ringwald typedef BOOL (WINAPI * BTstack_WinUsb_WriteIsochPipeAsap_t)(
131ecf76c8dSMatthias Ringwald     BTSTACK_PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle,
1325da69017SMatthias Ringwald     ULONG                       Offset,
1335da69017SMatthias Ringwald     ULONG                       Length,
1345da69017SMatthias Ringwald     BOOL                        ContinueStream,
1355da69017SMatthias Ringwald 	LPOVERLAPPED                Overlapped
1365da69017SMatthias Ringwald );
137b115538eSMatthias Ringwald typedef BOOL (WINAPI * BTstack_WinUsb_UnregisterIsochBuffer_t)(
138ecf76c8dSMatthias Ringwald 	BTSTACK_PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle
1395da69017SMatthias Ringwald );
140b115538eSMatthias Ringwald typedef BOOL (WINAPI * BTstack_WinUsb_GetCurrentFrameNumber_t)(
141f258f416SMatthias Ringwald     WINUSB_INTERFACE_HANDLE     InterfaceHandle,        // MSDN lists 'Device handle returned from CreateFile'
142f258f416SMatthias Ringwald     PULONG                      CurrentFrameNumber,
143f258f416SMatthias Ringwald     LARGE_INTEGER               *TimeStamp
144f258f416SMatthias Ringwald );
1455da69017SMatthias Ringwald 
146ecf76c8dSMatthias Ringwald static BTstack_WinUsb_QueryPipeEx_t 			BTstack_WinUsb_QueryPipeEx;
147ecf76c8dSMatthias Ringwald static BTstack_WinUsb_RegisterIsochBuffer_t 	BTstack_WinUsb_RegisterIsochBuffer;
148ecf76c8dSMatthias Ringwald static BTstack_WinUsb_ReadIsochPipe_t 			BTstack_WinUsb_ReadIsochPipe;
149ecf76c8dSMatthias Ringwald static BTstack_WinUsb_ReadIsochPipeAsap_t 		BTstack_WinUsb_ReadIsochPipeAsap;
150ecf76c8dSMatthias Ringwald static BTstack_WinUsb_WriteIsochPipe_t 			BTstack_WinUsb_WriteIsochPipe;
151ecf76c8dSMatthias Ringwald static BTstack_WinUsb_WriteIsochPipeAsap_t 		BTstack_WinUsb_WriteIsochPipeAsap;
152ecf76c8dSMatthias Ringwald static BTstack_WinUsb_UnregisterIsochBuffer_t 	BTstack_WinUsb_UnregisterIsochBuffer;
153ecf76c8dSMatthias Ringwald static BTstack_WinUsb_GetCurrentFrameNumber_t   BTstack_WinUsb_GetCurrentFrameNumber;
1545da69017SMatthias Ringwald #endif
1555da69017SMatthias Ringwald 
15608dc2fadSMatthias Ringwald // Doesn't work as expected
15708dc2fadSMatthias Ringwald // #define SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
15808dc2fadSMatthias Ringwald 
15908dc2fadSMatthias Ringwald // Not tested yet
16008dc2fadSMatthias Ringwald // #define SCHEDULE_SCO_OUT_TRANSFERS_MANUALLY
16108dc2fadSMatthias Ringwald 
1625da69017SMatthias Ringwald //
1635da69017SMatthias Ringwald // Bluetooth USB Transport Alternate Settings:
1645da69017SMatthias Ringwald //
1655da69017SMatthias Ringwald // 0: No active voice channels (for USB compliance)
1665da69017SMatthias Ringwald // 1: One 8 kHz voice channel with 8-bit encoding
1675da69017SMatthias Ringwald // 2: Two 8 kHz voice channels with 8-bit encoding or one 8 kHz voice channel with 16-bit encoding
1685da69017SMatthias Ringwald // 3: Three 8 kHz voice channels with 8-bit encoding
1695da69017SMatthias Ringwald // 4: Two 8 kHz voice channels with 16-bit encoding or one 16 kHz voice channel with 16-bit encoding
1705da69017SMatthias Ringwald // 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
1715da69017SMatthias Ringwald // --> support only a single SCO connection
1725da69017SMatthias Ringwald #define ALT_SETTING (1)
1735da69017SMatthias Ringwald 
174a93756fcSMatthias Ringwald // alt setting for 1-3 connections and 8/16 bit
175a93756fcSMatthias Ringwald const int alt_setting_8_bit[]  = {1,2,3};
176a93756fcSMatthias Ringwald const int alt_setting_16_bit[] = {2,4,5};
177a93756fcSMatthias Ringwald 
1785da69017SMatthias Ringwald // for ALT_SETTING >= 1 and 8-bit channel, we need the following isochronous packets
1795da69017SMatthias Ringwald // One complete SCO packet with 24 frames every 3 frames (== 3 ms)
18008dc2fadSMatthias Ringwald #define NUM_ISO_PACKETS (3)
1815da69017SMatthias Ringwald 
182a93756fcSMatthias Ringwald const uint16_t iso_packet_size_for_alt_setting[] = {
183a93756fcSMatthias Ringwald     0,
184a93756fcSMatthias Ringwald     9,
185a93756fcSMatthias Ringwald     17,
186a93756fcSMatthias Ringwald     25,
187a93756fcSMatthias Ringwald     33,
188a93756fcSMatthias Ringwald     49,
189a93756fcSMatthias Ringwald     63,
190a93756fcSMatthias Ringwald };
191a93756fcSMatthias Ringwald 
1925da69017SMatthias Ringwald // 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)
1935da69017SMatthias Ringwald // 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
1945a97f1baSMatthias Ringwald #define SCO_PACKET_SIZE  (49 * NUM_ISO_PACKETS)
1955da69017SMatthias Ringwald 
19608dc2fadSMatthias Ringwald #define ISOC_BUFFERS   8
1975da69017SMatthias Ringwald 
198f258f416SMatthias Ringwald // Outgoing SCO packet queue
199f258f416SMatthias Ringwald // simplified ring buffer implementation
20011cb6f2dSMatthias Ringwald #define SCO_RING_BUFFER_COUNT  (20)
201f258f416SMatthias Ringwald #define SCO_RING_BUFFER_SIZE (SCO_RING_BUFFER_COUNT * SCO_PACKET_SIZE)
202f258f416SMatthias Ringwald 
2035da69017SMatthias Ringwald /** Request type bits of the "bmRequestType" field in control transfers. */
2045da69017SMatthias Ringwald enum usb_request_type {
2055da69017SMatthias Ringwald 	USB_REQUEST_TYPE_STANDARD = (0x00 << 5),
2065da69017SMatthias Ringwald 	USB_REQUEST_TYPE_CLASS = (0x01 << 5),
2075da69017SMatthias Ringwald 	USB_REQUEST_TYPE_VENDOR = (0x02 << 5),
2085da69017SMatthias Ringwald };
2095da69017SMatthias Ringwald 
2105da69017SMatthias Ringwald /** Recipient bits of the "bmRequestType" field in control transfers. Values 4 through 31 are reserved. */
2115da69017SMatthias Ringwald enum usb_request_recipient {
2125da69017SMatthias Ringwald 	USB_RECIPIENT_DEVICE = 0x00,
2135da69017SMatthias Ringwald 	USB_RECIPIENT_INTERFACE = 0x01,
2145da69017SMatthias Ringwald 	USB_RECIPIENT_ENDPOINT = 0x02,
2155da69017SMatthias Ringwald 	USB_RECIPIENT_OTHER = 0x03,
2165da69017SMatthias Ringwald };
2175da69017SMatthias Ringwald 
2185da69017SMatthias Ringwald // This is the GUID for the USB device class
2195da69017SMatthias Ringwald static GUID GUID_DEVINTERFACE_USB_DEVICE =
2205da69017SMatthias Ringwald { 0xA5DCBF10L, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
2215da69017SMatthias Ringwald 
2225da69017SMatthias Ringwald static void usb_dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
2235da69017SMatthias Ringwald 
2245da69017SMatthias Ringwald static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = &usb_dummy_handler;
2255da69017SMatthias Ringwald 
2265da69017SMatthias Ringwald // endpoint addresses
2275da69017SMatthias Ringwald static int event_in_addr;
2285da69017SMatthias Ringwald static int acl_in_addr;
2295da69017SMatthias Ringwald static int acl_out_addr;
2305da69017SMatthias Ringwald static int sco_in_addr;
2315da69017SMatthias Ringwald static int sco_out_addr;
2325da69017SMatthias Ringwald 
2335da69017SMatthias Ringwald //
2345da69017SMatthias Ringwald static HANDLE usb_device_handle;
2355da69017SMatthias Ringwald static WINUSB_INTERFACE_HANDLE usb_interface_0_handle;
2365da69017SMatthias Ringwald static WINUSB_INTERFACE_HANDLE usb_interface_1_handle;
2375da69017SMatthias Ringwald static OVERLAPPED usb_overlapped_event_in;
2385da69017SMatthias Ringwald static OVERLAPPED usb_overlapped_command_out;
2395da69017SMatthias Ringwald static OVERLAPPED usb_overlapped_acl_in;
2405da69017SMatthias Ringwald static OVERLAPPED usb_overlapped_acl_out;
2415da69017SMatthias Ringwald static btstack_data_source_t usb_data_source_event_in;
2425da69017SMatthias Ringwald static btstack_data_source_t usb_data_source_command_out;
2435da69017SMatthias Ringwald static btstack_data_source_t usb_data_source_acl_in;
2445da69017SMatthias Ringwald static btstack_data_source_t usb_data_source_acl_out;
2455da69017SMatthias Ringwald 
2465da69017SMatthias Ringwald //
2475da69017SMatthias Ringwald static int usb_command_out_active;
2485da69017SMatthias Ringwald static int usb_acl_out_active;
2495da69017SMatthias Ringwald 
2505da69017SMatthias Ringwald // buffer for HCI Events and ACL Packets
2515da69017SMatthias Ringwald static uint8_t hci_event_in_buffer[2 + 255];
2525da69017SMatthias Ringwald static uint8_t hci_acl_in_buffer[HCI_INCOMING_PRE_BUFFER_SIZE + HCI_ACL_BUFFER_SIZE];
2535da69017SMatthias Ringwald 
254d2b52257SMatthias Ringwald // transport interface state
255d2b52257SMatthias Ringwald static int usb_transport_open;
256f258f416SMatthias Ringwald 
257e29e1f07SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
258e29e1f07SMatthias Ringwald 
259f258f416SMatthias Ringwald typedef enum {
260f258f416SMatthias Ringwald     H2_W4_SCO_HEADER = 1,
261f258f416SMatthias Ringwald     H2_W4_PAYLOAD,
262f258f416SMatthias Ringwald } H2_SCO_STATE;
263f258f416SMatthias Ringwald 
2645da69017SMatthias Ringwald // SCO Incoming Windows
2655da69017SMatthias Ringwald static uint8_t hci_sco_in_buffer[ISOC_BUFFERS * SCO_PACKET_SIZE];
266ecf76c8dSMatthias Ringwald static BTSTACK_WINUSB_ISOCH_BUFFER_HANDLE hci_sco_in_buffer_handle;
2675da69017SMatthias Ringwald static USBD_ISO_PACKET_DESCRIPTOR hci_sco_packet_descriptors[ISOC_BUFFERS * NUM_ISO_PACKETS];
2685da69017SMatthias Ringwald static OVERLAPPED usb_overlapped_sco_in[ISOC_BUFFERS];
269f258f416SMatthias Ringwald static int usb_sco_in_expected_transfer;
2705da69017SMatthias Ringwald 
2715da69017SMatthias Ringwald // SCO Incoming Run Loop
2725da69017SMatthias Ringwald static btstack_data_source_t usb_data_source_sco_in[ISOC_BUFFERS];
2735da69017SMatthias Ringwald 
2745da69017SMatthias Ringwald // SCO Incoming HCI
2755da69017SMatthias Ringwald static H2_SCO_STATE sco_state;
2765da69017SMatthias Ringwald static uint8_t  sco_buffer[SCO_PACKET_SIZE];
2775da69017SMatthias Ringwald static uint16_t sco_read_pos;
2785da69017SMatthias Ringwald static uint16_t sco_bytes_to_read;
2795da69017SMatthias Ringwald 
280f258f416SMatthias Ringwald // SCO Outgoing Windows
281ecf76c8dSMatthias Ringwald static BTSTACK_WINUSB_ISOCH_BUFFER_HANDLE hci_sco_out_buffer_handle;
282f258f416SMatthias Ringwald static OVERLAPPED usb_overlapped_sco_out[SCO_RING_BUFFER_COUNT];
283f258f416SMatthias Ringwald static int        sco_ring_transfers_active;
28483f31aa4SMatthias Ringwald static int        usb_sco_out_expected_transfer;
285f258f416SMatthias Ringwald 
286fdd32934SMatthias Ringwald #ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
287f258f416SMatthias Ringwald // next tranfer
288f258f416SMatthias Ringwald static ULONG sco_next_transfer_at_frame;
289fdd32934SMatthias Ringwald #endif
290f258f416SMatthias Ringwald 
291f258f416SMatthias Ringwald // SCO Outgoing Run Loop
292f258f416SMatthias Ringwald static btstack_data_source_t usb_data_source_sco_out[SCO_RING_BUFFER_COUNT];
293f258f416SMatthias Ringwald 
294f258f416SMatthias Ringwald // SCO Outgoing HCI
295f258f416SMatthias Ringwald static uint8_t  sco_ring_buffer[SCO_RING_BUFFER_SIZE];
296f258f416SMatthias Ringwald static int      sco_ring_write;  // packet idx
297f258f416SMatthias Ringwald 
2981b3ee5c3SMatthias Ringwald // SCO Reconfiguration - pause/resume
2991b3ee5c3SMatthias Ringwald static uint16_t sco_voice_setting;
3001b3ee5c3SMatthias Ringwald static int      sco_num_connections;
301c6ac61cdSMatthias Ringwald static int      sco_shutdown;
3025a97f1baSMatthias Ringwald 
3035a97f1baSMatthias Ringwald static uint16_t iso_packet_size;
304e29e1f07SMatthias Ringwald #endif
305e29e1f07SMatthias Ringwald 
30683159bb6SMatthias Ringwald // list of known devices, using VendorID/ProductID tuples
30783159bb6SMatthias Ringwald static const uint16_t known_bluetooth_devices[] = {
3089c30c2b3SMatthias Ringwald     // BCM20702A0 - DeLOCK Bluetooth 4.0
30983159bb6SMatthias Ringwald     0x0a5c, 0x21e8,
3109c30c2b3SMatthias Ringwald     // BCM20702A0 - Asus BT400
31183159bb6SMatthias Ringwald     0x0b05, 0x17cb,
3129c30c2b3SMatthias Ringwald     // BCM20702B0 - Generic USB Detuned Class 1 @ 20 MHz
313bc71fcbbSMatthias Ringwald     0x0a5c, 0x22be,
3149c30c2b3SMatthias Ringwald     // nRF5x Zephyr USB HCI, e.g nRF52840-PCA10056
3159c30c2b3SMatthias Ringwald     0x2fe3, 0x0100,
3169c30c2b3SMatthias Ringwald     0x2fe3, 0x000b,
31783159bb6SMatthias Ringwald };
31883159bb6SMatthias Ringwald 
31983159bb6SMatthias Ringwald static int num_known_devices = sizeof(known_bluetooth_devices) / sizeof(uint16_t) / 2;
32083159bb6SMatthias Ringwald 
32118343f0bSMatthias Ringwald // known devices
32218343f0bSMatthias Ringwald typedef struct {
32318343f0bSMatthias Ringwald     btstack_linked_item_t next;
32418343f0bSMatthias Ringwald     uint16_t vendor_id;
32518343f0bSMatthias Ringwald     uint16_t product_id;
32618343f0bSMatthias Ringwald } usb_known_device_t;
32718343f0bSMatthias Ringwald 
32818343f0bSMatthias Ringwald static btstack_linked_list_t usb_knwon_devices;
32918343f0bSMatthias Ringwald 
hci_transport_usb_add_device(uint16_t vendor_id,uint16_t product_id)33018343f0bSMatthias Ringwald void hci_transport_usb_add_device(uint16_t vendor_id, uint16_t product_id) {
33118343f0bSMatthias Ringwald     usb_known_device_t * device = malloc(sizeof(usb_known_device_t));
33218343f0bSMatthias Ringwald     if (device != NULL) {
33318343f0bSMatthias Ringwald         device->vendor_id = vendor_id;
33418343f0bSMatthias Ringwald         device->product_id = product_id;
33518343f0bSMatthias Ringwald         btstack_linked_list_add(&usb_knwon_devices, (btstack_linked_item_t *) device);
33683159bb6SMatthias Ringwald     }
33783159bb6SMatthias Ringwald }
33883159bb6SMatthias Ringwald 
usb_is_vmware_bluetooth_adapter(const char * device_path)33918343f0bSMatthias Ringwald static bool usb_is_vmware_bluetooth_adapter(const char * device_path){
3409d31f827SMilanka Ringwald     // VMware Vendor ID 0e0f
3419d31f827SMilanka Ringwald     const char * pos = strstr(device_path, "\\usb#vid_0e0f&pid");
34218343f0bSMatthias Ringwald     return (pos > 0);
34318343f0bSMatthias Ringwald }
34418343f0bSMatthias Ringwald 
usb_device_path_match(const char * device_path,uint16_t vendor_id,uint16_t product_id)34569007160SMatthias Ringwald static bool usb_device_path_match(const char * device_path, uint16_t vendor_id, uint16_t product_id){
34618343f0bSMatthias Ringwald     // construct pid/vid substring
34718343f0bSMatthias Ringwald     char substring[20];
348d900fa19SMatthias Ringwald     sprintf_s(substring, sizeof(substring), "vid_%04x&pid_%04x", vendor_id, product_id);
34918343f0bSMatthias Ringwald     const char * pos = strstr(device_path, substring);
35018343f0bSMatthias Ringwald     log_info("check %s in %s -> %p", substring, device_path, pos);
35118343f0bSMatthias Ringwald     return (pos > 0);
35218343f0bSMatthias Ringwald }
35318343f0bSMatthias Ringwald 
usb_is_known_bluetooth_device(const char * device_path)35418343f0bSMatthias Ringwald static bool usb_is_known_bluetooth_device(const char * device_path){
35518343f0bSMatthias Ringwald     int i;
35618343f0bSMatthias Ringwald     for (i=0; i<num_known_devices; i++){
35769007160SMatthias Ringwald         if (usb_device_path_match( device_path, known_bluetooth_devices[i*2], known_bluetooth_devices[i*2+1])){
35818343f0bSMatthias Ringwald             return true;
35918343f0bSMatthias Ringwald         }
36018343f0bSMatthias Ringwald     }
36118343f0bSMatthias Ringwald     btstack_linked_list_iterator_t it;
36218343f0bSMatthias Ringwald     btstack_linked_list_iterator_init(&it, &usb_knwon_devices);
36318343f0bSMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)) {
36418343f0bSMatthias Ringwald         usb_known_device_t * device = (usb_known_device_t *) btstack_linked_list_iterator_next(&it);
36569007160SMatthias Ringwald         if (usb_device_path_match( device_path, device->vendor_id, device->product_id)){
36618343f0bSMatthias Ringwald             return true;
36718343f0bSMatthias Ringwald         }
36818343f0bSMatthias Ringwald     }
36918343f0bSMatthias Ringwald     return false;
3709d31f827SMilanka Ringwald }
3719d31f827SMilanka Ringwald 
372f258f416SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
sco_ring_init(void)373f258f416SMatthias Ringwald static void sco_ring_init(void){
374f258f416SMatthias Ringwald     sco_ring_write = 0;
375f258f416SMatthias Ringwald     sco_ring_transfers_active = 0;
376f258f416SMatthias Ringwald }
sco_ring_have_space(void)377f258f416SMatthias Ringwald static int sco_ring_have_space(void){
378f258f416SMatthias Ringwald     return sco_ring_transfers_active < SCO_RING_BUFFER_COUNT;
379f258f416SMatthias Ringwald }
usb_sco_register_buffers(void)380c4cea4aeSMatthias Ringwald static void usb_sco_register_buffers(void){
381c4cea4aeSMatthias Ringwald     BOOL result;
382ecf76c8dSMatthias Ringwald     result = BTstack_WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_in_addr, hci_sco_in_buffer, sizeof(hci_sco_in_buffer), &hci_sco_in_buffer_handle);
383c4cea4aeSMatthias Ringwald     if (!result) {
384c4cea4aeSMatthias Ringwald         log_error("usb_sco_register_buffers: register in buffer failed, error %lu", GetLastError());
385c4cea4aeSMatthias Ringwald     }
386c4cea4aeSMatthias Ringwald     log_info("hci_sco_in_buffer_handle %p", hci_sco_in_buffer_handle);
387c4cea4aeSMatthias Ringwald 
388ecf76c8dSMatthias Ringwald     result = BTstack_WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_out_addr, sco_ring_buffer, sizeof(sco_ring_buffer), &hci_sco_out_buffer_handle);
389c4cea4aeSMatthias Ringwald     if (!result) {
390c4cea4aeSMatthias Ringwald         log_error("usb_sco_unregister_buffers: register out buffer failed, error %lu", GetLastError());
391c4cea4aeSMatthias Ringwald     }
392c4cea4aeSMatthias Ringwald     log_info("hci_sco_out_buffer_handle %p", hci_sco_out_buffer_handle);
393c4cea4aeSMatthias Ringwald }
usb_sco_unregister_buffers(void)394c4cea4aeSMatthias Ringwald static void usb_sco_unregister_buffers(void){
395c4cea4aeSMatthias Ringwald     if (hci_sco_in_buffer_handle){
396ecf76c8dSMatthias Ringwald         BTstack_WinUsb_UnregisterIsochBuffer(hci_sco_in_buffer_handle);
397c4cea4aeSMatthias Ringwald         hci_sco_in_buffer_handle = NULL;
398c4cea4aeSMatthias Ringwald     }
399c4cea4aeSMatthias Ringwald     if (hci_sco_out_buffer_handle){
400ecf76c8dSMatthias Ringwald         BTstack_WinUsb_UnregisterIsochBuffer(hci_sco_out_buffer_handle);
401c4cea4aeSMatthias Ringwald         hci_sco_out_buffer_handle = NULL;
402c4cea4aeSMatthias Ringwald     }
403c4cea4aeSMatthias Ringwald }
404f258f416SMatthias Ringwald #endif
405f258f416SMatthias Ringwald 
usb_register_packet_handler(void (* handler)(uint8_t packet_type,uint8_t * packet,uint16_t size))4065da69017SMatthias Ringwald static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
4075da69017SMatthias Ringwald     log_info("registering packet handler");
4085da69017SMatthias Ringwald     packet_handler = handler;
4095da69017SMatthias Ringwald }
4105da69017SMatthias Ringwald 
usb_dummy_handler(uint8_t packet_type,uint8_t * packet,uint16_t size)4115da69017SMatthias Ringwald static void usb_dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
4125da69017SMatthias Ringwald }
4135da69017SMatthias Ringwald 
usb_init(const void * transport_config)4145da69017SMatthias Ringwald static void usb_init(const void *transport_config){
4155da69017SMatthias Ringwald }
4165da69017SMatthias Ringwald 
usb_free_resources(void)4175da69017SMatthias Ringwald static void usb_free_resources(void){
4185da69017SMatthias Ringwald 	if (usb_interface_1_handle){
4195da69017SMatthias Ringwald 		WinUsb_Free(usb_interface_1_handle);
4205da69017SMatthias Ringwald 		usb_interface_1_handle = NULL;
4215da69017SMatthias Ringwald 	}
4225da69017SMatthias Ringwald 
4235da69017SMatthias Ringwald 	if (usb_interface_0_handle){
4245da69017SMatthias Ringwald 		WinUsb_Free(usb_interface_0_handle);
4255da69017SMatthias Ringwald 		usb_interface_0_handle = NULL;
4265da69017SMatthias Ringwald 	}
4275da69017SMatthias Ringwald 
4285da69017SMatthias Ringwald 	if (usb_device_handle) {
4295da69017SMatthias Ringwald 		CloseHandle(usb_device_handle);
4305da69017SMatthias Ringwald 		usb_device_handle = NULL;
4315da69017SMatthias Ringwald 	}
432e29e1f07SMatthias Ringwald 
433e29e1f07SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
434c4cea4aeSMatthias Ringwald     usb_sco_unregister_buffers();
435e29e1f07SMatthias Ringwald #endif
4365da69017SMatthias Ringwald }
4375da69017SMatthias Ringwald 
usb_submit_event_in_transfer(void)4385da69017SMatthias Ringwald static void usb_submit_event_in_transfer(void){
4395da69017SMatthias Ringwald 	// submit transfer
4405da69017SMatthias Ringwald 	BOOL result = WinUsb_ReadPipe(usb_interface_0_handle, event_in_addr, hci_event_in_buffer, sizeof(hci_event_in_buffer), NULL, &usb_overlapped_event_in);
4415da69017SMatthias Ringwald 	if (!result) {
4425da69017SMatthias Ringwald 		if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
4435da69017SMatthias Ringwald 	}
4445da69017SMatthias Ringwald 
4455da69017SMatthias Ringwald     // IO_PENDING -> wait for completed
4465da69017SMatthias Ringwald     btstack_run_loop_enable_data_source_callbacks(&usb_data_source_event_in, DATA_SOURCE_CALLBACK_READ);
4475da69017SMatthias Ringwald     return;
4485da69017SMatthias Ringwald 
4495da69017SMatthias Ringwald exit_on_error:
45083159bb6SMatthias Ringwald 	log_error("usb_submit_event_in_transfer: winusb last error %lu", GetLastError());
4515da69017SMatthias Ringwald }
4525da69017SMatthias Ringwald 
usb_submit_acl_in_transfer(void)4535da69017SMatthias Ringwald static void usb_submit_acl_in_transfer(void){
4545da69017SMatthias Ringwald 	// submit transfer
4555da69017SMatthias Ringwald 	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);
4565da69017SMatthias Ringwald 	if (!result) {
4575da69017SMatthias Ringwald 		if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
4585da69017SMatthias Ringwald 	}
4595da69017SMatthias Ringwald 
4605da69017SMatthias Ringwald     // IO_PENDING -> wait for completed
4615da69017SMatthias Ringwald     btstack_run_loop_enable_data_source_callbacks(&usb_data_source_acl_in, DATA_SOURCE_CALLBACK_READ);
4625da69017SMatthias Ringwald     return;
4635da69017SMatthias Ringwald 
4645da69017SMatthias Ringwald exit_on_error:
46583159bb6SMatthias Ringwald 	log_error("usb_submit_acl_in_transfer: winusb last error %lu", GetLastError());
4665da69017SMatthias Ringwald }
4675da69017SMatthias Ringwald 
46819423ce7SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
469fdd32934SMatthias Ringwald #ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
470f258f416SMatthias Ringwald 
471f258f416SMatthias Ringwald // frame number gets updated
usb_submit_sco_in_transfer_at_frame(int i,ULONG * frame_number)472f258f416SMatthias Ringwald static void usb_submit_sco_in_transfer_at_frame(int i, ULONG * frame_number){
473f258f416SMatthias Ringwald 
474c6ac61cdSMatthias Ringwald     if (sco_shutdown){
475c6ac61cdSMatthias Ringwald         log_info("USB SCO Shutdown:: usb_submit_sco_in_transfer_at_frame called");
476c6ac61cdSMatthias Ringwald         return;
477c6ac61cdSMatthias Ringwald     }
478c6ac61cdSMatthias Ringwald 
479f258f416SMatthias Ringwald     LARGE_INTEGER timestamp;
480f258f416SMatthias Ringwald     ULONG current_frame_number;
481f258f416SMatthias Ringwald     WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, &current_frame_number, &timestamp);
482f258f416SMatthias Ringwald 
483f258f416SMatthias Ringwald     ULONG frame_before = *frame_number;
484f258f416SMatthias Ringwald 
485ecf76c8dSMatthias Ringwald     BOOL result = BTstack_WinUsb_ReadIsochPipe(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, iso_packet_size * NUM_ISO_PACKETS,
486f258f416SMatthias Ringwald         frame_number, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]);
487f258f416SMatthias Ringwald 
488ecf76c8dSMatthias Ringwald     // log_info("BTstack_WinUsb_ReadIsochPipe #%02u: current %lu, planned %lu - buffer %lu", i, current_frame_number, frame_before, frame_before - current_frame_number);
489f258f416SMatthias Ringwald 
490f258f416SMatthias Ringwald     if (!result) {
491f258f416SMatthias Ringwald         if (GetLastError() == ERROR_IO_PENDING) {
492f258f416SMatthias Ringwald         } else {
493f258f416SMatthias Ringwald             goto exit_on_error;
494f258f416SMatthias Ringwald         }
495f258f416SMatthias Ringwald     }
496f258f416SMatthias Ringwald 
4975da69017SMatthias Ringwald     return;
4985da69017SMatthias Ringwald 
4995da69017SMatthias Ringwald exit_on_error:
50083159bb6SMatthias Ringwald     log_error("usb_submit_sco_in_transfer: winusb last error %lu", GetLastError());
5015da69017SMatthias Ringwald }
502fdd32934SMatthias Ringwald 
503fdd32934SMatthias Ringwald #else
504fdd32934SMatthias Ringwald 
usb_submit_sco_in_transfer_asap(int i,int continue_stream)505fdd32934SMatthias Ringwald static void usb_submit_sco_in_transfer_asap(int i, int continue_stream){
506fdd32934SMatthias Ringwald 
507c6ac61cdSMatthias Ringwald     if (sco_shutdown){
508c6ac61cdSMatthias Ringwald         log_info("USB SCO Shutdown:: usb_submit_sco_in_transfer_at_frame called");
509c6ac61cdSMatthias Ringwald         return;
510c6ac61cdSMatthias Ringwald     }
511c6ac61cdSMatthias Ringwald 
512fdd32934SMatthias Ringwald     LARGE_INTEGER timestamp;
513fdd32934SMatthias Ringwald     ULONG current_frame_number;
514ecf76c8dSMatthias Ringwald     BTstack_WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, &current_frame_number, &timestamp);
515fdd32934SMatthias Ringwald 
516fdd32934SMatthias Ringwald     // log_info("usb_submit_sco_in_transfer[%02u]: current frame %lu", i, current_frame_number);
517fdd32934SMatthias Ringwald 
518ecf76c8dSMatthias Ringwald     BOOL result = BTstack_WinUsb_ReadIsochPipeAsap(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, iso_packet_size * NUM_ISO_PACKETS,
519fdd32934SMatthias Ringwald         continue_stream, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]);
520fdd32934SMatthias Ringwald 
521fdd32934SMatthias Ringwald     if (!result) {
522fdd32934SMatthias Ringwald         if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
523fdd32934SMatthias Ringwald     }
524fdd32934SMatthias Ringwald 
525fdd32934SMatthias Ringwald     return;
526fdd32934SMatthias Ringwald 
527fdd32934SMatthias Ringwald exit_on_error:
528fdd32934SMatthias Ringwald     log_error("usb_submit_sco_in_transfer: winusb last error %lu", GetLastError());
529fdd32934SMatthias Ringwald }
530f258f416SMatthias Ringwald #endif
53119423ce7SMatthias Ringwald #endif
5325da69017SMatthias Ringwald 
usb_process_event_in(btstack_data_source_t * ds,btstack_data_source_callback_type_t callback_type)5335da69017SMatthias Ringwald static void usb_process_event_in(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type) {
5345da69017SMatthias Ringwald 
5355da69017SMatthias Ringwald     btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
5365da69017SMatthias Ringwald 
5375da69017SMatthias Ringwald     DWORD bytes_read;
5385da69017SMatthias Ringwald     BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_event_in, &bytes_read, FALSE);
5395da69017SMatthias Ringwald     if(!ok){
5405da69017SMatthias Ringwald         DWORD err = GetLastError();
5415da69017SMatthias Ringwald         if (err == ERROR_IO_INCOMPLETE){
5425da69017SMatthias Ringwald             // IO_INCOMPLETE -> wait for completed
5435da69017SMatthias Ringwald             btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
5445da69017SMatthias Ringwald         } else {
5455da69017SMatthias Ringwald             log_error("usb_process_event_in: error reading");
5465da69017SMatthias Ringwald         }
5475da69017SMatthias Ringwald         return;
5485da69017SMatthias Ringwald     }
5495da69017SMatthias Ringwald 
5505da69017SMatthias Ringwald     // notify uppper
551d900fa19SMatthias Ringwald     packet_handler(HCI_EVENT_PACKET, hci_event_in_buffer, (uint16_t) bytes_read);
5525da69017SMatthias Ringwald 
5535da69017SMatthias Ringwald 	// re-submit transfer
5545da69017SMatthias Ringwald 	usb_submit_event_in_transfer();
5555da69017SMatthias Ringwald }
5565da69017SMatthias Ringwald 
usb_process_acl_in(btstack_data_source_t * ds,btstack_data_source_callback_type_t callback_type)5575da69017SMatthias Ringwald static void usb_process_acl_in(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type) {
5585da69017SMatthias Ringwald 
5595da69017SMatthias Ringwald     btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
5605da69017SMatthias Ringwald 
5615da69017SMatthias Ringwald     DWORD bytes_read;
5625da69017SMatthias Ringwald     BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_acl_in, &bytes_read, FALSE);
5635da69017SMatthias Ringwald     if(!ok){
5645da69017SMatthias Ringwald         DWORD err = GetLastError();
5655da69017SMatthias Ringwald         if (err == ERROR_IO_INCOMPLETE){
5665da69017SMatthias Ringwald             // IO_INCOMPLETE -> wait for completed
5675da69017SMatthias Ringwald             btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
5685da69017SMatthias Ringwald         } else {
569507db037SMatthias Ringwald             log_error("usb_process_acl_in: error reading");
570507db037SMatthias Ringwald 
571507db037SMatthias Ringwald             // Reset Pipe
572507db037SMatthias Ringwald             err = WinUsb_ResetPipe(usb_interface_0_handle, acl_in_addr);
5731b5d2ea0SMatthias Ringwald             log_info("WinUsb_ResetPipe: result %u", (int) err);
574507db037SMatthias Ringwald             if (err){
5751b5d2ea0SMatthias Ringwald                 log_info("WinUsb_ResetPipe error %u", (int) GetLastError());
576507db037SMatthias Ringwald             }
577507db037SMatthias Ringwald 
578507db037SMatthias Ringwald             // re-submit transfer
579507db037SMatthias Ringwald             usb_submit_acl_in_transfer();
5805da69017SMatthias Ringwald         }
5815da69017SMatthias Ringwald         return;
5825da69017SMatthias Ringwald     }
5835da69017SMatthias Ringwald 
5845da69017SMatthias Ringwald     // notify uppper
585d900fa19SMatthias Ringwald     packet_handler(HCI_ACL_DATA_PACKET, hci_acl_in_buffer, (uint16_t) bytes_read);
5865da69017SMatthias Ringwald 
5875da69017SMatthias Ringwald 	// re-submit transfer
5885da69017SMatthias Ringwald 	usb_submit_acl_in_transfer();
5895da69017SMatthias Ringwald }
5905da69017SMatthias Ringwald 
59119423ce7SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
sco_state_machine_init(void)5925da69017SMatthias Ringwald static void sco_state_machine_init(void){
5935da69017SMatthias Ringwald     sco_state = H2_W4_SCO_HEADER;
5945da69017SMatthias Ringwald     sco_read_pos = 0;
5955da69017SMatthias Ringwald     sco_bytes_to_read = 3;
5965da69017SMatthias Ringwald }
5975da69017SMatthias Ringwald 
sco_handle_data(uint8_t * buffer,uint16_t size)5985da69017SMatthias Ringwald static void sco_handle_data(uint8_t * buffer, uint16_t size){
59983159bb6SMatthias Ringwald 	// printf("sco_handle_data: state %u, pos %u, to read %u, size %u", sco_state, sco_read_pos, sco_bytes_to_read, size);
6005da69017SMatthias Ringwald     while (size){
6015da69017SMatthias Ringwald         if (size < sco_bytes_to_read){
6025da69017SMatthias Ringwald             // just store incomplete data
6035da69017SMatthias Ringwald             memcpy(&sco_buffer[sco_read_pos], buffer, size);
6045da69017SMatthias Ringwald             sco_read_pos      += size;
6055da69017SMatthias Ringwald             sco_bytes_to_read -= size;
6065da69017SMatthias Ringwald             return;
6075da69017SMatthias Ringwald         }
6085da69017SMatthias Ringwald         // copy requested data
6095da69017SMatthias Ringwald         memcpy(&sco_buffer[sco_read_pos], buffer, sco_bytes_to_read);
6105da69017SMatthias Ringwald         sco_read_pos += sco_bytes_to_read;
6115da69017SMatthias Ringwald         buffer       += sco_bytes_to_read;
6125da69017SMatthias Ringwald         size         -= sco_bytes_to_read;
6135da69017SMatthias Ringwald 
6145da69017SMatthias Ringwald         // chunk read successfully, next action
6155da69017SMatthias Ringwald         switch (sco_state){
6165da69017SMatthias Ringwald             case H2_W4_SCO_HEADER:
6175da69017SMatthias Ringwald                 sco_state = H2_W4_PAYLOAD;
6185da69017SMatthias Ringwald                 sco_bytes_to_read = sco_buffer[2];
6195da69017SMatthias Ringwald                 if (sco_bytes_to_read > (sizeof(sco_buffer)-3)){
6205da69017SMatthias Ringwald                 	log_error("sco_handle_data: sco packet len > packet size");
6215da69017SMatthias Ringwald 	                sco_state_machine_init();
6225da69017SMatthias Ringwald                 }
6235da69017SMatthias Ringwald                 break;
6245da69017SMatthias Ringwald             case H2_W4_PAYLOAD:
6255da69017SMatthias Ringwald                 // packet complete
6265da69017SMatthias Ringwald                 packet_handler(HCI_SCO_DATA_PACKET, sco_buffer, sco_read_pos);
6275da69017SMatthias Ringwald                 sco_state_machine_init();
6285da69017SMatthias Ringwald                 break;
629b115538eSMatthias Ringwald 			default:
630b115538eSMatthias Ringwald 				btstack_unreachable();
631b115538eSMatthias Ringwald 				break;
6325da69017SMatthias Ringwald         }
6335da69017SMatthias Ringwald     }
6345da69017SMatthias Ringwald }
6355da69017SMatthias Ringwald 
usb_process_sco_out(btstack_data_source_t * ds,btstack_data_source_callback_type_t callback_type)636f258f416SMatthias Ringwald static void usb_process_sco_out(btstack_data_source_t *ds,  btstack_data_source_callback_type_t callback_type){
63783f31aa4SMatthias Ringwald 
638f258f416SMatthias Ringwald     btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE);
6395da69017SMatthias Ringwald 
640c6ac61cdSMatthias Ringwald     if (sco_shutdown){
641c6ac61cdSMatthias Ringwald         log_info("USB SCO Shutdown:: usb_process_sco_out called");
642c6ac61cdSMatthias Ringwald         return;
643c6ac61cdSMatthias Ringwald     }
644c6ac61cdSMatthias Ringwald 
64583f31aa4SMatthias Ringwald     // get current frame number
64683f31aa4SMatthias Ringwald     ULONG current_frame_number;
64783f31aa4SMatthias Ringwald     LARGE_INTEGER timestamp;
648ecf76c8dSMatthias Ringwald     BTstack_WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, &current_frame_number, &timestamp);
64983f31aa4SMatthias Ringwald 
6505da69017SMatthias Ringwald     // find index
65183f31aa4SMatthias Ringwald     int transfer_index;
65283f31aa4SMatthias Ringwald     for (transfer_index=0;transfer_index<SCO_RING_BUFFER_COUNT;transfer_index++){
65383f31aa4SMatthias Ringwald         if (ds == &usb_data_source_sco_out[transfer_index]) break;
6545da69017SMatthias Ringwald     }
655f258f416SMatthias Ringwald 
656c6ac61cdSMatthias Ringwald     // log_info("usb_process_sco_out[%02u] -- current frame %lu", transfer_index, current_frame_number);
6575da69017SMatthias Ringwald 
6585da69017SMatthias Ringwald     DWORD bytes_transferred;
659f258f416SMatthias Ringwald     BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_out[transfer_index], &bytes_transferred, FALSE);
660c6ac61cdSMatthias Ringwald     // log_info("usb_process_sco_out_done: #%u result %u, bytes %u, state %u", transfer_index, ok, (int) bytes_transferred, sco_state);
6615da69017SMatthias Ringwald     if(!ok){
6625da69017SMatthias Ringwald         DWORD err = GetLastError();
6635da69017SMatthias Ringwald         if (err == ERROR_IO_INCOMPLETE){
6645da69017SMatthias Ringwald             // IO_INCOMPLETE -> wait for completed
66583f31aa4SMatthias Ringwald             btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_out[transfer_index], DATA_SOURCE_CALLBACK_WRITE);
6665da69017SMatthias Ringwald             return;
6675da69017SMatthias Ringwald         }
66883f31aa4SMatthias Ringwald         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);
66983f31aa4SMatthias Ringwald     }
67083f31aa4SMatthias Ringwald 
67183f31aa4SMatthias Ringwald     // decrease tab
67283f31aa4SMatthias Ringwald     sco_ring_transfers_active--;
67383f31aa4SMatthias Ringwald 
67483f31aa4SMatthias Ringwald     // enable next data source callback
67583f31aa4SMatthias Ringwald     if (sco_ring_transfers_active){
67683f31aa4SMatthias Ringwald         // update expected and wait for completion
67783f31aa4SMatthias Ringwald         usb_sco_out_expected_transfer = (transfer_index+ 1) % SCO_RING_BUFFER_COUNT;
678c6ac61cdSMatthias Ringwald         // log_info("usb_process_sco_out_done[%02u]: wait for transfer %02u", transfer_index, usb_sco_out_expected_transfer);
67983f31aa4SMatthias Ringwald         btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_out[usb_sco_out_expected_transfer], DATA_SOURCE_CALLBACK_WRITE);
6805da69017SMatthias Ringwald     }
6815da69017SMatthias Ringwald 
682c6ac61cdSMatthias Ringwald     // log_info("usb_process_sco_out_done: transfers active %u", sco_ring_transfers_active);
68311cb6f2dSMatthias Ringwald 
684f258f416SMatthias Ringwald     // mark free
685f258f416SMatthias Ringwald     if (sco_ring_have_space()) {
686f258f416SMatthias Ringwald         uint8_t event[] = { HCI_EVENT_SCO_CAN_SEND_NOW, 0};
687f258f416SMatthias Ringwald         packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event));
688f258f416SMatthias Ringwald     }
689f258f416SMatthias Ringwald }
690f258f416SMatthias Ringwald 
usb_process_sco_in(btstack_data_source_t * ds,btstack_data_source_callback_type_t callback_type)691f258f416SMatthias Ringwald static void usb_process_sco_in(btstack_data_source_t *ds,  btstack_data_source_callback_type_t callback_type){
692f258f416SMatthias Ringwald 
69383f31aa4SMatthias Ringwald     btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
694f9644fc5SMatthias Ringwald 
695c6ac61cdSMatthias Ringwald     if (sco_shutdown){
696c6ac61cdSMatthias Ringwald         log_info("USB SCO Shutdown: usb_process_sco_out called");
697c6ac61cdSMatthias Ringwald         return;
698c6ac61cdSMatthias Ringwald     }
699f258f416SMatthias Ringwald 
700f9644fc5SMatthias Ringwald     // find index
701f9644fc5SMatthias Ringwald     int i;
702f9644fc5SMatthias Ringwald     for (i=0;i<ISOC_BUFFERS;i++){
703f9644fc5SMatthias Ringwald         if (ds == &usb_data_source_sco_in[i]) break;
704f9644fc5SMatthias Ringwald     }
705f9644fc5SMatthias Ringwald     int transfer_index = i;
706f9644fc5SMatthias Ringwald 
70708dc2fadSMatthias Ringwald     // ULONG current_frame_number;
70808dc2fadSMatthias Ringwald     // LARGE_INTEGER timestamp;
709ecf76c8dSMatthias Ringwald     // BTstack_WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, &current_frame_number, &timestamp);
71008dc2fadSMatthias Ringwald 
71108dc2fadSMatthias Ringwald     // log_info("usb_process_sco_in[%02u] -- current frame %lu", transfer_index, current_frame_number);
712f9644fc5SMatthias Ringwald 
713f9644fc5SMatthias Ringwald     DWORD bytes_transferred;
714f9644fc5SMatthias Ringwald     BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_in[transfer_index], &bytes_transferred, FALSE);
715f9644fc5SMatthias Ringwald 
716f9644fc5SMatthias Ringwald     if(!ok) {
717f9644fc5SMatthias Ringwald         DWORD err = GetLastError();
718f9644fc5SMatthias Ringwald         if (err == ERROR_IO_INCOMPLETE) {
719f9644fc5SMatthias Ringwald             // IO_INCOMPLETE -> wait for completed
720f9644fc5SMatthias Ringwald             btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
721f9644fc5SMatthias Ringwald             return;
722f9644fc5SMatthias Ringwald         }
723f9644fc5SMatthias Ringwald         log_error("usb_process_sco_in[%02u]: error reading %u, Internal %x", transfer_index, (int) err, (int) usb_overlapped_sco_out[i].Internal);
724f9644fc5SMatthias Ringwald     }
725f9644fc5SMatthias Ringwald 
726f9644fc5SMatthias Ringwald     if (ok){
727f9644fc5SMatthias Ringwald         for (i=0;i<NUM_ISO_PACKETS;i++){
728f9644fc5SMatthias Ringwald             USBD_ISO_PACKET_DESCRIPTOR * packet_descriptor = &hci_sco_packet_descriptors[transfer_index * NUM_ISO_PACKETS + i];
729f9644fc5SMatthias Ringwald             if (packet_descriptor->Length){
730f9644fc5SMatthias Ringwald                 uint8_t * iso_data = &hci_sco_in_buffer[transfer_index * SCO_PACKET_SIZE + packet_descriptor->Offset];
731d900fa19SMatthias Ringwald                 uint16_t  iso_len  = (uint16_t) packet_descriptor->Length;
732f9644fc5SMatthias Ringwald                 sco_handle_data(iso_data, iso_len);
733f9644fc5SMatthias Ringwald             }
734f9644fc5SMatthias Ringwald         }
735f9644fc5SMatthias Ringwald     }
736f9644fc5SMatthias Ringwald 
737fdd32934SMatthias Ringwald #ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
738fdd32934SMatthias Ringwald     usb_submit_sco_in_transfer_at_frame(i, &sco_next_transfer_at_frame);
739fdd32934SMatthias Ringwald #else
740fdd32934SMatthias Ringwald     usb_submit_sco_in_transfer_asap(transfer_index, 1);
741fdd32934SMatthias Ringwald #endif
742f9644fc5SMatthias Ringwald     // update expected and wait for completion
743f9644fc5SMatthias Ringwald     usb_sco_in_expected_transfer = (transfer_index+ 1) % ISOC_BUFFERS;
744f9644fc5SMatthias Ringwald 
745f9644fc5SMatthias Ringwald     // log_info("usb_process_sco_in[%02u]: enable data source %02u", transfer_index, usb_sco_in_expected_transfer);
74683f31aa4SMatthias Ringwald     btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[usb_sco_in_expected_transfer], DATA_SOURCE_CALLBACK_READ);
7475da69017SMatthias Ringwald }
74819423ce7SMatthias Ringwald #endif
7495da69017SMatthias Ringwald 
usb_process_command_out(btstack_data_source_t * ds,btstack_data_source_callback_type_t callback_type)7505da69017SMatthias Ringwald static void usb_process_command_out(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){
7515da69017SMatthias Ringwald 
7525da69017SMatthias Ringwald     btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE);
7535da69017SMatthias Ringwald 
7545da69017SMatthias Ringwald     // update stata before submitting transfer
7555da69017SMatthias Ringwald     usb_command_out_active = 0;
7565da69017SMatthias Ringwald 
7575da69017SMatthias Ringwald     // notify upper stack that provided buffer can be used again
7585da69017SMatthias Ringwald     uint8_t event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0};
7595da69017SMatthias Ringwald     packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event));
7605da69017SMatthias Ringwald }
7615da69017SMatthias Ringwald 
usb_process_acl_out(btstack_data_source_t * ds,btstack_data_source_callback_type_t callback_type)7625da69017SMatthias Ringwald static void usb_process_acl_out(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){
7635da69017SMatthias Ringwald 
7645da69017SMatthias Ringwald     btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE);
7655da69017SMatthias Ringwald 
7665da69017SMatthias Ringwald     // update stata before submitting transfer
7675da69017SMatthias Ringwald     usb_acl_out_active = 0;
7685da69017SMatthias Ringwald 
7695da69017SMatthias Ringwald     // notify upper stack that provided buffer can be used again
7705da69017SMatthias Ringwald     uint8_t event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0};
7715da69017SMatthias Ringwald     packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event));
7725da69017SMatthias Ringwald }
7735da69017SMatthias Ringwald 
usb_scan_for_bluetooth_endpoints(void)77498820164SMatthias Ringwald static BOOL usb_scan_for_bluetooth_endpoints(void) {
77598820164SMatthias Ringwald     int i;
77698820164SMatthias Ringwald     USB_INTERFACE_DESCRIPTOR usb_interface_descriptor;
77798820164SMatthias Ringwald 
77898820164SMatthias Ringwald     // reset
77998820164SMatthias Ringwald     event_in_addr = 0;
78098820164SMatthias Ringwald     acl_in_addr = 0;
78198820164SMatthias Ringwald     acl_out_addr = 0;
78298820164SMatthias Ringwald 
78398820164SMatthias Ringwald     log_info("Scanning USB Entpoints:");
78498820164SMatthias Ringwald 
78598820164SMatthias Ringwald     // look for Event and ACL pipes on Interface #0
78698820164SMatthias Ringwald     BOOL result = WinUsb_QueryInterfaceSettings(usb_interface_0_handle, 0, &usb_interface_descriptor);
78798820164SMatthias Ringwald     if (!result) goto exit_on_error;
78898820164SMatthias Ringwald     for (i=0;i<usb_interface_descriptor.bNumEndpoints;i++){
78998820164SMatthias Ringwald         WINUSB_PIPE_INFORMATION pipe;
79098820164SMatthias Ringwald         result = WinUsb_QueryPipe(
79198820164SMatthias Ringwald                      usb_interface_0_handle,
79298820164SMatthias Ringwald                      0,
79398820164SMatthias Ringwald                      (UCHAR) i,
79498820164SMatthias Ringwald                      &pipe);
79598820164SMatthias Ringwald         if (!result) goto exit_on_error;
79698820164SMatthias Ringwald         log_info("Interface #0, Alt #0, Pipe idx #%u: type %u, id 0x%02x, max packet size %u,",
79798820164SMatthias Ringwald             i, pipe.PipeType, pipe.PipeId, pipe.MaximumPacketSize);
79898820164SMatthias Ringwald         switch (pipe.PipeType){
79998820164SMatthias Ringwald             case USB_ENDPOINT_TYPE_INTERRUPT:
80098820164SMatthias Ringwald                 if (event_in_addr) continue;
80198820164SMatthias Ringwald                 event_in_addr = pipe.PipeId;
80298820164SMatthias Ringwald                 log_info("-> using 0x%2.2X for HCI Events", event_in_addr);
80398820164SMatthias Ringwald                 break;
80498820164SMatthias Ringwald             case USB_ENDPOINT_TYPE_BULK:
80598820164SMatthias Ringwald                 if (pipe.PipeId & 0x80) {
80698820164SMatthias Ringwald                     if (acl_in_addr) continue;
80798820164SMatthias Ringwald                     acl_in_addr = pipe.PipeId;
80898820164SMatthias Ringwald                     log_info("-> using 0x%2.2X for ACL Data In", acl_in_addr);
80998820164SMatthias Ringwald                 } else {
81098820164SMatthias Ringwald                     if (acl_out_addr) continue;
81198820164SMatthias Ringwald                     acl_out_addr = pipe.PipeId;
81298820164SMatthias Ringwald                     log_info("-> using 0x%2.2X for ACL Data Out", acl_out_addr);
81398820164SMatthias Ringwald                 }
81498820164SMatthias Ringwald                 break;
81598820164SMatthias Ringwald             default:
81698820164SMatthias Ringwald                 break;
81798820164SMatthias Ringwald         }
81898820164SMatthias Ringwald     }
81998820164SMatthias Ringwald 
82098820164SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
82198820164SMatthias Ringwald     sco_out_addr = 0;
82298820164SMatthias Ringwald     sco_in_addr = 0;
82398820164SMatthias Ringwald 
82499fc8027SMatthias Ringwald     // look for SCO pipes on Interface #1, Alt Setting 1
82599fc8027SMatthias Ringwald     int alt_setting = 1;
82699fc8027SMatthias Ringwald     result = WinUsb_QueryInterfaceSettings(usb_interface_1_handle, alt_setting, &usb_interface_descriptor);
82798820164SMatthias Ringwald     if (!result) goto exit_on_error;
82898820164SMatthias Ringwald     for (i=0;i<usb_interface_descriptor.bNumEndpoints;i++){
829ecf76c8dSMatthias Ringwald         BTSTACK_WINUSB_PIPE_INFORMATION_EX pipe;
830ecf76c8dSMatthias Ringwald         result = BTstack_WinUsb_QueryPipeEx(
83198820164SMatthias Ringwald                      usb_interface_1_handle,
83299fc8027SMatthias Ringwald                      alt_setting,
83398820164SMatthias Ringwald                      (UCHAR) i,
83498820164SMatthias Ringwald                      &pipe);
83598820164SMatthias Ringwald         if (!result) goto exit_on_error;
83698820164SMatthias Ringwald         log_info("Interface #1, Alt #%u, Pipe idx #%u: type %u, id 0x%02x, max packet size %u, interval %u, max bytes per interval %u",
83799fc8027SMatthias Ringwald             alt_setting, i, pipe.PipeType, pipe.PipeId, pipe.MaximumPacketSize, pipe.Interval, (int) pipe.MaximumBytesPerInterval);
83898820164SMatthias Ringwald         switch (pipe.PipeType){
83998820164SMatthias Ringwald             case USB_ENDPOINT_TYPE_ISOCHRONOUS:
84098820164SMatthias Ringwald                 if (pipe.PipeId & 0x80) {
84198820164SMatthias Ringwald                     if (sco_in_addr) continue;
84298820164SMatthias Ringwald                     sco_in_addr = pipe.PipeId;
84398820164SMatthias Ringwald                     log_info("-> using 0x%2.2X for SCO Data In", sco_in_addr);
84498820164SMatthias Ringwald                 } else {
84598820164SMatthias Ringwald                     if (sco_out_addr) continue;
84698820164SMatthias Ringwald                     sco_out_addr = pipe.PipeId;
84798820164SMatthias Ringwald                     log_info("-> using 0x%2.2X for SCO Data Out", sco_out_addr);
84898820164SMatthias Ringwald                 }
84998820164SMatthias Ringwald                 break;
85098820164SMatthias Ringwald             default:
85198820164SMatthias Ringwald                 break;
85298820164SMatthias Ringwald         }
85398820164SMatthias Ringwald     }
85498820164SMatthias Ringwald     if (!sco_in_addr){
85598820164SMatthias Ringwald         log_error("Couldn't find pipe for SCO IN!");
85698820164SMatthias Ringwald         return FALSE;
85798820164SMatthias Ringwald     }
85898820164SMatthias Ringwald     if (!sco_out_addr){
85998820164SMatthias Ringwald         log_error("Couldn't find pipe for SCO IN!");
86098820164SMatthias Ringwald         return FALSE;
86198820164SMatthias Ringwald     }
86298820164SMatthias Ringwald #endif
86398820164SMatthias Ringwald 
86498820164SMatthias Ringwald     // check if all found
86598820164SMatthias Ringwald     if (!event_in_addr){
86698820164SMatthias Ringwald         log_error("Couldn't find pipe for Event IN!");
86798820164SMatthias Ringwald         return FALSE;
86898820164SMatthias Ringwald     }
86998820164SMatthias Ringwald     if (!acl_in_addr){
87098820164SMatthias Ringwald         log_error("Couldn't find pipe for ACL IN!");
87198820164SMatthias Ringwald         return FALSE;
87298820164SMatthias Ringwald     }
87398820164SMatthias Ringwald     if (!acl_out_addr){
87498820164SMatthias Ringwald         log_error("Couldn't find pipe for ACL OUT!");
87598820164SMatthias Ringwald         return FALSE;
87698820164SMatthias Ringwald     }
87798820164SMatthias Ringwald 
87898820164SMatthias Ringwald     // all clear
87998820164SMatthias Ringwald     return TRUE;
88098820164SMatthias Ringwald 
88198820164SMatthias Ringwald exit_on_error:
88283159bb6SMatthias Ringwald     log_error("usb_scan_for_bluetooth_endpoints: last error %lu", GetLastError());
88398820164SMatthias Ringwald     return FALSE;
88498820164SMatthias Ringwald }
88598820164SMatthias Ringwald 
886b9227387SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
887c4cea4aeSMatthias Ringwald 
usb_sco_start(void)888b9227387SMatthias Ringwald static int usb_sco_start(void){
889b9227387SMatthias Ringwald     printf("usb_sco_start\n");
890b9227387SMatthias Ringwald     log_info("usb_sco_start");
891b9227387SMatthias Ringwald 
892c6ac61cdSMatthias Ringwald     sco_shutdown = 0;
893c6ac61cdSMatthias Ringwald 
894b9227387SMatthias Ringwald     sco_state_machine_init();
895b9227387SMatthias Ringwald     sco_ring_init();
896b9227387SMatthias Ringwald 
897b9227387SMatthias Ringwald     // calc alt setting
898b9227387SMatthias Ringwald     int alt_setting;
899b9227387SMatthias Ringwald     if (sco_voice_setting & 0x0020){
900b9227387SMatthias Ringwald         // 16-bit PCM
901b9227387SMatthias Ringwald         alt_setting = alt_setting_16_bit[sco_num_connections-1];
902b9227387SMatthias Ringwald     } else {
903b9227387SMatthias Ringwald         // 8-bit PCM or mSBC
904b9227387SMatthias Ringwald         alt_setting = alt_setting_8_bit[sco_num_connections-1];
905b9227387SMatthias Ringwald     }
9065a97f1baSMatthias Ringwald 
907b9227387SMatthias Ringwald     log_info("Switching to setting %u on interface 1..", alt_setting);
908b9227387SMatthias Ringwald     // WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds.
909b9227387SMatthias Ringwald     BOOL result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, alt_setting);
910b9227387SMatthias Ringwald     if (!result) goto exit_on_error;
911c6ac61cdSMatthias Ringwald 
9125a97f1baSMatthias Ringwald     // derive iso packet size from alt setting
9135a97f1baSMatthias Ringwald     iso_packet_size = iso_packet_size_for_alt_setting[alt_setting];
9145a97f1baSMatthias Ringwald 
915c6ac61cdSMatthias Ringwald     // register isochronous buffer after setting alternate setting
916c6ac61cdSMatthias Ringwald     usb_sco_register_buffers();
917b9227387SMatthias Ringwald 
918b9227387SMatthias Ringwald #ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
919b9227387SMatthias Ringwald     // get current frame number
920b9227387SMatthias Ringwald     ULONG current_frame_number;
921b9227387SMatthias Ringwald     LARGE_INTEGER timestamp;
922ecf76c8dSMatthias Ringwald     BTstack_WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, &current_frame_number, &timestamp);
923b9227387SMatthias Ringwald     // plan for next tranfer
924b9227387SMatthias Ringwald     sco_next_transfer_at_frame = current_frame_number + ISOC_BUFFERS * NUM_ISO_PACKETS;
925b9227387SMatthias Ringwald #endif
926b9227387SMatthias Ringwald 
927b9227387SMatthias Ringwald     int i;
928b9227387SMatthias Ringwald     for (i=0;i<ISOC_BUFFERS;i++){
929b9227387SMatthias Ringwald #ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
930b9227387SMatthias Ringwald         usb_submit_sco_in_transfer_at_frame(i, &sco_next_transfer_at_frame);
931b9227387SMatthias Ringwald #else
932b9227387SMatthias Ringwald         usb_submit_sco_in_transfer_asap(i, 0);
933b9227387SMatthias Ringwald #endif
934b9227387SMatthias Ringwald     }
935b9227387SMatthias Ringwald 
936b9227387SMatthias Ringwald     usb_sco_in_expected_transfer = 0;
937b9227387SMatthias Ringwald 
938b9227387SMatthias Ringwald     // only await first transfer to return
939b9227387SMatthias Ringwald     btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[usb_sco_in_expected_transfer], DATA_SOURCE_CALLBACK_READ);
940b9227387SMatthias Ringwald     return 1;
941b9227387SMatthias Ringwald 
942b9227387SMatthias Ringwald exit_on_error:
943b9227387SMatthias Ringwald     log_error("usb_sco_start: last error %lu", GetLastError());
944b9227387SMatthias Ringwald     usb_free_resources();
945b9227387SMatthias Ringwald     return 0;
946b9227387SMatthias Ringwald }
947b9227387SMatthias Ringwald 
usb_sco_stop(void)948b9227387SMatthias Ringwald static void usb_sco_stop(void){
949c6ac61cdSMatthias Ringwald     printf("usb_sco_stop\n");
950c6ac61cdSMatthias Ringwald     log_info("usb_sco_stop");
951c6ac61cdSMatthias Ringwald 
952c6ac61cdSMatthias Ringwald     sco_shutdown = 1;
953c6ac61cdSMatthias Ringwald 
954c6ac61cdSMatthias Ringwald     // abort SCO transfers
955b9227387SMatthias Ringwald     WinUsb_AbortPipe(usb_interface_0_handle, sco_in_addr);
956b9227387SMatthias Ringwald     WinUsb_AbortPipe(usb_interface_0_handle, sco_out_addr);
957c6ac61cdSMatthias Ringwald 
958c6ac61cdSMatthias Ringwald     // unlock/free SCO buffers
959c6ac61cdSMatthias Ringwald     usb_sco_unregister_buffers();
960c6ac61cdSMatthias Ringwald 
961b9227387SMatthias Ringwald     int alt_setting = 0;
962b9227387SMatthias Ringwald     log_info("Switching to setting %u on interface 1..", alt_setting);
963b9227387SMatthias Ringwald     // WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds.
964b9227387SMatthias Ringwald     WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, alt_setting);
965b9227387SMatthias Ringwald }
966b9227387SMatthias Ringwald #endif
967b9227387SMatthias Ringwald 
9685da69017SMatthias Ringwald // returns 0 if successful, -1 otherwise
usb_try_open_device(const char * device_path)9695da69017SMatthias Ringwald static int usb_try_open_device(const char * device_path){
9705da69017SMatthias Ringwald 
9715da69017SMatthias Ringwald 	// open file
9725da69017SMatthias Ringwald 	usb_device_handle = CreateFile(device_path,
9735da69017SMatthias Ringwald 		GENERIC_WRITE | GENERIC_READ,
9745da69017SMatthias Ringwald         FILE_SHARE_WRITE | FILE_SHARE_READ,
9755da69017SMatthias Ringwald         NULL,
9765da69017SMatthias Ringwald         OPEN_EXISTING,
9775da69017SMatthias Ringwald         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
9785da69017SMatthias Ringwald         NULL);
97983159bb6SMatthias Ringwald 	log_info("Opening USB device: %p", usb_device_handle);
9805da69017SMatthias Ringwald 	if (!usb_device_handle) goto exit_on_error;
9815da69017SMatthias Ringwald 
9825da69017SMatthias Ringwald 	// WinUsb_Initialize returns TRUE if the operation succeed
9835da69017SMatthias Ringwald 	BOOL result = WinUsb_Initialize(usb_device_handle, &usb_interface_0_handle);
9845da69017SMatthias Ringwald 	if (!result) goto exit_on_error;
98583159bb6SMatthias Ringwald 
98683159bb6SMatthias Ringwald     // Detect USB Dongle based Class, Subclass, and Protocol
98783159bb6SMatthias Ringwald     // The class code (bDeviceClass) is 0xE0 – Wireless Controller.
98883159bb6SMatthias Ringwald     // The SubClass code (bDeviceSubClass) is 0x01 – RF Controller.
98983159bb6SMatthias Ringwald     // The Protocol code (bDeviceProtocol) is 0x01 – Bluetooth programming.
99083159bb6SMatthias Ringwald     USB_INTERFACE_DESCRIPTOR usb_interface_descriptor;
99183159bb6SMatthias Ringwald     result = WinUsb_QueryInterfaceSettings(usb_interface_0_handle, 0, &usb_interface_descriptor);
99283159bb6SMatthias Ringwald     if (!result) goto exit_on_error;
9939d31f827SMilanka Ringwald 
9949d31f827SMilanka Ringwald     // ignore virtual Bluetooth adapter of VMware
995784eb10cSMilanka Ringwald     if (usb_is_vmware_bluetooth_adapter(device_path)) {
9969d31f827SMilanka Ringwald         log_info("Ignoring simulated VMware Bluetooth adapter");
9979d31f827SMilanka Ringwald         usb_free_resources();
9989d31f827SMilanka Ringwald         return -1;
9999d31f827SMilanka Ringwald     }
10009d31f827SMilanka Ringwald 
100183159bb6SMatthias Ringwald     //
100283159bb6SMatthias Ringwald     if (usb_interface_descriptor.bInterfaceClass    != 0xe0 ||
100383159bb6SMatthias Ringwald         usb_interface_descriptor.bInterfaceSubClass != 0x01 ||
100483159bb6SMatthias Ringwald         usb_interface_descriptor.bInterfaceProtocol != 0x01){
100583159bb6SMatthias Ringwald 
1006ecf76c8dSMatthias Ringwald         // check whitelist
1007ecf76c8dSMatthias Ringwald         if (!usb_is_known_bluetooth_device(device_path)){
100883159bb6SMatthias Ringwald             log_info("Class, Subclass, Protocol does not match Bluetooth device");
100983159bb6SMatthias Ringwald             usb_free_resources();
101083159bb6SMatthias Ringwald             return 0;
101183159bb6SMatthias Ringwald         }
1012ecf76c8dSMatthias Ringwald     }
10135da69017SMatthias Ringwald 
10145da69017SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
10155da69017SMatthias Ringwald 	log_info("Claiming interface 1...");
10165da69017SMatthias Ringwald 	// WinUsb_GetAssociatedInterface returns TRUE if the operation succeeds.
10175da69017SMatthias Ringwald 	// We use index 1 - assuming it refers to interface #1 with libusb
10185da69017SMatthias Ringwald 	// A value of 0 indicates the first associated interface, a value of 1 indicates the second associated interface, and so on.
10195da69017SMatthias Ringwald 	result = WinUsb_GetAssociatedInterface(usb_interface_0_handle, 0, &usb_interface_1_handle);
10205da69017SMatthias Ringwald 	if (!result) goto exit_on_error;
10215da69017SMatthias Ringwald 	log_info("Claiming interface 1: success");
10225da69017SMatthias Ringwald #endif
10235da69017SMatthias Ringwald 
102498820164SMatthias Ringwald     result = usb_scan_for_bluetooth_endpoints();
102598820164SMatthias Ringwald     if (!result) {
102698820164SMatthias Ringwald         log_error("Could not find all Bluetooth Endpoints!");
102798820164SMatthias Ringwald         usb_free_resources();
102898820164SMatthias Ringwald         return 0;
10295da69017SMatthias Ringwald     }
10305da69017SMatthias Ringwald 
103151a84de6SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
103298820164SMatthias Ringwald     int i;
103398820164SMatthias Ringwald 
10345da69017SMatthias Ringwald 	memset(hci_sco_packet_descriptors, 0, sizeof(hci_sco_packet_descriptors));
103583f31aa4SMatthias Ringwald 	log_info("Size of packet descriptors for SCO IN%u", (int) sizeof(hci_sco_packet_descriptors));
103683f31aa4SMatthias Ringwald 
103751a84de6SMatthias Ringwald 	// setup async io && btstack handler
10385da69017SMatthias Ringwald 	memset(&usb_overlapped_sco_in, 0, sizeof(usb_overlapped_sco_in));
10395da69017SMatthias Ringwald 	for (i=0;i<ISOC_BUFFERS;i++){
10405da69017SMatthias Ringwald 		usb_overlapped_sco_in[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
104183159bb6SMatthias Ringwald 		// log_info_hexdump(&usb_overlapped_sco_in[i], sizeof(OVERLAPPED));
1042f258f416SMatthias Ringwald         // log_info("data source SCO in %u, handle %p", i, usb_overlapped_sco_in[i].hEvent);
1043b43fd83dSMatthias Ringwald 		usb_data_source_sco_in[i].source.handle = usb_overlapped_sco_in[i].hEvent;
10445da69017SMatthias Ringwald 	    btstack_run_loop_set_data_source_handler(&usb_data_source_sco_in[i], &usb_process_sco_in);
10455da69017SMatthias Ringwald         btstack_run_loop_add_data_source(&usb_data_source_sco_in[i]);
10465da69017SMatthias Ringwald 	}
10475da69017SMatthias Ringwald 
1048f258f416SMatthias Ringwald     memset(&usb_overlapped_sco_out, 0, sizeof(usb_overlapped_sco_out));
1049f258f416SMatthias Ringwald     for (i=0;i<SCO_RING_BUFFER_COUNT;i++){
1050f258f416SMatthias Ringwald         usb_overlapped_sco_out[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1051f258f416SMatthias Ringwald         // log_info("data source SCO out %u, handle %p", i, usb_overlapped_sco_out[i].hEvent);
1052b43fd83dSMatthias Ringwald         usb_data_source_sco_out[i].source.handle = usb_overlapped_sco_out[i].hEvent;
1053f258f416SMatthias Ringwald         btstack_run_loop_set_data_source_handler(&usb_data_source_sco_out[i], &usb_process_sco_out);
1054f258f416SMatthias Ringwald         btstack_run_loop_add_data_source(&usb_data_source_sco_out[i]);
10555da69017SMatthias Ringwald     }
105619423ce7SMatthias Ringwald #endif
10575da69017SMatthias Ringwald 
10585da69017SMatthias Ringwald 	// setup async io
10595da69017SMatthias Ringwald     memset(&usb_overlapped_event_in,     0, sizeof(usb_overlapped_event_in));
10605da69017SMatthias Ringwald     memset(&usb_overlapped_command_out,  0, sizeof(usb_overlapped_command_out));
10615da69017SMatthias Ringwald     memset(&usb_overlapped_acl_out,      0, sizeof(usb_overlapped_acl_out));
10625da69017SMatthias Ringwald     memset(&usb_overlapped_acl_in,       0, sizeof(usb_overlapped_acl_in));
10635da69017SMatthias Ringwald     usb_overlapped_event_in.hEvent    = CreateEvent(NULL, TRUE, FALSE, NULL);
10645da69017SMatthias Ringwald     usb_overlapped_command_out.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
10655da69017SMatthias Ringwald     usb_overlapped_acl_in.hEvent      = CreateEvent(NULL, TRUE, FALSE, NULL);
10665da69017SMatthias Ringwald     usb_overlapped_acl_out.hEvent     = CreateEvent(NULL, TRUE, FALSE, NULL);
10675da69017SMatthias Ringwald 
10685da69017SMatthias Ringwald 	// setup btstack data soures
1069b43fd83dSMatthias Ringwald     usb_data_source_event_in.source.handle = usb_overlapped_event_in.hEvent;
10705da69017SMatthias Ringwald     btstack_run_loop_set_data_source_handler(&usb_data_source_event_in, &usb_process_event_in);
10715da69017SMatthias Ringwald     btstack_run_loop_add_data_source(&usb_data_source_event_in);
10725da69017SMatthias Ringwald 
1073b43fd83dSMatthias Ringwald     usb_data_source_command_out.source.handle = usb_overlapped_command_out.hEvent;
10745da69017SMatthias Ringwald     btstack_run_loop_set_data_source_handler(&usb_data_source_command_out, &usb_process_command_out);
10755da69017SMatthias Ringwald     btstack_run_loop_add_data_source(&usb_data_source_command_out);
10765da69017SMatthias Ringwald 
1077b43fd83dSMatthias Ringwald     usb_data_source_acl_in.source.handle = usb_overlapped_acl_in.hEvent;
10785da69017SMatthias Ringwald     btstack_run_loop_set_data_source_handler(&usb_data_source_acl_in, &usb_process_acl_in);
10795da69017SMatthias Ringwald     btstack_run_loop_add_data_source(&usb_data_source_acl_in);
10805da69017SMatthias Ringwald 
1081b43fd83dSMatthias Ringwald     usb_data_source_acl_out.source.handle = usb_overlapped_acl_out.hEvent;
10825da69017SMatthias Ringwald     btstack_run_loop_set_data_source_handler(&usb_data_source_acl_out, &usb_process_acl_out);
10835da69017SMatthias Ringwald     btstack_run_loop_add_data_source(&usb_data_source_acl_out);
10845da69017SMatthias Ringwald 
1085f258f416SMatthias Ringwald     // submit all incoming transfers
10865da69017SMatthias Ringwald     usb_submit_event_in_transfer();
10875da69017SMatthias Ringwald     usb_submit_acl_in_transfer();
10885da69017SMatthias Ringwald 	return 1;
10895da69017SMatthias Ringwald 
10905da69017SMatthias Ringwald exit_on_error:
109183159bb6SMatthias Ringwald 	log_error("usb_try_open_device: last error %lu", GetLastError());
10925da69017SMatthias Ringwald 	usb_free_resources();
10935da69017SMatthias Ringwald 	return 0;
10945da69017SMatthias Ringwald }
10955da69017SMatthias Ringwald 
1096cea08008SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
1097cea08008SMatthias Ringwald 
1098ecf76c8dSMatthias Ringwald #define WinUSB_Lookup(fn) do { BTstack_##fn = (BTstack_##fn##_t) GetProcAddress(h, #fn); log_info("%-30s %p", #fn, BTstack_##fn); if (!BTstack_##fn) return FALSE; } while(0)
10995da69017SMatthias Ringwald 
usb_lookup_symbols(void)1100f258f416SMatthias Ringwald static BOOL usb_lookup_symbols(void){
11015da69017SMatthias Ringwald 	// lookup runtime symbols missing in current mingw64 distribution
11025da69017SMatthias Ringwald 	HMODULE h = GetModuleHandleA("WinUSB");
110351a84de6SMatthias Ringwald 	log_info("%-30s %p", "WinUSB", h);
11045da69017SMatthias Ringwald 	WinUSB_Lookup(WinUsb_QueryPipeEx);
11055da69017SMatthias Ringwald 	WinUSB_Lookup(WinUsb_RegisterIsochBuffer);
11065da69017SMatthias Ringwald 	WinUSB_Lookup(WinUsb_ReadIsochPipe);
11075da69017SMatthias Ringwald 	WinUSB_Lookup(WinUsb_ReadIsochPipeAsap);
11085da69017SMatthias Ringwald 	WinUSB_Lookup(WinUsb_WriteIsochPipe);
11095da69017SMatthias Ringwald 	WinUSB_Lookup(WinUsb_WriteIsochPipeAsap);
11105da69017SMatthias Ringwald 	WinUSB_Lookup(WinUsb_UnregisterIsochBuffer);
1111f258f416SMatthias Ringwald     WinUSB_Lookup(WinUsb_GetCurrentFrameNumber);
1112f258f416SMatthias Ringwald     return TRUE;
11135da69017SMatthias Ringwald }
1114cea08008SMatthias Ringwald #endif
11155da69017SMatthias Ringwald 
11165da69017SMatthias Ringwald // returns 0 on success, -1 otherwise
usb_open(void)11175da69017SMatthias Ringwald static int usb_open(void){
11185da69017SMatthias Ringwald 
1119d2b52257SMatthias Ringwald     if (usb_transport_open) return 0;
1120d2b52257SMatthias Ringwald 
11215da69017SMatthias Ringwald     int r = -1;
11225da69017SMatthias Ringwald 
1123cea08008SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
1124f258f416SMatthias Ringwald 	BOOL ok = usb_lookup_symbols();
1125f258f416SMatthias Ringwald     if (!ok){
1126f258f416SMatthias Ringwald         log_error("usb_open: Failed to lookup WinSUB ISOCHRONOUS functions. Please disable ENABLE_SCO_OVER_HCI or use Windows 8.1 or higher");
1127f258f416SMatthias Ringwald         return r;
1128f258f416SMatthias Ringwald     }
1129f258f416SMatthias Ringwald     sco_state_machine_init();
1130f258f416SMatthias Ringwald     sco_ring_init();
1131cea08008SMatthias Ringwald #endif
1132cea08008SMatthias Ringwald 
11335da69017SMatthias Ringwald 	HDEVINFO                         hDevInfo;
11345da69017SMatthias Ringwald 	SP_DEVICE_INTERFACE_DATA         DevIntfData;
11355da69017SMatthias Ringwald 	PSP_DEVICE_INTERFACE_DETAIL_DATA DevIntfDetailData;
11365da69017SMatthias Ringwald 	SP_DEVINFO_DATA                  DevData;
11375da69017SMatthias Ringwald 
11385da69017SMatthias Ringwald 	DWORD dwSize;
11395da69017SMatthias Ringwald 	DWORD dwMemberIdx;
11405da69017SMatthias Ringwald 
11415da69017SMatthias Ringwald     // default endpoint addresses
11425da69017SMatthias Ringwald     event_in_addr = 0x81; // EP1, IN interrupt
11435da69017SMatthias Ringwald     acl_in_addr =   0x82; // EP2, IN bulk
11445da69017SMatthias Ringwald     acl_out_addr =  0x02; // EP2, OUT bulk
11455da69017SMatthias Ringwald     sco_in_addr  =  0x83; // EP3, IN isochronous
11465da69017SMatthias Ringwald     sco_out_addr =  0x03; // EP3, OUT isochronous
11475da69017SMatthias Ringwald 
11485da69017SMatthias Ringwald 	// We will try to get device information set for all USB devices that have a
11495da69017SMatthias Ringwald 	// device interface and are currently present on the system (plugged in).
11505da69017SMatthias Ringwald 	hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
11515da69017SMatthias Ringwald 
11525da69017SMatthias Ringwald 	log_info("usb_open: SetupDiGetClassDevs -> %p", hDevInfo);
11535da69017SMatthias Ringwald 	if (hDevInfo == INVALID_HANDLE_VALUE) return -1;
11545da69017SMatthias Ringwald 
11555da69017SMatthias Ringwald 	// Prepare to enumerate all device interfaces for the device information
11565da69017SMatthias Ringwald 	// set that we retrieved with SetupDiGetClassDevs(..)
11575da69017SMatthias Ringwald 	DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
11585da69017SMatthias Ringwald 	dwMemberIdx = 0;
11595da69017SMatthias Ringwald 
11605da69017SMatthias Ringwald 	// Next, we will keep calling this SetupDiEnumDeviceInterfaces(..) until this
11615da69017SMatthias Ringwald 	// function causes GetLastError() to return  ERROR_NO_MORE_ITEMS. With each
11625da69017SMatthias Ringwald 	// call the dwMemberIdx value needs to be incremented to retrieve the next
11635da69017SMatthias Ringwald 	// device interface information.
11645da69017SMatthias Ringwald 
11655da69017SMatthias Ringwald 	SetupDiEnumDeviceInterfaces(hDevInfo, NULL, (LPGUID) &GUID_DEVINTERFACE_USB_DEVICE,
11665da69017SMatthias Ringwald 		dwMemberIdx, &DevIntfData);
11675da69017SMatthias Ringwald 
11685da69017SMatthias Ringwald 	while(GetLastError() != ERROR_NO_MORE_ITEMS){
11695da69017SMatthias Ringwald 
11705da69017SMatthias Ringwald 		// As a last step we will need to get some more details for each
11715da69017SMatthias Ringwald 		// of device interface information we are able to retrieve. This
11725da69017SMatthias Ringwald 		// device interface detail gives us the information we need to identify
11735da69017SMatthias Ringwald 		// the device (VID/PID), and decide if it's useful to us. It will also
11745da69017SMatthias Ringwald 		// provide a DEVINFO_DATA structure which we can use to know the serial
11755da69017SMatthias Ringwald 		// port name for a virtual com port.
11765da69017SMatthias Ringwald 
11775da69017SMatthias Ringwald 		DevData.cbSize = sizeof(DevData);
11785da69017SMatthias Ringwald 
11795da69017SMatthias Ringwald 		// Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail with
11805da69017SMatthias Ringwald 		// a NULL DevIntfDetailData pointer, a DevIntfDetailDataSize
11815da69017SMatthias Ringwald 		// of zero, and a valid RequiredSize variable. In response to such a call,
11825da69017SMatthias Ringwald 		// this function returns the required buffer size at dwSize.
11835da69017SMatthias Ringwald 
11845da69017SMatthias Ringwald 		SetupDiGetDeviceInterfaceDetail(
11855da69017SMatthias Ringwald 			  hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL);
11865da69017SMatthias Ringwald 
11875da69017SMatthias Ringwald 		// Allocate memory for the DeviceInterfaceDetail struct. Don't forget to
11885da69017SMatthias Ringwald 		// deallocate it later!
11895da69017SMatthias Ringwald 		DevIntfDetailData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
11905da69017SMatthias Ringwald 		DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
11915da69017SMatthias Ringwald 
11925da69017SMatthias Ringwald 		if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData,
11935da69017SMatthias Ringwald 			DevIntfDetailData, dwSize, &dwSize, &DevData))
11945da69017SMatthias Ringwald 		{
11955da69017SMatthias Ringwald 			// Finally we can start checking if we've found a useable device,
11965da69017SMatthias Ringwald 			// by inspecting the DevIntfDetailData->DevicePath variable.
11975da69017SMatthias Ringwald 			// The DevicePath looks something like this:
11985da69017SMatthias Ringwald 			//
11995da69017SMatthias Ringwald 			// \\?\usb#vid_04d8&pid_0033#5&19f2438f&0&2#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
12005da69017SMatthias Ringwald 			//
12015da69017SMatthias Ringwald 
12025da69017SMatthias Ringwald 			log_info("usb_open: Device Path: %s", DevIntfDetailData->DevicePath);
12035da69017SMatthias Ringwald 
12045da69017SMatthias Ringwald #if 0
120583159bb6SMatthias Ringwald             // check for hard-coded vendor/product ids
12065da69017SMatthias Ringwald 			char vid_pid_match[30];
12075da69017SMatthias Ringwald 			uint16_t vid = 0x0a12;
12085da69017SMatthias Ringwald 			uint16_t pid = 0x0001;
12095da69017SMatthias Ringwald 			sprintf(vid_pid_match, "\\\\?\\usb#vid_%04x&pid_%04x", vid, pid);
12105da69017SMatthias Ringwald 			if (strncmp(DevIntfDetailData->DevicePath, &vid_pid_match[0], strlen(vid_pid_match)) == 0 ){
12115da69017SMatthias Ringwald 				log_info("Matched search string %s", vid_pid_match);
12125da69017SMatthias Ringwald 
12135da69017SMatthias Ringwald 				BOOL result = usb_try_open_device(DevIntfDetailData->DevicePath);
12145da69017SMatthias Ringwald 				if (result){
12155da69017SMatthias Ringwald 					log_info("usb_open: Device opened, stop scanning");
12165da69017SMatthias Ringwald 					r = 0;
12175da69017SMatthias Ringwald 				} else {
12185da69017SMatthias Ringwald 					log_error("usb_open: Device open failed");
12195da69017SMatthias Ringwald 				}
12205da69017SMatthias Ringwald 			}
122183159bb6SMatthias Ringwald #endif
12225da69017SMatthias Ringwald 
122383159bb6SMatthias Ringwald             // try all devices
122483159bb6SMatthias Ringwald             BOOL result = usb_try_open_device(DevIntfDetailData->DevicePath);
122583159bb6SMatthias Ringwald             if (result){
122683159bb6SMatthias Ringwald                 log_info("usb_open: Device opened, stop scanning");
122783159bb6SMatthias Ringwald                 r = 0;
122883159bb6SMatthias Ringwald             } else {
122983159bb6SMatthias Ringwald                 log_error("usb_open: Device open failed");
123083159bb6SMatthias Ringwald             }
123183159bb6SMatthias Ringwald         }
12325da69017SMatthias Ringwald 		HeapFree(GetProcessHeap(), 0, DevIntfDetailData);
12335da69017SMatthias Ringwald 
12345da69017SMatthias Ringwald 		if (r == 0) break;
12355da69017SMatthias Ringwald 
12365da69017SMatthias Ringwald 		// Continue looping
12375da69017SMatthias Ringwald 		SetupDiEnumDeviceInterfaces(
12385da69017SMatthias Ringwald 			hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE, ++dwMemberIdx, &DevIntfData);
12395da69017SMatthias Ringwald 	}
12405da69017SMatthias Ringwald 
12415da69017SMatthias Ringwald 	SetupDiDestroyDeviceInfoList(hDevInfo);
12425da69017SMatthias Ringwald 
1243d2b52257SMatthias Ringwald 	log_info("usb_open: done, r = %x", r);
1244d2b52257SMatthias Ringwald 
1245d2b52257SMatthias Ringwald     if (r == 0){
1246d2b52257SMatthias Ringwald         // opened
1247d2b52257SMatthias Ringwald         usb_transport_open = 1;
1248d2b52257SMatthias Ringwald     }
12495da69017SMatthias Ringwald 
12505da69017SMatthias Ringwald     return r;
12515da69017SMatthias Ringwald }
12525da69017SMatthias Ringwald 
usb_close(void)12535da69017SMatthias Ringwald static int usb_close(void){
1254cea08008SMatthias Ringwald 
1255eec4d6c6SMatthias Ringwald     if (!usb_transport_open) return 0;
1256d2b52257SMatthias Ringwald 
1257cea08008SMatthias Ringwald     // remove data sources
1258cea08008SMatthias Ringwald     btstack_run_loop_remove_data_source(&usb_data_source_command_out);
1259cea08008SMatthias Ringwald     btstack_run_loop_remove_data_source(&usb_data_source_event_in);
1260cea08008SMatthias Ringwald     btstack_run_loop_remove_data_source(&usb_data_source_acl_in);
1261cea08008SMatthias Ringwald     btstack_run_loop_remove_data_source(&usb_data_source_acl_out);
1262cea08008SMatthias Ringwald 
1263cea08008SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
1264cea08008SMatthias Ringwald     int i;
1265cea08008SMatthias Ringwald     for (i=0;i<ISOC_BUFFERS;i++){
1266cea08008SMatthias Ringwald         btstack_run_loop_remove_data_source(&usb_data_source_sco_in[i]);
1267cea08008SMatthias Ringwald     }
1268f258f416SMatthias Ringwald     for (i=0;i<SCO_RING_BUFFER_COUNT;i++){
1269f258f416SMatthias Ringwald         btstack_run_loop_remove_data_source(&usb_data_source_sco_out[i]);
1270f258f416SMatthias Ringwald     }
1271cea08008SMatthias Ringwald #endif
1272cea08008SMatthias Ringwald 
1273661115eeSMatthias Ringwald     log_info("usb_close abort event and acl pipes");
1274661115eeSMatthias Ringwald 
1275cea08008SMatthias Ringwald     // stop transfers
1276cea08008SMatthias Ringwald     WinUsb_AbortPipe(usb_interface_0_handle, event_in_addr);
1277cea08008SMatthias Ringwald     WinUsb_AbortPipe(usb_interface_0_handle, acl_in_addr);
1278cea08008SMatthias Ringwald     WinUsb_AbortPipe(usb_interface_0_handle, acl_out_addr);
1279cea08008SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
1280b9227387SMatthias Ringwald     usb_sco_stop();
1281cea08008SMatthias Ringwald #endif
1282cea08008SMatthias Ringwald     usb_acl_out_active = 0;
1283cea08008SMatthias Ringwald 
1284cea08008SMatthias Ringwald     // control transfer cannot be stopped, just wait for completion
1285cea08008SMatthias Ringwald     if (usb_command_out_active){
1286661115eeSMatthias Ringwald         log_info("usb_close command out active, wait for complete");
1287cea08008SMatthias Ringwald         DWORD bytes_transferred;
1288cea08008SMatthias Ringwald         WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_command_out, &bytes_transferred, TRUE);
1289cea08008SMatthias Ringwald         usb_command_out_active = 0;
1290cea08008SMatthias Ringwald     }
1291cea08008SMatthias Ringwald 
1292661115eeSMatthias Ringwald     log_info("usb_close free resources");
1293661115eeSMatthias Ringwald 
1294cea08008SMatthias Ringwald     // free everything
12955da69017SMatthias Ringwald     usb_free_resources();
1296d2b52257SMatthias Ringwald 
1297d2b52257SMatthias Ringwald     // transport closed
1298d2b52257SMatthias Ringwald     usb_transport_open = 0;
1299d2b52257SMatthias Ringwald 
1300cea08008SMatthias Ringwald     return 0;
13015da69017SMatthias Ringwald }
13025da69017SMatthias Ringwald 
usb_can_send_packet_now(uint8_t packet_type)13035da69017SMatthias Ringwald static int usb_can_send_packet_now(uint8_t packet_type){
1304f258f416SMatthias Ringwald     // return 0;
13055da69017SMatthias Ringwald     switch (packet_type){
13065da69017SMatthias Ringwald         case HCI_COMMAND_DATA_PACKET:
13075da69017SMatthias Ringwald             return !usb_command_out_active;
13085da69017SMatthias Ringwald         case HCI_ACL_DATA_PACKET:
13095da69017SMatthias Ringwald             return !usb_acl_out_active;
13105da69017SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
13115da69017SMatthias Ringwald         case HCI_SCO_DATA_PACKET:
131211cb6f2dSMatthias Ringwald             // return 0;
13135da69017SMatthias Ringwald             return sco_ring_have_space();
13145da69017SMatthias Ringwald #endif
13155da69017SMatthias Ringwald         default:
13165da69017SMatthias Ringwald             return 0;
13175da69017SMatthias Ringwald     }
13185da69017SMatthias Ringwald }
13195da69017SMatthias Ringwald 
usb_send_cmd_packet(uint8_t * packet,int size)13205da69017SMatthias Ringwald static int usb_send_cmd_packet(uint8_t *packet, int size){
13215da69017SMatthias Ringwald 
13225da69017SMatthias Ringwald     // update stata before submitting transfer
13235da69017SMatthias Ringwald     usb_command_out_active = 1;
13245da69017SMatthias Ringwald 
13255da69017SMatthias Ringwald 	// Start trasnsfer
13265da69017SMatthias Ringwald 	WINUSB_SETUP_PACKET setup_packet;
13275da69017SMatthias Ringwald 	memset(&setup_packet, 0, sizeof(setup_packet));
13285da69017SMatthias Ringwald 	setup_packet.RequestType =  USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_INTERFACE;
13295da69017SMatthias Ringwald 	setup_packet.Length = sizeof(size);
13305da69017SMatthias Ringwald 	BOOL result = WinUsb_ControlTransfer(usb_interface_0_handle, setup_packet, packet, size,  NULL, &usb_overlapped_command_out);
13315da69017SMatthias Ringwald 	if (!result) {
13325da69017SMatthias Ringwald 		if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
13335da69017SMatthias Ringwald 	}
13345da69017SMatthias Ringwald 
13355da69017SMatthias Ringwald     // IO_PENDING -> wait for completed
13365da69017SMatthias Ringwald     btstack_run_loop_enable_data_source_callbacks(&usb_data_source_command_out, DATA_SOURCE_CALLBACK_WRITE);
13375da69017SMatthias Ringwald 
13385da69017SMatthias Ringwald     return 0;
13395da69017SMatthias Ringwald 
13405da69017SMatthias Ringwald exit_on_error:
134183159bb6SMatthias Ringwald 	log_error("winusb: last error %lu", GetLastError());
13425da69017SMatthias Ringwald 	return -1;
13435da69017SMatthias Ringwald }
13445da69017SMatthias Ringwald 
usb_send_acl_packet(uint8_t * packet,int size)13455da69017SMatthias Ringwald static int usb_send_acl_packet(uint8_t *packet, int size){
13465da69017SMatthias Ringwald 
13475da69017SMatthias Ringwald     // update stata before submitting transfer
13485da69017SMatthias Ringwald     usb_acl_out_active = 1;
13495da69017SMatthias Ringwald 
13505da69017SMatthias Ringwald 	// Start trasnsfer
135183f31aa4SMatthias Ringwald 	BOOL ok = WinUsb_WritePipe(usb_interface_0_handle, acl_out_addr, packet, size,  NULL, &usb_overlapped_acl_out);
135283f31aa4SMatthias Ringwald 	if (!ok) {
13535da69017SMatthias Ringwald 		if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
13545da69017SMatthias Ringwald 	}
13555da69017SMatthias Ringwald 
13565da69017SMatthias Ringwald     // IO_PENDING -> wait for completed
13575da69017SMatthias Ringwald     btstack_run_loop_enable_data_source_callbacks(&usb_data_source_acl_out, DATA_SOURCE_CALLBACK_WRITE);
13585da69017SMatthias Ringwald     return 0;
13595da69017SMatthias Ringwald 
13605da69017SMatthias Ringwald exit_on_error:
136183159bb6SMatthias Ringwald 	log_error("winusb: last error %lu", GetLastError());
13625da69017SMatthias Ringwald 	return -1;
13635da69017SMatthias Ringwald }
13645da69017SMatthias Ringwald 
1365f258f416SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
usb_send_sco_packet(uint8_t * packet,int size)1366f258f416SMatthias Ringwald static int usb_send_sco_packet(uint8_t *packet, int size){
1367f258f416SMatthias Ringwald 
136883f31aa4SMatthias Ringwald     if (size > SCO_PACKET_SIZE){
136983f31aa4SMatthias Ringwald         log_error("usb_send_sco_packet: size %u > SCO_PACKET_SIZE %u", size, SCO_PACKET_SIZE);
1370f258f416SMatthias Ringwald         return -1;
1371f258f416SMatthias Ringwald     }
137283f31aa4SMatthias Ringwald 
137311cb6f2dSMatthias Ringwald     // get current frame number
137411cb6f2dSMatthias Ringwald     ULONG current_frame_number;
137511cb6f2dSMatthias Ringwald     LARGE_INTEGER timestamp;
1376ecf76c8dSMatthias Ringwald     BTstack_WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, &current_frame_number, &timestamp);
137783f31aa4SMatthias Ringwald 
137883f31aa4SMatthias Ringwald     // store packet in free slot
137983f31aa4SMatthias Ringwald     int transfer_index = sco_ring_write;
138083f31aa4SMatthias Ringwald     uint8_t * data = &sco_ring_buffer[transfer_index * SCO_PACKET_SIZE];
138183f31aa4SMatthias Ringwald     memcpy(data, packet, size);
138283f31aa4SMatthias Ringwald 
138383f31aa4SMatthias Ringwald 
138483f31aa4SMatthias Ringwald     // setup transfer
138583f31aa4SMatthias Ringwald     int continue_stream = sco_ring_transfers_active > 0;
1386ecf76c8dSMatthias Ringwald     BOOL ok = BTstack_WinUsb_WriteIsochPipeAsap(hci_sco_out_buffer_handle, transfer_index * SCO_PACKET_SIZE, size, continue_stream, &usb_overlapped_sco_out[transfer_index]);
1387c6ac61cdSMatthias Ringwald     // 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);
138883f31aa4SMatthias Ringwald     if (!ok) {
138983f31aa4SMatthias Ringwald         if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
139083f31aa4SMatthias Ringwald     }
139183f31aa4SMatthias Ringwald 
139283f31aa4SMatthias Ringwald     // successful started transfer, enable data source callback if first active transfer
139383f31aa4SMatthias Ringwald     if (sco_ring_transfers_active == 0){
139483f31aa4SMatthias Ringwald         usb_sco_out_expected_transfer = transfer_index;
139583f31aa4SMatthias Ringwald         btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_out[transfer_index], DATA_SOURCE_CALLBACK_WRITE);
139683f31aa4SMatthias Ringwald     }
1397f258f416SMatthias Ringwald 
1398f258f416SMatthias Ringwald     // mark slot as full
139983f31aa4SMatthias Ringwald     sco_ring_write = (sco_ring_write + 1) % SCO_RING_BUFFER_COUNT;
1400f258f416SMatthias Ringwald     sco_ring_transfers_active++;
1401f258f416SMatthias Ringwald 
1402f258f416SMatthias Ringwald     // notify upper stack that provided buffer can be used again
1403f258f416SMatthias Ringwald     uint8_t event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0};
1404f258f416SMatthias Ringwald     packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event));
1405f258f416SMatthias Ringwald 
1406c6ac61cdSMatthias Ringwald     // log_info("usb_send_sco_packet: transfers active %u", sco_ring_transfers_active);
140711cb6f2dSMatthias Ringwald 
1408f258f416SMatthias Ringwald     // and if we have more space for SCO packets
1409f258f416SMatthias Ringwald     if (sco_ring_have_space()) {
1410f258f416SMatthias Ringwald         uint8_t event_sco[] = { HCI_EVENT_SCO_CAN_SEND_NOW, 0};
1411f258f416SMatthias Ringwald         packet_handler(HCI_EVENT_PACKET, &event_sco[0], sizeof(event_sco));
1412f258f416SMatthias Ringwald     }
1413f258f416SMatthias Ringwald     return 0;
141483f31aa4SMatthias Ringwald 
141583f31aa4SMatthias Ringwald exit_on_error:
141683f31aa4SMatthias Ringwald     log_error("usb_send_sco_packet: last error %lu", GetLastError());
141783f31aa4SMatthias Ringwald     return -1;
1418f258f416SMatthias Ringwald }
1419f258f416SMatthias Ringwald #endif
14205da69017SMatthias Ringwald 
usb_send_packet(uint8_t packet_type,uint8_t * packet,int size)14215da69017SMatthias Ringwald static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){
14225da69017SMatthias Ringwald     switch (packet_type){
14235da69017SMatthias Ringwald         case HCI_COMMAND_DATA_PACKET:
14245da69017SMatthias Ringwald             return usb_send_cmd_packet(packet, size);
14255da69017SMatthias Ringwald         case HCI_ACL_DATA_PACKET:
14265da69017SMatthias Ringwald             return usb_send_acl_packet(packet, size);
14275da69017SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
14285da69017SMatthias Ringwald         case HCI_SCO_DATA_PACKET:
14295da69017SMatthias Ringwald             return usb_send_sco_packet(packet, size);
14305da69017SMatthias Ringwald #endif
14315da69017SMatthias Ringwald         default:
14325da69017SMatthias Ringwald             return -1;
14335da69017SMatthias Ringwald     }
14345da69017SMatthias Ringwald }
14355da69017SMatthias Ringwald 
14361b3ee5c3SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
usb_set_sco_config(uint16_t voice_setting,int num_connections)14371b3ee5c3SMatthias Ringwald static void usb_set_sco_config(uint16_t voice_setting, int num_connections){
14381b3ee5c3SMatthias Ringwald     log_info("usb_set_sco_config: voice settings 0x%04x, num connections %u", voice_setting, num_connections);
14391b3ee5c3SMatthias Ringwald 
14401b3ee5c3SMatthias Ringwald     if (num_connections != sco_num_connections){
14411b3ee5c3SMatthias Ringwald         sco_voice_setting = voice_setting;
14421b3ee5c3SMatthias Ringwald         if (sco_num_connections){
1443b5ffb4caSMatthias Ringwald            usb_sco_stop();
14441b3ee5c3SMatthias Ringwald         }
14451b3ee5c3SMatthias Ringwald         sco_num_connections = num_connections;
14461b3ee5c3SMatthias Ringwald         if (num_connections){
1447b5ffb4caSMatthias Ringwald            usb_sco_start();
14481b3ee5c3SMatthias Ringwald         }
14491b3ee5c3SMatthias Ringwald     }
14501b3ee5c3SMatthias Ringwald }
14511b3ee5c3SMatthias Ringwald #endif
14521b3ee5c3SMatthias Ringwald 
14535da69017SMatthias Ringwald // get usb singleton
14545da69017SMatthias Ringwald static const hci_transport_t hci_transport_usb = {
14555da69017SMatthias Ringwald     /* const char * name; */                                        "H2_WINUSB",
14565da69017SMatthias Ringwald     /* void   (*init) (const void *transport_config); */            &usb_init,
14575da69017SMatthias Ringwald     /* int    (*open)(void); */                                     &usb_open,
14585da69017SMatthias Ringwald     /* int    (*close)(void); */                                    &usb_close,
14595da69017SMatthias Ringwald     /* void   (*register_packet_handler)(void (*handler)(...); */   &usb_register_packet_handler,
14605da69017SMatthias Ringwald     /* int    (*can_send_packet_now)(uint8_t packet_type); */       &usb_can_send_packet_now,
14615da69017SMatthias Ringwald     /* int    (*send_packet)(...); */                               &usb_send_packet,
14625da69017SMatthias Ringwald     /* int    (*set_baudrate)(uint32_t baudrate); */                NULL,
14635da69017SMatthias Ringwald     /* void   (*reset_link)(void); */                               NULL,
1464c4218671SMatthias Ringwald #ifdef ENABLE_SCO_OVER_HCI
14651b3ee5c3SMatthias Ringwald     /* void   (*set_sco_config)(uint16_t voice_setting, int num_connections); */ usb_set_sco_config,
1466c4218671SMatthias Ringwald #else
1467c4218671SMatthias Ringwald     /* void   (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL,
1468c4218671SMatthias Ringwald #endif
14695da69017SMatthias Ringwald };
14705da69017SMatthias Ringwald 
hci_transport_usb_instance(void)14715da69017SMatthias Ringwald const hci_transport_t * hci_transport_usb_instance(void) {
14725da69017SMatthias Ringwald     return &hci_transport_usb;
14735da69017SMatthias Ringwald }
1474