#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; | |
| } |