此处将以zero.c为例进行讲解。

第一次接触zero.c驱动的时候,是因为某项目需要,提供一种usb字符设备,希望能够通过字符设备打开,读取和发送文件。当时能想到的就是zero.c文件,本打算按照zero驱动的编写方式,自己编写一个字符驱动,但后来为了节省工作时间,直接修改zero驱动,增加了字符设备的注册和操作函数。

zero.c驱动提供了丰富的功能接口,既包含自收自发的loopback(回环功能),又包含了主从通信的source link功能。该两功分别绑定各自的配置,配置在实际使用的过程中,是不同共存的,只能切换操作,当然,如果你足够厉害,将两功能汇总到一个配置里面,也不是不可能的事。该两种功能的具体实现会在下面详细介绍。

zero.c驱动功能上主要是实现了两个bulk端点,in和out,根据选定的配置,in和out针对性就不同了。但是如果你要是想和host端主机通信,source link功能必然是完美的选择,因为loopback顾明思意,就是采用的回环功能。

在介绍zero.c驱动过的实现之前,先介绍下zero中相关的变量和函数。

variable : //该驱动涉及到的变量和结构体。
|| autoresume
|| autoresume_interval_
|| autoresume_step_ms
|| autoresume_timer //表示zero设备与resume操作相关。|| dev_strings //设备字符串描述符
|| device_desc //设备描述符,唯一
|| func_inst_lb //loopback功能实例 此处特别的重要
|| func_inst_ss //source link功能实例,也特别的重要。
|| func_lb //loopback接口,又称功能
|| func_ss //source link接口,又称功能
|| gzero_options //涉及到zero设备中packet和bulk_buf长度等|| longname //产品名称|| loopdefault //是否支持loopback功能通过该变量判定,为0表示选择source link功能。
|| max_autoresume //最长的自动恢复时间,与定时器设置相关
|| otg_desc //关于otg功能的
|| otg_descriptor
|| serial /Serial 变量存储的是设备序列号,对于一个非正式设备,显得不是那么重要,随便填充一下
|| sourcesink_driver //基本上后缀命名的driver表示是usb config
|| strings_dev //字符串描述符
|| stringtab_dev //gadget字符串描述符,包括字符串描述符,以及支持的语种。默认是英文。
|| zero_driver //usb_composite_driver function
|| cleanup //卸载驱动函数。
|| init //驱动注册函数。
|| ss_config_setup //枚举响应函数,只有source link功能支持。
|| zero_autoresum //定时器调用函数
|| zero_bind //没有绑定,就没有各种配置和接口的实现,也就没有驱动和设备的绑定
|| zero_resume //个人认为主要是省电模式采用,设备处于空闲时,进入挂起状态,被唤醒后自动回复正常状态
    || zero_suspend           //设备挂起。

    || zero_unbind            //是否功能和功能实例。

之所以介绍zero驱动的相关变量、结构体和函数主要是为了对驱动整体框架有所了解。在分析该驱动的过程中,我们主要关注那些点,以及这些是怎么封装的,这样会对我们改zero驱动会有很大的帮助。

1=======模块的注册和卸载。此处不用多讲,我的另一篇文章linux usb gadget框架概述已经较为详细的介绍了驱动的注册过程。

 static __refdata struct usb_composite_driver zero_driver = {
.name = "zero",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER, //此处还支持usb3.0?没有测试过。
.bind = zero_bind,
.unbind = zero_unbind,
.suspend = zero_suspend,
.resume = zero_resume,
}; MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL"); static int __init init(void)
{
return usb_composite_probe(&zero_driver);
}
   module_init(init);

   static void __exit cleanup(void)
{
usb_composite_unregister(&zero_driver);
}
module_exit(cleanup);

2=====填充设备描述符。

   static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,//描述符类型,此处表示设备描述符,非接口描述符 .bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_VENDOR_SPEC, //#define USB_CLASS_VENDOR_SPEC   0xff 表示产商自定义类设备 .idVendor = cpu_to_le16(DRIVER_VENDOR_NUM),
.idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM),
.bNumConfigurations = , //介绍该设备有几个配置,此处写的两个,真好是loopback和source link两个配置。
};

3=====填充字符描述符。基本上usb_gadget_strings都是这个德行,封装三层,最后填充到zero_driver的.strings = dev_strings,中去。

 static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = longname,
[USB_GADGET_SERIAL_IDX].s = serial,
[USB_GZERO_SS_DESC].s = "source and sink data",
[USB_GZERO_LB_DESC].s = "loop input to output",
{ } /* end of list */
}; static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
}; static struct usb_gadget_strings *dev_strings[] = {
&stringtab_dev,
NULL,
};

 ==========填充配置描述符。loopback_driver&sourcesink_driver

   static struct usb_configuration sourcesink_driver = {
.label = "source/sink", //标签在主机枚举的时候获取,如果主机是window,可以在管理/设备管理器中找到
.setup = ss_config_setup, //setup主要是响应枚举过程中控制请求某些特效的操作。
.bConfigurationValue = , //当使用SetConfiguration和GetConfiguration请求时所指定的配置索引值。这个在响应枚举过程是十分重要的。
.bmAttributes = USB_CONFIG_ATT_SELFPOWER, //自供电
/* .iConfiguration = DYNAMIC */
};
static struct usb_configuration loopback_driver = {
.label = "loopback",
.bConfigurationValue = ,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
/* .iConfiguration = DYNAMIC */
};

 ==========填充接口描述符。func_ss&func_lb

该接口的实现挺有技巧的,下面详细讲述之。

 static struct usb_function *func_ss;
static struct usb_function_instance *func_inst_ss; //视乎编写一个gadget驱动的通用方法需要填充一个功能实例。
static struct usb_function *func_lb;
static struct usb_function_instance *func_inst_lb; //首先,填充usb_function_instance 这个结构体。
 func_inst_ss = usb_get_function_instance("SourceSink");  
 func_inst_lb = usb_get_function_instance("Loopback");

38 struct usb_function_instance *usb_get_function_instance(const char *name) //此处是怎么通过字符串得到的功能实例呢?
39 {
40 struct usb_function_instance *fi;
41 int ret;
42
43 fi = try_get_usb_function_instance(name);
44 if (!IS_ERR(fi))
45 return fi;
46 ret = PTR_ERR(fi);
47 if (ret != -ENOENT)
48 return fi;
49 ret = request_module("usbfunc:%s", name);
50 if (ret < 0)
51 return ERR_PTR(ret);
52 return try_get_usb_function_instance(name); //关键在于这个函数。
53 }

11 static struct usb_function_instance *try_get_usb_function_instance(const char *name)
12 {
13 struct usb_function_driver *fd;
14 struct usb_function_instance *fi;
15
16 fi = ERR_PTR(-ENOENT);
17 mutex_lock(&func_lock);
18 list_for_each_entry(fd, &func_list, list) {  //便利所有的功能链表,通过功能链表获取功能驱动,那功能驱动从哪里来的?那就设计到每个功能驱动的注册了。
19
20 if (strcmp(name, fd->name))
21 continue;
22
23 if (!try_module_get(fd->mod)) {
24 fi = ERR_PTR(-EBUSY);
25 break;
26 }
27 fi = fd->alloc_inst();  //功能实例是在这里生成的。
28 if (IS_ERR(fi))
29 module_put(fd->mod);
30 else
31 fi->fd = fd;
32 break;
33 }
34 mutex_unlock(&func_lock);
35 return fi;
36 }

那现在问题的关键点是在于找到usb_function_driver这个结构体,驱动肯定是需要调用注册函数进行注册的,功能驱动肯定是在各自功能实现文件里。

f_sourcesink.c f_loopback.c

usb_function_register(&SourceSinkusb_func);
int usb_function_register(struct usb_function_driver *newf)
{
struct usb_function_driver *fd;
int ret; ret = -EEXIST; mutex_lock(&func_lock);
list_for_each_entry(fd, &func_list, list) {
if (!strcmp(fd->name, newf->name))
goto out;
}
ret = ;
list_add_tail(&newf->list, &func_list); //从该函数可以看出,该注册函数正好是讲功能实例结构体中的链表加入全局链表func_list中,这样通过list_for_entry就可以获取实例了
 out: 

 mutex_unlock(&func_lock); 

 return ret;  }

那SourceSinkusb_func这个变量从哪里来呢?用ag搜索了整个内核目录都没有找到,这个时候不得不好好分析linux中宏定义的厉害了。

 DECLARE_USB_FUNCTION(SourceSink, source_sink_alloc_inst,
source_sink_alloc_func); #define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \
static struct usb_function_driver _name ## usb_func = { \ //这个地方就不用过多的介绍了吧。
.name = __stringify(_name), \
.mod = THIS_MODULE, \
.alloc_inst = _inst_alloc, \ //所以在try_get_usb_function_instance函数中其实才是真正的开始对功能实例进行初始化。
.alloc_func = _func_alloc, \ //usb_get_function其实也是调用的该函数实现得功能的初始化。
}; \
MODULE_ALIAS("usbfunc:"__stringify(_name));
下面在再来看一下alloc_func做了哪些操作。
 static struct usb_function *source_sink_alloc_func(
struct usb_function_instance *fi)
{
struct f_sourcesink *ss;
struct f_ss_opts *ss_opts; ss = kzalloc(sizeof(*ss), GFP_KERNEL);
if (!ss)
return NULL; ss_opts = container_of(fi, struct f_ss_opts, func_inst); mutex_lock(&ss_opts->lock);
ss_opts->refcnt++;
mutex_unlock(&ss_opts->lock); pattern = ss_opts->pattern;
isoc_interval = ss_opts->isoc_interval;
isoc_maxpacket = ss_opts->isoc_maxpacket;
isoc_mult = ss_opts->isoc_mult;
isoc_maxburst = ss_opts->isoc_maxburst;
buflen = ss_opts->bulk_buflen; ss->function.name = "source/sink"; //填充功能名称
ss->function.bind = sourcesink_bind;//主要实现了设备和驱动的绑定,已经端点的初始化的操作
ss->function.set_alt = sourcesink_set_alt;//这个会根据配置中功能的先后顺序,将某个功能配置为0
ss->function.get_alt = sourcesink_get_alt;
ss->function.disable = sourcesink_disable;
ss->function.setup = sourcesink_setup;
ss->function.strings = sourcesink_strings; ss->function.free_func = sourcesink_free_func; return &ss->function;
}

 ==========填充端点描述符。 //端点描述符有两种一个是支持全是一种是支持高速,主要针对host端是否支持来定,在bind函数中,将其加入function中。

 static struct usb_endpoint_descriptor fs_source_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
}; static struct usb_endpoint_descriptor fs_sink_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor fs_iso_source_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = cpu_to_le16(),
.bInterval = ,
}; static struct usb_endpoint_descriptor fs_iso_sink_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = cpu_to_le16(),
.bInterval = ,
};
static struct usb_descriptor_header *fs_source_sink_descs[] = {
(struct usb_descriptor_header *) &source_sink_intf_alt0, //在枚举过程中,都是通过usb_descriptor_header指针获取的。
(struct usb_descriptor_header *) &fs_sink_desc,
(struct usb_descriptor_header *) &fs_source_desc,
(struct usb_descriptor_header *) &source_sink_intf_alt1,
#define FS_ALT_IFC_1_OFFSET 3
(struct usb_descriptor_header *) &fs_sink_desc,
(struct usb_descriptor_header *) &fs_source_desc,
(struct usb_descriptor_header *) &fs_iso_sink_desc,
(struct usb_descriptor_header *) &fs_iso_source_desc,
NULL,
};

 至此,从驱动注册,各种配置描述符的初始化和实现已经讲完,是不是感觉什么也没讲明白,那是因为你不熟悉gadaget驱动的注册和枚举响应过程,下面将花大量的篇幅介绍gadget设备的枚举过程。

枚举过程主要分为如下几个步骤:

要了解gadget驱动的枚举过程,就必须了解usb设备的中断响应。要了解usb的中断响应就必须知道usb控制的注册过程,因为中断的注册实在控制器的注册中完成的。

下面将花少量篇幅介绍下udc的注册过程,以omap_udc.c为例进行讲解。

194 static struct platform_device udc_device = {
195 .name = "omap_udc",
196 .id = -1,
197 .dev = {
198 .dma_mask = &udc_dmamask,
199 .coherent_dma_mask = 0xffffffff,
200 },
201 .num_resources = ARRAY_SIZE(udc_resources),
202 .resource = udc_resources,
203 };

  static const char driver_name[] = "omap_udc";//此处是平台设备能够注册成功的关键,平台设备一般在板级信息中注册。
static struct platform_driver udc_driver = {
.probe = omap_udc_probe,
.remove = omap_udc_remove,
.suspend = omap_udc_suspend,
.resume = omap_udc_resume,
.driver = {
.owner = THIS_MODULE,
.name = (char *) driver_name,
},
};
一当内核查找到平台设备和驱动设备中名称匹配,就会加重驱动,调用驱动probe函数
 static int omap_udc_probe(struct platform_device *pdev)
{
int status = -ENODEV;
int hmc;
struct usb_phy *xceiv = NULL;
const char *type = NULL;
struct omap_usb_config *config = pdev->dev.platform_data;
struct clk *dc_clk = NULL;
struct clk *hhc_clk = NULL; if (cpu_is_omap7xx())
use_dma = ; /* NOTE: "knows" the order of the resources! */
if (!request_mem_region(pdev->resource[].start,
pdev->resource[].end - pdev->resource[].start + ,
driver_name)) {
DBG("request_mem_region failed\n");
return -EBUSY;
} 。。。。。。。。。。。
/* USB "non-iso" IRQ (PIO for all but ep0) */
2880 status = request_irq(pdev->resource[2].start, omap_udc_pio_irq, //最关键的函数,一当host发起任何从设备相关的操作,都会调用该函数。
, "omap_udc pio", udc);
if (status != ) {
ERR("can't get irq %d, err %d\n",
(int) pdev->resource[].start, status);
goto cleanup2;
}
 static irqreturn_t omap_udc_irq(int irq, void *_udc)
{
struct omap_udc *udc = _udc;
u16 irq_src;
irqreturn_t status = IRQ_NONE;
unsigned long flags; spin_lock_irqsave(&udc->lock, flags);
irq_src = omap_readw(UDC_IRQ_SRC); /* Device state change (usb ch9 stuff) */
if (irq_src & UDC_DS_CHG) {
devstate_irq(_udc, irq_src);
status = IRQ_HANDLED;
irq_src &= ~UDC_DS_CHG;
} /* EP0 control transfers */
1837 if (irq_src & (UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX)) {//针对枚举过程,中断源肯定是控制端点0发送过来的。
ep0_irq(_udc, irq_src);//如果是控制端点,则调用端点0中断函数响应中断。
status = IRQ_HANDLED;
irq_src &= ~(UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX);
}

//这个地方不对这个中断函数进行多讲,主要介绍其枚举响应分支。

           default:
delegate:
/* activate the ep0out fifo right away */
if (!udc->ep0_in && w_length) {
omap_writew(, UDC_EP_NUM);
omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
} /* gadget drivers see class/vendor specific requests,
1676 ┊* {SET,GET}_{INTERFACE,DESCRIPTOR,CONFIGURATION},
1677 ┊* and more
1678 ┊*/
VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n",
u.r.bRequestType, u.r.bRequest,
w_value, w_index, w_length); #undef w_value
#undef w_index
#undef w_length /* The gadget driver may return an error here,
1688 ┊* causing an immediate protocol stall.
1689 ┊*
1690 ┊* Else it must issue a response, either queueing a
1691 ┊* response buffer for the DATA stage, or halting ep0
1692 ┊* (causing a protocol stall, not a real halt). A
1693 ┊* zero length buffer means no DATA stage.
1694 ┊*
1695 ┊* It's fine to issue that response after the setup()
1696 ┊* call returns, and this IRQ was handled.
1697 ┊*/
udc->ep0_setup = ;
spin_unlock(&udc->lock);
status = udc->driver->setup(&udc->gadget, &u.r); 这个函数可不是zero.c中那个setup函数,而是composite.c中的setup函数。下面我们具体分析之。

=======枚举过程的关键性函数。

在介绍该函数时,先介绍下至关重要的一个结构体,usb_request,该结构体就是数据发送和接受的载体,类似于网络中的skb.

   struct usb_request {
void *buf; //需要传输的数据都会在此填充。
unsigned length; //buf长度
dma_addr_t dma; //与dma操作相关的 struct scatterlist *sg; //视乎是分散聚集表鄙人不是很明白。
unsigned num_sgs; //如上
unsigned num_mapped_sgs; //如上 unsigned stream_id:; //The stream id, when USB3.0 bulk streams are being used
unsigned no_interrupt:; //If true, hints that no completion irq is needed. Helpful sometimes with deep request queues that are handled directly by DMA controllers.
unsigned zero:; //是否是0包。
unsigned short_not_ok:; //对于0包,判定该报是否允许其为错包 void (*complete)(struct usb_ep *ep, //包发送完成后,会调用该函数。
struct usb_request *req);
void *context; //很简单
struct list_head list; //同一类型的usb_request int status; //当前状态,Reports completion code, zero or a negative errno.
unsigned actual; //实际传输包的长度。
};
 composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_request *req = cdev->req; //所有的数据传输都是靠usb_request函数完成的。
int value = -EOPNOTSUPP;
int status = ;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u8 intf = w_index & 0xFF;
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
struct usb_function *f = NULL;
u8 endp; /* partial re-init of the response message; the function or the
1241 ┊* gadget might need to intercept e.g. a control-OUT completion
1242 ┊* when we delegate to it.
1243 ┊*/
req->zero = ;
req->complete = composite_setup_complete;
req->length = ;
gadget->ep0->driver_data = cdev; switch (ctrl->bRequest) {
/* we handle all standard USB descriptors */
case USB_REQ_GET_DESCRIPTOR:
if (ctrl->bRequestType != USB_DIR_IN)
goto unknown;
switch (w_value >> ) { case USB_DT_DEVICE: //设备描述符
cdev->desc.bNumConfigurations =
count_configs(cdev, USB_DT_DEVICE);
cdev->desc.bMaxPacketSize0 =
cdev->gadget->ep0->maxpacket;
if (gadget_is_superspeed(gadget)) {
if (gadget->speed >= USB_SPEED_SUPER) {
cdev->desc.bcdUSB = cpu_to_le16(0x0300);
cdev->desc.bMaxPacketSize0 = ;
} else {
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
}
} value = min(w_length, (u16) sizeof cdev->desc);
memcpy(req->buf, &cdev->desc, value);
break;
case USB_DT_DEVICE_QUALIFIER: //the structure is used by USB client drivers to retrieve a USB-defined device qualifier descriptor.
if (!gadget_is_dualspeed(gadget) ||
┊ gadget->speed >= USB_SPEED_SUPER)
break;
device_qual(cdev);
value = min_t(int, w_length,
sizeof(struct usb_qualifier_descriptor));
break;
case USB_DT_OTHER_SPEED_CONFIG:
if (!gadget_is_dualspeed(gadget) ||
┊ gadget->speed >= USB_SPEED_SUPER)
break;
/* FALLTHROUGH */
case USB_DT_CONFIG: //配置描述符
value = config_desc(cdev, w_value);
if (value >= )
value = min(w_length, (u16) value);
break;
case USB_DT_STRING: //字符串描述符
value = get_string(cdev, req->buf,
w_index, w_value & 0xff);
if (value >= )
value = min(w_length, (u16) value);
break;
case USB_DT_BOS:
if (gadget_is_superspeed(gadget)) {
value = bos_desc(cdev);
value = min(w_length, (u16) value);
}
break;
}
break;
/* any number of configs can work */
case USB_REQ_SET_CONFIGURATION:
if (ctrl->bRequestType != )
goto unknown;
if (gadget_is_otg(gadget)) {
if (gadget->a_hnp_support)
DBG(cdev, "HNP available\n");
else if (gadget->a_alt_hnp_support)
DBG(cdev, "HNP on another port\n");
else
VDBG(cdev, "HNP inactive\n");
}
spin_lock(&cdev->lock);
value = set_config(cdev, ctrl, w_value);
spin_unlock(&cdev->lock);
break;
case USB_REQ_GET_CONFIGURATION:
if (ctrl->bRequestType != USB_DIR_IN)
goto unknown;
if (cdev->config)
*(u8 *)req->buf = cdev->config->bConfigurationValue;
else
*(u8 *)req->buf = ;
value = min(w_length, (u16) );
break;
/* function drivers must handle get/set altsetting; if there's
1334 ┊* no get() method, we know only altsetting zero works.
1335 ┊*/
case USB_REQ_SET_INTERFACE: //配置接口
if (ctrl->bRequestType != USB_RECIP_INTERFACE)
goto unknown;
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
if (w_value && !f->set_alt)
break;
value = f->set_alt(f, w_index, w_value); //设置当前接口为第一个接口0,在上文中有提及该函数的作用
if (value == USB_GADGET_DELAYED_STATUS) {
DBG(cdev,
┊"%s: interface %d (%s) requested delayed status\n",
__func__, intf, f->name);
cdev->delayed_status++;
DBG(cdev, "delayed_status count %d\n",
cdev->delayed_status);
}
break;
case USB_REQ_GET_INTERFACE:
if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
goto unknown;
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
case USB_REQ_GET_INTERFACE:
if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
goto unknown;
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
/* lots of interfaces only need altsetting zero... */
value = f->get_alt ? f->get_alt(f, w_index) : ;
if (value < )
break;
*((u8 *)req->buf) = value;
value = min(w_length, (u16) );
break; /*
1373 ┊* USB 3.0 additions:
1374 ┊* Function driver should handle get_status request. If such cb
1375 ┊* wasn't supplied we respond with default value = 0
1376 ┊* Note: function driver should supply such cb only for the first
1377 ┊* interface of the function
1378 ┊*/
case USB_REQ_GET_STATUS:
if (!gadget_is_superspeed(gadget))
goto unknown;
if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE))
goto unknown;
value = ; /* This is the length of the get_status reply */
value = ; /* This is the length of the get_status reply */
put_unaligned_le16(, req->buf);
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
status = f->get_status ? f->get_status(f) : ;
if (status < )
break;
put_unaligned_le16(status & 0x0000ffff, req->buf);
break;
/*
1397 ┊* Function drivers should handle SetFeature/ClearFeature
1398 ┊* (FUNCTION_SUSPEND) request. function_suspend cb should be supplied
1399 ┊* only for the first interface of the function
1400 ┊*/
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
if (!gadget_is_superspeed(gadget))
goto unknown;
if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE))
goto unknown;
switch (w_value) {
case USB_INTRF_FUNC_SUSPEND:
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
value = ;
if (f->func_suspend)
value = f->func_suspend(f, w_index >> );
if (value < ) {
ERROR(cdev,
┊ ┊ "func_suspend() returned error %d\n",
┊ ┊ value);
value = ;
}
break;
}
break;
default:
unknown: //倘若枚举过程中发送了无法识别的请求。
VDBG(cdev,
"non-core control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length); /* functions always handle their interfaces and endpoints...
1434 ┊* punt other recipients (other, WUSB, ...) to the current
1435 ┊* configuration code.
1436 ┊*
1437 ┊* REVISIT it could make sense to let the composite device
1435 ┊* configuration code.
1436 ┊*
1437 ┊* REVISIT it could make sense to let the composite device
1438 ┊* take such requests too, if that's ever needed: to work
1439 ┊* in config 0, etc.
1440 ┊*/
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
break; case USB_RECIP_ENDPOINT:
endp = ((w_index & 0x80) >> ) | (w_index & 0x0f);
list_for_each_entry(f, &cdev->config->functions, list) {
if (test_bit(endp, f->endpoints))
break;
}
if (&f->list == &cdev->config->functions)
f = NULL;
break;
} if (f && f->setup)
value = f->setup(f, ctrl); //当时在分析该代码时候,很不明白function中为啥还设置setup函数,至少zero驱动中source sink只是简单的处理了下。
else {
struct usb_configuration *c; c = cdev->config;
if (!c)
goto done; /* try current config's setup */
if (c->setup) {
value = c->setup(c, ctrl);
goto done;
} /* try the only function in the current config */
if (!list_is_singular(&c->functions))
goto done;
f = list_first_entry(&c->functions, struct usb_function,
┊ ┊list);
if (f->setup)
value = f->setup(f, ctrl);
} goto done;
}
//其实在每一个case语句里面,都对req进行了填充。并调用发送函数usb_eq_queue,做好执行回馈函数。
/* respond with data transfer before status phase? */
if (value >= && value != USB_GADGET_DELAYED_STATUS) {
req->length = value;
req->zero = value < w_length;
value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);//将封装好的数据,发送到udc指定的fifo中返回给主机,并调用回调函数composite_setup_complete
if (value < ) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = ;
composite_setup_complete(gadget->ep0, req);//complete响应函数
}
} else if (value == USB_GADGET_DELAYED_STATUS && w_length != ) {
WARN(cdev,
"%s: Delayed status not supported for w_length != 0",
__func__);
} done:
/* device either stalls (value < 0) or reports success */
if (value < ) {
printk("control error %d req%02x.%02x v%04x i%04x l%d\n", value,
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
}
return value;
}
usb_ep_queue实际调用的是udc中实现的queue函数。
 static struct usb_ep_ops omap_ep_ops = {
.enable = omap_ep_enable,
.disable = omap_ep_disable, .alloc_request = omap_alloc_request,
.free_request = omap_free_request, 1123 .queue = omap_ep_queue,//该函数的作用主要是讲req写入
到对应的端点中的queue队列中去。
1002         list_add_tail(&req->queue, &ep->queue);

下面好好分析下填充好了req后,是怎样通过omap_ep_queue函数发送的。

omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
{
struct omap_ep *ep = container_of(_ep, struct omap_ep, ep);
struct omap_req *req = container_of(_req, struct omap_req, req);
struct omap_udc *udc;
unsigned long flags;
int is_iso = ; /* catch various bogus parameters */
if (!_req || !req->req.complete || !req->req.buf //毋庸置疑,为了代码的严谨性,对req进行相关判定是必然的。
|| !list_empty(&req->queue)) {
DBG("%s, bad params\n", __func__);
return -EINVAL;
} if (!_ep || (!ep->ep.desc && ep->bEndpointAddress)) {//这个时候端点描述符显得尤为重要。
DBG("%s, bad ep\n", __func__);
return -EINVAL;
}
if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
if (req->req.length > ep->ep.maxpacket)
return -EMSGSIZE;
is_iso = ;
} /* this isn't bogus, but OMAP DMA isn't the only hardware to
893 ┊* have a hard time with partial packet reads... reject it.
894 ┊*/
if (use_dma
&& ep->has_dma
&& ep->bEndpointAddress !=
&& (ep->bEndpointAddress & USB_DIR_IN) ==
&& (req->req.length % ep->ep.maxpacket) != ) {
DBG("%s, no partial packet OUT reads\n", __func__);
return -EMSGSIZE;
} udc = ep->udc; //获取ucd控制器实例,这个时候就是req和udc结合高潮来临的节奏。
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN; if (use_dma && ep->has_dma)//dma支持吗?
usb_gadget_map_request(&udc->gadget, &req->req,
usb_gadget_map_request(&udc->gadget, &req->req,
(ep->bEndpointAddress & USB_DIR_IN)); VDBG("%s queue req %p, len %d buf %p\n",
ep->ep.name, _req, _req->length, _req->buf); spin_lock_irqsave(&udc->lock, flags); req->req.status = -EINPROGRESS;
req->req.actual = ; /* maybe kickstart non-iso i/o queues */
if (is_iso) {
u16 w; w = omap_readw(UDC_IRQ_EN);
w |= UDC_SOF_IE;
omap_writew(w, UDC_IRQ_EN);
} else if (list_empty(&ep->queue) && !ep->stopped && !ep->ackwait) {
int is_in; if (ep->bEndpointAddress == ) { //若是控制端点0
if (!udc->ep0_pending || !list_empty(&ep->queue)) {
spin_unlock_irqrestore(&udc->lock, flags);
return -EL2HLT;
} /* empty DATA stage? */
is_in = udc->ep0_in;
if (!req->req.length) { //对于端点0,并且或者req数据长度为0,显然,不用进行回复,直接调用done函数表示完成传输。 /* chip became CONFIGURED or ADDRESSED
941 ┊* earlier; drivers may already have queued
942 ┊* requests to non-control endpoints
943 ┊*/
if (udc->ep0_set_config) {
u16 irq_en = omap_readw(UDC_IRQ_EN); irq_en |= UDC_DS_CHG_IE | UDC_EP0_IE;
if (!udc->ep0_reset_config)
irq_en |= UDC_EPN_RX_IE
| UDC_EPN_TX_IE;
omap_writew(irq_en, UDC_IRQ_EN); //是能udc中断
} /* STATUS for zero length DATA stages is
955 ┊* always an IN ... even for IN transfers,
956 ┊* a weird case which seem to stall OMAP.
957 ┊*/
omap_writew(UDC_EP_SEL | UDC_EP_DIR,
UDC_EP_NUM);
omap_writew(UDC_CLR_EP, UDC_CTRL);
omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);//设置fifo使能
omap_writew(UDC_EP_DIR, UDC_EP_NUM);//配置端点放向和号 /* cleanup */
udc->ep0_pending = ; //将控制端点0设置为非挂起状态,即空闲状态。
done(ep, req, ); req = NULL; /* non-empty DATA stage */
} else if (is_in) {
omap_writew(UDC_EP_SEL | UDC_EP_DIR,
UDC_EP_NUM);
} else {
if (udc->ep0_setup) //若端点处理建立状态则说明该端点中数据还没发送完成,即需要发送请求。则将req加入控制器队列queue中。
goto irq_wait;
omap_writew(UDC_EP_SEL, UDC_EP_NUM);
}
} else {
is_in = ep->bEndpointAddress & USB_DIR_IN;
if (!ep->has_dma)
use_ep(ep, UDC_EP_SEL);
/* if ISO: SOF IRQs must be enabled/disabled! */
} if (ep->has_dma)
(is_in ? next_in_dma : next_out_dma)(ep, req);
else if (req) {
if ((is_in ? write_fifo : read_fifo)(ep, req) == ) //若是in则调用write_fifo,将req写入队列中,若是out,则调用read函数,并释放req
req = NULL;
deselect_ep();
if (!is_in) { omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
ep->ackwait = + ep->double_buf;
}
/* IN: 6 wait states before it'll tx */
}
} irq_wait:
/* irq handler advances the queue */
if (req != NULL)
list_add_tail(&req->queue, &ep->queue);//将usb加入队列中,等待中断处理,处理完后就调用中断回调函数,done
spin_unlock_irqrestore(&udc->lock, flags); return ;
} 自此,枚举过程已经讲完,如果成功的话,会返回0值,小于0值,那就得调用composite_setup_complete,表明枚举过程失败了。

讲完枚举过程,这里还讲下usb_request的传输过程。当时开发项目时,主要用到loopback功能,主要是对ep_in和out的包进行拦截,后并进行处理。

looback功能很按照字面意思很简单,就是普通的回复,即收到的req转给发出的req。

host和gadge之间的通信主要依靠中断,在gadget端,进行数据通信时,收到bulk处理指令后都会调用各自端点实现的complete函数,对于loopback来说,关键的处理函数如下:

此处针对omap_udc.c控制器简单的介绍下:

omap_udc_pio_irq-->write_fifo/read_fifo--->done->req.complete; //看起来很简单,其实是花费笔者较长时间才弄明白的。

 static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_loopback *loop = ep->driver_data;
struct usb_composite_dev *cdev = loop->function.config->cdev;
int status = req->status; switch (status) { case : /* normal completion? */
if (ep == loop->out_ep) { //如果host端发送过来的out_ep,则需要将包转发给ep_in端点。
/* loop this OUT packet back IN to the host */
req->zero = (req->actual < req->length);
req->length = req->actual;
status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);//转发很简单,直接把收到的转到另外一个端点即可,对于需要通过loopback功能
//将req->buf中的包进行某些处理的,可以增加字符操作接口,对buf进行处理后再发送
//给对应得端点。
if (status == )
return; /* "should never get here" */
ERROR(cdev, "can't loop %s to %s: %d\n",
ep->name, loop->in_ep->name,
status);
} /* queue the buffer for some later OUT packet */
req->length = buflen;
status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC); //很显然,如果是in_ep端点的话,需要将out_ep中包发出。
if (status == )
return; /* "should never get here" */
/* FALLTHROUGH */ default:
ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
status, req->actual, req->length);
/* FALLTHROUGH */ /* NOTE: since this driver doesn't maintain an explicit record
284 ┊* of requests it submitted (just maintains qlen count), we
285 ┊* rely on the hardware driver to clean up on disconnect or
286 ┊* endpoint disable.
287 ┊*/
case -ECONNABORTED: /* hardware forced ep reset */
case -ECONNRESET: /* request dequeued */
case -ESHUTDOWN: /* disconnect from host */
free_ep_req(ep, req);
return;
}
}

最新文章

  1. ViewState与Session [转]
  2. 编译运行java程序出现Exception in thread &quot;main&quot; java.lang.UnsupportedClassVersionError: M : Unsupported major.minor version 51.0
  3. 最近Google经常打不开?
  4. POJ2533Longest Ordered Subsequence(DP)
  5. hdoj 2682 Tree
  6. 高密度Java应用部署的一些实践
  7. javascript对象的理解
  8. 【自由谈】城域网IPv6过渡技术——MAP技术(4)
  9. C# 连接 SQLServer 及操作
  10. springboot idea 代码更改自己编译设置
  11. Linux - PWM的驱动编写【转】
  12. c#读取Sybase中文乱码的解决办法
  13. room 二分图最大匹配KM
  14. mysql触发器 学习
  15. 在Windows上弄一个redis的docker容器
  16. Windows进程间共享内存通信实例
  17. go指针的一个小坑
  18. CentOS搭建NFS服务
  19. UWP 轨道视图Orbit View
  20. 20135337朱荟潼Java实验报告二

热门文章

  1. Maven插件和细节
  2. windows用一键安装包安装(推荐)
  3. asp.net5中程序根目录的获取
  4. SpringMVC03 ParameterMethodNameResolver(参数方法名称解析器) And XmlViewResolver(视图解析器)
  5. 基于android-uitableview扩展-uilistview项目
  6. 【mysql】mysql 配置
  7. iOS核心动画高级技巧之核心动画(三)
  8. 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:9.观察者模式
  9. uvm_regex——DPI在UVM中的实现(三)
  10. GBase数据库存储过程——批量查询多个数据表的磁盘占用情况