Просмотр исходного кода

Merge https://gitlab.denx.de/u-boot/custodians/u-boot-usb

- Assorted gadget changes including:
  - dfu: Fix handling of UBI partitions in MTD backend
  - gadget: f_thor: fix wrong file size cast
  - Extend cmd: bcb
  - Fixes for fastboot and rockchip gadgets
  - dfu: Add SCRIPT and SKIP entities
  - dfu/thor: Add `dfu_alt_info` reinitialization from flashed script
  - u-boot: Reduce size of u-boot as usbd_device_* arrays are not exported
Tom Rini 3 лет назад
Родитель
Сommit
b4804cdd57

+ 70 - 18
cmd/bcb.c

@@ -6,10 +6,12 @@
  */
 
 #include <android_bootloader_message.h>
+#include <bcb.h>
 #include <command.h>
 #include <common.h>
 #include <log.h>
 #include <part.h>
+#include <malloc.h>
 
 enum bcb_cmd {
 	BCB_CMD_LOAD,
@@ -110,8 +112,7 @@ static int bcb_field_get(char *name, char **fieldp, int *sizep)
 	return 0;
 }
 
-static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc,
-		       char *const argv[])
+static int __bcb_load(int devnum, const char *partp)
 {
 	struct blk_desc *desc;
 	struct disk_partition info;
@@ -119,17 +120,19 @@ static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc,
 	char *endp;
 	int part, ret;
 
-	ret = blk_get_device_by_str("mmc", argv[1], &desc);
-	if (ret < 0)
+	desc = blk_get_devnum_by_type(IF_TYPE_MMC, devnum);
+	if (!desc) {
+		ret = -ENODEV;
 		goto err_read_fail;
+	}
 
-	part = simple_strtoul(argv[2], &endp, 0);
+	part = simple_strtoul(partp, &endp, 0);
 	if (*endp == '\0') {
 		ret = part_get_info(desc, part, &info);
 		if (ret)
 			goto err_read_fail;
 	} else {
-		part = part_get_info_by_name(desc, argv[2], &info);
+		part = part_get_info_by_name(desc, partp, &info);
 		if (part < 0) {
 			ret = part;
 			goto err_read_fail;
@@ -151,10 +154,10 @@ static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc,
 
 	return CMD_RET_SUCCESS;
 err_read_fail:
-	printf("Error: mmc %s:%s read failed (%d)\n", argv[1], argv[2], ret);
+	printf("Error: mmc %d:%s read failed (%d)\n", devnum, partp, ret);
 	goto err;
 err_too_small:
-	printf("Error: mmc %s:%s too small!", argv[1], argv[2]);
+	printf("Error: mmc %d:%s too small!", devnum, partp);
 	goto err;
 err:
 	bcb_dev = -1;
@@ -163,33 +166,58 @@ err:
 	return CMD_RET_FAILURE;
 }
 
-static int do_bcb_set(struct cmd_tbl *cmdtp, int flag, int argc,
-		      char *const argv[])
+static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc,
+		       char * const argv[])
+{
+	char *endp;
+	int devnum = simple_strtoul(argv[1], &endp, 0);
+
+	if (*endp != '\0') {
+		printf("Error: Device id '%s' not a number\n", argv[1]);
+		return CMD_RET_FAILURE;
+	}
+
+	return __bcb_load(devnum, argv[2]);
+}
+
+static int __bcb_set(char *fieldp, const char *valp)
 {
 	int size, len;
-	char *field, *str, *found;
+	char *field, *str, *found, *tmp;
 
-	if (bcb_field_get(argv[1], &field, &size))
+	if (bcb_field_get(fieldp, &field, &size))
 		return CMD_RET_FAILURE;
 
-	len = strlen(argv[2]);
+	len = strlen(valp);
 	if (len >= size) {
 		printf("Error: sizeof('%s') = %d >= %d = sizeof(bcb.%s)\n",
-		       argv[2], len, size, argv[1]);
+		       valp, len, size, fieldp);
+		return CMD_RET_FAILURE;
+	}
+	str = strdup(valp);
+	if (!str) {
+		printf("Error: Out of memory while strdup\n");
 		return CMD_RET_FAILURE;
 	}
-	str = argv[2];
 
+	tmp = str;
 	field[0] = '\0';
-	while ((found = strsep(&str, ":"))) {
+	while ((found = strsep(&tmp, ":"))) {
 		if (field[0] != '\0')
 			strcat(field, "\n");
 		strcat(field, found);
 	}
+	free(str);
 
 	return CMD_RET_SUCCESS;
 }
 
+static int do_bcb_set(struct cmd_tbl *cmdtp, int flag, int argc,
+		      char * const argv[])
+{
+	return __bcb_set(argv[1], argv[2]);
+}
+
 static int do_bcb_clear(struct cmd_tbl *cmdtp, int flag, int argc,
 			char *const argv[])
 {
@@ -250,8 +278,7 @@ static int do_bcb_dump(struct cmd_tbl *cmdtp, int flag, int argc,
 	return CMD_RET_SUCCESS;
 }
 
-static int do_bcb_store(struct cmd_tbl *cmdtp, int flag, int argc,
-			char *const argv[])
+static int __bcb_store(void)
 {
 	struct blk_desc *desc;
 	struct disk_partition info;
@@ -282,6 +309,31 @@ err:
 	return CMD_RET_FAILURE;
 }
 
+static int do_bcb_store(struct cmd_tbl *cmdtp, int flag, int argc,
+			char * const argv[])
+{
+	return __bcb_store();
+}
+
+int bcb_write_reboot_reason(int devnum, char *partp, const char *reasonp)
+{
+	int ret;
+
+	ret = __bcb_load(devnum, partp);
+	if (ret != CMD_RET_SUCCESS)
+		return ret;
+
+	ret = __bcb_set("command", reasonp);
+	if (ret != CMD_RET_SUCCESS)
+		return ret;
+
+	ret = __bcb_store();
+	if (ret != CMD_RET_SUCCESS)
+		return ret;
+
+	return 0;
+}
+
 static struct cmd_tbl cmd_bcb_sub[] = {
 	U_BOOT_CMD_MKENT(load, CONFIG_SYS_MAXARGS, 1, do_bcb_load, "", ""),
 	U_BOOT_CMD_MKENT(set, CONFIG_SYS_MAXARGS, 1, do_bcb_set, "", ""),

+ 11 - 2
cmd/dfu.c

@@ -34,7 +34,6 @@ static int do_dfu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 #if defined(CONFIG_DFU_TIMEOUT) || defined(CONFIG_DFU_OVER_TFTP)
 	unsigned long value = 0;
 #endif
-
 	if (argc >= 4) {
 		interface = argv[2];
 		devstring = argv[3];
@@ -67,8 +66,18 @@ static int do_dfu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 	}
 
 	int controller_index = simple_strtoul(usb_controller, NULL, 0);
+	bool retry = false;
+	do {
+		run_usb_dnl_gadget(controller_index, "usb_dnl_dfu");
 
-	run_usb_dnl_gadget(controller_index, "usb_dnl_dfu");
+		if (dfu_reinit_needed) {
+			dfu_free_entities();
+			ret = dfu_init_env_entities(interface, devstring);
+			if (ret)
+				goto done;
+			retry = true;
+		}
+	} while (retry);
 
 done:
 	dfu_free_entities();

+ 12 - 7
cmd/thordown.c

@@ -52,13 +52,18 @@ int do_thor_down(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 		goto exit;
 	}
 
-	ret = thor_handle();
-	if (ret) {
-		pr_err("THOR failed: %d\n", ret);
-		ret = CMD_RET_FAILURE;
-		goto exit;
-	}
-
+	do {
+		ret = thor_handle();
+		if (ret == THOR_DFU_REINIT_NEEDED) {
+			dfu_free_entities();
+			ret = dfu_init_env_entities(interface, devstring);
+		}
+		if (ret) {
+			pr_err("THOR failed: %d\n", ret);
+			ret = CMD_RET_FAILURE;
+			goto exit;
+		}
+	} while (ret == 0);
 exit:
 	g_dnl_unregister();
 	usb_gadget_release(controller_index);

+ 2 - 2
cmd/usb_mass_storage.c

@@ -115,8 +115,8 @@ static int ums_init(const char *devtype, const char *devnums_part_str)
 		ums[ums_count].name = name;
 		ums[ums_count].block_dev = *block_dev;
 
-		printf("UMS: LUN %d, dev %d, hwpart %d, sector %#x, count %#x\n",
-		       ums_count, ums[ums_count].block_dev.devnum,
+		printf("UMS: LUN %d, dev %s %d, hwpart %d, sector %#x, count %#x\n",
+		       ums_count, devtype, ums[ums_count].block_dev.devnum,
 		       ums[ums_count].block_dev.hwpart,
 		       ums[ums_count].start_sector,
 		       ums[ums_count].num_sectors);

+ 3 - 0
common/dfu.c

@@ -98,6 +98,9 @@ int run_usb_dnl_gadget(int usbctrl_index, char *usb_dnl_gadget)
 		}
 #endif
 
+		if (dfu_reinit_needed)
+			goto exit;
+
 		WATCHDOG_RESET();
 		usb_gadget_handle_interrupts(usbctrl_index);
 	}

+ 29 - 1
doc/README.dfu

@@ -17,7 +17,7 @@ Overview:
   - The access to mediums is done in DFU backends (driver/dfu)
 
   Today the supported DFU backends are:
-  - MMC (RAW or FAT / EXT2 / EXT3 / EXT4 file system)
+  - MMC (RAW or FAT / EXT2 / EXT3 / EXT4 file system / SKIP / SCRIPT)
   - NAND
   - RAM
   - SF (serial flash)
@@ -91,6 +91,8 @@ Commands:
       <name> part <dev> <part_id> [mmcpart <num>]  raw access to partition
       <name> fat <dev> <part_id> [mmcpart <num>]   file in FAT partition
       <name> ext4 <dev> <part_id> [mmcpart <num>]  file in EXT4 partition
+      <name> skip 0 0                              ignore flashed data
+      <name> script 0 0                            execute commands in shell
 
       with <partid> being the GPT or DOS partition index,
       with <num> being the eMMC hardware partition number.
@@ -103,6 +105,32 @@ Commands:
 
       "u-boot raw 0x80 0x800;uImage ext4 0 2"
 
+    If don't want to flash given image file to storage, use "skip" type
+    entity.
+    - It can be used to protect flashing wrong image for the specific board.
+    - Especailly, this layout will be useful when thor protocol is used,
+      which performs flashing in batch mode, where more than one file is
+      processed.
+    For example, if one makes a single tar file with support for the two
+    boards with u-boot-<board1>.bin and u-boot-<board2>.bin files, one
+    can use it to flash a proper u-boot image on both without a failure:
+
+      "u-boot-<board1>.bin raw 0x80 0x800; u-boot-<board2>.bin skip 0 0"
+
+    When flashing new system image requires do some more complex things
+    than just writing data to the storage medium, one can use 'script'
+    type. Data written to such entity will be executed as a command list
+    in the u-boot's shell. This for example allows to re-create partition
+    layout and even set new dfu_alt_info for the newly created paritions.
+    Such script would look like:
+	--->8---
+	setenv dfu_alt_info ...
+	setenv mbr_parts ...
+	mbr write ...
+	--->8---
+    Please note that this means that user will be able to execute any
+    arbitrary commands just like in the u-boot's shell.
+
   "nand" (raw slc nand device)
     cmd: dfu 0 nand <dev>
     each element in "dfu_alt_info" =

+ 6 - 1
drivers/dfu/dfu.c

@@ -26,6 +26,8 @@ static struct hash_algo *dfu_hash_algo;
 static unsigned long dfu_timeout = 0;
 #endif
 
+bool dfu_reinit_needed = false;
+
 /*
  * The purpose of the dfu_flush_callback() function is to
  * provide callback for dfu user
@@ -139,6 +141,8 @@ int dfu_init_env_entities(char *interface, char *devstr)
 	char *env_bkp;
 	int ret = 0;
 
+	dfu_reinit_needed = false;
+
 #ifdef CONFIG_SET_DFU_ALT_INFO
 	set_dfu_alt_info(interface, devstr);
 #endif
@@ -614,7 +618,8 @@ const char *dfu_get_dev_type(enum dfu_device_type t)
 const char *dfu_get_layout(enum dfu_layout l)
 {
 	const char *const dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2",
-					  "EXT3", "EXT4", "RAM_ADDR" };
+					  "EXT3", "EXT4", "RAM_ADDR", "SKIP",
+					  "SCRIPT" };
 	return dfu_layout[l];
 }
 

+ 34 - 5
drivers/dfu/dfu_mmc.c

@@ -16,6 +16,7 @@
 #include <fat.h>
 #include <mmc.h>
 #include <part.h>
+#include <command.h>
 
 static unsigned char *dfu_file_buf;
 static u64 dfu_file_buf_len;
@@ -108,6 +109,8 @@ static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu,
 	case DFU_FS_EXT4:
 		fstype = FS_TYPE_EXT;
 		break;
+	case DFU_SKIP:
+		return 0;
 	default:
 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
 		       dfu_get_layout(dfu->layout));
@@ -204,6 +207,12 @@ int dfu_write_medium_mmc(struct dfu_entity *dfu,
 	case DFU_FS_EXT4:
 		ret = mmc_file_buf_write(dfu, offset, buf, len);
 		break;
+	case DFU_SCRIPT:
+		ret = run_command_list(buf, *len, 0);
+		break;
+	case DFU_SKIP:
+		ret = 0;
+		break;
 	default:
 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
 		       dfu_get_layout(dfu->layout));
@@ -216,9 +225,21 @@ int dfu_flush_medium_mmc(struct dfu_entity *dfu)
 {
 	int ret = 0;
 
-	if (dfu->layout != DFU_RAW_ADDR) {
-		/* Do stuff here. */
+	switch (dfu->layout) {
+	case DFU_FS_FAT:
+	case DFU_FS_EXT4:
 		ret = mmc_file_buf_write_finish(dfu);
+		break;
+	case DFU_SCRIPT:
+		/* script may have changed the dfu_alt_info */
+		dfu_reinit_needed = true;
+		break;
+	case DFU_RAW_ADDR:
+	case DFU_SKIP:
+		break;
+	default:
+		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
+		       dfu_get_layout(dfu->layout));
 	}
 
 	return ret;
@@ -238,6 +259,9 @@ int dfu_get_medium_size_mmc(struct dfu_entity *dfu, u64 *size)
 		if (ret < 0)
 			return ret;
 		return 0;
+	case DFU_SCRIPT:
+	case DFU_SKIP:
+		return 0;
 	default:
 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
 		       dfu_get_layout(dfu->layout));
@@ -316,7 +340,7 @@ void dfu_free_entity_mmc(struct dfu_entity *dfu)
 int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
 {
 	const char *entity_type;
-	size_t second_arg;
+	ssize_t second_arg;
 	size_t third_arg;
 
 	struct mmc *mmc;
@@ -339,7 +363,7 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
 	 * Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8,
 	 * with default 10.
 	 */
-	second_arg = simple_strtoul(argv[1], NULL, 0);
+	second_arg = simple_strtol(argv[1], NULL, 0);
 	third_arg = simple_strtoul(argv[2], NULL, 0);
 
 	mmc = find_mmc_device(dfu->data.mmc.dev_num);
@@ -399,6 +423,10 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
 		dfu->layout = DFU_FS_FAT;
 	} else if (!strcmp(entity_type, "ext4")) {
 		dfu->layout = DFU_FS_EXT4;
+	} else if (!strcmp(entity_type, "skip")) {
+		dfu->layout = DFU_SKIP;
+	} else if (!strcmp(entity_type, "script")) {
+		dfu->layout = DFU_SCRIPT;
 	} else {
 		pr_err("Memory layout (%s) not supported!\n", entity_type);
 		return -ENODEV;
@@ -406,7 +434,8 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
 
 	/* if it's NOT a raw write */
 	if (strcmp(entity_type, "raw")) {
-		dfu->data.mmc.dev = second_arg;
+		dfu->data.mmc.dev = (second_arg != -1) ? second_arg :
+							 dfu->data.mmc.dev_num;
 		dfu->data.mmc.part = third_arg;
 	}
 

+ 2 - 2
drivers/dfu/dfu_mtd.c

@@ -204,7 +204,7 @@ static int dfu_flush_medium_mtd(struct dfu_entity *dfu)
 	int ret;
 
 	/* in case of ubi partition, erase rest of the partition */
-	if (dfu->data.nand.ubi) {
+	if (dfu->data.mtd.ubi) {
 		struct erase_info erase_op = {};
 
 		erase_op.mtd = dfu->data.mtd.info;
@@ -242,7 +242,7 @@ static unsigned int dfu_polltimeout_mtd(struct dfu_entity *dfu)
 	 * ubi partition, as sectors which are not used need
 	 * to be erased
 	 */
-	if (dfu->data.nand.ubi)
+	if (dfu->data.mtd.ubi)
 		return DFU_MANIFEST_POLL_TIMEOUT;
 
 	return DFU_DEFAULT_POLL_TIMEOUT;

+ 9 - 0
drivers/usb/gadget/Kconfig

@@ -98,6 +98,15 @@ config USB_GADGET_DWC2_OTG_PHY_BUS_WIDTH_8
 
 endif # USB_GADGET_DWC2_OTG
 
+config USB_GADGET_OS_DESCRIPTORS
+	bool "USB OS Feature Descriptors support"
+	help
+	  This is a porting patch from linux kernel: 37a3a533429e
+	  ("usb: gadget: OS Feature Descriptors support"), the original commit
+	  log see below:
+	  There is a custom (non-USB IF) extension to the USB standard:
+	  http://msdn.microsoft.com/library/windows/hardware/gg463182
+
 config CI_UDC
 	bool "ChipIdea device controller"
 	select USB_GADGET_DUALSPEED

+ 3 - 2
drivers/usb/gadget/ci_udc.c

@@ -145,6 +145,7 @@ static struct ci_drv controller = {
 		.name	= "ci_udc",
 		.ops	= &ci_udc_ops,
 		.is_dualspeed = 1,
+		.max_speed = USB_SPEED_HIGH,
 	},
 };
 
@@ -335,6 +336,7 @@ static int ci_ep_enable(struct usb_ep *ep,
 	num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
 	in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
 	ci_ep->desc = desc;
+	ep->desc = desc;
 
 	if (num) {
 		int max = get_unaligned_le16(&desc->wMaxPacketSize);
@@ -357,6 +359,7 @@ static int ci_ep_disable(struct usb_ep *ep)
 	struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
 
 	ci_ep->desc = NULL;
+	ep->desc = NULL;
 	return 0;
 }
 
@@ -1015,8 +1018,6 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
 		return -EINVAL;
 	if (!driver->bind || !driver->setup || !driver->disconnect)
 		return -EINVAL;
-	if (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH)
-		return -EINVAL;
 
 #if CONFIG_IS_ENABLED(DM_USB)
 	ret = usb_setup_ehci_gadget(&controller.ctrl);

+ 366 - 26
drivers/usb/gadget/composite.c

@@ -12,6 +12,7 @@
 #include <linux/bitops.h>
 #include <linux/bug.h>
 #include <linux/usb/composite.h>
+#include "u_os_desc.h"
 
 #define USB_BUFSIZ	4096
 
@@ -19,12 +20,32 @@
 typedef struct { __le16 val; } __packed __le16_packed;
 
 static struct usb_composite_driver *composite;
+static struct usb_configuration *os_desc_config;
+
+/* Microsoft OS String Descriptor */
+static char qw_sign_buf[OS_STRING_QW_SIGN_LEN / 2] = {'M', 'S', 'F', 'T', '1', '0', '0'};
 
 static inline void le16_add_cpu_packed(__le16_packed *var, u16 val)
 {
 	var->val = cpu_to_le16(le16_to_cpu(var->val) + val);
 }
 
+/**
+ * struct usb_os_string - represents OS String to be reported by a gadget
+ * @bLength: total length of the entire descritor, always 0x12
+ * @bDescriptorType: USB_DT_STRING
+ * @qwSignature: the OS String proper
+ * @bMS_VendorCode: code used by the host for subsequent requests
+ * @bPad: not used, must be zero
+ */
+struct usb_os_string {
+	__u8	bLength;
+	__u8	bDescriptorType;
+	__u8	qwSignature[OS_STRING_QW_SIGN_LEN];
+	__u8	bMS_VendorCode;
+	__u8	bPad;
+} __packed;
+
 /**
  * usb_add_function() - add a function to a configuration
  * @config: the configuration
@@ -67,6 +88,8 @@ int usb_add_function(struct usb_configuration *config,
 		config->fullspeed = 1;
 	if (!config->highspeed && function->hs_descriptors)
 		config->highspeed = 1;
+	if (!config->superspeed && function->ss_descriptors)
+		config->superspeed = 1;
 
 done:
 	if (value)
@@ -202,7 +225,9 @@ static int config_buf(struct usb_configuration *config,
 
 	/* add each function's descriptors */
 	list_for_each_entry(f, &config->functions, list) {
-		if (speed == USB_SPEED_HIGH)
+		if (speed == USB_SPEED_SUPER)
+			descriptors = f->ss_descriptors;
+		else if (speed == USB_SPEED_HIGH)
 			descriptors = f->hs_descriptors;
 		else
 			descriptors = f->descriptors;
@@ -228,8 +253,11 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
 	u8				type = w_value >> 8;
 	int                             hs = 0;
 	struct usb_configuration	*c;
+	struct list_head		*pos;
 
-	if (gadget_is_dualspeed(gadget)) {
+	if (gadget_is_superspeed(gadget)) {
+		speed = gadget->speed;
+	} else if (gadget_is_dualspeed(gadget)) {
 		if (gadget->speed == USB_SPEED_HIGH)
 			hs = 1;
 		if (type == USB_DT_OTHER_SPEED_CONFIG)
@@ -239,8 +267,24 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
 	}
 
 	w_value &= 0xff;
-	list_for_each_entry(c, &cdev->configs, list) {
-		if (speed == USB_SPEED_HIGH) {
+
+	pos = &cdev->configs;
+	c = cdev->os_desc_config;
+	if (c)
+		goto check_config;
+
+	while ((pos = pos->next) !=  &cdev->configs) {
+		c = list_entry(pos, typeof(*c), list);
+
+		/* skip OS Descriptors config which is handled separately */
+		if (c == cdev->os_desc_config)
+			continue;
+
+check_config:
+		if (speed == USB_SPEED_SUPER) {
+			if (!c->superspeed)
+				continue;
+		} else if (speed == USB_SPEED_HIGH) {
 			if (!c->highspeed)
 				continue;
 		} else {
@@ -259,8 +303,12 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
 	struct usb_gadget		*gadget = cdev->gadget;
 	unsigned			count = 0;
 	int				hs = 0;
+	int				ss = 0;
 	struct usb_configuration	*c;
 
+	if (gadget->speed == USB_SPEED_SUPER)
+		ss = 1;
+
 	if (gadget_is_dualspeed(gadget)) {
 		if (gadget->speed == USB_SPEED_HIGH)
 			hs = 1;
@@ -269,7 +317,10 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
 	}
 	list_for_each_entry(c, &cdev->configs, list) {
 		/* ignore configs that won't work at this speed */
-		if (hs) {
+		if (ss) {
+			if (!c->superspeed)
+				continue;
+		} else if (hs) {
 			if (!c->highspeed)
 				continue;
 		} else {
@@ -353,6 +404,9 @@ static int set_config(struct usb_composite_dev *cdev,
 		     case USB_SPEED_HIGH:
 			     speed = "high";
 			     break;
+		     case USB_SPEED_SUPER:
+			     speed = "super";
+			     break;
 		     default:
 			     speed = "?";
 			     break;
@@ -377,7 +431,9 @@ static int set_config(struct usb_composite_dev *cdev,
 		 * function's setup callback instead of the current
 		 * configuration's setup callback.
 		 */
-		if (gadget->speed == USB_SPEED_HIGH)
+		if (gadget->speed == USB_SPEED_SUPER)
+			descriptors = f->ss_descriptors;
+		else if (gadget->speed == USB_SPEED_HIGH)
 			descriptors = f->hs_descriptors;
 		else
 			descriptors = f->descriptors;
@@ -457,8 +513,9 @@ int usb_add_config(struct usb_composite_dev *cdev,
 		list_del(&config->list);
 		config->cdev = NULL;
 	} else {
-		debug("cfg %d/%p speeds:%s%s\n",
+		debug("cfg %d/%p speeds:%s%s%s\n",
 			config->bConfigurationValue, config,
+			config->superspeed ? " super" : "",
 			config->highspeed ? " high" : "",
 			config->fullspeed
 				? (gadget_is_dualspeed(cdev->gadget)
@@ -475,8 +532,24 @@ int usb_add_config(struct usb_composite_dev *cdev,
 		}
 	}
 
+	/*
+	 * If one function of config is not super speed capable,
+	 * force the gadget to be high speed so controller driver
+	 * can init HW to be USB 2.0
+	 */
+	if (gadget_is_superspeed(cdev->gadget)) {
+		list_for_each_entry(f, &config->functions, list) {
+			if (!f->ss_descriptors)
+				cdev->gadget->max_speed =
+					USB_SPEED_HIGH;
+		}
+	}
+
 	usb_ep_autoconfig_reset(cdev->gadget);
 
+	os_desc_config = config;
+	cdev->os_desc_config = os_desc_config;
+
 done:
 	if (status)
 		debug("added config '%s'/%u --> %d\n", config->label,
@@ -577,6 +650,16 @@ static int get_string(struct usb_composite_dev *cdev,
 		return s->bLength;
 	}
 
+	if (cdev->use_os_string && language == 0 && id == OS_STRING_IDX) {
+		struct usb_os_string *b = buf;
+		b->bLength = sizeof(*b);
+		b->bDescriptorType = USB_DT_STRING;
+		memcpy(&b->qwSignature, cdev->qw_sign, sizeof(b->qwSignature));
+		b->bMS_VendorCode = cdev->b_vendor_code;
+		b->bPad = 0;
+		return sizeof(*b);
+	}
+
 	/*
 	 * Otherwise, look up and return a specified string.  String IDs
 	 * are device-scoped, so we look up each string table we're told
@@ -703,6 +786,7 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
 static int bos_desc(struct usb_composite_dev *cdev)
 {
 	struct usb_ext_cap_descriptor   *usb_ext;
+	struct usb_dcd_config_params	dcd_config_params;
 	struct usb_bos_descriptor       *bos = cdev->req->buf;
 
 	bos->bLength = USB_DT_BOS_SIZE;
@@ -746,13 +830,173 @@ static int bos_desc(struct usb_composite_dev *cdev)
 				    USB_HIGH_SPEED_OPERATION |
 				    USB_5GBPS_OPERATION);
 		ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
-		ss_cap->bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT;
-		ss_cap->bU2DevExitLat =
-			cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+
+		/* Get Controller configuration */
+		if (cdev->gadget->ops->get_config_params) {
+			cdev->gadget->ops->get_config_params(
+				&dcd_config_params);
+		} else {
+			dcd_config_params.bU1devExitLat =
+				USB_DEFAULT_U1_DEV_EXIT_LAT;
+			dcd_config_params.bU2DevExitLat =
+				cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+		}
+		ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
+		ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
 	}
 	return le16_to_cpu(bos->wTotalLength);
 }
 
+static int count_ext_compat(struct usb_configuration *c)
+{
+	int i, res;
+
+	res = 0;
+	for (i = 0; i < c->next_interface_id; ++i) {
+		struct usb_function *f;
+		int j;
+
+		f = c->interface[i];
+		for (j = 0; j < f->os_desc_n; ++j) {
+			struct usb_os_desc *d;
+
+			if (i != f->os_desc_table[j].if_id)
+				continue;
+			d = f->os_desc_table[j].os_desc;
+			if (d && d->ext_compat_id)
+				++res;
+		}
+	}
+	BUG_ON(res > 255);
+	return res;
+}
+
+static void fill_ext_compat(struct usb_configuration *c, u8 *buf)
+{
+	int i, count;
+
+	count = 16;
+	for (i = 0; i < c->next_interface_id; ++i) {
+		struct usb_function *f;
+		int j;
+
+		f = c->interface[i];
+		for (j = 0; j < f->os_desc_n; ++j) {
+			struct usb_os_desc *d;
+
+			if (i != f->os_desc_table[j].if_id)
+				continue;
+			d = f->os_desc_table[j].os_desc;
+			if (d && d->ext_compat_id) {
+				*buf++ = i;
+				*buf++ = 0x01;
+				memcpy(buf, d->ext_compat_id, 16);
+				buf += 22;
+			} else {
+				++buf;
+				*buf = 0x01;
+				buf += 23;
+			}
+			count += 24;
+			if (count >= 4096)
+				return;
+		}
+	}
+}
+
+static int count_ext_prop(struct usb_configuration *c, int interface)
+{
+	struct usb_function *f;
+	int j;
+
+	f = c->interface[interface];
+	for (j = 0; j < f->os_desc_n; ++j) {
+		struct usb_os_desc *d;
+
+		if (interface != f->os_desc_table[j].if_id)
+			continue;
+		d = f->os_desc_table[j].os_desc;
+		if (d && d->ext_compat_id)
+			return d->ext_prop_count;
+	}
+	return 0;
+}
+
+static int len_ext_prop(struct usb_configuration *c, int interface)
+{
+	struct usb_function *f;
+	struct usb_os_desc *d;
+	int j, res;
+
+	res = 10; /* header length */
+	f = c->interface[interface];
+	for (j = 0; j < f->os_desc_n; ++j) {
+		if (interface != f->os_desc_table[j].if_id)
+			continue;
+		d = f->os_desc_table[j].os_desc;
+		if (d)
+			return min(res + d->ext_prop_len, 4096);
+	}
+	return res;
+}
+
+static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf)
+{
+	struct usb_function *f;
+	struct usb_os_desc *d;
+	struct usb_os_desc_ext_prop *ext_prop;
+	int j, count, n, ret;
+	u8 *start = buf;
+
+	f = c->interface[interface];
+	for (j = 0; j < f->os_desc_n; ++j) {
+		if (interface != f->os_desc_table[j].if_id)
+			continue;
+		d = f->os_desc_table[j].os_desc;
+		if (d)
+			list_for_each_entry(ext_prop, &d->ext_prop, entry) {
+				/* 4kB minus header length */
+				n = buf - start;
+				if (n >= 4086)
+					return 0;
+
+				count = ext_prop->data_len +
+					ext_prop->name_len + 14;
+				if (count > 4086 - n)
+					return -EINVAL;
+				usb_ext_prop_put_size(buf, count);
+				usb_ext_prop_put_type(buf, ext_prop->type);
+				ret = usb_ext_prop_put_name(buf, ext_prop->name,
+							    ext_prop->name_len);
+				if (ret < 0)
+					return ret;
+				switch (ext_prop->type) {
+				case USB_EXT_PROP_UNICODE:
+				case USB_EXT_PROP_UNICODE_ENV:
+				case USB_EXT_PROP_UNICODE_LINK:
+					usb_ext_prop_put_unicode(buf, ret,
+							 ext_prop->data,
+							 ext_prop->data_len);
+					break;
+				case USB_EXT_PROP_BINARY:
+					usb_ext_prop_put_binary(buf, ret,
+							ext_prop->data,
+							ext_prop->data_len);
+					break;
+				case USB_EXT_PROP_LE32:
+					/* not implemented */
+				case USB_EXT_PROP_BE32:
+					/* not implemented */
+				default:
+					return -EINVAL;
+				}
+				buf += count;
+			}
+	}
+
+	return 0;
+}
+
 /*
  * The setup() callback implements all the ep0 functionality that's
  * not handled lower down, in hardware or the hardware driver(like
@@ -801,32 +1045,28 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 			cdev->desc.bNumConfigurations =
 				count_configs(cdev, USB_DT_DEVICE);
 
-			/*
-			 * If the speed is Super speed, then the supported
-			 * max packet size is 512 and it should be sent as
-			 * exponent of 2. So, 9(2^9=512) should be filled in
-			 * bMaxPacketSize0. Also fill USB version as 3.0
-			 * if speed is Super speed.
-			 */
-			if (cdev->gadget->speed == USB_SPEED_SUPER) {
+			cdev->desc.bMaxPacketSize0 =
+				cdev->gadget->ep0->maxpacket;
+			if (gadget->speed >= USB_SPEED_SUPER) {
+				cdev->desc.bcdUSB = cpu_to_le16(0x0310);
 				cdev->desc.bMaxPacketSize0 = 9;
-				cdev->desc.bcdUSB = cpu_to_le16(0x0300);
 			} else {
-				cdev->desc.bMaxPacketSize0 =
-					cdev->gadget->ep0->maxpacket;
+				cdev->desc.bcdUSB = cpu_to_le16(0x0200);
 			}
 			value = min(w_length, (u16) sizeof cdev->desc);
 			memcpy(req->buf, &cdev->desc, value);
 			break;
 		case USB_DT_DEVICE_QUALIFIER:
-			if (!gadget_is_dualspeed(gadget))
+			if (!gadget_is_dualspeed(gadget) ||
+			    gadget->speed >= USB_SPEED_SUPER)
 				break;
 			device_qual(cdev);
 			value = min_t(int, w_length,
 				      sizeof(struct usb_qualifier_descriptor));
 			break;
 		case USB_DT_OTHER_SPEED_CONFIG:
-			if (!gadget_is_dualspeed(gadget))
+			if (!gadget_is_dualspeed(gadget) ||
+			    gadget->speed >= USB_SPEED_SUPER)
 				break;
 
 		case USB_DT_CONFIG:
@@ -841,10 +1081,16 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 				value = min(w_length, (u16) value);
 			break;
 		case USB_DT_BOS:
-			if (gadget_is_superspeed(cdev->gadget))
+			/*
+			 * Super speed connection should support BOS, and
+			 * USB compliance test (USB 2.0 Command Verifier)
+			 * also issues this request, return for now for
+			 * USB 2.0 connection.
+			 */
+			if (gadget->speed >= USB_SPEED_SUPER) {
 				value = bos_desc(cdev);
-			if (value >= 0)
 				value = min(w_length, (u16)value);
+			}
 			break;
 		default:
 			goto unknown;
@@ -909,6 +1155,91 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 		break;
 	default:
 unknown:
+		/*
+		 * OS descriptors handling
+		 */
+		if (CONFIG_IS_ENABLED(USB_GADGET_OS_DESCRIPTORS) && cdev->use_os_string &&
+		    cdev->os_desc_config && (ctrl->bRequestType & USB_TYPE_VENDOR) &&
+		    ctrl->bRequest == cdev->b_vendor_code) {
+			struct usb_configuration	*os_desc_cfg;
+			u8				*buf;
+			int				interface;
+			int				count = 0;
+
+			buf = req->buf;
+			os_desc_cfg = cdev->os_desc_config;
+			memset(buf, 0, w_length);
+			buf[5] = 0x01;
+			switch (ctrl->bRequestType & USB_RECIP_MASK) {
+			case USB_RECIP_DEVICE:
+				if (w_index != 0x4 || (w_value >> 8))
+					break;
+				buf[6] = w_index;
+				if (w_length == 0x10) {
+					/* Number of ext compat interfaces */
+					count = count_ext_compat(os_desc_cfg);
+					buf[8] = count;
+					count *= 24; /* 24 B/ext compat desc */
+					count += 16; /* header */
+					put_unaligned_le32(count, buf);
+					value = w_length;
+				} else {
+					/* "extended compatibility ID"s */
+					count = count_ext_compat(os_desc_cfg);
+					buf[8] = count;
+					count *= 24; /* 24 B/ext compat desc */
+					count += 16; /* header */
+					put_unaligned_le32(count, buf);
+					buf += 16;
+					fill_ext_compat(os_desc_cfg, buf);
+					value = w_length;
+				}
+				break;
+			case USB_RECIP_INTERFACE:
+				if (w_index != 0x5 || (w_value >> 8))
+					break;
+				interface = w_value & 0xFF;
+				buf[6] = w_index;
+				if (w_length == 0x0A) {
+					count = count_ext_prop(os_desc_cfg,
+						interface);
+					put_unaligned_le16(count, buf + 8);
+					count = len_ext_prop(os_desc_cfg,
+						interface);
+					put_unaligned_le32(count, buf);
+
+					value = w_length;
+				} else {
+					count = count_ext_prop(os_desc_cfg,
+						interface);
+					put_unaligned_le16(count, buf + 8);
+					count = len_ext_prop(os_desc_cfg,
+						interface);
+					put_unaligned_le32(count, buf);
+					buf += 10;
+					value = fill_ext_prop(os_desc_cfg,
+							      interface, buf);
+					if (value < 0)
+						return value;
+
+					value = w_length;
+				}
+				break;
+			}
+
+			if (value >= 0) {
+				req->length = value;
+				req->zero = value < w_length;
+				value = usb_ep_queue(gadget->ep0, req, GFP_KERNEL);
+				if (value < 0) {
+					debug("ep_queue --> %d\n", value);
+					req->status = 0;
+					composite_setup_complete(gadget->ep0, req);
+				}
+			}
+			return value;
+		}
+
 		debug("non-core control req%02x.%02x v%04x i%04x l%d\n",
 			ctrl->bRequestType, ctrl->bRequest,
 			w_value, w_index, w_length);
@@ -1082,6 +1413,15 @@ static int composite_bind(struct usb_gadget *gadget)
 	       sizeof(struct usb_device_descriptor));
 	cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
 
+	if (cdev->use_os_string) {
+		/* TODO: Do we want to pass this via platform? */
+		cdev->b_vendor_code = 0x40;
+
+		/* Microsoft OS String Descriptor */
+		utf8_to_utf16le(qw_sign_buf, (__le16 *)cdev->qw_sign,
+				OS_STRING_QW_SIGN_LEN / 2);
+	}
+
 	debug("%s: ready\n", composite->name);
 	return 0;
 
@@ -1129,7 +1469,7 @@ composite_resume(struct usb_gadget *gadget)
 }
 
 static struct usb_gadget_driver composite_driver = {
-	.speed		= USB_SPEED_HIGH,
+	.speed		= USB_SPEED_SUPER,
 
 	.bind		= composite_bind,
 	.unbind         = composite_unbind,

+ 4 - 41
drivers/usb/gadget/core.c

@@ -36,7 +36,7 @@ extern struct usb_function_driver ep0_driver;
 int registered_functions;
 int registered_devices;
 
-char *usbd_device_events[] = {
+__maybe_unused static char *usbd_device_events[] = {
 	"DEVICE_UNKNOWN",
 	"DEVICE_INIT",
 	"DEVICE_CREATE",
@@ -56,52 +56,15 @@ char *usbd_device_events[] = {
 	"DEVICE_FUNCTION_PRIVATE",
 };
 
-char *usbd_device_states[] = {
-	"STATE_INIT",
-	"STATE_CREATED",
-	"STATE_ATTACHED",
-	"STATE_POWERED",
-	"STATE_DEFAULT",
-	"STATE_ADDRESSED",
-	"STATE_CONFIGURED",
-	"STATE_UNKNOWN",
-};
-
-char *usbd_device_requests[] = {
-	"GET STATUS",		/* 0 */
-	"CLEAR FEATURE",	/* 1 */
-	"RESERVED",		/* 2 */
-	"SET FEATURE",		/* 3 */
-	"RESERVED",		/* 4 */
-	"SET ADDRESS",		/* 5 */
-	"GET DESCRIPTOR",	/* 6 */
-	"SET DESCRIPTOR",	/* 7 */
-	"GET CONFIGURATION",	/* 8 */
-	"SET CONFIGURATION",	/* 9 */
-	"GET INTERFACE",	/* 10 */
-	"SET INTERFACE",	/* 11 */
-	"SYNC FRAME",		/* 12 */
-};
-
-char *usbd_device_descriptors[] = {
-	"UNKNOWN",		/* 0 */
-	"DEVICE",		/* 1 */
-	"CONFIG",		/* 2 */
-	"STRING",		/* 3 */
-	"INTERFACE",		/* 4 */
-	"ENDPOINT",		/* 5 */
-	"DEVICE QUALIFIER",	/* 6 */
-	"OTHER SPEED",		/* 7 */
-	"INTERFACE POWER",	/* 8 */
-};
-
-char *usbd_device_status[] = {
+__maybe_unused static char *usbd_device_status[] = {
 	"USBD_OPENING",
 	"USBD_OK",
 	"USBD_SUSPENDED",
 	"USBD_CLOSING",
 };
 
+#define USBD_DEVICE_STATUS(x) (((unsigned int)x <= USBD_CLOSING) ? usbd_device_status[x] : "UNKNOWN")
+
 
 /* Descriptor support functions ************************************************************** */
 

+ 46 - 0
drivers/usb/gadget/ep0.c

@@ -46,6 +46,52 @@
 #define dbg_ep0(lvl,fmt,args...)
 #endif
 
+__maybe_unused static char *usbd_device_descriptors[] = {
+	"UNKNOWN",		/* 0 */
+	"DEVICE",		/* 1 */
+	"CONFIG",		/* 2 */
+	"STRING",		/* 3 */
+	"INTERFACE",		/* 4 */
+	"ENDPOINT",		/* 5 */
+	"DEVICE QUALIFIER",	/* 6 */
+	"OTHER SPEED",		/* 7 */
+	"INTERFACE POWER",	/* 8 */
+};
+
+#define USBD_DEVICE_DESCRIPTORS(x) (((unsigned int)x <= USB_DESCRIPTOR_TYPE_INTERFACE_POWER) ? \
+		usbd_device_descriptors[x] : "UNKNOWN")
+
+__maybe_unused static char *usbd_device_states[] = {
+	"STATE_INIT",
+	"STATE_CREATED",
+	"STATE_ATTACHED",
+	"STATE_POWERED",
+	"STATE_DEFAULT",
+	"STATE_ADDRESSED",
+	"STATE_CONFIGURED",
+	"STATE_UNKNOWN",
+};
+
+#define USBD_DEVICE_STATE(x) (((unsigned int)x <= STATE_UNKNOWN) ? usbd_device_states[x] : "UNKNOWN")
+
+__maybe_unused static char *usbd_device_requests[] = {
+	"GET STATUS",		/* 0 */
+	"CLEAR FEATURE",	/* 1 */
+	"RESERVED",		/* 2 */
+	"SET FEATURE",		/* 3 */
+	"RESERVED",		/* 4 */
+	"SET ADDRESS",		/* 5 */
+	"GET DESCRIPTOR",	/* 6 */
+	"SET DESCRIPTOR",	/* 7 */
+	"GET CONFIGURATION",	/* 8 */
+	"SET CONFIGURATION",	/* 9 */
+	"GET INTERFACE",	/* 10 */
+	"SET INTERFACE",	/* 11 */
+	"SYNC FRAME",		/* 12 */
+};
+
+#define USBD_DEVICE_REQUESTS(x) (((unsigned int)x <= USB_REQ_SYNCH_FRAME) ? usbd_device_requests[x] : "UNKNOWN")
+
 /* EP0 Configuration Set ********************************************************************* */
 
 

+ 6 - 0
drivers/usb/gadget/epautoconf.c

@@ -167,6 +167,10 @@ static int ep_matches(
 			size = 64;
 		put_unaligned(cpu_to_le16(size), &desc->wMaxPacketSize);
 	}
+
+	if (gadget->ops->ep_conf)
+		return gadget->ops->ep_conf(gadget, ep, desc);
+
 	return 1;
 }
 
@@ -258,6 +262,7 @@ struct usb_ep *usb_ep_autoconfig(
 		ep = find_ep(gadget, "ep1-bulk");
 		if (ep && ep_matches(gadget, ep, desc))
 			return ep;
+#ifndef CONFIG_SPL_BUILD
 	} else if (gadget_is_dwc3(gadget)) {
 		const char *name = NULL;
 		/*
@@ -280,6 +285,7 @@ struct usb_ep *usb_ep_autoconfig(
 			ep = find_ep(gadget, name);
 		if (ep && ep_matches(gadget, ep, desc))
 			return ep;
+#endif
 	}
 
 	if (gadget->ops->match_ep)

+ 80 - 5
drivers/usb/gadget/f_fastboot.c

@@ -46,6 +46,25 @@ struct f_fastboot {
 	struct usb_request *in_req, *out_req;
 };
 
+static char fb_ext_prop_name[] = "DeviceInterfaceGUID";
+static char fb_ext_prop_data[] = "{4866319A-F4D6-4374-93B9-DC2DEB361BA9}";
+
+static struct usb_os_desc_ext_prop fb_ext_prop = {
+	.type = 1,		/* NUL-terminated Unicode String (REG_SZ) */
+	.name = fb_ext_prop_name,
+	.data = fb_ext_prop_data,
+};
+
+/* 16 bytes of "Compatible ID" and "Subcompatible ID" */
+static char fb_cid[16] = {'W', 'I', 'N', 'U', 'S', 'B'};
+static struct usb_os_desc fb_os_desc = {
+	.ext_compat_id = fb_cid,
+};
+
+static struct usb_os_desc_table fb_os_desc_table = {
+	.os_desc = &fb_os_desc,
+};
+
 static inline struct f_fastboot *func_to_fastboot(struct usb_function *f)
 {
 	return container_of(f, struct f_fastboot, usb_function);
@@ -109,10 +128,45 @@ static struct usb_descriptor_header *fb_hs_function[] = {
 	NULL,
 };
 
+/* Super speed */
+static struct usb_endpoint_descriptor ss_ep_in = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize		= cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_ep_out = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_OUT,
+	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize		= cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor fb_ss_bulk_comp_desc = {
+	.bLength =		sizeof(fb_ss_bulk_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *fb_ss_function[] = {
+	(struct usb_descriptor_header *)&interface_desc,
+	(struct usb_descriptor_header *)&ss_ep_in,
+	(struct usb_descriptor_header *)&fb_ss_bulk_comp_desc,
+	(struct usb_descriptor_header *)&ss_ep_out,
+	(struct usb_descriptor_header *)&fb_ss_bulk_comp_desc,
+	NULL,
+};
+
 static struct usb_endpoint_descriptor *
 fb_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
-	    struct usb_endpoint_descriptor *hs)
+	    struct usb_endpoint_descriptor *hs,
+	    struct usb_endpoint_descriptor *ss)
 {
+	if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER)
+		return ss;
+
 	if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
 		return hs;
 	return fs;
@@ -161,6 +215,19 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
 		return id;
 	interface_desc.bInterfaceNumber = id;
 
+	/* Enable OS and Extended Properties Feature Descriptor */
+	c->cdev->use_os_string = 1;
+	f->os_desc_table = &fb_os_desc_table;
+	f->os_desc_n = 1;
+	f->os_desc_table->if_id = id;
+	INIT_LIST_HEAD(&fb_os_desc.ext_prop);
+	fb_ext_prop.name_len = strlen(fb_ext_prop.name) * 2 + 2;
+	fb_os_desc.ext_prop_len = 10 + fb_ext_prop.name_len;
+	fb_os_desc.ext_prop_count = 1;
+	fb_ext_prop.data_len = strlen(fb_ext_prop.data) * 2 + 2;
+	fb_os_desc.ext_prop_len += fb_ext_prop.data_len + 4;
+	list_add_tail(&fb_ext_prop.entry, &fb_os_desc.ext_prop);
+
 	id = usb_string_id(c->cdev);
 	if (id < 0)
 		return id;
@@ -187,6 +254,12 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
 		f->hs_descriptors = fb_hs_function;
 	}
 
+	if (gadget_is_superspeed(gadget)) {
+		ss_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
+		ss_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
+		f->ss_descriptors = fb_ss_function;
+	}
+
 	s = env_get("serial#");
 	if (s)
 		g_dnl_set_serialnumber((char *)s);
@@ -196,6 +269,8 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
 
 static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
 {
+	f->os_desc_table = NULL;
+	list_del(&fb_os_desc.ext_prop);
 	memset(fastboot_func, 0, sizeof(*fastboot_func));
 }
 
@@ -249,7 +324,7 @@ static int fastboot_set_alt(struct usb_function *f,
 	debug("%s: func: %s intf: %d alt: %d\n",
 	      __func__, f->name, interface, alt);
 
-	d = fb_ep_desc(gadget, &fs_ep_out, &hs_ep_out);
+	d = fb_ep_desc(gadget, &fs_ep_out, &hs_ep_out, &ss_ep_out);
 	ret = usb_ep_enable(f_fb->out_ep, d);
 	if (ret) {
 		puts("failed to enable out ep\n");
@@ -264,7 +339,7 @@ static int fastboot_set_alt(struct usb_function *f,
 	}
 	f_fb->out_req->complete = rx_handler_command;
 
-	d = fb_ep_desc(gadget, &fs_ep_in, &hs_ep_in);
+	d = fb_ep_desc(gadget, &fs_ep_in, &hs_ep_in, &ss_ep_in);
 	ret = usb_ep_enable(f_fb->in_ep, d);
 	if (ret) {
 		puts("failed to enable in ep\n");
@@ -315,7 +390,7 @@ static int fastboot_add(struct usb_configuration *c)
 	status = usb_add_function(c, &f_fb->usb_function);
 	if (status) {
 		free(f_fb);
-		fastboot_func = f_fb;
+		fastboot_func = NULL;
 	}
 
 	return status;
@@ -352,7 +427,7 @@ static unsigned int rx_bytes_expected(struct usb_ep *ep)
 {
 	int rx_remain = fastboot_data_remaining();
 	unsigned int rem;
-	unsigned int maxpacket = ep->maxpacket;
+	unsigned int maxpacket = usb_endpoint_maxp(ep->desc);
 
 	if (rx_remain <= 0)
 		return 0;

+ 4 - 3
drivers/usb/gadget/f_rockusb.c

@@ -110,7 +110,7 @@ struct f_rockusb *get_rkusb(void)
 	if (!f_rkusb) {
 		f_rkusb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_rkusb));
 		if (!f_rkusb)
-			return 0;
+			return NULL;
 
 		rockusb_func = f_rkusb;
 		memset(f_rkusb, 0, sizeof(*f_rkusb));
@@ -120,7 +120,7 @@ struct f_rockusb *get_rkusb(void)
 		f_rkusb->buf_head = memalign(CONFIG_SYS_CACHELINE_SIZE,
 					     RKUSB_BUF_SIZE);
 		if (!f_rkusb->buf_head)
-			return 0;
+			return NULL;
 
 		f_rkusb->buf = f_rkusb->buf_head;
 		memset(f_rkusb->buf_head, 0, RKUSB_BUF_SIZE);
@@ -309,8 +309,9 @@ static int rockusb_add(struct usb_configuration *c)
 
 	status = usb_add_function(c, &f_rkusb->usb_function);
 	if (status) {
+		free(f_rkusb->buf_head);
 		free(f_rkusb);
-		rockusb_func = f_rkusb;
+		rockusb_func = NULL;
 	}
 	return status;
 }

+ 7 - 4
drivers/usb/gadget/f_thor.c

@@ -30,6 +30,7 @@
 #include <linux/usb/cdc.h>
 #include <g_dnl.h>
 #include <dfu.h>
+#include <thor.h>
 
 #include "f_thor.h"
 
@@ -266,8 +267,8 @@ static long long int process_rqt_download(const struct rqt_box *rqt)
 
 	switch (rqt->rqt_data) {
 	case RQT_DL_INIT:
-		thor_file_size = (unsigned long long int)rqt->int_data[0] +
-				 (((unsigned long long int)rqt->int_data[1])
+		thor_file_size = (uint64_t)(uint32_t)rqt->int_data[0] +
+				 (((uint64_t)(uint32_t)rqt->int_data[1])
 				  << 32);
 		debug("INIT: total %llu bytes\n", thor_file_size);
 		break;
@@ -280,8 +281,8 @@ static long long int process_rqt_download(const struct rqt_box *rqt)
 			break;
 		}
 
-		thor_file_size = (unsigned long long int)rqt->int_data[1] +
-				 (((unsigned long long int)rqt->int_data[2])
+		thor_file_size = (uint64_t)(uint32_t)rqt->int_data[1] +
+				 (((uint64_t)(uint32_t)rqt->int_data[2])
 				  << 32);
 		memcpy(f_name, rqt->str_data[0], F_NAME_BUF_SIZE);
 		f_name[F_NAME_BUF_SIZE] = '\0';
@@ -735,6 +736,8 @@ int thor_handle(void)
 			printf("%s: No data received!\n", __func__);
 			break;
 		}
+		if (dfu_reinit_needed)
+			return THOR_DFU_REINIT_NEEDED;
 	}
 
 	return 0;

+ 1 - 0
drivers/usb/gadget/g_dnl.c

@@ -286,6 +286,7 @@ static struct usb_composite_driver g_dnl_driver = {
 	.name = NULL,
 	.dev = &device_desc,
 	.strings = g_dnl_composite_strings,
+	.max_speed = USB_SPEED_SUPER,
 
 	.bind = g_dnl_bind,
 	.unbind = g_dnl_unbind,

+ 123 - 0
drivers/usb/gadget/u_os_desc.h

@@ -0,0 +1,123 @@
+/*
+ * u_os_desc.h
+ *
+ * Utility definitions for "OS Descriptors" support
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.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 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __U_OS_DESC_H__
+#define __U_OS_DESC_H__
+
+#include <linux/utf.h>
+
+#define USB_EXT_PROP_DW_SIZE			0
+#define USB_EXT_PROP_DW_PROPERTY_DATA_TYPE	4
+#define USB_EXT_PROP_W_PROPERTY_NAME_LENGTH	8
+#define USB_EXT_PROP_B_PROPERTY_NAME		10
+#define USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH	10
+#define USB_EXT_PROP_B_PROPERTY_DATA		14
+
+#define USB_EXT_PROP_RESERVED			0
+#define USB_EXT_PROP_UNICODE			1
+#define USB_EXT_PROP_UNICODE_ENV		2
+#define USB_EXT_PROP_BINARY			3
+#define USB_EXT_PROP_LE32			4
+#define USB_EXT_PROP_BE32			5
+#define USB_EXT_PROP_UNICODE_LINK		6
+#define USB_EXT_PROP_UNICODE_MULTI		7
+
+static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset)
+{
+	return buf + offset;
+}
+
+static inline u8 *usb_ext_prop_size_ptr(u8 *buf)
+{
+	return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE);
+}
+
+static inline u8 *usb_ext_prop_type_ptr(u8 *buf)
+{
+	return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE);
+}
+
+static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf)
+{
+	return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH);
+}
+
+static inline u8 *usb_ext_prop_name_ptr(u8 *buf)
+{
+	return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME);
+}
+
+static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off)
+{
+	return __usb_ext_prop_ptr(buf,
+				  USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off);
+}
+
+static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off)
+{
+	return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off);
+}
+
+static inline void usb_ext_prop_put_size(u8 *buf, int dw_size)
+{
+	put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf));
+}
+
+static inline void usb_ext_prop_put_type(u8 *buf, int type)
+{
+	put_unaligned_le32(type, usb_ext_prop_type_ptr(buf));
+}
+
+static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl)
+{
+	int result;
+
+	put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf));
+	memset(usb_ext_prop_name_ptr(buf), 0, 2 * strlen(name));
+	result = utf8_to_utf16le(name, (__le16 *)usb_ext_prop_name_ptr(buf),
+				 strlen(name));
+	if (result < 0)
+		return result;
+
+	put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]);
+
+	return pnl;
+}
+
+static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const char *data,
+					   int data_len)
+{
+	put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
+	memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len);
+}
+
+static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string,
+					   int data_len)
+{
+	int result;
+	put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
+	memset(usb_ext_prop_data_ptr(buf, pnl), 0, 2 * (data_len >> 1));
+	result = utf8_to_utf16le(string, (__le16 *) usb_ext_prop_data_ptr(buf, pnl),
+				 data_len >> 1);
+	if (result < 0)
+		return result;
+
+	put_unaligned_le16(0,
+		&buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]);
+
+	return data_len;
+}
+
+#endif /* __U_OS_DESC_H__ */

+ 1 - 73
drivers/usb/gadget/usbstring.c

@@ -10,79 +10,7 @@
 #include <linux/errno.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
-
-#include <asm/unaligned.h>
-
-
-static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
-{
-	int	count = 0;
-	u8	c;
-	u16	uchar;
-
-	/*
-	 * this insists on correct encodings, though not minimal ones.
-	 * BUT it currently rejects legit 4-byte UTF-8 code points,
-	 * which need surrogate pairs.  (Unicode 3.1 can use them.)
-	 */
-	while (len != 0 && (c = (u8) *s++) != 0) {
-		if ((c & 0x80)) {
-			/*
-			 * 2-byte sequence:
-			 * 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
-			 */
-			if ((c & 0xe0) == 0xc0) {
-				uchar = (c & 0x1f) << 6;
-
-				c = (u8) *s++;
-				if ((c & 0xc0) != 0x80)
-					goto fail;
-				c &= 0x3f;
-				uchar |= c;
-
-			/*
-			 * 3-byte sequence (most CJKV characters):
-			 * zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
-			 */
-			} else if ((c & 0xf0) == 0xe0) {
-				uchar = (c & 0x0f) << 12;
-
-				c = (u8) *s++;
-				if ((c & 0xc0) != 0x80)
-					goto fail;
-				c &= 0x3f;
-				uchar |= c << 6;
-
-				c = (u8) *s++;
-				if ((c & 0xc0) != 0x80)
-					goto fail;
-				c &= 0x3f;
-				uchar |= c;
-
-				/* no bogus surrogates */
-				if (0xd800 <= uchar && uchar <= 0xdfff)
-					goto fail;
-
-			/*
-			 * 4-byte sequence (surrogate pairs, currently rare):
-			 * 11101110wwwwzzzzyy + 110111yyyyxxxxxx
-			 *     = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
-			 * (uuuuu = wwww + 1)
-			 * FIXME accept the surrogate code points (only)
-			 */
-			} else
-				goto fail;
-		} else
-			uchar = c;
-		put_unaligned_le16(uchar, cp++);
-		count++;
-		len--;
-	}
-	return count;
-fail:
-	return -1;
-}
-
+#include <linux/utf.h>
 
 /**
  * usb_gadget_get_string - fill out a string descriptor

+ 21 - 0
include/bcb.h

@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Eugeniu Rosca <rosca.eugeniu@gmail.com>
+ *
+ * Android Bootloader Control Block Header
+ */
+
+#ifndef __BCB_H__
+#define __BCB_H__
+
+#if CONFIG_IS_ENABLED(CMD_BCB)
+int bcb_write_reboot_reason(int devnum, char *partp, const char *reasonp);
+#else
+#include <linux/errno.h>
+static inline int bcb_write_reboot_reason(int devnum, char *partp, const char *reasonp)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
+#endif /* __BCB_H__ */

+ 4 - 0
include/dfu.h

@@ -33,6 +33,8 @@ enum dfu_layout {
 	DFU_FS_EXT3,
 	DFU_FS_EXT4,
 	DFU_RAM_ADDR,
+	DFU_SKIP,
+	DFU_SCRIPT,
 };
 
 enum dfu_op {
@@ -496,6 +498,8 @@ static inline int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr,
 }
 #endif
 
+extern bool dfu_reinit_needed;
+
 #if CONFIG_IS_ENABLED(DFU_WRITE_ALT)
 /**
  * dfu_write_by_name() - write data to DFU medium

+ 71 - 0
include/linux/usb/composite.h

@@ -37,6 +37,53 @@
 
 struct usb_configuration;
 
+/**
+ * struct usb_os_desc_ext_prop - describes one "Extended Property"
+ * @entry: used to keep a list of extended properties
+ * @type: Extended Property type
+ * @name_len: Extended Property unicode name length, including terminating '\0'
+ * @name: Extended Property name
+ * @data_len: Length of Extended Property blob (for unicode store double len)
+ * @data: Extended Property blob
+ */
+struct usb_os_desc_ext_prop {
+	struct list_head	entry;
+	u8			type;
+	int			name_len;
+	char			*name;
+	int			data_len;
+	char			*data;
+};
+
+/**
+ * struct usb_os_desc - describes OS descriptors associated with one interface
+ * @ext_compat_id: 16 bytes of "Compatible ID" and "Subcompatible ID"
+ * @ext_prop: Extended Properties list
+ * @ext_prop_len: Total length of Extended Properties blobs
+ * @ext_prop_count: Number of Extended Properties
+ */
+struct usb_os_desc {
+	char			*ext_compat_id;
+	struct list_head	ext_prop;
+	int			ext_prop_len;
+	int			ext_prop_count;
+};
+
+/**
+ * struct usb_os_desc_table - describes OS descriptors associated with one
+ * interface of a usb_function
+ * @if_id: Interface id
+ * @os_desc: "Extended Compatibility ID" and "Extended Properties" of the
+ *	interface
+ *
+ * Each interface can have at most one "Extended Compatibility ID" and a
+ * number of "Extended Properties".
+ */
+struct usb_os_desc_table {
+	int			if_id;
+	struct usb_os_desc	*os_desc;
+};
+
 /**
  * struct usb_function - describes one function of a configuration
  * @name: For diagnostics, identifies the function.
@@ -50,6 +97,10 @@ struct usb_configuration;
  *	the function will not be available at high speed.
  * @config: assigned when @usb_add_function() is called; this is the
  *	configuration with which this function is associated.
+ * @os_desc_table: Table of (interface id, os descriptors) pairs. The function
+ *	can expose more than one interface. If an interface is a member of
+ *	an IAD, only the first interface of IAD has its entry in the table.
+ * @os_desc_n: Number of entries in os_desc_table
  * @bind: Before the gadget can register, all of its functions bind() to the
  *	available resources including string and interface identifiers used
  *	in interface or class descriptors; endpoints; I/O buffers; and so on.
@@ -95,9 +146,13 @@ struct usb_function {
 	struct usb_gadget_strings	**strings;
 	struct usb_descriptor_header	**descriptors;
 	struct usb_descriptor_header	**hs_descriptors;
+	struct usb_descriptor_header    **ss_descriptors;
 
 	struct usb_configuration	*config;
 
+	struct usb_os_desc_table	*os_desc_table;
+	unsigned			os_desc_n;
+
 	/* REVISIT:  bind() functions can be marked __init, which
 	 * makes trouble for section mismatch analysis.  See if
 	 * we can't restructure things to avoid mismatching.
@@ -225,6 +280,7 @@ struct usb_configuration {
 	u8			next_interface_id;
 	unsigned		highspeed:1;
 	unsigned		fullspeed:1;
+	unsigned		superspeed:1;
 	struct usb_function	*interface[MAX_CONFIG_INTERFACES];
 };
 
@@ -238,6 +294,7 @@ int usb_add_config(struct usb_composite_dev *,
  *	identifiers.
  * @strings: tables of strings, keyed by identifiers assigned during bind()
  *	and language IDs provided in control requests
+ * @max_speed: Highest speed the driver supports.
  * @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
@@ -265,6 +322,7 @@ struct usb_composite_driver {
 	const char				*name;
 	const struct usb_device_descriptor	*dev;
 	struct usb_gadget_strings		**strings;
+	enum usb_device_speed			max_speed;
 
 	/* REVISIT:  bind() functions can be marked __init, which
 	 * makes trouble for section mismatch analysis.  See if
@@ -284,13 +342,20 @@ struct usb_composite_driver {
 extern int usb_composite_register(struct usb_composite_driver *);
 extern void usb_composite_unregister(struct usb_composite_driver *);
 
+#define OS_STRING_QW_SIGN_LEN		14
+#define OS_STRING_IDX			0xEE
 
 /**
  * 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
+ * @os_desc_req: used for OS descriptors responses; buffer is pre-allocated
  * @config: the currently active configuration
+ * @qw_sign: qwSignature part of the OS string
+ * @b_vendor_code: bMS_VendorCode part of the OS string
+ * @use_os_string: false by default, interested gadgets set it
+ * @os_desc_config: the configuration to be used with OS descriptors
  *
  * One of these devices is allocated and initialized before the
  * associated device driver's bind() is called.
@@ -324,6 +389,12 @@ struct usb_composite_dev {
 
 	struct usb_configuration	*config;
 
+	/* OS String is a custom (yet popular) extension to the USB standard. */
+	u8				qw_sign[OS_STRING_QW_SIGN_LEN];
+	u8				b_vendor_code;
+	struct usb_configuration	*os_desc_config;
+	unsigned int			use_os_string:1;
+
 	/* private: */
 	/* internals */
 	unsigned int			suspended:1;

+ 9 - 0
include/linux/usb/gadget.h

@@ -449,6 +449,11 @@ static inline void usb_ep_fifo_flush(struct usb_ep *ep)
 
 /*-------------------------------------------------------------------------*/
 
+struct usb_dcd_config_params {
+	__u8  bU1devExitLat;	/* U1 Device exit Latency */
+	__le16 bU2DevExitLat;	/* U2 Device exit Latency */
+};
+
 struct usb_gadget;
 struct usb_gadget_driver;
 
@@ -464,12 +469,16 @@ 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_ep *(*match_ep)(struct usb_gadget *,
 			struct usb_endpoint_descriptor *,
 			struct usb_ss_ep_comp_descriptor *);
+	int   (*ep_conf)(struct usb_gadget *,
+			struct usb_ep *,
+			struct usb_endpoint_descriptor *);
 	void	(*udc_set_speed)(struct usb_gadget *gadget,
 				 enum usb_device_speed);
 };

+ 75 - 0
include/linux/utf.h

@@ -0,0 +1,75 @@
+#ifndef _LINUX_UTF_H
+#define _LINUX_UTF_H
+
+#include <asm/unaligned.h>
+
+static inline int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
+{
+	int	count = 0;
+	u8	c;
+	u16	uchar;
+
+	/*
+	 * this insists on correct encodings, though not minimal ones.
+	 * BUT it currently rejects legit 4-byte UTF-8 code points,
+	 * which need surrogate pairs.  (Unicode 3.1 can use them.)
+	 */
+	while (len != 0 && (c = (u8) *s++) != 0) {
+		if ((c & 0x80)) {
+			/*
+			 * 2-byte sequence:
+			 * 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
+			 */
+			if ((c & 0xe0) == 0xc0) {
+				uchar = (c & 0x1f) << 6;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0x80)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c;
+
+			/*
+			 * 3-byte sequence (most CJKV characters):
+			 * zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
+			 */
+			} else if ((c & 0xf0) == 0xe0) {
+				uchar = (c & 0x0f) << 12;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0x80)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c << 6;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0x80)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c;
+
+				/* no bogus surrogates */
+				if (0xd800 <= uchar && uchar <= 0xdfff)
+					goto fail;
+
+			/*
+			 * 4-byte sequence (surrogate pairs, currently rare):
+			 * 11101110wwwwzzzzyy + 110111yyyyxxxxxx
+			 *     = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
+			 * (uuuuu = wwww + 1)
+			 * FIXME accept the surrogate code points (only)
+			 */
+			} else
+				goto fail;
+		} else
+			uchar = c;
+		put_unaligned_le16(uchar, cp++);
+		count++;
+		len--;
+	}
+	return count;
+fail:
+	return -1;
+}
+
+#endif /* _LINUX_UTF_H */

+ 2 - 0
include/thor.h

@@ -12,6 +12,8 @@
 
 #include <linux/usb/composite.h>
 
+#define THOR_DFU_REINIT_NEEDED	0xFFFFFFFE
+
 int thor_handle(void);
 int thor_init(void);
 int thor_add(struct usb_configuration *c);

+ 0 - 15
include/usbdevice.h

@@ -264,8 +264,6 @@ struct usb_bus_instance;
 #define USB_REQ_SET_INTERFACE		0x0B
 #define USB_REQ_SYNCH_FRAME		0x0C
 
-#define USBD_DEVICE_REQUESTS(x) (((unsigned int)x <= USB_REQ_SYNCH_FRAME) ? usbd_device_requests[x] : "UNKNOWN")
-
 /*
  * HID requests
  */
@@ -332,9 +330,6 @@ struct usb_bus_instance;
 #define USB_DESCRIPTOR_TYPE_HID				0x21
 #define USB_DESCRIPTOR_TYPE_REPORT			0x22
 
-#define USBD_DEVICE_DESCRIPTORS(x) (((unsigned int)x <= USB_DESCRIPTOR_TYPE_INTERFACE_POWER) ? \
-		usbd_device_descriptors[x] : "UNKNOWN")
-
 /*
  * standard feature selectors
  */
@@ -388,8 +383,6 @@ typedef enum usb_device_state {
 	STATE_UNKNOWN,		/* destroyed */
 } usb_device_state_t;
 
-#define USBD_DEVICE_STATE(x) (((unsigned int)x <= STATE_UNKNOWN) ? usbd_device_states[x] : "UNKNOWN")
-
 /*
  * Device status
  *
@@ -402,8 +395,6 @@ typedef enum usb_device_status {
 	USBD_CLOSING,		/* we are currently closing */
 } usb_device_status_t;
 
-#define USBD_DEVICE_STATUS(x) (((unsigned int)x <= USBD_CLOSING) ? usbd_device_status[x] : "UNKNOWN")
-
 /*
  * Device Events
  *
@@ -617,12 +608,6 @@ struct usb_bus_instance {
 
 };
 
-extern char *usbd_device_events[];
-extern char *usbd_device_states[];
-extern char *usbd_device_status[];
-extern char *usbd_device_requests[];
-extern char *usbd_device_descriptors[];
-
 void urb_link_init (urb_link * ul);
 void urb_detach (struct urb *urb);
 urb_link *first_urb_link (urb_link * hd);