USB gadget設(shè)備驅(qū)動(dòng)解析(3)
Linux USB 設(shè)備端驅(qū)動(dòng)有兩部分組成。一部分是USB 設(shè)備控制器(USBDevice Controller,UDC)驅(qū)動(dòng)、另一部分是硬件無關(guān)的功能驅(qū)動(dòng)(如:鼠標(biāo)、u盤、usb串口、usb網(wǎng)絡(luò)等);也可以分為3層的,分別是:ControllerDrivers、Gadget Drivers、Upper Layers,大概意思都差不多。
一、控制器(USB Device Controller, UDC)驅(qū)動(dòng)
Gadget 框架提出了一套標(biāo)準(zhǔn) API, 在底層, USB 設(shè)備控制器驅(qū)動(dòng)則實(shí)現(xiàn)這一套 API, 不同的 UDC需要不同的驅(qū)動(dòng), 甚至基于同樣的 UDC 的不同板子也需要進(jìn)行代碼修改。這一層是硬件相關(guān)層。
Linux 標(biāo)準(zhǔn)內(nèi)核里支持各種主流 SOC 的 udc驅(qū)動(dòng),如:S3C2410、PXA270等。你可以通過內(nèi)核直接配置支持。你也可以通過修改它們獲取更高的效率。如:s3c2410_uda.c中并沒有利用到控制器的dma功能,你可以根據(jù)需要修改它。
要理解UDC驅(qū)動(dòng)代碼就必須對(duì)相應(yīng)的硬件控制器熟悉。當(dāng)然,如果你對(duì)此不感興趣,或沒時(shí)間熟悉,也可以暫時(shí)跳過對(duì)硬件相關(guān)部分。本文也側(cè)重于對(duì)軟件結(jié)構(gòu)的描述,不關(guān)心硬件細(xì)節(jié)。
下面給出在UDC驅(qū)動(dòng)中涉及到的一些關(guān)鍵數(shù)據(jù)結(jié)構(gòu)及API,參考s3c2410_uda.c
1.關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)及API
gadget api 提供了usb device controller 驅(qū)動(dòng)和上層gadget驅(qū)動(dòng)交互的接口。下面列出一些關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)。
struct usb_gadget {//代表一個(gè)UDC設(shè)備
/* readonly to gadget driver */
const struct usb_gadget_ops *ops; //設(shè)備的操作集
struct usb_ep *ep0; //ep0(USB協(xié)議中的端點(diǎn)0), 處理setup()請(qǐng)求
struct list_head ep_list; /* of usb_ep */本設(shè)備支持的端點(diǎn)鏈表
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; //當(dāng)前是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 {//代表一個(gè)gadget設(shè)備driver,如:file_storage.c中的fsg_driver
//又如:如zero.c中的zero_driver
char *function; //一個(gè)字符串,如"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 {//代表設(shè)備的操作集
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 {//代表一個(gè)端點(diǎn)
void *driver_data //
...
const struct usb_ep_ops *ops; //端點(diǎn)的操作集,如上
struct list_head ep_list; //gadget的所有ep的list
...
};
struct usb_ep_ops {//表示端點(diǎn)的操作集
...
int (*queue) (struct usb_ep *ep, struct usb_request *req,
gfp_t gfp_flags); //將一個(gè)usb_request提交給endpoint
//是數(shù)據(jù)傳輸?shù)年P(guān)鍵函數(shù)
...
};
struct usb_request {//表示一個(gè)傳輸?shù)恼?qǐng)求,這與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;
};
上述結(jié)構(gòu)中具體每項(xiàng)的含義可以參考
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'. Ifyou don't set this field, and the usb controller needs one, it isresponsible for mapping and unmapping the buffer.
no_interrupt
If true, hints that no completion irq isneeded. Helpful sometimes with deep request queues that are handleddirectly 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 ashort 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) stopthe queue from advancing until the completion function returns, so thatany 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 anegative errno. Normally, faults block the transfer queue from advancinguntil the completion callback returns. Code “-ESHUTDOWN”indicates completion caused by device disconnect, or when the driverdisabled the endpoint.
actual
Reports bytes transferred to/from thebuffer. For reads (OUT transfers) this may be less than the requestedlength. If the short_not_ok flag is set, short reads are treatedas errors even when status otherwise indicates successful completion.Note that for writes (IN transfers) some data bytes may stillreside in a device-side FIFO when the request is reported as complete.
Description
These are allocated/freed through the endpointthey're used with. The hardware's driver can add extra per-request datato the memory it returns,whichoften avoids separate memory allocations(potential failures), later when the request is queued.
Request flags affect request handling, such aswhether a zero length packet is written (the “zero” flag), whether ashort read should be treated as anerror (blocking request queueadvance, the “short_not_ok” flag), or hinting that an interrupt is notrequired (the “no_interrupt” flag, for use with deeprequest queues).
Bulk endpoints can use any size buffers, andcan also be used for interrupt transfers. interrupt-only endpoints canbe much less functional.
2、為USB gadget功能驅(qū)動(dòng)提供的注冊(cè)、注銷函數(shù)
EXPORT_SYMBOL(usb_gadget_unregister_driver); //注銷一個(gè)USB gadget功能驅(qū)動(dòng)
EXPORT_SYMBOL(usb_gadget_register_driver);//注冊(cè)一個(gè)USB gadget功能驅(qū)動(dòng)
二、USB gadget功能驅(qū)動(dòng)
如果內(nèi)核已經(jīng)支持了SOC的UDC驅(qū)動(dòng),很多時(shí)候,我們可以只關(guān)心這部分代碼的編寫。那么我們?nèi)绾尉帉懗鲆粋€(gè)類似usb 功能驅(qū)動(dòng)呢?
usb 功能驅(qū)動(dòng)應(yīng)該至少要實(shí)現(xiàn)如下功能:
. 實(shí)現(xiàn)USB協(xié)議中端點(diǎn)0部分和具體功能相關(guān)的部分(UDC驅(qū)動(dòng)無法幫我們完成的部分)。如:USB_REQ_GET_DESCRIPTOR、USB_REQ_GET_CONFIGURATION等;
完成了這個(gè)功能以后,USB主機(jī)端系統(tǒng)就會(huì)設(shè)別出我們是一個(gè)什么樣的設(shè)備。
. 實(shí)現(xiàn)數(shù)據(jù)交互功能
即如何實(shí)現(xiàn)向硬件控制器的端點(diǎn)發(fā)出讀、寫請(qǐng)求來完成數(shù)據(jù)交互;
. 具體功能的實(shí)現(xiàn)如:如何實(shí)現(xiàn)一個(gè)usb net驅(qū)動(dòng),或是一個(gè)usb storage驅(qū)動(dòng)。
接下來以zero.c為例,說明這3個(gè)方面是如何實(shí)現(xiàn)的。
1、zero設(shè)備介紹
作為一個(gè)簡(jiǎn)單的 gadget 驅(qū)動(dòng),zero 的功能基于兩個(gè) BULK 端點(diǎn)實(shí)現(xiàn)了簡(jiǎn)單的輸入輸出功能, 它可以用作寫新的 gadget 驅(qū)動(dòng)的一個(gè)實(shí)例。
兩個(gè) BULK 端點(diǎn)為一個(gè) IN 端點(diǎn), 一個(gè) OUT端點(diǎn)?;谶@兩個(gè)(由底層提供的)端點(diǎn),g_zero 驅(qū)動(dòng)實(shí)現(xiàn)了兩個(gè)configuration。 第一個(gè) configuration 提供了sink/source功能:兩個(gè)端點(diǎn)一個(gè)負(fù)責(zé)輸入,一個(gè)負(fù)責(zé)輸出,其中輸出的內(nèi)容根據(jù)設(shè)置可以是全0,也可以是按照某種算法生成的數(shù)據(jù)。另一個(gè)configuration 提供了 loopback 接口, IN 端點(diǎn)負(fù)責(zé)把從 OUT 端點(diǎn)收到的數(shù)據(jù)反饋給 Host.
2、zero設(shè)備注冊(cè)、注銷
static int __init init(void)
{
return usb_gadget_register_driver(&zero_driver);
}
module_init(init);
static struct usb_gadget_driver zero_driver = {
#ifdef CONFIG_USB_GADGET_DUALSPEE
.speed = USB_SPEED_HIGH,
#else
.speed = USB_SPEED_FULL,
#endif
.function = (char *) longname,
.bind = zero_bind,
.unbind = __exit_p(zero_unbind),
.setup = zero_setup,
.disconnect = zero_disconnect,
.suspend = zero_suspend,
.resume = zero_resume,
.driver = {
.name = (char *) shortname,
.owner = THIS_MODULE,
},
};
構(gòu)建一個(gè)usb_gadget_driver,調(diào)用usb_gadget_register_driver注冊(cè)函數(shù)即可注冊(cè)一個(gè)usbgadget驅(qū)動(dòng)。需要注意的是,目前S3C2410主機(jī)控制器只能注冊(cè)一個(gè)gadget功能驅(qū)動(dòng)。這主要是由協(xié)議決定的。參考s3c2410_udc.c中的這段代碼
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{……
if (udc->driver)//如果已經(jīng)注冊(cè)過了
return -EBUSY;
……
}
3、usb_gadget_driver結(jié)構(gòu)
事實(shí)上我們的工作就是構(gòu)建這個(gè)usb_gadget_driver結(jié)構(gòu)。那么這個(gè)結(jié)構(gòu)這樣和我們上面要實(shí)現(xiàn)的3個(gè)目標(biāo)聯(lián)系起來呢。
. Setup (zero_setup)
處理host端發(fā)來的request,如:處理host端發(fā)來的get_descriptor請(qǐng)求。 在這實(shí)現(xiàn)了前面提到的必須要實(shí)現(xiàn)的第一個(gè)功能。
. bind (zero_bind)
綁定dev與driver,在gadget driver,注冊(cè)驅(qū)動(dòng)時(shí)被usb_gadget_register_driver調(diào)用,綁定之后driver才能處理setup請(qǐng)求
另外,通過usb_ep_autoconfig函數(shù),可以分配到名為EP_IN_NAME、EP_OUT_NAME兩個(gè)端點(diǎn)。后面可以對(duì)兩個(gè)端點(diǎn)發(fā)起數(shù)據(jù)傳輸請(qǐng)求,和USB 主機(jī)端的urb請(qǐng)求非常相似,大家可以和urb對(duì)照一些。
發(fā)起數(shù)據(jù)請(qǐng)求大致有以下幾步:
struct usb_request *req;
req = alloc_ep_req(ep, buflen);//分配請(qǐng)求,數(shù)據(jù)傳輸?shù)姆较蛴蒭p本身決定
req->complete = source_sink_complete; //請(qǐng)求完成后的處理函數(shù)
status = usb_ep_queue(ep, req, GFP_ATOMIC);//遞交請(qǐng)求
free_ep_req(ep, req);//釋放請(qǐng)求,通常在請(qǐng)求處理函數(shù)complete中調(diào)用
. 通常在bind和unbind函數(shù)中注冊(cè)具體的功能驅(qū)動(dòng)
如果為了實(shí)現(xiàn)某個(gè)特定功能需要在設(shè)備端注冊(cè)字符、塊、網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)的話,選擇的場(chǎng)
合通常是bind中注冊(cè),unbind中卸載。如ether.c文件中:
static int __init
eth_bind (struct usb_gadget *gadget)
{
……
status = register_netdev (dev->net); //注冊(cè)網(wǎng)卡驅(qū)動(dòng)
……
}
static void /* __init_or_exit */
eth_unbind (struct usb_gadget *gadget)
{
……
unregister_netdev (dev->net); //注銷網(wǎng)卡驅(qū)動(dòng)
……
}
這也讓我們對(duì)在設(shè)備端實(shí)現(xiàn)一個(gè)字符、塊、網(wǎng)絡(luò)驅(qū)動(dòng)的結(jié)構(gòu)有了一些了解。
總結(jié)
本文對(duì)gadget的驅(qū)動(dòng)結(jié)構(gòu)做了簡(jiǎn)要的介紹。下一篇將介紹如何編寫一個(gè)簡(jiǎn)單的gadget驅(qū)動(dòng)及應(yīng)用測(cè)試程序。