I first heard about libusb back in 2011 when I was working on USRP1. At the time, USRP had a really good C++ abstraction library called UHD, which did the heavy lifting talking to libusb. Hopefully, it takes me less than a decade to do a second example.

What is libusb? Link to heading

libusb website says it all

libusb is a C library that provides generic access to USB devices. It is intended to be used by developers to facilitate the production of applications that communicate with USB hardware.

It is portable: Using a single cross-platform API, it provides access to USB devices on Linux, macOS, Windows, etc.

It is user-mode: No special privilege or elevation is required for the application to communicate with a device.

It is version-agnostic: All versions of the USB protocol, from 1.0 to 3.1 (latest), are supported.

Hello world Link to heading

This first example is from [libstdevs.c])2

int main(void)
{
	libusb_device **devs;
	int r;
	ssize_t cnt;

	r = libusb_init(NULL);
	if (r < 0)
		return r;

	cnt = libusb_get_device_list(NULL, &devs);
	if (cnt < 0){
		libusb_exit(NULL);
		return (int) cnt;
	}

	print_devs(devs);
	libusb_free_device_list(devs, 1);

	libusb_exit(NULL);
	return 0;
}

Here is a rundown of APIs used in above snippet

libusb_init Link to heading

From API docs

int libusb_init ( libusb_context ** context )

Initialize libusb. This function must be called before calling any other libusb function.

libusb_get_device_list Link to heading

From API docs

ssize_t libusb_get_device_list ( libusb_context * ctx, libusb_device *** list )

Returns a list of USB devices currently attached to the system. This is your entry point into finding a USB device to operate.

USB descriptors Link to heading

The next part in listdev.c is looping over the device list and print device descriptor and device-specific info.

static void print_devs(libusb_device **devs)
{
	libusb_device *dev;
	int i = 0, j = 0;
	uint8_t path[8]; 

	while ((dev = devs[i++]) != NULL) {
		struct libusb_device_descriptor desc;
		int r = libusb_get_device_descriptor(dev, &desc);
		if (r < 0) {
			fprintf(stderr, "failed to get device descriptor");
			return;
		}

		printf("%04x:%04x (bus %d, device %d)",
			desc.idVendor, desc.idProduct,
			libusb_get_bus_number(dev), libusb_get_device_address(dev));

		r = libusb_get_port_numbers(dev, path, sizeof(path));
		if (r > 0) {
			printf(" path: %d", path[0]);
			for (j = 1; j < r; j++)
				printf(".%d", path[j]);
		}
		printf("\n");
	}
}

Device descriptor libusb_get_device_descriptor Link to heading

libusb_device_descriptor defined in API doc is returned by libusb_get_device_descriptor.

Device descriptor fields are defined in USB2 specs section 9.6.1. We can also see the fields by doing lsusb -v. For example,

Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               3.00
  bDeviceClass            9 Hub
  bDeviceSubClass         0 
  bDeviceProtocol         3 
  bMaxPacketSize0         9
  idVendor           
  idProduct          
  bcdDevice            5.04
  iManufacturer           3 
  iProduct                2 
  iSerial                 1 
  bNumConfigurations      1

Device information API Link to heading

There are also APIs to get info about device like libusb_get_bus_number. bus number or port number depends on the implementation and OS running on the host.

I traced libusb_get_bus_number in libusb repo to libusb/os/linux_usbfs.c where it reads the bus number assigned from /dev/bus/.

if (!strncmp(dev_node, "/dev/bus/usb", 12))
    sscanf(dev_node, "/dev/bus/usb/%hhu/%hhu", busnum, devaddr);