📄 g_composite.c
字号:
case USB_DT_DEVICE:
value = min(w_length, (u16) sizeof cdev->dev);
memcpy(req->buf, &cdev->dev, value);
break;
case USB_DT_DEVICE_QUALIFIER:
if (!gadget_is_dualspeed(gadget))
break;
value = min(w_length, (u16) sizeof cdev->qual);
memcpy(req->buf, &cdev->qual, value);
break;
case USB_DT_OTHER_SPEED_CONFIG:
if (!gadget_is_dualspeed(gadget))
break;
/* FALLTHROUGH */
case USB_DT_CONFIG:
INFO(cdev, "Get Config Descriptor\n");
/* one config ... so it must always be index 0 */
if (w_value & 0xff)
break;
value = config_buf(req->buf, w_value >> 8);
if (value >= 0)
value = min(w_length, (u16) value);
break;
case USB_DT_STRING:
value = composite_lookup_string(req->buf, w_index,
w_value & 0xff);
if (value >= 0)
value = min(w_length, (u16) value);
break;
}
break;
/* currently one config, two speeds */
case USB_REQ_SET_CONFIGURATION:
if (ctrl->bRequestType != 0)
goto unknown;
if (gadget->a_hnp_support)
DBG(cdev, "HNP available\n");
else if (gadget->a_alt_hnp_support)
DBG(cdev, "HNP needs a different root port\n");
else
VDBG(cdev, "HNP inactive\n");
spin_lock(&cdev->lock);
value = composite_set_config(ctrl, w_value);
spin_unlock(&cdev->lock);
break;
case USB_REQ_GET_CONFIGURATION:
if (ctrl->bRequestType != USB_DIR_IN)
goto unknown;
*(u8 *)req->buf = cdev->config.bConfigurationValue;
value = min(w_length, (u16) 1);
break;
/* function drivers must handle get/set altsetting */
case USB_REQ_SET_INTERFACE:
if (ctrl->bRequestType != USB_RECIP_INTERFACE)
goto unknown;
if (!cdev->config.bConfigurationValue
|| w_index >= MAX_COMPOSITE_INTERFACES
|| !cdev->interface[w_index])
break;
spin_lock(&cdev->lock);
value = cdev->interface[w_index]->setup(cdev, ctrl, cdev->interface[w_index]);
spin_unlock(&cdev->lock);
break;
case USB_REQ_GET_INTERFACE:
if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
goto unknown;
if (!cdev->config.bConfigurationValue
|| w_index >= MAX_COMPOSITE_INTERFACES
|| !cdev->interface[w_index])
break;
spin_lock(&cdev->lock);
/* function must set cdev->req->buf[0] */
value = cdev->interface[w_index]->setup(cdev, ctrl, cdev->interface[w_index]);
spin_unlock(&cdev->lock);
value = min(w_length, (u16) 1);
break;
default:
unknown:
VDBG(cdev,
"unknown control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
}
}
/* respond with data transfer before status phase? */
if (value >= 0) {
req->length = value;
req->zero = (value < w_length)
&& ((value % gadget->ep0->maxpacket) == 0);
value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
if (value < 0) {
INFO(cdev, "ep_queue --> %d\n", value);
req->status = 0;
composite_setup_complete(gadget->ep0, req);
}
}
/* device either stalls (value < 0) or reports success */
return value;
}
static void
composite_disconnect(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
unsigned long flags;
struct usb_ctrlrequest req;
DBG(cdev, "disconnect\n");
memset(&req, 0, sizeof req);
spin_lock_irqsave(&cdev->lock, flags);
composite_reset_config(&req);
spin_unlock_irqrestore(&cdev->lock, flags);
}
/*-------------------------------------------------------------------------*/
static void /* __init_or_exit */
composite_unbind(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_function *f;
DBG(cdev, "unbind\n");
list_for_each_entry(f, &cdev->functions, function) {
if (f->unbind)
f->unbind(cdev, f);
if (f == cdev->current_bind)
break;
}
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;
}
static void
string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
{
struct usb_string *str = tab->strings;
for (str = tab->strings; str->s; str++) {
if (str->id == id) {
str->s = s;
return;
}
}
}
static void
string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
{
while (*tab) {
string_override_one(*tab, id, s);
tab++;
}
}
static int __init
composite_bind(struct usb_gadget *gadget)
{
struct usb_function *f;
int status = -ENOMEM;
if (cdev)
return -EBUSY;
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->functions);
/* 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 */
usb_ep_autoconfig_reset(gadget);
/* composite gadget needs to assign strings for whole device (like
* serial number), register function drivers, potentially update
* power state and consumption, etc
*/
cdev->current_bind = gadget;
status = composite->bind(cdev);
if (status < 0)
goto fail;
cdev->current_bind = NULL;
cdev->dev = *composite->dev;
cdev->dev.bMaxPacketSize0 = gadget->ep0->maxpacket;
/* standardized runtime overrides for device ID data */
/*if (idVendor)
cdev->dev.idVendor = idVendor;
if (idProduct)
cdev->dev.idProduct = idProduct;
if (bcdDevice)
cdev->dev.bcdDevice = bcdDevice;
if (cdev->dev.iManufacturer && iManufacturer)
string_override(composite->strings,
cdev->dev.iManufacturer, iManufacturer);
if (cdev->dev.iProduct && iProduct)
string_override(composite->strings,
cdev->dev.iProduct, iProduct);
if (cdev->dev.iSerialNumber && iSerialNumber)
string_override(composite->strings,
cdev->dev.iSerialNumber, iSerialNumber);*/
/* function binding involves updating interface, class, and
* endpoint descriptors:
* - allocating any string IDs needed (interface, class)
* - allocating interface IDs (ditto)
* - allocating endpoints (endpoint, class)
* - allocating non-USB resources (buffers etc)
*
* NOTE: we assume that if there are multiple controllers, they
* are all the same type (e.g. net2280 in pci slots) so functions
* never need different descriptors.
*/
list_for_each_entry(f, &cdev->functions, function) {
if (!f->bind || !f->setup) {
status = -EINVAL;
goto fail;
}
cdev->current_bind = f;
status = f->bind(cdev, f);
if (status < 0)
goto fail;
if ((gadget_is_dualspeed(gadget) && !f->hs_descriptors)
|| !f->descriptors) {
status = -ENXIO;
goto fail;
}
}
cdev->current_bind = NULL;
cdev->config.bNumInterfaces = cdev->next_interface_id;
/* REVISIT eventually we want e.g. RNDIS and non-RNDIS configs,
* at each available device speed ...
*/
cdev->dev.bNumConfigurations = 1;
if (gadget_is_dualspeed(gadget)) {
cdev->qual.bLength = sizeof cdev->qual;
cdev->qual.bDescriptorType = USB_DT_DEVICE_QUALIFIER;
cdev->qual.bcdUSB = cdev->dev.bcdUSB;
cdev->qual.bDeviceClass = cdev->dev.bDeviceClass;
cdev->qual.bDeviceProtocol = cdev->dev.bDeviceProtocol;
/* assume ep0 uses the same value for both speeds ... */
cdev->qual.bMaxPacketSize0 = cdev->dev.bMaxPacketSize0;
cdev->qual.bNumConfigurations = cdev->dev.bNumConfigurations;
}
INFO(cdev, "%s ready\n", composite->name);
return 0;
fail:
composite_unbind(gadget);
return status;
}
/*
* usb_composite_string_id() is called from bind() callbacks to allocate
* a new string ID. The function (or composite gadget) driver will then
* store that ID in the appropriate descriptors and string table.
*/
int usb_composite_string_id(struct usb_composite_dev *cdev)
{
if (cdev->current_bind && cdev->next_string_id < 254) {
/* string id 0 is reserved */
cdev->next_string_id++;
return cdev->next_string_id;
}
return -ENODEV;
}
/*
* usb_composite_interface_id() is called from bind() callbacks to
* allocate a new interface ID. The function (or composite gadget)
* driver will then store that ID in interface, association, cdc union,
* and other appropriate descriptors.
*/
int usb_composite_interface_id(struct usb_composite_dev *cdev)
{
if (cdev->next_interface_id < MAX_COMPOSITE_INTERFACES
&& cdev->current_bind) {
cdev->interface[cdev->next_interface_id] = cdev->current_bind;
return cdev->next_interface_id++;
}
return -ENODEV;
}
/*-------------------------------------------------------------------------*/
static void
composite_suspend(struct usb_gadget *gadget)
{
struct usb_function *f;
DBG(cdev, "suspend\n");
/* revisit -- iterate cdev->interface? */
list_for_each_entry(f, &cdev->functions, function) {
if (!f->suspend)
continue;
f->suspend(cdev);
}
}
static void
composite_resume(struct usb_gadget *gadget)
{
struct usb_function *f;
DBG(cdev, "resume\n");
/* revisit -- iterate cdev->interface? */
list_for_each_entry(f, &cdev->functions, function) {
if (!f->resume)
continue;
f->resume(cdev);
}
}
/*-------------------------------------------------------------------------*/
static struct usb_gadget_driver composite_driver = {
.speed = USB_SPEED_FULL,
.bind = composite_bind,
.unbind = __exit_p(composite_unbind),
.setup = composite_setup,
.disconnect = composite_disconnect,
.suspend = composite_suspend,
.resume = composite_resume,
.driver = {
.owner = THIS_MODULE,
},
};
int usb_composite_register(struct usb_composite_driver *d)
{
if (!d || !d->dev || !d->bind || composite)
return -EINVAL;
if (!d->name)
d->name = "composite";
composite_driver.function = (char *) d->name;
composite_driver.driver.name = d->name;
composite = d;
return usb_gadget_register_driver(&composite_driver);
}
void usb_composite_unregister(struct usb_composite_driver *d)
{
if (composite != d)
return;
usb_gadget_unregister_driver(&composite_driver);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -