9
0
Fork 0

Merge branch 'for-next/usb-gadget'

Conflicts:
	commands/Makefile
	common/Kconfig
	common/Makefile
	drivers/usb/gadget/dfu.c
This commit is contained in:
Sascha Hauer 2014-08-07 13:13:58 +02:00
commit 7782c08047
43 changed files with 4980 additions and 1484 deletions

View File

@ -88,3 +88,58 @@ the following:
The ``dfu-util`` command automatically finds DFU-capable devices. If there are
multiple devices found, you need to identify one with the ``-d``/``-p`` options.
USB serial console
^^^^^^^^^^^^^^^^^^
barebox can provide a serial console over USB. This can be initialized with the
:ref:`command_usbserial` command. Once the host is plugged in it should show a
new serial device, on Linux for example ``/dev/ttyACM0``.
Android Fastboot support
^^^^^^^^^^^^^^^^^^^^^^^^
barebox has support for the android fastboot protocol. There is no dedicated command
for initializing the fastboot protocol, instead it is integrated into the Multifunction
Composite Gadget, see :`ref:command_usbgadget` for a usage description.
The Fastboot gadget supports the following commands:
- fastboot flash
- fastboot getvar
- fastboot boot
- fastboot reboot
**NOTE** ``fastboot erase`` is not yet implemented. This means flashing MTD partitions
does not yet work.
The barebox Fastboot gadget supports the following non standard extensions:
- ``fastboot getvar all``
Shows a list of all variables
- ``fastboot oem getenv <varname>``
Shows a barebox environment variable
- ``fastboot oem setenv <varname>=<value>``
Sets a barebox environment variable
- ``fastboot oem exec <cmd>``
executes a shell command. Note the output can't be seen on the host, but the fastboot
command returns successfully when the barebox command was successful and it fails when
the barebox command fails.
USB Composite Multifunction Gadget
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
With the Composite Multifunction Gadget it is possible to create a USB device with
multiple functions. A useful combination is creating a Fastboot gadget and a USB serial
console. This combination can be created with:
.. code-block:: sh
usbgadget -A /dev/mmc2.0(root),/dev/mmc2.1(data) -a
The ``-A`` option will create a Fastboot function providing ``/dev/mmc2.0`` as root
partition and ``/dev/mmc2.1`` as data partition. The ``-a`` option will create a
USB CDC ACM compliant serial device.
Unlike the :ref:`command_dfu` command the ``usbgadget`` command returns immediately
after creating the gadget. The gadget can be removed with ``usbgadget -d``.

View File

@ -1848,6 +1848,11 @@ config CMD_USB
Options:
-f force rescan
config CMD_USBGADGET
bool
depends on USB_GADGET
prompt "usbgadget"
config CMD_WD
bool
depends on WATCHDOG

View File

@ -102,3 +102,4 @@ obj-$(CONFIG_CMD_REGULATOR) += regulator.o
obj-$(CONFIG_CMD_LSPCI) += lspci.o
obj-$(CONFIG_CMD_IMD) += imd.o
obj-$(CONFIG_CMD_HWCLOCK) += hwclock.o
obj-$(CONFIG_CMD_USBGADGET) += usbgadget.o

View File

@ -24,71 +24,7 @@
#include <fs.h>
#include <xfuncs.h>
#include <usb/dfu.h>
#define PARSE_DEVICE 0
#define PARSE_NAME 1
#define PARSE_FLAGS 2
static int dfu_do_parse_one(char *partstr, char **endstr, struct usb_dfu_dev *dfu)
{
int i = 0, state = PARSE_DEVICE;
char device[PATH_MAX];
char name[PATH_MAX];
memset(device, 0, sizeof(device));
memset(name, 0, sizeof(name));
dfu->flags = 0;
while (*partstr && *partstr != ',') {
switch (state) {
case PARSE_DEVICE:
if (*partstr == '(') {
state = PARSE_NAME;
i = 0;
} else {
device[i++] = *partstr;
}
break;
case PARSE_NAME:
if (*partstr == ')') {
state = PARSE_FLAGS;
i = 0;
} else {
name[i++] = *partstr;
}
break;
case PARSE_FLAGS:
switch (*partstr) {
case 's':
dfu->flags |= DFU_FLAG_SAFE;
break;
case 'r':
dfu->flags |= DFU_FLAG_READBACK;
break;
case 'c':
dfu->flags |= DFU_FLAG_CREATE;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
partstr++;
}
if (state != PARSE_FLAGS)
return -EINVAL;
dfu->name = xstrdup(name);
dfu->dev = xstrdup(device);
if (*partstr == ',')
partstr++;
*endstr = partstr;
return 0;
}
#include <linux/err.h>
/* dfu /dev/self0(bootloader)sr,/dev/nand0.root.bb(root)
*
@ -97,73 +33,26 @@ static int dfu_do_parse_one(char *partstr, char **endstr, struct usb_dfu_dev *df
*/
static int do_dfu(int argc, char *argv[])
{
int opt, n = 0;
struct usb_dfu_pdata pdata;
char *endptr, *argstr;
struct f_dfu_opts opts;
char *argstr;
struct usb_dfu_dev *dfu_alts = NULL;
char *manufacturer = "barebox";
const char *productname = barebox_get_model();
u16 idVendor = 0, idProduct = 0;
int ret;
while((opt = getopt(argc, argv, "m:p:V:P:")) > 0) {
switch(opt) {
case 'm':
manufacturer = optarg;
break;
case 'p':
productname = optarg;
break;
case 'V':
idVendor = simple_strtoul(optarg, NULL, 0);
break;
case 'P':
idProduct = simple_strtoul(optarg, NULL, 0);
break;
}
}
if (argc != optind + 1)
return COMMAND_ERROR_USAGE;
argstr = argv[optind];
if (!idProduct && !idVendor) {
idVendor = 0x1d50; /* Openmoko, Inc */
idProduct = 0x60a2; /* barebox bootloader USB DFU Mode */
opts.files = file_list_parse(argstr);
if (IS_ERR(opts.files)) {
ret = PTR_ERR(opts.files);
goto out;
}
if ((idProduct && !idVendor) || (!idProduct && idVendor)) {
printf("Only one of vendor id or product id given\n");
return -EINVAL;
}
for (n = 0; *argstr; n++) {
dfu_alts = xrealloc(dfu_alts, sizeof(*dfu_alts) * (n + 1));
if (dfu_do_parse_one(argstr, &endptr, &dfu_alts[n])) {
printf("parse error\n");
ret = -EINVAL;
goto out;
}
argstr = endptr;
}
pdata.alts = dfu_alts;
pdata.num_alts = n;
pdata.manufacturer = manufacturer;
pdata.productname = productname;
pdata.idVendor = idVendor;
pdata.idProduct = idProduct;
ret = usb_dfu_register(&pdata);
ret = usb_dfu_register(&opts);
file_list_free(opts.files);
out:
while (n) {
n--;
free(dfu_alts[n].name);
free(dfu_alts[n].dev);
};
free(dfu_alts);
@ -182,17 +71,12 @@ BAREBOX_CMD_HELP_TEXT("'s': safe mode (download the complete image before flashi
BAREBOX_CMD_HELP_TEXT("'r': readback of the firmware is allowed; ")
BAREBOX_CMD_HELP_TEXT("'c': the file will be created (for use with regular files).")
BAREBOX_CMD_HELP_TEXT("")
BAREBOX_CMD_HELP_TEXT("Options:")
BAREBOX_CMD_HELP_OPT ("-m STR", "Manufacturer string (barebox)")
BAREBOX_CMD_HELP_OPT ("-p STR", "product string")
BAREBOX_CMD_HELP_OPT ("-V ID", "vendor id")
BAREBOX_CMD_HELP_OPT ("-P ID", "product id")
BAREBOX_CMD_HELP_END
BAREBOX_CMD_START(dfu)
.cmd = do_dfu,
BAREBOX_CMD_DESC("device firmware update")
BAREBOX_CMD_OPTS("[-mpVP] DESC")
BAREBOX_CMD_OPTS("DESC")
BAREBOX_CMD_GROUP(CMD_GRP_MISC)
BAREBOX_CMD_HELP(cmd_dfu_help)
BAREBOX_CMD_END

108
commands/usbgadget.c Normal file
View File

@ -0,0 +1,108 @@
/*
* usbserial.c - usb serial gadget command
*
* Copyright (c) 2011 Eric Bénard <eric@eukrea.com>, Eukréa Electromatique
* based on dfu.c which is :
* Copyright (c) 2009 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* 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.
*
*/
#include <common.h>
#include <command.h>
#include <errno.h>
#include <malloc.h>
#include <getopt.h>
#include <fs.h>
#include <xfuncs.h>
#include <usb/usbserial.h>
#include <usb/dfu.h>
#include <usb/gadget-multi.h>
static int do_usbgadget(int argc, char *argv[])
{
int opt;
int acm = 1, create_serial = 0;
char *fastboot_opts = NULL, *dfu_opts = NULL;
struct f_multi_opts opts = {};
while ((opt = getopt(argc, argv, "asdA:D:")) > 0) {
switch (opt) {
case 'a':
acm = 1;
create_serial = 1;
break;
case 's':
acm = 0;
create_serial = 1;
break;
case 'D':
dfu_opts = optarg;
break;
case 'A':
fastboot_opts = optarg;
break;
case 'd':
usb_multi_unregister();
return 0;
default:
return -EINVAL;
}
}
if (!dfu_opts && !fastboot_opts && !create_serial)
return COMMAND_ERROR_USAGE;
/*
* Creating a gadget with both DFU and Fastboot doesn't work.
* Both client tools seem to assume that the device only has
* a single configuration
*/
if (fastboot_opts && dfu_opts) {
printf("Only one of Fastboot and DFU allowed\n");
return -EINVAL;
}
if (fastboot_opts) {
opts.fastboot_opts.files = file_list_parse(fastboot_opts);
}
if (dfu_opts) {
opts.dfu_opts.files = file_list_parse(dfu_opts);
}
if (create_serial) {
opts.create_acm = acm;
}
return usb_multi_register(&opts);
}
BAREBOX_CMD_HELP_START(usbgadget)
BAREBOX_CMD_HELP_TEXT("Enable / disable a USB composite gadget on the USB device interface.")
BAREBOX_CMD_HELP_TEXT("")
BAREBOX_CMD_HELP_TEXT("Options:")
BAREBOX_CMD_HELP_OPT ("-a", "Create CDC ACM function")
BAREBOX_CMD_HELP_OPT ("-s", "Create Generic Serial function")
BAREBOX_CMD_HELP_OPT ("-A <desc>", "Create Android Fastboot function")
BAREBOX_CMD_HELP_OPT ("-D <desc>", "Create DFU function")
BAREBOX_CMD_HELP_OPT ("-d", "Disable the serial gadget")
BAREBOX_CMD_HELP_END
BAREBOX_CMD_START(usbgadget)
.cmd = do_usbgadget,
BAREBOX_CMD_DESC("Create USB Gadget multifunction device")
BAREBOX_CMD_OPTS("[-asdAD]")
BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
BAREBOX_CMD_HELP(cmd_usbgadget_help)
BAREBOX_CMD_END

View File

@ -31,35 +31,15 @@ static int do_usbserial(int argc, char *argv[])
{
int opt;
struct usb_serial_pdata pdata;
char *manufacturer = "barebox";
const char *productname = barebox_get_model();
u16 idVendor = 0, idProduct = 0;
int mode = 0;
int acm = 1;
while ((opt = getopt(argc, argv, "m:p:V:P:asd")) > 0) {
while ((opt = getopt(argc, argv, "asd")) > 0) {
switch (opt) {
case 'm':
manufacturer = optarg;
break;
case 'p':
productname = optarg;
break;
case 'V':
idVendor = simple_strtoul(optarg, NULL, 0);
break;
case 'P':
idProduct = simple_strtoul(optarg, NULL, 0);
break;
case 'a':
mode = 0;
acm = 1;
break;
#ifdef HAVE_OBEX
case 'o':
mode = 1;
break;
#endif
case 's':
mode = 2;
acm = 0;
break;
case 'd':
usb_serial_unregister();
@ -67,11 +47,7 @@ static int do_usbserial(int argc, char *argv[])
}
}
pdata.manufacturer = manufacturer;
pdata.productname = productname;
pdata.idVendor = idVendor;
pdata.idProduct = idProduct;
pdata.mode = mode;
pdata.acm = acm;
return usb_serial_register(&pdata);
}
@ -80,14 +56,7 @@ BAREBOX_CMD_HELP_START(usbserial)
BAREBOX_CMD_HELP_TEXT("Enable / disable a serial gadget on the USB device interface.")
BAREBOX_CMD_HELP_TEXT("")
BAREBOX_CMD_HELP_TEXT("Options:")
BAREBOX_CMD_HELP_OPT ("-m STR", "Manufacturer string (barebox)")
BAREBOX_CMD_HELP_OPT ("-p STR", "product string")
BAREBOX_CMD_HELP_OPT ("-V ID", "vendor id")
BAREBOX_CMD_HELP_OPT ("-P ID", "product id")
BAREBOX_CMD_HELP_OPT ("-a", "CDC ACM (default)")
#ifdef HAVE_OBEX
BAREBOX_CMD_HELP_OPT ("-o", "CDC OBEX")
#endif
BAREBOX_CMD_HELP_OPT ("-s", "Generic Serial")
BAREBOX_CMD_HELP_OPT ("-d", "Disable the serial gadget")
BAREBOX_CMD_HELP_END
@ -95,11 +64,7 @@ BAREBOX_CMD_HELP_END
BAREBOX_CMD_START(usbserial)
.cmd = do_usbserial,
BAREBOX_CMD_DESC("serial gadget enable/disable")
BAREBOX_CMD_OPTS("[-mpVPa"
#ifdef HAVE_OBEX
"o"
#endif
"sd] <description>")
BAREBOX_CMD_OPTS("[-asd] <description>")
BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
BAREBOX_CMD_HELP(cmd_usbserial_help)
BAREBOX_CMD_END

View File

@ -88,6 +88,9 @@ config EFI_GUID
config EFI_DEVICEPATH
bool
config FILE_LIST
bool
menu "General Settings"
config LOCALVERSION

View File

@ -48,6 +48,7 @@ obj-$(CONFIG_EFI_GUID) += efi-guid.o
obj-$(CONFIG_EFI_DEVICEPATH) += efi-devicepath.o
lwl-$(CONFIG_IMD) += imd-barebox.o
obj-$(CONFIG_IMD) += imd.o
obj-$(CONFIG_FILE_LIST) += file-list.o
quiet_cmd_pwd_h = PWDH $@
ifdef CONFIG_PASSWORD

113
common/file-list.c Normal file
View File

@ -0,0 +1,113 @@
#include <common.h>
#include <malloc.h>
#include <fs.h>
#include <file-list.h>
#include <linux/err.h>
#define PARSE_DEVICE 0
#define PARSE_NAME 1
#define PARSE_FLAGS 2
static int file_list_parse_one(struct file_list *files, const char *partstr, const char **endstr)
{
int i = 0, state = PARSE_DEVICE;
char filename[PATH_MAX];
char name[PATH_MAX];
struct file_list_entry *entry = xzalloc(sizeof(*entry));
memset(filename, 0, sizeof(filename));
memset(name, 0, sizeof(name));
while (*partstr && *partstr != ',') {
switch (state) {
case PARSE_DEVICE:
if (*partstr == '(') {
state = PARSE_NAME;
i = 0;
} else {
filename[i++] = *partstr;
}
break;
case PARSE_NAME:
if (*partstr == ')') {
state = PARSE_FLAGS;
i = 0;
} else {
name[i++] = *partstr;
}
break;
case PARSE_FLAGS:
switch (*partstr) {
case 's':
entry->flags |= FILE_LIST_FLAG_SAFE;
break;
case 'r':
entry->flags |= FILE_LIST_FLAG_READBACK;
break;
case 'c':
entry->flags |= FILE_LIST_FLAG_CREATE;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
partstr++;
}
if (state != PARSE_FLAGS)
return -EINVAL;
entry->name = xstrdup(name);
entry->filename = xstrdup(filename);
if (*partstr == ',')
partstr++;
*endstr = partstr;
list_add_tail(&entry->list, &files->list);
return 0;
}
struct file_list *file_list_parse(const char *str)
{
struct file_list *files;
int ret;
const char *endptr;
files = xzalloc(sizeof(*files));
INIT_LIST_HEAD(&files->list);
while (*str) {
if (file_list_parse_one(files, str, &endptr)) {
printf("parse error\n");
ret = -EINVAL;
goto out;
}
str = endptr;
files->num_entries++;
}
return files;
out:
free(files);
return ERR_PTR(ret);
}
void file_list_free(struct file_list *files)
{
struct file_list_entry *entry, *tmp;
list_for_each_entry_safe(entry, tmp, &files->list, list) {
free(entry->name);
free(entry->filename);
free(entry);
}
free(files);
}

View File

@ -6,6 +6,10 @@ const char version_string[] =
"barebox " UTS_RELEASE " " UTS_VERSION "\n";
EXPORT_SYMBOL(version_string);
const char release_string[] =
"barebox-" UTS_RELEASE;
EXPORT_SYMBOL(release_string);
void barebox_banner (void)
{
pr_info("\n\n%s\n\n", version_string);

View File

@ -1,3 +1,4 @@
obj-$(CONFIG_USB_HOST) += usb.o
obj-$(CONFIG_USB) += common.o
obj-$(CONFIG_OFDEVICE) += of.o

19
drivers/usb/core/common.c Normal file
View File

@ -0,0 +1,19 @@
#include <common.h>
#include <usb/ch9.h>
static const char *const speed_names[] = {
[USB_SPEED_UNKNOWN] = "UNKNOWN",
[USB_SPEED_LOW] = "low-speed",
[USB_SPEED_FULL] = "full-speed",
[USB_SPEED_HIGH] = "high-speed",
[USB_SPEED_WIRELESS] = "wireless",
[USB_SPEED_SUPER] = "super-speed",
};
const char *usb_speed_string(enum usb_device_speed speed)
{
if (speed < 0 || speed >= ARRAY_SIZE(speed_names))
speed = USB_SPEED_UNKNOWN;
return speed_names[speed];
}
EXPORT_SYMBOL_GPL(usb_speed_string);

View File

@ -51,6 +51,7 @@
#include <dma.h>
#include <usb/usb.h>
#include <usb/ch9.h>
/* #define USB_DEBUG */

View File

@ -41,12 +41,16 @@ comment "USB Gadget drivers"
config USB_GADGET_DFU
bool
select FILE_LIST
prompt "Device Firmware Update Gadget"
config USB_GADGET_SERIAL
bool
depends on EXPERIMENTAL && !CONSOLE_NONE
depends on !CONSOLE_NONE
prompt "Serial Gadget"
endif
config USB_GADGET_FASTBOOT
bool
prompt "Android Fastboot support"
endif

View File

@ -1,7 +1,8 @@
obj-$(CONFIG_USB_GADGET) += composite.o config.o usbstring.o epautoconf.o
obj-$(CONFIG_USB_GADGET) += composite.o config.o usbstring.o epautoconf.o udc-core.o functions.o config.o multi.o
obj-$(CONFIG_USB_GADGET_SERIAL) += u_serial.o serial.o f_serial.o f_acm.o
obj-$(CONFIG_USB_GADGET_DFU) += dfu.o
obj-$(CONFIG_USB_GADGET_FASTBOOT) += f_fastboot.o
obj-$(CONFIG_USB_GADGET_DRIVER_ARC) += fsl_udc.o
obj-$(CONFIG_USB_GADGET_DRIVER_AT91) += at91_udc.o
obj-$(CONFIG_USB_GADGET_DRIVER_PXA27X) += pxa27x_udc.o

View File

@ -749,20 +749,6 @@ static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on)
return 0;
}
static const struct usb_gadget_ops at91_udc_ops = {
.get_frame = at91_get_frame,
.wakeup = at91_wakeup,
.set_selfpowered = at91_set_selfpowered,
.vbus_session = at91_vbus_session,
.pullup = at91_pullup,
/*
* VBUS-powered devices may also also want to support bigger
* power budgets after an appropriate SET_CONFIGURATION.
*/
/* .vbus_power = at91_vbus_power, */
};
/*-------------------------------------------------------------------------*/
static int handle_ep(struct at91_ep *ep)
@ -1244,6 +1230,49 @@ static void at91_udc_irq (void *_udc)
}
}
static int at91_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver)
{
struct at91_udc *udc = container_of(gadget, struct at91_udc, gadget);
if (!udc->iclk)
return -ENODEV;
udc->driver = driver;
udc->enabled = 1;
udc->selfpowered = 1;
DBG(udc, "bound to %s\n", driver->function);
return 0;
}
static int at91_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver)
{
struct at91_udc *udc = container_of(gadget, struct at91_udc, gadget);
udc->enabled = 0;
at91_udp_write(udc, AT91_UDP_IDR, ~0);
udc->driver = NULL;
DBG(udc, "unbound from %s\n", driver->function);
return 0;
}
static const struct usb_gadget_ops at91_udc_ops = {
.get_frame = at91_get_frame,
.wakeup = at91_wakeup,
.set_selfpowered = at91_set_selfpowered,
.vbus_session = at91_vbus_session,
.pullup = at91_pullup,
/*
* VBUS-powered devices may also also want to support bigger
* power budgets after an appropriate SET_CONFIGURATION.
*/
/* .vbus_power = at91_vbus_power, */
.udc_start = at91_udc_start,
.udc_stop = at91_udc_stop,
};
/*-------------------------------------------------------------------------*/
static struct at91_udc controller = {
@ -1346,66 +1375,6 @@ int usb_gadget_poll(void)
return value;
}
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
struct at91_udc *udc = &controller;
int retval;
if (!udc->iclk)
return -ENODEV;
if (!driver
|| driver->speed < USB_SPEED_FULL
|| !driver->bind
|| !driver->setup) {
DBG(udc, "bad parameter.\n");
return -EINVAL;
}
if (udc->driver) {
DBG(udc, "UDC already has a gadget driver\n");
return -EBUSY;
}
udc->driver = driver;
udc->enabled = 1;
udc->selfpowered = 1;
retval = driver->bind(&udc->gadget);
if (retval) {
DBG(udc, "bind() returned %d\n", retval);
udc->driver = NULL;
udc->enabled = 0;
udc->selfpowered = 0;
return retval;
}
pullup(udc, 1);
DBG(udc, "bound to %s\n", driver->function);
return 0;
}
EXPORT_SYMBOL (usb_gadget_register_driver);
int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
{
struct at91_udc *udc = &controller;
if (!driver || driver != udc->driver || !driver->unbind)
return -EINVAL;
udc->enabled = 0;
at91_udp_write(udc, AT91_UDP_IDR, ~0);
pullup(udc, 0);
driver->unbind(&udc->gadget);
udc->driver = NULL;
DBG(udc, "unbound from %s\n", driver->function);
return 0;
}
EXPORT_SYMBOL (usb_gadget_unregister_driver);
/*-------------------------------------------------------------------------*/
static void at91_udc_poller(struct poller_struct *poller)
@ -1513,6 +1482,10 @@ static int __init at91udc_probe(struct device_d *dev)
poller_register(&poller);
retval = usb_add_gadget_udc_release(dev, &udc->gadget, NULL);
if (retval)
goto fail0a;
INFO(udc, "%s version %s\n", driver_name, DRIVER_VERSION);
return 0;

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,19 @@
/*
* usb/gadget/config.c -- simplify building config descriptors
*
* Copyright (C) 2003 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.
*/
#include <common.h>
#include <malloc.h>
#include <errno.h>
#include <usb/ch9.h>
#include <usb/gadget.h>
#include <usb/composite.h>
/**
* usb_descriptor_fillbuf - fill buffer with descriptors
@ -36,6 +48,60 @@ usb_descriptor_fillbuf(void *buf, unsigned buflen,
}
return dest - (u8 *)buf;
}
EXPORT_SYMBOL_GPL(usb_descriptor_fillbuf);
/**
* usb_gadget_config_buf - builts a complete configuration descriptor
* @config: Header for the descriptor, including characteristics such
* as power requirements and number of interfaces.
* @desc: Null-terminated vector of pointers to the descriptors (interface,
* endpoint, etc) defining all functions in this device configuration.
* @buf: Buffer for the resulting configuration descriptor.
* @length: Length of buffer. If this is not big enough to hold the
* entire configuration descriptor, an error code will be returned.
*
* This copies descriptors into the response buffer, building a descriptor
* for that configuration. It returns the buffer length or a negative
* status code. The config.wTotalLength field is set to match the length
* of the result, but other descriptor fields (including power usage and
* interface count) must be set by the caller.
*
* Gadget drivers could use this when constructing a config descriptor
* in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the
* resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
*/
int usb_gadget_config_buf(
const struct usb_config_descriptor *config,
void *buf,
unsigned length,
const struct usb_descriptor_header **desc
)
{
struct usb_config_descriptor *cp = buf;
int len;
/* config descriptor first */
if (length < USB_DT_CONFIG_SIZE || !desc)
return -EINVAL;
*cp = *config;
/* then interface/endpoint/class/vendor/... */
len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
length - USB_DT_CONFIG_SIZE, desc);
if (len < 0)
return len;
len += USB_DT_CONFIG_SIZE;
if (len > 0xffff)
return -EINVAL;
/* patch up the config descriptor */
cp->bLength = USB_DT_CONFIG_SIZE;
cp->bDescriptorType = USB_DT_CONFIG;
cp->wTotalLength = cpu_to_le16(len);
cp->bmAttributes |= USB_CONFIG_ATT_ONE;
return len;
}
EXPORT_SYMBOL_GPL(usb_gadget_config_buf);
/**
* usb_copy_descriptors - copy a vector of USB descriptors
@ -49,7 +115,7 @@ usb_descriptor_fillbuf(void *buf, unsigned buflen,
* with identifiers (for interfaces, strings, endpoints, and more)
* as needed by a given function instance.
*/
struct usb_descriptor_header **__init
struct usb_descriptor_header **
usb_copy_descriptors(struct usb_descriptor_header **src)
{
struct usb_descriptor_header **tmp;
@ -85,29 +151,41 @@ usb_copy_descriptors(struct usb_descriptor_header **src)
return ret;
}
EXPORT_SYMBOL_GPL(usb_copy_descriptors);
/**
* usb_find_endpoint - find a copy of an endpoint descriptor
* @src: original vector of descriptors
* @copy: copy of @src
* @match: endpoint descriptor found in @src
*
* This returns the copy of the @match descriptor made for @copy. Its
* intended use is to help remembering the endpoint descriptor to use
* when enabling a given endpoint.
*/
struct usb_endpoint_descriptor *__init
usb_find_endpoint(
struct usb_descriptor_header **src,
struct usb_descriptor_header **copy,
struct usb_endpoint_descriptor *match
)
int usb_assign_descriptors(struct usb_function *f,
struct usb_descriptor_header **fs,
struct usb_descriptor_header **hs,
struct usb_descriptor_header **ss)
{
while (*src) {
if (*src == (void *) match)
return (void *)*copy;
src++;
copy++;
struct usb_gadget *g = f->config->cdev->gadget;
if (fs) {
f->fs_descriptors = usb_copy_descriptors(fs);
if (!f->fs_descriptors)
goto err;
}
return NULL;
if (hs && gadget_is_dualspeed(g)) {
f->hs_descriptors = usb_copy_descriptors(hs);
if (!f->hs_descriptors)
goto err;
}
if (ss && gadget_is_superspeed(g)) {
f->ss_descriptors = usb_copy_descriptors(ss);
if (!f->ss_descriptors)
goto err;
}
return 0;
err:
usb_free_all_descriptors(f);
return -ENOMEM;
}
EXPORT_SYMBOL_GPL(usb_assign_descriptors);
void usb_free_all_descriptors(struct usb_function *f)
{
usb_free_descriptors(f->fs_descriptors);
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->ss_descriptors);
}
EXPORT_SYMBOL_GPL(usb_free_all_descriptors);

View File

@ -44,6 +44,7 @@
#include <usb/gadget.h>
#include <linux/stat.h>
#include <libfile.h>
#include <linux/err.h>
#include <usb/ch9.h>
#include <usb/dfu.h>
#include <config.h>
@ -55,16 +56,81 @@
#include <init.h>
#include <fs.h>
#define USB_DT_DFU 0x21
struct usb_dfu_func_descriptor {
u_int8_t bLength;
u_int8_t bDescriptorType;
u_int8_t bmAttributes;
#define USB_DFU_CAN_DOWNLOAD (1 << 0)
#define USB_DFU_CAN_UPLOAD (1 << 1)
#define USB_DFU_MANIFEST_TOL (1 << 2)
#define USB_DFU_WILL_DETACH (1 << 3)
u_int16_t wDetachTimeOut;
u_int16_t wTransferSize;
u_int16_t bcdDFUVersion;
} __attribute__ ((packed));
#define USB_DT_DFU_SIZE 9
#define USB_TYPE_DFU (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
/* DFU class-specific requests (Section 3, DFU Rev 1.1) */
#define USB_REQ_DFU_DETACH 0x00
#define USB_REQ_DFU_DNLOAD 0x01
#define USB_REQ_DFU_UPLOAD 0x02
#define USB_REQ_DFU_GETSTATUS 0x03
#define USB_REQ_DFU_CLRSTATUS 0x04
#define USB_REQ_DFU_GETSTATE 0x05
#define USB_REQ_DFU_ABORT 0x06
struct dfu_status {
u_int8_t bStatus;
u_int8_t bwPollTimeout[3];
u_int8_t bState;
u_int8_t iString;
} __attribute__((packed));
#define DFU_STATUS_OK 0x00
#define DFU_STATUS_errTARGET 0x01
#define DFU_STATUS_errFILE 0x02
#define DFU_STATUS_errWRITE 0x03
#define DFU_STATUS_errERASE 0x04
#define DFU_STATUS_errCHECK_ERASED 0x05
#define DFU_STATUS_errPROG 0x06
#define DFU_STATUS_errVERIFY 0x07
#define DFU_STATUS_errADDRESS 0x08
#define DFU_STATUS_errNOTDONE 0x09
#define DFU_STATUS_errFIRMWARE 0x0a
#define DFU_STATUS_errVENDOR 0x0b
#define DFU_STATUS_errUSBR 0x0c
#define DFU_STATUS_errPOR 0x0d
#define DFU_STATUS_errUNKNOWN 0x0e
#define DFU_STATUS_errSTALLEDPKT 0x0f
enum dfu_state {
DFU_STATE_appIDLE = 0,
DFU_STATE_appDETACH = 1,
DFU_STATE_dfuIDLE = 2,
DFU_STATE_dfuDNLOAD_SYNC = 3,
DFU_STATE_dfuDNBUSY = 4,
DFU_STATE_dfuDNLOAD_IDLE = 5,
DFU_STATE_dfuMANIFEST_SYNC = 6,
DFU_STATE_dfuMANIFEST = 7,
DFU_STATE_dfuMANIFEST_WAIT_RST = 8,
DFU_STATE_dfuUPLOAD_IDLE = 9,
DFU_STATE_dfuERROR = 10,
};
#define USB_DT_DFU_SIZE 9
#define USB_DT_DFU 0x21
#define CONFIG_USBD_DFU_XFER_SIZE 4096
#define DFU_TEMPFILE "/dfu_temp"
static int dfualt;
struct file_list_entry *dfu_file_entry;
static int dfufd = -EINVAL;
static struct usb_dfu_dev *dfu_devs;
static int dfu_num_alt;
static struct file_list *dfu_files;
static int dfudetach;
/* USB DFU functional descriptor */
@ -103,35 +169,64 @@ static struct usb_gadget_strings *dfu_strings[] = {
NULL,
};
static struct usb_interface_descriptor dfu_control_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bNumEndpoints = 0,
.bInterfaceClass = 0xfe,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 1,
/* .iInterface = DYNAMIC */
};
static void dn_complete(struct usb_ep *ep, struct usb_request *req);
static int
dfu_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct usb_descriptor_header **header;
struct usb_interface_descriptor *desc;
struct file_list_entry *fentry;
struct f_dfu *dfu = container_of(f, struct f_dfu, func);
int i;
int status;
struct usb_string *us;
if (!dfu_files) {
const struct usb_function_instance *fi = f->fi;
struct f_dfu_opts *opts = container_of(fi, struct f_dfu_opts, func_inst);
dfu_files = opts->files;
}
dfu_string_defs = xzalloc(sizeof(struct usb_string) * (dfu_files->num_entries + 2));
dfu_string_defs[0].s = "Generic DFU";
i = 0;
file_list_for_each_entry(dfu_files, fentry) {
dfu_string_defs[i + 1].s = fentry->name;
i++;
}
dfu_string_defs[i + 1].s = NULL;
dfu_string_table.strings = dfu_string_defs;
dfu->dfu_state = DFU_STATE_dfuIDLE;
dfu->dfu_status = DFU_STATUS_OK;
dfu->dnreq = usb_ep_alloc_request(c->cdev->gadget->ep0);
if (!dfu->dnreq) {
printf("usb_ep_alloc_request failed\n");
goto out;
}
dfu->dnreq->buf = dma_alloc(CONFIG_USBD_DFU_XFER_SIZE);
dfu->dnreq->complete = dn_complete;
dfu->dnreq->zero = 0;
us = usb_gstrings_attach(cdev, dfu_strings, dfu_files->num_entries + 1);
if (IS_ERR(us)) {
status = PTR_ERR(us);
goto out;
}
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
return status;
goto out;
dfu_control_interface_desc.bInterfaceNumber = status;
header = xzalloc(sizeof(void *) * (dfu_num_alt + 2));
desc = xzalloc(sizeof(struct usb_interface_descriptor) * dfu_num_alt);
for (i = 0; i < dfu_num_alt; i++) {
header = xzalloc(sizeof(void *) * (dfu_files->num_entries + 2));
desc = xzalloc(sizeof(struct usb_interface_descriptor) * dfu_files->num_entries);
for (i = 0; i < dfu_files->num_entries; i++) {
desc[i].bLength = USB_DT_INTERFACE_SIZE;
desc[i].bDescriptorType = USB_DT_INTERFACE;
desc[i].bNumEndpoints = 0;
@ -139,33 +234,31 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f)
desc[i].bInterfaceSubClass = 1;
desc[i].bInterfaceProtocol = 1;
desc[i].bAlternateSetting = i;
desc[i].iInterface = dfu_string_defs[i + 1].id;
desc[i].iInterface = us[i + 1].id;
header[i] = (struct usb_descriptor_header *)&desc[i];
}
header[i] = (struct usb_descriptor_header *) &usb_dfu_func;
header[i + 1] = NULL;
/* copy descriptors, and track endpoint copies */
f->descriptors = usb_copy_descriptors(header);
if (!f->descriptors)
goto out;
status = usb_assign_descriptors(f, header, header, NULL);
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
if (gadget_is_dualspeed(c->cdev->gadget)) {
/* copy descriptors, and track endpoint copies */
f->hs_descriptors = usb_copy_descriptors(header);
}
for (i = 0; i < dfu_num_alt; i++)
printf("dfu: register alt%d(%s) with device %s\n",
i, dfu_devs[i].name, dfu_devs[i].dev);
out:
free(desc);
free(header);
if (status)
goto out;
i = 0;
file_list_for_each_entry(dfu_files, fentry) {
printf("dfu: register alt%d(%s) with device %s\n",
i, fentry->name, fentry->filename);
i++;
}
return 0;
out:
free(dfu_string_defs);
if (status)
ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
@ -177,20 +270,27 @@ dfu_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_dfu *dfu = func_to_dfu(f);
free(f->descriptors);
if (gadget_is_dualspeed(c->cdev->gadget))
free(f->hs_descriptors);
usb_free_all_descriptors(f);
dma_free(dfu->dnreq->buf);
usb_ep_free_request(c->cdev->gadget->ep0, dfu->dnreq);
free(dfu);
}
static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
dfualt = alt;
struct file_list_entry *fentry;
int i = 0;
return 0;
file_list_for_each_entry(dfu_files, fentry) {
if (i == alt) {
dfu_file_entry = fentry;
return 0;
}
i++;
}
return -EINVAL;
}
static int dfu_status(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
@ -245,14 +345,14 @@ static int handle_dnload(struct usb_function *f, const struct usb_ctrlrequest *c
if (w_length == 0) {
dfu->dfu_state = DFU_STATE_dfuIDLE;
if (dfu_devs[dfualt].flags & DFU_FLAG_SAFE) {
if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) {
int fd;
unsigned flags = O_WRONLY;
if (dfu_devs[dfualt].flags & DFU_FLAG_CREATE)
if (dfu_file_entry->flags & FILE_LIST_FLAG_CREATE)
flags |= O_CREAT | O_TRUNC;
fd = open(dfu_devs[dfualt].dev, flags);
fd = open(dfu_file_entry->filename, flags);
if (fd < 0) {
perror("open");
ret = -EINVAL;
@ -265,7 +365,7 @@ static int handle_dnload(struct usb_function *f, const struct usb_ctrlrequest *c
ret = -EINVAL;
goto err_out;
}
ret = copy_file(DFU_TEMPFILE, dfu_devs[dfualt].dev, 0);
ret = copy_file(DFU_TEMPFILE, dfu_file_entry->filename, 0);
if (ret) {
printf("copy file failed\n");
ret = -EINVAL;
@ -352,26 +452,6 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
}
switch (dfu->dfu_state) {
case DFU_STATE_appIDLE:
switch (ctrl->bRequest) {
case USB_REQ_DFU_DETACH:
dfu->dfu_state = DFU_STATE_appDETACH;
value = 0;
goto out;
break;
default:
value = -EINVAL;
}
break;
case DFU_STATE_appDETACH:
switch (ctrl->bRequest) {
default:
dfu->dfu_state = DFU_STATE_appIDLE;
value = -EINVAL;
goto out;
break;
}
break;
case DFU_STATE_dfuIDLE:
switch (ctrl->bRequest) {
case USB_REQ_DFU_DNLOAD:
@ -380,16 +460,16 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
value = -EINVAL;
goto out;
}
debug("dfu: starting download to %s\n", dfu_devs[dfualt].dev);
if (dfu_devs[dfualt].flags & DFU_FLAG_SAFE) {
debug("dfu: starting download to %s\n", dfu_file_entry->filename);
if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) {
dfufd = open(DFU_TEMPFILE, O_WRONLY | O_CREAT);
} else {
unsigned flags = O_WRONLY;
if (dfu_devs[dfualt].flags & DFU_FLAG_CREATE)
if (dfu_file_entry->flags & FILE_LIST_FLAG_CREATE)
flags |= O_CREAT | O_TRUNC;
dfufd = open(dfu_devs[dfualt].dev, flags);
dfufd = open(dfu_file_entry->filename, flags);
}
if (dfufd < 0) {
@ -411,12 +491,12 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
break;
case USB_REQ_DFU_UPLOAD:
dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
debug("dfu: starting upload from %s\n", dfu_devs[dfualt].dev);
if (!(dfu_devs[dfualt].flags & DFU_FLAG_READBACK)) {
debug("dfu: starting upload from %s\n", dfu_file_entry->filename);
if (!(dfu_file_entry->flags & FILE_LIST_FLAG_READBACK)) {
dfu->dfu_state = DFU_STATE_dfuERROR;
goto out;
}
dfufd = open(dfu_devs[dfualt].dev, O_RDONLY);
dfufd = open(dfu_file_entry->filename, O_RDONLY);
if (dfufd < 0) {
dfu->dfu_state = DFU_STATE_dfuERROR;
perror("open");
@ -430,13 +510,6 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
value = 0;
break;
case USB_REQ_DFU_DETACH:
/* Proprietary extension: 'detach' from idle mode and
* get back to runtime mode in case of USB Reset. As
* much as I dislike this, we just can't use every USB
* bus reset to switch back to runtime mode, since at
* least the Linux USB stack likes to send a number of resets
* in a row :( */
dfu->dfu_state = DFU_STATE_dfuMANIFEST_WAIT_RST;
value = 0;
dfudetach = 1;
break;
@ -498,7 +571,6 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
case DFU_STATE_dfuDNBUSY:
case DFU_STATE_dfuMANIFEST_SYNC:
case DFU_STATE_dfuMANIFEST:
case DFU_STATE_dfuMANIFEST_WAIT_RST:
dfu->dfu_state = DFU_STATE_dfuERROR;
value = -EINVAL;
goto out;
@ -524,17 +596,7 @@ static void dfu_disable(struct usb_function *f)
{
struct f_dfu *dfu = func_to_dfu(f);
switch (dfu->dfu_state) {
case DFU_STATE_appDETACH:
dfu->dfu_state = DFU_STATE_dfuIDLE;
break;
case DFU_STATE_dfuMANIFEST_WAIT_RST:
dfu->dfu_state = DFU_STATE_appIDLE;
break;
default:
dfu->dfu_state = DFU_STATE_appDETACH;
break;
}
dfu->dfu_state = DFU_STATE_dfuIDLE;
dfu_cleanup(dfu);
}
@ -560,74 +622,6 @@ static struct usb_gadget_strings *dev_strings[] = {
NULL,
};
static int dfu_bind_config(struct usb_configuration *c)
{
struct f_dfu *dfu;
struct usb_function *func;
int status;
int i;
/* config description */
status = usb_string_id(c->cdev);
if (status < 0)
return status;
strings_dev[STRING_DESCRIPTION_IDX].id = status;
status = usb_string_id(c->cdev);
if (status < 0)
return status;
dfu_control_interface_desc.iInterface = status;
/* allocate and initialize one new instance */
dfu = xzalloc(sizeof *dfu);
dfu->dfu_state = DFU_STATE_appIDLE;
dfu->dfu_status = DFU_STATUS_OK;
dfu_string_defs = xzalloc(sizeof(struct usb_string) * (dfu_num_alt + 2));
dfu_string_defs[0].s = "Generic DFU";
dfu_string_defs[0].id = status;
for (i = 0; i < dfu_num_alt; i++) {
dfu_string_defs[i + 1].s = dfu_devs[i].name;
status = usb_string_id(c->cdev);
if (status < 0)
goto out;
dfu_string_defs[i + 1].id = status;
}
dfu_string_defs[i + 1].s = NULL;
dfu_string_table.strings = dfu_string_defs;
func = &dfu->func;
func->name = "DFU";
func->strings = dfu_strings;
/* descriptors are per-instance copies */
func->bind = dfu_bind;
func->unbind = dfu_unbind;
func->set_alt = dfu_set_alt;
func->setup = dfu_setup;
func->disable = dfu_disable;
dfu->dnreq = usb_ep_alloc_request(c->cdev->gadget->ep0);
if (!dfu->dnreq) {
printf("usb_ep_alloc_request failed\n");
goto out;
}
dfu->dnreq->buf = dma_alloc(CONFIG_USBD_DFU_XFER_SIZE);
dfu->dnreq->complete = dn_complete;
dfu->dnreq->zero = 0;
status = usb_add_function(c, func);
if (status)
goto out;
return 0;
out:
free(dfu);
free(dfu_string_defs);
return status;
}
static void dfu_unbind_config(struct usb_configuration *c)
{
free(dfu_string_defs);
@ -635,7 +629,6 @@ static void dfu_unbind_config(struct usb_configuration *c)
static struct usb_configuration dfu_config_driver = {
.label = "USB DFU",
.bind = dfu_bind_config,
.unbind = dfu_unbind_config,
.bConfigurationValue = 1,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
@ -654,10 +647,25 @@ static struct usb_device_descriptor dfu_dev_descriptor = {
.bNumConfigurations = 0x01,
};
static struct usb_function_instance *fi_dfu;
static struct usb_function *f_dfu;
static int dfu_driver_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
int status;
if (gadget->vendor_id && gadget->product_id) {
dfu_dev_descriptor.idVendor = cpu_to_le16(gadget->vendor_id);
dfu_dev_descriptor.idProduct = cpu_to_le16(gadget->product_id);
} else {
dfu_dev_descriptor.idVendor = cpu_to_le16(0x1d50); /* Openmoko, Inc */
dfu_dev_descriptor.idProduct = cpu_to_le16(0x60a2); /* barebox bootloader USB DFU Mode */
}
strings_dev[STRING_MANUFACTURER_IDX].s = gadget->manufacturer;
strings_dev[STRING_PRODUCT_IDX].s = gadget->productname;
status = usb_string_id(cdev);
if (status < 0)
goto fail;
@ -677,50 +685,129 @@ static int dfu_driver_bind(struct usb_composite_dev *cdev)
strings_dev[STRING_DESCRIPTION_IDX].id = status;
dfu_config_driver.iConfiguration = status;
status = usb_add_config(cdev, &dfu_config_driver);
status = usb_add_config_only(cdev, &dfu_config_driver);
if (status < 0)
goto fail;
fi_dfu = usb_get_function_instance("dfu");
if (IS_ERR(fi_dfu)) {
status = PTR_ERR(fi_dfu);
goto fail;
}
f_dfu = usb_get_function(fi_dfu);
if (IS_ERR(f_dfu)) {
status = PTR_ERR(f_dfu);
goto fail;
}
status = usb_add_function(&dfu_config_driver, f_dfu);
if (status)
goto fail;
return 0;
fail:
return status;
}
static int dfu_driver_unbind(struct usb_composite_dev *cdev)
{
usb_put_function(f_dfu);
usb_put_function_instance(fi_dfu);
return 0;
}
static struct usb_composite_driver dfu_driver = {
.name = "g_dfu",
.dev = &dfu_dev_descriptor,
.strings = dev_strings,
.bind = dfu_driver_bind,
.unbind = dfu_driver_unbind,
};
int usb_dfu_register(struct usb_dfu_pdata *pdata)
int usb_dfu_register(struct f_dfu_opts *opts)
{
int ret;
dfu_devs = pdata->alts;
dfu_num_alt = pdata->num_alts;
dfu_dev_descriptor.idVendor = pdata->idVendor;
dfu_dev_descriptor.idProduct = pdata->idProduct;
strings_dev[STRING_MANUFACTURER_IDX].s = pdata->manufacturer;
strings_dev[STRING_PRODUCT_IDX].s = pdata->productname;
if (dfu_files)
return -EBUSY;
ret = usb_composite_register(&dfu_driver);
dfu_files = opts->files;
ret = usb_composite_probe(&dfu_driver);
if (ret)
return ret;
goto out;
while (1) {
ret = usb_gadget_poll();
if (ret < 0)
return ret;
goto out1;
if (ctrlc() || dfudetach)
goto out;
if (dfudetach) {
ret = 0;
goto out1;
}
if (ctrlc()) {
ret = -EINTR;
goto out1;
}
}
out:
out1:
dfudetach = 0;
usb_composite_unregister(&dfu_driver);
out:
dfu_files = NULL;
return 0;
return ret;
}
static void dfu_free_func(struct usb_function *f)
{
struct f_dfu *dfu = container_of(f, struct f_dfu, func);
free(dfu);
}
static struct usb_function *dfu_alloc_func(struct usb_function_instance *fi)
{
struct f_dfu *dfu;
dfu = xzalloc(sizeof(*dfu));
dfu->func.name = "dfu";
dfu->func.strings = dfu_strings;
/* descriptors are per-instance copies */
dfu->func.bind = dfu_bind;
dfu->func.set_alt = dfu_set_alt;
dfu->func.setup = dfu_setup;
dfu->func.disable = dfu_disable;
dfu->func.unbind = dfu_unbind;
dfu->func.free_func = dfu_free_func;
return &dfu->func;
}
static void dfu_free_instance(struct usb_function_instance *fi)
{
struct f_dfu_opts *opts;
opts = container_of(fi, struct f_dfu_opts, func_inst);
kfree(opts);
}
static struct usb_function_instance *dfu_alloc_instance(void)
{
struct f_dfu_opts *opts;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->func_inst.free_func_inst = dfu_free_instance;
return &opts->func_inst;
}
DECLARE_USB_FUNCTION_INIT(dfu, dfu_alloc_instance, dfu_alloc_func);

View File

@ -7,17 +7,11 @@
* 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.
*
*
*/
#include <init.h>
#include <common.h>
#include <linux/ctype.h>
#include <asm/byteorder.h>
@ -26,15 +20,6 @@
#include "gadget_chips.h"
/* we must assign addresses for configurable endpoints (like net2280) */
static __initdata unsigned epnum;
// #define MANY_ENDPOINTS
#ifdef MANY_ENDPOINTS
/* more than 15 configurable endpoints */
static __initdata unsigned in_epnum;
#endif
/*
* This should work with endpoints from controller drivers sharing the
* same endpoint naming convention. By example:
@ -51,23 +36,26 @@ static __initdata unsigned in_epnum;
* NOTE: each endpoint is unidirectional, as specified by its USB
* descriptor; and isn't specific to a configuration or altsetting.
*/
static int __init
static int
ep_matches (
struct usb_gadget *gadget,
struct usb_ep *ep,
struct usb_endpoint_descriptor *desc
struct usb_endpoint_descriptor *desc,
struct usb_ss_ep_comp_descriptor *ep_comp
)
{
u8 type;
const char *tmp;
u16 max;
int num_req_streams = 0;
/* endpoint already claimed? */
if (NULL != ep->driver_data)
return 0;
/* only support ep0 for portable CONTROL traffic */
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
type = usb_endpoint_type(desc);
if (USB_ENDPOINT_XFER_CONTROL == type)
return 0;
@ -120,28 +108,48 @@ ep_matches (
}
}
/*
* Get the number of required streams from the EP companion
* descriptor and see if the EP matches it
*/
if (usb_endpoint_xfer_bulk(desc)) {
if (ep_comp && gadget->max_speed >= USB_SPEED_SUPER) {
num_req_streams = ep_comp->bmAttributes & 0x1f;
if (num_req_streams > ep->max_streams)
return 0;
}
}
/*
* If the protocol driver hasn't yet decided on wMaxPacketSize
* and wants to know the maximum possible, provide the info.
*/
if (desc->wMaxPacketSize == 0)
desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket_limit);
/* endpoint maxpacket size is an input parameter, except for bulk
* where it's an output parameter representing the full speed limit.
* the usb spec fixes high speed bulk maxpacket at 512 bytes.
*/
max = 0x7ff & le16_to_cpu(desc->wMaxPacketSize);
max = 0x7ff & usb_endpoint_maxp(desc);
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* INT: limit 64 bytes full speed, 1024 high speed */
if (!gadget->is_dualspeed && max > 64)
/* INT: limit 64 bytes full speed, 1024 high/super speed */
if (!gadget_is_dualspeed(gadget) && max > 64)
return 0;
/* FALLTHROUGH */
case USB_ENDPOINT_XFER_ISOC:
/* ISO: limit 1023 bytes full speed, 1024 high speed */
if (ep->maxpacket < max)
/* ISO: limit 1023 bytes full speed, 1024 high/super speed */
if (ep->maxpacket_limit < max)
return 0;
if (!gadget->is_dualspeed && max > 1023)
if (!gadget_is_dualspeed(gadget) && max > 1023)
return 0;
/* BOTH: "high bandwidth" works only at high speed */
if ((desc->wMaxPacketSize & cpu_to_le16(3<<11))) {
if (!gadget->is_dualspeed)
if (!gadget_is_dualspeed(gadget))
return 0;
/* configure your hardware with enough buffering!! */
}
@ -155,31 +163,30 @@ ep_matches (
if (isdigit (ep->name [2])) {
u8 num = simple_strtoul (&ep->name [2], NULL, 10);
desc->bEndpointAddress |= num;
#ifdef MANY_ENDPOINTS
} else if (desc->bEndpointAddress & USB_DIR_IN) {
if (++in_epnum > 15)
if (++gadget->in_epnum > 15)
return 0;
desc->bEndpointAddress = USB_DIR_IN | in_epnum;
#endif
desc->bEndpointAddress = USB_DIR_IN | gadget->in_epnum;
} else {
if (++epnum > 15)
if (++gadget->out_epnum > 15)
return 0;
desc->bEndpointAddress |= epnum;
desc->bEndpointAddress |= gadget->out_epnum;
}
/* report (variable) full speed bulk maxpacket */
if (USB_ENDPOINT_XFER_BULK == type) {
int size = ep->maxpacket;
if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) {
int size = ep->maxpacket_limit;
/* min() doesn't work on bitfields with gcc-3.5 */
if (size > 64)
size = 64;
desc->wMaxPacketSize = cpu_to_le16(size);
}
ep->address = desc->bEndpointAddress;
return 1;
}
static struct usb_ep * __init
static struct usb_ep *
find_ep (struct usb_gadget *gadget, const char *name)
{
struct usb_ep *ep;
@ -192,7 +199,125 @@ find_ep (struct usb_gadget *gadget, const char *name)
}
/**
* usb_ep_autoconfig - choose an endpoint matching the descriptor
* usb_ep_autoconfig_ss() - choose an endpoint matching the ep
* descriptor and ep companion descriptor
* @gadget: The device to which the endpoint must belong.
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
* initialized. For periodic transfers, the maximum packet
* size must also be initialized. This is modified on
* success.
* @ep_comp: Endpoint companion descriptor, with the required
* number of streams. Will be modified when the chosen EP
* supports a different number of streams.
*
* This routine replaces the usb_ep_autoconfig when needed
* superspeed enhancments. If such enhancemnets are required,
* the FD should call usb_ep_autoconfig_ss directly and provide
* the additional ep_comp parameter.
*
* By choosing an endpoint to use with the specified descriptor,
* this routine simplifies writing gadget drivers that work with
* multiple USB device controllers. The endpoint would be
* passed later to usb_ep_enable(), along with some descriptor.
*
* That second descriptor won't always be the same as the first one.
* For example, isochronous endpoints can be autoconfigured for high
* bandwidth, and then used in several lower bandwidth altsettings.
* Also, high and full speed descriptors will be different.
*
* Be sure to examine and test the results of autoconfiguration
* on your hardware. This code may not make the best choices
* about how to use the USB controller, and it can't know all
* the restrictions that may apply. Some combinations of driver
* and hardware won't be able to autoconfigure.
*
* On success, this returns an un-claimed usb_ep, and modifies the endpoint
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
* is initialized as if the endpoint were used at full speed and
* the bmAttribute field in the ep companion descriptor is
* updated with the assigned number of streams if it is
* different from the original value. To prevent the endpoint
* from being returned by a later autoconfig call, claim it by
* assigning ep->driver_data to some non-null value.
*
* On failure, this returns a null endpoint descriptor.
*/
struct usb_ep *usb_ep_autoconfig_ss(
struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc,
struct usb_ss_ep_comp_descriptor *ep_comp
)
{
struct usb_ep *ep;
u8 type;
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
/* First, apply chip-specific "best usage" knowledge.
* This might make a good usb_gadget_ops hook ...
*/
if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
/* ep-e, ep-f are PIO with only 64 byte fifos */
ep = find_ep (gadget, "ep-e");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
ep = find_ep (gadget, "ep-f");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
} else if (gadget_is_goku (gadget)) {
if (USB_ENDPOINT_XFER_INT == type) {
/* single buffering is enough */
ep = find_ep(gadget, "ep3-bulk");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
} else if (USB_ENDPOINT_XFER_BULK == type
&& (USB_DIR_IN & desc->bEndpointAddress)) {
/* DMA may be available */
ep = find_ep(gadget, "ep2-bulk");
if (ep && ep_matches(gadget, ep, desc,
ep_comp))
goto found_ep;
}
#ifdef CONFIG_BLACKFIN
} else if (gadget_is_musbhdrc(gadget)) {
if ((USB_ENDPOINT_XFER_BULK == type) ||
(USB_ENDPOINT_XFER_ISOC == type)) {
if (USB_DIR_IN & desc->bEndpointAddress)
ep = find_ep (gadget, "ep5in");
else
ep = find_ep (gadget, "ep6out");
} else if (USB_ENDPOINT_XFER_INT == type) {
if (USB_DIR_IN & desc->bEndpointAddress)
ep = find_ep(gadget, "ep1in");
else
ep = find_ep(gadget, "ep2out");
} else
ep = NULL;
if (ep && ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
#endif
}
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
if (ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
}
/* Fail */
return NULL;
found_ep:
ep->desc = NULL;
ep->comp_desc = NULL;
return ep;
}
EXPORT_SYMBOL_GPL(usb_ep_autoconfig_ss);
/**
* usb_ep_autoconfig() - choose an endpoint matching the
* descriptor
* @gadget: The device to which the endpoint must belong.
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
* initialized. For periodic transfers, the maximum packet
@ -221,63 +346,14 @@ find_ep (struct usb_gadget *gadget, const char *name)
*
* On failure, this returns a null endpoint descriptor.
*/
struct usb_ep * __init usb_ep_autoconfig (
struct usb_ep *usb_ep_autoconfig(
struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc
)
{
struct usb_ep *ep;
u8 type;
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
/* First, apply chip-specific "best usage" knowledge.
* This might make a good usb_gadget_ops hook ...
*/
if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
/* ep-e, ep-f are PIO with only 64 byte fifos */
ep = find_ep (gadget, "ep-e");
if (ep && ep_matches (gadget, ep, desc))
return ep;
ep = find_ep (gadget, "ep-f");
if (ep && ep_matches (gadget, ep, desc))
return ep;
} else if (gadget_is_goku (gadget)) {
if (USB_ENDPOINT_XFER_INT == type) {
/* single buffering is enough */
ep = find_ep (gadget, "ep3-bulk");
if (ep && ep_matches (gadget, ep, desc))
return ep;
} else if (USB_ENDPOINT_XFER_BULK == type
&& (USB_DIR_IN & desc->bEndpointAddress)) {
/* DMA may be available */
ep = find_ep (gadget, "ep2-bulk");
if (ep && ep_matches (gadget, ep, desc))
return ep;
}
} else if (gadget_is_sh (gadget) && USB_ENDPOINT_XFER_INT == type) {
/* single buffering is enough; maybe 8 byte fifo is too */
ep = find_ep (gadget, "ep3in-bulk");
if (ep && ep_matches (gadget, ep, desc))
return ep;
} else if (gadget_is_mq11xx (gadget) && USB_ENDPOINT_XFER_INT == type) {
ep = find_ep (gadget, "ep1-bulk");
if (ep && ep_matches (gadget, ep, desc))
return ep;
}
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
if (ep_matches (gadget, ep, desc))
return ep;
}
/* Fail */
return NULL;
return usb_ep_autoconfig_ss(gadget, desc, NULL);
}
EXPORT_SYMBOL_GPL(usb_ep_autoconfig);
/**
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
@ -288,17 +364,14 @@ struct usb_ep * __init usb_ep_autoconfig (
* state such as ep->driver_data and the record of assigned endpoints
* used by usb_ep_autoconfig().
*/
void __init usb_ep_autoconfig_reset (struct usb_gadget *gadget)
void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
{
struct usb_ep *ep;
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
ep->driver_data = NULL;
}
#ifdef MANY_ENDPOINTS
in_epnum = 0;
#endif
epnum = 0;
gadget->in_epnum = 0;
gadget->out_epnum = 0;
}
EXPORT_SYMBOL_GPL(usb_ep_autoconfig_reset);

View File

@ -4,6 +4,8 @@
* Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
* Copyright (C) 2008 by David Brownell
* Copyright (C) 2008 by Nokia Corporation
* Copyright (C) 2009 by Samsung Electronics
* Author: Michal Nazarewicz (mina86@mina86.com)
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
@ -14,10 +16,12 @@
#include <common.h>
#include <usb/cdc.h>
#include <linux/err.h>
#include <asm/byteorder.h>
#include <usb/composite.h>
#include "gadget_chips.h"
#include "u_serial.h"
#include "gadget_chips.h"
/*
@ -37,12 +41,6 @@
* descriptors (roughly equivalent to CDC Unions) may sometimes help.
*/
struct acm_ep_descs {
struct usb_endpoint_descriptor *in;
struct usb_endpoint_descriptor *out;
struct usb_endpoint_descriptor *notify;
};
struct f_acm {
struct gserial port;
u8 ctrl_id, data_id;
@ -50,11 +48,13 @@ struct f_acm {
u8 pending;
struct acm_ep_descs fs;
struct acm_ep_descs hs;
/* lock is mostly for pending and notify_req ... they get accessed
* by callbacks both from tty (open/close/break) under its spinlock,
* and notify_req.complete() which can't use that lock.
*/
spinlock_t lock;
struct usb_ep *notify;
struct usb_endpoint_descriptor *notify_desc;
struct usb_request *notify_req;
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
@ -89,11 +89,25 @@ static inline struct f_acm *port_to_acm(struct gserial *p)
/* notification endpoint uses smallish and infrequent fixed-size messages */
#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
#define GS_NOTIFY_INTERVAL_MS 32
#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */
/* interface and class descriptors: */
static struct usb_interface_assoc_descriptor
acm_iad_descriptor = {
.bLength = sizeof acm_iad_descriptor,
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
/* .bFirstInterface = DYNAMIC, */
.bInterfaceCount = 2, // control + data
.bFunctionClass = USB_CLASS_COMM,
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
.bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
/* .iFunction = DYNAMIC */
};
static struct usb_interface_descriptor acm_control_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
@ -155,7 +169,7 @@ static struct usb_endpoint_descriptor acm_fs_notify_desc = {
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
.bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
.bInterval = GS_NOTIFY_INTERVAL_MS,
};
static struct usb_endpoint_descriptor acm_fs_in_desc = {
@ -173,6 +187,7 @@ static struct usb_endpoint_descriptor acm_fs_out_desc = {
};
static struct usb_descriptor_header *acm_fs_function[] = {
(struct usb_descriptor_header *) &acm_iad_descriptor,
(struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc,
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
@ -186,14 +201,13 @@ static struct usb_descriptor_header *acm_fs_function[] = {
};
/* high speed support: */
static struct usb_endpoint_descriptor acm_hs_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
.bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
.bInterval = USB_MS_TO_HS_INTERVAL(GS_NOTIFY_INTERVAL_MS),
};
static struct usb_endpoint_descriptor acm_hs_in_desc = {
@ -211,6 +225,7 @@ static struct usb_endpoint_descriptor acm_hs_out_desc = {
};
static struct usb_descriptor_header *acm_hs_function[] = {
(struct usb_descriptor_header *) &acm_iad_descriptor,
(struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc,
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
@ -223,16 +238,54 @@ static struct usb_descriptor_header *acm_hs_function[] = {
NULL,
};
static struct usb_endpoint_descriptor acm_ss_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_endpoint_descriptor acm_ss_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor acm_ss_bulk_comp_desc = {
.bLength = sizeof acm_ss_bulk_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
};
static struct usb_descriptor_header *acm_ss_function[] = {
(struct usb_descriptor_header *) &acm_iad_descriptor,
(struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc,
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
(struct usb_descriptor_header *) &acm_descriptor,
(struct usb_descriptor_header *) &acm_union_desc,
(struct usb_descriptor_header *) &acm_hs_notify_desc,
(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
(struct usb_descriptor_header *) &acm_data_interface_desc,
(struct usb_descriptor_header *) &acm_ss_in_desc,
(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
(struct usb_descriptor_header *) &acm_ss_out_desc,
(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
NULL,
};
/* string descriptors: */
#define ACM_CTRL_IDX 0
#define ACM_DATA_IDX 1
#define ACM_IAD_IDX 2
/* static strings, in UTF-8 */
static struct usb_string acm_string_defs[] = {
[ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
[ACM_DATA_IDX].s = "CDC ACM Data",
{ /* ZEROES END LIST */ },
[ACM_IAD_IDX ].s = "CDC Serial",
{ } /* end of list */
};
static struct usb_gadget_strings acm_string_table = {
@ -257,6 +310,7 @@ static void acm_complete_set_line_coding(struct usb_ep *ep,
struct usb_request *req)
{
struct f_acm *acm = ep->driver_data;
struct usb_composite_dev *cdev = acm->port.func.config->cdev;
if (req->status != 0) {
DBG(cdev, "acm ttyGS%d completion, err %d\n",
@ -309,6 +363,7 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
if (w_length != sizeof(struct usb_cdc_line_coding)
|| w_index != acm->ctrl_id)
goto invalid;
value = w_length;
cdev->gadget->ep0->driver_data = acm;
req->complete = acm_complete_set_line_coding;
@ -319,6 +374,7 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
| USB_CDC_REQ_GET_LINE_CODING:
if (w_index != acm->ctrl_id)
goto invalid;
value = min_t(unsigned, w_length,
sizeof(struct usb_cdc_line_coding));
memcpy(req->buf, &acm->port_line_coding, value);
@ -376,25 +432,28 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
usb_ep_disable(acm->notify);
} else {
VDBG(cdev, "init acm ctrl interface %d\n", intf);
acm->notify_desc = ep_choose(cdev->gadget,
acm->hs.notify,
acm->fs.notify);
if (config_ep_by_speed(cdev->gadget, f, acm->notify))
return -EINVAL;
}
usb_ep_enable(acm->notify, acm->notify_desc);
usb_ep_enable(acm->notify);
acm->notify->driver_data = acm;
} else if (intf == acm->data_id) {
if (acm->port.in->driver_data) {
DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
gserial_disconnect(&acm->port);
} else {
DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
acm->port.in_desc = ep_choose(cdev->gadget,
acm->hs.in, acm->fs.in);
acm->port.out_desc = ep_choose(cdev->gadget,
acm->hs.out, acm->fs.out);
}
if (!acm->port.in->desc || !acm->port.out->desc) {
DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
if (config_ep_by_speed(cdev->gadget, f,
acm->port.in) ||
config_ep_by_speed(cdev->gadget, f,
acm->port.out)) {
acm->port.in->desc = NULL;
acm->port.out->desc = NULL;
return -EINVAL;
}
}
gserial_connect(&acm->port, acm->port_num);
} else
@ -406,8 +465,9 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
static void acm_disable(struct usb_function *f)
{
struct f_acm *acm = func_to_acm(f);
struct usb_composite_dev *cdev = f->config->cdev;
VDBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
gserial_disconnect(&acm->port);
usb_ep_disable(acm->notify);
acm->notify->driver_data = NULL;
@ -424,7 +484,7 @@ static void acm_disable(struct usb_function *f)
* @length: size of data
* Context: irqs blocked, acm->lock held, acm_notify_req non-null
*
* Returns zero on sucess or a negative errno.
* Returns zero on success or a negative errno.
*
* See section 6.3.5 of the CDC 1.1 specification for information
* about the only notification we issue: SerialState change.
@ -441,7 +501,7 @@ static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
req = acm->notify_req;
acm->notify_req = NULL;
acm->pending = 0;
acm->pending = false;
req->length = len;
notify = req->buf;
@ -470,15 +530,16 @@ static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
static int acm_notify_serial_state(struct f_acm *acm)
{
struct usb_composite_dev *cdev = acm->port.func.config->cdev;
int status;
if (acm->notify_req) {
VDBG(cdev, "acm ttyGS%d serial state %04x\n",
DBG(cdev, "acm ttyGS%d serial state %04x\n",
acm->port_num, acm->serial_state);
status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
0, &acm->serial_state, sizeof(acm->serial_state));
} else {
acm->pending = 1;
acm->pending = true;
status = 0;
}
@ -488,8 +549,11 @@ static int acm_notify_serial_state(struct f_acm *acm)
static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_acm *acm = req->context;
u8 doit = 0;
u8 doit = false;
/* on this call path we do NOT hold the port spinlock,
* which is why ACM needs its own spinlock
*/
if (req->status != -ESHUTDOWN)
doit = acm->pending;
acm->notify_req = req;
@ -533,19 +597,34 @@ static int acm_send_break(struct gserial *port, int duration)
/*-------------------------------------------------------------------------*/
/* ACM function driver setup/binding */
static int __init
static int
acm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_acm *acm = func_to_acm(f);
struct usb_string *us;
int status;
struct usb_ep *ep;
/* REVISIT might want instance-specific strings to help
* distinguish instances ...
*/
/* maybe allocate device-global string IDs, and patch descriptors */
us = usb_gstrings_attach(cdev, acm_strings,
ARRAY_SIZE(acm_string_defs));
if (IS_ERR(us))
return PTR_ERR(us);
acm_control_interface_desc.iInterface = us[ACM_CTRL_IDX].id;
acm_data_interface_desc.iInterface = us[ACM_DATA_IDX].id;
acm_iad_descriptor.iFunction = us[ACM_IAD_IDX].id;
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
acm->ctrl_id = status;
acm_iad_descriptor.bFirstInterface = status;
acm_control_interface_desc.bInterfaceNumber = status;
acm_union_desc .bMasterInterface0 = status;
@ -589,43 +668,26 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
acm->notify_req->complete = acm_cdc_notify_complete;
acm->notify_req->context = acm;
/* copy descriptors, and track endpoint copies */
f->descriptors = usb_copy_descriptors(acm_fs_function);
if (!f->descriptors)
goto fail;
acm->fs.in = usb_find_endpoint(acm_fs_function,
f->descriptors, &acm_fs_in_desc);
acm->fs.out = usb_find_endpoint(acm_fs_function,
f->descriptors, &acm_fs_out_desc);
acm->fs.notify = usb_find_endpoint(acm_fs_function,
f->descriptors, &acm_fs_notify_desc);
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
if (gadget_is_dualspeed(c->cdev->gadget)) {
acm_hs_in_desc.bEndpointAddress =
acm_fs_in_desc.bEndpointAddress;
acm_hs_out_desc.bEndpointAddress =
acm_fs_out_desc.bEndpointAddress;
acm_hs_notify_desc.bEndpointAddress =
acm_fs_notify_desc.bEndpointAddress;
acm_hs_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
acm_hs_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
acm_hs_notify_desc.bEndpointAddress =
acm_fs_notify_desc.bEndpointAddress;
/* copy descriptors, and track endpoint copies */
f->hs_descriptors = usb_copy_descriptors(acm_hs_function);
acm_ss_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
acm->hs.in = usb_find_endpoint(acm_hs_function,
f->hs_descriptors, &acm_hs_in_desc);
acm->hs.out = usb_find_endpoint(acm_hs_function,
f->hs_descriptors, &acm_hs_out_desc);
acm->hs.notify = usb_find_endpoint(acm_hs_function,
f->hs_descriptors, &acm_hs_notify_desc);
}
status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function,
acm_ss_function);
if (status)
goto fail;
DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
acm->port_num,
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
acm->port.in->name, acm->port.out->name,
acm->notify->name);
@ -648,80 +710,31 @@ fail:
return status;
}
static void
acm_unbind(struct usb_configuration *c, struct usb_function *f)
static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_acm *acm = func_to_acm(f);
acm_string_defs[0].id = 0;
usb_free_all_descriptors(f);
if (acm->notify_req)
gs_free_req(acm->notify, acm->notify_req);
}
static void acm_free_func(struct usb_function *f)
{
struct f_acm *acm = func_to_acm(f);
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
gs_free_req(acm->notify, acm->notify_req);
kfree(acm);
}
/* Some controllers can't support CDC ACM ... */
static inline int can_support_cdc(struct usb_configuration *c)
static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
{
/* SH3 doesn't support multiple interfaces */
if (gadget_is_sh(c->cdev->gadget))
return 0;
struct f_serial_opts *opts;
struct f_acm *acm;
/* sa1100 doesn't have a third interrupt endpoint */
if (gadget_is_sa1100(c->cdev->gadget))
return 0;
/* everything else is *probably* fine ... */
return 1;
}
/**
* acm_bind_config - add a CDC ACM function to a configuration
* @c: the configuration to support the CDC ACM instance
* @port_num: /dev/ttyGS* port this interface will use
* Context: single threaded during gadget setup
*
* Returns zero on success, else negative errno.
*
* Caller must have called @gserial_setup() with enough ports to
* handle all the ones it binds. Caller is also responsible
* for calling @gserial_cleanup() before module unload.
*/
int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
{
struct f_acm *acm;
int status;
if (!can_support_cdc(c))
return -EINVAL;
/* REVISIT might want instance-specific strings to help
* distinguish instances ...
*/
/* maybe allocate device-global string IDs, and patch descriptors */
if (acm_string_defs[ACM_CTRL_IDX].id == 0) {
status = usb_string_id(c->cdev);
if (status < 0)
return status;
acm_string_defs[ACM_CTRL_IDX].id = status;
acm_control_interface_desc.iInterface = status;
status = usb_string_id(c->cdev);
if (status < 0)
return status;
acm_string_defs[ACM_DATA_IDX].id = status;
acm_data_interface_desc.iInterface = status;
}
/* allocate and initialize one new instance */
acm = kzalloc(sizeof *acm, GFP_KERNEL);
acm = kzalloc(sizeof(*acm), GFP_KERNEL);
if (!acm)
return -ENOMEM;
acm->port_num = port_num;
return ERR_PTR(-ENOMEM);
acm->port.connect = acm_connect;
acm->port.disconnect = acm_disconnect;
@ -731,13 +744,42 @@ int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
acm->port.func.strings = acm_strings;
/* descriptors are per-instance copies */
acm->port.func.bind = acm_bind;
acm->port.func.unbind = acm_unbind;
acm->port.func.set_alt = acm_set_alt;
acm->port.func.setup = acm_setup;
acm->port.func.disable = acm_disable;
status = usb_add_function(c, &acm->port.func);
if (status)
kfree(acm);
return status;
opts = container_of(fi, struct f_serial_opts, func_inst);
acm->port_num = opts->port_num;
acm->port.func.unbind = acm_unbind;
acm->port.func.free_func = acm_free_func;
return &acm->port.func;
}
static void acm_free_instance(struct usb_function_instance *fi)
{
struct f_serial_opts *opts;
opts = container_of(fi, struct f_serial_opts, func_inst);
gserial_free_line(opts->port_num);
kfree(opts);
}
static struct usb_function_instance *acm_alloc_instance(void)
{
struct f_serial_opts *opts;
int ret;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->func_inst.free_func_inst = acm_free_instance;
ret = gserial_alloc_line(&opts->port_num);
if (ret) {
kfree(opts);
return ERR_PTR(ret);
}
return &opts->func_inst;
}
DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,890 @@
/*
* (C) Copyright 2008 - 2009
* Windriver, <www.windriver.com>
* Tom Rix <Tom.Rix@windriver.com>
*
* Copyright 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
* Copyright 2014 Linaro, Ltd.
* Rob Herring <robh@kernel.org>
*
* Copyright 2014 Sascha Hauer <s.hauer@pengutronix.de>
* Ported to barebox
*
* SPDX-License-Identifier: GPL-2.0+
*/
#define pr_fmt(fmt) "fastboot: " fmt
#include <common.h>
#include <errno.h>
#include <malloc.h>
#include <fcntl.h>
#include <clock.h>
#include <ioctl.h>
#include <libbb.h>
#include <boot.h>
#include <dma.h>
#include <fs.h>
#include <file-list.h>
#include <progress.h>
#include <environment.h>
#include <globalvar.h>
#include <usb/ch9.h>
#include <usb/gadget.h>
#include <usb/fastboot.h>
#include <usb/composite.h>
#include <linux/err.h>
#include <linux/compiler.h>
#include <linux/stat.h>
#include <linux/mtd/mtd-abi.h>
#define FASTBOOT_VERSION "0.4"
#define FASTBOOT_INTERFACE_CLASS 0xff
#define FASTBOOT_INTERFACE_SUB_CLASS 0x42
#define FASTBOOT_INTERFACE_PROTOCOL 0x03
#define FASTBOOT_TMPFILE "/.fastboot.img"
#define EP_BUFFER_SIZE 4096
struct fb_variable {
char *name;
char *value;
struct list_head list;
};
struct f_fastboot {
struct usb_function func;
/* IN/OUT EP's and correspoinding requests */
struct usb_ep *in_ep, *out_ep;
struct usb_request *in_req, *out_req;
struct file_list *files;
int download_fd;
size_t download_bytes;
size_t download_size;
struct list_head variables;
};
static inline struct f_fastboot *func_to_fastboot(struct usb_function *f)
{
return container_of(f, struct f_fastboot, func);
}
static struct usb_endpoint_descriptor fs_ep_in = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(64),
.bInterval = 0x00,
};
static struct usb_endpoint_descriptor fs_ep_out = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(64),
.bInterval = 0x00,
};
static struct usb_endpoint_descriptor hs_ep_in = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
.bInterval = 0x00,
};
static struct usb_endpoint_descriptor hs_ep_out = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
.bInterval = 0x00,
};
static struct usb_interface_descriptor interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0x00,
.bAlternateSetting = 0x00,
.bNumEndpoints = 0x02,
.bInterfaceClass = FASTBOOT_INTERFACE_CLASS,
.bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS,
.bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL,
};
static struct usb_descriptor_header *fb_fs_descs[] = {
(struct usb_descriptor_header *)&interface_desc,
(struct usb_descriptor_header *)&fs_ep_in,
(struct usb_descriptor_header *)&fs_ep_out,
NULL,
};
static struct usb_descriptor_header *fb_hs_descs[] = {
(struct usb_descriptor_header *)&interface_desc,
(struct usb_descriptor_header *)&hs_ep_in,
(struct usb_descriptor_header *)&hs_ep_out,
NULL,
};
/*
* static strings, in UTF-8
*/
static const char fastboot_name[] = "Android Fastboot";
static struct usb_string fastboot_string_defs[] = {
[0].s = fastboot_name,
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_fastboot = {
.language = 0x0409, /* en-us */
.strings = fastboot_string_defs,
};
static struct usb_gadget_strings *fastboot_strings[] = {
&stringtab_fastboot,
NULL,
};
static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
static int in_req_complete;
static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
{
int status = req->status;
in_req_complete = 1;
pr_debug("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual);
}
static struct usb_request *fastboot_alloc_request(struct usb_ep *ep)
{
struct usb_request *req;
req = usb_ep_alloc_request(ep);
if (!req)
return NULL;
req->length = EP_BUFFER_SIZE;
req->buf = dma_alloc(EP_BUFFER_SIZE);
if (!req->buf) {
usb_ep_free_request(ep, req);
return NULL;
}
memset(req->buf, 0, EP_BUFFER_SIZE);
return req;
}
static void fb_setvar(struct fb_variable *var, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
var->value = vasprintf(fmt, ap);
va_end(ap);
}
static struct fb_variable *fb_addvar(struct f_fastboot *f_fb, const char *fmt, ...)
{
struct fb_variable *var = xzalloc(sizeof(*var));
va_list ap;
va_start(ap, fmt);
var->name = vasprintf(fmt, ap);
va_end(ap);
list_add_tail(&var->list, &f_fb->variables);
return var;
}
static int fastboot_add_partition_variables(struct f_fastboot *f_fb,
struct file_list_entry *fentry)
{
struct stat s;
size_t size = 0;
int fd, ret;
struct mtd_info_user mtdinfo;
char *type = NULL;
struct fb_variable *var;
ret = stat(fentry->filename, &s);
if (ret) {
if (fentry->flags & FILE_LIST_FLAG_CREATE) {
ret = 0;
type = "file";
goto out;
}
goto out;
}
fd = open(fentry->filename, O_RDWR);
if (fd < 0) {
ret = -EINVAL;
goto out;
}
size = s.st_size;
ret = ioctl(fd, MEMGETINFO, &mtdinfo);
close(fd);
if (!ret) {
switch (mtdinfo.type) {
case MTD_NANDFLASH:
type = "NAND-flash";
break;
case MTD_NORFLASH:
type = "NOR-flash";
break;
case MTD_UBIVOLUME:
type = "UBI";
break;
default:
type = "flash";
break;
}
goto out;
}
type = "basic";
ret = 0;
out:
if (ret)
return ret;
var = fb_addvar(f_fb, "partition-size:%s", fentry->name);
fb_setvar(var, "%08zx", size);
var = fb_addvar(f_fb, "partition-type:%s", fentry->name);
fb_setvar(var, "%s", type);
return ret;
}
static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
int id, ret;
struct usb_gadget *gadget = c->cdev->gadget;
struct f_fastboot *f_fb = func_to_fastboot(f);
struct usb_string *us;
const struct usb_function_instance *fi = f->fi;
struct f_fastboot_opts *opts = container_of(fi, struct f_fastboot_opts, func_inst);
struct file_list_entry *fentry;
struct fb_variable *var;
f_fb->files = opts->files;
var = fb_addvar(f_fb, "version");
fb_setvar(var, "0.4");
var = fb_addvar(f_fb, "bootloader-version");
fb_setvar(var, release_string);
file_list_for_each_entry(f_fb->files, fentry) {
ret = fastboot_add_partition_variables(f_fb, fentry);
if (ret)
return ret;
}
/* DYNAMIC interface numbers assignments */
id = usb_interface_id(c, f);
if (id < 0)
return id;
interface_desc.bInterfaceNumber = id;
id = usb_string_id(c->cdev);
if (id < 0)
return id;
fastboot_string_defs[0].id = id;
interface_desc.iInterface = id;
us = usb_gstrings_attach(cdev, fastboot_strings, 1);
if (IS_ERR(us)) {
ret = PTR_ERR(us);
return ret;
}
f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in);
if (!f_fb->in_ep)
return -ENODEV;
f_fb->in_ep->driver_data = c->cdev;
f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out);
if (!f_fb->out_ep)
return -ENODEV;
f_fb->out_ep->driver_data = c->cdev;
hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
f_fb->out_req = fastboot_alloc_request(f_fb->out_ep);
if (!f_fb->out_req) {
puts("failed to alloc out req\n");
ret = -EINVAL;
return ret;
}
f_fb->out_req->complete = rx_handler_command;
f_fb->out_req->context = f_fb;
f_fb->in_req = fastboot_alloc_request(f_fb->in_ep);
if (!f_fb->in_req) {
puts("failed alloc req in\n");
ret = -EINVAL;
return ret;
}
f_fb->in_req->complete = fastboot_complete;
f_fb->out_req->context = f_fb;
ret = usb_assign_descriptors(f, fb_fs_descs, fb_hs_descs, NULL);
if (ret)
return ret;
return 0;
}
static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_fastboot *f_fb = func_to_fastboot(f);
struct fb_variable *var, *tmp;
usb_ep_dequeue(f_fb->in_ep, f_fb->in_req);
free(f_fb->in_req->buf);
usb_ep_free_request(f_fb->in_ep, f_fb->in_req);
f_fb->in_req = NULL;
usb_ep_dequeue(f_fb->out_ep, f_fb->out_req);
free(f_fb->out_req->buf);
usb_ep_free_request(f_fb->out_ep, f_fb->out_req);
f_fb->out_req = NULL;
list_for_each_entry_safe(var, tmp, &f_fb->variables, list) {
free(var->name);
free(var->value);
list_del(&var->list);
free(var);
}
}
static void fastboot_disable(struct usb_function *f)
{
struct f_fastboot *f_fb = func_to_fastboot(f);
usb_ep_disable(f_fb->out_ep);
usb_ep_disable(f_fb->in_ep);
}
static int fastboot_set_alt(struct usb_function *f,
unsigned interface, unsigned alt)
{
int ret;
struct f_fastboot *f_fb = func_to_fastboot(f);
pr_debug("%s: func: %s intf: %d alt: %d\n",
__func__, f->name, interface, alt);
ret = config_ep_by_speed(f->config->cdev->gadget, f,
f_fb->out_ep);
if (ret)
return ret;
ret = usb_ep_enable(f_fb->out_ep);
if (ret) {
pr_err("failed to enable out ep: %s\n", strerror(-ret));
return ret;
}
ret = config_ep_by_speed(f->config->cdev->gadget, f,
f_fb->in_ep);
if (ret)
return ret;
ret = usb_ep_enable(f_fb->in_ep);
if (ret) {
pr_err("failed to enable in ep: %s\n", strerror(-ret));
return ret;
}
memset(f_fb->out_req->buf, 0, EP_BUFFER_SIZE);
ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req);
if (ret)
goto err;
return 0;
err:
fastboot_disable(f);
return ret;
}
static void fastboot_free_func(struct usb_function *f)
{
struct f_fastboot *f_fb = container_of(f, struct f_fastboot, func);
free(f_fb);
}
static struct usb_function *fastboot_alloc_func(struct usb_function_instance *fi)
{
struct f_fastboot *f_fb;
f_fb = xzalloc(sizeof(*f_fb));
INIT_LIST_HEAD(&f_fb->variables);
f_fb->func.name = "fastboot";
f_fb->func.strings = fastboot_strings;
f_fb->func.bind = fastboot_bind;
f_fb->func.set_alt = fastboot_set_alt;
f_fb->func.disable = fastboot_disable;
f_fb->func.unbind = fastboot_unbind;
f_fb->func.free_func = fastboot_free_func;
return &f_fb->func;
}
static void fastboot_free_instance(struct usb_function_instance *fi)
{
struct f_fastboot_opts *opts;
opts = container_of(fi, struct f_fastboot_opts, func_inst);
kfree(opts);
}
static struct usb_function_instance *fastboot_alloc_instance(void)
{
struct f_fastboot_opts *opts;
opts = xzalloc(sizeof(*opts));
opts->func_inst.free_func_inst = fastboot_free_instance;
return &opts->func_inst;
}
DECLARE_USB_FUNCTION_INIT(fastboot, fastboot_alloc_instance, fastboot_alloc_func);
static int fastboot_tx_write(struct f_fastboot *f_fb, const char *buffer, unsigned int buffer_size)
{
struct usb_request *in_req = f_fb->in_req;
uint64_t start;
int ret;
memcpy(in_req->buf, buffer, buffer_size);
in_req->length = buffer_size;
in_req_complete = 0;
ret = usb_ep_queue(f_fb->in_ep, in_req);
if (ret)
pr_err("Error %d on queue\n", ret);
start = get_time_ns();
while (!in_req_complete) {
if (is_timeout(start, 2 * SECOND))
return -ETIMEDOUT;
usb_gadget_poll();
}
return 0;
}
static int fastboot_tx_print(struct f_fastboot *f_fb, const char *fmt, ...)
{
char buf[64];
va_list ap;
int n;
va_start(ap, fmt);
n = vsnprintf(buf, 64, fmt, ap);
va_end(ap);
if (n > 64)
n = 64;
return fastboot_tx_write(f_fb, buf, n);
}
static void compl_do_reset(struct usb_ep *ep, struct usb_request *req)
{
reset_cpu(0);
}
static void cb_reboot(struct usb_ep *ep, struct usb_request *req, const char *cmd)
{
struct f_fastboot *f_fb = req->context;
f_fb->in_req->complete = compl_do_reset;
fastboot_tx_print(f_fb, "OKAY");
}
static int strcmp_l1(const char *s1, const char *s2)
{
if (!s1 || !s2)
return -1;
return strncmp(s1, s2, strlen(s1));
}
static void cb_getvar(struct usb_ep *ep, struct usb_request *req, const char *cmd)
{
struct f_fastboot *f_fb = req->context;
struct fb_variable *var;
pr_debug("getvar: \"%s\"\n", cmd);
if (!strcmp_l1(cmd, "all")) {
list_for_each_entry(var, &f_fb->variables, list) {
fastboot_tx_print(f_fb, "INFO%s: %s", var->name, var->value);
}
fastboot_tx_print(f_fb, "OKAY");
return;
}
list_for_each_entry(var, &f_fb->variables, list) {
if (!strcmp(cmd, var->name)) {
fastboot_tx_print(f_fb, "OKAY%s", var->value);
return;
}
}
fastboot_tx_print(f_fb, "OKAY");
}
static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
{
struct f_fastboot *f_fb = req->context;
const unsigned char *buffer = req->buf;
int ret;
if (req->status != 0) {
pr_err("Bad status: %d\n", req->status);
return;
}
ret = write(f_fb->download_fd, buffer, req->actual);
if (ret < 0) {
fastboot_tx_print(f_fb, "FAIL%s", strerror(-ret));
return;
}
f_fb->download_bytes += req->actual;
req->length = f_fb->download_size - f_fb->download_bytes;
if (req->length > EP_BUFFER_SIZE)
req->length = EP_BUFFER_SIZE;
show_progress(f_fb->download_bytes);
/* Check if transfer is done */
if (f_fb->download_bytes >= f_fb->download_size) {
req->complete = rx_handler_command;
req->length = EP_BUFFER_SIZE;
fastboot_tx_print(f_fb, "INFODownloading %d bytes finished",
f_fb->download_bytes);
fastboot_tx_print(f_fb, "OKAY");
printf("\n");
}
req->actual = 0;
usb_ep_queue(ep, req);
}
static void cb_download(struct usb_ep *ep, struct usb_request *req, const char *cmd)
{
struct f_fastboot *f_fb = req->context;
f_fb->download_size = simple_strtoul(cmd, NULL, 16);
f_fb->download_bytes = 0;
fastboot_tx_print(f_fb, "INFODownloading %d bytes...", f_fb->download_size);
init_progression_bar(f_fb->download_size);
f_fb->download_fd = open(FASTBOOT_TMPFILE, O_WRONLY | O_CREAT | O_TRUNC);
if (f_fb->download_fd < 0) {
fastboot_tx_print(f_fb, "FAILInternal Error");
return;
}
if (!f_fb->download_size) {
fastboot_tx_print(f_fb, "FAILdata invalid size");
} else {
fastboot_tx_print(f_fb, "DATA%08x", f_fb->download_size);
req->complete = rx_handler_dl_image;
req->length = EP_BUFFER_SIZE;
if (req->length < ep->maxpacket)
req->length = ep->maxpacket;
}
}
static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
{
int ret;
struct bootm_data data = {
.initrd_address = UIMAGE_INVALID_ADDRESS,
.os_address = UIMAGE_SOME_ADDRESS,
};
pr_info("Booting kernel..\n");
globalvar_set_match("linux.bootargs.dyn.", "");
globalvar_set_match("bootm.", "");
data.os_file = xstrdup(FASTBOOT_TMPFILE);
ret = bootm_boot(&data);
if (ret)
pr_err("Booting failed\n");
}
static void cb_boot(struct usb_ep *ep, struct usb_request *req, const char *opt)
{
struct f_fastboot *f_fb = req->context;
f_fb->in_req->complete = do_bootm_on_complete;
fastboot_tx_print(f_fb, "OKAY");
}
static void cb_flash(struct usb_ep *ep, struct usb_request *req, const char *cmd)
{
struct f_fastboot *f_fb = req->context;
struct file_list_entry *fentry;
int ret;
const char *filename = NULL;
enum filetype filetype = file_name_detect_type(FASTBOOT_TMPFILE);
fastboot_tx_print(f_fb, "INFOCopying file to %s...", cmd);
file_list_for_each_entry(f_fb->files, fentry) {
if (!strcmp(cmd, fentry->name)) {
filename = fentry->filename;
break;
}
}
if (!filename) {
fastboot_tx_print(f_fb, "FAILNo such partition: %s", cmd);
return;
}
if (filetype == filetype_ubi) {
char *cmd = asprintf("ubiformat -y -f %s %s", FASTBOOT_TMPFILE, filename);
fastboot_tx_print(f_fb, "INFOThis is an UBI image...");
ret = run_command(cmd);
free(cmd);
if (ret) {
fastboot_tx_print(f_fb, "FAILwrite partition: %s", strerror(-ret));
return;
}
goto out;
}
ret = copy_file(FASTBOOT_TMPFILE, filename, 1);
if (ret) {
fastboot_tx_print(f_fb, "FAILwrite partition: %s", strerror(-ret));
return;
}
out:
fastboot_tx_print(f_fb, "OKAY");
}
static void cb_erase(struct usb_ep *ep, struct usb_request *req, const char *cmd)
{
struct f_fastboot *f_fb = req->context;
struct file_list_entry *fentry;
int ret;
const char *filename = NULL;
int fd;
fastboot_tx_print(f_fb, "INFOErasing %s...", cmd);
file_list_for_each_entry(f_fb->files, fentry) {
if (!strcmp(cmd, fentry->name)) {
filename = fentry->filename;
break;
}
}
if (!filename) {
fastboot_tx_print(f_fb, "FAILNo such partition: %s", cmd);
return;
}
fd = open(filename, O_RDWR);
if (fd < 0)
fastboot_tx_print(f_fb, "FAIL%s", strerror(-fd));
ret = erase(fd, ~0, 0);
close(fd);
if (ret)
fastboot_tx_print(f_fb, "FAILcannot erase partition %s: %s",
filename, strerror(-ret));
else
fastboot_tx_print(f_fb, "OKAY");
}
struct cmd_dispatch_info {
char *cmd;
void (*cb)(struct usb_ep *ep, struct usb_request *req, const char *opt);
};
static void fb_run_command(struct usb_ep *ep, struct usb_request *req, const char *cmd,
const struct cmd_dispatch_info *cmds, int num_commands)
{
void (*func_cb)(struct usb_ep *ep, struct usb_request *req, const char *cmd) = NULL;
struct f_fastboot *f_fb = req->context;
int i;
for (i = 0; i < num_commands; i++) {
if (!strcmp_l1(cmds[i].cmd, cmd)) {
func_cb = cmds[i].cb;
cmd += strlen(cmds[i].cmd);
func_cb(ep, req, cmd);
return;
}
}
fastboot_tx_print(f_fb, "FAILunknown command %s", cmd);
}
static void cb_oem_getenv(struct usb_ep *ep, struct usb_request *req, const char *cmd)
{
struct f_fastboot *f_fb = req->context;
const char *value;
pr_debug("%s: \"%s\"\n", __func__, cmd);
value = getenv(cmd);
fastboot_tx_print(f_fb, "INFO%s", value ? value : "");
fastboot_tx_print(f_fb, "OKAY");
}
static void cb_oem_setenv(struct usb_ep *ep, struct usb_request *req, const char *cmd)
{
struct f_fastboot *f_fb = req->context;
char *var = xstrdup(cmd);
char *value;
int ret;
pr_debug("%s: \"%s\"\n", __func__, cmd);
value = strchr(var, '=');
if (!value) {
ret = -EINVAL;
goto out;
}
*value++ = 0;
ret = setenv(var, value);
if (ret)
goto out;
fastboot_tx_print(f_fb, "OKAY");
out:
free(var);
if (ret)
fastboot_tx_print(f_fb, "FAIL%s", strerror(-ret));
}
static void cb_oem_exec(struct usb_ep *ep, struct usb_request *req, const char *cmd)
{
struct f_fastboot *f_fb = req->context;
int ret;
ret = run_command(cmd);
if (ret < 0)
fastboot_tx_print(f_fb, "FAIL%s", strerror(-ret));
else if (ret > 0)
fastboot_tx_print(f_fb, "FAIL");
else
fastboot_tx_print(f_fb, "OKAY");
}
static const struct cmd_dispatch_info cmd_oem_dispatch_info[] = {
{
.cmd = "getenv ",
.cb = cb_oem_getenv,
}, {
.cmd = "setenv ",
.cb = cb_oem_setenv,
}, {
.cmd = "exec ",
.cb = cb_oem_exec,
},
};
static void cb_oem(struct usb_ep *ep, struct usb_request *req, const char *cmd)
{
pr_debug("%s: \"%s\"\n", __func__, cmd);
fb_run_command(ep, req, cmd, cmd_oem_dispatch_info, ARRAY_SIZE(cmd_oem_dispatch_info));
}
static const struct cmd_dispatch_info cmd_dispatch_info[] = {
{
.cmd = "reboot",
.cb = cb_reboot,
}, {
.cmd = "getvar:",
.cb = cb_getvar,
}, {
.cmd = "download:",
.cb = cb_download,
}, {
.cmd = "boot",
.cb = cb_boot,
}, {
.cmd = "flash:",
.cb = cb_flash,
}, {
.cmd = "erase:",
.cb = cb_erase,
}, {
.cmd = "oem ",
.cb = cb_oem,
},
};
static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
{
char *cmdbuf = req->buf;
if (req->status != 0)
return;
*(cmdbuf + req->actual) = 0;
fb_run_command(ep, req, cmdbuf, cmd_dispatch_info,
ARRAY_SIZE(cmd_dispatch_info));
*cmdbuf = '\0';
req->actual = 0;
memset(req->buf, 0, EP_BUFFER_SIZE);
usb_ep_queue(ep, req);
}

View File

@ -12,9 +12,11 @@
#include <common.h>
#include <asm/byteorder.h>
#include <linux/err.h>
#include "gadget_chips.h"
#include "u_serial.h"
#include "gadget_chips.h"
/*
* This function packages a simple "generic serial" port with no real
@ -25,18 +27,10 @@
* if you can arrange appropriate host side drivers.
*/
struct gser_descs {
struct usb_endpoint_descriptor *in;
struct usb_endpoint_descriptor *out;
};
struct f_gser {
struct gserial port;
u8 data_id;
u8 port_num;
struct gser_descs fs;
struct gser_descs hs;
};
static inline struct f_gser *func_to_gser(struct usb_function *f)
@ -105,6 +99,34 @@ static struct usb_descriptor_header *gser_hs_function[] = {
NULL,
};
static struct usb_endpoint_descriptor gser_ss_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_endpoint_descriptor gser_ss_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = {
.bLength = sizeof gser_ss_bulk_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
};
static struct usb_descriptor_header *gser_ss_function[] = {
(struct usb_descriptor_header *) &gser_interface_desc,
(struct usb_descriptor_header *) &gser_ss_in_desc,
(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
(struct usb_descriptor_header *) &gser_ss_out_desc,
(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
NULL,
};
/* string descriptors: */
static struct usb_string gser_string_defs[] = {
@ -133,21 +155,25 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (gser->port.in->driver_data) {
DBG(cdev, "reset generic ttyGS%d\n", gser->port_num);
} else {
DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
gser->port.in_desc = ep_choose(cdev->gadget,
gser->hs.in, gser->fs.in);
gser->port.out_desc = ep_choose(cdev->gadget,
gser->hs.out, gser->fs.out);
gserial_connect(&gser->port, gser->port_num);
gserial_disconnect(&gser->port);
}
if (!gser->port.in->desc || !gser->port.out->desc) {
DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
if (config_ep_by_speed(cdev->gadget, f, gser->port.in) ||
config_ep_by_speed(cdev->gadget, f, gser->port.out)) {
gser->port.in->desc = NULL;
gser->port.out->desc = NULL;
return -EINVAL;
}
}
gserial_connect(&gser->port, gser->port_num);
return 0;
}
static void gser_disable(struct usb_function *f)
{
struct f_gser *gser = func_to_gser(f);
struct f_gser *gser = func_to_gser(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
gserial_disconnect(&gser->port);
@ -157,14 +183,25 @@ static void gser_disable(struct usb_function *f)
/* serial function driver setup/binding */
static int
gser_bind(struct usb_configuration *c, struct usb_function *f)
static int gser_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_gser *gser = func_to_gser(f);
int status;
struct usb_ep *ep;
/* REVISIT might want instance-specific strings to help
* distinguish instances ...
*/
/* maybe allocate device-global string ID */
if (gser_string_defs[0].id == 0) {
status = usb_string_id(c->cdev);
if (status < 0)
return status;
gser_string_defs[0].id = status;
}
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
@ -187,36 +224,23 @@ gser_bind(struct usb_configuration *c, struct usb_function *f)
gser->port.out = ep;
ep->driver_data = cdev; /* claim */
/* copy descriptors, and track endpoint copies */
f->descriptors = usb_copy_descriptors(gser_fs_function);
gser->fs.in = usb_find_endpoint(gser_fs_function,
f->descriptors, &gser_fs_in_desc);
gser->fs.out = usb_find_endpoint(gser_fs_function,
f->descriptors, &gser_fs_out_desc);
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
if (gadget_is_dualspeed(c->cdev->gadget)) {
gser_hs_in_desc.bEndpointAddress =
gser_fs_in_desc.bEndpointAddress;
gser_hs_out_desc.bEndpointAddress =
gser_fs_out_desc.bEndpointAddress;
gser_hs_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
gser_hs_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
/* copy descriptors, and track endpoint copies */
f->hs_descriptors = usb_copy_descriptors(gser_hs_function);
gser->hs.in = usb_find_endpoint(gser_hs_function,
f->hs_descriptors, &gser_hs_in_desc);
gser->hs.out = usb_find_endpoint(gser_hs_function,
f->hs_descriptors, &gser_hs_out_desc);
}
gser_ss_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function,
gser_ss_function);
if (status)
goto fail;
DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
gser->port_num,
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
gser->port.in->name, gser->port.out->name);
return 0;
@ -233,50 +257,60 @@ fail:
return status;
}
static void
gser_unbind(struct usb_configuration *c, struct usb_function *f)
static void gser_free_inst(struct usb_function_instance *f)
{
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
kfree(func_to_gser(f));
struct f_serial_opts *opts;
opts = container_of(f, struct f_serial_opts, func_inst);
gserial_free_line(opts->port_num);
kfree(opts);
}
/**
* gser_bind_config - add a generic serial function to a configuration
* @c: the configuration to support the serial instance
* @port_num: /dev/ttyGS* port this interface will use
* Context: single threaded during gadget setup
*
* Returns zero on success, else negative errno.
*
* Caller must have called @gserial_setup() with enough ports to
* handle all the ones it binds. Caller is also responsible
* for calling @gserial_cleanup() before module unload.
*/
int gser_bind_config(struct usb_configuration *c, u8 port_num)
static struct usb_function_instance *gser_alloc_inst(void)
{
struct f_gser *gser;
int status;
struct f_serial_opts *opts;
int ret;
/* REVISIT might want instance-specific strings to help
* distinguish instances ...
*/
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
/* maybe allocate device-global string ID */
if (gser_string_defs[0].id == 0) {
status = usb_string_id(c->cdev);
if (status < 0)
return status;
gser_string_defs[0].id = status;
opts->func_inst.free_func_inst = gser_free_inst;
ret = gserial_alloc_line(&opts->port_num);
if (ret) {
kfree(opts);
return ERR_PTR(ret);
}
/* allocate and initialize one new instance */
gser = kzalloc(sizeof *gser, GFP_KERNEL);
if (!gser)
return -ENOMEM;
return &opts->func_inst;
}
gser->port_num = port_num;
static void gser_free(struct usb_function *f)
{
struct f_gser *serial;
serial = func_to_gser(f);
kfree(serial);
}
static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
{
usb_free_all_descriptors(f);
}
static struct usb_function *gser_alloc(struct usb_function_instance *fi)
{
struct f_gser *gser;
struct f_serial_opts *opts;
/* allocate and initialize one new instance */
gser = kzalloc(sizeof(*gser), GFP_KERNEL);
if (!gser)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_serial_opts, func_inst);
gser->port_num = opts->port_num;
gser->port.func.name = "gser";
gser->port.func.strings = gser_strings;
@ -284,9 +318,12 @@ int gser_bind_config(struct usb_configuration *c, u8 port_num)
gser->port.func.unbind = gser_unbind;
gser->port.func.set_alt = gser_set_alt;
gser->port.func.disable = gser_disable;
gser->port.func.free_func = gser_free;
status = usb_add_function(c, &gser->port.func);
if (status)
kfree(gser);
return status;
return &gser->port.func;
}
DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Al Borchers");
MODULE_AUTHOR("David Brownell");

View File

@ -1047,6 +1047,11 @@ static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req)
req = container_of(_req, struct fsl_req, req);
if (!list_empty(&req->queue)) {
printk("%s: Freeing queued request\n", __func__);
dump_stack();
}
if (_req)
kfree(req);
}
@ -1275,7 +1280,7 @@ static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
int ep_num, stopped, ret = 0;
u32 epctrl;
if (!_ep || !_req)
if (!_ep || !_req || !ep->desc)
return -EINVAL;
stopped = ep->stopped;
@ -1999,58 +2004,33 @@ int usb_gadget_poll(void)
* Hook to gadget drivers
* Called by initialization code of gadget drivers
*----------------------------------------------------------------*/
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
static int fsl_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver)
{
int retval = -ENODEV;
if (!udc_controller)
return -ENODEV;
if (!driver || (driver->speed != USB_SPEED_FULL
&& driver->speed != USB_SPEED_HIGH)
|| !driver->bind || !driver->disconnect
|| !driver->setup)
return -EINVAL;
if (udc_controller->driver)
return -EBUSY;
/*
* We currently have PHY no driver which could call vbus_connect,
* so when the USB gadget core calls usb_gadget_connect() the
* driver decides to disable the device because it has no vbus.
* Work around this by enabling vbus here.
*/
usb_gadget_vbus_connect(gadget);
/* hook up the driver */
udc_controller->driver = driver;
/* bind udc driver to gadget driver */
retval = driver->bind(&udc_controller->gadget);
if (retval) {
VDBG("bind to gadget --> %d", retval);
udc_controller->driver = NULL;
goto out;
}
/* Enable DR IRQ reg and Set usbcmd reg Run bit */
dr_controller_run(udc_controller);
udc_controller->usb_state = USB_STATE_ATTACHED;
udc_controller->ep0_state = WAIT_FOR_SETUP;
udc_controller->ep0_dir = 0;
out:
if (retval)
printk(KERN_WARNING "gadget driver register failed %d\n",
retval);
return retval;
return 0;
}
EXPORT_SYMBOL(usb_gadget_register_driver);
/* Disconnect from gadget driver */
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
static int fsl_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver)
{
struct fsl_ep *loop_ep;
if (!udc_controller)
return -ENODEV;
if (!driver || driver != udc_controller->driver || !driver->unbind)
return -EINVAL;
/* stop DR, disable intr */
dr_controller_stop(udc_controller);
@ -2066,16 +2046,8 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
ep.ep_list)
nuke(loop_ep, -ESHUTDOWN);
/* report disconnect; the controller is already quiesced */
driver->disconnect(&udc_controller->gadget);
/* unbind gadget and unhook driver. */
driver->unbind(&udc_controller->gadget);
udc_controller->driver = NULL;
return 0;
}
EXPORT_SYMBOL(usb_gadget_unregister_driver);
static int struct_udc_setup(struct fsl_udc *udc,
struct device_d *dev)
@ -2202,12 +2174,14 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on)
udc = container_of(gadget, struct fsl_udc, gadget);
udc->softconnect = (is_on != 0);
if (can_pullup(udc))
if (can_pullup(udc)) {
writel((readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
&dr_regs->usbcmd);
else
} else {
writel((readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
&dr_regs->usbcmd);
}
return 0;
}
@ -2220,6 +2194,8 @@ static struct usb_gadget_ops fsl_gadget_ops = {
.vbus_session = fsl_vbus_session,
.vbus_draw = fsl_vbus_draw,
.pullup = fsl_pullup,
.udc_start = fsl_udc_start,
.udc_stop = fsl_udc_stop,
};
/*----------------------------------------------------------------
@ -2243,7 +2219,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
/* for ep0: maxP defined in desc
* for other eps, maxP is set by epautoconfig() called by gadget layer
*/
ep->ep.maxpacket = (unsigned short) ~0;
usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
/* the queue lists any req for this ep */
INIT_LIST_HEAD(&ep->queue);
@ -2300,10 +2276,10 @@ int ci_udc_register(struct device_d *dev, void __iomem *regs)
/* Setup gadget structure */
udc_controller->gadget.ops = &fsl_gadget_ops;
udc_controller->gadget.is_dualspeed = 1;
udc_controller->gadget.ep0 = &udc_controller->eps[0].ep;
INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
udc_controller->gadget.max_speed = USB_SPEED_HIGH;
udc_controller->gadget.name = "fsl-usb2-udc";
/* setup QH and epctrl for ep0 */
@ -2330,6 +2306,11 @@ int ci_udc_register(struct device_d *dev, void __iomem *regs)
poller_register(&poller);
ret = usb_add_gadget_udc_release(dev, &udc_controller->gadget,
NULL);
if (ret)
goto err_out;
return 0;
err_out:
return ret;

View File

@ -0,0 +1,99 @@
#include <common.h>
#include <linux/err.h>
#include <usb/composite.h>
static LIST_HEAD(func_list);
static struct usb_function_instance *try_get_usb_function_instance(const char *name)
{
struct usb_function_driver *fd;
struct usb_function_instance *fi;
fi = ERR_PTR(-ENOENT);
list_for_each_entry(fd, &func_list, list) {
if (strcmp(name, fd->name))
continue;
fi = fd->alloc_inst();
if (!IS_ERR(fi))
fi->fd = fd;
break;
}
return fi;
}
struct usb_function_instance *usb_get_function_instance(const char *name)
{
struct usb_function_instance *fi;
int ret;
fi = try_get_usb_function_instance(name);
if (!IS_ERR(fi))
return fi;
ret = PTR_ERR(fi);
if (ret != -ENOENT)
return fi;
return try_get_usb_function_instance(name);
}
EXPORT_SYMBOL_GPL(usb_get_function_instance);
struct usb_function *usb_get_function(struct usb_function_instance *fi)
{
struct usb_function *f;
f = fi->fd->alloc_func(fi);
if (IS_ERR(f))
return f;
f->fi = fi;
return f;
}
EXPORT_SYMBOL_GPL(usb_get_function);
void usb_put_function_instance(struct usb_function_instance *fi)
{
struct module *mod;
if (!fi)
return;
mod = fi->fd->mod;
fi->free_func_inst(fi);
}
EXPORT_SYMBOL_GPL(usb_put_function_instance);
void usb_put_function(struct usb_function *f)
{
if (!f)
return;
f->free_func(f);
}
EXPORT_SYMBOL_GPL(usb_put_function);
int usb_function_register(struct usb_function_driver *newf)
{
struct usb_function_driver *fd;
int ret;
ret = -EEXIST;
list_for_each_entry(fd, &func_list, list) {
if (!strcmp(fd->name, newf->name))
goto out;
}
ret = 0;
list_add_tail(&newf->list, &func_list);
out:
return ret;
}
EXPORT_SYMBOL_GPL(usb_function_register);
void usb_function_unregister(struct usb_function_driver *fd)
{
list_del(&fd->list);
}
EXPORT_SYMBOL_GPL(usb_function_unregister);

View File

@ -1,7 +1,55 @@
#define gadget_is_pxa(x) 0
#define gadget_is_goku(x) 0
#define gadget_is_sh(x) 0
#define gadget_is_mq11xx(x) 0
#define gadget_is_net2280(x) 0
#define gadget_is_sa1100(x) 0
/*
* USB device controllers have lots of quirks. Use these macros in
* gadget drivers or other code that needs to deal with them, and which
* autoconfigures instead of using early binding to the hardware.
*
* This SHOULD eventually work like the ARM mach_is_*() stuff, driven by
* some config file that gets updated as new hardware is supported.
* (And avoiding all runtime comparisons in typical one-choice configs!)
*
* NOTE: some of these controller drivers may not be available yet.
* Some are available on 2.4 kernels; several are available, but not
* yet pushed in the 2.6 mainline tree.
*/
#ifndef __GADGET_CHIPS_H
#define __GADGET_CHIPS_H
#include <usb/gadget.h>
/*
* NOTICE: the entries below are alphabetical and should be kept
* that way.
*
* Always be sure to add new entries to the correct position or
* accept the bashing later.
*
* If you have forgotten the alphabetical order let VIM/EMACS
* do that for you.
*/
#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name))
#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name))
#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name))
#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name))
#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name))
#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name))
/**
* gadget_supports_altsettings - return true if altsettings work
* @gadget: the gadget in question
*/
static inline bool gadget_supports_altsettings(struct usb_gadget *gadget)
{
/* PXA 21x/25x/26x has no altsettings at all */
if (gadget_is_pxa(gadget))
return false;
/* PXA 27x and 3xx have *broken* altsetting support */
if (gadget_is_pxa27x(gadget))
return false;
/* Everything else is *presumably* fine ... */
return true;
}
#endif /* __GADGET_CHIPS_H */

248
drivers/usb/gadget/multi.c Normal file
View File

@ -0,0 +1,248 @@
/*
* multi.c -- Multifunction Composite driver
*
* Copyright (C) 2008 David Brownell
* Copyright (C) 2008 Nokia Corporation
* Copyright (C) 2009 Samsung Electronics
* Author: Michal Nazarewicz (mina86@mina86.com)
*
* 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 <common.h>
#include <usb/gadget-multi.h>
#include <linux/err.h>
#include "u_serial.h"
#define DRIVER_DESC "Multifunction Composite Gadget"
/***************************** Device Descriptor ****************************/
#define MULTI_VENDOR_NUM 0x1d6b /* Linux Foundation */
#define MULTI_PRODUCT_NUM 0x0104 /* Multifunction Composite Gadget */
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_MISC /* 0xEF */,
.bDeviceSubClass = 2,
.bDeviceProtocol = 1,
};
#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = "",
[USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = "Multifunction Composite Gadget",
{ } /* end of list */
};
static struct usb_gadget_strings *dev_strings[] = {
&(struct usb_gadget_strings){
.language = 0x0409, /* en-us */
.strings = strings_dev,
},
NULL,
};
static struct usb_function_instance *fi_acm;
static struct usb_function *f_acm;
static struct usb_function_instance *fi_dfu;
static struct usb_function *f_dfu;
static struct usb_function_instance *fi_fastboot;
static struct usb_function *f_fastboot;
static struct usb_configuration config = {
.bConfigurationValue = 1,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
struct f_multi_opts *gadget_multi_opts;
static int multi_bind_acm(struct usb_composite_dev *cdev)
{
int ret;
fi_acm = usb_get_function_instance("acm");
if (IS_ERR(fi_acm)) {
ret = PTR_ERR(fi_acm);
fi_acm = NULL;
return ret;
}
f_acm = usb_get_function(fi_acm);
if (IS_ERR(f_acm)) {
ret = PTR_ERR(f_acm);
f_acm = NULL;
return ret;
}
return usb_add_function(&config, f_acm);
}
static int multi_bind_dfu(struct usb_composite_dev *cdev)
{
int ret;
struct f_dfu_opts *opts;
fi_dfu = usb_get_function_instance("dfu");
if (IS_ERR(fi_dfu)) {
ret = PTR_ERR(fi_dfu);
fi_dfu = NULL;
return ret;
}
opts = container_of(fi_dfu, struct f_dfu_opts, func_inst);
opts->files = gadget_multi_opts->dfu_opts.files;
f_dfu = usb_get_function(fi_dfu);
if (IS_ERR(f_dfu)) {
ret = PTR_ERR(f_dfu);
f_dfu = NULL;
return ret;
}
return usb_add_function(&config, f_dfu);
}
static int multi_bind_fastboot(struct usb_composite_dev *cdev)
{
int ret;
struct f_fastboot_opts *opts;
fi_fastboot = usb_get_function_instance("fastboot");
if (IS_ERR(fi_fastboot)) {
ret = PTR_ERR(fi_fastboot);
fi_fastboot = NULL;
return ret;
}
opts = container_of(fi_fastboot, struct f_fastboot_opts, func_inst);
opts->files = gadget_multi_opts->fastboot_opts.files;
f_fastboot = usb_get_function(fi_fastboot);
if (IS_ERR(f_fastboot)) {
ret = PTR_ERR(f_fastboot);
f_fastboot = NULL;
return ret;
}
return usb_add_function(&config, f_fastboot);
}
static int multi_unbind(struct usb_composite_dev *cdev)
{
if (gadget_multi_opts->create_acm) {
usb_put_function(f_acm);
usb_put_function_instance(fi_acm);
}
if (gadget_multi_opts->dfu_opts.files) {
usb_put_function(f_dfu);
usb_put_function_instance(fi_dfu);
}
if (gadget_multi_opts->fastboot_opts.files) {
usb_put_function(f_fastboot);
usb_put_function_instance(fi_fastboot);
}
return 0;
}
static int multi_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
int ret;
/* allocate string IDs */
ret = usb_string_ids_tab(cdev, strings_dev);
if (ret < 0)
return ret;
if (gadget->vendor_id && gadget->product_id) {
device_desc.idVendor = cpu_to_le16(gadget->vendor_id);
device_desc.idProduct = cpu_to_le16(gadget->product_id);
} else {
device_desc.idVendor = cpu_to_le16(MULTI_VENDOR_NUM);
device_desc.idProduct = cpu_to_le16(MULTI_PRODUCT_NUM);
}
strings_dev[USB_GADGET_MANUFACTURER_IDX].s = gadget->manufacturer;
strings_dev[USB_GADGET_PRODUCT_IDX].s = gadget->productname;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
config.label = strings_dev[STRING_DESCRIPTION_IDX].s;
config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id;
ret = usb_add_config_only(cdev, &config);
if (ret)
return ret;
if (gadget_multi_opts->fastboot_opts.files) {
printf("%s: creating Fastboot function\n", __func__);
ret = multi_bind_fastboot(cdev);
if (ret)
goto out;
}
if (gadget_multi_opts->dfu_opts.files) {
printf("%s: creating DFU function\n", __func__);
ret = multi_bind_dfu(cdev);
if (ret)
goto out;
}
if (gadget_multi_opts->create_acm) {
printf("%s: creating ACM function\n", __func__);
ret = multi_bind_acm(cdev);
if (ret)
goto out;
}
usb_ep_autoconfig_reset(cdev->gadget);
dev_info(&gadget->dev, DRIVER_DESC "\n");
return 0;
out:
multi_unbind(cdev);
return ret;
}
static struct usb_composite_driver multi_driver = {
.name = "g_multi",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = multi_bind,
.unbind = multi_unbind,
.needs_serial = 1,
};
int usb_multi_register(struct f_multi_opts *opts)
{
gadget_multi_opts = opts;
return usb_composite_probe(&multi_driver);
}
void usb_multi_unregister(void)
{
if (gadget_multi_opts)
usb_composite_unregister(&multi_driver);
gadget_multi_opts = NULL;
}

View File

@ -882,11 +882,16 @@ static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
return 0;
}
static int pxa_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver);
static int pxa_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver);
static const struct usb_gadget_ops pxa_udc_ops = {
.get_frame = pxa_udc_get_frame,
.wakeup = pxa_udc_wakeup,
.pullup = pxa_udc_pullup,
.vbus_session = pxa_udc_vbus_session,
.udc_start = pxa_udc_start,
.udc_stop = pxa_udc_stop,
};
static void clk_enable(void)
@ -976,40 +981,20 @@ static void udc_enable(struct pxa_udc *udc)
udc->enabled = 1;
}
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
static int pxa_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver)
{
struct pxa_udc *udc = the_controller;
int retval;
if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind
|| !driver->disconnect || !driver->setup)
return -EINVAL;
if (!udc)
return -ENODEV;
if (udc->driver)
return -EBUSY;
/* first hook up the driver ... */
udc->driver = driver;
dplus_pullup(udc, 1);
retval = driver->bind(&udc->gadget);
if (retval) {
dev_err(udc->dev, "bind to function %s --> error %d\n",
driver->function, retval);
goto bind_fail;
}
dev_dbg(udc->dev, "registered gadget function '%s'\n",
driver->function);
if (should_enable_udc(udc))
udc_enable(udc);
return 0;
bind_fail:
return retval;
}
EXPORT_SYMBOL(usb_gadget_register_driver);
static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver)
{
@ -1027,7 +1012,7 @@ static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver)
driver->disconnect(&udc->gadget);
}
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
static int pxa_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver)
{
struct pxa_udc *udc = the_controller;
@ -1038,7 +1023,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
stop_activity(udc, driver);
udc_disable(udc);
dplus_pullup(udc, 0);
driver->disconnect(&udc->gadget);
driver->unbind(&udc->gadget);
@ -1050,7 +1034,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
*/
return 0;
}
EXPORT_SYMBOL(usb_gadget_unregister_driver);
static void handle_ep0_ctrl_req(struct pxa_udc *udc,
struct pxa27x_request *req)
@ -1481,7 +1464,7 @@ static struct poller_struct poller = {
static int __init pxa_udc_probe(struct device_d *dev)
{
struct pxa_udc *udc = &memory;
int gpio;
int gpio, ret;
udc->regs = dev_request_mem_region(dev, 0);
if (!udc->regs)
@ -1503,6 +1486,10 @@ static int __init pxa_udc_probe(struct device_d *dev)
pxa_eps_setup(udc);
poller_register(&poller);
ret = usb_add_gadget_udc_release(dev, &udc->gadget, NULL);
if (ret)
return ret;
return 0;
}

View File

@ -1,6 +1,19 @@
/*
* serial.c -- USB gadget serial driver
*
* Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
* Copyright (C) 2008 by David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
* either version 2 of that License or (at your option) any later version.
*/
#include <common.h>
#include <errno.h>
#include <init.h>
#include <linux/err.h>
#include <usb/ch9.h>
#include <usb/gadget.h>
#include <usb/composite.h>
@ -8,6 +21,8 @@
#include <asm/byteorder.h>
#include "u_serial.h"
#include "gadget_chips.h"
/* Defines */
@ -17,6 +32,9 @@
#define GS_LONG_NAME "Gadget Serial"
#define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR
/*-------------------------------------------------------------------------*/
static struct usb_composite_overwrite coverwrite;
/* Thanks to NetChip Technologies for donating this product ID.
*
* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
@ -29,15 +47,12 @@
/* string IDs are assigned dynamically */
#define STRING_MANUFACTURER_IDX 0
#define STRING_PRODUCT_IDX 1
#define STRING_DESCRIPTION_IDX 2
static char manufacturer[50];
#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_string strings_dev[] = {
[STRING_MANUFACTURER_IDX].s = manufacturer,
[STRING_PRODUCT_IDX].s = GS_VERSION_NAME,
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = GS_VERSION_NAME,
[USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */,
{ } /* end of list */
};
@ -52,30 +67,6 @@ static struct usb_gadget_strings *dev_strings[] = {
NULL,
};
static int use_acm = 1;
#ifdef HAVE_OBEX
static int use_obex = 0;
#endif
static unsigned n_ports = 1;
static int serial_bind_config(struct usb_configuration *c)
{
unsigned i;
int status = 0;
for (i = 0; i < n_ports && status == 0; i++) {
if (use_acm)
status = acm_bind_config(c, i);
#ifdef HAVE_OBEX
else if (use_obex)
status = obex_bind_config(c, i);
#endif
else
status = gser_bind_config(c, i);
}
return status;
}
static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
@ -84,99 +75,180 @@ static struct usb_device_descriptor device_desc = {
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
/* .bMaxPacketSize0 = f(hardware) */
.idVendor = cpu_to_le16(GS_VENDOR_ID),
/* .idProduct = f(use_acm) */
/* .bcdDevice = f(hardware) */
.bcdDevice = cpu_to_le16(GS_VERSION_NUM),
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
.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,
};
/*-------------------------------------------------------------------------*/
/* Module */
MODULE_DESCRIPTION(GS_VERSION_NAME);
MODULE_AUTHOR("Al Borchers");
MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");
static bool use_acm = true;
static bool use_obex = false;
static unsigned n_ports = 1;
/*-------------------------------------------------------------------------*/
static struct usb_configuration serial_config_driver = {
/* .label = f(use_acm) */
.bind = serial_bind_config,
/* .bConfigurationValue = f(use_acm) */
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
static int gs_bind(struct usb_composite_dev *cdev)
{
int gcnum;
struct usb_gadget *gadget = cdev->gadget;
int status;
static struct usb_function_instance *fi_serial[MAX_U_SERIAL_PORTS];
static struct usb_function *f_serial[MAX_U_SERIAL_PORTS];
status = gserial_setup(cdev->gadget, n_ports);
if (status < 0)
return status;
static int serial_register_ports(struct usb_composite_dev *cdev,
struct usb_configuration *c, const char *f_name)
{
int i;
int ret;
ret = usb_add_config_only(cdev, c);
if (ret)
goto out;
for (i = 0; i < n_ports; i++) {
fi_serial[i] = usb_get_function_instance(f_name);
if (IS_ERR(fi_serial[i])) {
ret = PTR_ERR(fi_serial[i]);
goto fail;
}
f_serial[i] = usb_get_function(fi_serial[i]);
if (IS_ERR(f_serial[i])) {
ret = PTR_ERR(f_serial[i]);
goto err_get_func;
}
ret = usb_add_function(c, f_serial[i]);
if (ret)
goto err_add_func;
}
return 0;
err_add_func:
usb_put_function(f_serial[i]);
err_get_func:
usb_put_function_instance(fi_serial[i]);
fail:
i--;
while (i >= 0) {
usb_remove_function(c, f_serial[i]);
usb_put_function(f_serial[i]);
usb_put_function_instance(fi_serial[i]);
i--;
}
out:
return ret;
}
static int __init gs_bind(struct usb_composite_dev *cdev)
{
int status;
struct usb_gadget *gadget = cdev->gadget;
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
/* device description: manufacturer, product */
sprintf(manufacturer, "barebox with %s",
gadget->name);
status = usb_string_id(cdev);
if (gadget->vendor_id && gadget->product_id) {
device_desc.idVendor = cpu_to_le16(gadget->vendor_id);
device_desc.idProduct = cpu_to_le16(gadget->product_id);
} else {
device_desc.idVendor = cpu_to_le16(GS_VENDOR_ID);
if (use_acm)
device_desc.idProduct = cpu_to_le16(GS_CDC_PRODUCT_ID);
else
device_desc.idProduct = cpu_to_le16(GS_PRODUCT_ID);
}
strings_dev[USB_GADGET_MANUFACTURER_IDX].s = gadget->manufacturer;
strings_dev[USB_GADGET_PRODUCT_IDX].s = gadget->productname;
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail;
strings_dev[STRING_MANUFACTURER_IDX].id = status;
device_desc.iManufacturer = status;
status = usb_string_id(cdev);
if (status < 0)
goto fail;
strings_dev[STRING_PRODUCT_IDX].id = status;
device_desc.iProduct = status;
/* config description */
status = usb_string_id(cdev);
if (status < 0)
goto fail;
strings_dev[STRING_DESCRIPTION_IDX].id = status;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
status = strings_dev[STRING_DESCRIPTION_IDX].id;
serial_config_driver.iConfiguration = status;
/* set up other descriptors */
// gcnum = usb_gadget_controller_number(gadget);
gcnum = 0x19;
if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16(GS_VERSION_NUM | gcnum);
else {
/* this is so simple (for now, no altsettings) that it
* SHOULD NOT have problems with bulk-capable hardware.
* so warn about unrcognized controllers -- don't panic.
*
* things like configuration and altsetting numbering
* can need hardware-specific attention though.
*/
pr_warning("gs_bind: controller '%s' not recognized\n",
gadget->name);
device_desc.bcdDevice =
cpu_to_le16(GS_VERSION_NUM | 0x0099);
if (gadget_is_otg(cdev->gadget)) {
serial_config_driver.descriptors = otg_desc;
serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
/* register our configuration */
status = usb_add_config(cdev, &serial_config_driver);
if (use_acm) {
status = serial_register_ports(cdev, &serial_config_driver,
"acm");
usb_ep_autoconfig_reset(cdev->gadget);
} else if (use_obex)
status = serial_register_ports(cdev, &serial_config_driver,
"obex");
else {
status = serial_register_ports(cdev, &serial_config_driver,
"gser");
}
if (status < 0)
goto fail;
usb_composite_overwrite_options(cdev, &coverwrite);
INFO(cdev, "%s\n", GS_VERSION_NAME);
return 0;
fail:
// gserial_cleanup();
return status;
}
static int gs_unbind(struct usb_composite_dev *cdev)
{
int i;
for (i = 0; i < n_ports; i++) {
usb_put_function(f_serial[i]);
usb_put_function_instance(fi_serial[i]);
}
return 0;
}
static struct usb_composite_driver gserial_driver = {
.name = "g_serial",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER,
.bind = gs_bind,
.unbind = gs_unbind,
};
int usb_serial_register(struct usb_serial_pdata *pdata)
@ -185,65 +257,27 @@ int usb_serial_register(struct usb_serial_pdata *pdata)
* but neither of these product IDs was defined that way.
*/
use_acm = pdata->acm;
/*
* PXA CPU suffer a silicon bug which prevents them from being a
* compound device, forbiding the ACM configurations.
*/
#ifdef CONFIG_ARCH_PXA2XX
use_acm = 0;
#endif
switch (pdata->mode) {
case 1:
#ifdef HAVE_OBEX
use_obex = 1;
#endif
if (IS_ENABLED(CONFIG_ARCH_PXA2XX))
use_acm = 0;
break;
case 2:
#ifdef HAVE_OBEX
use_obex = 1;
#endif
use_acm = 0;
break;
default:
#ifdef HAVE_OBEX
use_obex = 0;
#endif
use_acm = 1;
}
if (use_acm) {
serial_config_driver.label = "CDC ACM config";
serial_config_driver.bConfigurationValue = 2;
device_desc.bDeviceClass = USB_CLASS_COMM;
device_desc.idProduct =
cpu_to_le16(GS_CDC_PRODUCT_ID);
}
#ifdef HAVE_OBEX
else if (use_obex) {
serial_config_driver.label = "CDC OBEX config";
serial_config_driver.bConfigurationValue = 3;
device_desc.bDeviceClass = USB_CLASS_COMM;
device_desc.idProduct =
cpu_to_le16(GS_CDC_OBEX_PRODUCT_ID);
}
#endif
else {
} else {
serial_config_driver.label = "Generic Serial config";
serial_config_driver.bConfigurationValue = 1;
device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
device_desc.idProduct =
cpu_to_le16(GS_PRODUCT_ID);
}
strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label;
if (pdata->idVendor)
device_desc.idVendor = pdata->idVendor;
if (pdata->idProduct)
device_desc.idProduct = pdata->idProduct;
strings_dev[STRING_MANUFACTURER_IDX].s = pdata->manufacturer;
strings_dev[STRING_PRODUCT_IDX].s = pdata->productname;
return usb_composite_register(&gserial_driver);
return usb_composite_probe(&gserial_driver);
}
void usb_serial_unregister(void)

View File

@ -22,6 +22,8 @@
#include <usb/cdc.h>
#include <kfifo.h>
#include <clock.h>
#include <linux/err.h>
#include <dma.h>
#include "u_serial.h"
@ -31,11 +33,12 @@
* "serial port" functionality through the USB gadget stack. Each such
* port is exposed through a /dev/ttyGS* node.
*
* After initialization (gserial_setup), these TTY port devices stay
* available until they are removed (gserial_cleanup). Each one may be
* connected to a USB function (gserial_connect), or disconnected (with
* gserial_disconnect) when the USB host issues a config change event.
* Data can only flow when the port is connected to the host.
* After this module has been loaded, the individual TTY port can be requested
* (gserial_alloc_line()) and it will stay available until they are removed
* (gserial_free_line()). Each one may be connected to a USB function
* (gserial_connect), or disconnected (with gserial_disconnect) when the USB
* host issues a config change event. Data can only flow when the port is
* connected to the host.
*
* A given TTY port can be made available in multiple configurations.
* For example, each one might expose a ttyGS0 node which provides a
@ -74,21 +77,27 @@
* next layer of buffering. For TX that's a circular buffer; for RX
* consider it a NOP. A third layer is provided by the TTY code.
*/
#define QUEUE_SIZE 128
#define QUEUE_SIZE 16
#define WRITE_BUF_SIZE 8192 /* TX only */
#define RECV_FIFO_SIZE (1024 * 8)
/* circular buffer */
struct gs_buf {
unsigned buf_size;
char *buf_buf;
char *buf_get;
char *buf_put;
};
/*
* The port structure holds info for each port, one for each minor number
* (and thus for each /dev/ node).
*/
struct gs_port {
struct gserial *port_usb;
struct console_device cdev;
struct kfifo *recv_fifo;
unsigned open_count;
int openclose; /* open/close in progress */
u8 port_num;
struct list_head read_pool;
@ -100,12 +109,9 @@ struct gs_port {
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
};
/* increase N_PORTS if you need more */
#define N_PORTS 4
static struct portmaster {
struct gs_port *port;
} ports[N_PORTS];
static unsigned n_ports;
} ports[MAX_U_SERIAL_PORTS];
#define GS_CLOSE_TIMEOUT 15 /* seconds */
@ -162,6 +168,10 @@ static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
gs_start_rx(port);
}
/*-------------------------------------------------------------------------*/
/* I/O glue between TTY (upper) and USB function (lower) driver layers */
static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
{
struct gs_port *port = ep->driver_data;
@ -176,7 +186,6 @@ static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
/* FALL THROUGH */
case 0:
/* normal completion */
// gs_start_tx(port);
break;
case -ESHUTDOWN:
@ -201,11 +210,24 @@ gs_alloc_req(struct usb_ep *ep, unsigned len)
if (req != NULL) {
req->length = len;
req->buf = xmemalign(32, len);
req->buf = dma_alloc(len);
}
return req;
}
EXPORT_SYMBOL_GPL(gs_alloc_req);
/*
* gs_free_req
*
* Free a usb_request and its buffer.
*/
void gs_free_req(struct usb_ep *ep, struct usb_request *req)
{
kfree(req->buf);
usb_ep_free_request(ep, req);
}
EXPORT_SYMBOL_GPL(gs_free_req);
static void gs_free_requests(struct usb_ep *ep, struct list_head *head)
{
@ -276,9 +298,7 @@ static int gs_start_io(struct gs_port *port)
started = gs_start_rx(port);
/* unblock any pending writes into our circular buffer */
if (started) {
// tty_wakeup(port->port_tty);
} else {
if (!started) {
gs_free_requests(ep, head);
gs_free_requests(port->port_usb->in, &port->write_pool);
status = -EIO;
@ -287,76 +307,84 @@ static int gs_start_io(struct gs_port *port)
return status;
}
/*
* gs_free_req
*
* Free a usb_request and its buffer.
*/
void gs_free_req(struct usb_ep *ep, struct usb_request *req)
{
kfree(req->buf);
usb_ep_free_request(ep, req);
}
/*-------------------------------------------------------------------------*/
static int __init
static int
gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
{
struct gs_port *port;
int ret = 0;
if (ports[port_num].port) {
ret = -EBUSY;
goto out;
}
port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
if (port == NULL)
return -ENOMEM;
port->port_num = port_num;
port->port_line_coding = *coding;
if (port == NULL) {
ret = -ENOMEM;
goto out;
}
INIT_LIST_HEAD(&port->read_pool);
INIT_LIST_HEAD(&port->write_pool);
ports[port_num].port = port;
port->port_num = port_num;
port->port_line_coding = *coding;
return 0;
ports[port_num].port = port;
out:
return ret;
}
/**
* gserial_setup - initialize TTY driver for one or more ports
* @g: gadget to associate with these ports
* @count: how many ports to support
* Context: may sleep
*
* The TTY stack needs to know in advance how many devices it should
* plan to manage. Use this call to set up the ports you will be
* exporting through USB. Later, connect them to functions based
* on what configuration is activated by the USB host; and disconnect
* them as appropriate.
*
* An example would be a two-configuration device in which both
* configurations expose port 0, but through different functions.
* One configuration could even expose port 1 while the other
* one doesn't.
*
* Returns negative errno or zero.
*/
int __init gserial_setup(struct usb_gadget *g, unsigned count)
static void gserial_free_port(struct gs_port *port)
{
kfree(port);
}
void gserial_free_line(unsigned char port_num)
{
struct gs_port *port;
if (WARN_ON(!ports[port_num].port))
return;
port = ports[port_num].port;
ports[port_num].port = NULL;
gserial_free_port(port);
}
EXPORT_SYMBOL_GPL(gserial_free_line);
int gserial_alloc_line(unsigned char *line_num)
{
struct usb_cdc_line_coding coding;
int i, status;
int ret;
int port_num;
/* make devices be openable */
for (i = 0; i < count; i++) {
status = gs_port_alloc(i, &coding);
if (status) {
count = i;
goto fail;
}
coding.dwDTERate = cpu_to_le32(9600);
coding.bCharFormat = 8;
coding.bParityType = USB_CDC_NO_PARITY;
coding.bDataBits = USB_CDC_1_STOP_BITS;
for (port_num = 0; port_num < MAX_U_SERIAL_PORTS; port_num++) {
ret = gs_port_alloc(port_num, &coding);
if (ret == -EBUSY)
continue;
if (ret)
return ret;
break;
}
n_ports = count;
return 0;
fail:
while (count--)
kfree(ports[count].port);
return status;
if (ret)
return ret;
/* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
*line_num = port_num;
return ret;
}
EXPORT_SYMBOL_GPL(gserial_alloc_line);
static void serial_putc(struct console_device *cdev, char c)
{
@ -422,33 +450,59 @@ timeout:
static void serial_flush(struct console_device *cdev)
{
}
static int serial_setbaudrate(struct console_device *cdev, int baudrate)
{
return 0;
}
static struct console_device *mycdev;
/**
* gserial_connect - notify TTY I/O glue that USB link is active
* @gser: the function, set up with endpoints and descriptors
* @port_num: which port is active
* Context: any (usually from irq)
*
* This is called activate endpoints and let the TTY layer know that
* the connection is active ... not unlike "carrier detect". It won't
* necessarily start I/O queues; unless the TTY is held open by any
* task, there would be no point. However, the endpoints will be
* activated so the USB host can perform I/O, subject to basic USB
* hardware flow control.
*
* Caller needs to have set up the endpoints and USB function in @dev
* before calling this, as well as the appropriate (speed-specific)
* endpoint descriptors, and also have allocate @port_num by calling
* @gserial_alloc_line().
*
* Returns negative errno or zero.
* On success, ep->driver_data will be overwritten.
*/
int gserial_connect(struct gserial *gser, u8 port_num)
{
struct gs_port *port;
int status;
struct console_device *cdev;
/* we "know" gserial_cleanup() hasn't been called */
port = ports[port_num].port;
if (port_num >= MAX_U_SERIAL_PORTS)
return -ENXIO;
/* In case of multiple activation (ie. multiple SET_INTERFACE) */
if (port->port_usb)
return 0;
port = ports[port_num].port;
if (!port) {
pr_err("serial line %d not allocated.\n", port_num);
return -EINVAL;
}
if (port->port_usb) {
pr_err("serial line %d is in use.\n", port_num);
return -EBUSY;
}
/* activate the endpoints */
status = usb_ep_enable(gser->in, gser->in_desc);
status = usb_ep_enable(gser->in);
if (status < 0)
return status;
gser->in->driver_data = port;
status = usb_ep_enable(gser->out, gser->out_desc);
status = usb_ep_enable(gser->out);
if (status < 0)
goto fail_out;
gser->out->driver_data = port;
@ -481,7 +535,21 @@ int gserial_connect(struct gserial *gser, u8 port_num)
if (status)
goto fail_out;
mycdev = cdev;
dev_set_param(&cdev->class_dev, "active", "ioe");
/* REVISIT if waiting on "carrier detect", signal. */
/* if it's already open, start I/O ... and notify the serial
* protocol about open/close status (connect/disconnect).
*/
if (1) {
pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
if (gser->connect)
gser->connect(gser);
} else {
if (gser->disconnect)
gser->disconnect(gser);
}
return status;
@ -490,27 +558,7 @@ fail_out:
gser->in->driver_data = NULL;
return status;
}
#include <command.h>
static int do_mycdev(int argc, char *argv[])
{
int i,j;
for (i = 'a'; i < 'z'; i++) {
mycdev->putc(mycdev, i);
printf("%c", i);
mdelay(500);
for (j = 0; j < 100; j++)
usb_gadget_poll();
}
return 0;
}
BAREBOX_CMD_START(mycdev)
.cmd = do_mycdev,
BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
BAREBOX_CMD_COMPLETE(empty_complete)
BAREBOX_CMD_END
EXPORT_SYMBOL_GPL(gserial_connect);
/**
* gserial_disconnect - notify TTY I/O glue that USB link is inactive
@ -531,7 +579,10 @@ void gserial_disconnect(struct gserial *gser)
if (!port)
return;
cdev = &port->cdev;
/* tell the TTY glue not to do I/O here any more */
console_unregister(cdev);
/* REVISIT as above: how best to track this? */
port->port_line_coding = gser->port_line_coding;
@ -550,6 +601,5 @@ void gserial_disconnect(struct gserial *gser)
gs_free_requests(gser->out, &port->read_pool);
gs_free_requests(gser->in, &port->write_pool);
cdev = &port->cdev;
console_unregister(cdev);
kfifo_free(port->recv_fifo);
}

View File

@ -15,6 +15,13 @@
#include <usb/composite.h>
#include <usb/cdc.h>
#define MAX_U_SERIAL_PORTS 4
struct f_serial_opts {
struct usb_function_instance func_inst;
u8 port_num;
};
/*
* One non-multiplexed "serial" I/O port ... there can be several of these
* on any given USB peripheral device, if it provides enough endpoints.
@ -35,8 +42,6 @@ struct gserial {
struct usb_ep *in;
struct usb_ep *out;
struct usb_endpoint_descriptor *in_desc;
struct usb_endpoint_descriptor *out_desc;
/* REVISIT avoid this CDC-ACM support harder ... */
struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
@ -51,16 +56,15 @@ struct gserial {
struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len);
void gs_free_req(struct usb_ep *, struct usb_request *req);
/* port setup/teardown is handled by gadget driver */
int gserial_setup(struct usb_gadget *g, unsigned n_ports);
void gserial_cleanup(void);
/* management of individual TTY ports */
int gserial_alloc_line(unsigned char *port_line);
void gserial_free_line(unsigned char port_line);
/* connect/disconnect is handled by individual functions */
int gserial_connect(struct gserial *, u8 port_num);
void gserial_disconnect(struct gserial *);
/* functions are bound to configurations by a config or gadget driver */
int acm_bind_config(struct usb_configuration *c, u8 port_num);
int gser_bind_config(struct usb_configuration *c, u8 port_num);
int obex_bind_config(struct usb_configuration *c, u8 port_num);

View File

@ -0,0 +1,368 @@
/**
* udc.c - Core UDC Framework
*
* Copyright (C) 2010 Texas Instruments
* Author: Felipe Balbi <balbi@ti.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 of
* the License as published by the Free Software Foundation.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#define VERBOSE_DEBUG
#include <common.h>
#include <driver.h>
#include <init.h>
#include <usb/ch9.h>
#include <usb/gadget.h>
/**
* struct usb_udc - describes one usb device controller
* @driver - the gadget driver pointer. For use by the class code
* @dev - the child device to the actual controller
* @gadget - the gadget. For use by the class code
* @list - for use by the udc class driver
*
* This represents the internal data structure which is used by the UDC-class
* to hold information about udc driver and gadget together.
*/
struct usb_udc {
struct usb_gadget_driver *driver;
struct usb_gadget *gadget;
struct device_d dev;
struct list_head list;
};
static LIST_HEAD(udc_list);
/* ------------------------------------------------------------------------- */
#ifdef CONFIG_KERNEL_HAS_DMA
int usb_gadget_map_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in)
{
if (req->length == 0)
return 0;
if (req->num_sgs) {
int mapped;
mapped = dma_map_sg(&gadget->dev, req->sg, req->num_sgs,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
if (mapped == 0) {
dev_err(&gadget->dev, "failed to map SGs\n");
return -EFAULT;
}
req->num_mapped_sgs = mapped;
} else {
req->dma = dma_map_single(&gadget->dev, req->buf, req->length,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
if (dma_mapping_error(&gadget->dev, req->dma)) {
dev_err(&gadget->dev, "failed to map buffer\n");
return -EFAULT;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(usb_gadget_map_request);
void usb_gadget_unmap_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in)
{
if (req->length == 0)
return;
if (req->num_mapped_sgs) {
dma_unmap_sg(&gadget->dev, req->sg, req->num_mapped_sgs,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
req->num_mapped_sgs = 0;
} else {
dma_unmap_single(&gadget->dev, req->dma, req->length,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
}
}
EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
#endif /* CONFIG_HAS_DMA */
/* ------------------------------------------------------------------------- */
void usb_gadget_set_state(struct usb_gadget *gadget,
enum usb_device_state state)
{
gadget->state = state;
}
EXPORT_SYMBOL_GPL(usb_gadget_set_state);
/* ------------------------------------------------------------------------- */
/**
* usb_gadget_udc_start - tells usb device controller to start up
* @gadget: The gadget we want to get started
* @driver: The driver we want to bind to @gadget
*
* This call is issued by the UDC Class driver when it's about
* to register a gadget driver to the device controller, before
* calling gadget driver's bind() method.
*
* It allows the controller to be powered off until strictly
* necessary to have it powered on.
*
* Returns zero on success, else negative errno.
*/
static inline int usb_gadget_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
return gadget->ops->udc_start(gadget, driver);
}
/**
* usb_gadget_udc_stop - tells usb device controller we don't need it anymore
* @gadget: The device we want to stop activity
* @driver: The driver to unbind from @gadget
*
* This call is issued by the UDC Class driver after calling
* gadget driver's unbind() method.
*
* The details are implementation specific, but it can go as
* far as powering off UDC completely and disable its data
* line pullups.
*/
static inline void usb_gadget_udc_stop(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
gadget->ops->udc_stop(gadget, driver);
}
/**
* usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
* @parent: the parent device to this udc. Usually the controller driver's
* device.
* @gadget: the gadget to be added to the list.
* @release: a gadget release function.
*
* Returns zero on success, negative errno otherwise.
*/
int usb_add_gadget_udc_release(struct device_d *parent, struct usb_gadget *gadget,
void (*release)(struct device_d *dev))
{
struct usb_udc *udc;
int ret = -ENOMEM;
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
if (!udc)
goto err1;
strcpy(gadget->dev.name, "usbgadget");
gadget->dev.id = DEVICE_ID_SINGLE;
gadget->dev.parent = parent;
ret = register_device(&gadget->dev);
if (ret)
goto err2;
dev_add_param_int(&gadget->dev, "product", NULL, NULL,
&gadget->product_id, "0x%04x", NULL);
dev_add_param_int(&gadget->dev, "vendor", NULL, NULL,
&gadget->vendor_id, "0x%04x", NULL);
gadget->manufacturer = xstrdup("barebox");
dev_add_param_string(&gadget->dev, "manufacturer", NULL, NULL,
&gadget->manufacturer, NULL);
gadget->productname = xstrdup(barebox_get_model());
dev_add_param_string(&gadget->dev, "productname", NULL, NULL,
&gadget->productname, NULL);
strcpy(udc->dev.name, "udc");
udc->dev.id = DEVICE_ID_DYNAMIC;
udc->gadget = gadget;
list_add_tail(&udc->list, &udc_list);
register_device(&udc->dev);
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
return 0;
err2:
kfree(udc);
err1:
return ret;
}
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
/**
* usb_add_gadget_udc - adds a new gadget to the udc class driver list
* @parent: the parent device to this udc. Usually the controller
* driver's device.
* @gadget: the gadget to be added to the list
*
* Returns zero on success, negative errno otherwise.
*/
int usb_add_gadget_udc(struct device_d *parent, struct usb_gadget *gadget)
{
return usb_add_gadget_udc_release(parent, gadget, NULL);
}
EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
static void usb_gadget_remove_driver(struct usb_udc *udc)
{
dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
udc->gadget->name);
usb_gadget_disconnect(udc->gadget);
udc->driver->disconnect(udc->gadget);
udc->driver->unbind(udc->gadget);
usb_gadget_udc_stop(udc->gadget, NULL);
udc->driver = NULL;
udc->dev.driver = NULL;
udc->gadget->dev.driver = NULL;
}
/**
* usb_del_gadget_udc - deletes @udc from udc_list
* @gadget: the gadget to be removed.
*
* This, will call usb_gadget_unregister_driver() if
* the @udc is still busy.
*/
void usb_del_gadget_udc(struct usb_gadget *gadget)
{
struct usb_udc *udc = NULL;
list_for_each_entry(udc, &udc_list, list)
if (udc->gadget == gadget)
goto found;
dev_err(gadget->dev.parent, "gadget not registered.\n");
return;
found:
dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
list_del(&udc->list);
if (udc->driver)
usb_gadget_remove_driver(udc);
unregister_device(&udc->dev);
unregister_device(&gadget->dev);
}
EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
/* ------------------------------------------------------------------------- */
static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
{
int ret;
dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
driver->function);
udc->driver = driver;
udc->dev.driver = &driver->driver;
udc->gadget->dev.driver = &driver->driver;
ret = driver->bind(udc->gadget, driver);
if (ret)
goto err1;
ret = usb_gadget_udc_start(udc->gadget, driver);
if (ret) {
driver->unbind(udc->gadget);
goto err1;
}
usb_gadget_connect(udc->gadget);
return 0;
err1:
if (ret != -EISNAM)
dev_err(&udc->dev, "failed to start %s: %d\n",
udc->driver->function, ret);
udc->driver = NULL;
udc->dev.driver = NULL;
udc->gadget->dev.driver = NULL;
return ret;
}
int udc_attach_driver(const char *name, struct usb_gadget_driver *driver)
{
struct usb_udc *udc = NULL;
int ret = -ENODEV;
list_for_each_entry(udc, &udc_list, list) {
ret = strcmp(name, dev_name(&udc->dev));
if (!ret)
break;
}
if (ret) {
ret = -ENODEV;
goto out;
}
if (udc->driver) {
ret = -EBUSY;
goto out;
}
ret = udc_bind_to_driver(udc, driver);
out:
return ret;
}
EXPORT_SYMBOL_GPL(udc_attach_driver);
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
{
struct usb_udc *udc = NULL;
int ret;
if (!driver || !driver->bind || !driver->setup)
return -EINVAL;
list_for_each_entry(udc, &udc_list, list) {
/* For now we take the first one */
if (!udc->driver)
goto found;
}
pr_debug("couldn't find an available UDC\n");
return -ENODEV;
found:
ret = udc_bind_to_driver(udc, driver);
return ret;
}
EXPORT_SYMBOL_GPL(usb_gadget_probe_driver);
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
struct usb_udc *udc = NULL;
int ret = -ENODEV;
if (!driver || !driver->unbind)
return -EINVAL;
list_for_each_entry(udc, &udc_list, list)
if (udc->driver == driver) {
usb_gadget_remove_driver(udc);
ret = 0;
break;
}
return ret;
}
EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);

View File

@ -259,6 +259,7 @@ int open_and_lseek(const char *filename, int mode, loff_t pos);
#define RW_BUF_SIZE (unsigned)4096
extern const char version_string[];
extern const char release_string[];
#ifdef CONFIG_BANNER
void barebox_banner(void);
#else

26
include/file-list.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef __FILE_LIST
#define __FILE_LIST
#define FILE_LIST_FLAG_SAFE (1 << 0)
#define FILE_LIST_FLAG_READBACK (1 << 1)
#define FILE_LIST_FLAG_CREATE (1 << 2)
struct file_list_entry {
char *name;
char *filename;
unsigned long flags;
struct list_head list;
};
struct file_list {
struct list_head list;
int num_entries;
};
struct file_list *file_list_parse(const char *str);
void file_list_free(struct file_list *);
#define file_list_for_each_entry(files, entry) \
list_for_each_entry(entry, &files->list, list)
#endif /* __FILE_LIST */

View File

@ -31,6 +31,11 @@ int dev_add_param(struct device_d *dev, const char *name,
const char *(*get)(struct device_d *, struct param_d *p),
unsigned long flags);
struct param_d *dev_add_param_string(struct device_d *dev, const char *name,
int (*set)(struct param_d *p, void *priv),
int (*get)(struct param_d *p, void *priv),
char **value, void *priv);
struct param_d *dev_add_param_int(struct device_d *dev, const char *name,
int (*set)(struct param_d *p, void *priv),
int (*get)(struct param_d *p, void *priv),
@ -95,6 +100,14 @@ static inline int dev_add_param(struct device_d *dev, char *name,
return 0;
}
static inline struct param_d *dev_add_param_string(struct device_d *dev, const char *name,
int (*set)(struct param_d *p, void *priv),
int (*get)(struct param_d *p, void *priv),
char **value, void *priv)
{
return NULL;
}
static inline struct param_d *dev_add_param_int(struct device_d *dev, const char *name,
int (*set)(struct param_d *p, void *priv),
int (*get)(struct param_d *p, void *priv),

View File

@ -30,10 +30,11 @@
* particular descriptor type.
*/
#ifndef __LINUX_USB_CH9_H
#define __LINUX_USB_CH9_H
#ifndef _UAPI__LINUX_USB_CH9_H
#define _UAPI__LINUX_USB_CH9_H
#include <linux/types.h> /* __u8 etc */
#include <asm/byteorder.h> /* le16_to_cpu */
/*-------------------------------------------------------------------------*/
@ -87,6 +88,8 @@
#define USB_REQ_GET_INTERFACE 0x0A
#define USB_REQ_SET_INTERFACE 0x0B
#define USB_REQ_SYNCH_FRAME 0x0C
#define USB_REQ_SET_SEL 0x30
#define USB_REQ_SET_ISOCH_DELAY 0x31
#define USB_REQ_SET_ENCRYPTION 0x0D /* Wireless USB */
#define USB_REQ_GET_ENCRYPTION 0x0E
@ -123,8 +126,44 @@
#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */
#define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */
/*
* Test Mode Selectors
* See USB 2.0 spec Table 9-7
*/
#define TEST_J 1
#define TEST_K 2
#define TEST_SE0_NAK 3
#define TEST_PACKET 4
#define TEST_FORCE_EN 5
/*
* New Feature Selectors as added by USB 3.0
* See USB 3.0 spec Table 9-7
*/
#define USB_DEVICE_U1_ENABLE 48 /* dev may initiate U1 transition */
#define USB_DEVICE_U2_ENABLE 49 /* dev may initiate U2 transition */
#define USB_DEVICE_LTM_ENABLE 50 /* dev may send LTM */
#define USB_INTRF_FUNC_SUSPEND 0 /* function suspend */
#define USB_INTR_FUNC_SUSPEND_OPT_MASK 0xFF00
/*
* Suspend Options, Table 9-8 USB 3.0 spec
*/
#define USB_INTRF_FUNC_SUSPEND_LP (1 << (8 + 0))
#define USB_INTRF_FUNC_SUSPEND_RW (1 << (8 + 1))
/*
* Interface status, Figure 9-5 USB 3.0 spec
*/
#define USB_INTRF_STAT_FUNC_RW_CAP 1
#define USB_INTRF_STAT_FUNC_RW 2
#define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */
/* Bit array elements as returned by the USB_REQ_GET_STATUS request. */
#define USB_DEV_STAT_U1_ENABLED 2 /* transition into U1 state */
#define USB_DEV_STAT_U2_ENABLED 3 /* transition into U2 state */
#define USB_DEV_STAT_LTM_ENABLED 4 /* Latency tolerance messages */
/**
* struct usb_ctrlrequest - SETUP data for a USB device control request
@ -191,6 +230,8 @@ struct usb_ctrlrequest {
#define USB_DT_WIRE_ADAPTER 0x21
#define USB_DT_RPIPE 0x22
#define USB_DT_CS_RADIO_CONTROL 0x23
/* From the T10 UAS specification */
#define USB_DT_PIPE_USAGE 0x24
/* From the USB 3.0 spec */
#define USB_DT_SS_ENDPOINT_COMP 0x30
@ -258,6 +299,8 @@ struct usb_device_descriptor {
#define USB_CLASS_APP_SPEC 0xfe
#define USB_CLASS_VENDOR_SPEC 0xff
#define USB_SUBCLASS_VENDOR_SPEC 0xff
/*-------------------------------------------------------------------------*/
/* USB_DT_CONFIG: Configuration descriptor information.
@ -355,6 +398,22 @@ struct usb_endpoint_descriptor {
#define USB_ENDPOINT_XFER_INT 3
#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80
/* The USB 3.0 spec redefines bits 5:4 of bmAttributes as interrupt ep type. */
#define USB_ENDPOINT_INTRTYPE 0x30
#define USB_ENDPOINT_INTR_PERIODIC (0 << 4)
#define USB_ENDPOINT_INTR_NOTIFICATION (1 << 4)
#define USB_ENDPOINT_SYNCTYPE 0x0c
#define USB_ENDPOINT_SYNC_NONE (0 << 2)
#define USB_ENDPOINT_SYNC_ASYNC (1 << 2)
#define USB_ENDPOINT_SYNC_ADAPTIVE (2 << 2)
#define USB_ENDPOINT_SYNC_SYNC (3 << 2)
#define USB_ENDPOINT_USAGE_MASK 0x30
#define USB_ENDPOINT_USAGE_DATA 0x00
#define USB_ENDPOINT_USAGE_FEEDBACK 0x10
#define USB_ENDPOINT_USAGE_IMPLICIT_FB 0x20 /* Implicit feedback Data endpoint */
/*-------------------------------------------------------------------------*/
/**
@ -467,7 +526,7 @@ static inline int usb_endpoint_xfer_isoc(
static inline int usb_endpoint_is_bulk_in(
const struct usb_endpoint_descriptor *epd)
{
return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd));
return usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd);
}
/**
@ -480,7 +539,7 @@ static inline int usb_endpoint_is_bulk_in(
static inline int usb_endpoint_is_bulk_out(
const struct usb_endpoint_descriptor *epd)
{
return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd));
return usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd);
}
/**
@ -493,7 +552,7 @@ static inline int usb_endpoint_is_bulk_out(
static inline int usb_endpoint_is_int_in(
const struct usb_endpoint_descriptor *epd)
{
return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd));
return usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd);
}
/**
@ -506,7 +565,7 @@ static inline int usb_endpoint_is_int_in(
static inline int usb_endpoint_is_int_out(
const struct usb_endpoint_descriptor *epd)
{
return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd));
return usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd);
}
/**
@ -519,7 +578,7 @@ static inline int usb_endpoint_is_int_out(
static inline int usb_endpoint_is_isoc_in(
const struct usb_endpoint_descriptor *epd)
{
return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd));
return usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd);
}
/**
@ -532,7 +591,24 @@ static inline int usb_endpoint_is_isoc_in(
static inline int usb_endpoint_is_isoc_out(
const struct usb_endpoint_descriptor *epd)
{
return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd));
return usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd);
}
/**
* usb_endpoint_maxp - get endpoint's max packet size
* @epd: endpoint to be checked
*
* Returns @epd's max packet
*/
static inline int usb_endpoint_maxp(const struct usb_endpoint_descriptor *epd)
{
return __le16_to_cpu(epd->wMaxPacketSize);
}
static inline int usb_endpoint_interrupt_type(
const struct usb_endpoint_descriptor *epd)
{
return epd->bmAttributes & USB_ENDPOINT_INTRTYPE;
}
/*-------------------------------------------------------------------------*/
@ -544,11 +620,33 @@ struct usb_ss_ep_comp_descriptor {
__u8 bMaxBurst;
__u8 bmAttributes;
__u16 wBytesPerInterval;
__le16 wBytesPerInterval;
} __attribute__ ((packed));
#define USB_DT_SS_EP_COMP_SIZE 6
/* Bits 4:0 of bmAttributes if this is a bulk endpoint */
static inline int
usb_ss_max_streams(const struct usb_ss_ep_comp_descriptor *comp)
{
int max_streams;
if (!comp)
return 0;
max_streams = comp->bmAttributes & 0x1f;
if (!max_streams)
return 0;
max_streams = 1 << max_streams;
return max_streams;
}
/* Bits 1:0 of bmAttributes if this is an isoc endpoint */
#define USB_SS_MULT(p) (1 + ((p) & 0x3))
/*-------------------------------------------------------------------------*/
/* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */
@ -663,6 +761,7 @@ struct usb_bos_descriptor {
__u8 bNumDeviceCaps;
} __attribute__((packed));
#define USB_DT_BOS_SIZE 5
/*-------------------------------------------------------------------------*/
/* USB_DT_DEVICE_CAPABILITY: grouped with BOS */
@ -700,16 +799,61 @@ struct usb_wireless_cap_descriptor { /* Ultra Wide Band */
__u8 bReserved;
} __attribute__((packed));
/* USB 2.0 Extension descriptor */
#define USB_CAP_TYPE_EXT 2
struct usb_ext_cap_descriptor { /* Link Power Management */
__u8 bLength;
__u8 bDescriptorType;
__u8 bDevCapabilityType;
__u8 bmAttributes;
__le32 bmAttributes;
#define USB_LPM_SUPPORT (1 << 1) /* supports LPM */
#define USB_BESL_SUPPORT (1 << 2) /* supports BESL */
#define USB_BESL_BASELINE_VALID (1 << 3) /* Baseline BESL valid*/
#define USB_BESL_DEEP_VALID (1 << 4) /* Deep BESL valid */
#define USB_GET_BESL_BASELINE(p) (((p) & (0xf << 8)) >> 8)
#define USB_GET_BESL_DEEP(p) (((p) & (0xf << 12)) >> 12)
} __attribute__((packed));
#define USB_DT_USB_EXT_CAP_SIZE 7
/*
* SuperSpeed USB Capability descriptor: Defines the set of SuperSpeed USB
* specific device level capabilities
*/
#define USB_SS_CAP_TYPE 3
struct usb_ss_cap_descriptor { /* Link Power Management */
__u8 bLength;
__u8 bDescriptorType;
__u8 bDevCapabilityType;
__u8 bmAttributes;
#define USB_LTM_SUPPORT (1 << 1) /* supports LTM */
__le16 wSpeedSupported;
#define USB_LOW_SPEED_OPERATION (1) /* Low speed operation */
#define USB_FULL_SPEED_OPERATION (1 << 1) /* Full speed operation */
#define USB_HIGH_SPEED_OPERATION (1 << 2) /* High speed operation */
#define USB_5GBPS_OPERATION (1 << 3) /* Operation at 5Gbps */
__u8 bFunctionalitySupport;
__u8 bU1devExitLat;
__le16 bU2DevExitLat;
} __attribute__((packed));
#define USB_DT_USB_SS_CAP_SIZE 10
/*
* Container ID Capability descriptor: Defines the instance unique ID used to
* identify the instance across all operating modes
*/
#define CONTAINER_ID_TYPE 4
struct usb_ss_container_id_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDevCapabilityType;
__u8 bReserved;
__u8 ContainerID[16]; /* 128-bit number */
} __attribute__((packed));
#define USB_DT_USB_SS_CONTN_ID_SIZE 20
/*-------------------------------------------------------------------------*/
/* USB_DT_WIRELESS_ENDPOINT_COMP: companion descriptor associated with
@ -767,10 +911,11 @@ enum usb_device_speed {
USB_SPEED_UNKNOWN = 0, /* enumerating */
USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */
USB_SPEED_HIGH, /* usb 2.0 */
USB_SPEED_VARIABLE, /* wireless (usb 2.5) */
USB_SPEED_WIRELESS, /* wireless (usb 2.5) */
USB_SPEED_SUPER, /* usb 3.0 */
};
enum usb_device_state {
/* NOTATTACHED isn't in the USB spec, and this state acts
* the same as ATTACHED ... but it's clearer this way.
@ -796,4 +941,76 @@ enum usb_device_state {
*/
};
#endif /* __LINUX_USB_CH9_H */
enum usb3_link_state {
USB3_LPM_U0 = 0,
USB3_LPM_U1,
USB3_LPM_U2,
USB3_LPM_U3
};
/*
* A U1 timeout of 0x0 means the parent hub will reject any transitions to U1.
* 0xff means the parent hub will accept transitions to U1, but will not
* initiate a transition.
*
* A U1 timeout of 0x1 to 0x7F also causes the hub to initiate a transition to
* U1 after that many microseconds. Timeouts of 0x80 to 0xFE are reserved
* values.
*
* A U2 timeout of 0x0 means the parent hub will reject any transitions to U2.
* 0xff means the parent hub will accept transitions to U2, but will not
* initiate a transition.
*
* A U2 timeout of 0x1 to 0xFE also causes the hub to initiate a transition to
* U2 after N*256 microseconds. Therefore a U2 timeout value of 0x1 means a U2
* idle timer of 256 microseconds, 0x2 means 512 microseconds, 0xFE means
* 65.024ms.
*/
#define USB3_LPM_DISABLED 0x0
#define USB3_LPM_U1_MAX_TIMEOUT 0x7F
#define USB3_LPM_U2_MAX_TIMEOUT 0xFE
#define USB3_LPM_DEVICE_INITIATED 0xFF
struct usb_set_sel_req {
__u8 u1_sel;
__u8 u1_pel;
__le16 u2_sel;
__le16 u2_pel;
} __attribute__ ((packed));
/*
* The Set System Exit Latency control transfer provides one byte each for
* U1 SEL and U1 PEL, so the max exit latency is 0xFF. U2 SEL and U2 PEL each
* are two bytes long.
*/
#define USB3_LPM_MAX_U1_SEL_PEL 0xFF
#define USB3_LPM_MAX_U2_SEL_PEL 0xFFFF
/*-------------------------------------------------------------------------*/
/*
* As per USB compliance update, a device that is actively drawing
* more than 100mA from USB must report itself as bus-powered in
* the GetStatus(DEVICE) call.
* http://compliance.usb.org/index.asp?UpdateFile=Electrical&Format=Standard#34
*/
#define USB_SELF_POWER_VBUS_MAX_DRAW 100
/**
* usb_speed_string() - Returns human readable-name of the speed.
* @speed: The speed to return human-readable name for. If it's not
* any of the speeds defined in usb_device_speed enum, string for
* USB_SPEED_UNKNOWN will be returned.
*/
const char *usb_speed_string(enum usb_device_speed speed);
/**
* usb_state_string - Returns human readable name for the state.
* @state: The state to return a human-readable name for. If it's not
* any of the states devices in usb_device_state_string enum,
* the string UNKNOWN will be returned.
*/
const char *usb_state_string(enum usb_device_state state);
#endif /* _UAPI__LINUX_USB_CH9_H */

View File

@ -12,6 +12,10 @@
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __LINUX_USB_COMPOSITE_H
@ -29,11 +33,25 @@
* might alternatively be packaged in individual configurations, but in
* the composite model the host can use both functions at the same time.
*/
#include <init.h>
#include <usb/ch9.h>
#include <usb/gadget.h>
#include <linux/log2.h>
#include <linux/stringify.h>
/*
* USB function drivers should return USB_GADGET_DELAYED_STATUS if they
* wish to delay the data/status stages of the control transfer till they
* are ready. The control transfer will then be kept from completing till
* all the function drivers that requested for USB_GADGET_DELAYED_STAUS
* invoke usb_composite_setup_continue().
*/
#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */
/* big enough to hold our biggest descriptor */
#define USB_COMP_EP0_BUFSIZ 1024
#define USB_MS_TO_HS_INTERVAL(x) (ilog2((x * 1000 / 125)) + 1)
struct usb_configuration;
/**
@ -41,12 +59,16 @@ struct usb_configuration;
* @name: For diagnostics, identifies the function.
* @strings: tables of strings, keyed by identifiers assigned during bind()
* and by language IDs provided in control requests
* @descriptors: Table of full (or low) speed descriptors, using interface and
* @fs_descriptors: Table of full (or low) speed descriptors, using interface and
* string identifiers assigned during @bind(). If this pointer is null,
* the function will not be available at full speed (or at low speed).
* @hs_descriptors: Table of high speed descriptors, using interface and
* string identifiers assigned during @bind(). If this pointer is null,
* the function will not be available at high speed.
* @ss_descriptors: Table of super speed descriptors, using interface and
* string identifiers assigned during @bind(). If this
* pointer is null after initiation, the function will not
* be available at super speed.
* @config: assigned when @usb_add_function() is called; this is the
* configuration with which this function is associated.
* @bind: Before the gadget can register, all of its functions bind() to the
@ -54,6 +76,8 @@ struct usb_configuration;
* in interface or class descriptors; endpoints; I/O buffers; and so on.
* @unbind: Reverses @bind; called as a side effect of unregistering the
* driver which added this function.
* @free_func: free the struct usb_function.
* @mod: (internal) points to the module that created this structure.
* @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may
* initialize usb_ep.driver data at this time (when it is used).
* Note that setting an interface to its current altsetting resets
@ -65,6 +89,10 @@ struct usb_configuration;
* @setup: Used for interface-specific control requests.
* @suspend: Notifies functions when the host stops sending USB traffic.
* @resume: Notifies functions when the host restarts USB traffic.
* @get_status: Returns function status as a reply to
* GetStatus() request when the recipient is Interface.
* @func_suspend: callback to be called when
* SetFeature(FUNCTION_SUSPEND) is reseived
*
* A single USB function uses one or more interfaces, and should in most
* cases support operation at both full and high speeds. Each function is
@ -89,11 +117,13 @@ struct usb_configuration;
* two or more distinct instances within the same configuration, providing
* several independent logical data links to a USB host.
*/
struct usb_function {
const char *name;
struct usb_gadget_strings **strings;
struct usb_descriptor_header **descriptors;
struct usb_descriptor_header **fs_descriptors;
struct usb_descriptor_header **hs_descriptors;
struct usb_descriptor_header **ss_descriptors;
struct usb_configuration *config;
@ -108,6 +138,8 @@ struct usb_function {
struct usb_function *);
void (*unbind)(struct usb_configuration *,
struct usb_function *);
void (*free_func)(struct usb_function *f);
struct module *mod;
/* runtime state management */
int (*set_alt)(struct usb_function *,
@ -120,9 +152,15 @@ struct usb_function {
void (*suspend)(struct usb_function *);
void (*resume)(struct usb_function *);
/* USB 3.0 additions */
int (*get_status)(struct usb_function *);
int (*func_suspend)(struct usb_function *,
u8 suspend_opt);
/* private: */
/* internals */
struct list_head list;
DECLARE_BITMAP(endpoints, 32);
const struct usb_function_instance *fi;
};
int usb_add_function(struct usb_configuration *, struct usb_function *);
@ -132,20 +170,8 @@ int usb_function_activate(struct usb_function *);
int usb_interface_id(struct usb_configuration *, struct usb_function *);
/**
* ep_choose - select descriptor endpoint at current device speed
* @g: gadget, connected and running at some speed
* @hs: descriptor to use for high speed operation
* @fs: descriptor to use for full or low speed operation
*/
static inline struct usb_endpoint_descriptor *
ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
struct usb_endpoint_descriptor *fs)
{
if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
return hs;
return fs;
}
int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
struct usb_ep *_ep);
#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */
@ -156,8 +182,6 @@ ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
* and by language IDs provided in control requests.
* @descriptors: Table of descriptors preceding all function descriptors.
* Examples include OTG and vendor-specific descriptors.
* @bind: Called from @usb_add_config() to allocate resources unique to this
* configuration and to call @usb_add_function() for each function used.
* @unbind: Reverses @bind; called as a side effect of unregistering the
* driver which added this configuration.
* @setup: Used to delegate control requests that aren't handled by standard
@ -165,7 +189,8 @@ ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
* @bConfigurationValue: Copied into configuration descriptor.
* @iConfiguration: Copied into configuration descriptor.
* @bmAttributes: Copied into configuration descriptor.
* @bMaxPower: Copied into configuration descriptor.
* @MaxPower: Power consumtion in mA. Used to compute bMaxPower in the
* configuration descriptor after considering the bus speed.
* @cdev: assigned by @usb_add_config() before calling @bind(); this is
* the device associated with this configuration.
*
@ -185,7 +210,7 @@ ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
* @bind() method is then used to initialize all the functions and then
* call @usb_add_function() for them.
*
* Those functions would normally be independant of each other, but that's
* Those functions would normally be independent of each other, but that's
* not mandatory. CDC WMC devices are an example where functions often
* depend on other functions, with some functions subsidiary to others.
* Such interdependency may be managed in any way, so long as all of the
@ -202,8 +227,7 @@ struct usb_configuration {
* we can't restructure things to avoid mismatching...
*/
/* configuration management: bind/unbind */
int (*bind)(struct usb_configuration *);
/* configuration management: unbind/setup */
void (*unbind)(struct usb_configuration *);
int (*setup)(struct usb_configuration *,
const struct usb_ctrlrequest *);
@ -212,7 +236,7 @@ struct usb_configuration {
u8 bConfigurationValue;
u8 iConfiguration;
u8 bmAttributes;
u8 bMaxPower;
u16 MaxPower;
struct usb_composite_dev *cdev;
@ -221,34 +245,54 @@ struct usb_configuration {
struct list_head list;
struct list_head functions;
u8 next_interface_id;
unsigned superspeed:1;
unsigned highspeed:1;
unsigned fullspeed:1;
struct usb_function *interface[MAX_CONFIG_INTERFACES];
};
int usb_add_config(struct usb_composite_dev *,
struct usb_configuration *,
int (*)(struct usb_configuration *));
void usb_remove_config(struct usb_composite_dev *,
struct usb_configuration *);
/* predefined index for usb_composite_driver */
enum {
USB_GADGET_MANUFACTURER_IDX = 0,
USB_GADGET_PRODUCT_IDX,
USB_GADGET_SERIAL_IDX,
USB_GADGET_FIRST_AVAIL_IDX,
};
/**
* struct usb_composite_driver - groups configurations into a gadget
* @name: For diagnostics, identifies the driver.
* @dev: Template descriptor for the device, including default device
* identifiers.
* @strings: tables of strings, keyed by identifiers assigned during bind()
* and language IDs provided in control requests
* @strings: tables of strings, keyed by identifiers assigned during @bind
* and language IDs provided in control requests. Note: The first entries
* are predefined. The first entry that may be used is
* USB_GADGET_FIRST_AVAIL_IDX
* @max_speed: Highest speed the driver supports.
* @needs_serial: set to 1 if the gadget needs userspace to provide
* a serial number. If one is not provided, warning will be printed.
* @bind: (REQUIRED) Used to allocate resources that are shared across the
* whole device, such as string IDs, and add its configurations using
* @usb_add_config(). This may fail by returning a negative errno
* @usb_add_config(). This may fail by returning a negative errno
* value; it should return zero on successful initialization.
* @unbind: Reverses @bind(); called as a side effect of unregistering
* @unbind: Reverses @bind; called as a side effect of unregistering
* this driver.
* @disconnect: optional driver disconnect method
* @suspend: Notifies when the host stops sending USB traffic,
* after function notifications
* @resume: Notifies configuration when the host restarts USB traffic,
* before function notifications
* @gadget_driver: Gadget driver controlling this driver
*
* Devices default to reporting self powered operation. Devices which rely
* on bus powered operation should report this in their @bind() method.
* on bus powered operation should report this in their @bind method.
*
* Before returning from @bind, various fields in the template descriptor
* may be overridden. These include the idVendor/idProduct/bcdDevice values
@ -262,29 +306,37 @@ struct usb_composite_driver {
const char *name;
const struct usb_device_descriptor *dev;
struct usb_gadget_strings **strings;
enum usb_device_speed max_speed;
unsigned needs_serial:1;
/* REVISIT: bind() functions can be marked __init, which
* makes trouble for section mismatch analysis. See if
* we can't restructure things to avoid mismatching...
*/
int (*bind)(struct usb_composite_dev *);
int (*bind)(struct usb_composite_dev *cdev);
int (*unbind)(struct usb_composite_dev *);
void (*disconnect)(struct usb_composite_dev *);
/* global suspend hooks */
void (*suspend)(struct usb_composite_dev *);
void (*resume)(struct usb_composite_dev *);
struct usb_gadget_driver gadget_driver;
};
extern int usb_composite_register(struct usb_composite_driver *);
extern void usb_composite_unregister(struct usb_composite_driver *);
extern int usb_composite_probe(struct usb_composite_driver *driver);
extern void usb_composite_unregister(struct usb_composite_driver *driver);
extern void usb_composite_setup_continue(struct usb_composite_dev *cdev);
extern int composite_dev_prepare(struct usb_composite_driver *composite,
struct usb_composite_dev *cdev);
void composite_dev_cleanup(struct usb_composite_dev *cdev);
static inline struct usb_composite_driver *to_cdriver(
struct usb_gadget_driver *gdrv)
{
return container_of(gdrv, struct usb_composite_driver, gadget_driver);
}
/**
* struct usb_composite_device - represents one composite usb gadget
* @gadget: read-only, abstracts the gadget's usb peripheral controller
* @req: used for control responses; buffer is pre-allocated
* @bufsiz: size of buffer pre-allocated in @req
* @config: the currently active configuration
*
* One of these devices is allocated and initialized before the
@ -315,30 +367,124 @@ extern void usb_composite_unregister(struct usb_composite_driver *);
struct usb_composite_dev {
struct usb_gadget *gadget;
struct usb_request *req;
unsigned bufsiz;
struct usb_configuration *config;
/* private: */
/* internals */
unsigned int suspended:1;
struct usb_device_descriptor desc;
struct list_head configs;
struct list_head gstrings;
struct usb_composite_driver *driver;
u8 next_string_id;
char *def_manufacturer;
/* the gadget driver won't enable the data pullup
* while the deactivation count is nonzero.
*/
unsigned deactivations;
/* the composite driver won't complete the control transfer's
* data/status stages till delayed_status is zero.
*/
int delayed_status;
/* protects deactivations and delayed_status counts*/
spinlock_t lock;
int in_reset_config;
};
extern int usb_string_id(struct usb_composite_dev *c);
extern int usb_string_ids_tab(struct usb_composite_dev *c,
struct usb_string *str);
extern struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev,
struct usb_gadget_strings **sp, unsigned n_strings);
extern int usb_string_ids_n(struct usb_composite_dev *c, unsigned n);
extern void composite_disconnect(struct usb_gadget *gadget);
extern int composite_setup(struct usb_gadget *gadget,
const struct usb_ctrlrequest *ctrl);
/*
* Some systems will need runtime overrides for the product identifiers
* published in the device descriptor, either numbers or strings or both.
* String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
*/
struct usb_composite_overwrite {
u16 idVendor;
u16 idProduct;
u16 bcdDevice;
char *serial_number;
char *manufacturer;
char *product;
};
void usb_composite_overwrite_options(struct usb_composite_dev *cdev,
struct usb_composite_overwrite *covr);
static inline u16 get_default_bcdDevice(void)
{
/* The Kernel version the current USB code is based on */
return 0x0316;
}
struct usb_function_driver {
const char *name;
struct module *mod;
struct list_head list;
struct usb_function_instance *(*alloc_inst)(void);
struct usb_function *(*alloc_func)(struct usb_function_instance *inst);
};
struct usb_function_instance {
struct list_head cfs_list;
struct usb_function_driver *fd;
int (*set_inst_name)(struct usb_function_instance *inst,
const char *name);
void (*free_func_inst)(struct usb_function_instance *inst);
};
void usb_function_unregister(struct usb_function_driver *f);
int usb_function_register(struct usb_function_driver *newf);
void usb_put_function_instance(struct usb_function_instance *fi);
void usb_put_function(struct usb_function *f);
struct usb_function_instance *usb_get_function_instance(const char *name);
struct usb_function *usb_get_function(struct usb_function_instance *fi);
struct usb_configuration *usb_get_config(struct usb_composite_dev *cdev,
int val);
int usb_add_config_only(struct usb_composite_dev *cdev,
struct usb_configuration *config);
void usb_remove_function(struct usb_configuration *c, struct usb_function *f);
#define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \
static struct usb_function_driver _name ## usb_func = { \
.name = __stringify(_name), \
.alloc_inst = _inst_alloc, \
.alloc_func = _func_alloc, \
};
#define DECLARE_USB_FUNCTION_INIT(_name, _inst_alloc, _func_alloc) \
DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \
static int _name ## mod_init(void) \
{ \
return usb_function_register(&_name ## usb_func); \
} \
device_initcall(_name ## mod_init)
/* messaging utils */
#define DBG(d, fmt, args...)
#define VDBG(d, fmt, args...)
#define ERROR(d, fmt, args...)
#define WARNING(d, fmt, args...)
#define INFO(d, fmt, args...)
#define DBG(d, fmt, args...) \
dev_dbg(&(d)->gadget->dev , fmt , ## args)
#define VDBG(d, fmt, args...) \
dev_vdbg(&(d)->gadget->dev , fmt , ## args)
#define ERROR(d, fmt, args...) \
dev_err(&(d)->gadget->dev , fmt , ## args)
#define WARNING(d, fmt, args...) \
dev_warn(&(d)->gadget->dev , fmt , ## args)
#define INFO(d, fmt, args...) \
dev_info(&(d)->gadget->dev , fmt , ## args)
#endif /* __LINUX_USB_COMPOSITE_H */

View File

@ -21,94 +21,14 @@
*/
#include <linux/types.h>
#include <file-list.h>
#include <usb/composite.h>
#define DFU_FLAG_SAFE (1 << 0)
#define DFU_FLAG_READBACK (1 << 1)
#define DFU_FLAG_CREATE (1 << 2)
struct usb_dfu_dev {
char *name;
char *dev;
unsigned long flags;
struct f_dfu_opts {
struct usb_function_instance func_inst;
struct file_list *files;
};
struct usb_dfu_pdata {
char *manufacturer;
const char *productname;
u16 idVendor;
u16 idProduct;
struct usb_dfu_dev *alts;
int num_alts;
};
int usb_dfu_register(struct usb_dfu_pdata *);
#define USB_DT_DFU 0x21
struct usb_dfu_func_descriptor {
u_int8_t bLength;
u_int8_t bDescriptorType;
u_int8_t bmAttributes;
#define USB_DFU_CAN_DOWNLOAD (1 << 0)
#define USB_DFU_CAN_UPLOAD (1 << 1)
#define USB_DFU_MANIFEST_TOL (1 << 2)
#define USB_DFU_WILL_DETACH (1 << 3)
u_int16_t wDetachTimeOut;
u_int16_t wTransferSize;
u_int16_t bcdDFUVersion;
} __attribute__ ((packed));
#define USB_DT_DFU_SIZE 9
#define USB_TYPE_DFU (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
/* DFU class-specific requests (Section 3, DFU Rev 1.1) */
#define USB_REQ_DFU_DETACH 0x00
#define USB_REQ_DFU_DNLOAD 0x01
#define USB_REQ_DFU_UPLOAD 0x02
#define USB_REQ_DFU_GETSTATUS 0x03
#define USB_REQ_DFU_CLRSTATUS 0x04
#define USB_REQ_DFU_GETSTATE 0x05
#define USB_REQ_DFU_ABORT 0x06
struct dfu_status {
u_int8_t bStatus;
u_int8_t bwPollTimeout[3];
u_int8_t bState;
u_int8_t iString;
} __attribute__((packed));
#define DFU_STATUS_OK 0x00
#define DFU_STATUS_errTARGET 0x01
#define DFU_STATUS_errFILE 0x02
#define DFU_STATUS_errWRITE 0x03
#define DFU_STATUS_errERASE 0x04
#define DFU_STATUS_errCHECK_ERASED 0x05
#define DFU_STATUS_errPROG 0x06
#define DFU_STATUS_errVERIFY 0x07
#define DFU_STATUS_errADDRESS 0x08
#define DFU_STATUS_errNOTDONE 0x09
#define DFU_STATUS_errFIRMWARE 0x0a
#define DFU_STATUS_errVENDOR 0x0b
#define DFU_STATUS_errUSBR 0x0c
#define DFU_STATUS_errPOR 0x0d
#define DFU_STATUS_errUNKNOWN 0x0e
#define DFU_STATUS_errSTALLEDPKT 0x0f
enum dfu_state {
DFU_STATE_appIDLE = 0,
DFU_STATE_appDETACH = 1,
DFU_STATE_dfuIDLE = 2,
DFU_STATE_dfuDNLOAD_SYNC = 3,
DFU_STATE_dfuDNBUSY = 4,
DFU_STATE_dfuDNLOAD_IDLE = 5,
DFU_STATE_dfuMANIFEST_SYNC = 6,
DFU_STATE_dfuMANIFEST = 7,
DFU_STATE_dfuMANIFEST_WAIT_RST = 8,
DFU_STATE_dfuUPLOAD_IDLE = 9,
DFU_STATE_dfuERROR = 10,
};
int usb_dfu_register(struct f_dfu_opts *);
#endif /* _USB_DFU_H */

13
include/usb/fastboot.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef _USB_FASTBOOT_H
#define _USB_FASTBOOT_H
#include <linux/types.h>
#include <file-list.h>
#include <usb/composite.h>
struct f_fastboot_opts {
struct usb_function_instance func_inst;
struct file_list *files;
};
#endif /* _USB_FASTBOOT_H */

View File

@ -0,0 +1,17 @@
#ifndef __USB_GADGET_MULTI_H
#define __USB_GADGET_MULTI_H
#include <usb/fastboot.h>
#include <usb/dfu.h>
#include <usb/usbserial.h>
struct f_multi_opts {
struct f_fastboot_opts fastboot_opts;
struct f_dfu_opts dfu_opts;
int create_acm;
};
int usb_multi_register(struct f_multi_opts *opts);
void usb_multi_unregister(void);
#endif /* __USB_GADGET_MULTI_H */

View File

@ -15,10 +15,10 @@
#ifndef __LINUX_USB_GADGET_H
#define __LINUX_USB_GADGET_H
#include <usb/ch9.h>
#include <malloc.h>
#include <errno.h>
#include <driver.h>
#include <linux/list.h>
#include <usb/ch9.h>
struct usb_ep;
@ -29,7 +29,11 @@ struct usb_ep;
* @dma: DMA address corresponding to 'buf'. If you don't set this
* field, and the usb controller needs one, it is responsible
* for mapping and unmapping the buffer.
* @sg: a scatterlist for SG-capable controllers.
* @num_sgs: number of SG entries
* @num_mapped_sgs: number of SG entries mapped to DMA (internal)
* @length: Length of that data
* @stream_id: The stream id, when USB3.0 bulk streams are being used
* @no_interrupt: If true, hints that no completion irq is needed.
* Helpful sometimes with deep request queues that are handled
* directly by DMA controllers.
@ -75,7 +79,7 @@ struct usb_ep;
* Bulk endpoints can use any size buffers, and can also be used for interrupt
* transfers. interrupt-only endpoints can be much less functional.
*
* NOTE: this is analagous to 'struct urb' on the host side, except that
* NOTE: this is analogous to 'struct urb' on the host side, except that
* it's thinner and promotes more pre-allocation.
*/
@ -84,6 +88,11 @@ struct usb_request {
unsigned length;
dma_addr_t dma;
struct scatterlist *sg;
unsigned num_sgs;
unsigned num_mapped_sgs;
unsigned stream_id:16;
unsigned no_interrupt:1;
unsigned zero:1;
unsigned short_not_ok:1;
@ -132,8 +141,20 @@ struct usb_ep_ops {
* @maxpacket:The maximum packet size used on this endpoint. The initial
* value can sometimes be reduced (hardware allowing), according to
* the endpoint descriptor used to configure the endpoint.
* @driver_data:for use by the gadget driver. all other fields are
* read-only to gadget drivers.
* @maxpacket_limit:The maximum packet size value which can be handled by this
* endpoint. It's set once by UDC driver when endpoint is initialized, and
* should not be changed. Should not be confused with maxpacket.
* @max_streams: The maximum number of streams supported
* by this EP (0 - 16, actual number is 2^n)
* @mult: multiplier, 'mult' value for SS Isoc EPs
* @maxburst: the maximum number of bursts supported by this EP (for usb3)
* @driver_data:for use by the gadget driver.
* @address: used to identify the endpoint when finding descriptor that
* matches connection speed
* @desc: endpoint descriptor. This pointer is set before the endpoint is
* enabled and remains valid until the endpoint is disabled.
* @comp_desc: In case of SuperSpeed support, this is the endpoint companion
* descriptor that is used to configure the endpoint
*
* the bus controller driver lists all the general purpose endpoints in
* gadget->ep_list. the control endpoint (gadget->ep0) is not in that list,
@ -146,19 +167,38 @@ struct usb_ep {
const struct usb_ep_ops *ops;
struct list_head ep_list;
unsigned maxpacket:16;
unsigned maxpacket_limit:16;
unsigned max_streams:16;
unsigned mult:2;
unsigned maxburst:5;
u8 address;
const struct usb_endpoint_descriptor *desc;
const struct usb_ss_ep_comp_descriptor *comp_desc;
};
/*-------------------------------------------------------------------------*/
/**
* usb_ep_set_maxpacket_limit - set maximum packet size limit for endpoint
* @ep:the endpoint being configured
* @maxpacket_limit:value of maximum packet size limit
*
* This function shoud be used only in UDC drivers to initialize endpoint
* (usually in probe function).
*/
static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
unsigned maxpacket_limit)
{
ep->maxpacket_limit = maxpacket_limit;
ep->maxpacket = maxpacket_limit;
}
/**
* usb_ep_enable - configure endpoint, making it usable
* @ep:the endpoint being configured. may not be the endpoint named "ep0".
* drivers discover endpoints through the ep_list of a usb_gadget.
* @desc:descriptor for desired behavior. caller guarantees this pointer
* remains valid until the endpoint is disabled; the data byte order
* is little-endian (usb-standard).
*
* when configurations are set, or when interface settings change, the driver
* When configurations are set, or when interface settings change, the driver
* will enable or disable the relevant endpoints. while it is enabled, an
* endpoint may be used for i/o until the driver receives a disconnect() from
* the host or until the endpoint is disabled.
@ -173,10 +213,9 @@ struct usb_ep {
*
* returns zero, or a negative error code.
*/
static inline int usb_ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
static inline int usb_ep_enable(struct usb_ep *ep)
{
return ep->ops->enable(ep, desc);
return ep->ops->enable(ep, ep->desc);
}
/**
@ -199,6 +238,7 @@ static inline int usb_ep_disable(struct usb_ep *ep)
/**
* usb_ep_alloc_request - allocate a request object to use with this endpoint
* @ep:the endpoint to be used with with the request
* @gfp_flags:GFP_* flags to use
*
* Request objects must be allocated with this call, since they normally
* need controller-specific setup and may even need endpoint-specific
@ -233,6 +273,8 @@ static inline void usb_ep_free_request(struct usb_ep *ep,
* usb_ep_queue - queues (submits) an I/O request to an endpoint.
* @ep:the endpoint associated with the request
* @req:the request being submitted
* @gfp_flags: GFP_* flags to use in case the lower level driver couldn't
* pre-allocate all necessary memory with the request.
*
* This tells the device controller to perform the specified request through
* that endpoint (reading or writing a buffer). When the request completes,
@ -266,7 +308,7 @@ static inline void usb_ep_free_request(struct usb_ep *ep,
*
* Control endpoints ... after getting a setup() callback, the driver queues
* one response (even if it would be zero length). That enables the
* status ack, after transfering data as specified in the response. Setup
* status ack, after transferring data as specified in the response. Setup
* functions may return negative error codes to generate protocol stalls.
* (Note that some USB device controllers disallow protocol stall responses
* in some cases.) When control responses are deferred (the response is
@ -413,7 +455,16 @@ static inline void usb_ep_fifo_flush(struct usb_ep *ep)
/*-------------------------------------------------------------------------*/
struct usb_dcd_config_params {
__u8 bU1devExitLat; /* U1 Device exit Latency */
#define USB_DEFAULT_U1_DEV_EXIT_LAT 0x01 /* Less then 1 microsec */
__le16 bU2DevExitLat; /* U2 Device exit Latency */
#define USB_DEFAULT_U2_DEV_EXIT_LAT 0x1F4 /* Less then 500 microsec */
};
struct usb_gadget;
struct usb_gadget_driver;
/* the rest of the api to the controller hardware: device operations,
* which don't involve endpoints (or i/o).
@ -427,17 +478,30 @@ struct usb_gadget_ops {
int (*pullup) (struct usb_gadget *, int is_on);
int (*ioctl)(struct usb_gadget *,
unsigned code, unsigned long param);
void (*get_config_params)(struct usb_dcd_config_params *);
int (*udc_start)(struct usb_gadget *,
struct usb_gadget_driver *);
int (*udc_stop)(struct usb_gadget *,
struct usb_gadget_driver *);
};
/**
* struct usb_gadget - represents a usb slave device
* @work: (internal use) Workqueue to be used for sysfs_notify()
* @ops: Function pointers used to access hardware-specific operations.
* @ep0: Endpoint zero, used when reading or writing responses to
* driver setup() requests
* @ep_list: List of other endpoints supported by the device.
* @speed: Speed of current connection to USB host.
* @is_dualspeed: True if the controller supports both high and full speed
* operation. If it does, the gadget driver must also support both.
* @max_speed: Maximal speed the UDC can handle. UDC must support this
* and all slower speeds.
* @state: the state we are now (attached, suspended, configured, etc)
* @name: Identifies the controller hardware type. Used in diagnostics
* and sometimes configuration.
* @dev: Driver model state for this abstract device.
* @out_epnum: last used out ep number
* @in_epnum: last used in ep number
* @sg_supported: true if we can handle scatter-gather
* @is_otg: True if the USB device port uses a Mini-AB jack, so that the
* gadget driver must provide a USB OTG descriptor.
* @is_a_peripheral: False unless is_otg, the "A" end of a USB cable
@ -449,9 +513,8 @@ struct usb_gadget_ops {
* only supports HNP on a different root port.
* @b_hnp_enable: OTG device feature flag, indicating that the A-Host
* enabled HNP support.
* @name: Identifies the controller hardware type. Used in diagnostics
* and sometimes configuration.
* @dev: Driver model state for this abstract device.
* @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to
* MaxPacketSize.
*
* Gadgets have a mostly-portable "gadget driver" implementing device
* functions, handling all usb configurations and interfaces. Gadget
@ -477,45 +540,81 @@ struct usb_gadget {
struct usb_ep *ep0;
struct list_head ep_list; /* of usb_ep */
enum usb_device_speed speed;
unsigned is_dualspeed:1;
enum usb_device_speed max_speed;
enum usb_device_state state;
const char *name;
struct device_d dev;
unsigned out_epnum;
unsigned in_epnum;
unsigned sg_supported:1;
unsigned is_otg:1;
unsigned is_a_peripheral:1;
unsigned b_hnp_enable:1;
unsigned a_hnp_support:1;
unsigned a_alt_hnp_support:1;
const char *name;
void *priv;
unsigned quirk_ep_out_aligned_size:1;
uint32_t vendor_id;
uint32_t product_id;
char *manufacturer;
char *productname;
};
#define work_to_gadget(w) (container_of((w), struct usb_gadget, work))
static inline void set_gadget_data(struct usb_gadget *gadget, void *data)
{
gadget->priv = data;
gadget->dev.priv = data;
}
static inline void *get_gadget_data(struct usb_gadget *gadget)
{
return gadget->priv;
return gadget->dev.priv;
}
static inline struct usb_gadget *dev_to_usb_gadget(struct device_d *dev)
{
return container_of(dev, struct usb_gadget, dev);
}
/* iterates the non-control endpoints; 'tmp' is a struct usb_ep pointer */
#define gadget_for_each_ep(tmp,gadget) \
#define gadget_for_each_ep(tmp, gadget) \
list_for_each_entry(tmp, &(gadget)->ep_list, ep_list)
/**
* usb_ep_align_maybe - returns @len aligned to ep's maxpacketsize if gadget
* requires quirk_ep_out_aligned_size, otherwise reguens len.
* @g: controller to check for quirk
* @ep: the endpoint whose maxpacketsize is used to align @len
* @len: buffer size's length to align to @ep's maxpacketsize
*
* This helper is used in case it's required for any reason to check and maybe
* align buffer's size to an ep's maxpacketsize.
*/
static inline size_t
usb_ep_align_maybe(struct usb_gadget *g, struct usb_ep *ep, size_t len)
{
return !g->quirk_ep_out_aligned_size ? len :
round_up(len, (size_t)ep->desc->wMaxPacketSize);
}
/**
* gadget_is_dualspeed - return true iff the hardware handles high speed
* @g: controller that might support both high and full speeds
*/
static inline int gadget_is_dualspeed(struct usb_gadget *g)
{
#ifdef CONFIG_USB_GADGET_DUALSPEED
/* runtime test would check "g->is_dualspeed" ... that might be
* useful to work around hardware bugs, but is mostly pointless
*/
return 1;
#else
return 0;
#endif
return g->max_speed >= USB_SPEED_HIGH;
}
/**
* gadget_is_superspeed() - return true if the hardware handles superspeed
* @g: controller that might support superspeed
*/
static inline int gadget_is_superspeed(struct usb_gadget *g)
{
return g->max_speed >= USB_SPEED_SUPER;
}
/**
@ -703,12 +802,7 @@ int usb_gadget_poll(void);
/**
* struct usb_gadget_driver - driver for usb 'slave' devices
* @function: String describing the gadget's function
* @speed: Highest speed the driver handles.
* @bind: Invoked when the driver is bound to a gadget, usually
* after registering the driver.
* At that point, ep0 is fully initialized, and ep_list holds
* the currently-available endpoints.
* Called in a context that permits sleeping.
* @max_speed: Highest speed the driver handles.
* @setup: Invoked for ep0 control requests that aren't handled by
* the hardware level driver. Most calls must be handled by
* the gadget driver, including descriptor and configuration
@ -719,6 +813,7 @@ int usb_gadget_poll(void);
* when the host is disconnected. May be called in_interrupt; this
* may not sleep. Some devices can't detect disconnect, so this might
* not be called except as part of controller shutdown.
* @bind: the driver's bind callback
* @unbind: Invoked when the driver is unbound from a gadget,
* usually from rmmod (after a disconnect is reported).
* Called in a context that permits sleeping.
@ -772,8 +867,9 @@ int usb_gadget_poll(void);
*/
struct usb_gadget_driver {
char *function;
enum usb_device_speed speed;
int (*bind)(struct usb_gadget *);
enum usb_device_speed max_speed;
int (*bind)(struct usb_gadget *gadget,
struct usb_gadget_driver *driver);
void (*unbind)(struct usb_gadget *);
int (*setup)(struct usb_gadget *,
const struct usb_ctrlrequest *);
@ -782,7 +878,7 @@ struct usb_gadget_driver {
void (*resume)(struct usb_gadget *);
/* FIXME support safe rmmod */
// struct device_driver driver;
struct driver_d driver;
};
@ -797,17 +893,17 @@ struct usb_gadget_driver {
*/
/**
* usb_gadget_register_driver - register a gadget driver
* @driver:the driver being registered
* usb_gadget_probe_driver - probe a gadget driver
* @driver: the driver being registered
* Context: can sleep
*
* Call this in your gadget driver's module initialization function,
* to tell the underlying usb controller driver about your driver.
* The driver's bind() function will be called to bind it to a
* gadget before this registration call returns. It's expected that
* the bind() functions will be in init sections.
* The @bind() function will be called to bind it to a gadget before this
* registration call returns. It's expected that the @bind() function will
* be in init sections.
*/
int usb_gadget_register_driver(struct usb_gadget_driver *driver);
int usb_gadget_probe_driver(struct usb_gadget_driver *driver);
/**
* usb_gadget_unregister_driver - unregister a gadget driver
@ -824,6 +920,13 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver);
*/
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
extern int usb_add_gadget_udc_release(struct device_d *parent,
struct usb_gadget *gadget, void (*release)(struct device_d *dev));
extern int usb_add_gadget_udc(struct device_d *parent, struct usb_gadget *gadget);
extern void usb_del_gadget_udc(struct usb_gadget *gadget);
extern int udc_attach_driver(const char *name,
struct usb_gadget_driver *driver);
/*-------------------------------------------------------------------------*/
/* utility to simplify dealing with string descriptors */
@ -854,6 +957,11 @@ struct usb_gadget_strings {
struct usb_string *strings;
};
struct usb_gadget_string_container {
struct list_head list;
u8 *stash[0];
};
/* put descriptor for string with that id into buf (buflen >= 256) */
int usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf);
@ -873,21 +981,39 @@ int usb_gadget_config_buf(const struct usb_config_descriptor *config,
struct usb_descriptor_header **usb_copy_descriptors(
struct usb_descriptor_header **);
/* return copy of endpoint descriptor given original descriptor set */
struct usb_endpoint_descriptor *usb_find_endpoint(
struct usb_descriptor_header **src,
struct usb_descriptor_header **copy,
struct usb_endpoint_descriptor *match);
/**
* usb_free_descriptors - free descriptors returned by usb_copy_descriptors()
* @v: vector of descriptors
*/
static inline void usb_free_descriptors(struct usb_descriptor_header **v)
{
free(v);
kfree(v);
}
struct usb_function;
int usb_assign_descriptors(struct usb_function *f,
struct usb_descriptor_header **fs,
struct usb_descriptor_header **hs,
struct usb_descriptor_header **ss);
void usb_free_all_descriptors(struct usb_function *f);
/*-------------------------------------------------------------------------*/
/* utility to simplify map/unmap of usb_requests to/from DMA */
extern int usb_gadget_map_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in);
extern void usb_gadget_unmap_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in);
/*-------------------------------------------------------------------------*/
/* utility to set gadget state properly */
extern void usb_gadget_set_state(struct usb_gadget *gadget,
enum usb_device_state state);
/*-------------------------------------------------------------------------*/
/* utility wrapping a simple endpoint selection policy */
@ -895,6 +1021,11 @@ static inline void usb_free_descriptors(struct usb_descriptor_header **v)
extern struct usb_ep *usb_ep_autoconfig(struct usb_gadget *,
struct usb_endpoint_descriptor *);
extern struct usb_ep *usb_ep_autoconfig_ss(struct usb_gadget *,
struct usb_endpoint_descriptor *,
struct usb_ss_ep_comp_descriptor *);
extern void usb_ep_autoconfig_reset(struct usb_gadget *);
#endif /* __LINUX_USB_GADGET_H */

View File

@ -2,18 +2,10 @@
#define _USB_SERIAL_H
struct usb_serial_pdata {
char *manufacturer;
const char *productname;
u16 idVendor;
u16 idProduct;
int mode;
bool acm;
};
int usb_serial_register(struct usb_serial_pdata *pdata);
void usb_serial_unregister(void);
/* OBEX support is missing in barebox */
/* #define HAVE_OBEX */
#endif /* _USB_SERIAL_H */

View File

@ -201,6 +201,82 @@ int dev_add_param_fixed(struct device_d *dev, char *name, const char *value)
return 0;
}
struct param_string {
struct param_d param;
char **value;
int (*set)(struct param_d *p, void *priv);
int (*get)(struct param_d *p, void *priv);
};
static inline struct param_string *to_param_string(struct param_d *p)
{
return container_of(p, struct param_string, param);
}
static int param_string_set(struct device_d *dev, struct param_d *p, const char *val)
{
struct param_string *ps = to_param_string(p);
int ret;
char *value_save = *ps->value;
if (!val)
return -EINVAL;
*ps->value = xstrdup(val);
if (!ps->set)
return 0;
ret = ps->set(p, p->driver_priv);
if (ret) {
free(*ps->value);
*ps->value = value_save;
} else {
free(value_save);
}
return ret;
}
static const char *param_string_get(struct device_d *dev, struct param_d *p)
{
struct param_string *ps = to_param_string(p);
int ret;
if (ps->get) {
ret = ps->get(p, p->driver_priv);
if (ret)
return NULL;
}
return *ps->value;
}
struct param_d *dev_add_param_string(struct device_d *dev, const char *name,
int (*set)(struct param_d *p, void *priv),
int (*get)(struct param_d *p, void *priv),
char **value, void *priv)
{
struct param_string *ps;
struct param_d *p;
int ret;
ps = xzalloc(sizeof(*ps));
ps->value = value;
ps->set = set;
ps->get = get;
p = &ps->param;
p->driver_priv = priv;
ret = __dev_add_param(p, dev, name, param_string_set, param_string_get, 0);
if (ret) {
free(ps);
return ERR_PTR(ret);
}
return &ps->param;
}
struct param_int {
struct param_d param;
int *value;