Skip to content

USB 子系统

USB 架构概览

用户空间
    │  libusb / /dev/bus/usb/

┌─────────────────────────────────────┐
│           USB 核心层                 │
│  usb_register_driver / urb 管理      │
└──────┬──────────────────────────────┘

┌──────▼──────────────────────────────┐
│        USB 主机控制器驱动            │
│   xHCI (USB3) / EHCI (USB2) / OHCI  │
└──────┬──────────────────────────────┘
       │  USB 总线
    USB 设备(HID / CDC / 存储 / 自定义)

USB 设备驱动框架

c
#include <linux/module.h>
#include <linux/usb.h>

#define MY_VENDOR_ID   0x1234
#define MY_PRODUCT_ID  0x5678

struct my_usb_priv {
    struct usb_device    *udev;
    struct usb_interface *interface;
    u8   bulk_in_ep;
    u8   bulk_out_ep;
    size_t bulk_in_size;
    u8   *bulk_in_buf;
};

static int my_usb_probe(struct usb_interface *interface,
                         const struct usb_device_id *id)
{
    struct usb_device *udev = interface_to_usbdev(interface);
    struct my_usb_priv *priv;
    struct usb_host_interface *iface_desc;
    struct usb_endpoint_descriptor *ep;
    int i, ret;

    priv = devm_kzalloc(&interface->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    priv->udev      = usb_get_dev(udev);
    priv->interface = interface;

    /* 遍历端点,找到 Bulk IN 和 Bulk OUT */
    iface_desc = interface->cur_altsetting;
    for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
        ep = &iface_desc->endpoint[i].desc;

        if (usb_endpoint_is_bulk_in(ep)) {
            priv->bulk_in_ep   = ep->bEndpointAddress;
            priv->bulk_in_size = usb_endpoint_maxp(ep);
        }
        if (usb_endpoint_is_bulk_out(ep)) {
            priv->bulk_out_ep = ep->bEndpointAddress;
        }
    }

    if (!priv->bulk_in_ep || !priv->bulk_out_ep) {
        dev_err(&interface->dev, "could not find bulk endpoints\n");
        ret = -ENODEV;
        goto error;
    }

    priv->bulk_in_buf = devm_kmalloc(&interface->dev,
                                      priv->bulk_in_size, GFP_KERNEL);
    if (!priv->bulk_in_buf) {
        ret = -ENOMEM;
        goto error;
    }

    usb_set_intfdata(interface, priv);

    dev_info(&interface->dev, "USB device connected: VID=%04x PID=%04x\n",
             id->idVendor, id->idProduct);
    return 0;

error:
    usb_put_dev(udev);
    return ret;
}

static void my_usb_disconnect(struct usb_interface *interface)
{
    struct my_usb_priv *priv = usb_get_intfdata(interface);
    usb_set_intfdata(interface, NULL);
    usb_put_dev(priv->udev);
    dev_info(&interface->dev, "USB device disconnected\n");
}

/* Bulk 传输 */
static int my_usb_bulk_write(struct my_usb_priv *priv,
                              void *data, size_t len)
{
    int actual_len;
    return usb_bulk_msg(priv->udev,
                        usb_sndbulkpipe(priv->udev, priv->bulk_out_ep),
                        data, len, &actual_len, 5000);  /* 5s 超时 */
}

static const struct usb_device_id my_usb_table[] = {
    { USB_DEVICE(MY_VENDOR_ID, MY_PRODUCT_ID) },
    { USB_DEVICE_AND_INTERFACE_INFO(0x0000, 0x0000,
                                    USB_CLASS_CDC_DATA, 0, 0) },
    { }
};
MODULE_DEVICE_TABLE(usb, my_usb_table);

static struct usb_driver my_usb_driver = {
    .name       = "my-usb-driver",
    .probe      = my_usb_probe,
    .disconnect = my_usb_disconnect,
    .id_table   = my_usb_table,
};

module_usb_driver(my_usb_driver);
MODULE_LICENSE("GPL");

URB(USB Request Block)异步传输

c
struct urb *urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
    return -ENOMEM;

/* 填充 Bulk URB */
usb_fill_bulk_urb(urb,
    priv->udev,
    usb_rcvbulkpipe(priv->udev, priv->bulk_in_ep),
    priv->bulk_in_buf,
    priv->bulk_in_size,
    my_urb_complete,    /* 完成回调 */
    priv               /* 上下文 */
);

/* 提交 URB */
ret = usb_submit_urb(urb, GFP_KERNEL);

/* 完成回调(在中断上下文中执行) */
static void my_urb_complete(struct urb *urb)
{
    struct my_usb_priv *priv = urb->context;

    if (urb->status) {
        dev_err(&priv->interface->dev, "URB error: %d\n", urb->status);
        return;
    }
    /* 处理接收到的数据 urb->actual_length 字节 */
    /* 重新提交以持续接收 */
    usb_submit_urb(urb, GFP_ATOMIC);
}

/* 取消 URB */
usb_kill_urb(urb);
usb_free_urb(urb);

端点类型

类型方向特点典型用途
Control双向可靠,低速设备枚举、配置
Bulk双向可靠,无带宽保证大数据传输(存储)
InterruptIN/OUT低延迟,周期轮询HID(键鼠)、串口
IsochronousIN/OUT实时,允许丢包音视频流

调试工具

bash
# 查看 USB 设备树
lsusb -t

# 查看设备详细信息
lsusb -v -d 1234:5678

# 实时监控 USB 事件
udevadm monitor --udev --subsystem-match=usb

# usbmon 抓包(需 CONFIG_USB_MON=y)
modprobe usbmon
cat /sys/kernel/debug/usb/usbmon/1u

褚成志的笔记