USB 设备驱动的整体结构
Linux 实现了以下几类通用的USB设备驱动,并且为它们分配了主次设备号:
- 音频设备类
- 通信设备类
- HID(人机接口)设备类
- 显示设备类
- 海量存储设备类
- 电源设备类
- 打印设备类
- 集线器设备类
在 sysfs (/sys/bus/usb) 和 debugfs (/sys/kernel/debug/usb) 下都可以查看USB设备信息 。
设备驱动的抽象
使用 usb_driver 结构体来描述一个 USB 设备驱动。
USB设备的抽象
使用 usb_device_id 结构体来表示驱动所支持的设备,由 usb_driver 的 id_table 成员指向这个结构体的一个实例化的数组。如:
/* drivers/hid/usbhid/usbkbd.c */
static struct usb_device_id usb_kbd_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_KEYBOARD) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
使用 usb_interface 结构体来表示一个“USB接口设备”,它可以对USB设备进行具体数据传输的“端点”进行管理。一个物理上的USB设备通常包括多个配置-接口-端点这样的层级关系,USB协议中使用设备描述符、配置描述符、接口描述符、端点描述符这样的概念来进行描述。而接口描述符,提供了接口所使用的类、子类、协议以及该接口的端点数等信息。
使用 usb_device 结构体来表示一个USB设备,它通常被包含在具体设备的私有数据的结构体中;在驱动与USB核心层/USB总线的交互过程中,会使用到这个结构体的实例。
通常在驱动的probe函数中,通过 interface_to_usbdev() 函数来由 usb_interface 获得 usb_device。
/* linux/sub.h */
static inline struct usb_device *interface_to_usbdev(struct usb_interface *intf);
USB数据传输的抽象
URB(USB request block, USB 请求块)是 USB 设备驱动中用来描述与 USB 设备通信所用的基本载体和核心数据结构,在内核中使用 urb 结构体来表示。
核心数据结构:urb
struct urb 被定义在 linux/usb.h 中。
urb的生命周期
- 设备驱动:创建 urb
- 设备驱动:为 urb 指定一个端点
- 设备驱动:将 urb 提交给USB核心,并设置一个回调函数
- USB 核心:提交给设备对应的USB主机控制器驱动
- USB Host:完成一次 USB 传送,并通知设备驱动
- 设备驱动:回调函数被调用,回调是在中断上下文运行的
同时,应当注意,urb 可以被提交它的驱动在任意时刻取消,或者被USB核心从系统中移出。
urb 的创建和销毁
/* drivers/usb/core/urb.c */
struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
void usb_free_urb(struct urb *urb);
4类urb 的初始化
/* linux/usb.h */
/* 中断 urb */
static inline void usb_fill_int_urb (struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete,
void *context,
int interval);
/* 批量 urb */
static inline void usb_fill_bulk_urb (struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete,
void *context);
/* 控制 urb */
static inline void usb_fill_control_urb (struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
unsigned char *setup_packet,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete,
void *context);
/* 等时 urb */
/* USB 等时传输,并没有其它3种传输方式那样的辅助函数,而是手动实现初始化 */
/* 以下是 drivers/usb/media/uvc/uvc_video.c 中的一段实例 */
// uvc_init_video_isoc()
for (i = 0; i < UVC_URBS; ++i) {
urb = usb_alloc_urb(npackets, gfp_flags);
if (urb == NULL) {
uvc_uninit_video(stream, 1);
return -ENOMEM;
}
urb->dev = stream->dev->udev;
urb->context = stream;
urb->pipe = usb_rcvisocpipe(stream->dev->udev,
ep->desc.bEndpointAddress);
#ifndef CONFIG_DMA_NONCOHERENT
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = stream->urb_dma[i];
#else
urb->transfer_flags = URB_ISO_ASAP;
#endif
urb->interval = ep->desc.bInterval;
urb->transfer_buffer = stream->urb_buffer[i];
urb->complete = uvc_video_complete;
urb->number_of_packets = npackets;
urb->transfer_buffer_length = size;
for (j = 0; j < npackets; ++j) {
urb->iso_frame_desc[j].offset = j * psize;
urb->iso_frame_desc[j].length = psize;
}
stream->urb[i] = urb;
}
urb 的提交
/* drivers/usb/core/urb.c */
int usb_submit_urb(struct urb *urb, int mem_flags);
urb 的取消
提交urb给USB核心之后,设备驱动是可以主动取消urb的。usb_unlink_urb() 是异步的,通常在urb 的complete函数中调用;usb_kill_urb() 则是终止 urb 的生命周期,它会在执行完成之后才返回,通常在设备的disconnect函数中调用。
/* drivers/usb/core/urb.c */
int usb_unlink_urb(struct urb *urb);
void usb_kill_urb(struct urb *urb);
urb 的完成
urb 结构体的 complete 成员指向其“完成时的回调函数”;当urb被完成时,这个函数被usb核心调用。
以下几种情况,此 complete 函数会被调用:
- urb 被成功发送给设备,即数据被成功发送到外部(OUT),或者请求的数据被成功接收(IN)
- 传输过程中发生错误,这时候 status 成员会记录对应的错误值
- 驱动程序主动取消urb的传输
- urb被提交,但是对应的设备已经不存在
在 complete 函数中判断 urb 的 status 就能知道,urb完成的时候是处于哪种具体的情况:
错误代码 | 原因 |
---|---|
ECONNRESET | 被 usb_unlink_urb() 杀死 |
ENOENT | 被 usb_kill_urb() 杀死 |
EPROTO | 发生bitstuff错误或者硬件超时 |
ENODEV | USB设备已被移除 |
EXDEV | 等时传输只完成一部分 |
驱动代码分析
init, exit
在设备的 init 函数中注册 usb 驱动;在设备的 exit 函数中注销 usb 驱动。
在驱动注册之前 usb_driver 结构体需要被正确地初始化,有5个成员要求必须被初始化:
static struct usb_driver xxx_driver = {
.owner = THIS_MODULE,
.name = "xxx",
.id_table = xxx_table,
.probe = xxx_probe,
.disconnect = xxx_disconnect,
};
init 和 exit 函数:
static int __init xxx_init(void)
{
int result = usb_register(&xxx_driver);
if (result == 0)
info(DRIVER_VERSION ":" DRIVER_DESC);
return result;
}
static void __exit xxx_exit(void)
{
usb_deregister(&xxx_driver);
}
module_init(xxx_init);
module_exit(xxx_exit);
也可以直接使用 module_usb_driver 宏来注册驱动
module_usb_driver(xxx_driver);
probe, disconnect
它们分别在一个USB设备被接入并且和驱动匹配,以及设备断开连接的时候被调用。设备和驱动匹配时,usb_interface 结构体被实例化,并传入驱动的 porbe 函数中。
probe 和 disconnect 函数的回调都在USB集线器内核线程的上下文中被调用,因此它们中睡眠是合法的。
USB 设备驱动的probe函数和disconnect函数的原型如下:
static int xxx_probe(struct usb_interface *iface, const struct usb_device_id *id);
static void xxx_disconnect(struct usb_interface *intf);
probe() 函数的主要工作
(1) 探测设备的端点地址、缓冲区大小,初始化与USB设备控制相关的结构体
(2) 把已经初始化的设备结构体保存到接口设备中
(3) 注册USB设备
disconnect() 函数的主要工作
(1) 释放所有为设备分配的资源
(2) 设置接口设备的数据指针为NULL
(3) 注销USB设备
相关API
/* linux/usb.h */
static inline void usb_set_intfdata(struct usb_interface *intf, void *data);
static inline void *usb_get_intfdata(struct usb_interface *intf);
/* drivers/usb/core/file.c */
int usb_register_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver);
void usb_deregister_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver);
/* linux/usb.h */
struct usb_class_driver {
char *name;
char *(*devnode)(struct device *dev, umode_t *mode);
const struct file_operations *fops;
int minor_base;
};
usb_class_driver 用来指明 usb设备它本身的设备类型,如字符设备、输入设备、块设备、tty设备等;对于字符设备而言,usb_class_driver 结构体的 fops 成员中的 open, read, write 等函数,其地位完全与 file_operations结构体的成员相同;其它类型的设备,则调用其对应的注册函数。但是这些设备挂接在USB总线上,其数据的传输,都是通过 urb 来完成的。
usb_register_dev() 并非必须的,它在设备驱动使用USB主设备号的 时候才必须被调用;它会在 sysfs 文件系统下根据 usb_class_driver 创建相应的目录。
设备驱动中对 urb 的处理
由前面的“urb的生命周期”一节可以知道,在设备驱动中对urb的处理包括:创建和初始化urb,提交urb,取消urb,以及urb完成之后的处理。
以下通过一个例子来说明urb在设备驱动中被处理的过程:
【drivers/usb/usb-skeleton.c: skel_write()】
//分配urb
urb = usb_alloc_urb(0, GFP_KERNEL);
if(!urb) {
retval = -ENOMEM;
goto error;
}
//分配内存,并映射到一个DMA缓冲,这样传递到驱动的数据会被拷贝到缓冲
buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
if(!buf) {
retval = -ENOMEM;
goto error;
}
//从用户空间拷贝数据到内核空间
if(copy_from_user(buf, user_buffer, count)) {
retval = -EFAULT;
goto error;
}
//初始化urb,把对应USB端点、内核缓存指针、complete函数等信息提供给结构体
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf, count, skel_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
//提交urb到usb核心
retval = usb_submit_urb(urb, GFP_KERNEL);
if(retval) {
err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
goto error;
}
urb完成回调函数。错误代码 -ENOENT, -ECONNRESET, -ESHUTDOWN 不是真正的传送错误, 只是报告伴随成功传送的情况。
static void skel_write_bulk_callback(struct urb *urb)
{
struct usb_skel *dev;
dev = urb->context;
/* sync/async unlink faults aren't errors */
if (urb->status) {
if (!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN))
dev_err(&dev->interface->dev,
"%s - nonzero write bulk status received: %d\n",
__func__, urb->status);
spin_lock(&dev->err_lock);
dev->errors = urb->status;
spin_unlock(&dev->err_lock);
}
/* free up our allocated buffer */
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
up(&dev->limit_sem);
}
简单的 bulk 和 contorl 消息
对于批量传输或者控制传输,如果只是发送一些简单的数据,可以使用两个快捷的API:usb_bulk_msg() 以及 usb_control_msg() ;这两个函数是同步的,会等待URB完成之后才返回,成功时返回完成传输(发送或接收)的字节数,失败时返回负值错误代码;因为等待返回的过程中会被挂起,这两个函数不能在中断上下文或者持有自旋锁的情况下使用。
另外,由于这两个函数不能被其它函数取消,所以,在设备的 disconnect 函数中应该要保证能够判断和等待 usb_bulk_msg() 或者 usb_control_msg() 的调用结束。
/* drivers/usb/core/message.c */
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length, int timeout);
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);
参考源码
linux/usb.h
drivers/usb/core/urb.c
drivers/usb/core/file.c
drivers/usb/core/message.c
drivers/usb/media/uvc/uvc_video.c
drivers/usb/usb-skeleton.c
drivers/hid/usbhid/usbkbd.c