From 1b3c4cb1e392bf9365ac633d305c6a1aac9b94e1 Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Wed, 2 Sep 2020 13:06:06 +0200 Subject: [PATCH 01/34] dfu: Fix handling of UBI partitions in MTD backend For UBI partitions ("partubi" in dfu_alt_info), dfu_fill_entity_mtd sets the mtd.ubi flag; however other functions incorrectly check for nand.ubi instead. Fix this by checking for the correct flag. Signed-off-by: Guillermo Rodriguez Cc: Lukasz Majewski Reviewed-by: Patrick Delaunay --- drivers/dfu/dfu_mtd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dfu/dfu_mtd.c b/drivers/dfu/dfu_mtd.c index b34975dbb0d..ca67585a7e4 100644 --- a/drivers/dfu/dfu_mtd.c +++ b/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; From 8b387f112bc96688d40281fc22531863eb812511 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Fri, 16 Oct 2020 16:29:02 +0900 Subject: [PATCH 02/34] gadget: f_thor: fix wrong file size cast Casting 32bit int value directly into 64bit unsigned type causes wrong value for file size equal or larger than 2GB. Fix the wrong file size by casting uint32_t first. Fixes: commit 1fe9ae76b113 ("gadget: f_thor: update to support more than 4GB file as thor 5.0") Reported-by: Junghoon Kim Signed-off-by: Seung-Woo Kim Reviewed-by: Jaehoon Chung --- drivers/usb/gadget/f_thor.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c index 88fc87f2e90..559ffb759ed 100644 --- a/drivers/usb/gadget/f_thor.c +++ b/drivers/usb/gadget/f_thor.c @@ -266,8 +266,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 +280,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'; From 24e85bbd6943a576f5a8451172f256f66471b0b2 Mon Sep 17 00:00:00 2001 From: Eugeniu Rosca Date: Fri, 23 Oct 2020 11:52:19 +0300 Subject: [PATCH 03/34] cmd: bcb: Extract '__bcb_load' from 'do_bcb_load' for internal needs Enriching the functionality of U-Boot 'bcb' may assume using the existing sub-commands as building blocks for the next ones. A clean way to achive the above is to expose a number of static routines, each mapped to an existing user command (e.g. load/set/store), with a user/caller-friendly prototype (i.e. do not force the caller to wrap an integer into a string). This first patch makes '__bcb_load' available for internal needs. No functional change, except for a tiny update in error handling. Signed-off-by: Eugeniu Rosca Signed-off-by: Roman Kovalivskyi --- cmd/bcb.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/cmd/bcb.c b/cmd/bcb.c index e03218066bf..2ed8b801a3e 100644 --- a/cmd/bcb.c +++ b/cmd/bcb.c @@ -110,8 +110,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 +118,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 +152,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,6 +164,20 @@ err: return CMD_RET_FAILURE; } +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 do_bcb_set(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { From 02ebe6deae994c377dedafa7df4abd70fd2e3f44 Mon Sep 17 00:00:00 2001 From: Eugeniu Rosca Date: Fri, 23 Oct 2020 11:52:20 +0300 Subject: [PATCH 04/34] cmd: bcb: Extract '__bcb_set' from 'do_bcb_set' for internal needs Enriching the functionality of U-Boot 'bcb' may assume using the existing sub-commands as building blocks for the next ones. A clean way to achive the above is to expose a number of static routines, each mapped to an existing user command (e.g. load/set/store), with a user/caller-friendly prototype (i.e. do not force the caller to wrap an integer into a string). This second patch makes '__bcb_set' available for internal needs. No functional change intended. Signed-off-by: Eugeniu Rosca Signed-off-by: Roman Kovalivskyi --- cmd/bcb.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/cmd/bcb.c b/cmd/bcb.c index 2ed8b801a3e..113f04ffe6b 100644 --- a/cmd/bcb.c +++ b/cmd/bcb.c @@ -178,22 +178,21 @@ static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc, return __bcb_load(devnum, argv[2]); } -static int do_bcb_set(struct cmd_tbl *cmdtp, int flag, int argc, - char *const argv[]) +static int __bcb_set(char *fieldp, char *valp) { int size, len; char *field, *str, *found; - 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 = argv[2]; + str = valp; field[0] = '\0'; while ((found = strsep(&str, ":"))) { @@ -205,6 +204,12 @@ static int do_bcb_set(struct cmd_tbl *cmdtp, int flag, int argc, 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[]) { From e2ffb3be4986e9ecbfb6ec22e8458ef16873912d Mon Sep 17 00:00:00 2001 From: Eugeniu Rosca Date: Fri, 23 Oct 2020 11:52:21 +0300 Subject: [PATCH 05/34] cmd: bcb: Extract '__bcb_store' from 'do_bcb_store' for internal needs Enriching the functionality of U-Boot 'bcb' may assume using the existing sub-commands as building blocks for the next ones. A clean way to achive the above is to expose a number of static routines, each mapped to an existing user command (e.g. load/set/store), with a user/caller-friendly prototype (i.e. do not force the caller to wrap an integer into a string). This third patch makes '__bcb_store' available for internal needs. No functional change intended. Signed-off-by: Eugeniu Rosca Signed-off-by: Roman Kovalivskyi --- cmd/bcb.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/bcb.c b/cmd/bcb.c index 113f04ffe6b..b9cd20ea3d5 100644 --- a/cmd/bcb.c +++ b/cmd/bcb.c @@ -270,8 +270,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; @@ -302,6 +301,12 @@ err: return CMD_RET_FAILURE; } +static int do_bcb_store(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + return __bcb_store(); +} + 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, "", ""), From e74670adfe6425b94b8615b74b8aadc5425fb246 Mon Sep 17 00:00:00 2001 From: Eugeniu Rosca Date: Fri, 23 Oct 2020 11:52:22 +0300 Subject: [PATCH 06/34] cmd: bcb: Expose 'bcb_write_reboot_reason' to external callers Fastboot is evolving and beginning with commit [1], the upstream implementation expects bootloaders to offer support for: - reboot-recovery - reboot-fastboot The most natural way to achieve the above is through a set of pre-defined "reboot reason" strings, written into / read from the BCB "command" field, e.g.: - bootonce-bootloader [2] - boot-fastboot [3] - boot-recovery [4] Expose the first 'bcb' API meant to be called by e.g. fastboot stack, to allow updating the BCB reboot reason via the BCB 'command' field. [1] https://android.googlesource.com/platform/system/core/+/dea91b4b5354af2 ("Add fastbootd.") [2] https://android.googlesource.com/platform/bootable/recovery/+/cba7fa88d8b9 ("Add 'reboot bootloader' to bootloader_message.") [3] https://android.googlesource.com/platform/bootable/recovery/+/eee4e260f9f6 ("recovery: Add "boot-fastboot" command to BCB.") [4] https://android.googlesource.com/platform/system/core/+/5e98b633a748695f ("init: Write the reason in BCB on "reboot recovery"") Signed-off-by: Eugeniu Rosca Signed-off-by: Roman Kovalivskyi [lukma - added missing #include to avoid build breaks] --- cmd/bcb.c | 20 ++++++++++++++++++++ include/bcb.h | 21 +++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 include/bcb.h diff --git a/cmd/bcb.c b/cmd/bcb.c index b9cd20ea3d5..5da3526142a 100644 --- a/cmd/bcb.c +++ b/cmd/bcb.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -307,6 +308,25 @@ static int do_bcb_store(struct cmd_tbl *cmdtp, int flag, int argc, return __bcb_store(); } +int bcb_write_reboot_reason(int devnum, char *partp, 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, "", ""), diff --git a/include/bcb.h b/include/bcb.h new file mode 100644 index 00000000000..5bb8d8346c1 --- /dev/null +++ b/include/bcb.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Eugeniu Rosca + * + * 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, char *reasonp); +#else +#include +static inline int bcb_write_reboot_reason(int devnum, char *partp, char *reasonp) +{ + return -EOPNOTSUPP; +} +#endif + +#endif /* __BCB_H__ */ From bafdf4caac15bd14c4926c8935e1a22e297fb0e3 Mon Sep 17 00:00:00 2001 From: Eugeniu Rosca Date: Fri, 23 Oct 2020 11:52:23 +0300 Subject: [PATCH 07/34] cmd: bcb: Add support for processing const string literals in bcb_set() On request/suggestion from Simon Glass back in May 22 2019 [1], the 'strsep' mechanism implemented in bcb_set() was set to work directly with user-provided argv strings, to avoid duplicating memory and for the sake of simpler implementation. However, since we recently exposed bcb_write_reboot_reason() API to be called by U-Boot fastboot, the idea is to be able to pass const string literals to this new BCB API, carrying the reboot reason. Since 'strsep' (just like its older/superseded sibling 'strtok') modifies the input string passed as parameter, BCB command in its current state would attempt to perform in-place modifications in a readonly string, which might lead to unexpected results. Fix the above with the cost of one dynamic memory allocation ('strdup'). This will also ensure no compiler warnings when passing string literals to bcb_write_reboot_reason(). [1] http://u-boot.10912.n7.nabble.com/PATCH-v2-0-2-Add-bcb-command-to-read-modify-write-Android-BCB-td369934i20.html#a370456 Cc: Simon Glass Signed-off-by: Eugeniu Rosca Signed-off-by: Roman Kovalivskyi --- cmd/bcb.c | 17 ++++++++++++----- include/bcb.h | 4 ++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/cmd/bcb.c b/cmd/bcb.c index 5da3526142a..6b6f1e9a2f1 100644 --- a/cmd/bcb.c +++ b/cmd/bcb.c @@ -11,6 +11,7 @@ #include #include #include +#include enum bcb_cmd { BCB_CMD_LOAD, @@ -179,10 +180,10 @@ static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc, return __bcb_load(devnum, argv[2]); } -static int __bcb_set(char *fieldp, char *valp) +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(fieldp, &field, &size)) return CMD_RET_FAILURE; @@ -193,14 +194,20 @@ static int __bcb_set(char *fieldp, char *valp) valp, len, size, fieldp); return CMD_RET_FAILURE; } - str = valp; + str = strdup(valp); + if (!str) { + printf("Error: Out of memory while strdup\n"); + return CMD_RET_FAILURE; + } + 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; } @@ -308,7 +315,7 @@ static int do_bcb_store(struct cmd_tbl *cmdtp, int flag, int argc, return __bcb_store(); } -int bcb_write_reboot_reason(int devnum, char *partp, char *reasonp) +int bcb_write_reboot_reason(int devnum, char *partp, const char *reasonp) { int ret; diff --git a/include/bcb.h b/include/bcb.h index 5bb8d8346c1..897e83d371f 100644 --- a/include/bcb.h +++ b/include/bcb.h @@ -9,10 +9,10 @@ #define __BCB_H__ #if CONFIG_IS_ENABLED(CMD_BCB) -int bcb_write_reboot_reason(int devnum, char *partp, char *reasonp); +int bcb_write_reboot_reason(int devnum, char *partp, const char *reasonp); #else #include -static inline int bcb_write_reboot_reason(int devnum, char *partp, char *reasonp) +static inline int bcb_write_reboot_reason(int devnum, char *partp, const char *reasonp) { return -EOPNOTSUPP; } From 0234609d9631652e2faf77e23f332be29570d7d2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 3 Dec 2020 17:32:03 +0200 Subject: [PATCH 08/34] f_rockusb: Use NULL instead of 0 for pointers get_rkusb() mistakenly uses integers without cast. Convert them to proper type. Signed-off-by: Andy Shevchenko --- drivers/usb/gadget/f_rockusb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/f_rockusb.c b/drivers/usb/gadget/f_rockusb.c index 9ae02ae78c1..9dd10f9e9aa 100644 --- a/drivers/usb/gadget/f_rockusb.c +++ b/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); From ff52577c1b5188a5f25bd7d7d41730bb5de4b688 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 3 Dec 2020 17:32:04 +0200 Subject: [PATCH 09/34] f_rockusb: Avoid use-after-free in the global pointer variable In case of usb_add_function() failure the error path has two issues: - the potentially allocated structure isn't getting freed - the global pointer variable is assigned to garbage Fix the above mentioned issues by freeing memory and assigning NULL. Signed-off-by: Andy Shevchenko --- drivers/usb/gadget/f_rockusb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_rockusb.c b/drivers/usb/gadget/f_rockusb.c index 9dd10f9e9aa..bd846ce9a77 100644 --- a/drivers/usb/gadget/f_rockusb.c +++ b/drivers/usb/gadget/f_rockusb.c @@ -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; } From 6ffc8e29934391e1569066ca1a1638d261839fa7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 3 Dec 2020 17:32:05 +0200 Subject: [PATCH 10/34] f_fastboot: Avoid use-after-free in the global pointer variable In case of usb_add_function() failure the error path has an issue, i.e the global pointer variable is assigned to garbage Fix the above mentioned issue by assigning pointer to NULL. Signed-off-by: Andy Shevchenko --- drivers/usb/gadget/f_fastboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index d1d087e12b2..d0d865cf3d0 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -315,7 +315,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; From c954ff5fbffbb164f1a34127eae78e7fd86180d0 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 17 Dec 2020 11:47:11 +0100 Subject: [PATCH 11/34] cmd: usb_mass_storage: show device interface name Show the interface name (i.e. 'mmc') in the information string to ease user checking which device is exported via USB Mass Storage protocol. Signed-off-by: Marek Szyprowski Reviewed-by: Lukasz Majewski --- cmd/usb_mass_storage.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/usb_mass_storage.c b/cmd/usb_mass_storage.c index cf2f55994e0..14fa7233c71 100644 --- a/cmd/usb_mass_storage.c +++ b/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); From f21358ad6cb5d39fff432fa0af0c4fd24288b2b7 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 22 Dec 2020 11:32:20 +0100 Subject: [PATCH 12/34] dfu: mmc: use the default MMC device if entity specifies it as -1 Use the default MMC device set in the command line if entity specifies it as -1. This allows to use the same dfu_alt_info string for different MMC devices (like embedded eMMC and external SD card if data layout is the same on both devices). Signed-off-by: Marek Szyprowski --- drivers/dfu/dfu_mmc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index 691d01c7ebd..784d0ec76b2 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -316,7 +316,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 +339,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); @@ -406,7 +406,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; } From b5f3405b9d8666bd0ab627c298cea1c927da081a Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Tue, 22 Dec 2020 11:32:21 +0100 Subject: [PATCH 13/34] dfu: add 'SKIP' entity Define a new 'SKIP' type for the DFU entities. The flashed data for that entity is simply ignored without returning any error values. This allows to have one flashing procedure and images for the different board types or variants, where each board uses only the images relevant to it and skips the rest. This is especially usefull for the THOR protocol, which usually transfers more than one file in a single session. Signed-off-by: Jaehoon Chung Reviewed-by: Minkyu Kang [mszyprow: rephrased commit message and docs for easier reading, changed subject to "dfu: add 'SKIP' entity"] Signed-off-by: Marek Szyprowski --- doc/README.dfu | 15 ++++++++++++++- drivers/dfu/dfu.c | 2 +- drivers/dfu/dfu_mmc.c | 9 +++++++++ include/dfu.h | 1 + 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/doc/README.dfu b/doc/README.dfu index be53b5b393c..6cb1cba9d79 100644 --- a/doc/README.dfu +++ b/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) - NAND - RAM - SF (serial flash) @@ -91,6 +91,7 @@ Commands: part [mmcpart ] raw access to partition fat [mmcpart ] file in FAT partition ext4 [mmcpart ] file in EXT4 partition + skip 0 0 ignore flashed data with being the GPT or DOS partition index, with being the eMMC hardware partition number. @@ -103,6 +104,18 @@ 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-.bin and u-boot-.bin files, one + can use it to flash a proper u-boot image on both without a failure: + + "u-boot-.bin raw 0x80 0x800; u-boot-.bin skip 0 0" + "nand" (raw slc nand device) cmd: dfu 0 nand each element in "dfu_alt_info" = diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 501a60b3440..fc32a533234 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -614,7 +614,7 @@ 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" }; return dfu_layout[l]; } diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index 784d0ec76b2..d1af11d94c9 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -108,6 +108,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 +206,9 @@ 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_SKIP: + ret = 0; + break; default: printf("%s: Layout (%s) not (yet) supported!\n", __func__, dfu_get_layout(dfu->layout)); @@ -238,6 +243,8 @@ int dfu_get_medium_size_mmc(struct dfu_entity *dfu, u64 *size) if (ret < 0) return ret; return 0; + case DFU_SKIP: + return 0; default: printf("%s: Layout (%s) not (yet) supported!\n", __func__, dfu_get_layout(dfu->layout)); @@ -399,6 +406,8 @@ 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 { pr_err("Memory layout (%s) not supported!\n", entity_type); return -ENODEV; diff --git a/include/dfu.h b/include/dfu.h index a767adee410..0b1dae0b3ba 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -33,6 +33,7 @@ enum dfu_layout { DFU_FS_EXT3, DFU_FS_EXT4, DFU_RAM_ADDR, + DFU_SKIP, }; enum dfu_op { From c533f94c102127b94abda206dbe41fe026bcde76 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 22 Dec 2020 11:32:22 +0100 Subject: [PATCH 14/34] dfu: add 'SCRIPT' entity Define a new 'SCRIPT' type for DFU entities. The downloaded data are treated as simple u-boot's scripts and executed with run_command_list() function. Flashing the 'SCRIPT' entity might result in changing the 'dfu_alt_info' environment variable from the flashed script, so add a global variable for tracking the potential need to reinitialize the dfu_alt_info related structures. Signed-off-by: Marek Szyprowski --- doc/README.dfu | 17 ++++++++++++++++- drivers/dfu/dfu.c | 7 ++++++- drivers/dfu/dfu_mmc.c | 23 +++++++++++++++++++++-- include/dfu.h | 3 +++ 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/doc/README.dfu b/doc/README.dfu index 6cb1cba9d79..eacd5bbfb41 100644 --- a/doc/README.dfu +++ b/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 / SKIP) + - MMC (RAW or FAT / EXT2 / EXT3 / EXT4 file system / SKIP / SCRIPT) - NAND - RAM - SF (serial flash) @@ -92,6 +92,7 @@ Commands: fat [mmcpart ] file in FAT partition ext4 [mmcpart ] file in EXT4 partition skip 0 0 ignore flashed data + script 0 0 execute commands in shell with being the GPT or DOS partition index, with being the eMMC hardware partition number. @@ -116,6 +117,20 @@ Commands: "u-boot-.bin raw 0x80 0x800; u-boot-.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 each element in "dfu_alt_info" = diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index fc32a533234..213a20e7bcb 100644 --- a/drivers/dfu/dfu.c +++ b/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", "SKIP" }; + "EXT3", "EXT4", "RAM_ADDR", "SKIP", + "SCRIPT" }; return dfu_layout[l]; } diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index d1af11d94c9..e63fa84ce4f 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -16,6 +16,7 @@ #include #include #include +#include static unsigned char *dfu_file_buf; static u64 dfu_file_buf_len; @@ -206,6 +207,9 @@ 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; @@ -221,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; @@ -243,6 +259,7 @@ 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: @@ -408,6 +425,8 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s) 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; diff --git a/include/dfu.h b/include/dfu.h index 0b1dae0b3ba..d18b7017282 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -34,6 +34,7 @@ enum dfu_layout { DFU_FS_EXT4, DFU_RAM_ADDR, DFU_SKIP, + DFU_SCRIPT, }; enum dfu_op { @@ -497,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 From 9129f2f16488e95f8a71bb87937397aaf333bc1b Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 22 Dec 2020 11:32:23 +0100 Subject: [PATCH 15/34] dfu: add support for the dfu_alt_info reintialization from the flashed script Reinitialize DFU USB gadget after flashing the 'SCRIPT' entity to ensure that the potential changes to the 'dfu_alt_info' environment variable are applied. Signed-off-by: Marek Szyprowski [lukma - I've moved the bool retry to avoid build (CI) errors] --- cmd/dfu.c | 13 +++++++++++-- common/dfu.c | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cmd/dfu.c b/cmd/dfu.c index 7310595a027..ef4f897ce07 100644 --- a/cmd/dfu.c +++ b/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(); diff --git a/common/dfu.c b/common/dfu.c index d23cf67f194..16bd1ba588a 100644 --- a/common/dfu.c +++ b/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); } From e47431aa5cf0fe5e113ee9e33624084c93d4ec58 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 22 Dec 2020 11:32:24 +0100 Subject: [PATCH 16/34] thor: add support for the dfu_alt_info reintialization from the flashed script Reinitialize dfu_env_entities after flashing the 'SCRIPT' entity to ensure that the potential changes to the 'dfu_alt_info' environment variable are applied. Signed-off-by: Marek Szyprowski --- cmd/thordown.c | 19 ++++++++++++------- drivers/usb/gadget/f_thor.c | 3 +++ include/thor.h | 2 ++ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/cmd/thordown.c b/cmd/thordown.c index ae20dddfddb..838764ccef7 100644 --- a/cmd/thordown.c +++ b/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); diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c index 559ffb759ed..47ef55b2fd3 100644 --- a/drivers/usb/gadget/f_thor.c +++ b/drivers/usb/gadget/f_thor.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "f_thor.h" @@ -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; diff --git a/include/thor.h b/include/thor.h index 62501bda17c..ee67ab0a270 100644 --- a/include/thor.h +++ b/include/thor.h @@ -12,6 +12,8 @@ #include +#define THOR_DFU_REINIT_NEEDED 0xFFFFFFFE + int thor_handle(void); int thor_init(void); int thor_add(struct usb_configuration *c); From f2fa49988301965c4f8e7bfdf83145e677b1fbcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Sat, 26 Dec 2020 19:12:11 +0100 Subject: [PATCH 17/34] usb: gadget: Do not export usbd_device_* arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each array is used only in one file (core.c or ep0.c). Move their content to correct file, mark them as static and do not export out of current file. This change allows to decrease size of u-boot.bin as more of those strings are not used. Signed-off-by: Pali Rohár --- drivers/usb/gadget/core.c | 45 ++++---------------------------------- drivers/usb/gadget/ep0.c | 46 +++++++++++++++++++++++++++++++++++++++ include/usbdevice.h | 15 ------------- 3 files changed, 50 insertions(+), 56 deletions(-) diff --git a/drivers/usb/gadget/core.c b/drivers/usb/gadget/core.c index 3781d25fd6d..888f0cfea66 100644 --- a/drivers/usb/gadget/core.c +++ b/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 ************************************************************** */ diff --git a/drivers/usb/gadget/ep0.c b/drivers/usb/gadget/ep0.c index 6fabee24ce9..457679f0a4c 100644 --- a/drivers/usb/gadget/ep0.c +++ b/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 ********************************************************************* */ diff --git a/include/usbdevice.h b/include/usbdevice.h index f479724e37b..611cd6e4abf 100644 --- a/include/usbdevice.h +++ b/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); From 71a5732b43b9798d4c288f4dd118247ebdf88e9b Mon Sep 17 00:00:00 2001 From: Ye Li Date: Mon, 25 Jan 2021 21:43:44 +0800 Subject: [PATCH 18/34] usb: gadget: Add ep_config call back to usb_gadget_ops Since some new fields in usb_ep structure been moved to usb_ss_ep. The CDNS3 gadget driver should replies on this operation to bind the usb_ss_ep with the endpoint descriptor when function layer uses usb_ep_autoconfig to add endpoint descriptors to gadget. So that CDNS3 driver can know the EP information and configure the EP once the set configuration request is received. Signed-off-by: Sherry Sun Signed-off-by: Ye Li Signed-off-by: Peng Fan --- drivers/usb/gadget/epautoconf.c | 4 ++++ include/linux/usb/gadget.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index e61fe5d1144..3f8842976d9 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/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; } diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 06292ddeb62..8d54b91734c 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -470,6 +470,9 @@ struct usb_gadget_ops { 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); }; From c93edbf5385ecd3fd57179acdda813f7ab8cf201 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 25 Jan 2021 21:43:45 +0800 Subject: [PATCH 19/34] usb: gadget: don't change ep name for dwc3 while ep autoconfig As the SDP protocol use the predefined ep num for communication, we can't change its name hence reset its ep num while do ep autoconfig, this is only apply for SPL. Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/epautoconf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 3f8842976d9..7da334f5d31 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -262,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; /* @@ -284,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) From 1c7aacb9a88844858b4af45522685819c826d0d2 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 25 Jan 2021 21:43:46 +0800 Subject: [PATCH 20/34] usb: gadget: OS String support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a porting patch from linux kernel: 19824d5eeece ("usb: gadget: OS String support"), 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 They grant permission to use the specification - there is "Microsoft OS Descriptor Specification License Agreement" under the link mentioned above, and its Section 2 "Grant of License", letter (b) reads: "Patent license. Microsoft hereby grants to You a nonexclusive, royalty-free, nontransferable, worldwide license under Microsoft鈥檚 patents embodied solely within the Specification and that are owned or licensable by Microsoft to make, use, import, offer to sell, sell and distribute directly or indirectly to Your Licensees Your Implementation. You may sublicense this patent license to Your Licensees under the same terms and conditions." The said extension is maintained by Microsoft for Microsoft. Yet it is fairly common for various devices to use it, and a popular proprietary operating system expects devices to provide "OS descriptors", so Linux-based USB gadgets whishing to be able to talk to a variety of operating systems should be able to provide the "OS descriptors". This patch adds optional support for gadgets whishing to expose the so called "OS String" under index 0xEE of language 0. The contents of the string is generated based on the qw_sign array and b_vendor_code. Interested gadgets need to set the cdev->use_os_string flag, fill cdev->qw_sign with appropriate values and fill cdev->b_vendor_code with a value of their choice. This patch does not however implement responding to any vendor-specific USB requests." Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/composite.c | 26 ++++++++++++++++++++++++++ include/linux/usb/composite.h | 10 ++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 91ed7fcec5a..63855af52e9 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -25,6 +25,22 @@ 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 @@ -577,6 +593,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 diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index a49a66f2f82..d4f2a49869e 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -284,6 +284,8 @@ 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 @@ -291,6 +293,9 @@ extern void usb_composite_unregister(struct usb_composite_driver *); * @req: used for control responses; buffer is pre-allocated * @bufsiz: size of buffer pre-allocated in @req * @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 * * One of these devices is allocated and initialized before the * associated device driver's bind() is called. @@ -324,6 +329,11 @@ 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; + unsigned int use_os_string:1; + /* private: */ /* internals */ unsigned int suspended:1; From 6777483f5d1e9907a571aca55ec7d86bbce3040b Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 25 Jan 2021 21:43:47 +0800 Subject: [PATCH 21/34] usb: gadget: move utf8_to_utf16le to header file As other users may use utf8_to_utf16le() to convert the utf8 to utf16 for usb, so move it to head file. Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/usbstring.c | 74 +-------------------------------- include/linux/utf.h | 75 ++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 73 deletions(-) create mode 100644 include/linux/utf.h diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index 83cdd8a2595..e2464ad923f 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -10,79 +10,7 @@ #include #include #include - -#include - - -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 /** * usb_gadget_get_string - fill out a string descriptor diff --git a/include/linux/utf.h b/include/linux/utf.h new file mode 100644 index 00000000000..e1f7d3bd1d6 --- /dev/null +++ b/include/linux/utf.h @@ -0,0 +1,75 @@ +#ifndef _LINUX_UTF_H +#define _LINUX_UTF_H + +#include + +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 */ From 16fd832e0e2d06c1a76d9bef1f62e9d94c3fed98 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 25 Jan 2021 21:43:48 +0800 Subject: [PATCH 22/34] usb: gadget: add Kconfig for OS descriptors Add Kconfig for OS descriptors Signed-off-by: Peng Fan --- drivers/usb/gadget/Kconfig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 7c0df5c264d..4a3b22e6def 100644 --- a/drivers/usb/gadget/Kconfig +++ b/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 From a764c941289c404ae3a2605a204fc0375b566bb2 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 25 Jan 2021 21:43:49 +0800 Subject: [PATCH 23/34] usb: gadget: OS Feature Descriptors support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a proting 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 They grant permission to use the specification - there is "Microsoft OS Descriptor Specification License Agreement" under the link mentioned above, and its Section 2 "Grant of License", letter (b) reads: "Patent license. Microsoft hereby grants to You a nonexclusive, royalty-free, nontransferable, worldwide license under Microsoft鈥檚 patents embodied solely within the Specification and that are owned or licensable by Microsoft to make, use, import, offer to sell, sell and distribute directly or indirectly to Your Licensees Your Implementation. You may sublicense this patent license to Your Licensees under the same terms and conditions." The said extension is maintained by Microsoft for Microsoft. Yet it is fairly common for various devices to use it, and a popular proprietary operating system expects devices to provide "OS descriptors", so Linux-based USB gadgets whishing to be able to talk to a variety of operating systems should be able to provide the "OS descriptors". This patch adds optional support for gadgets whishing to expose the so called "OS Feature Descriptors", that is "Extended Compatibility ID" and "Extended Properties". Hosts which do request "OS descriptors" from gadgets do so during the enumeration phase and before the configuration is set with SET_CONFIGURATION. What is more, those hosts never ask for configurations at indices other than 0. Therefore, gadgets whishing to provide "OS descriptors" must designate one configuration to be used with this kind of hosts - this is what os_desc_config is added for in struct usb_composite_dev. There is an additional advantage to it: if a gadget provides "OS descriptors" and designates one configuration to be used with such non-USB-compliant hosts it can invoke "usb_add_config" in any order because the designated configuration will be reported to be at index 0 anyway. This patch also adds handling vendor-specific requests addressed at device or interface and related to handling "OS descriptors"." Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/composite.c | 252 ++++++++++++++++++++++++++++++++- drivers/usb/gadget/u_os_desc.h | 123 ++++++++++++++++ include/linux/usb/composite.h | 57 ++++++++ 3 files changed, 431 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/gadget/u_os_desc.h diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 63855af52e9..a0c28dbe598 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -12,6 +12,7 @@ #include #include #include +#include "u_os_desc.h" #define USB_BUFSIZ 4096 @@ -244,6 +245,7 @@ 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->speed == USB_SPEED_HIGH) @@ -255,7 +257,20 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) } w_value &= 0xff; - list_for_each_entry(c, &cdev->configs, list) { + + 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_HIGH) { if (!c->highspeed) continue; @@ -779,6 +794,156 @@ static int bos_desc(struct usb_composite_dev *cdev) 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 @@ -935,6 +1100,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); diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h new file mode 100644 index 00000000000..4dab4814a36 --- /dev/null +++ b/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 + * + * 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 + +#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__ */ diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index d4f2a49869e..d75a0bc4c49 100644 --- a/include/linux/usb/composite.h +++ b/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. @@ -98,6 +149,9 @@ struct usb_function { 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. @@ -292,10 +346,12 @@ extern void usb_composite_unregister(struct usb_composite_driver *); * @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. @@ -332,6 +388,7 @@ struct usb_composite_dev { /* 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: */ From 37e4fc3354dab8b4cbf835f83ed439753234ce12 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 25 Jan 2021 21:43:50 +0800 Subject: [PATCH 24/34] usb: gadget: add WCID support for mfgtool Enable WCID(Microsoft Compatible ID Feature Descriptor) for mfgtool. Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/composite.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index a0c28dbe598..cd61bfec387 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -20,6 +20,10 @@ 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) { @@ -395,6 +399,10 @@ static int set_config(struct usb_composite_dev *cdev, goto done; cdev->config = c; + if (cdev->use_os_string) { + cdev->os_desc_config = c; + os_desc_config = c; + } /* Initialize all interfaces by setting them to altsetting zero. */ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { @@ -1358,6 +1366,18 @@ 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); + + if (os_desc_config) + cdev->os_desc_config = os_desc_config; + } + debug("%s: ready\n", composite->name); return 0; From 8043cf8fbd93dfceeec14a8c00eda292deaca938 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 25 Jan 2021 21:43:51 +0800 Subject: [PATCH 25/34] usb: gadget: fastboot: add ext properties for WCID Add device interface GUID for Microsoft Extended Properties Feature Descriptor. Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/f_fastboot.c | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index d0d865cf3d0..f5ac2234981 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/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); @@ -161,6 +180,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; @@ -196,6 +228,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)); } From 4e643671f53173bf0f95f11e3b5a1872ffe029d3 Mon Sep 17 00:00:00 2001 From: Jun Li Date: Mon, 25 Jan 2021 21:43:52 +0800 Subject: [PATCH 26/34] usb: gadget: set correct usb_configuration for os_desc_config The current way to set cdev->os_desc_config is wrong if user restart fastboot, as the old config is not used anymore and new allocated usb_configuration will be used, so set the os_desc_config while usb_add_config. Reviewed-by: Ye Li Signed-off-by: Li Jun Signed-off-by: Peter Chen Signed-off-by: Peng Fan --- drivers/usb/gadget/composite.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index cd61bfec387..e6fdefd3d0d 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -399,10 +399,6 @@ static int set_config(struct usb_composite_dev *cdev, goto done; cdev->config = c; - if (cdev->use_os_string) { - cdev->os_desc_config = c; - os_desc_config = c; - } /* Initialize all interfaces by setting them to altsetting zero. */ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { @@ -516,6 +512,9 @@ int usb_add_config(struct usb_composite_dev *cdev, usb_ep_autoconfig_reset(cdev->gadget); + if (os_desc_config) + cdev->os_desc_config = os_desc_config; + done: if (status) debug("added config '%s'/%u --> %d\n", config->label, @@ -1373,9 +1372,6 @@ static int composite_bind(struct usb_gadget *gadget) /* Microsoft OS String Descriptor */ utf8_to_utf16le(qw_sign_buf, (__le16 *)cdev->qw_sign, OS_STRING_QW_SIGN_LEN / 2); - - if (os_desc_config) - cdev->os_desc_config = os_desc_config; } debug("%s: ready\n", composite->name); From 42a594de14637f809050cd2e2ea8b4a7f51bb31a Mon Sep 17 00:00:00 2001 From: Jun Li Date: Mon, 25 Jan 2021 21:43:53 +0800 Subject: [PATCH 27/34] usb: gadget: update os_desc_config when add config Always use the new added config for os_desc_config to fix cdev-> os_desc_config may miss set in case we restart usb gadget driver. Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/composite.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index e6fdefd3d0d..404da12754c 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -512,8 +512,8 @@ int usb_add_config(struct usb_composite_dev *cdev, usb_ep_autoconfig_reset(cdev->gadget); - if (os_desc_config) - cdev->os_desc_config = os_desc_config; + os_desc_config = config; + cdev->os_desc_config = os_desc_config; done: if (status) From 8745b9ebccae599856045c5d6354463178604f72 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 25 Jan 2021 21:43:54 +0800 Subject: [PATCH 28/34] usb: gadget: add super speed support This patch is to add usb gadget super speed support in common driver, including BOS descriptor and select the super speed descriptor from function driver. Reviewed-by: Ye Li Reviewed-by: Peter Chen Tested-by: faqiang.zhu Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/composite.c | 85 ++++++++++++++++++++++++---------- include/linux/usb/composite.h | 4 ++ include/linux/usb/gadget.h | 6 +++ 3 files changed, 70 insertions(+), 25 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 404da12754c..1063c571d80 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -88,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) @@ -223,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; @@ -251,7 +255,9 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) 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) @@ -275,7 +281,10 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) continue; check_config: - if (speed == USB_SPEED_HIGH) { + if (speed == USB_SPEED_SUPER) { + if (!c->superspeed) + continue; + } else if (speed == USB_SPEED_HIGH) { if (!c->highspeed) continue; } else { @@ -294,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; @@ -304,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 { @@ -388,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; @@ -412,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; @@ -492,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) @@ -751,6 +773,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; @@ -794,9 +817,19 @@ 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); } @@ -999,32 +1032,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: @@ -1039,10 +1068,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; @@ -1421,7 +1456,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, diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index d75a0bc4c49..935e5c0cbb1 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -146,6 +146,7 @@ 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; @@ -279,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]; }; @@ -292,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 @@ -319,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 diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 8d54b91734c..7e6d329e542 100644 --- a/include/linux/usb/gadget.h +++ b/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,6 +469,7 @@ 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 *); From 761dfe0fb2ba82704d1212021b4623da34800de4 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 25 Jan 2021 21:43:55 +0800 Subject: [PATCH 29/34] usb: fastboot: add super speed support Add super speed EP config. Reviewed-by: Ye Li Reviewed-by: Peter Chen Tested-by: faqiang.zhu Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/f_fastboot.c | 47 ++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index f5ac2234981..f3ce47a0d2a 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -128,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; @@ -219,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); @@ -283,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"); @@ -298,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"); From 429e2f299f3a978cf248c3597f60308c5d636ad8 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 25 Jan 2021 21:43:56 +0800 Subject: [PATCH 30/34] usb: gadget: dnl: set dnl to be super speed Set its max_speed to be super speed. Reviewed-by: Ye Li Reviewed-by: Peter Chen Tested-by: faqiang.zhu Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/g_dnl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index 86fdd16b012..afb7b74f305 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/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, From 4df50f89f5769732c6cce67f956371140680ff5d Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 25 Jan 2021 21:43:57 +0800 Subject: [PATCH 31/34] usb: composite: force gadget to be USB2 for HS only function If one of functions is not super speed capable, we need force the udc to be high speed, this is an equivalent implementation of usb_gadget_udc_set_speed() in kernel but simple, which set the gadget max_speed to be high speed, so afterwards when start gadget duc can set the HW to be USB 2.0 mode. Reviewed-by: Ye Li Reviewed-by: Peter Chen Tested-by: faqiang.zhu Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/composite.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 1063c571d80..2a309e624e1 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -532,6 +532,19 @@ 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; From 54b22f409cdaaeb3f312a3afcfaddf2fd184209b Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 25 Jan 2021 21:43:58 +0800 Subject: [PATCH 32/34] usb: udc: ci: update speed handling Remove the gadget driver speed check, and set its max_speed to be USB_SPEED_HIGH. Reviewed-by: Ye Li Reviewed-by: Peter Chen Tested-by: faqiang.zhu Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/ci_udc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index cdb8f6fb3d0..b64e4bb605d 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/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, }, }; @@ -1015,8 +1016,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); From 27c9141b1114fd5721437abbb1c694e45e765f19 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 25 Jan 2021 21:43:59 +0800 Subject: [PATCH 33/34] usb: gadget: fastboot: use correct max packet size Change to use wMaxPacketSize of current speed EP desc for request length wrap up. Reviewed-by: Peter Chen Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/f_fastboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index f3ce47a0d2a..950cc119495 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -427,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; From 723fd5668ff2c8dd19e808778b5670d0fa6bdc4b Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 25 Jan 2021 21:44:00 +0800 Subject: [PATCH 34/34] usb: gaget: ci: set ep's desc when enable ep As we need standard usb_ep's desc, so set it when enable ep. Reviewed-by: Peter Chen Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/ci_udc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index b64e4bb605d..226a9e6d671 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/drivers/usb/gadget/ci_udc.c @@ -336,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); @@ -358,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; }