diff options
-rw-r--r-- | commands/Kconfig | 5 | ||||
-rw-r--r-- | commands/Makefile | 1 | ||||
-rw-r--r-- | commands/usbgadget.c | 108 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/multi.c | 248 | ||||
-rw-r--r-- | include/usb/gadget-multi.h | 17 |
6 files changed, 380 insertions, 1 deletions
diff --git a/commands/Kconfig b/commands/Kconfig index 174a5b602d..506b3d0a4e 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -1831,6 +1831,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 diff --git a/commands/Makefile b/commands/Makefile index d42aca5c0c..f3cacebf6e 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -100,3 +100,4 @@ obj-$(CONFIG_CMD_MENUTREE) += menutree.o obj-$(CONFIG_CMD_2048) += 2048.o obj-$(CONFIG_CMD_REGULATOR) += regulator.o obj-$(CONFIG_CMD_LSPCI) += lspci.o +obj-$(CONFIG_CMD_USBGADGET) += usbgadget.o diff --git a/commands/usbgadget.c b/commands/usbgadget.c new file mode 100644 index 0000000000..fc2252ad09 --- /dev/null +++ b/commands/usbgadget.c @@ -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 diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index fce979a0e4..9ef594575b 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,5 +1,5 @@ -obj-$(CONFIG_USB_GADGET) += composite.o config.o usbstring.o epautoconf.o udc-core.o functions.o config.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 diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c new file mode 100644 index 0000000000..13fa622f01 --- /dev/null +++ b/drivers/usb/gadget/multi.c @@ -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; +} diff --git a/include/usb/gadget-multi.h b/include/usb/gadget-multi.h new file mode 100644 index 0000000000..5ca462326a --- /dev/null +++ b/include/usb/gadget-multi.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 */ |