作者:刘洪涛,www.5idzw.com嵌入式学院讲师。
Linux USB 设备端驱动有两部分组成。一部分是USB 设备控制器(USB Device Controller, UDC)驱动、另一部分是硬件无关的功能驱动(如:鼠标、u盘、usb串口、usb网络等);也可以分为3层的,分别是:controller Drivers、Gadget Drivers、Upper Layers,大概意思都差不多。
一、控制器(USB device Controller, UDC)驱动
Gadget 框架提出了一套标准 API, 在底层, USB 设备控制器驱动则实现这一套 API, 不同的 UDC需要不同的驱动, 甚至基于同样的 UDC 的不同板子也需要进行代码修改。这一层是硬件相关层。
Linux 标准内核里支持各种主流 SOC 的 udc 驱动,如:S3C2410、PXA270等。你可以通过内核直接配置支持。你也可以通过修改它们获取更高的效率。如:s3c2410_uda.c 中并没有利用到控制器的dma功能,你可以根据需要修改它。
要理解UDC驱动代码就必须对相应的硬件控制器熟悉。当然,如果你对此不感兴趣,或没时间熟悉,也可以暂时跳过对硬件相关部分。本文也侧重于对软件结构的描述,不关心硬件细节。
下面给出在UDC驱动中涉及到的一些关键数据结构及API,参考s3c2410_uda.c
1.关键的数据结构及API
gadget api 提供了usb device controller 驱动和上层gadget驱动交互的接口。下面列出一些关键的数据结构。
struct usb_gadget {//代表一个UDC设备
/* readonly to gadget driver */
const struct usb_gadget_ops *ops; //设备的操作集
struct usb_ep *ep0; //ep0(USB协议中的端点0), 处理setup()请求
struct list_head ep_list; /* of usb_ep */本设备支持的端点链表
enum usb_device_speed speed; //如:USB_SPEED_LOW、USB_SPEED_FULL等
unsigned is_dualspeed:1; //支持full/high speed
unsigned is_otg:1; //OTG的特性
unsigned is_a_peripheral:1; //当前是A-peripheral,而不是A-host
unsigned b_hnp_enable:1;
unsigned a_hnp_support:1;
unsigned a_alt_hnp_support:1;
const char *name;
struct device dev;
};
struct usb_gadget_driver {//代表一个gadget设备driver,如:file_storage.c中的fsg_driver
//又如:如zero.c中的zero_driver
char *function; //一个字符串,如"Gadget Zero"
enum usb_device_speed speed;
int (*bind)(struct usb_gadget *);
void (*unbind)(struct usb_gadget *);
int (*setup)(struct usb_gadget *,
const struct usb_ctrlrequest *);
void (*disconnect)(struct usb_gadget *);
void (*suspend)(struct usb_gadget *);
void (*resume)(struct usb_gadget *)
/* FIXME support safe rmmod */
struct device_driver driver;
};
struct usb_gadget_ops {//代表设备的操作集
int (*get_frame)(struct usb_gadget *);
int (*wakeup)(struct usb_gadget *);
int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
nt (*vbus_session) (struct usb_gadget *, int is_active);
int (*vbus_draw) (struct usb_gadget *, unsigned mA);
int (*pullup) (struct usb_gadget *, int is_on);
int (*ioctl)(struct usb_gadget *,
unsigned code, unsigned long param);
};
struct usb_ep {//代表一个端点
void *driver_data //
...
const struct usb_ep_ops *ops; //端点的操作集,如上
struct list_head ep_list; //gadget的所有ep的list
...
};
struct usb_ep_ops {//表示端点的操作集
...
int (*queue) (struct usb_ep *ep, struct usb_request *req,
gfp_t gfp_flags); //将一个usb_request提交给endpoint
//是数据传输的关键函数
...
};
struct usb_request {//表示一个传输的请求,这与usb host端的urb类似
void *buf;
unsigned length;
dma_addr_t dma;
unsigned no_interrupt:1;
unsigned zero:1;
unsigned short_not_ok:1;
void (*complete)(struct usb_ep *ep,
struct usb_request *req);
void *context;
struct list_head list;
int status;
unsigned actual;
};
上述结构中具体每项的含义可以参考http://tali.admingilde.org/linux-docbook/gadget/
如:struct usb_request
在http://tali.admingilde.org/linux-docbook/gadget/re02.html中
Name
struct usb_request — describes one i/o request
Synopsis
struct usb_request {
void * buf;
unsigned length;
dma_addr_t dma;
unsigned no_interrupt:1;
unsigned zero:1;
unsigned short_not_ok:1;
void (* complete) (struct usb_ep *ep,struct usb_request *req);
void * context;
struct list_head list;
int status;
unsigned actual;
};
Members
buf
Buffer used for data. Always provide this; some controllers only use PIO, or don't use DMA for some endpoints.
length
Length of that data
dma
DMA address corresponding to 'buf'. If you don't set this field, and the usb controller needs one, it is responsible for mapping and unmapping the buffer.
no_interrupt
If true, hints that no completion irq is needed. Helpful sometimes with deep request queues that are handled directly by DMA controllers.
zero
If true, when writing data, makes the last packet be “short” by adding a zero length packet as needed;
short_not_ok
When reading data, makes short packets be treated as errors (queue stops advancing till cleanup).
complete
Function called when request completes, so this request and its buffer may be re-used. Reads terminate with a short packet, or when the buffer fills, whichever comes first. When writes terminate, some data bytes will usually still be in flight (often in a hardware fifo). Errors (for reads or writes) stop the queue from advancing until the completion function returns, so that any transfers invalidated by the error may first be dequeued.
context
For use by the completion callback
list
For use by the gadget driver.
status
Reports completion code, zero or a negative errno. Normally, faults block the transfer queue from advancing until the completion callback returns. Code “-ESHUTDOWN” indicates completion caused by device disconnect, or when the driver disabled the endpoint.
actual
Reports bytes transferred to/from the buffer. For reads (OUT transfers) this may be less than the requested length. If the short_not_ok flag is set, short reads are treated as errors even when status otherwise indicates successful completion. Note that for writes (IN transfers) some data bytes may still reside in a device-side FIFO when the request is reported as complete.
,USB gadget设备驱动解析(3)