Skip to content

Instantly share code, notes, and snippets.

@h4k1m0u
Last active April 20, 2024 21:26
Show Gist options
  • Save h4k1m0u/7bc54293aff24038f15458263b46b05d to your computer and use it in GitHub Desktop.
Save h4k1m0u/7bc54293aff24038f15458263b46b05d to your computer and use it in GitHub Desktop.
Notes about USB
#include <stdio.h>
#include <libusb-1.0/libusb.h>
/**
* Print manufacturer name & product name for mouse & usb stick
* How to build & run:
* $ gcc -lusb-1.0 list_usb_devices.c -o app
* $ sudo ./app
*
* Reference: https://github.com/libusb/libusb/blob/master/examples/listdevs.c
*/
int main(void) {
// initialize libusb (NULL => use default context)
int r;
r = libusb_init_context(NULL, NULL, 0);
if (r < 0) {
printf("Failure to initialize libusb\n");
return 1;
}
// list of attached usb devices
libusb_device** list;
ssize_t n_devices = libusb_get_device_list(NULL, &list);
if (n_devices < 0) {
libusb_exit(NULL);
printf("Failure to get list of attached usb devices\n");
return 1;
}
// list of devices
for (int i_device = 0; i_device < n_devices; ++i_device) {
libusb_device* device = list[i_device];
// get device descriptor
struct libusb_device_descriptor descriptor;
r = libusb_get_device_descriptor(device, &descriptor);
if (r < 0) {
printf("Failure to get device %d descriptor\n", i_device);
break;
}
// each interface specifies its own class info (case of usb stick & mouse)
if (descriptor.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE)
continue;
// obtain handle (to perform IO on device)
libusb_device_handle *handle;
r = libusb_open(device, &handle);
printf("- Device %d: vendor id=%04X, product id=%04X, class=%02X, subclass=%02X\n", i_device, descriptor.idVendor, descriptor.idProduct, descriptor.bDeviceClass, descriptor.bDeviceSubClass);
if (r < 0) {
const char* err_message = libusb_error_name(r);
printf("Error in libusb_open(): %s\n", err_message);
break;
}
// get proudct name & manufacturer name for device
const size_t SIZE_BUFFER = 256;
unsigned char product[SIZE_BUFFER];
unsigned char manufacturer[SIZE_BUFFER];
int n_bytes1 = libusb_get_string_descriptor_ascii(handle, descriptor.iProduct, product, SIZE_BUFFER);
if (n_bytes1 <= 0) {
printf("Error in libusb_get_string_descriptor_ascii(): %s\n", libusb_error_name(n_bytes1));
libusb_close(handle);
continue;
}
int n_bytes2 = libusb_get_string_descriptor_ascii(handle, descriptor.iManufacturer, manufacturer, SIZE_BUFFER);
if (n_bytes2 <= 0) {
printf("Error in libusb_get_string_descriptor_ascii(): %s\n", libusb_error_name(n_bytes2));
libusb_close(handle);
continue;
}
printf("product: %s - %s\n", manufacturer, product);
libusb_close(handle);
} // END DEVICES
// unreference devices & free list
libusb_free_device_list(list, 1);
// deinitialize libusb (NULL => use default context)
libusb_exit(NULL);
return 0;
}

Below some notions about USB that could be useful to access USB devices programatically with libusb. For more details refer to USB2.0 specification.

Terminology

  • Host: Computer.
  • USB device: Peripheral connected to computer via USB.
  • Hub: USB device providing additionnal connections to the USB.
  • IRQ (Interrupt request): a hardware signal from device requesting attention from host.
  • Pipe: Logical abstraction for associating device's endpoint and host's software. There two types of pipes:
    • Stream pipe: data no USB-defined structure.
    • Message pipe: data has a USB-defined structure.

Notions

  • Descriptors: Data structures providing information about USB device (stored on device).
  • Configuration: Configuration descriptors are read by host from device (e.g. max power consumption).
  • Interfaces: Each interface represents a feature/function provided by device (e.g. a multifunction printer has multiple functions: as a printer, a fax, a scanner). It may include one or more endpoints (multiple ones for simultenous communication, e.g. left & right audio streams).
  • Endpoints: Communication channels via which data is transferred between host and device. The are the ultimate consumer (sink) or provider (source) of data.
  • Unique address: Assigned by system to logical device on attachment.

Transfer types:

A transfer type is chosen according to the intended use case:

  • Control transfer: Used for device configuration and control (e.g. device initialization, its confguration). Guaranteed to be reliable.
  • Bulk: Used for the non-time critical transfer of a large amount of data async (e..g file transfers). Guarantees data delivery.
  • Interrupt: Used to send small time-sensitive data packets (e.g. periodic data updates in real-time from mouse).
  • Isochronous: Used for streaming data continously (e.g. audio and video streaming). No error detection or retransmission.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment