windows: USB implementation based on WinUSB

This commit is contained in:
Matthias Ringwald 2017-01-04 21:59:26 +01:00
parent 036dc6b8c0
commit 5da690179b
5 changed files with 1178 additions and 39 deletions

View File

@ -0,0 +1,896 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
/*
* hci_transport_usb.c
*
* HCI Transport API implementation for USB
*
* Created by Matthias Ringwald on 7/5/09.
*/
// Interface Number - Alternate Setting - suggested Endpoint Address - Endpoint Type - Suggested Max Packet Size
// HCI Commands 0 0 0x00 Control 8/16/32/64
// HCI Events 0 0 0x81 Interrupt (IN) 16
// ACL Data 0 0 0x82 Bulk (IN) 32/64
// ACL Data 0 0 0x02 Bulk (OUT) 32/64
// SCO Data 0 0 0x83 Isochronous (IN)
// SCO Data 0 0 0x03 Isochronous (Out)
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <unistd.h> /* UNIX standard function definitions */
#include <sys/types.h>
#include "btstack_config.h"
#include "btstack_debug.h"
#include "hci.h"
#include "hci_transport.h"
#include <Windows.h>
#include <SetupAPI.h>
#include <Winusb.h>
#if 1
// Isochronous Add-On
typedef struct _WINUSB_PIPE_INFORMATION_EX {
USBD_PIPE_TYPE PipeType;
UCHAR PipeId;
USHORT MaximumPacketSize;
UCHAR Interval;
ULONG MaximumBytesPerInterval;
} WINUSB_PIPE_INFORMATION_EX, *PWINUSB_PIPE_INFORMATION_EX;
typedef PVOID WINUSB_ISOCH_BUFFER_HANDLE, *PWINUSB_ISOCH_BUFFER_HANDLE;
typedef WINBOOL (WINAPI * WinUsb_QueryPipeEx_t) (
WINUSB_INTERFACE_HANDLE InterfaceHandle,
UCHAR AlternateInterfaceNumber,
UCHAR PipeIndex,
PWINUSB_PIPE_INFORMATION_EX PipeInformationEx
);
typedef WINBOOL (WINAPI * WinUsb_RegisterIsochBuffer_t)(
WINUSB_INTERFACE_HANDLE InterfaceHandle,
UCHAR PipeID,
PVOID Buffer,
ULONG BufferLength,
PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle
);
typedef WINBOOL (WINAPI * WinUsb_ReadIsochPipe_t)(
PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle,
ULONG Offset,
ULONG Length,
PULONG FrameNumber,
PULONG NumberOfPackets,
PULONG IsoPacketDescriptors,
LPOVERLAPPED Overlapped
);
typedef WINBOOL (WINAPI * WinUsb_ReadIsochPipeAsap_t)(
PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle,
ULONG Offset,
ULONG Length,
BOOL ContinueStream,
ULONG NumberOfPackets,
PUSBD_ISO_PACKET_DESCRIPTOR IsoPacketDescriptors,
LPOVERLAPPED Overlapped
);
typedef WINBOOL (WINAPI * WinUsb_WriteIsochPipe_t)(
PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle,
ULONG Offset,
ULONG Length,
PULONG FrameNumber,
LPOVERLAPPED Overlapped
);
typedef WINBOOL (WINAPI * WinUsb_WriteIsochPipeAsap_t)(
PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle,
ULONG Offset,
ULONG Length,
BOOL ContinueStream,
LPOVERLAPPED Overlapped
);
typedef WINBOOL (WINAPI * WinUsb_UnregisterIsochBuffer_t)(
PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle
);
static WinUsb_QueryPipeEx_t WinUsb_QueryPipeEx;
static WinUsb_RegisterIsochBuffer_t WinUsb_RegisterIsochBuffer;
static WinUsb_ReadIsochPipe_t WinUsb_ReadIsochPipe;
static WinUsb_ReadIsochPipeAsap_t WinUsb_ReadIsochPipeAsap;
static WinUsb_WriteIsochPipe_t WinUsb_WriteIsochPipe;
static WinUsb_WriteIsochPipeAsap_t WinUsb_WriteIsochPipeAsap;
static WinUsb_UnregisterIsochBuffer_t WinUsb_UnregisterIsochBuffer;
#endif
//
// Bluetooth USB Transport Alternate Settings:
//
// 0: No active voice channels (for USB compliance)
// 1: One 8 kHz voice channel with 8-bit encoding
// 2: Two 8 kHz voice channels with 8-bit encoding or one 8 kHz voice channel with 16-bit encoding
// 3: Three 8 kHz voice channels with 8-bit encoding
// 4: Two 8 kHz voice channels with 16-bit encoding or one 16 kHz voice channel with 16-bit encoding
// 5: Three 8 kHz voice channels with 16-bit encoding or one 8 kHz voice channel with 16-bit encoding and one 16 kHz voice channel with 16-bit encoding
// --> support only a single SCO connection
#define ALT_SETTING (1)
// #define ASYNC_BUFFERS 10
// for ALT_SETTING >= 1 and 8-bit channel, we need the following isochronous packets
// One complete SCO packet with 24 frames every 3 frames (== 3 ms)
#define NUM_ISO_PACKETS (10)
// results in 9 bytes per frame
#define ISO_PACKET_SIZE (9)
// 49 bytes is the max usb packet size for alternate setting 5 (Three 8 kHz 16-bit channels or one 8 kHz 16-bit channel and one 16 kHz 16-bit channel)
// note: alt setting 6 has max packet size of 63 every 7.5 ms = 472.5 bytes / HCI packet, while max SCO packet has 255 byte payload
#define SCO_PACKET_SIZE (NUM_ISO_PACKETS * ISO_PACKET_SIZE)
#define ISOC_BUFFERS 20
/** Request type bits of the "bmRequestType" field in control transfers. */
enum usb_request_type {
USB_REQUEST_TYPE_STANDARD = (0x00 << 5),
USB_REQUEST_TYPE_CLASS = (0x01 << 5),
USB_REQUEST_TYPE_VENDOR = (0x02 << 5),
};
/** Recipient bits of the "bmRequestType" field in control transfers. Values 4 through 31 are reserved. */
enum usb_request_recipient {
USB_RECIPIENT_DEVICE = 0x00,
USB_RECIPIENT_INTERFACE = 0x01,
USB_RECIPIENT_ENDPOINT = 0x02,
USB_RECIPIENT_OTHER = 0x03,
};
// This is the GUID for the USB device class
static GUID GUID_DEVINTERFACE_USB_DEVICE =
{ 0xA5DCBF10L, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
static void usb_dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = &usb_dummy_handler;
// endpoint addresses
static int event_in_addr;
static int acl_in_addr;
static int acl_out_addr;
static int sco_in_addr;
static int sco_out_addr;
//
static HANDLE usb_device_handle;
static WINUSB_INTERFACE_HANDLE usb_interface_0_handle;
static WINUSB_INTERFACE_HANDLE usb_interface_1_handle;
static OVERLAPPED usb_overlapped_event_in;
static OVERLAPPED usb_overlapped_command_out;
static OVERLAPPED usb_overlapped_acl_in;
static OVERLAPPED usb_overlapped_acl_out;
static btstack_data_source_t usb_data_source_event_in;
static btstack_data_source_t usb_data_source_command_out;
static btstack_data_source_t usb_data_source_acl_in;
static btstack_data_source_t usb_data_source_acl_out;
//
static int usb_command_out_active;
static int usb_acl_out_active;
// buffer for HCI Events and ACL Packets
static uint8_t hci_event_in_buffer[2 + 255];
static uint8_t hci_acl_in_buffer[HCI_INCOMING_PRE_BUFFER_SIZE + HCI_ACL_BUFFER_SIZE];
// SCO Incoming Windows
static uint8_t hci_sco_in_buffer[ISOC_BUFFERS * SCO_PACKET_SIZE];
static WINUSB_ISOCH_BUFFER_HANDLE hci_sco_in_buffer_handle;
static USBD_ISO_PACKET_DESCRIPTOR hci_sco_packet_descriptors[ISOC_BUFFERS * NUM_ISO_PACKETS];
// static OVERLAPPED canary_overlapped;
static OVERLAPPED usb_overlapped_sco_in[ISOC_BUFFERS];
// SCO Incoming Run Loop
static btstack_data_source_t usb_data_source_sco_in[ISOC_BUFFERS];
// SCO Incoming HCI
typedef enum {
H2_W4_SCO_HEADER = 1,
H2_W4_PAYLOAD,
} H2_SCO_STATE;
static H2_SCO_STATE sco_state;
static uint8_t sco_buffer[SCO_PACKET_SIZE];
static uint16_t sco_read_pos;
static uint16_t sco_bytes_to_read;
static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
log_info("registering packet handler");
packet_handler = handler;
}
static void usb_dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
}
static void usb_init(const void *transport_config){
}
static void usb_free_resources(void){
if (usb_interface_1_handle){
WinUsb_Free(usb_interface_1_handle);
usb_interface_1_handle = NULL;
}
if (usb_interface_0_handle){
WinUsb_Free(usb_interface_0_handle);
usb_interface_0_handle = NULL;
}
if (usb_device_handle) {
CloseHandle(usb_device_handle);
usb_device_handle = NULL;
}
}
static void usb_submit_event_in_transfer(void){
// submit transfer
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);
if (!result) {
if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
}
// IO_PENDING -> wait for completed
btstack_run_loop_enable_data_source_callbacks(&usb_data_source_event_in, DATA_SOURCE_CALLBACK_READ);
return;
exit_on_error:
log_error("usb_submit_event_in_transfer: winusb last error %lu\n", GetLastError());
}
static void usb_submit_acl_in_transfer(void){
// submit transfer
BOOL result = WinUsb_ReadPipe(usb_interface_0_handle, acl_in_addr, hci_acl_in_buffer, sizeof(hci_acl_in_buffer), NULL, &usb_overlapped_acl_in);
if (!result) {
if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
}
// IO_PENDING -> wait for completed
btstack_run_loop_enable_data_source_callbacks(&usb_data_source_acl_in, DATA_SOURCE_CALLBACK_READ);
return;
exit_on_error:
log_error("usb_submit_acl_in_transfer: winusb last error %lu\n", GetLastError());
}
static void usb_submit_sco_in_transfer(int i, int continue_stream){
// log_info("WinUsb_ReadIsochPipeAsap #%u - handle, %u, %u, %u, %u, %p, %p", i, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE, (i == 0) ? FALSE : TRUE, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]);
// log_info_hexdump(&usb_overlapped_sco_in[i], sizeof(OVERLAPPED));
// log_info_hexdump(&canary_overlapped, sizeof(OVERLAPPED));
BOOL result = WinUsb_ReadIsochPipeAsap(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE,
continue_stream, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]);
if (!result) {
if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
}
#if 0 // check result
DWORD bytes_transferred;
BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_in[i], &bytes_transferred, FALSE);
if (!ok){
int err = (int) GetLastError();
if (err == 87 && continue_stream == 1){
// invalid parameters, try without continue
log_info("usb_submit_sco_in_transfer[%u] error 87, reset pipe", i);
result = WinUsb_ResetPipe(usb_interface_1_handle, sco_in_addr);
log_info("usb_submit_sco_in_transfer[%u] result %u, error %u - retry without continue flag", i, result, (int) GetLastError());
WinUsb_ReadIsochPipeAsap(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE,
0, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]);
} else {
log_info("WinUsb_GetOverlappedResult:[%u] error %u", i, (int) GetLastError());
}
}
#endif
// IO_PENDING -> wait for completed
btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[i], DATA_SOURCE_CALLBACK_READ);
return;
exit_on_error:
log_error("usb_submit_sco_in_transfer: winusb last error %lu\n", GetLastError());
}
static void usb_process_event_in(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type) {
btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
DWORD bytes_read;
BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_event_in, &bytes_read, FALSE);
if(!ok){
DWORD err = GetLastError();
if (err == ERROR_IO_INCOMPLETE){
// IO_INCOMPLETE -> wait for completed
btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
} else {
log_error("usb_process_event_in: error reading");
}
return;
}
// notify uppper
packet_handler(HCI_EVENT_PACKET, hci_event_in_buffer, bytes_read);
// re-submit transfer
usb_submit_event_in_transfer();
}
static void usb_process_acl_in(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type) {
btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
DWORD bytes_read;
BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_acl_in, &bytes_read, FALSE);
if(!ok){
DWORD err = GetLastError();
if (err == ERROR_IO_INCOMPLETE){
// IO_INCOMPLETE -> wait for completed
btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
} else {
log_error("usb_process_acl_in: error writing");
}
return;
}
// notify uppper
packet_handler(HCI_ACL_DATA_PACKET, hci_acl_in_buffer, bytes_read);
// re-submit transfer
usb_submit_acl_in_transfer();
}
static void sco_state_machine_init(void){
sco_state = H2_W4_SCO_HEADER;
sco_read_pos = 0;
sco_bytes_to_read = 3;
}
static void sco_handle_data(uint8_t * buffer, uint16_t size){
// printf("sco_handle_data: state %u, pos %u, to read %u, size %u\n", sco_state, sco_read_pos, sco_bytes_to_read, size);
while (size){
if (size < sco_bytes_to_read){
// just store incomplete data
memcpy(&sco_buffer[sco_read_pos], buffer, size);
sco_read_pos += size;
sco_bytes_to_read -= size;
return;
}
// copy requested data
memcpy(&sco_buffer[sco_read_pos], buffer, sco_bytes_to_read);
sco_read_pos += sco_bytes_to_read;
buffer += sco_bytes_to_read;
size -= sco_bytes_to_read;
// chunk read successfully, next action
switch (sco_state){
case H2_W4_SCO_HEADER:
sco_state = H2_W4_PAYLOAD;
sco_bytes_to_read = sco_buffer[2];
if (sco_bytes_to_read > (sizeof(sco_buffer)-3)){
log_error("sco_handle_data: sco packet len > packet size");
sco_state_machine_init();
}
break;
case H2_W4_PAYLOAD:
// packet complete
packet_handler(HCI_SCO_DATA_PACKET, sco_buffer, sco_read_pos);
sco_state_machine_init();
break;
}
}
}
static void usb_process_sco_in(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){
btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
// find index
int i;
for (i=0;i<ISOC_BUFFERS;i++){
if (ds == &usb_data_source_sco_in[i]) break;
}
// log_info("usb_process_sco_in[%u]", i);
int transfer_index = i;
DWORD bytes_transferred;
BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_in[transfer_index], &bytes_transferred, FALSE);
// log_info("usb_process_sco_in_done: #%u result %u, bytes %u, state %u", transfer_index, result, (int) bytes_transferred, sco_state);
if(!ok){
DWORD err = GetLastError();
if (err == ERROR_IO_INCOMPLETE){
// IO_INCOMPLETE -> wait for completed
btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[transfer_index], DATA_SOURCE_CALLBACK_READ);
return;
} else {
log_error("usb_process_sco_in_done[%u]: error reading %u, Internal %x", transfer_index, (int) err, (int) usb_overlapped_sco_in[i].Internal);
log_info_hexdump(&usb_overlapped_sco_in[transfer_index], sizeof(OVERLAPPED));
// log_info_hexdump(&canary_overlapped, sizeof(OVERLAPPED));
return;
}
}
if (ok){
for (i=0;i<NUM_ISO_PACKETS;i++){
USBD_ISO_PACKET_DESCRIPTOR * packet_descriptor = &hci_sco_packet_descriptors[transfer_index * NUM_ISO_PACKETS + i];
if (packet_descriptor->Length){
uint8_t * iso_data = &hci_sco_in_buffer[transfer_index * SCO_PACKET_SIZE + packet_descriptor->Offset];
uint16_t iso_len = packet_descriptor->Length;
// log_info_hexdump(iso_data, iso_len);
sco_handle_data(iso_data, iso_len);
}
}
}
if (!ok) return;
usb_submit_sco_in_transfer(transfer_index, 1);
}
static void usb_process_command_out(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){
btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE);
// update stata before submitting transfer
usb_command_out_active = 0;
// notify upper stack that provided buffer can be used again
uint8_t event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0};
packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event));
}
static void usb_process_acl_out(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){
btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE);
// update stata before submitting transfer
usb_acl_out_active = 0;
// notify upper stack that provided buffer can be used again
uint8_t event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0};
packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event));
}
#if 0
// Scan for Bluetooth Endpoints
// example: https://android.googlesource.com/platform/development/+/master/host/windows/usb/winusb/adb_winusb_interface.cpp
// example: https://msdn.microsoft.com/en-us/windows/hardware/dn376866(v=vs.85)
static void usb_scan_for_bluetooth_endpoints(void) {
}
#endif
// returns 0 if successful, -1 otherwise
static int usb_try_open_device(const char * device_path){
// open file
usb_device_handle = CreateFile(device_path,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
log_info("USB Dev: %p", usb_device_handle);
if (!usb_device_handle) goto exit_on_error;
log_info("Claiming interface 0...");
// USB_INTERFACE_DESCRIPTOR iface_descriptor;
// WinUsb_Initialize returns TRUE if the operation succeed
BOOL result = WinUsb_Initialize(usb_device_handle, &usb_interface_0_handle);
if (!result) goto exit_on_error;
log_info("Claiming interface 0: success, handle %p", usb_interface_0_handle);
#ifdef ENABLE_SCO_OVER_HCI
log_info("Claiming interface 1...");
// WinUsb_GetAssociatedInterface returns TRUE if the operation succeeds.
// We use index 1 - assuming it refers to interface #1 with libusb
// A value of 0 indicates the first associated interface, a value of 1 indicates the second associated interface, and so on.
result = WinUsb_GetAssociatedInterface(usb_interface_0_handle, 0, &usb_interface_1_handle);
if (!result) goto exit_on_error;
log_info("Claiming interface 1: success");
#if 1
log_info("Switching to setting %u on interface 1..", ALT_SETTING);
// WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds.
result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, ALT_SETTING);
if (!result) goto exit_on_error;
#endif
// check ISO Pipes
USB_INTERFACE_DESCRIPTOR usb_interface_descriptor;
result = WinUsb_QueryInterfaceSettings(usb_interface_1_handle, ALT_SETTING, &usb_interface_descriptor);
if (!result) goto exit_on_error;
int i;
for (i=0;i<usb_interface_descriptor.bNumEndpoints;i++){
WINUSB_PIPE_INFORMATION_EX pipe;
result = WinUsb_QueryPipeEx(
usb_interface_1_handle,
ALT_SETTING,
(UCHAR) i,
&pipe);
if (!result) continue;
log_info("Pipe idx #%u: type %u, id 0x%02x, max packet size %u, interval %u, max bytes per interval %u",
i, pipe.PipeType, pipe.PipeId, pipe.MaximumPacketSize, pipe.Interval, (int) pipe.MaximumBytesPerInterval);
//
}
// AUTO_CLEAR_STALL, RAW_IO is not callable for ISO EP
// uint8_t value_on = 1;
// result = WinUsb_SetPipePolicy(usb_interface_1_handle, sco_in_addr, RAW_IO, sizeof(value_on), &value_on);
// if (!result) goto exit_on_error;
memset(hci_sco_packet_descriptors, 0, sizeof(hci_sco_packet_descriptors));
log_info("Size of packet descriptors %u", (int) sizeof(hci_sco_packet_descriptors));
result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_in_addr, hci_sco_in_buffer, ISOC_BUFFERS * SCO_PACKET_SIZE, &hci_sco_in_buffer_handle);
if (!result) goto exit_on_error;
log_info("hci_sco_in_buffer_handle %p", hci_sco_in_buffer_handle);
// setup overlapped && btstack handler
memset(&usb_overlapped_sco_in, 0, sizeof(usb_overlapped_sco_in));
for (i=0;i<ISOC_BUFFERS;i++){
usb_overlapped_sco_in[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
log_info_hexdump(&usb_overlapped_sco_in[i], sizeof(OVERLAPPED));
usb_data_source_sco_in[i].handle = usb_overlapped_sco_in[i].hEvent;
btstack_run_loop_set_data_source_handler(&usb_data_source_sco_in[i], &usb_process_sco_in);
btstack_run_loop_add_data_source(&usb_data_source_sco_in[i]);
}
// test code to not miss a packet/frame
// while (1){
for (i=0;i<ISOC_BUFFERS;i++){
usb_submit_sco_in_transfer(i, 0);
#if 0
HANDLE handles[1];
handles[0] = usb_overlapped_sco_in[i].hEvent;
int res = WaitForMultipleObjects(1, &handles[0], FALSE, INFINITE);
log_info("WaitForMultipleObjects res %x", res);
// usb_process_sco_in_done(0);
#else
// IO_PENDING -> wait for completed
// btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[i], DATA_SOURCE_CALLBACK_READ);
#endif
// }
}
#endif
// setup async io
memset(&usb_overlapped_event_in, 0, sizeof(usb_overlapped_event_in));
memset(&usb_overlapped_command_out, 0, sizeof(usb_overlapped_command_out));
memset(&usb_overlapped_acl_out, 0, sizeof(usb_overlapped_acl_out));
memset(&usb_overlapped_acl_in, 0, sizeof(usb_overlapped_acl_in));
usb_overlapped_event_in.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
usb_overlapped_command_out.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
usb_overlapped_acl_in.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
usb_overlapped_acl_out.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
// setup btstack data soures
usb_data_source_event_in.handle = usb_overlapped_event_in.hEvent;
btstack_run_loop_set_data_source_handler(&usb_data_source_event_in, &usb_process_event_in);
btstack_run_loop_add_data_source(&usb_data_source_event_in);
usb_data_source_command_out.handle = usb_overlapped_command_out.hEvent;
btstack_run_loop_set_data_source_handler(&usb_data_source_command_out, &usb_process_command_out);
btstack_run_loop_add_data_source(&usb_data_source_command_out);
usb_data_source_acl_in.handle = usb_overlapped_acl_in.hEvent;
btstack_run_loop_set_data_source_handler(&usb_data_source_acl_in, &usb_process_acl_in);
btstack_run_loop_add_data_source(&usb_data_source_acl_in);
usb_data_source_acl_out.handle = usb_overlapped_acl_out.hEvent;
btstack_run_loop_set_data_source_handler(&usb_data_source_acl_out, &usb_process_acl_out);
btstack_run_loop_add_data_source(&usb_data_source_acl_out);
// re-submit transfer
usb_submit_event_in_transfer();
usb_submit_acl_in_transfer();
return 1;
exit_on_error:
log_error("winusb: last error %lu\n", GetLastError());
usb_free_resources();
return 0;
}
#define WinUSB_Lookup(fn) do { fn = (fn##_t) GetProcAddress(h, #fn); log_info("%30s %p", #fn, fn); if (!fn) return; } while(0)
static void usb_lookup_symbols(void){
// lookup runtime symbols missing in current mingw64 distribution
HMODULE h = GetModuleHandleA("WinUSB");
log_info("%30s %p", "WinUSB", h);
WinUSB_Lookup(WinUsb_QueryPipeEx);
WinUSB_Lookup(WinUsb_RegisterIsochBuffer);
WinUSB_Lookup(WinUsb_ReadIsochPipe);
WinUSB_Lookup(WinUsb_ReadIsochPipeAsap);
WinUSB_Lookup(WinUsb_WriteIsochPipe);
WinUSB_Lookup(WinUsb_WriteIsochPipeAsap);
WinUSB_Lookup(WinUsb_UnregisterIsochBuffer);
}
// returns 0 on success, -1 otherwise
static int usb_open(void){
usb_lookup_symbols();
int r = -1;
HDEVINFO hDevInfo;
SP_DEVICE_INTERFACE_DATA DevIntfData;
PSP_DEVICE_INTERFACE_DETAIL_DATA DevIntfDetailData;
SP_DEVINFO_DATA DevData;
DWORD dwSize;
DWORD dwMemberIdx;
// default endpoint addresses
event_in_addr = 0x81; // EP1, IN interrupt
acl_in_addr = 0x82; // EP2, IN bulk
acl_out_addr = 0x02; // EP2, OUT bulk
sco_in_addr = 0x83; // EP3, IN isochronous
sco_out_addr = 0x03; // EP3, OUT isochronous
sco_state_machine_init();
// We will try to get device information set for all USB devices that have a
// device interface and are currently present on the system (plugged in).
hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
log_info("usb_open: SetupDiGetClassDevs -> %p", hDevInfo);
if (hDevInfo == INVALID_HANDLE_VALUE) return -1;
// Prepare to enumerate all device interfaces for the device information
// set that we retrieved with SetupDiGetClassDevs(..)
DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
dwMemberIdx = 0;
// Next, we will keep calling this SetupDiEnumDeviceInterfaces(..) until this
// function causes GetLastError() to return ERROR_NO_MORE_ITEMS. With each
// call the dwMemberIdx value needs to be incremented to retrieve the next
// device interface information.
SetupDiEnumDeviceInterfaces(hDevInfo, NULL, (LPGUID) &GUID_DEVINTERFACE_USB_DEVICE,
dwMemberIdx, &DevIntfData);
while(GetLastError() != ERROR_NO_MORE_ITEMS){
// As a last step we will need to get some more details for each
// of device interface information we are able to retrieve. This
// device interface detail gives us the information we need to identify
// the device (VID/PID), and decide if it's useful to us. It will also
// provide a DEVINFO_DATA structure which we can use to know the serial
// port name for a virtual com port.
DevData.cbSize = sizeof(DevData);
// Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail with
// a NULL DevIntfDetailData pointer, a DevIntfDetailDataSize
// of zero, and a valid RequiredSize variable. In response to such a call,
// this function returns the required buffer size at dwSize.
SetupDiGetDeviceInterfaceDetail(
hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL);
// Allocate memory for the DeviceInterfaceDetail struct. Don't forget to
// deallocate it later!
DevIntfDetailData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData,
DevIntfDetailData, dwSize, &dwSize, &DevData))
{
// Finally we can start checking if we've found a useable device,
// by inspecting the DevIntfDetailData->DevicePath variable.
// The DevicePath looks something like this:
//
// \\?\usb#vid_04d8&pid_0033#5&19f2438f&0&2#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
//
log_info("usb_open: Device Path: %s", DevIntfDetailData->DevicePath);
#if 0
// to access USB Class, Subclass, .. go through registry
// https://msdn.microsoft.com/en-us/library/windows/hardware/jj649944(v=vs.85).aspx
// DWORD dwType;
// HKEY hKey;
// BYTE lpData[1024];
hKey = SetupDiOpenDevRegKey(
hDevInfo,
&DevData,
DICS_FLAG_GLOBAL,
0,
DIREG_DEV,
KEY_READ
);
dwType = REG_SZ;
dwSize = sizeof(lpData);
RegQueryValueEx(hKey, _T("PortName"), NULL, &dwType, lpData, &dwSize);
RegCloseKey(hKey);
#endif
char vid_pid_match[30];
uint16_t vid = 0x0a12;
uint16_t pid = 0x0001;
sprintf(vid_pid_match, "\\\\?\\usb#vid_%04x&pid_%04x", vid, pid);
if (strncmp(DevIntfDetailData->DevicePath, &vid_pid_match[0], strlen(vid_pid_match)) == 0 ){
log_info("Matched search string %s", vid_pid_match);
BOOL result = usb_try_open_device(DevIntfDetailData->DevicePath);
if (result){
log_info("usb_open: Device opened, stop scanning");
r = 0;
} else {
log_error("usb_open: Device open failed");
}
}
}
HeapFree(GetProcessHeap(), 0, DevIntfDetailData);
if (r == 0) break;
// Continue looping
SetupDiEnumDeviceInterfaces(
hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE, ++dwMemberIdx, &DevIntfData);
}
SetupDiDestroyDeviceInfoList(hDevInfo);
log_info("usb_open: done\n");
return r;
}
static int usb_close(void){
int r = -1;
usb_free_resources();
return r;
}
static int usb_can_send_packet_now(uint8_t packet_type){
switch (packet_type){
case HCI_COMMAND_DATA_PACKET:
return !usb_command_out_active;
case HCI_ACL_DATA_PACKET:
return !usb_acl_out_active;
#if 0
#ifdef ENABLE_SCO_OVER_HCI
case HCI_SCO_DATA_PACKET:
return sco_ring_have_space();
#endif
#endif
default:
return 0;
}
}
static int usb_send_cmd_packet(uint8_t *packet, int size){
// update stata before submitting transfer
usb_command_out_active = 1;
// Start trasnsfer
WINUSB_SETUP_PACKET setup_packet;
memset(&setup_packet, 0, sizeof(setup_packet));
setup_packet.RequestType = USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_INTERFACE;
setup_packet.Length = sizeof(size);
BOOL result = WinUsb_ControlTransfer(usb_interface_0_handle, setup_packet, packet, size, NULL, &usb_overlapped_command_out);
if (!result) {
if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
}
// IO_PENDING -> wait for completed
btstack_run_loop_enable_data_source_callbacks(&usb_data_source_command_out, DATA_SOURCE_CALLBACK_WRITE);
return 0;
exit_on_error:
log_error("winusb: last error %lu\n", GetLastError());
return -1;
}
static int usb_send_acl_packet(uint8_t *packet, int size){
// update stata before submitting transfer
usb_acl_out_active = 1;
// Start trasnsfer
BOOL result = WinUsb_WritePipe(usb_interface_0_handle, acl_out_addr, packet, size, NULL, &usb_overlapped_acl_out);
if (!result) {
if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
}
// IO_PENDING -> wait for completed
btstack_run_loop_enable_data_source_callbacks(&usb_data_source_acl_out, DATA_SOURCE_CALLBACK_WRITE);
return 0;
exit_on_error:
log_error("winusb: last error %lu\n", GetLastError());
return -1;
}
static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){
switch (packet_type){
case HCI_COMMAND_DATA_PACKET:
return usb_send_cmd_packet(packet, size);
case HCI_ACL_DATA_PACKET:
return usb_send_acl_packet(packet, size);
#if 0
#ifdef ENABLE_SCO_OVER_HCI
case HCI_SCO_DATA_PACKET:
return usb_send_sco_packet(packet, size);
#endif
#endif
default:
return -1;
}
}
// get usb singleton
static const hci_transport_t hci_transport_usb = {
/* const char * name; */ "H2_WINUSB",
/* void (*init) (const void *transport_config); */ &usb_init,
/* int (*open)(void); */ &usb_open,
/* int (*close)(void); */ &usb_close,
/* void (*register_packet_handler)(void (*handler)(...); */ &usb_register_packet_handler,
/* int (*can_send_packet_now)(uint8_t packet_type); */ &usb_can_send_packet_now,
/* int (*send_packet)(...); */ &usb_send_packet,
/* int (*set_baudrate)(uint32_t baudrate); */ NULL,
/* void (*reset_link)(void); */ NULL,
};
const hci_transport_t * hci_transport_usb_instance(void) {
return &hci_transport_usb;
}

39
port/windows-winusb/.gitignore vendored Normal file
View File

@ -0,0 +1,39 @@
ancs_client_demo
ancs_client_demo.h
ble_central_test
ble_peripheral_test
bnep_test
classic_test
gap_dedicated_bonding
gap_inquiry
gap_inquiry_and_bond
gap_le_advertisements
gap_le_advertisements
gatt_battery_query
gatt_browser
hfp_ag_demo
hfp_hf_demo
hsp_ag_demo
hsp_hs_demo
l2cap_test
le_counter
le_counter.h
le_streamer
le_streamer
le_streamer.h
led_counter
panu_demo
profile.h
sdp_bnep_query
sdp_general_query
sdp_rfcomm_query
sm_pairing_central
sm_pairing_peripheral
sm_pairing_peripheral.h
spp_and_le_counter
spp_and_le_counter.h
spp_counter
spp_streamer
spp_streamer
sco_input*
sco_output*

View File

@ -0,0 +1,33 @@
# Makefile for windows WinUSB based examples
BTSTACK_ROOT = ../..
CORE += main.c stdin_support.c
COMMON += hci_transport_h2_winusb.c btstack_run_loop_windows.c le_device_db_fs.c btstack_link_key_db_fs.c
include ${BTSTACK_ROOT}/example/Makefile.inc
# CC = gcc-fsf-4.9
CFLAGS += -g -Wall -Wmissing-prototypes -Wstrict-prototypes -Wshadow -Werror
# CFLAGS += -Werror
CFLAGS += -I${BTSTACK_ROOT}/platform/windows \
-I${BTSTACK_ROOT}/platform/posix \
-I${BTSTACK_ROOT}/platform/embedded
VPATH += ${BTSTACK_ROOT}/platform/embedded
VPATH += ${BTSTACK_ROOT}/platform/posix
VPATH += ${BTSTACK_ROOT}/platform/windows
# use pkg-config for portaudio
# CFLAGS += $(shell pkg-config portaudio-2.0 --cflags) -DHAVE_PORTAUDIO
# LDFLAGS += $(shell pkg-config portaudio-2.0 --libs)
# hard coded flags for portaudio in /usr/local/lib
# CFLAGS += -I/usr/local/include -DHAVE_PORTAUDIO
# LDFLAGS += -L/sw/lib -lportaudio -Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,Carbon
LDFLAGS += -lsetupapi -lwinusb
EXAMPLES=le_counter hfp_hf_demo
all: ${EXAMPLES}

171
port/windows-winusb/main.c Normal file
View File

@ -0,0 +1,171 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
// *****************************************************************************
//
// minimal setup for HCI code
//
// *****************************************************************************
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "btstack_config.h"
#include "btstack_debug.h"
#include "btstack_event.h"
#include "btstack_link_key_db_fs.h"
#include "btstack_memory.h"
#include "btstack_run_loop.h"
#include "btstack_run_loop_windows.h"
#include "hal_led.h"
#include "hci.h"
#include "hci_dump.h"
#include "stdin_support.h"
int btstack_main(int argc, const char * argv[]);
static btstack_packet_callback_registration_t hci_event_callback_registration;
static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
if (packet_type != HCI_EVENT_PACKET) return;
if (hci_event_packet_get_type(packet) != BTSTACK_EVENT_STATE) return;
if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return;
printf("BTstack up and running.\n");
}
static void sigint_handler(int param){
#ifndef _WIN32
// reset anyway
btstack_stdin_reset();
#endif
log_info(" <= SIGINT received, shutting down..\n");
hci_power_control(HCI_POWER_OFF);
hci_close();
log_info("Good bye, see you.\n");
exit(0);
}
static int led_state = 0;
void hal_led_toggle(void){
led_state = 1 - led_state;
printf("LED State %u\n", led_state);
}
#define USB_MAX_PATH_LEN 7
int main(int argc, const char * argv[]){
// Prevent stdout buffering
setvbuf(stdout, NULL, _IONBF, 0);
printf("BTstack/windows-winusb booting up\n");
#if 0
int usb_path_len = 0;
uint8_t usb_path[USB_MAX_PATH_LEN];
if (argc >= 3 && strcmp(argv[1], "-u") == 0){
// parse command line options for "-u 11:22:33"
const char * port_str = argv[2];
printf("Specified USB Path: ");
while (1){
char * delimiter;
int port = strtol(port_str, &delimiter, 16);
usb_path[usb_path_len] = port;
usb_path_len++;
printf("%02x ", port);
if (!delimiter) break;
if (*delimiter != ':' && *delimiter != '-') break;
port_str = delimiter+1;
}
printf("\n");
}
#endif
/// GET STARTED with BTstack ///
btstack_memory_init();
btstack_run_loop_init(btstack_run_loop_windows_get_instance());
// if (usb_path_len){
// hci_transport_usb_set_path(usb_path_len, usb_path);
// }
// use logger: format HCI_DUMP_PACKETLOGGER, HCI_DUMP_BLUEZ or HCI_DUMP_STDOUT
#if 0
char pklg_path[100];
strcpy(pklg_path, "hci_dump");
#if 0
if (usb_path_len){
strcat(pklg_path, "_");
strcat(pklg_path, argv[2]);
}
#endif
strcat(pklg_path, ".pklg");
printf("Packet Log: %s\n", pklg_path);
hci_dump_open(pklg_path, HCI_DUMP_PACKETLOGGER);
#else
hci_dump_open(NULL, HCI_DUMP_STDOUT);
#endif
// init HCI
hci_init(hci_transport_usb_instance(), NULL);
#ifdef ENABLE_CLASSIC
hci_set_link_key_db(btstack_link_key_db_fs_instance());
#endif
// inform about BTstack state
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
// handle CTRL-c
signal(SIGINT, sigint_handler);
// setup app
btstack_main(argc, argv);
// go
btstack_run_loop_execute();
return 0;
}