基于s3c6410 otg controller的gadget driver及usb枚举分 析
一.简介
一个完整的USB系统由两部分构成,即usb主机(usb host)和usb设备(usb device)。usb主机通常是指我们的pc机、具有host controller的嵌入式设备;像u盘、usb鼠标、键盘属于usb设备,具有otg controller的usb设备,它即可工作在host模式又可以工作在device模式,模式之间通过HNP协议来进行转换,如我们平时比较常用的智能手机,当它连接到电脑上时,工作在device模式,电脑可以像u盘一样对手机里的存储空间进行操作,而当把SD卡或其它外设接到手机上时,手机就可以对SD卡等外设进行操作,这个时候它就工作在host模式。
由于USB系统由两部组成,所以usb驱动也由相应两部分组成。主机侧驱动主要由应用层驱动,用于管理hub和hcd的usb core层驱动,主机控制器驱动这三部分组成,应用层驱动主要是通过usb的基本数据通信结构urb对usb设备进行操作,这类驱动是针对某一个usb接口写的,所以也叫作usb 接口驱动,通常在主机端驱动人员要写的就是这类驱动。usb core驱动里包含一个守护进程,通常进程是休眠的,当usb port上产生变化,usb root hub监控到其端口上有设备插入或拔出,它就会去唤醒守护进程,然后运行usb设备的枚举,进而运行后面操作。主机控制器驱动里包含两部分,一部分是和usb接口标准相关的驱动,一部分是和控制器本身相关的,其中和接口标准相关的驱动占主要部分。主机控制器接口标准分三类:ohci,uhci和ehci,不同的生产产家有着不同的usb标准,一个usb控制器生产出来时就已经决定了其标准类型,像inter生产的通常是uhci标准的,而像本文中要用到的s3c6410是采用ohci标准的,不同标准的usb设备之间软件和硬件上分工不同,像uhci设备它在硬件逻辑上比较简单,而软件实现上比较复杂,而ohci在硬件实现上比较复杂,软件上比较简单。要完成一个usb控制器驱动无非是运用内核里各种usb 标准的API。
在usb设备侧,驱动由三层组成:Upper Layers,gadget drivers, device controller drivers, upper layers属于应用层,通过gadget drivers来使用和控制device controllers,gadget drivers使用gadget API,实现与具体硬件无关,device controller drivers提供gadget API,实现gadget ops和endpoint ops,具体的可参考<Linux-USB Gadget API Framework>.
不管是在主机侧还是设备侧,一个usb device它主要可由配置,接口,端口三部分表示,在主机侧它们分别由usb_host_config, usb_inferface,usb_host_endpoint表示,而设备侧由 usb_configuration,usb_function表示。配置,像我们现在的智能手机它可以用来拍照也可以用来当U盘,这两种就属于不同的配置,一个usb设备它可能包含多个配置,在使用某些功能前必需选择好相应的配置;一个配置由多个接口组成 ,一个接口表示一个功能,一个接口里可以包含多个接口设置,每个接口设置里包含与实现某一功能相关的端口,配置,接口和端口结构示意如图1所示。
图1 配置,接口和端口结构示意图
当一个usb设备插入到host端时,主机端的root hub通过轮训监控或中断方式来触发usb枚举,当usb设备通过枚举后,通过usb总线找到与其配置驱动,然后usb设备才能正常工作,这个枚举过程由主机和设备共同完成,对于没有运行操作系统的usb设备,生产产家已经把设备侧枚举所需过程固化在设备里,驱动工程师可不用去关心设备枚举,他们只需要完成usb 接口驱动程序。但对于有运行操作系统的usb设备就完全不一样了,驱动工程师不仅要实现usb设备功能驱动,还得实现与主机枚举过程相对应的驱动,如主机在对usb设备进行复位操作后,主机会发usb请求去设置usb设备的地址,相应的usb设备侧必须实现设备地址机制,并返回0长度usb请求,做为收到数据ACK,当主机要获取usb设备各种描述符或设置各种配置时,usb设备侧也要实现相应操作。本文主机为pc机,设备以samsung s3c6410 的otg controller为控制器,驱动为zero_driver,通过对zero_driver分析,尽量掌握gadget 驱动系统框架及设备侧枚举过程。
二. usb枚举过程分析
usb设备主要有6种状态:attached,powered,default,address,configured,suspend.当root hub监测到有设备插入时,将其设置为attached和powered态,然后复位hub port,将其设置成default,复位成功后设置usb设备地址,获取设备、配置描述符,最后选择并设置合适的usb配置。usb请求都是由usb主机发起的,usb设备只是被动响应主机的请求,不会主动向主机发送任何usb请求,以6410为控制器的设备枚举设备侧程序流程大致如图2,图3所示。
图2 以s3c6410为设备控制器的usb枚举过程设备侧程序流程1
图3 以s3c6410为设备控制器的usb枚举过程设备侧程序流程2
如图2,由于usb设备处理被动地位,不能主动去发送数据,所有的请求都是由主机发起,而usb设备在接收到来自主机的请求后,产生接收数据中断,在中断处理程序中,通过不同的请求类型响应不同操作。
三.程序分析
在响应host端的枚举过程前,device侧要先把device controller和gadget driver注册到系统中,否则device侧不会对host请求做出任务反映,这点在<usb gadget api for linux>里的3.1 driver life cycle讲得比较清楚,所以在讲枚举过程前要先分析device controller和gadget driver注册到系统过程,下面几个数据结构是device侧经常用到,在这里简单介绍一下:
struct usb_gadget_driver:用来存放gadget驱动;struct usb_request:用来存放usb device侧usb请求数据;struct usb_gadget:用来表示一个usb设备侧设备;struct usb_gadget_ops:gadget API函数接口,和设备控制器相关;struct usb_ep_ops:用于操作usb设备endpoint函数接口,和设备控制器相关。struct usb_composite_driver:复合型设备驱动;struct usb_composite_dev:复合型设备;
1. usb device controller注册
s3c6410 otg controller driver通过平台注册方式注册,当平台驱动和平台设备通过platform_match时,就会调用s3c_hsotg_probe.
static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
{
struct s3c_hsotg_plat *plat = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
struct s3c_hsotg *hsotg;
struct resource *res;
int epnum;
int ret;
if (!plat)
plat = &s3c_hsotg_default_pdata;
hsotg = kzalloc(sizeof(struct s3c_hsotg) +
sizeof(struct s3c_hsotg_ep) * S3C_HSOTG_EPS,
GFP_KERNEL);
if (!hsotg) {
dev_err(dev, "cannot get memoryn");
return -ENOMEM;
}
hsotg->dev = dev;
hsotg->plat = plat;
hsotg->clk = clk_get(&pdev->dev, "otg");
if (IS_ERR(hsotg->clk)) {
dev_err(dev, "cannot get otg clockn");
ret = PTR_ERR(hsotg->clk);
goto err_mem;
}
platform_set_drvdata(pdev, hsotg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "cannot find register resource 0n");
ret = -EINVAL;
goto err_clk;
}
hsotg->regs_res = request_mem_region(res->start, resource_size(res),
dev_name(dev));
if (!hsotg->regs_res) {
dev_err(dev, "cannot reserve registersn");
ret = -ENOENT;
goto err_clk;
}
hsotg->regs = ioremap(res->start, resource_size(res));
if (!hsotg->regs) {
dev_err(dev, "cannot map registersn");
ret = -ENXIO;
goto err_regs_res;
}
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(dev, "cannot find IRQn");
goto err_regs;
}
hsotg->irq = ret;
ret = request_irq(ret, s3c_hsotg_irq, 0, dev_name(dev), hsotg);
if (ret < 0) {
dev_err(dev, "cannot claim IRQn");
goto err_regs;
}
dev_info(dev, "regs %p, irq %dn", hsotg->regs, hsotg->irq);
device_initialize(&hsotg->gadget.dev);
dev_set_name(&hsotg->gadget.dev, "gadget");
hsotg->gadget.is_dualspeed = 1;
hsotg->gadget.ops = &s3c_hsotg_gadget_ops;
hsotg->gadget.name = dev_name(dev);
hsotg->gadget.dev.parent = dev;
hsotg->gadget.dev.dma_mask = dev->dma_mask;
/* setup endpoint information */
INIT_LIST_HEAD(&hsotg->gadget.ep_list);
hsotg->gadget.ep0 = &hsotg->eps[0].ep;
/* allocate EP0 request */
hsotg->ctrl_req = s3c_hsotg_ep_alloc_request(&hsotg->eps[0].ep,
GFP_KERNEL);
if (!hsotg->ctrl_req) {
dev_err(dev, "failed to allocate ctrl reqn");
goto err_regs;
}
/* reset the system */
clk_enable(hsotg->clk);
s3c_hsotg_gate(pdev, true);
s3c_hsotg_otgreset(hsotg);
s3c_hsotg_corereset(hsotg);
s3c_hsotg_init(hsotg);
/* initialise the endpoints now the core has been initialised */
for (epnum = 0; epnum < S3C_HSOTG_EPS; epnum++)
s3c_hsotg_initep(hsotg, &hsotg->eps[epnum], epnum);
ret = usb_add_gadget_udc(&pdev->dev, &hsotg->gadget);
if (ret)
goto err_add_udc;
s3c_hsotg_create_debug(hsotg);
s3c_hsotg_dump(hsotg);
our_hsotg = hsotg;
return 0;
err_add_udc:
s3c_hsotg_gate(pdev, false);
clk_disable(hsotg->clk);
clk_put(hsotg->clk);
err_regs:
iounmap(hsotg->regs);
err_regs_res:
release_resource(hsotg->regs_res);
kfree(hsotg->regs_res);
err_clk:
clk_put(hsotg->clk);
err_mem:
kfree(hsotg);
return ret;
}
s3c_hsotg_probe主要分为3个部分内容:
第一部分由12-67行,主要用于申请内存、中断资源,并映射内存、申请中断。
第二部分69-108行,主要实现用gadget结构,各endpoint初始化,并为endpoint0申请用于接收host请求的usb request数据结构.
第三部分110行,通过usb_add_gadget_udc把gadget设备注册到系统中,系统中有一个udc_list,专门用于管理通过usb_add_gadget_udc注册到系统的struct usb_udc.
2.usb composite driver和gadget driver注册
根据usb协议5.3.2节定义"A device that has multiple interfaces controlled independently of each other is referred to as a composite device",如果一个usb设备由多个相互独立控制接口构成,则做复合型usb设备,具体可参考<>这里有对composite device进行比较详细说明。由于复合型设备由各个独立的接口组成,一个接口对应一个功能驱动,对于一个新的复合型设备,就没必要开发一个完整驱动,完全可以直接使用现有的接口驱动,所以复合型设备有利于usb设备驱动开发。
目前,内核中提供了usb_composite_driver和usb_composite_dev用表征一个复合型驱动和设备,除此之外,内核还提供了像usb_add_config,usb_add_function等接口用来添加一个设备的配置和功能接口。
本文将以内核中比较简单的zero_driver来对composite框架进行分析,zero是用来测试主机控制器驱动的设备侧驱动,主机端一般用usb_test,usb_skeleton模块,zero模块包含两种配置,一个是回环传输,它有两个端口(endpoint),一个是in,一个是out,in用来接收host端数据,out用来将host收到的数据转发给host;另一个配置是source/sink传输,它也有两个端口sink和source,sink是用来接收来自 host端口数据,而source则用来发送数据给host,发数的数据要么全0或由算法得到。
static struct usb_composite_driver zero_driver = {
.name = "zero",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER,
.unbind = zero_unbind,
.suspend = zero_suspend,
.resume = zero_resume,
};
zero_driver中dev定义了初始的usb设备描述符,之后会在zero_bind中补全其它参量,在usb枚举的过程中会将这个完整的设备描述符发送回给host。
strings定义了struct usb_gadget_string结构数组,里而包含了生产产家,产品编号及序列号等信息。
composite设备驱动由usb_composite_probe注册,usb_composite_unregister接口卸载。
static int __init init(void)
{
return usb_composite_probe(&zero_driver, zero_bind);
}
module_init(init);
static void __exit cleanup(void)
{
usb_composite_unregister(&zero_driver);
}
module_exit(cleanup);
usb_composite_probe接口定义如下,它有两个型参,一个是usb_composite_driver,一个是 composite驱动的回调函数,回调函数工作主要有:通过usb_add_config来添加usb配置,补全设备描述符,获取设备ID,具体后面用到时再分析,接下来先看usb_composite_probe函数。
int usb_composite_probe(struct usb_composite_driver *driver,
int (*bind)(struct usb_composite_dev *cdev))
{
if (!driver || !driver->dev || !bind || composite)
return -EINVAL;
if (!driver->name)
driver->name = "composite";
if (!driver->iProduct)
driver->iProduct = driver->name;
composite_driver.function = (char *) driver->name;
composite_driver.driver.name = driver->name;
composite_driver.speed = min((u8)composite_driver.speed,
(u8)driver->max_speed);
composite = driver;
composite_gadget_bind = bind;
return usb_gadget_probe_driver(&composite_driver, composite_bind);
}
usb_composite_probe用来将usb_composite_driver注册到composite driver框架中,这里通过一个composite变量用来保存composite driver,composite_gadget_bind保存bind,并通过composite driver中的些变量来补全一个usb_gadget_driver composite_driver.之前简介里有提到过,gadget设备侧程序主要由gadget driver和device controller driver组成 ,这里这个composite_driver就是device controller driver上面的那与硬件无关的那一层,composite_driver定义如下:
static struct usb_gadget_driver composite_driver = {
#ifdef CONFIG_USB_GADGET_SUPERSPEED
.speed = USB_SPEED_SUPER,
#else
.speed = USB_SPEED_HIGH,
#endif
.unbind = composite_unbind,
.setup = composite_setup,
.disconnect = composite_disconnect,
.suspend = composite_suspend,
.resume = composite_resume,
.driver = {
.owner = THIS_MODULE,
},
};
composite_driver中的setup函数最为重要,简介中曾经说过,device controller driver用来完成和硬件相关的设备或回应,如设置usb设备地址,ACK应答之类机制。但像获取设备描述符,设置配置,设置接口等机制并没有实现,这些请求的处理机制就是在这个setup里完成的。
usb_gadget_driver由usb_gadget_probe_driver和usb_gadget_unregister_dirver完成。usb_gadget_probe_driver除了包含gadget driver外,还包括了gadget驱动中的回调函数composite_bind.
int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct usb_udc *udc = NULL;
int ret;
if (!driver || !bind || !driver->setup)
return -EINVAL;
mutex_lock(&udc_lock);
list_for_each_entry(udc, &udc_list, list) {
/* For now we take the first one */
if (!udc->driver)
goto found;
}
pr_debug("couldn't find an available UDCn");
mutex_unlock(&udc_lock);
return -ENODEV;
found:
dev_dbg(&udc->dev, "registering UDC driver [%s]n",
driver->function);
udc->driver = driver;
udc->dev.driver = &driver->driver;
if (udc_is_newstyle(udc)) {
ret = bind(udc->gadget);
if (ret)
goto err1;
ret = usb_gadget_udc_start(udc->gadget, driver);
if (ret) {
driver->unbind(udc->gadget);
goto err1;
}
usb_gadget_connect(udc->gadget);
} else {
ret = usb_gadget_start(udc->gadget, driver, bind);
if (ret)
goto err1;
}
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
mutex_unlock(&udc_lock);
return 0;
err1:
dev_err(&udc->dev, "failed to start %s: %dn",
udc->driver->function, ret);
udc->driver = NULL;
udc->dev.driver = NULL;
mutex_unlock(&udc_lock);
return ret;
}
这前在讲device controller 注册时讲过,通过usb_add_gadget_udc注册device controller时,linux系统是通过一个struct usb_udc结构的udc_list列表中进行管理设备控制器的,struct usb_udc结构如下:
struct usb_udc {
struct usb_gadget_driver *driver;
struct usb_gadget *gadget;
struct device dev;
struct list_head list;
};
driver用于表示一个udc驱动,而 list就是用来加入到udc_list列表的入口变量。
在
usb_gadget_probe_driver中的11-15行,对udc_list 列表进行遍历,查看是否有udc控制器没有添加相对应的驱动,如果找不到说明系统中没有注册udc或每个udc都有相应的驱动。
如果udc列表中有某个udc没有驱动,则将usb_gadgett_driver和相应udc关联在一起。
28-44行通过判断usb device controller实现模式为选择运行程序,由于版本兼容原因,操作控制器硬件的ops里包含了新的和旧的操作接口。
int (*udc_start)(struct usb_gadget *, struct usb_gadget_driver *);
int (*udc_stop)(struct usb_gadget *, struct usb_gadget_driver *);
/* Those two are deprecated */
int (*start)(struct usb_gadget_driver *, int (*bind)(struct usb_gadget *));
int (*stop)(struct usb_gadget_driver *);
其中,udc_start 和udc_stop为新版本操作接口 ,而start 和stop老版本操作接口。
由于本文所采用的内核是老版本的接口,所以会运行下面usb_gadget_start函数。
usb_gadget_start函数将会以gadget_driver和composite_bind为参数调用usb device controller的ops中的start回调函数,在注册usb device controller时就定义了ops 的start回调函数为s3c_hsotg_start
如果usb_gadget_start运行正常则通过object_uevent来通知用户发生了KOBJ_CHANGE事件,用户空间中的udev程序就会采取相应操作。
s3c_hsotg_start函数里主要是涉及低层寄存器控制,在这里就不去深入研究了,composite_bind将会在s3c_hsotg_start中被调用。
static int composite_bind(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev;
int status = -ENOMEM;
cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
if (!cdev)
return status;
spin_lock_init(&cdev->lock);
cdev->gadget = gadget;
set_gadget_data(gadget, cdev);
INIT_LIST_HEAD(&cdev->configs);
/* preallocate control response and buffer */
cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
if (!cdev->req)
goto fail;
cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
if (!cdev->req->buf)
goto fail;
cdev->req->complete = composite_setup_complete;
gadget->ep0->driver_data = cdev;
cdev->bufsiz = USB_BUFSIZ;
cdev->driver = composite;
/*
* As per USB compliance update, a device that is actively drawing
* more than 100mA from USB must report itself as bus-powered in
* the GetStatus(DEVICE) call.
*/
if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW)
usb_gadget_set_selfpowered(gadget);
/* interface and string IDs start at zero via kzalloc.
* we force endpoints to start unassigned; few controller
* drivers will zero ep->driver_data.
*/
usb_ep_autoconfig_reset(cdev->gadget);
/* composite gadget needs to assign strings for whole device (like
* serial number), register function drivers, potentially update
* power state and consumption, etc
*/
status = composite_gadget_bind(cdev);
if (status < 0)
goto fail;
cdev->desc = *composite->dev;
/* standardized runtime overrides for device ID data */
if (idVendor)
cdev->desc.idVendor = cpu_to_le16(idVendor);
if (idProduct)
cdev->desc.idProduct = cpu_to_le16(idProduct);
if (bcdDevice)
cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
/* string overrides */
if (iManufacturer || !cdev->desc.iManufacturer) {
if (!iManufacturer && !composite->iManufacturer &&
!*composite_manufacturer)
snprintf(composite_manufacturer,
sizeof composite_manufacturer,
"%s %s with %s",
init_utsname()->sysname,
init_utsname()->release,
gadget->name);
cdev->manufacturer_override =
override_id(cdev, &cdev->desc.iManufacturer);
}
if (iProduct || (!cdev->desc.iProduct && composite->iProduct))
cdev->product_override =
override_id(cdev, &cdev->desc.iProduct);
if (iSerialNumber)
cdev->serial_override =
override_id(cdev, &cdev->desc.iSerialNumber);
/* has userspace failed to provide a serial number? */
if (composite->needs_serial && !cdev->desc.iSerialNumber)
WARNING(cdev, "userspace failed to provide iSerialNumbern");
/* finish up */
status = device_create_file(&gadget->dev, &dev_attr_suspended);
if (status)
goto fail;
INFO(cdev, "%s readyn", composite->name);
return 0;
fail:
composite_unbind(gadget);
return status;
}
在将gadget driver和usb device controller联系在一起后,composite_bind将会生成一个composite dev数据结构,并对其进行初始化,然后申请用于回复endpoint0的控制端口的usb request资源,最后调用composite回调函数zero_bind用来为composite usb device 添加配置。
第6-8行为composite dev结构申请空间。
第10-13行初始化composite dev中自旋锁,对向列表头等。
第16-23行,申请用于回复endpoint0控制口的usb请求资源,这个请求用来回复host端获取设备描述符,配置描述符等需要返回数据的usb请求,并为usb请求申请用于存放发送数据内存空间,为usb请求回调函数赋值,当usb请求被发送出去后就会调用回调函数来通知usb请求发出者.
第26行,把之前保存在composite里的composite_driver赋值给cdev->driver。
第46行,调用usb_composite_driver回调函数zero_bind。
第47行,把复合型设备描述符保存在cdev的desc里。
第52-57行,如果在加载模块时指定了生产产家ID,产口ID和设备版本号,则用指定的值来代替原来的值。
第60-80行,用于设置设备描述符中的iManufacturer,iProduct和iSerialNumber,如果没有设置这几个变量的index,则通过override_id来获取ID,本文已经在zero_driver中的zero_bind获取了。
第84-85行,如果设置在usb_composite_driver设置了need_serial项,则需要用户空间提供usb设备的serial id,如果没有提供则发出warn。
第88行,通过device_create_file生成一个用生查询usb设备是否suspend状态的设备属性函数。
如果假设zero_bind运行正常,还有下面创建设备文件也正常,则 一个gadget设备准备工作都已经完成 ,gadget设备可以与host设备进行通信 。
在讲具体的枚举过程之前,先完成对zero_bind函数里面分析。
static int __init zero_bind(struct usb_composite_dev *cdev)
{
int gcnum;
struct usb_gadget *gadget = cdev->gadget;
int id;
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
id = usb_string_id(cdev);
if (id < 0)
return id;
strings_dev[STRING_MANUFACTURER_IDX].id = id;
device_desc.iManufacturer = id;
id = usb_string_id(cdev);
if (id < 0)
return id;
strings_dev[STRING_PRODUCT_IDX].id = id;
device_desc.iProduct = id;
id = usb_string_id(cdev);
if (id < 0)
return id;
strings_dev[STRING_SERIAL_IDX].id = id;
device_desc.iSerialNumber = id;
setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev);
/* Register primary, then secondary configuration. Note that
* SH3 only allows one config...
*/
if (loopdefault) {
loopback_add(cdev, autoresume != 0);
sourcesink_add(cdev, autoresume != 0);
} else {
sourcesink_add(cdev, autoresume != 0);
loopback_add(cdev, autoresume != 0);
}
gcnum = usb_gadget_controller_number(gadget);
if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
else {
/* gadget zero is so simple (for now, no altsettings) that
* it SHOULD NOT have problems with bulk-capable hardware.
* so just warn about unrcognized controllers -- don't panic.
*
* things like configuration and altsetting numbering
* can need hardware-specific attention though.
*/
pr_warning("%s: controller '%s' not recognizedn",
longname, gadget->name);
device_desc.bcdDevice = cpu_to_le16(0x9999);
}
INFO(cdev, "%s, version: " DRIVER_VERSION "n", longname);
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
init_utsname()->sysname, init_utsname()->release,
gadget->name);
return 0;
}
zero_bind主要用于设置设备描述符中bcdDevice,iManufacturer,iProduct及iSerialNumber,然后通过loopback_add和sourcesink_add添加usb配置。
10-14行,通过usb_string_id来为生产产家获取一个 ID,在usb_composite_dev中的next_string_id专门用来管理string ID,只要next_string_id小于254就可以合法获取string ID,获取到stringID后把它保存到usb设备描述符里的iManufacturer及复合设备结构中的strings里。
16-20行,用于获取产品ID,并存入设备描述符和复合设备结构中的strings里。
22-26行,用于获取设备系列号,并存入相应结构中。
28行,设备一个timer_list定时器,用于设备suspend和resume这里不深入分析。
34-39行通过loopback_add和sourcesink_add添加usb配置,前面有提到过,loopback配置用来将从host 接收到的数据发送给host,而sourcesink配置则是在接收到host数据后,向host发送全0或算法生成的数据,两者只是在发送回给host的数据存在差别,这里只对其中的loopback进行分析, sourcesind_add就不深入研究了。
41-55行根据device controller类型来获取设备版本号,如果device controller没有匹配项,则将设备版本号设置成0X9999.
int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume)
{
int id;
/* allocate string ID(s) */
id = usb_string_id(cdev);
if (id < 0)
return id;
strings_loopback[0].id = id;
loopback_intf.iInterface = id;
loopback_driver.iConfiguration = id;
/* support autoresume for remote wakeup testing */
if (autoresume)
sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
/* support OTG systems */
if (gadget_is_otg(cdev->gadget)) {
loopback_driver.descriptors = otg_desc;
loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
return usb_add_config(cdev, &loopback_driver, loopback_bind_config);
}
loopback_add主要是完成loopback这个配置描述符里的配置ID设置,并通过usb_add_config向usb设备添加loopback_driver这个配置。
6-12行用于获取配置的ID,并保存到配置描述符中。
15-16行,如果usb设备这个配置支持唤醒功能,则在usb_configuration时的bmAttributes里wakeup置位。
19-22行,如果支持otg功能,则还需要添加otg 描述符。
24行通过usb_add_config添加loopback_driver配置,loopback_driver定义如下:
static struct usb_configuration loopback_driver = {
.label = "loopback",
.strings = loopback_strings,
.bConfigurationValue = 2,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
/* .iConfiguration = DYNAMIC */
};
label为配置名字,strings里用于申明配置功能的string,bConfigurationValue用来表示当前配置值,在USB枚举时就能通过选择合适的配置值来设备usb设备的配置,bmAttributes用来表明配置的特性,如自供电,远程唤醒等。
用usb_add_config添加配置时,需要三个参数,usb_composite_dev结构cdev, usb配置loopback_driver和配置的回调函数loopback_bind_config.
int usb_add_config(struct usb_composite_dev *cdev,
struct usb_configuration *config,
int (*bind)(struct usb_configuration *))
{
int status = -EINVAL;
struct usb_configuration *c;
DBG(cdev, "adding config #%u '%s'/%pn",
config->bConfigurationValue,
config->label, config);
if (!config->bConfigurationValue || !bind)
goto done;
/* Prevent duplicate configuration identifiers */
list_for_each_entry(c, &cdev->configs, list) {
if (c->bConfigurationValue == config->bConfigurationValue) {
status = -EBUSY;
goto done;
}
}
config->cdev = cdev;
list_add_tail(&config->list, &cdev->configs);
INIT_LIST_HEAD(&config->functions);
config->next_interface_id = 0;
status = bind(config);
if (status < 0) {
list_del(&config->list);
config->cdev = NULL;
} else {
unsigned i;
DBG(cdev, "cfg %d/%p speeds:%s%s%sn",
config->bConfigurationValue, config,
config->superspeed ? " super" : "",
config->highspeed ? " high" : "",
config->fullspeed
? (gadget_is_dualspeed(cdev->gadget)
? " full"
: " full/low")
: "");
for (i = 0; i < MAX_CONFIG_INTERFACES; i++) {
struct usb_function *f = config->interface[i];
if (!f)
continue;
DBG(cdev, " interface %d = %s/%pn",
i, f->name, f);
}
}
/* set_alt(), or next bind(), sets up
* ep->driver_data as needed.
*/
usb_ep_autoconfig_reset(cdev->gadget);
done:
if (status)
DBG(cdev, "added config '%s'/%u --> %dn", config->label,
config->bConfigurationValue, status);
return status;
}
usb_add_config用来向复合型usb设备添加一个usb配置,并对配置进行初始化,最后调用配置的回调函数loopback_bind_config向当前配置添加usb接口。
在配置描述符中的bConfigurationValue用来表示配置值,一个复合型设备有可能包含多个配置,配置之间通过这个bConfigurationValue来区别。第12-21行,在添加配置前会先判断配置值和回调函数是否已经设置,然后再通过遍历复合设备中配置列表来判断当前配置是否已经添加到设备,如果已经添加就会返回busy错误,以防止向设备添加同一配置值的配置。
23-24行,配置和复合设备进行相互绑定,复合设备结构中有专门用于管理设备配置的双向列表,把当前配置加入到双向列表中。
25-27行,对配置结构中function列表头和用于管理当前配置接口ID变量进行初始化。
29行,调用配置回调函数loopback_bind_config,向配置中添加usb接口(usb_function,在usb中,一个接口对应一个功能)。
static int __init loopback_bind_config(struct usb_configuration *c)
{
struct f_loopback *loop;
int status;
loop = kzalloc(sizeof *loop, GFP_KERNEL);
if (!loop)
return -ENOMEM;
loop->function.name = "loopback";
loop->function.descriptors = fs_loopback_descs;
loop->function.bind = loopback_bind;
loop->function.unbind = loopback_unbind;
loop->function.set_alt = loopback_set_alt;
loop->function.disable = loopback_disable;
status = usb_add_function(c, &loop->function);
if (status)
kfree(loop);
return status;
}
loopback_bind_config用来向当前配置添加接口功能,它先申请了一个f_loopback数据结构,里面包含了usb的接口和端口,然后对接口一些函数接口进行初始化,最后调用usb_add_function把接口添加到配置中。在接口的操作函数接口中,bind和set_alt相对比较重要,在usb_add_function中会调用bind来完成某个接口中各端口(endpoint)的初始化,而在枚举过程中设置接口USB请求就是通过这个接口实现。
int usb_add_function(struct usb_configuration *config,
struct usb_function *function)
{
int value = -EINVAL;
DBG(config->cdev, "adding '%s'/%p to config '%s'/%pn",
function->name, function,
config->label, config);
if (!function->set_alt || !function->disable)
goto done;
function->config = config;
list_add_tail(&function->list, &config->functions);
/* REVISIT *require* function->bind? */
if (function->bind) {
value = function->bind(config, function);
if (value < 0) {
list_del(&function->list);
function->config = NULL;
}
} else
value = 0;
/* We allow configurations that don't work at both speeds.
* If we run into a lowspeed Linux system, treat it the same
* as full speed ... it's the function drivers that will need
* to avoid bulk and ISO transfers.
*/
if (!config->fullspeed && function->descriptors)
config->fullspeed = true;
if (!config->highspeed && function->hs_descriptors)
config->highspeed = true;
if (!config->superspeed && function->ss_descriptors)
config->superspeed = true;
done:
if (value)
DBG(config->cdev, "adding '%s'/%p --> %dn",
function->name, function, value);
return value;
}
usb_add_function主要用来绑定配置和接口,然后调用接口中bind函数。
13-14行,配置和接口相互绑定,将接口添加到配置里用于管理接口的双向列表中。
17-24行,哪里在接口操作函数中定义了bind函数,则调用bind函数,如调用失败则将绑定的配置和接口分开。
31-36行,根据接口中定义的各种速度的接口和字符串标识来确定当前配置所支持速度。
static int __init
loopback_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_loopback *loop = func_to_loop(f);
int id;
/* allocate interface ID(s) */
id = usb_interface_id(c, f);
if (id < 0)
return id;
loopback_intf.bInterfaceNumber = id;
/* allocate endpoints */
loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
if (!loop->in_ep) {
autoconf_fail:
ERROR(cdev, "%s: can't autoconfigure on %sn",
f->name, cdev->gadget->name);
return -ENODEV;
}
loop->in_ep->driver_data = cdev; /* claim */
loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
if (!loop->out_ep)
goto autoconf_fail;
loop->out_ep->driver_data = cdev; /* claim */
/* support high speed hardware */
if (gadget_is_dualspeed(c->cdev->gadget)) {
hs_loop_source_desc.bEndpointAddress =
fs_loop_source_desc.bEndpointAddress;
hs_loop_sink_desc.bEndpointAddress =
fs_loop_sink_desc.bEndpointAddress;
f->hs_descriptors = hs_loopback_descs;
}
/* support super speed hardware */
if (gadget_is_superspeed(c->cdev->gadget)) {
ss_loop_source_desc.bEndpointAddress =
fs_loop_source_desc.bEndpointAddress;
ss_loop_sink_desc.bEndpointAddress =
fs_loop_sink_desc.bEndpointAddress;
f->ss_descriptors = ss_loopback_descs;
}
DBG(cdev, "%s speed %s: IN/%s, OUT/%sn",
(gadget_is_superspeed(c->cdev->gadget) ? "super" :
(gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
f->name, loop->in_ep->name, loop->out_ep->name);
return 0;
}
loopback_bind主要用于配置接口中的端口。
一个usb配置有可能包含多个接口,可能用接口号来表示不同的接口,前面讲过配置中有一个专门用来管理接口号的变量next_interface_id,9-12行,用于获取一个接口号并把它存入到接口描述符中。
第16-23和25-28行,分别 调用usb_ep_autoconfig函数,从gadget的ep_list找到in和out端口;
第16-23和25-28行,分别 调用usb_ep_autoconfig函数,从gadget的ep_list找到in和out端口;
在usb接口数据结构中有三个指针数组descriptors,hs_descriptors, ss_descriptors,它们分别用来存放三种不同速度的接口和端口描述符指针头,descriptors用来存放全速接口和端口描述符指针头,而hs_descriptors用来保存高速指针头,ss_descriptors则用来保存超速指针头。
第31-37行,判断该usb设备是否支持高速传输,如果支持将高速端口描述符中的bEndpointAddress设置成和全速一样,bEndpointAddress用来描述端口传输方向和端口号,这个和传输速度无关,所以都设置成一样的。
第40-46行,判断该usb设备是否支持超高速传输,如果支持超高速端口描述符中的bEndpointAddress设置成和全速一样。
struct usb_ep *usb_ep_autoconfig(
struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc
)
{
return usb_ep_autoconfig_ss(gadget, desc, NULL);
}
usb_ep_autoconfig函数是从gadget里的ep_list找到与所给端口描述符相匹配的端口,函数里面实际上是调用usb_ep_autoconfig_ss来实现端口匹配的。
struct usb_ep *usb_ep_autoconfig_ss(
struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc,
struct usb_ss_ep_comp_descriptor *ep_comp
)
{
struct usb_ep *ep;
u8 type;
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
/* First, apply chip-specific "best usage" knowledge.
* This might make a good usb_gadget_ops hook ...
*/
if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
/* ep-e, ep-f are PIO with only 64 byte fifos */
ep = find_ep (gadget, "ep-e");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
return ep;
ep = find_ep (gadget, "ep-f");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
return ep;
} else if (gadget_is_goku (gadget)) {
if (USB_ENDPOINT_XFER_INT == type) {
/* single buffering is enough */
ep = find_ep(gadget, "ep3-bulk");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
return ep;
} else if (USB_ENDPOINT_XFER_BULK == type
&& (USB_DIR_IN & desc->bEndpointAddress)) {
/* DMA may be available */
ep = find_ep(gadget, "ep2-bulk");
if (ep && ep_matches(gadget, ep, desc,
ep_comp))
return ep;
}
#ifdef CONFIG_BLACKFIN
} else if (gadget_is_musbhdrc(gadget)) {
if ((USB_ENDPOINT_XFER_BULK == type) ||
(USB_ENDPOINT_XFER_ISOC == type)) {
if (USB_DIR_IN & desc->bEndpointAddress)
ep = find_ep (gadget, "ep5in");
else
ep = find_ep (gadget, "ep6out");
} else if (USB_ENDPOINT_XFER_INT == type) {
if (USB_DIR_IN & desc->bEndpointAddress)
ep = find_ep(gadget, "ep1in");
else
ep = find_ep(gadget, "ep2out");
} else
ep = NULL;
if (ep && ep_matches(gadget, ep, desc, ep_comp))
return ep;
#endif
}
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
if (ep_matches(gadget, ep, desc, ep_comp))
return ep;
}
/* Fail */
return NULL;
}
usb_ep_autoconfig_ss函数有三个形参,分别是表示usb设备侧设备gadget,端口描述符desc和专门用于描述超高速端口特性(usb 3.0里才有)的ep_comp,它首先通过匹配设备控制器芯片和端口传输类型来选择端口,如没有找到则在gadget的ep_list列表中寻找ep,并通过ep_matches匹配,如果找到相匹配的endpoint就返回该端口,否则返回null。
static int
ep_matches (
struct usb_gadget *gadget,
struct usb_ep *ep,
struct usb_endpoint_descriptor *desc,
struct usb_ss_ep_comp_descriptor *ep_comp
)
{
u8 type;
const char *tmp;
u16 max;
int num_req_streams = 0;
/* endpoint already claimed? */
if (NULL != ep->driver_data)
return 0;
/* only support ep0 for portable CONTROL traffic */
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
if (USB_ENDPOINT_XFER_CONTROL == type)
return 0;
/* some other naming convention */
if ('e' != ep->name[0])
return 0;
/* type-restriction: "-iso", "-bulk", or "-int".
* direction-restriction: "in", "out".
*/
if ('-' != ep->name[2]) {
tmp = strrchr (ep->name, '-');
if (tmp) {
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* bulk endpoints handle interrupt transfers,
* except the toggle-quirky iso-synch kind
*/
if ('s' == tmp[2]) // == "-iso"
return 0;
/* for now, avoid PXA "interrupt-in";
* it's documented as never using DATA1.
*/
if (gadget_is_pxa (gadget)
&& 'i' == tmp [1])
return 0;
break;
case USB_ENDPOINT_XFER_BULK:
if ('b' != tmp[1]) // != "-bulk"
return 0;
break;
case USB_ENDPOINT_XFER_ISOC:
if ('s' != tmp[2]) // != "-iso"
return 0;
}
} else {
tmp = ep->name + strlen (ep->name);
}
/* direction-restriction: "..in-..", "out-.." */
tmp--;
if (!isdigit (*tmp)) {
if (desc->bEndpointAddress & USB_DIR_IN) {
if ('n' != *tmp)
return 0;
} else {
if ('t' != *tmp)
return 0;
}
}
}
/*
* Get the number of required streams from the EP companion
* descriptor and see if the EP matches it
*/
if (usb_endpoint_xfer_bulk(desc)) {
if (ep_comp) {
num_req_streams = ep_comp->bmAttributes & 0x1f;
if (num_req_streams > ep->max_streams)
return 0;
/* Update the ep_comp descriptor if needed */
if (num_req_streams != ep->max_streams)
ep_comp->bmAttributes = ep->max_streams;
}
}
/*
* If the protocol driver hasn't yet decided on wMaxPacketSize
* and wants to know the maximum possible, provide the info.
*/
if (desc->wMaxPacketSize == 0)
desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket);
/* endpoint maxpacket size is an input parameter, except for bulk
* where it's an output parameter representing the full speed limit.
* the usb spec fixes high speed bulk maxpacket at 512 bytes.
*/
max = 0x7ff & le16_to_cpu(desc->wMaxPacketSize);
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* INT: limit 64 bytes full speed, 1024 high/super speed */
if (!gadget->is_dualspeed && max > 64)
return 0;
/* FALLTHROUGH */
case USB_ENDPOINT_XFER_ISOC:
/* ISO: limit 1023 bytes full speed, 1024 high/super speed */
if (ep->maxpacket < max)
return 0;
if (!gadget->is_dualspeed && max > 1023)
return 0;
/* BOTH: "high bandwidth" works only at high speed */
if ((desc->wMaxPacketSize & cpu_to_le16(3<<11))) {
if (!gadget->is_dualspeed)
return 0;
/* configure your hardware with enough buffering!! */
}
break;
}
/* MATCH!! */
/* report address */
desc->bEndpointAddress &= USB_DIR_IN;
if (isdigit (ep->name [2])) {
u8 num = simple_strtoul (&ep->name [2], NULL, 10);
desc->bEndpointAddress |= num;
#ifdef MANY_ENDPOINTS
} else if (desc->bEndpointAddress & USB_DIR_IN) {
if (++in_epnum > 15)
return 0;
desc->bEndpointAddress = USB_DIR_IN | in_epnum;
#endif
} else {
if (++epnum > 15)
return 0;
desc->bEndpointAddress |= epnum;
}
/* report (variable) full speed bulk maxpacket */
if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) {
int size = ep->maxpacket;
/* min() doesn't work on bitfields with gcc-3.5 */
if (size > 64)
size = 64;
desc->wMaxPacketSize = cpu_to_le16(size);
}
ep->address = desc->bEndpointAddress;
return 1;
}
ep_matches通过端口描述符的一些特性来和gadget ep_list进行比较,device controller在初始化endpoint时,会将endpoint的名字设置成好几种类型:
1.ep1, ep2, ... 地址确定,方向和类型不确定;
2. ep1in, ep2out, ...地址和方向确定,类型不确定;
3. ep1-bulk, ep2-bulk, ... 地址和类型确定,方向不确定;
4. ep1in-bulk, ep2out-iso, ... 地址,方向和类型都确定;
5. ep-* ... 没有任何限制;
ep_matches首先通过比较端口传输地址,方向和类型,然后再比较速度等信息来匹配,如果不匹配则返回0,否则如果条件都满足则返回1。
31-58行,首先根据endpoint的name和端口描述符所指定的传输类型进行比较,对于中断传输如'-'后面第二个字符是s,即表示iso,那肯定不匹配,直接返回,对于批量传输如果‘-’后面第一个字符不是b,则不匹配,对于等时传输,‘-’后第二个字符应该是s.