Testing Linux I2C, USB, and PCI Drivers Using Userspace Tools and `Sysfs`

Linux provides robust mechanisms for interacting with devices via sysfs. This allows developers to query and manipulate kernel objects from user space. Tools like lsusb, lsi2c, and lspci offer easy ways to test USB, I2C, and PCI devices. This article explains how to use these tools, highlights their underlying mechanisms, and provides practical steps to test Linux drivers.


Understanding sysfs

sysfs is a virtual filesystem used to expose kernel and device attributes to user space. It allows users to access hardware details directly.

Key directories include:

  • /sys/class/: Lists device classes like usb, i2c-dev, and pci.
  • /sys/devices/: Provides a hierarchical view of devices.
  • /sys/bus/: Groups devices and drivers by their bus types (e.g., usb, i2c, pci).

Common Commands

1
2
3
ls /sys/bus/usb/devices/    # List USB devices
ls /sys/bus/i2c/devices/ # List I2C devices
ls /sys/bus/pci/devices/ # List PCI devices

Testing USB Drivers with lsusb

lsusb is a user-space tool for listing USB devices. It retrieves device descriptors, vendor IDs, and product IDs using sysfs and /proc.

How lsusb Works

The source code of lsusb interacts with USB devices via libusb. Here’s a simplified excerpt from the source code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <libusb.h>

int main() {
libusb_context *ctx;
libusb_device **list;
ssize_t count;

libusb_init(&ctx);
count = libusb_get_device_list(ctx, &list);

for (ssize_t i = 0; i < count; i++) {
struct libusb_device_descriptor desc;
libusb_get_device_descriptor(list[i], &desc);
printf("Vendor: %04x, Product: %04x\n", desc.idVendor, desc.idProduct);
}

libusb_free_device_list(list, 1);
libusb_exit(ctx);
return 0;
}

Installing and Running lsusb

  1. Install lsusb:
    1
    sudo apt install usbutils
  2. Run lsusb to list connected USB devices:
    1
    lsusb

Testing I2C Drivers with lsi2c

lsi2c is a user-space tool for detecting and interacting with I2C devices. It communicates with devices using the /dev/i2c-* interface provided by the i2c-dev kernel module.

How lsi2c Works

The tool reads and writes I2C registers using the ioctl system call. Below is a simplified excerpt from the source code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>

int main() {
int file = open("/dev/i2c-1", O_RDWR);
int addr = 0x48;

ioctl(file, I2C_SLAVE, addr);
char buf[1] = {0};
read(file, buf, 1);
printf("Data: 0x%x\n", buf[0]);
close(file);
return 0;
}

Installing and Running lsi2c

  1. Clone the repository:
    1
    2
    git clone https://github.com/costad2/i2cdev.git
    cd i2cdev
  2. Build and run:
    1
    2
    make
    ./lsi2c

Testing PCI Drivers with lspci

lspci is a user-space tool for listing PCI devices and querying their details. It uses the /sys/bus/pci/devices/ directory and the /proc/bus/pci filesystem.

How lspci Works

The source code interacts with PCI configuration space and device information files. Here’s a simplified example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <pci/pci.h>

int main() {
struct pci_access *pacc;
struct pci_dev *dev;
char namebuf[1024];

pacc = pci_alloc();
pci_init(pacc);
pci_scan_bus(pacc);

for (dev = pacc->devices; dev; dev = dev->next) {
pci_fill_info(dev, PCI_FILL_IDENT);
pci_lookup_name(pacc, namebuf, sizeof(namebuf), PCI_LOOKUP_DEVICE, dev->vendor_id, dev->device_id);
printf("%04x:%02x:%02x.%d %s\n", dev->domain, dev->bus, dev->dev, dev->func, namebuf);
}

pci_cleanup(pacc);
return 0;
}

Installing and Running lspci

  1. Install lspci:
    1
    sudo apt install pciutils
  2. Run lspci to list PCI devices:
    1
    lspci
  3. Use lspci -vvv to display detailed information about each device.

Practical Examples

USB Device Testing

  1. Connect a USB device.
  2. Use lsusb to verify detection:
    1
    lsusb
  3. Check driver binding:
    1
    ls /sys/bus/usb/drivers/

I2C Device Testing

  1. Load the i2c-dev kernel module:
    1
    sudo modprobe i2c-dev
  2. Use lsi2c to interact with an I2C device:
    1
    ./lsi2c

PCI Device Testing

  1. Run lspci to list devices:
    1
    lspci
  2. Check driver binding for a specific device:
    1
    lspci -vvv -s <bus:slot.func>

Conclusion

sysfs provides a rich interface for querying and manipulating kernel devices, while tools like lsusb, lsi2c, and lspci simplify testing and debugging of USB, I2C, and PCI devices. Understanding how these tools interact with the kernel and hardware equips developers to validate driver functionality and troubleshoot issues effectively.