《Linux 设备驱动开发详解:基于最新的 Linux 4.0 内核》 第 16 章 Linux USB 主机与设备驱动
·
《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年
更多推荐



所有评论(0)