You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下识别USB Hub(根设备)及连接子设备的实现方法

Linux下USB Hub及其子设备枚举实现方案

核心思路

libusb本身不直接提供USB设备的拓扑父子关系,需结合Linux的sysfs文件系统获取设备层级信息——sysfs会完整保存USB设备的连接结构,包括Hub端口与子设备的对应关系。

实现步骤

  1. 匹配目标Hub:通过idVendor和idProduct枚举所有USB设备,筛选出目标Cable Matters Hub。
  2. 读取Hub信息:打开匹配到的Hub设备,读取其iSerial等属性。
  3. 枚举子设备:通过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;
}

编译与运行

  1. 替换代码中的TARGET_VENDOR和TARGET_PRODUCT为你的Hub实际ID(通过lsusb命令获取)。
  2. 编译命令:gcc -o usb_hub_scan usb_hub_scan.c -lusb-1.0
  3. 以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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 12:45:43