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

📄 g_composite.c

📁 Linux2.6系统下USB2.0复合设备 端驱动器
💻 C
📖 第 1 页 / 共 2 页
字号:

		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 + -