#libusb async API example.
bluetooth usb dongle sample program.
- send HCI_RESET.
- send HCI_READ_BD_ADDR.
#libusb async API example.
bluetooth usb dongle sample program.
/* | |
* btusb.c | |
* | |
* gcc -Wall -o btusb btusb.c -lusb-1.0 | |
* | |
*/ | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <libusb-1.0/libusb.h> | |
#define HCI_TYPE_COMMAND 1 | |
#define HCI_TYPE_EVENT 4 | |
#define HCI_EVT_BUFFER_SIZE (2 + 256) | |
#define EVT_BUFFER_COUNT 2 | |
static uint8_t hci_cmd_buffer[3 + 256 + LIBUSB_CONTROL_SETUP_SIZE]; | |
static uint8_t hci_evt_buffer[EVT_BUFFER_COUNT][HCI_EVT_BUFFER_SIZE]; | |
static libusb_device_handle * handle; | |
static struct libusb_transfer *cmd_transfer; | |
static struct libusb_transfer *evt_transfer[EVT_BUFFER_COUNT]; | |
static struct libusb_transfer *completed_transfer_list; | |
static int usb_command_active = 0; | |
static int usb_command_resp = 0; | |
static int evt_ep_addr; | |
LIBUSB_CALL static void btusb_async_callback(struct libusb_transfer *transfer) | |
{ | |
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { | |
transfer->user_data = NULL; // terminate linked list | |
if (completed_transfer_list == NULL) { | |
completed_transfer_list = transfer; | |
return; | |
} | |
struct libusb_transfer *temp = completed_transfer_list; | |
while (temp->user_data) { | |
temp = (struct libusb_transfer*)temp->user_data; | |
} | |
temp->user_data = transfer; | |
} | |
return; | |
} | |
static void handle_completed_transfer(struct libusb_transfer *transfer) | |
{ | |
if (transfer->endpoint == evt_ep_addr) { | |
printf("EVT"); | |
for (int i = 0; i < transfer->actual_length; i++) { | |
printf(" %02x", transfer->buffer[i]); | |
} | |
printf("\n"); | |
usb_command_resp = 1; | |
} else if (transfer->endpoint == 0) { | |
usb_command_active = 0; | |
} else { | |
} | |
} | |
static void btusb_polling(void) | |
{ | |
// polling. if transfer done, call async callback. | |
struct timeval tv; | |
memset(&tv, 0, sizeof(struct timeval)); | |
libusb_handle_events_timeout(NULL, &tv); | |
// handle transfer | |
while (completed_transfer_list) { | |
handle_completed_transfer(completed_transfer_list); | |
completed_transfer_list = (struct libusb_transfer*) completed_transfer_list->user_data; | |
} | |
} | |
static int is_btusb_device (struct libusb_device *dev) | |
{ | |
struct libusb_device_descriptor desc; | |
int ret; | |
ret = libusb_get_device_descriptor(dev, &desc); | |
if (ret < 0) { return false; } | |
if (( | |
(0xe0 == libusb_le16_to_cpu(desc.bDeviceClass)) && | |
(0x01 == libusb_le16_to_cpu(desc.bDeviceSubClass)) && | |
(0x01 == libusb_le16_to_cpu(desc.bDeviceProtocol)) | |
) || ( | |
(0xef == libusb_le16_to_cpu(desc.bDeviceClass)) && | |
(0x02 == libusb_le16_to_cpu(desc.bDeviceSubClass)) && | |
(0x01 == libusb_le16_to_cpu(desc.bDeviceProtocol)) | |
)) | |
{ | |
return true; | |
} | |
return false; | |
} | |
static bool btusb_open(void) | |
{ | |
int ret; | |
ret = libusb_init(NULL); | |
if (ret < 0) { return -1; } | |
libusb_device *dev; | |
libusb_device **devs; | |
ssize_t num_devices; | |
completed_transfer_list = NULL; | |
handle = NULL; | |
num_devices = libusb_get_device_list(NULL, &devs); | |
if (num_devices < 0) { return -1; } | |
do { | |
for (int i = 0; (dev = devs[i]) != NULL; i++) { | |
//check USB class. | |
if (is_btusb_device (dev) == true) { break; } | |
dev = NULL; | |
} | |
if (dev == NULL) { break; } | |
ret = libusb_open(dev, &handle); | |
if (ret < 0 || handle==NULL) { break; } | |
ret = libusb_reset_device(handle); | |
if (ret < 0) { break; } | |
ret = libusb_claim_interface(handle, 0); | |
if (ret < 0) { break; } | |
struct libusb_config_descriptor *config_descriptor; | |
ret = libusb_get_active_config_descriptor(dev, &config_descriptor); | |
if (ret < 0) { break; } | |
evt_ep_addr = 0; | |
for (int i = 0; i < config_descriptor->bNumInterfaces; i++) { | |
const struct libusb_interface_descriptor * interface_descriptor = config_descriptor->interface[i].altsetting; | |
const struct libusb_endpoint_descriptor *endpoint = interface_descriptor->endpoint; | |
for (int r = 0; r < interface_descriptor->bNumEndpoints; r++, endpoint++) { | |
switch (endpoint->bmAttributes & 0x3) { | |
case LIBUSB_TRANSFER_TYPE_INTERRUPT: | |
if (evt_ep_addr) continue; | |
evt_ep_addr = endpoint->bEndpointAddress; | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
} while (0); | |
libusb_free_device_list(devs, 1); | |
if (handle == NULL) { | |
return false; | |
} | |
return true; | |
} | |
static int btusb_prepare_pipe(void) | |
{ | |
int ret; | |
if (handle == NULL) { return -1; } | |
// allocate control transfer handler | |
cmd_transfer = libusb_alloc_transfer(0); | |
if (!cmd_transfer) { | |
return LIBUSB_ERROR_NO_MEM; | |
} | |
// allocate interrupt transfer handler | |
for (int i = 0 ; i < EVT_BUFFER_COUNT ; i++) { | |
evt_transfer[i] = libusb_alloc_transfer(0); // 0 isochronous transfers Events | |
if (!evt_transfer[i]) { | |
return LIBUSB_ERROR_NO_MEM; | |
} | |
} | |
// configure interrupt transfer handler | |
for (int i = 0 ; i < EVT_BUFFER_COUNT ; i++) { | |
libusb_fill_interrupt_transfer(evt_transfer[i], handle, evt_ep_addr, | |
hci_evt_buffer[i], HCI_EVT_BUFFER_SIZE, btusb_async_callback, NULL, 0) ; | |
ret = libusb_submit_transfer(evt_transfer[i]); | |
if (ret) { | |
printf("Error submitting interrupt transfer %d\n", ret); | |
return ret; | |
} | |
} | |
return 0; | |
} | |
static int btusb_send_cmd(uint8_t *packet, int size) | |
{ | |
int ret; | |
printf("CMD"); | |
for (int i = 0; i < size; i++) { | |
printf(" %02x", packet[i]); | |
} | |
printf("\n"); | |
// use async API | |
libusb_fill_control_setup(hci_cmd_buffer, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, 0, 0, 0, size); | |
memcpy(hci_cmd_buffer + LIBUSB_CONTROL_SETUP_SIZE, packet, size); | |
// configure controll transfer handler | |
libusb_fill_control_transfer(cmd_transfer, handle, hci_cmd_buffer, btusb_async_callback, NULL, 0); | |
cmd_transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER; | |
usb_command_resp = 0; | |
usb_command_active = 1; | |
ret = libusb_submit_transfer(cmd_transfer); | |
if (ret < 0) { | |
usb_command_active = 0; | |
printf("Error submitting cmd transfer %d\n", ret); | |
return ret; | |
} | |
// wait command resp event | |
while(usb_command_resp == 0) { | |
usleep(100*1000); | |
btusb_polling(); | |
} | |
usb_command_resp = 0; | |
return 0; | |
} | |
int main(void) | |
{ | |
int ret; | |
ret = btusb_open(); | |
if (!ret) { return -1; } | |
ret = btusb_prepare_pipe(); | |
if (ret < 0) { return -1; } | |
//send HCI_CMD_RESET | |
uint8_t reset_dat[] = {0x03, 0x0c, 0x00}; | |
uint32_t reset_len = 3; | |
btusb_send_cmd(reset_dat, reset_len); | |
//send HCI_READ_BD_ADDR | |
uint8_t read_bda_dat[] = {0x09, 0x10, 0x00}; | |
uint32_t read_bda_len = 3; | |
btusb_send_cmd(read_bda_dat, read_bda_len); | |
return 0; | |
} |