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;
}
转自:http://blog.chinaunix.net/uid-14518381-id-3940391.html visualfan的chinaunix博客