Linux下识别USB Hub(根设备)及连接子设备的实现方法
Linux下USB Hub及其子设备枚举实现方案
核心思路
libusb本身不直接提供USB设备的拓扑父子关系,需结合Linux的sysfs文件系统获取设备层级信息——sysfs会完整保存USB设备的连接结构,包括Hub端口与子设备的对应关系。
实现步骤
- 匹配目标Hub:通过
idVendor和idProduct枚举所有USB设备,筛选出目标Cable Matters Hub。 - 读取Hub信息:打开匹配到的Hub设备,读取其iSerial等属性。
- 枚举子设备:通过sysfs中Hub的
ports目录定位所有连接的子设备,再通过libusb读取子设备信息。
代码实现
#include <libusb-1.0/libusb.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <dirent.h> #include <fcntl.h> // 替换为你的Cable Matters Hub实际ID(通过lsusb命令获取) #define TARGET_VENDOR 0x0B95 #define TARGET_PRODUCT 0x1790 // 获取设备对应的sysfs路径 char* get_sysfs_path(libusb_device *dev) { uint8_t bus = libusb_get_bus_number(dev); uint8_t addr = libusb_get_device_address(dev); char *path = malloc(64); snprintf(path, 64, "/sys/bus/usb/devices/%d-%d", bus, addr); return path; } // 读取设备的iSerial字符串 int get_serial_number(libusb_device_handle *handle, char *serial, size_t max_len) { int desc_idx = libusb_get_string_descriptor_index(handle, LIBUSB_DESC_STRING_SERIAL); if (desc_idx < 0) return -1; return libusb_get_string_descriptor_ascii(handle, desc_idx, (unsigned char*)serial, max_len); } // 枚举Hub下所有子设备,并读取子设备基本信息 void enumerate_hub_children(libusb_context *ctx, int hub_bus, int hub_addr) { char hub_ports_path[64]; snprintf(hub_ports_path, 64, "/sys/bus/usb/devices/%d-%d/ports", hub_bus, hub_addr); DIR *dir = opendir(hub_ports_path); if (!dir) { perror("Failed to open Hub ports directory"); return; } struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (entry->d_type != DT_DIR || !strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue; // 构造子设备的sysfs路径,读取子设备地址 char child_dev_path[128]; snprintf(child_dev_path, 128, "%s/%s/devnum", hub_ports_path, entry->d_name); FILE *f = fopen(child_dev_path, "r"); if (!f) continue; int child_addr; if (fscanf(f, "%d", &child_addr) != 1) { fclose(f); continue; } fclose(f); // 通过总线号和设备地址找到对应的libusb设备 libusb_device **devs; ssize_t cnt = libusb_get_device_list(ctx, &devs); if (cnt < 0) continue; for (ssize_t i = 0; i < cnt; i++) { libusb_device *dev = devs[i]; if (libusb_get_bus_number(dev) != hub_bus || libusb_get_device_address(dev) != child_addr) continue; struct libusb_device_descriptor desc; if (libusb_get_device_descriptor(dev, &desc) < 0) continue; printf(" 子设备: Bus %d, Addr %d | Vendor: 0x%04X, Product: 0x%04X\n", hub_bus, child_addr, desc.idVendor, desc.idProduct); // 可选:读取子设备的iSerial或其他属性 libusb_device_handle *handle = NULL; if (libusb_open(dev, &handle) == 0) { char serial[256]; int ret = get_serial_number(handle, serial, sizeof(serial)); if (ret > 0) { printf(" iSerial: %s\n", serial); } libusb_close(handle); } break; } libusb_free_device_list(devs, 1); } closedir(dir); } int main() { libusb_context *ctx = NULL; int r = libusb_init(&ctx); if (r < 0) { fprintf(stderr, "libusb init failed: %d\n", r); return 1; } libusb_device **devs; ssize_t cnt = libusb_get_device_list(ctx, &devs); if (cnt < 0) { fprintf(stderr, "Failed to get device list: %zd\n", cnt); libusb_exit(ctx); return 1; } // 遍历所有设备,寻找目标Hub for (ssize_t i = 0; i < cnt; i++) { libusb_device *dev = devs[i]; struct libusb_device_descriptor desc; if (libusb_get_device_descriptor(dev, &desc) < 0) continue; if (desc.idVendor == TARGET_VENDOR && desc.idProduct == TARGET_PRODUCT) { int bus = libusb_get_bus_number(dev); int addr = libusb_get_device_address(dev); printf("找到目标USB Hub:\n"); printf("Bus: %d, Address: %d | Vendor: 0x%04X, Product: 0x%04X\n", bus, addr, desc.idVendor, desc.idProduct); // 读取Hub的iSerial libusb_device_handle *handle = NULL; if (libusb_open(dev, &handle) == 0) { char serial[256]; int ret = get_serial_number(handle, serial, sizeof(serial)); if (ret > 0) { printf("iSerial: %s\n", serial); } else { printf("无法读取iSerial\n"); } libusb_close(handle); } else { fprintf(stderr, "无法打开Hub设备(需root权限)\n"); } // 枚举子设备 printf("\nHub下连接的子设备:\n"); enumerate_hub_children(ctx, bus, addr); } } libusb_free_device_list(devs, 1); libusb_exit(ctx); return 0; }
编译与运行
- 替换代码中的
TARGET_VENDOR和TARGET_PRODUCT为你的Hub实际ID(通过lsusb命令获取)。 - 编译命令:
gcc -o usb_hub_scan usb_hub_scan.c -lusb-1.0 - 以root权限运行:
sudo ./usb_hub_scan
验证方法
可以用lsusb -t命令查看系统USB拓扑结构,对比程序输出的子设备列表是否一致:
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 480M |__ Port 3: Dev 2, If 0, Class=Hub, Driver=hub/5p, 480M |__ Port 1: Dev 3, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M |__ Port 2: Dev 4, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
内容的提问来源于stack exchange,提问作者raj123
相关产品推荐
相关产品推荐

