《Linux 设备驱动开发详解:基于最新的 Linux 4.0 内核》

第 16 章 Linux USB 主机与设备驱动

参考:宋宝华 著,机械工业出版社,2015年版


16.1 USB 协议基础

16.1.1 USB 概述

USB(Universal Serial Bus,通用串行总线)是目前最广泛使用的外设连接总线,具有热插拔、即插即用、高速传输等特点:

USB 发展历程:

版本          速率              连接器          典型应用
─────────────────────────────────────────────────────────────
USB 1.0/1.1  1.5Mbps(低速)   Type-A/B        键盘、鼠标
             12Mbps(全速)
USB 2.0      480Mbps(高速)   Type-A/B/Mini/  U盘、摄像头
                               Micro
USB 3.0      5Gbps(超速)     Type-A/B/       高速存储
                               Micro-B
USB 3.1      10Gbps            Type-C          高速传输
USB 3.2      20Gbps            Type-C          高速传输
USB 4.0      40Gbps            Type-C          雷电兼容

16.1.2 USB 拓扑结构

USB 采用星形拓扑,以 Hub 为中心连接多个设备:

USB 拓扑结构(最多 7 层级联):

主机(Host)
  │
  ├── Root Hub(根集线器,内置于主机控制器)
  │       ├── USB 键盘(直连)
  │       ├── USB Hub(外部集线器)
  │       │       ├── USB 鼠标
  │       │       ├── U 盘
  │       │       └── USB 摄像头
  │       └── USB 打印机(直连)
  │
  └── 最多支持 127 个设备(7位地址)

USB 通信特点:
  - 主从模式:主机(Host)主动发起所有传输
  - 设备(Device)被动响应
  - 所有通信都经过主机控制器
  - 设备不能主动发起传输(只能等待主机轮询)

16.1.3 USB 描述符

USB 设备通过**描述符(Descriptor)**向主机描述自己的特性:

USB 描述符层次结构:

设备描述符(Device Descriptor)
  ├── 配置描述符(Configuration Descriptor)
  │       ├── 接口描述符(Interface Descriptor)
  │       │       ├── 端点描述符(Endpoint Descriptor)
  │       │       ├── 端点描述符
  │       │       └── ...
  │       └── 接口描述符
  │               └── ...
  └── 配置描述符(可有多个配置)

设备描述符关键字段:
  bDeviceClass:设备类(0=由接口决定,0xFF=厂商自定义)
  bDeviceSubClass:设备子类
  bDeviceProtocol:设备协议
  idVendor:厂商 ID(VID,由 USB-IF 分配)
  idProduct:产品 ID(PID,由厂商分配)
  bcdDevice:设备版本号

接口描述符关键字段:
  bInterfaceClass:接口类(HID=3, Mass Storage=8, CDC=2)
  bInterfaceSubClass:接口子类
  bInterfaceProtocol:接口协议
  bNumEndpoints:端点数量

端点描述符关键字段:
  bEndpointAddress:端点地址(bit7=方向:0=OUT,1=IN)
  bmAttributes:传输类型(0=控制,1=等时,2=批量,3=中断)
  wMaxPacketSize:最大包大小
  bInterval:轮询间隔(中断/等时端点)

16.1.4 USB 传输类型

USB 四种传输类型:

1. 控制传输(Control Transfer)
   - 用于设备配置和控制命令
   - 每个设备必须有端点0(控制端点)
   - 三阶段:SETUP → DATA(可选)→ STATUS
   - 保证数据完整性(有错误重传)
   - 典型用途:枚举、获取描述符、设置配置

2. 批量传输(Bulk Transfer)
   - 用于大量数据传输
   - 无带宽保证,但保证数据完整性
   - 有错误重传机制
   - 典型用途:U盘、打印机、扫描仪

3. 中断传输(Interrupt Transfer)
   - 用于低延迟、小数据量传输
   - 主机定期轮询设备
   - 保证最大延迟(轮询间隔)
   - 典型用途:键盘、鼠标、游戏手柄

4. 等时传输(Isochronous Transfer)
   - 用于实时数据流
   - 保证带宽,但不保证数据完整性(无重传)
   - 典型用途:音频、视频、摄像头

传输类型对比:
┌──────────────┬──────────┬──────────┬──────────┬──────────┐
│              │  控制    │  批量    │  中断    │  等时    │
├──────────────┼──────────┼──────────┼──────────┼──────────┤
│ 带宽保证     │ 否       │ 否       │ 是(最大)│ 是       │
│ 错误重传     │ 是       │ 是       │ 是       │ 否       │
│ 延迟保证     │ 否       │ 否       │ 是       │ 是       │
│ 典型用途     │ 枚举配置 │ 存储设备 │ HID设备  │ 音视频   │
└──────────────┴──────────┴──────────┴──────────┴──────────┘

16.1.5 USB 枚举过程

USB 设备枚举过程(设备插入时):

1. 设备插入,Hub 检测到电压变化
   ↓
2. Hub 通知主机(中断传输)
   ↓
3. 主机复位设备(发送 Reset 信号)
   ↓
4. 主机以地址0读取设备描述符(前8字节)
   ↓
5. 主机分配唯一地址(1~127)
   ↓
6. 主机读取完整设备描述符
   ↓
7. 主机读取配置描述符(含接口和端点描述符)
   ↓
8. 主机读取字符串描述符(厂商名、产品名等)
   ↓
9. 主机选择配置(SET_CONFIGURATION)
   ↓
10. 内核加载对应的 USB 设备驱动(根据 VID:PID 匹配)
    ↓
11. 驱动 probe 函数执行,设备就绪

16.2 Linux USB 驱动架构

16.2.1 Linux USB 子系统层次结构

Linux USB 驱动架构:

┌─────────────────────────────────────────────────────────────┐
│                      用户空间                                │
│  应用程序通过 /dev/usb/xxx 或 libusb 访问 USB 设备           │
└──────────────────────────┬──────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────┐
│                   USB 设备驱动层                              │
│  针对具体 USB 设备的驱动(U盘、摄像头、键盘等)               │
│  usb_driver + usb_interface                                 │
└──────────────────────────┬──────────────────────────────────┘
                           │ usb_submit_urb()
┌──────────────────────────▼──────────────────────────────────┐
│                   USB 核心层(USB Core)                     │
│  管理 USB 设备、驱动的注册/注销                              │
│  提供 URB(USB Request Block)传输机制                       │
│  处理设备枚举、电源管理                                      │
└──────────────────────────┬──────────────────────────────────┘
                           │ hcd_submit_urb()
┌──────────────────────────▼──────────────────────────────────┐
│                   USB 主机控制器驱动层(HCD)                 │
│  EHCI(USB 2.0)/ OHCI / UHCI(USB 1.x)                   │
│  xHCI(USB 3.x)                                            │
└──────────────────────────┬──────────────────────────────────┘
                           │ 操作硬件寄存器
┌──────────────────────────▼──────────────────────────────────┐
│                   USB 主机控制器硬件                          │
│  SoC 内置 USB 控制器 / 外部 USB 控制器芯片                   │
└─────────────────────────────────────────────────────────────┘

USB Gadget 架构(设备端):
┌─────────────────────────────────────────────────────────────┐
│  Gadget 功能驱动(g_mass_storage / g_serial / g_ether 等)  │
└──────────────────────────┬──────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────┐
│                   USB Gadget 核心                            │
└──────────────────────────┬──────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────┐
│                   USB 设备控制器驱动(UDC)                   │
│  dwc2 / musb / chipidea 等                                  │
└─────────────────────────────────────────────────────────────┘

16.2.2 URB —— USB 请求块

URB(USB Request Block)是 Linux USB 子系统中最核心的数据结构,用于描述一次 USB 传输请求:

#include <linux/usb.h>

/*
 * urb:USB 请求块
 * 类似网络子系统中的 sk_buff
 * 描述一次完整的 USB 传输请求
 */
struct urb {
    /* 链表和引用计数 */
    struct kref         kref;
    struct list_head    urb_list;

    /* 关联的 USB 设备和端点 */
    struct usb_device   *dev;
    struct usb_host_endpoint *ep;
    unsigned int        pipe;       /* 端点管道(编码了方向和端点号)*/

    /* 传输数据 */
    void                *transfer_buffer;     /* 数据缓冲区 */
    dma_addr_t          transfer_dma;         /* DMA 地址 */
    u32                 transfer_buffer_length; /* 缓冲区长度 */
    u32                 actual_length;        /* 实际传输长度 */

    /* 控制传输专用 */
    unsigned char       *setup_packet;        /* SETUP 包 */
    dma_addr_t          setup_dma;

    /* 传输参数 */
    int                 interval;             /* 轮询间隔(中断/等时)*/
    int                 start_frame;          /* 等时传输起始帧 */
    int                 number_of_packets;    /* 等时包数量 */

    /* 完成回调 */
    usb_complete_t      complete;             /* 传输完成回调函数 */
    void                *context;            /* 传给回调函数的参数 */

    /* 状态 */
    int                 status;              /* 传输状态(0=成功)*/
    unsigned int        transfer_flags;      /* 传输标志 */
};

/* 管道(pipe)的构造 */
/* 控制管道 */
unsigned int pipe = usb_sndctrlpipe(dev, 0);  /* 控制输出(端点0)*/
unsigned int pipe = usb_rcvctrlpipe(dev, 0);  /* 控制输入(端点0)*/

/* 批量管道 */
unsigned int pipe = usb_sndbulkpipe(dev, endpoint_addr); /* 批量输出 */
unsigned int pipe = usb_rcvbulkpipe(dev, endpoint_addr); /* 批量输入 */

/* 中断管道 */
unsigned int pipe = usb_sndintpipe(dev, endpoint_addr);  /* 中断输出 */
unsigned int pipe = usb_rcvintpipe(dev, endpoint_addr);  /* 中断输入 */

/* 等时管道 */
unsigned int pipe = usb_sndisocpipe(dev, endpoint_addr); /* 等时输出 */
unsigned int pipe = usb_rcvisocpipe(dev, endpoint_addr); /* 等时输入 */

URB 的操作函数

/* 分配 URB */
struct urb *urb = usb_alloc_urb(iso_packets, GFP_KERNEL);
/* iso_packets:等时包数量,非等时传输为0 */

/* 释放 URB */
usb_free_urb(urb);

/* 初始化批量 URB */
usb_fill_bulk_urb(urb,
                  dev,           /* USB 设备 */
                  pipe,          /* 管道 */
                  buf,           /* 数据缓冲区 */
                  buf_len,       /* 缓冲区长度 */
                  complete_fn,   /* 完成回调函数 */
                  context);      /* 回调函数参数 */

/* 初始化中断 URB */
usb_fill_int_urb(urb, dev, pipe, buf, buf_len,
                 complete_fn, context,
                 interval);      /* 轮询间隔 */

/* 初始化控制 URB */
usb_fill_control_urb(urb, dev, pipe,
                     (unsigned char *)setup_packet,
                     buf, buf_len,
                     complete_fn, context);

/* 提交 URB(异步,立即返回)*/
int ret = usb_submit_urb(urb, GFP_ATOMIC);

/* 取消 URB */
usb_kill_urb(urb);    /* 等待 URB 完成或取消 */
usb_unlink_urb(urb);  /* 异步取消 */

/* 同步传输(内部使用 URB,但阻塞等待完成)*/
int ret = usb_bulk_msg(dev, pipe, buf, len, &actual_len, timeout);
int ret = usb_control_msg(dev, pipe, request, requesttype,
                           value, index, buf, size, timeout);

16.3 USB 主机控制器驱动

16.3.1 主机控制器类型

USB 主机控制器(HCD,Host Controller Driver)类型:

UHCI(Universal Host Controller Interface):
  - Intel 开发,用于 USB 1.x(1.5Mbps/12Mbps)
  - 驱动:uhci-hcd

OHCI(Open Host Controller Interface):
  - Compaq/Microsoft 开发,用于 USB 1.x
  - 嵌入式系统常用
  - 驱动:ohci-hcd

EHCI(Enhanced Host Controller Interface):
  - Intel 开发,用于 USB 2.0(480Mbps)
  - 向下兼容 USB 1.x(通过 companion controller)
  - 驱动:ehci-hcd

xHCI(eXtensible Host Controller Interface):
  - Intel 开发,用于 USB 3.x(5Gbps/10Gbps)
  - 向下兼容 USB 2.0/1.x
  - 驱动:xhci-hcd

16.3.2 主机控制器驱动框架

#include <linux/usb/hcd.h>

/*
 * usb_hcd:USB 主机控制器描述符
 * 每个 USB 主机控制器对应一个 usb_hcd
 */
struct usb_hcd {
    struct usb_bus          self;       /* USB 总线 */
    const struct hc_driver  *driver;    /* 主机控制器驱动 */
    void __iomem            *regs;      /* 寄存器基地址 */
    /* ... */
};

/*
 * hc_driver:主机控制器操作函数集
 */
struct hc_driver {
    const char  *description;   /* 控制器描述 */
    const char  *product_desc;  /* 产品描述 */
    size_t       hcd_priv_size; /* 私有数据大小 */

    /* 初始化和复位 */
    int (*reset)(struct usb_hcd *hcd);
    int (*start)(struct usb_hcd *hcd);
    void (*stop)(struct usb_hcd *hcd);
    void (*shutdown)(struct usb_hcd *hcd);

    /* URB 处理 */
    int (*urb_enqueue)(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);
    int (*urb_dequeue)(struct usb_hcd *hcd, struct urb *urb, int status);

    /* 端点管理 */
    int (*endpoint_disable)(struct usb_hcd *hcd, struct usb_host_endpoint *ep);

    /* Hub 操作 */
    int (*hub_status_data)(struct usb_hcd *hcd, char *buf);
    int (*hub_control)(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                       u16 wIndex, char *buf, u16 wLength);

    /* 电源管理 */
    int (*bus_suspend)(struct usb_hcd *hcd);
    int (*bus_resume)(struct usb_hcd *hcd);
};

/* 分配和注册 HCD */
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
                                struct device *dev,
                                const char *bus_name);
int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum,
                unsigned long irqflags);
void usb_remove_hcd(struct usb_hcd *hcd);
void usb_put_hcd(struct usb_hcd *hcd);

16.3.3 简化的 HCD 驱动示例

/*
 * 简化的 USB 主机控制器驱动框架
 * 展示 HCD 驱动的基本结构
 */

/* 控制器寄存器定义 */
#define HC_CTRL_REG     0x00
#define HC_STATUS_REG   0x04
#define HC_INTR_REG     0x08
#define HC_PORTSC_REG   0x0C

struct my_hcd_priv {
    void __iomem    *base;
    int              irq;
    spinlock_t       lock;
};

/* 获取私有数据 */
static inline struct my_hcd_priv *hcd_to_priv(struct usb_hcd *hcd)
{
    return (struct my_hcd_priv *)(hcd->hcd_priv);
}

/* 复位控制器 */
static int my_hcd_reset(struct usb_hcd *hcd)
{
    struct my_hcd_priv *priv = hcd_to_priv(hcd);

    /* 软件复位 */
    writel(0x01, priv->base + HC_CTRL_REG);
    msleep(10);
    writel(0x00, priv->base + HC_CTRL_REG);

    return 0;
}

/* 启动控制器 */
static int my_hcd_start(struct usb_hcd *hcd)
{
    struct my_hcd_priv *priv = hcd_to_priv(hcd);

    /* 使能控制器 */
    writel(0x01, priv->base + HC_CTRL_REG);

    /* 使能中断 */
    writel(0xFF, priv->base + HC_INTR_REG);

    hcd->state = HC_STATE_RUNNING;
    return 0;
}

/* 停止控制器 */
static void my_hcd_stop(struct usb_hcd *hcd)
{
    struct my_hcd_priv *priv = hcd_to_priv(hcd);

    /* 禁止中断 */
    writel(0x00, priv->base + HC_INTR_REG);

    /* 停止控制器 */
    writel(0x00, priv->base + HC_CTRL_REG);
}

/* 提交 URB */
static int my_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
                               gfp_t mem_flags)
{
    /* 将 URB 提交到硬件队列 */
    /* 实际实现需要根据传输类型(控制/批量/中断/等时)分别处理 */
    return 0;
}

/* 取消 URB */
static int my_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
                               int status)
{
    /* 从硬件队列中移除 URB */
    usb_hcd_giveback_urb(hcd, urb, status);
    return 0;
}

/* Hub 控制(端口复位、电源控制等)*/
static int my_hcd_hub_control(struct usb_hcd *hcd, u16 typeReq,
                               u16 wValue, u16 wIndex,
                               char *buf, u16 wLength)
{
    struct my_hcd_priv *priv = hcd_to_priv(hcd);

    switch (typeReq) {
    case GetPortStatus:
        /* 读取端口状态 */
        *(u32 *)buf = readl(priv->base + HC_PORTSC_REG);
        break;
    case SetPortFeature:
        /* 设置端口特性(如复位)*/
        if (wValue == USB_PORT_FEAT_RESET) {
            writel(0x10, priv->base + HC_PORTSC_REG);
            msleep(50);
            writel(0x00, priv->base + HC_PORTSC_REG);
        }
        break;
    }
    return 0;
}

static const struct hc_driver my_hc_driver = {
    .description     = "my-hcd",
    .product_desc    = "My USB Host Controller",
    .hcd_priv_size   = sizeof(struct my_hcd_priv),
    .reset           = my_hcd_reset,
    .start           = my_hcd_start,
    .stop            = my_hcd_stop,
    .urb_enqueue     = my_hcd_urb_enqueue,
    .urb_dequeue     = my_hcd_urb_dequeue,
    .hub_control     = my_hcd_hub_control,
};

static int my_hcd_probe(struct platform_device *pdev)
{
    struct usb_hcd *hcd;
    struct my_hcd_priv *priv;
    struct resource *res;
    int irq, ret;

    /* 分配 HCD */
    hcd = usb_create_hcd(&my_hc_driver, &pdev->dev, dev_name(&pdev->dev));
    if (!hcd)
        return -ENOMEM;

    priv = hcd_to_priv(hcd);

    /* 获取寄存器资源 */
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    hcd->rsrc_start = res->start;
    hcd->rsrc_len   = resource_size(res);
    priv->base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(priv->base)) {
        ret = PTR_ERR(priv->base);
        goto err_put;
    }

    /* 获取中断号 */
    irq = platform_get_irq(pdev, 0);
    if (irq < 0) {
        ret = irq;
        goto err_put;
    }

    spin_lock_init(&priv->lock);

    /* 注册 HCD */
    ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
    if (ret)
        goto err_put;

    platform_set_drvdata(pdev, hcd);
    dev_info(&pdev->dev, "USB 主机控制器注册成功\n");
    return 0;

err_put:
    usb_put_hcd(hcd);
    return ret;
}

static int my_hcd_remove(struct platform_device *pdev)
{
    struct usb_hcd *hcd = platform_get_drvdata(pdev);
    usb_remove_hcd(hcd);
    usb_put_hcd(hcd);
    return 0;
}

16.4 USB 设备驱动

16.4.1 USB 设备驱动的核心结构

#include <linux/usb.h>

/*
 * usb_driver:USB 设备驱动
 * 通过 id_table 中的 VID:PID 与设备匹配
 */
struct usb_driver {
    const char          *name;          /* 驱动名称 */

    /* 设备与驱动匹配时调用 */
    int (*probe)(struct usb_interface *intf,
                 const struct usb_device_id *id);

    /* 设备断开时调用 */
    void (*disconnect)(struct usb_interface *intf);

    /* 设备 ID 表(VID:PID 列表)*/
    const struct usb_device_id *id_table;

    /* 电源管理 */
    int (*suspend)(struct usb_interface *intf, pm_message_t message);
    int (*resume)(struct usb_interface *intf);

    struct device_driver driver;
};

/*
 * usb_device_id:USB 设备 ID
 * 用于匹配特定的 USB 设备
 */
struct usb_device_id {
    __u16   match_flags;    /* 匹配标志 */
    __u16   idVendor;       /* 厂商 ID */
    __u16   idProduct;      /* 产品 ID */
    __u16   bcdDevice_lo;   /* 设备版本范围(低)*/
    __u16   bcdDevice_hi;   /* 设备版本范围(高)*/
    __u8    bDeviceClass;   /* 设备类 */
    __u8    bDeviceSubClass;/* 设备子类 */
    __u8    bDeviceProtocol;/* 设备协议 */
    __u8    bInterfaceClass;/* 接口类 */
    __u8    bInterfaceSubClass; /* 接口子类 */
    __u8    bInterfaceProtocol; /* 接口协议 */
    kernel_ulong_t driver_info; /* 驱动私有信息 */
};

/* 常用的 ID 匹配宏 */
USB_DEVICE(vendor, product)           /* 匹配特定 VID:PID */
USB_DEVICE_VER(vendor, product, lo, hi) /* 匹配特定版本范围 */
USB_DEVICE_INFO(class, subclass, protocol) /* 匹配设备类 */
USB_INTERFACE_INFO(class, subclass, protocol) /* 匹配接口类 */

16.4.2 完整的 USB 设备驱动案例(USB 串口转换器)

/*
 * usb_serial_simple.c —— 简单的 USB 串口转换器驱动
 * 以 CH340/CH341 USB 转串口芯片为例
 *
 * CH340 VID:PID = 0x1A86:0x7523
 */

#include <linux/module.h>
#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>

#define CH340_VID       0x1A86
#define CH340_PID       0x7523
#define CH340_BUF_SIZE  512

/* 驱动私有数据 */
struct ch340_priv {
    struct usb_device       *udev;
    struct usb_interface    *intf;

    /* 端点信息 */
    unsigned int             bulk_in_endpointAddr;
    unsigned int             bulk_out_endpointAddr;
    unsigned int             int_in_endpointAddr;

    /* 缓冲区 */
    unsigned char           *bulk_in_buf;
    unsigned char           *bulk_out_buf;
    size_t                   bulk_in_size;

    /* URB */
    struct urb              *read_urb;
    struct urb              *write_urb;
    struct urb              *int_urb;

    /* 同步 */
    struct mutex             io_mutex;
    bool                     disconnected;
};

/* ── 接收完成回调 ──────────────────────────────────────────── */

static void ch340_read_bulk_callback(struct urb *urb)
{
    struct ch340_priv *priv = urb->context;
    int status = urb->status;
    int ret;

    if (status) {
        if (status == -ENOENT || status == -ECONNRESET ||
            status == -ESHUTDOWN) {
            return;  /* URB 被取消 */
        }
        pr_err("ch340: 接收错误:%d\n", status);
        goto resubmit;
    }

    if (urb->actual_length > 0) {
        /* 处理接收到的数据 */
        pr_info("ch340: 接收 %d 字节\n", urb->actual_length);
        /* 实际驱动中会将数据推送到 TTY 层 */
        /* tty_insert_flip_string(port, data, len); */
        /* tty_flip_buffer_push(port); */
    }

resubmit:
    /* 重新提交 URB,继续接收 */
    ret = usb_submit_urb(urb, GFP_ATOMIC);
    if (ret)
        pr_err("ch340: 重新提交接收 URB 失败:%d\n", ret);
}

/* ── 发送完成回调 ──────────────────────────────────────────── */

static void ch340_write_bulk_callback(struct urb *urb)
{
    struct ch340_priv *priv = urb->context;

    if (urb->status) {
        pr_err("ch340: 发送错误:%d\n", urb->status);
    } else {
        pr_info("ch340: 发送完成,%d 字节\n", urb->actual_length);
    }
}

/* ── 发送数据 ──────────────────────────────────────────────── */

static int ch340_write(struct ch340_priv *priv,
                        const unsigned char *buf, int count)
{
    int ret;

    if (priv->disconnected)
        return -ENODEV;

    if (count > CH340_BUF_SIZE)
        count = CH340_BUF_SIZE;

    mutex_lock(&priv->io_mutex);

    /* 复制数据到发送缓冲区 */
    memcpy(priv->bulk_out_buf, buf, count);

    /* 初始化发送 URB */
    usb_fill_bulk_urb(priv->write_urb,
                      priv->udev,
                      usb_sndbulkpipe(priv->udev,
                                      priv->bulk_out_endpointAddr),
                      priv->bulk_out_buf,
                      count,
                      ch340_write_bulk_callback,
                      priv);

    /* 提交发送 URB */
    ret = usb_submit_urb(priv->write_urb, GFP_KERNEL);
    if (ret)
        pr_err("ch340: 提交发送 URB 失败:%d\n", ret);

    mutex_unlock(&priv->io_mutex);
    return ret ? ret : count;
}

/* ── probe 函数 ──────────────────────────────────────────────── */

static int ch340_probe(struct usb_interface *intf,
                        const struct usb_device_id *id)
{
    struct usb_device *udev = interface_to_usbdev(intf);
    struct usb_host_interface *iface_desc;
    struct usb_endpoint_descriptor *endpoint;
    struct ch340_priv *priv;
    int i, ret;

    pr_info("ch340: 检测到设备 VID=%04X PID=%04X\n",
            le16_to_cpu(udev->descriptor.idVendor),
            le16_to_cpu(udev->descriptor.idProduct));

    /* 分配私有数据 */
    priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    priv->udev = usb_get_dev(udev);
    priv->intf = intf;
    mutex_init(&priv->io_mutex);

    /* 遍历接口的所有端点,找到 Bulk IN/OUT 和 Interrupt IN */
    iface_desc = intf->cur_altsetting;
    for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
        endpoint = &iface_desc->endpoint[i].desc;

        if (usb_endpoint_is_bulk_in(endpoint)) {
            priv->bulk_in_endpointAddr = endpoint->bEndpointAddress;
            priv->bulk_in_size = le16_to_cpu(endpoint->wMaxPacketSize);
            pr_info("ch340: Bulk IN 端点:0x%02x,最大包:%zu\n",
                    priv->bulk_in_endpointAddr, priv->bulk_in_size);
        }

        if (usb_endpoint_is_bulk_out(endpoint)) {
            priv->bulk_out_endpointAddr = endpoint->bEndpointAddress;
            pr_info("ch340: Bulk OUT 端点:0x%02x\n",
                    priv->bulk_out_endpointAddr);
        }

        if (usb_endpoint_is_int_in(endpoint)) {
            priv->int_in_endpointAddr = endpoint->bEndpointAddress;
            pr_info("ch340: Interrupt IN 端点:0x%02x\n",
                    priv->int_in_endpointAddr);
        }
    }

    /* 检查是否找到必要的端点 */
    if (!priv->bulk_in_endpointAddr || !priv->bulk_out_endpointAddr) {
        pr_err("ch340: 未找到必要的端点\n");
        ret = -ENODEV;
        goto err_free;
    }

    /* 分配接收缓冲区 */
    priv->bulk_in_buf = kmalloc(priv->bulk_in_size, GFP_KERNEL);
    if (!priv->bulk_in_buf) {
        ret = -ENOMEM;
        goto err_free;
    }

    /* 分配发送缓冲区 */
    priv->bulk_out_buf = kmalloc(CH340_BUF_SIZE, GFP_KERNEL);
    if (!priv->bulk_out_buf) {
        ret = -ENOMEM;
        goto err_in_buf;
    }

    /* 分配接收 URB */
    priv->read_urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!priv->read_urb) {
        ret = -ENOMEM;
        goto err_out_buf;
    }

    /* 分配发送 URB */
    priv->write_urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!priv->write_urb) {
        ret = -ENOMEM;
        goto err_read_urb;
    }

    /* 初始化并提交接收 URB */
    usb_fill_bulk_urb(priv->read_urb,
                      udev,
                      usb_rcvbulkpipe(udev, priv->bulk_in_endpointAddr),
                      priv->bulk_in_buf,
                      priv->bulk_in_size,
                      ch340_read_bulk_callback,
                      priv);

    ret = usb_submit_urb(priv->read_urb, GFP_KERNEL);
    if (ret) {
        pr_err("ch340: 提交接收 URB 失败:%d\n", ret);
        goto err_write_urb;
    }

    /* 保存私有数据 */
    usb_set_intfdata(intf, priv);

    pr_info("ch340: 驱动加载成功\n");
    return 0;

err_write_urb:
    usb_free_urb(priv->write_urb);
err_read_urb:
    usb_free_urb(priv->read_urb);
err_out_buf:
    kfree(priv->bulk_out_buf);
err_in_buf:
    kfree(priv->bulk_in_buf);
err_free:
    usb_put_dev(udev);
    kfree(priv);
    return ret;
}

/* ── disconnect 函数 ──────────────────────────────────────────── */

static void ch340_disconnect(struct usb_interface *intf)
{
    struct ch340_priv *priv = usb_get_intfdata(intf);

    usb_set_intfdata(intf, NULL);

    mutex_lock(&priv->io_mutex);
    priv->disconnected = true;
    mutex_unlock(&priv->io_mutex);

    /* 取消所有 URB */
    usb_kill_urb(priv->read_urb);
    usb_kill_urb(priv->write_urb);

    /* 释放资源 */
    usb_free_urb(priv->write_urb);
    usb_free_urb(priv->read_urb);
    kfree(priv->bulk_out_buf);
    kfree(priv->bulk_in_buf);
    usb_put_dev(priv->udev);
    kfree(priv);

    pr_info("ch340: 设备已断开\n");
}

/* ── 设备 ID 表 ──────────────────────────────────────────────── */

static const struct usb_device_id ch340_id_table[] = {
    { USB_DEVICE(CH340_VID, CH340_PID) },
    { USB_DEVICE(0x1A86, 0x7522) },  /* CH340K */
    { USB_DEVICE(0x1A86, 0x5523) },  /* CH341A */
    {}
};
MODULE_DEVICE_TABLE(usb, ch340_id_table);

/* ── USB 驱动结构体 ──────────────────────────────────────────── */

static struct usb_driver ch340_driver = {
    .name       = "ch340",
    .probe      = ch340_probe,
    .disconnect = ch340_disconnect,
    .id_table   = ch340_id_table,
};

module_usb_driver(ch340_driver);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CH340/CH341 USB 转串口驱动");

16.4.3 USB 控制传输

/*
 * USB 控制传输(Control Transfer)
 * 用于设备配置和控制命令
 */

/* 同步控制传输(阻塞等待完成)*/
int usb_control_msg(struct usb_device *dev,
                    unsigned int pipe,
                    __u8 request,
                    __u8 requesttype,
                    __u16 value,
                    __u16 index,
                    void *data,
                    __u16 size,
                    int timeout);

/* 案例:CH340 设置波特率 */
static int ch340_set_baudrate(struct usb_device *udev, int baudrate)
{
    unsigned char factor;
    unsigned char divisor;

    /* 计算波特率因子和分频值 */
    /* CH340 波特率计算公式(简化)*/
    factor  = 0x9C;
    divisor = 0x80;

    /* 发送控制命令设置波特率 */
    return usb_control_msg(udev,
                            usb_sndctrlpipe(udev, 0),
                            0x9A,           /* 请求码:设置波特率 */
                            0x40,           /* 请求类型:厂商,设备 */
                            (factor << 8) | divisor, /* 值 */
                            0,              /* 索引 */
                            NULL,           /* 无数据 */
                            0,              /* 数据长度 */
                            1000);          /* 超时 1000ms */
}

/* 案例:读取 USB 设备描述符 */
static int read_device_descriptor(struct usb_device *udev)
{
    struct usb_device_descriptor desc;
    int ret;

    ret = usb_control_msg(udev,
                           usb_rcvctrlpipe(udev, 0),
                           USB_REQ_GET_DESCRIPTOR,  /* 0x06 */
                           USB_DIR_IN,              /* 0x80 */
                           (USB_DT_DEVICE << 8),    /* 描述符类型 */
                           0,
                           &desc,
                           sizeof(desc),
                           1000);

    if (ret == sizeof(desc)) {
        pr_info("VID=0x%04x, PID=0x%04x\n",
                le16_to_cpu(desc.idVendor),
                le16_to_cpu(desc.idProduct));
    }

    return ret;
}

16.4.4 USB 批量传输

/*
 * USB 批量传输(Bulk Transfer)
 * 用于大量数据传输(U盘、打印机等)
 */

/* 同步批量传输 */
int usb_bulk_msg(struct usb_device *usb_dev,
                 unsigned int pipe,
                 void *data,
                 int len,
                 int *actual_length,
                 int timeout);

/* 案例:向 USB 设备发送数据 */
static int usb_send_data(struct usb_device *udev,
                          unsigned int out_endpoint,
                          const void *data, int len)
{
    int actual_len;
    int ret;

    ret = usb_bulk_msg(udev,
                        usb_sndbulkpipe(udev, out_endpoint),
                        (void *)data,
                        len,
                        &actual_len,
                        5000);  /* 5秒超时 */

    if (ret)
        pr_err("发送失败:%d\n", ret);
    else
        pr_info("发送成功:%d/%d 字节\n", actual_len, len);

    return ret;
}

/* 案例:从 USB 设备接收数据 */
static int usb_recv_data(struct usb_device *udev,
                          unsigned int in_endpoint,
                          void *buf, int buf_len)
{
    int actual_len;
    int ret;

    ret = usb_bulk_msg(udev,
                        usb_rcvbulkpipe(udev, in_endpoint),
                        buf,
                        buf_len,
                        &actual_len,
                        5000);

    if (ret)
        pr_err("接收失败:%d\n", ret);
    else
        pr_info("接收成功:%d 字节\n", actual_len);

    return ret < 0 ? ret : actual_len;
}

16.5 USB Gadget 驱动

16.5.1 USB Gadget 的概念

USB Gadget 是 Linux 中用于实现 **USB 设备端(Device Side)**功能的框架。当 Linux 系统作为 USB 设备(而非主机)时,需要使用 Gadget 驱动:

USB Gadget 的应用场景:

场景一:Android 手机连接 PC
  手机(Linux/Android)作为 USB 设备
  PC 作为 USB 主机
  手机通过 Gadget 驱动实现:
    - MTP(媒体传输协议)
    - ADB(Android 调试桥)
    - RNDIS(USB 网络)
    - Mass Storage(U盘模式)

场景二:嵌入式开发板调试
  开发板作为 USB 设备
  PC 作为 USB 主机
  开发板通过 Gadget 驱动实现:
    - USB 串口(g_serial)
    - USB 网络(g_ether)
    - USB 存储(g_mass_storage)

场景三:USB OTG(On-The-Go)
  设备可以动态切换主机/设备角色
  通过 ID 引脚检测角色

16.5.2 USB Gadget 架构

USB Gadget 软件架构:

┌─────────────────────────────────────────────────────────────┐
│                   Gadget 功能驱动                            │
│  g_mass_storage  g_serial  g_ether  g_webcam  g_audio       │
│  (实现具体的 USB 功能,如存储、串口、网络等)               │
└──────────────────────────┬──────────────────────────────────┘
                           │ usb_ep_queue()
┌──────────────────────────▼──────────────────────────────────┐
│                   Gadget API 层                              │
│  usb_gadget / usb_ep / usb_request                          │
│  提供统一的端点操作接口                                      │
└──────────────────────────┬──────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────┐
│                   UDC 驱动(USB Device Controller)          │
│  dwc2 / musb / chipidea / s3c-udc 等                        │
│  控制具体的 USB 设备控制器硬件                               │
└──────────────────────────┬──────────────────────────────────┘
                           │ 操作硬件寄存器
┌──────────────────────────▼──────────────────────────────────┐
│                   USB 设备控制器硬件                          │
│  SoC 内置 USB OTG 控制器                                    │
└─────────────────────────────────────────────────────────────┘

16.5.3 Gadget API 核心数据结构

#include <linux/usb/gadget.h>

/*
 * usb_gadget:USB 设备控制器抽象
 * 由 UDC 驱动提供,Gadget 功能驱动使用
 */
struct usb_gadget {
    struct usb_udc          *udc;
    const struct usb_gadget_ops *ops;
    struct usb_ep           *ep0;       /* 控制端点0 */
    struct list_head         ep_list;   /* 所有端点列表 */
    enum usb_device_speed    speed;     /* 当前速度 */
    unsigned                 is_otg:1;  /* 是否支持 OTG */
    /* ... */
};

/*
 * usb_ep:USB 端点
 * 代表一个 USB 端点(IN 或 OUT)
 */
struct usb_ep {
    void                    *driver_data;
    const char              *name;
    const struct usb_ep_ops *ops;
    struct list_head         ep_list;
    struct usb_endpoint_descriptor *desc;
    unsigned                 maxpacket;
};

/*
 * usb_request:USB 传输请求
 * 类似主机端的 URB
 */
struct usb_request {
    void                    *buf;       /* 数据缓冲区 */
    unsigned                 length;    /* 数据长度 */
    dma_addr_t               dma;       /* DMA 地址 */
    unsigned                 actual;    /* 实际传输长度 */
    int                      status;    /* 传输状态 */

    /* 完成回调 */
    void (*complete)(struct usb_ep *ep, struct usb_request *req);
    void                    *context;   /* 回调参数 */
};

/* Gadget API 操作函数 */

/* 使能端点 */
int usb_ep_enable(struct usb_ep *ep);

/* 禁用端点 */
int usb_ep_disable(struct usb_ep *ep);

/* 分配请求 */
struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);

/* 释放请求 */
void usb_ep_free_request(struct usb_ep *ep, struct usb_request *req);

/* 提交请求(异步)*/
int usb_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags);

/* 取消请求 */
int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req);

16.5.4 简单的 Gadget 功能驱动(USB 串口)

/*
 * 简化的 USB 串口 Gadget 驱动框架
 * 实现一个简单的 USB 串口设备
 */

#include <linux/module.h>
#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>

/* USB 描述符 */
static struct usb_device_descriptor device_desc = {
    .bLength            = USB_DT_DEVICE_SIZE,
    .bDescriptorType    = USB_DT_DEVICE,
    .bcdUSB             = cpu_to_le16(0x0200),  /* USB 2.0 */
    .bDeviceClass       = USB_CLASS_COMM,
    .idVendor           = cpu_to_le16(0x1234),  /* 厂商 ID */
    .idProduct          = cpu_to_le16(0x5678),  /* 产品 ID */
    .bcdDevice          = cpu_to_le16(0x0100),
    .bNumConfigurations = 1,
};

static struct usb_string strings_dev[] = {
    [USB_GADGET_MANUFACTURER_IDX].s = "My Company",
    [USB_GADGET_PRODUCT_IDX].s      = "USB Serial Gadget",
    [USB_GADGET_SERIAL_IDX].s       = "20240101",
    {}
};

static struct usb_gadget_strings stringtab_dev = {
    .language = 0x0409,  /* 英语 */
    .strings  = strings_dev,
};

static struct usb_gadget_strings *dev_strings[] = {
    &stringtab_dev,
    NULL,
};

/* Gadget 功能驱动私有数据 */
struct my_serial_dev {
    struct usb_gadget   *gadget;
    struct usb_ep       *in_ep;     /* Bulk IN 端点(设备→主机)*/
    struct usb_ep       *out_ep;    /* Bulk OUT 端点(主机→设备)*/
    struct usb_request  *in_req;    /* 发送请求 */
    struct usb_request  *out_req;   /* 接收请求 */
    bool                 connected;
};

/* 接收完成回调 */
static void my_serial_out_complete(struct usb_ep *ep,
                                    struct usb_request *req)
{
    struct my_serial_dev *dev = ep->driver_data;

    if (req->status == 0) {
        pr_info("my_serial: 接收 %d 字节:%.*s\n",
                req->actual, req->actual, (char *)req->buf);

        /* 回显数据(将接收到的数据发回主机)*/
        memcpy(dev->in_req->buf, req->buf, req->actual);
        dev->in_req->length = req->actual;
        usb_ep_queue(dev->in_ep, dev->in_req, GFP_ATOMIC);
    }

    /* 重新提交接收请求 */
    req->length = 512;
    usb_ep_queue(ep, req, GFP_ATOMIC);
}

/* 发送完成回调 */
static void my_serial_in_complete(struct usb_ep *ep,
                                   struct usb_request *req)
{
    if (req->status)
        pr_err("my_serial: 发送错误:%d\n", req->status);
    else
        pr_info("my_serial: 发送完成:%d 字节\n", req->actual);
}

/* 绑定函数(设备连接时调用)*/
static int my_serial_bind(struct usb_configuration *c,
                           struct usb_function *f)
{
    struct my_serial_dev *dev = container_of(f, struct my_serial_dev, func);
    struct usb_composite_dev *cdev = c->cdev;
    int ret;

    /* 分配接口编号 */
    ret = usb_interface_id(c, f);
    if (ret < 0)
        return ret;

    /* 查找并使能 Bulk IN 端点 */
    dev->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
    if (!dev->in_ep)
        return -ENODEV;
    dev->in_ep->driver_data = dev;

    /* 查找并使能 Bulk OUT 端点 */
    dev->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
    if (!dev->out_ep)
        return -ENODEV;
    dev->out_ep->driver_data = dev;

    /* 分配请求 */
    dev->in_req = usb_ep_alloc_request(dev->in_ep, GFP_KERNEL);
    dev->out_req = usb_ep_alloc_request(dev->out_ep, GFP_KERNEL);

    /* 分配缓冲区 */
    dev->in_req->buf  = kmalloc(512, GFP_KERNEL);
    dev->out_req->buf = kmalloc(512, GFP_KERNEL);

    /* 设置回调 */
    dev->in_req->complete  = my_serial_in_complete;
    dev->out_req->complete = my_serial_out_complete;

    return 0;
}

/* 设置函数(主机选择配置时调用)*/
static int my_serial_set_alt(struct usb_function *f,
                              unsigned intf, unsigned alt)
{
    struct my_serial_dev *dev = container_of(f, struct my_serial_dev, func);

    /* 使能端点 */
    usb_ep_enable(dev->in_ep);
    usb_ep_enable(dev->out_ep);

    /* 提交接收请求 */
    dev->out_req->length = 512;
    usb_ep_queue(dev->out_ep, dev->out_req, GFP_ATOMIC);

    dev->connected = true;
    pr_info("my_serial: 设备已连接\n");
    return 0;
}

/* 禁用函数(设备断开时调用)*/
static void my_serial_disable(struct usb_function *f)
{
    struct my_serial_dev *dev = container_of(f, struct my_serial_dev, func);

    usb_ep_disable(dev->in_ep);
    usb_ep_disable(dev->out_ep);

    dev->connected = false;
    pr_info("my_serial: 设备已断开\n");
}

16.5.5 使用 ConfigFS 配置 USB Gadget

Linux 4.0 引入了基于 ConfigFS 的 USB Gadget 配置方式,无需编写内核代码即可配置 USB Gadget:

# 挂载 ConfigFS
mount -t configfs none /sys/kernel/config

# 创建 Gadget
mkdir /sys/kernel/config/usb_gadget/my_gadget
cd /sys/kernel/config/usb_gadget/my_gadget

# 设置 VID 和 PID
echo 0x1234 > idVendor
echo 0x5678 > idProduct

# 设置字符串描述符
mkdir strings/0x409
echo "My Company"      > strings/0x409/manufacturer
echo "USB Gadget Demo" > strings/0x409/product
echo "20240101"        > strings/0x409/serialnumber

# 创建配置
mkdir configs/c.1
echo 250 > configs/c.1/MaxPower  # 250mA

# 添加 USB 串口功能
mkdir functions/acm.usb0
ln -s functions/acm.usb0 configs/c.1/

# 绑定到 UDC(USB 设备控制器)
ls /sys/class/udc/
# 20980000.usb   ← UDC 名称(因平台而异)
echo "20980000.usb" > UDC

# 验证
ls /dev/ttyGS0   # USB 串口设备文件

# 在主机端查看
lsusb
# Bus 001 Device 002: ID 1234:5678 My Company USB Gadget Demo

# 主机端使用串口
screen /dev/ttyACM0 115200

# 禁用 Gadget
echo "" > UDC

# 清理
rm configs/c.1/acm.usb0
rmdir functions/acm.usb0
rmdir configs/c.1/strings/0x409
rmdir configs/c.1
rmdir strings/0x409
cd /
rmdir /sys/kernel/config/usb_gadget/my_gadget

16.5.6 常用 Gadget 功能驱动

# ── USB 存储(Mass Storage)──────────────────────────────────
# 将开发板模拟为 U 盘
modprobe g_mass_storage file=/dev/mmcblk0 removable=1
# 主机端会看到一个 U 盘设备

# ── USB 串口(Serial)────────────────────────────────────────
# 将开发板模拟为 USB 串口
modprobe g_serial
# 开发板端:/dev/ttyGS0
# 主机端:/dev/ttyACM0

# ── USB 网络(Ethernet)──────────────────────────────────────
# 将开发板模拟为 USB 网卡
modprobe g_ether
# 开发板端:usb0 网络接口
# 主机端:eth1 或 usb0 网络接口
ifconfig usb0 192.168.7.2
# 主机端:
ifconfig usb0 192.168.7.1

# ── USB 复合设备(多功能)────────────────────────────────────
# 同时提供串口和网络功能
modprobe g_cdc
# 开发板端:/dev/ttyGS0 + usb0

# ── Android ADB(调试桥)─────────────────────────────────────
# Android 设备使用的 ADB 功能
# 通过 ConfigFS 配置 ffs(FunctionFS)功能

本章小结

章节 核心知识点 关键 API
16.1 USB协议基础 USB版本演进;星形拓扑;描述符层次(设备/配置/接口/端点);四种传输类型对比;枚举过程(10步) 概念理解
16.2 Linux USB驱动架构 主机端架构(设备驱动→USB核心→HCD);Gadget端架构(功能驱动→Gadget核心→UDC);URB结构体与操作函数 usb_alloc_urb()usb_fill_bulk_urb()usb_submit_urb()
16.3 USB主机控制器驱动 HCD类型(UHCI/OHCI/EHCI/xHCI);hc_driver操作函数集;usb_create_hcd/usb_add_hcd;简化HCD驱动框架 usb_create_hcd()usb_add_hcd()usb_hcd_giveback_urb()
16.4 USB设备驱动 usb_driver/usb_device_id结构体;端点遍历(usb_endpoint_is_bulk_in等);完整CH340驱动(probe/disconnect/URB收发);控制传输/批量传输API usb_fill_bulk_urb()usb_control_msg()usb_bulk_msg()
16.5 USB Gadget驱动 Gadget应用场景;Gadget架构(功能驱动→Gadget API→UDC);usb_gadget/usb_ep/usb_request;简单串口Gadget框架;ConfigFS配置方式;常用Gadget功能驱动 usb_ep_queue()usb_ep_enable()、ConfigFS

USB 驱动开发要点

主机端设备驱动:
1. 在 id_table 中列出支持的 VID:PID
2. probe 中遍历端点,找到所需的 IN/OUT 端点
3. 分配 URB 和缓冲区
4. 提交接收 URB,在回调中处理数据并重新提交
5. disconnect 中取消所有 URB,释放资源

Gadget 设备驱动:
1. 定义 USB 描述符(设备/配置/接口/端点)
2. 实现 bind/set_alt/disable 函数
3. 使用 usb_ep_queue 提交传输请求
4. 在完成回调中处理数据
5. 使用 ConfigFS 配置(推荐,无需修改内核代码)

调试工具:
  lsusb -v          ← 查看 USB 设备描述符
  usbmon            ← 抓取 USB 数据包
  wireshark         ← 分析 USB 数据包
  /sys/bus/usb/devices/  ← USB 设备信息

参考文献:宋宝华《Linux设备驱动开发详解:基于最新的Linux 4.0内核》,机械工业出版社,2015年

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐