Gadget设备层
这一层是可选的,介于UDC驱动层和Gadget功能层之间。主要源码在composite.c和composite.h文件中,设备层其实和硬件无关,主要实现一些通用性的代码,减少gadget功能层的代码重复工作。Gadget设备层其中承上启下的作用,联系Gadget功能层和UDC驱动层。
将composite源码独立出来,还为复合设备的实现提供了一个通用性的框架。复合设备是指在一个配置描述符中支持多个功能,或者支持多个配置的设备中,每个配置都有一个不同的功能。如一个设备同时支持网络和存储,一个设备同时支持键盘和鼠标功能等。
1、usb_function
struct usb_function {//描述一个配置的功能 const char *name; //功能名称 struct usb_gadget_strings **strings; // struct usb_descriptor_header **descriptors; //全速和低速的描述符表,用于bind中分配的接口描述符和string描述符 struct usb_descriptor_header **hs_descriptors;//高速描述符 struct usb_configuration *config; /* REVISIT: bind() functions can be marked __init, which * makes trouble for section mismatch analysis. See if * we can't restructure things to avoid mismatching. * Related: unbind() may kfree() but bind() won't... */ /* configuration management: bind/unbind */ int (*bind)(struct usb_configuration *,struct usb_function *); void (*unbind)(struct usb_configuration *,struct usb_function *); /* runtime state management */ int (*set_alt)(struct usb_function *,unsigned interface, unsigned alt); int (*get_alt)(struct usb_function *,unsigned interface); void (*disable)(struct usb_function *); int (*setup)(struct usb_function *,const struct usb_ctrlrequest *);//接口相关的控制处理 void (*suspend)(struct usb_function *); void (*resume)(struct usb_function *); /* private: */ /* internals */ struct list_head list; };2、usb_configuration
struct usb_configuration {//表示一个gadget配置 const char *label; //配置名称 struct usb_gadget_strings **strings; const struct usb_descriptor_header **descriptors;//功能描述符表 /* REVISIT: bind() functions can be marked __init, which * makes trouble for section mismatch analysis. See if * we can't restructure things to avoid mismatching... */ /* configuration management: bind/unbind */ int (*bind)(struct usb_configuration *); void (*unbind)(struct usb_configuration *); int (*setup)(struct usb_configuration *,const struct usb_ctrlrequest *); //处理驱动框架不能处理的配置控制请求 /* fields in the config descriptor */ u8 bConfigurationValue; u8 iConfiguration; u8 bmAttributes; u8 bMaxPower; struct usb_composite_dev *cdev; /* private: */ /* internals */ struct list_head list; struct list_head functions; u8 next_interface_id; unsigned highspeed:1; unsigned fullspeed:1; struct usb_function *interface[MAX_CONFIG_INTERFACES]; };3、usb_composite_driver
struct usb_composite_driver { const char *name; //驱动名称 const struct usb_device_descriptor *dev; struct usb_gadget_strings **strings; /* REVISIT: bind() functions can be marked __init, which * makes trouble for section mismatch analysis. See if * we can't restructure things to avoid mismatching... */ int (*bind)(struct usb_composite_dev *); int (*unbind)(struct usb_composite_dev *); /* global suspend hooks */ void (*suspend)(struct usb_composite_dev *); void (*resume)(struct usb_composite_dev *); };4、usb_composite_dev
struct usb_composite_dev {//表示一个composite设备 struct usb_gadget *gadget;//关联的gadget struct usb_request *req; //用于控制响应,提前分配 unsigned bufsiz; //req中提前分配的buffer长度 struct usb_configuration *config; /* private: */ /* internals */ struct usb_device_descriptor desc; struct list_head configs; struct usb_composite_driver *driver; u8 next_string_id; /* the gadget driver won't enable the data pullup * while the deactivation count is nonzero. */ unsigned deactivations; /* protects at least deactivation count */ spinlock_t lock; };
重要函数
static int __init 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; //将udc gadget与composite设备建立联系 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; 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);//将所有端点的driver_data成员清零 /* composite gadget needs to assign strings for whole device (like * serial number), register function drivers, potentially update * power state and consumption, etc */ status = composite->bind(cdev); if (status < 0) goto fail; //设置composite设备的设备描述符 cdev->desc = *composite->dev; cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; //设置composite设备的设备描述符信息 /* 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); //设置字符串描述符 /* strings can't be assigned before bind() allocates the * releavnt identifiers */ if (cdev->desc.iManufacturer && iManufacturer) string_override(composite->strings, cdev->desc.iManufacturer, iManufacturer); if (cdev->desc.iProduct && iProduct) string_override(composite->strings, cdev->desc.iProduct, iProduct); if (cdev->desc.iSerialNumber && iSerialNumber) string_override(composite->strings, cdev->desc.iSerialNumber, iSerialNumber); INFO(cdev, "%s ready\n", composite->name); return 0; fail: composite_unbind(gadget); return status; }Composite_bind函数主要完成UDC驱动层gadget设备和设备层composite设备关系的建立,分配控制端点0的请求数据结构,设置设备描述符,并调用功能层的bind函数分配功能层所需要的资源等工作。
static void /* __init_or_exit */ composite_unbind(struct usb_gadget *gadget) { struct usb_composite_dev *cdev = get_gadget_data(gadget); /* composite_disconnect() must already have been called * by the underlying peripheral controller driver! * so there's no i/o concurrency that could affect the * state protected by cdev->lock. */ WARN_ON(cdev->config); while (!list_empty(&cdev->configs)) {//遍历设备的配置链表 struct usb_configuration *c; c = list_first_entry(&cdev->configs,struct usb_configuration, list); while (!list_empty(&c->functions)) {//遍历这个配置的所有功能 struct usb_function *f; f = list_first_entry(&c->functions,struct usb_function, list); list_del(&f->list); if (f->unbind) { DBG(cdev, "unbind function '%s'/%p\n",f->name, f); f->unbind(c, f); /* may free memory for "f" */ } } list_del(&c->list); if (c->unbind) { DBG(cdev, "unbind config '%s'/%p\n", c->label, c); c->unbind(c); /* may free memory for "c" */ } } if (composite->unbind) composite->unbind(cdev); if (cdev->req) { kfree(cdev->req->buf); usb_ep_free_request(gadget->ep0, cdev->req); } kfree(cdev); set_gadget_data(gadget, NULL); composite = NULL; }在配置中增加一个功能,每个配置初始化后都必须至少有一个功能。
int __init usb_add_function(struct usb_configuration *config,struct usb_function *function) { int value = -EINVAL; DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n", 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; done: if (value) DBG(config->cdev, "adding '%s'/%p --> %d\n", function->name, function, value); return value; }为设备增加一个配置,这个实现原理和在配置下增加一个功能类似
int __init usb_add_config(struct usb_composite_dev *cdev, struct usb_configuration *config) { int status = -EINVAL; struct usb_configuration *c; DBG(cdev, "adding config #%u '%s'/%p\n", config->bConfigurationValue, config->label, config); if (!config->bConfigurationValue || !config->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); //一个USB设备可以有多个配置 //这句是将配置加入设备的配置链表中 INIT_LIST_HEAD(&config->functions); //初始化配置的functions链表,functions链表要链接struct usb_function类型的数据结构,这个数据结构也很重要,其实他代表一个USB接口 config->next_interface_id = 0; status = config->bind(config);//完成这个配置的资源分配 //这里函数调用的是sourcesink_bind_config, //初始化一个struct usb_function结构,并且将其加入到配置的functions链表 if (status < 0) { list_del(&config->list); config->cdev = NULL; } else { unsigned i; DBG(cdev, "cfg %d/%p speeds:%s%s\n", config->bConfigurationValue, config, 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/%p\n", i, f->name, f); } } /* set_alt(), or next config->bind(), sets up * ep->driver_data as needed. */ usb_ep_autoconfig_reset(cdev->gadget); //这个函数的主要作用就是将cdev->gadget的所有端点的driver_data清空 done: if (status) DBG(cdev, "added config '%s'/%u --> %d\n", config->label, config->bConfigurationValue, status); return status; }
设置一个配置
static int set_config(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl, unsigned number) { struct usb_gadget *gadget = cdev->gadget; struct usb_configuration *c = NULL; int result = -EINVAL; unsigned power = gadget_is_otg(gadget) ? 8 : 100; int tmp; if (cdev->config) reset_config(cdev);//清除设备的配置值 if (number) {//根据配置值找到对应的配置 list_for_each_entry(c, &cdev->configs, list) { if (c->bConfigurationValue == number) { result = 0; break; } } if (result < 0) goto done; } else result = 0; INFO(cdev, "%s speed config #%d: %s\n", ({ char *speed; switch (gadget->speed) { case USB_SPEED_LOW: speed = "low"; break; case USB_SPEED_FULL: speed = "full"; break; case USB_SPEED_HIGH: speed = "high"; break; default: speed = "?"; break; } ; speed; }), number, c ? c->label : "unconfigured"); if (!c) goto done; cdev->config = c; /* Initialize all interfaces by setting them to altsetting zero. */ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { struct usb_function *f = c->interface[tmp]; if (!f) break; result = f->set_alt(f, tmp, 0); if (result < 0) { DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", tmp, f->name, f, result); reset_config(cdev); goto done; } } /* when we return, be sure our power usage is valid */ power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW; done: usb_gadget_vbus_draw(gadget, power); return result; }这个setup函数完成了ep0所需要处理的而底层不能处理的功能,由于这一个层实现了这个函数,所以功能层就不需要关注于USB协议的这些事务性处理,而重点放在需要实现的功能上
static int 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; int value = -EOPNOTSUPP; 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; /* partial re-init of the response message; the function or the * gadget might need to intercept e.g. a control-OUT completion * when we delegate to it. */ req->zero = 0; req->complete = composite_setup_complete; req->length = USB_BUFSIZ; 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 >> 8) { case USB_DT_DEVICE: cdev->desc.bNumConfigurations = count_configs(cdev, USB_DT_DEVICE); value = min(w_length, (u16) sizeof cdev->desc); memcpy(req->buf, &cdev->desc, value); break; case USB_DT_DEVICE_QUALIFIER: if (!gadget_is_dualspeed(gadget)) 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)) break; /* FALLTHROUGH */ case USB_DT_CONFIG: value = config_desc(cdev, w_value); if (value >= 0) value = min(w_length, (u16) value); break; case USB_DT_STRING: value = get_string(cdev, req->buf, w_index, w_value & 0xff); if (value >= 0) value = min(w_length, (u16) value); break; } break; /* any number of configs can work */ case USB_REQ_SET_CONFIGURATION: if (ctrl->bRequestType != 0) 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 = 0; value = min(w_length, (u16) 1); break; /* function drivers must handle get/set altsetting; if there's * no get() method, we know only altsetting zero works. */ case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != USB_RECIP_INTERFACE) goto unknown; if (!cdev->config || w_index >= 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); break; case USB_REQ_GET_INTERFACE: if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) goto unknown; if (!cdev->config || w_index >= 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) : 0; if (value < 0) break; *((u8 *)req->buf) = value; value = min(w_length, (u16) 1); 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 ... punt other * recipients (endpoint, other, WUSB, ...) to the current * configuration code. * * REVISIT it could make sense to let the composite device * take such requests too, if that's ever needed: to work * in config 0, etc. */ if ((ctrl->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { f = cdev->config->interface[intf]; if (f && f->setup) value = f->setup(f, ctrl); else f = NULL; } if (value < 0 && !f) { struct usb_configuration *c; c = cdev->config; if (c && c->setup) value = c->setup(c, ctrl); } goto done; } /* respond with data transfer before status phase? */ if (value >= 0) { req->length = value; req->zero = value < w_length; value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); //usb_ep_queue函数主要作用就是将req中的数据写入到FIFO中 if (value < 0) { DBG(cdev, "ep_queue --> %d\n", value); req->status = 0; composite_setup_complete(gadget->ep0, req); } } done: /* device either stalls (value < 0) or reports success */ return value; }