mirror of
https://github.com/torvalds/linux.git
synced 2024-11-18 18:11:56 +00:00
ed9cbda63d
The bcdDevice field is defined as |Device release number in binary-coded decimal in the USB 2.0 specification. We use this field to distinguish the UDCs from each other. In theory this could be used on the host side to apply certain quirks if the "special" UDC in combination with this gadget is used. This hasn't been done as far as I am aware. In practice it would be better to fix the UDC driver before shipping since a later release might not need this quirk anymore. There are some driver in tree (on the host side) which use the bcdDevice field to figure out special workarounds for a given firmware revision. This seems to make sense. Therefore this patch converts all gadgets (except a few) to use the kernel version instead a random 2 or 3 plus the UDC number. The few that don't report kernel's version are: - webcam This one reports always a version 0x10 so allow it to do so in future. - nokia This one reports always 0x211. The comment says that this gadget works only if the UDC supports altsettings so I added a check for this. - serial This one reports 0x2400 + UDC number. Since the gadget version is 2.4 this could make sense. Therefore bcdDevice is 0x2400 here. I also remove various gadget_is_<name> macros which are unused. The remaining few macros should be moved to feature / bug bitfield. Acked-by: Michal Nazarewicz <mina86@mina86.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Signed-off-by: Felipe Balbi <balbi@ti.com>
224 lines
5.5 KiB
C
224 lines
5.5 KiB
C
/*
|
|
* cdc2.c -- CDC Composite driver, with ECM and ACM support
|
|
*
|
|
* Copyright (C) 2008 David Brownell
|
|
* Copyright (C) 2008 Nokia Corporation
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
|
|
#include "u_ether.h"
|
|
#include "u_serial.h"
|
|
|
|
|
|
#define DRIVER_DESC "CDC Composite Gadget"
|
|
#define DRIVER_VERSION "King Kamehameha Day 2008"
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
|
|
* Instead: allocate your own, using normal USB-IF procedures.
|
|
*/
|
|
|
|
/* Thanks to NetChip Technologies for donating this product ID.
|
|
* It's for devices with only this composite CDC configuration.
|
|
*/
|
|
#define CDC_VENDOR_NUM 0x0525 /* NetChip */
|
|
#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
USB_GADGET_COMPOSITE_OPTIONS();
|
|
|
|
/*
|
|
* Kbuild is not very cooperative with respect to linking separately
|
|
* compiled library objects into one module. So for now we won't use
|
|
* separate compilation ... ensuring init/exit sections work to shrink
|
|
* the runtime footprint, and giving us at least some parts of what
|
|
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
|
*/
|
|
|
|
#include "u_serial.c"
|
|
#include "f_acm.c"
|
|
#include "f_ecm.c"
|
|
#include "u_ether.c"
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
static struct usb_device_descriptor device_desc = {
|
|
.bLength = sizeof device_desc,
|
|
.bDescriptorType = USB_DT_DEVICE,
|
|
|
|
.bcdUSB = cpu_to_le16(0x0200),
|
|
|
|
.bDeviceClass = USB_CLASS_COMM,
|
|
.bDeviceSubClass = 0,
|
|
.bDeviceProtocol = 0,
|
|
/* .bMaxPacketSize0 = f(hardware) */
|
|
|
|
/* Vendor and product id can be overridden by module parameters. */
|
|
.idVendor = cpu_to_le16(CDC_VENDOR_NUM),
|
|
.idProduct = cpu_to_le16(CDC_PRODUCT_NUM),
|
|
/* .bcdDevice = f(hardware) */
|
|
/* .iManufacturer = DYNAMIC */
|
|
/* .iProduct = DYNAMIC */
|
|
/* NO SERIAL NUMBER */
|
|
.bNumConfigurations = 1,
|
|
};
|
|
|
|
static struct usb_otg_descriptor otg_descriptor = {
|
|
.bLength = sizeof otg_descriptor,
|
|
.bDescriptorType = USB_DT_OTG,
|
|
|
|
/* REVISIT SRP-only hardware is possible, although
|
|
* it would not be called "OTG" ...
|
|
*/
|
|
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
|
|
};
|
|
|
|
static const struct usb_descriptor_header *otg_desc[] = {
|
|
(struct usb_descriptor_header *) &otg_descriptor,
|
|
NULL,
|
|
};
|
|
|
|
|
|
/* string IDs are assigned dynamically */
|
|
static struct usb_string strings_dev[] = {
|
|
[USB_GADGET_MANUFACTURER_IDX].s = "",
|
|
[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
|
|
[USB_GADGET_SERIAL_IDX].s = "",
|
|
{ } /* end of list */
|
|
};
|
|
|
|
static struct usb_gadget_strings stringtab_dev = {
|
|
.language = 0x0409, /* en-us */
|
|
.strings = strings_dev,
|
|
};
|
|
|
|
static struct usb_gadget_strings *dev_strings[] = {
|
|
&stringtab_dev,
|
|
NULL,
|
|
};
|
|
|
|
static u8 hostaddr[ETH_ALEN];
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
* We _always_ have both CDC ECM and CDC ACM functions.
|
|
*/
|
|
static int __init cdc_do_config(struct usb_configuration *c)
|
|
{
|
|
int status;
|
|
|
|
if (gadget_is_otg(c->cdev->gadget)) {
|
|
c->descriptors = otg_desc;
|
|
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
|
}
|
|
|
|
status = ecm_bind_config(c, hostaddr);
|
|
if (status < 0)
|
|
return status;
|
|
|
|
status = acm_bind_config(c, 0);
|
|
if (status < 0)
|
|
return status;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct usb_configuration cdc_config_driver = {
|
|
.label = "CDC Composite (ECM + ACM)",
|
|
.bConfigurationValue = 1,
|
|
/* .iConfiguration = DYNAMIC */
|
|
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
|
};
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
static int __init cdc_bind(struct usb_composite_dev *cdev)
|
|
{
|
|
struct usb_gadget *gadget = cdev->gadget;
|
|
int status;
|
|
|
|
if (!can_support_ecm(cdev->gadget)) {
|
|
dev_err(&gadget->dev, "controller '%s' not usable\n",
|
|
gadget->name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* set up network link layer */
|
|
status = gether_setup(cdev->gadget, hostaddr);
|
|
if (status < 0)
|
|
return status;
|
|
|
|
/* set up serial link layer */
|
|
status = gserial_setup(cdev->gadget, 1);
|
|
if (status < 0)
|
|
goto fail0;
|
|
|
|
/* Allocate string descriptor numbers ... note that string
|
|
* contents can be overridden by the composite_dev glue.
|
|
*/
|
|
|
|
status = usb_string_ids_tab(cdev, strings_dev);
|
|
if (status < 0)
|
|
goto fail1;
|
|
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
|
|
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
|
|
|
|
/* register our configuration */
|
|
status = usb_add_config(cdev, &cdc_config_driver, cdc_do_config);
|
|
if (status < 0)
|
|
goto fail1;
|
|
|
|
usb_composite_overwrite_options(cdev, &coverwrite);
|
|
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
|
|
DRIVER_DESC);
|
|
|
|
return 0;
|
|
|
|
fail1:
|
|
gserial_cleanup();
|
|
fail0:
|
|
gether_cleanup();
|
|
return status;
|
|
}
|
|
|
|
static int __exit cdc_unbind(struct usb_composite_dev *cdev)
|
|
{
|
|
gserial_cleanup();
|
|
gether_cleanup();
|
|
return 0;
|
|
}
|
|
|
|
static __refdata struct usb_composite_driver cdc_driver = {
|
|
.name = "g_cdc",
|
|
.dev = &device_desc,
|
|
.strings = dev_strings,
|
|
.max_speed = USB_SPEED_HIGH,
|
|
.bind = cdc_bind,
|
|
.unbind = __exit_p(cdc_unbind),
|
|
};
|
|
|
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
|
MODULE_AUTHOR("David Brownell");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
static int __init init(void)
|
|
{
|
|
return usb_composite_probe(&cdc_driver);
|
|
}
|
|
module_init(init);
|
|
|
|
static void __exit cleanup(void)
|
|
{
|
|
usb_composite_unregister(&cdc_driver);
|
|
}
|
|
module_exit(cleanup);
|