Merge branch 'for-next/usb-gadget'
Conflicts: commands/Makefile common/Kconfig common/Makefile drivers/usb/gadget/dfu.c
This commit is contained in:
commit
7782c08047
|
@ -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``.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
136
commands/dfu.c
136
commands/dfu.c
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -88,6 +88,9 @@ config EFI_GUID
|
|||
config EFI_DEVICEPATH
|
||||
bool
|
||||
|
||||
config FILE_LIST
|
||||
bool
|
||||
|
||||
menu "General Settings"
|
||||
|
||||
config LOCALVERSION
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
obj-$(CONFIG_USB_HOST) += usb.o
|
||||
obj-$(CONFIG_USB) += common.o
|
||||
obj-$(CONFIG_OFDEVICE) += of.o
|
||||
|
|
|
@ -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);
|
|
@ -51,6 +51,7 @@
|
|||
#include <dma.h>
|
||||
|
||||
#include <usb/usb.h>
|
||||
#include <usb/ch9.h>
|
||||
|
||||
/* #define USB_DEBUG */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
|
@ -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),
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue