⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 g_composite.c

📁 Linux2.6系统下USB2.0复合设备 端驱动器
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 * 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 + -