1. 枚举USB音频设备

任务目标

编写一个C语言程序,枚举系统中所有USB音频设备(具有音频接口的USB设备),并打印每个设备的厂商ID、产品ID、设备描述符中的关键字段,以及配置描述符中音频接口的接口编号、接口类别和端点信息。

环境与依赖

本案例基于Linux系统,使用libusb-1.0库访问USB设备。安装依赖:

sudo apt-get install libusb-1.0-0-dev

核心实现

程序通过libusb遍历USB总线上的设备,对每个设备获取设备描述符和配置描述符,遍历配置描述符中的所有接口,筛选出bInterfaceClass == 0x01(音频类)的接口并打印相关信息。

#include <stdio.h>
#include <libusb-1.0/libusb.h>

static void print_audio_interface(const struct libusb_interface_descriptor *intf) {
    printf("  Audio Interface:\n");
    printf("    Interface Number: %d\n", intf->bInterfaceNumber);
    printf("    Interface Class: 0x%02X\n", intf->bInterfaceClass);
    printf("    Interface SubClass: 0x%02X\n", intf->bInterfaceSubClass);
    printf("    Interface Protocol: 0x%02X\n", intf->bInterfaceProtocol);
    printf("    Endpoint Count: %d\n", intf->bNumEndpoints);
    for (int e = 0; e < intf->bNumEndpoints; e++) {
        const struct libusb_endpoint_descriptor *ep = &intf->endpoint[e];
        printf("      EP 0x%02X: Attr=0x%02X MaxPacket=%d Interval=%d\n",
               ep->bEndpointAddress, ep->bmAttributes,
               ep->wMaxPacketSize, ep->bInterval);
    }
}

static void check_device(libusb_device *dev) {
    struct libusb_device_descriptor desc;
    int ret = libusb_get_device_descriptor(dev, &desc);
    if (ret != 0) return;

    struct libusb_config_descriptor *config = NULL;
    ret = libusb_get_active_config_descriptor(dev, &config);
    if (ret != 0) return;

    int has_audio = 0;
    for (int i = 0; i < config->bNumInterfaces; i++) {
        const struct libusb_interface *iface = &config->interface[i];
        for (int a = 0; a < iface->num_altsetting; a++) {
            if (iface->altsetting[a].bInterfaceClass == 0x01) {
                if (!has_audio) {
                    printf("USB Audio Device Found:\n");
                    printf("  VID=0x%04X PID=0x%04X\n", desc.idVendor, desc.idProduct);
                    printf("  Device Class: 0x%02X SubClass: 0x%02X Protocol: 0x%02X\n",
                           desc.bDeviceClass, desc.bDeviceSubClass, desc.bDeviceProtocol);
                    printf("  Manufacturer: 0x%02X Product: 0x%02X Serial: 0x%02X\n",
                           desc.iManufacturer, desc.iProduct, desc.iSerialNumber);
                    printf("  Config Value: %d NumInterfaces: %d\n",
                           config->bConfigurationValue, config->bNumInterfaces);
                    has_audio = 1;
                }
                print_audio_interface(&iface->altsetting[a]);
            }
        }
    }
    libusb_free_config_descriptor(config);
}

int main(void) {
    libusb_device **devs;
    ssize_t cnt;
    int ret = libusb_init(NULL);
    if (ret < 0) {
        fprintf(stderr, "libusb_init failed: %d\n", ret);
        return 1;
    }

    cnt = libusb_get_device_list(NULL, &devs);
    if (cnt < 0) {
        fprintf(stderr, "libusb_get_device_list failed\n");
        libusb_exit(NULL);
        return 1;
    }

    printf("Scanning %zd USB devices...\n\n", cnt);
    for (ssize_t i = 0; i < cnt; i++) {
        check_device(devs[i]);
    }

    libusb_free_device_list(devs, 1);
    libusb_exit(NULL);
    return 0;
}

编译与运行

gcc -o usb_audio_enum usb_audio_enum.c $(pkg-config --cflags --libs libusb-1.0)
sudo ./usb_audio_enum

需要sudo权限访问USB设备信息。

验证方法

运行程序后,观察输出:

  • 若系统连接了USB音频设备(如USB声卡、USB耳机),程序会打印该设备的VID/PID及音频接口详情。
  • 若没有音频设备,程序仅显示扫描到的设备总数,不打印设备信息。
  • 可通过lsusb命令交叉验证:lsusb -t查看设备树,或lsusb -v -d 1234:5678(替换为实际VID/PID)查看详细描述符,对比程序输出的接口类别是否为0x01。

判定通过条件:程序能正确识别并打印至少一个USB音频设备的音频接口信息,且接口类别字段为0x01,端点信息与lsusb -v输出一致。