Skip to content

Instantly share code, notes, and snippets.

@eggman
Last active April 4, 2020 05:01
Show Gist options
  • Save eggman/2528d559c6c8fabf5aa212c4eea54391 to your computer and use it in GitHub Desktop.
Save eggman/2528d559c6c8fabf5aa212c4eea54391 to your computer and use it in GitHub Desktop.

#libusb async API example.

bluetooth usb dongle sample program.

  • send HCI_RESET.
  • send HCI_READ_BD_ADDR.
/*
* 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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment