Gadget设备层
这一层是可选的,介于UDC驱动层和Gadget功能层之间。主要源码在composite.c和composite.h文件中,设备层其实和硬件无关,主要实现一些通用性的代码,减少gadget功能层的代码重复工作。Gadget设备层其中承上启下的作用,联系Gadget功能层和UDC驱动层。
将composite源码独立出来,还为复合设备的实现提供了一个通用性的框架。复合设备是指在一个配置描述符中支持多个功能,或者支持多个配置的设备中,每个配置都有一个不同的功能。如一个设备同时支持网络和存储,一个设备同时支持键盘和鼠标功能等。
Gadget设备层的主要数据结构:
1. Function
struct usb_function { //描述一个配置的一个功能
const char *name; //功能名称
struct usb_gadget_strings **strings; //string数组,通过bind中分配的id访问
struct usb_descriptor_header **descriptors; //全速和低速的描述符表,用于bind中分配的接口描述符和string描述符
struct usb_descriptor_header **hs_descriptors; //高速描述符表
struct usb_configuration *config;//调用usb_add_function()函数赋值,是这个功能关联的配置描述符
/* 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 */
/*在Gadget注册时,分配资源*/
int (*bind)(struct usb_configuration *, struct usb_function *);
/*unbind的逆操作*/
void (*unbind)(struct usb_configuration *, struct usb_function *);
/* runtime state management */
/*重新配置altsetting,*/
int (*set_alt)(struct usb_function *,unsigned interface, unsigned alt);
/*获取当前altsetting*/
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;
DECLARE_BITMAP(endpoints, 32);
};
2. Config
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 */
/*在usb_add_config函数中调用,分配资源等*/
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;
/*和composite设备关联,在usb_add_config函数中设置*/
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. 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. 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;
};
其中重要的函数:
1.
static int __init composite_bind(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev;
int status = -ENOMEM;
cdev = kzalloc(sizeof *cdev, GFP_KERNEL); //分配一个composite设备对象
if (!cdev)
return status;
spin_lock_init(&cdev->lock);
/*将udc gadget和composite设备建立关系*/
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; //端的0的完成函数
gadget->ep0->driver_data = cdev;
cdev->bufsiz = USB_BUFSIZ;
cdev->driver = composite;
/*设置gadget的供电模式*/
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.
*/
/*将所有端点的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
*/
/*调用功能层提供的bind函数,分配资源*/
status = composite->bind(cdev);
if (status < 0)
goto fail;
/*设置composite设备的设备描述符*/
cdev->desc = *composite->dev;
cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
/* standardized runtime overrides for device ID data */
/*设置composite设备的设备描述符信息*/
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函数分配功能层所需要的资源等工作。
2.
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) { //调用功能的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) { //调用配置的unbind函数
DBG(cdev, "unbind config '%s'/%p\n", c->label, c);
c->unbind(c);
/* may free memory for "c" */
}
}
if (composite->unbind) //调用功能驱动的unbind函数
composite->unbind(cdev);
if (cdev->req) { //释放分配的控制请求的缓存区
kfree(cdev->req->buf);
usb_ep_free_request(gadget->ep0, cdev->req);
}
kfree(cdev); //释放composite设备
set_gadget_data(gadget, NULL);
composite = NULL;
}
Unbind函数完成bind函数中分配的资源,并遍历所有配置和各个配置下的功能,调用其对应得unbind函数来释放资源。
其他重要函数:
在配置中增加一个功能,每个配置初始化后都必须至少有一个功能。
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) { //调用功能的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;
}
}
/*将这个配置加入composite设备*/
config->cdev = cdev;
list_add_tail(&config->list, &cdev->configs);
/*初始化这个配置的功能链表头*/
INIT_LIST_HEAD(&config->functions);
config->next_interface_id = 0;
status = config->bind(config); //完成这个配置的资源分配
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.
*/
//将gadget设备的所有端点的driver_data清零
usb_ep_autoconfig_reset(cdev->gadget);
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];
struct usb_descriptor_header **descriptors;
if (!f)
break;
/*
* Record which endpoints are used by the function. This is used
* to dispatch control requests targeted at that endpoint to the
* function's setup callback instead of the current
* configuration's setup callback.
*/
if (gadget->speed == USB_SPEED_HIGH)
descriptors = f->hs_descriptors;
else
descriptors = f->descriptors;
for (; *descriptors; ++descriptors) { //遍历功能的所有端点描述符
struct usb_endpoint_descriptor *ep;
int addr;
if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)
continue;
ep = (struct usb_endpoint_descriptor *)*descriptors;
addr = ((ep->bEndpointAddress & 0x80) >> 3) | (ep->bEndpointAddress & 0x0f);
set_bit(addr, f->endpoints); //这个功能采用的端点号
}
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); //由gadget获取composite设备
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;
u8 endp;
/* 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: //返回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 and endpoints...
* punt other recipients (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.
*/
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
f = cdev->config->interface[intf];
break;
case USB_RECIP_ENDPOINT:
endp = ((w_index & 0x80) >> 3) | (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);
else {
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);
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;
}