📄 g_composite.c
字号:
/*
* composite.c - infrastructure for Composite USB Gadgets
*
* Copyright (C) 2006-2007 David Brownell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/usb/g_composite.h>
/*
* The code in this file is utility code, used to build a gadget driver
* from one or more "function" drivers and a "usb_composite_driver" to
* glue them together along with the relevant device-wide data.
*/
#define DBG(comp, fmt, args...) \
dev_dbg(&(comp)->gadget->dev , fmt , ## args)
#define VDBG(comp, fmt, args...) \
dev_vdbg(&(comp)->gadget->dev , fmt , ## args)
#define ERROR(comp, fmt, args...) \
dev_err(&(comp)->gadget->dev , fmt , ## args)
#define WARN(comp, fmt, args...) \
dev_warn(&(comp)->gadget->dev , fmt , ## args)
#define INFO(comp, fmt, args...) \
dev_info(&(comp)->gadget->dev , fmt , ## args)
/* big enough to hold our biggest descriptor */
#define USB_BUFSIZ 512
static struct usb_composite_driver *composite;
static struct usb_composite_dev *cdev;
/* Some systems will need runtime overrides for the product identifers
* published in the device descriptor, either numbers or strings or both.
* String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
*/
#if 0static ushort idVendor;
module_param(idVendor, ushort, S_IRUGO);
MODULE_PARM_DESC(idVendor, "USB Vendor ID");
static ushort idProduct;
module_param(idProduct, ushort, S_IRUGO);
MODULE_PARM_DESC(idProduct, "USB Product ID");
static ushort bcdDevice;
module_param(bcdDevice, ushort, S_IRUGO);
MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
static char *iManufacturer;
module_param(iManufacturer, charp, S_IRUGO);
MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
static char *iProduct;
module_param(iProduct, charp, S_IRUGO);
MODULE_PARM_DESC(iProduct, "USB Product string");
static char *iSerialNumber;
module_param(iSerialNumber, charp, S_IRUGO);
MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
#endif
/*-------------------------------------------------------------------------*/
/* To simplify, we expect to have only ONE real configuration, working the
* same no matter what speed it connects with. A given function may expose
* multiple interfaces. Each interface includes its descriptor, plus optional
* class and endpoint descriptors (as usual).
*
* Note that the configuration numbers are *NOT* related to how many configs
* a device has ... some hardware places restrictions on config numbers.
* So having a single configuration, number 3, would be fine.
*
* REVISIT we do need to lift this restriction, at least for RNDIS.
* For PXA compat, stick to config numbers 1, 2, and 3; for SH, just 1.
*/
#define CONFIG_NUMBER 1
static int
config_buf(void *buf, u8 type)
{
struct usb_config_descriptor *c = buf;
void *next = buf + USB_DT_CONFIG_SIZE;
int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
int hs;
struct usb_function *f;
INFO(cdev, "config buf\n");
if (gadget_is_dualspeed(cdev->gadget)) {
hs = (cdev->gadget->speed == USB_SPEED_HIGH);
if (type == USB_DT_OTHER_SPEED_CONFIG)
hs = !hs;
} else
hs = 0;
/* write a config descriptor */
*c = cdev->config;
c->bLength = USB_DT_CONFIG_SIZE;
c->bDescriptorType = type;
c->bConfigurationValue = CONFIG_NUMBER;
c->iConfiguration = cdev->iConfiguration;
c->bmAttributes = USB_CONFIG_ATT_ONE | cdev->bmAttributes;
c->bMaxPower = cdev->bMaxPower;
INFO(cdev, "device config\n");
/* REVISIT some configurations might need other descriptors,
* independent of the interfaces they implement ... notably
* OTG descriptors.
*/
/* add each function's descriptors */
INFO(cdev, "speed: %d\n",hs);
list_for_each_entry(f, &cdev->functions, function) {
int status;
status = usb_descriptor_fillbuf(next, len,
hs ? f->hs_descriptors : f->descriptors);
if (status < 0)
return status;
len -= status;
next += status;
INFO(cdev, "interface config\n");
}
len = next - buf;
c->wTotalLength = cpu_to_le16(len);
return len;
}
/*-------------------------------------------------------------------------*/
static void composite_reset_config(struct usb_ctrlrequest *req)
{
struct usb_function *f;
int result;
DBG(cdev, "reset config\n");
req->bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
req->bRequest = USB_REQ_SET_CONFIGURATION;
list_for_each_entry(f, &cdev->functions, function) {
result = f->setup(cdev, req,f);
if (result < 0)
DBG(cdev, "reset function %s --> %d\n",
f->name, result);
}
cdev->config.bConfigurationValue = 0;
}
static int
composite_set_config(const struct usb_ctrlrequest *ctrl, unsigned number)
{
int result = 0;
int tmp;
struct usb_gadget *gadget = cdev->gadget;
struct usb_ctrlrequest req;
memset(&req, 0, sizeof req);
/* for now function drivers should assume SET_CONFIGURATION means
* reset/deconfigure, with SET_INTERFACE to each interface used
* to activate some altsetting in "the single" configuration.
*/
composite_reset_config(&req);
switch (number) {
default:
result = -EINVAL;
req.wValue = cpu_to_le16(0);
/* FALLTHROUGH */
case 0:
for (tmp = 0; tmp < MAX_COMPOSITE_INTERFACES; tmp++) {
struct usb_function *f = cdev->interface[tmp];
if (!f)
continue;
f->setup(cdev, ctrl, f);
}
usb_gadget_vbus_draw(gadget, gadget_is_otg(gadget) ? 8 : 100);
break;
case CONFIG_NUMBER:
req.bRequestType = USB_DIR_OUT
| USB_TYPE_STANDARD
| USB_RECIP_INTERFACE;
req.bRequest = USB_REQ_SET_INTERFACE;
for (tmp = 0; tmp < MAX_COMPOSITE_INTERFACES; tmp++) {
struct usb_function *f = cdev->interface[tmp];
if (!f)
continue;
req.wIndex = cpu_to_le16(tmp);
result = f->setup(cdev, &req, f);
if (result < 0) {
DBG(cdev, "interface %d/%s alt 0--> %d\n",
tmp, f->name, result);
(void) composite_set_config(ctrl, 0);
return result;
}
}
cdev->config.bConfigurationValue = number;
usb_gadget_vbus_draw(gadget, 2 * cdev->config.bMaxPower);
break;
}
INFO(cdev, "%s speed config #%d\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);
return result;
}
/*-------------------------------------------------------------------------*/
static void
composite_collect_langs(struct usb_gadget_strings **sp, __le16 *buf)
{
const struct usb_gadget_strings *s;
u16 language;
__le16 *tmp;
while (*sp) {
s = *sp;
language = cpu_to_le16(s->language);
for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) {
if (*tmp == language)
goto repeat;
}
*tmp++ = language;
repeat:
sp++;
}
}
static int composite_check_string(
struct usb_gadget_strings **sp,
void *buf,
u16 language,
int id
)
{
struct usb_gadget_strings *s;
int value;
while (*sp) {
s = *sp++;
if (s->language != language)
continue;
value = usb_gadget_get_string(s, id, buf);
if (value > 0)
return value;
}
return -EINVAL;
}
static int composite_lookup_string(void *buf, u16 language, int id)
{
struct usb_function *f;
int len;
/* 0 == report all available language codes */
if (id == 0) {
struct usb_string_descriptor *s = buf;
struct usb_gadget_strings **sp;
memset(s, 0, 256);
s->bDescriptorType = USB_DT_STRING;
sp = composite->strings;
if (sp)
composite_collect_langs(sp, s->wData);
list_for_each_entry(f, &cdev->functions, function) {
sp = f->strings;
if (sp)
composite_collect_langs(sp, s->wData);
}
for (len = 0; s->wData[len] && len <= 126; len++)
continue;
if (!len)
return -EINVAL;
s->bLength = 2 * (len + 1);
return s->bLength;
}
/* otherwise, look up and return a specified string */
if (composite->strings) {
len = composite_check_string(composite->strings,
buf, language, id);
if (len > 0)
return len;
}
list_for_each_entry(f, &cdev->functions, function) {
if (!f->strings)
continue;
len = composite_check_string(f->strings, buf, language, id);
if (len > 0)
return len;
}
return -EINVAL;
}
/*-------------------------------------------------------------------------*/
static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
{
if (req->status || req->actual != req->length)
DBG((struct usb_composite_dev *) ep->driver_data,
"setup complete --> %d, %d/%d\n",
req->status, req->actual, req->length);
}
/*
* The setup() callback implements all the ep0 functionality that's
* not handled lower down, in hardware or the hardware driver(like
* device and endpoint feature flags, and their status). It's all
* housekeeping for the gadget function we're implementing. Most of
* the work is in config-specific setup.
*/
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);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
int tmp,result;
/* partial re-init of the response message; the function or the
* gadget might need to intercept e.g. a control-OUT completion.
*/
req->zero = 0;
req->complete = composite_setup_complete;
gadget->ep0->driver_data = cdev;
INFO(cdev,
"control req %02x.%02x v %04x i %04x l %d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
if((ctrl->bRequestType&USB_TYPE_MASK) != USB_TYPE_STANDARD)
{
for (tmp = 0; tmp < MAX_COMPOSITE_INTERFACES; tmp++)
{
struct usb_function *f = cdev->interface[tmp];
if (!f)
continue;
result = f->setup(cdev, ctrl, f);
if(result > 0)
value = result;
value = min(w_length, (u16) value);
}
((char *)(req->buf))[value]=0;
INFO(cdev, "Result Len: %d %s\n",value,&req->buf[2]);
}
else
{
switch (ctrl->bRequest) {
case USB_REQ_GET_DESCRIPTOR:
if (ctrl->bRequestType != USB_DIR_IN)
goto unknown;
switch (w_value >> 8) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -