mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-06-09 23:36:03 +09:00
- Assorted gadget changes including: - dfu: Fix handling of UBI partitions in MTD backend - gadget: f_thor: fix wrong file size cast - Extend cmd: bcb - Fixes for fastboot and rockchip gadgets - dfu: Add SCRIPT and SKIP entities - dfu/thor: Add `dfu_alt_info` reinitialization from flashed script - u-boot: Reduce size of u-boot as usbd_device_* arrays are not exported
This commit is contained in:
commit
b4804cdd57
98
cmd/bcb.c
98
cmd/bcb.c
|
@ -6,10 +6,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <android_bootloader_message.h>
|
#include <android_bootloader_message.h>
|
||||||
|
#include <bcb.h>
|
||||||
#include <command.h>
|
#include <command.h>
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <log.h>
|
#include <log.h>
|
||||||
#include <part.h>
|
#include <part.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
enum bcb_cmd {
|
enum bcb_cmd {
|
||||||
BCB_CMD_LOAD,
|
BCB_CMD_LOAD,
|
||||||
|
@ -110,8 +112,7 @@ static int bcb_field_get(char *name, char **fieldp, int *sizep)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc,
|
static int __bcb_load(int devnum, const char *partp)
|
||||||
char *const argv[])
|
|
||||||
{
|
{
|
||||||
struct blk_desc *desc;
|
struct blk_desc *desc;
|
||||||
struct disk_partition info;
|
struct disk_partition info;
|
||||||
|
@ -119,17 +120,19 @@ static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||||
char *endp;
|
char *endp;
|
||||||
int part, ret;
|
int part, ret;
|
||||||
|
|
||||||
ret = blk_get_device_by_str("mmc", argv[1], &desc);
|
desc = blk_get_devnum_by_type(IF_TYPE_MMC, devnum);
|
||||||
if (ret < 0)
|
if (!desc) {
|
||||||
|
ret = -ENODEV;
|
||||||
goto err_read_fail;
|
goto err_read_fail;
|
||||||
|
}
|
||||||
|
|
||||||
part = simple_strtoul(argv[2], &endp, 0);
|
part = simple_strtoul(partp, &endp, 0);
|
||||||
if (*endp == '\0') {
|
if (*endp == '\0') {
|
||||||
ret = part_get_info(desc, part, &info);
|
ret = part_get_info(desc, part, &info);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_read_fail;
|
goto err_read_fail;
|
||||||
} else {
|
} else {
|
||||||
part = part_get_info_by_name(desc, argv[2], &info);
|
part = part_get_info_by_name(desc, partp, &info);
|
||||||
if (part < 0) {
|
if (part < 0) {
|
||||||
ret = part;
|
ret = part;
|
||||||
goto err_read_fail;
|
goto err_read_fail;
|
||||||
|
@ -151,10 +154,10 @@ static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||||
|
|
||||||
return CMD_RET_SUCCESS;
|
return CMD_RET_SUCCESS;
|
||||||
err_read_fail:
|
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;
|
goto err;
|
||||||
err_too_small:
|
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;
|
goto err;
|
||||||
err:
|
err:
|
||||||
bcb_dev = -1;
|
bcb_dev = -1;
|
||||||
|
@ -163,33 +166,58 @@ err:
|
||||||
return CMD_RET_FAILURE;
|
return CMD_RET_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_bcb_set(struct cmd_tbl *cmdtp, int flag, int argc,
|
static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||||
char *const argv[])
|
char * const argv[])
|
||||||
{
|
{
|
||||||
int size, len;
|
char *endp;
|
||||||
char *field, *str, *found;
|
int devnum = simple_strtoul(argv[1], &endp, 0);
|
||||||
|
|
||||||
if (bcb_field_get(argv[1], &field, &size))
|
if (*endp != '\0') {
|
||||||
return CMD_RET_FAILURE;
|
printf("Error: Device id '%s' not a number\n", argv[1]);
|
||||||
|
|
||||||
len = strlen(argv[2]);
|
|
||||||
if (len >= size) {
|
|
||||||
printf("Error: sizeof('%s') = %d >= %d = sizeof(bcb.%s)\n",
|
|
||||||
argv[2], len, size, argv[1]);
|
|
||||||
return CMD_RET_FAILURE;
|
return CMD_RET_FAILURE;
|
||||||
}
|
}
|
||||||
str = argv[2];
|
|
||||||
|
|
||||||
|
return __bcb_load(devnum, argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __bcb_set(char *fieldp, const char *valp)
|
||||||
|
{
|
||||||
|
int size, len;
|
||||||
|
char *field, *str, *found, *tmp;
|
||||||
|
|
||||||
|
if (bcb_field_get(fieldp, &field, &size))
|
||||||
|
return CMD_RET_FAILURE;
|
||||||
|
|
||||||
|
len = strlen(valp);
|
||||||
|
if (len >= size) {
|
||||||
|
printf("Error: sizeof('%s') = %d >= %d = sizeof(bcb.%s)\n",
|
||||||
|
valp, len, size, fieldp);
|
||||||
|
return CMD_RET_FAILURE;
|
||||||
|
}
|
||||||
|
str = strdup(valp);
|
||||||
|
if (!str) {
|
||||||
|
printf("Error: Out of memory while strdup\n");
|
||||||
|
return CMD_RET_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = str;
|
||||||
field[0] = '\0';
|
field[0] = '\0';
|
||||||
while ((found = strsep(&str, ":"))) {
|
while ((found = strsep(&tmp, ":"))) {
|
||||||
if (field[0] != '\0')
|
if (field[0] != '\0')
|
||||||
strcat(field, "\n");
|
strcat(field, "\n");
|
||||||
strcat(field, found);
|
strcat(field, found);
|
||||||
}
|
}
|
||||||
|
free(str);
|
||||||
|
|
||||||
return CMD_RET_SUCCESS;
|
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,
|
static int do_bcb_clear(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||||
char *const argv[])
|
char *const argv[])
|
||||||
{
|
{
|
||||||
|
@ -250,8 +278,7 @@ static int do_bcb_dump(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||||
return CMD_RET_SUCCESS;
|
return CMD_RET_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_bcb_store(struct cmd_tbl *cmdtp, int flag, int argc,
|
static int __bcb_store(void)
|
||||||
char *const argv[])
|
|
||||||
{
|
{
|
||||||
struct blk_desc *desc;
|
struct blk_desc *desc;
|
||||||
struct disk_partition info;
|
struct disk_partition info;
|
||||||
|
@ -282,6 +309,31 @@ err:
|
||||||
return CMD_RET_FAILURE;
|
return CMD_RET_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int do_bcb_store(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||||
|
char * const argv[])
|
||||||
|
{
|
||||||
|
return __bcb_store();
|
||||||
|
}
|
||||||
|
|
||||||
|
int bcb_write_reboot_reason(int devnum, char *partp, const char *reasonp)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = __bcb_load(devnum, partp);
|
||||||
|
if (ret != CMD_RET_SUCCESS)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = __bcb_set("command", reasonp);
|
||||||
|
if (ret != CMD_RET_SUCCESS)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = __bcb_store();
|
||||||
|
if (ret != CMD_RET_SUCCESS)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct cmd_tbl cmd_bcb_sub[] = {
|
static struct cmd_tbl cmd_bcb_sub[] = {
|
||||||
U_BOOT_CMD_MKENT(load, CONFIG_SYS_MAXARGS, 1, do_bcb_load, "", ""),
|
U_BOOT_CMD_MKENT(load, CONFIG_SYS_MAXARGS, 1, do_bcb_load, "", ""),
|
||||||
U_BOOT_CMD_MKENT(set, CONFIG_SYS_MAXARGS, 1, do_bcb_set, "", ""),
|
U_BOOT_CMD_MKENT(set, CONFIG_SYS_MAXARGS, 1, do_bcb_set, "", ""),
|
||||||
|
|
13
cmd/dfu.c
13
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)
|
#if defined(CONFIG_DFU_TIMEOUT) || defined(CONFIG_DFU_OVER_TFTP)
|
||||||
unsigned long value = 0;
|
unsigned long value = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (argc >= 4) {
|
if (argc >= 4) {
|
||||||
interface = argv[2];
|
interface = argv[2];
|
||||||
devstring = argv[3];
|
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);
|
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:
|
done:
|
||||||
dfu_free_entities();
|
dfu_free_entities();
|
||||||
|
|
|
@ -52,13 +52,18 @@ int do_thor_down(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = thor_handle();
|
do {
|
||||||
if (ret) {
|
ret = thor_handle();
|
||||||
pr_err("THOR failed: %d\n", ret);
|
if (ret == THOR_DFU_REINIT_NEEDED) {
|
||||||
ret = CMD_RET_FAILURE;
|
dfu_free_entities();
|
||||||
goto exit;
|
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:
|
exit:
|
||||||
g_dnl_unregister();
|
g_dnl_unregister();
|
||||||
usb_gadget_release(controller_index);
|
usb_gadget_release(controller_index);
|
||||||
|
|
|
@ -115,8 +115,8 @@ static int ums_init(const char *devtype, const char *devnums_part_str)
|
||||||
ums[ums_count].name = name;
|
ums[ums_count].name = name;
|
||||||
ums[ums_count].block_dev = *block_dev;
|
ums[ums_count].block_dev = *block_dev;
|
||||||
|
|
||||||
printf("UMS: LUN %d, dev %d, hwpart %d, sector %#x, count %#x\n",
|
printf("UMS: LUN %d, dev %s %d, hwpart %d, sector %#x, count %#x\n",
|
||||||
ums_count, ums[ums_count].block_dev.devnum,
|
ums_count, devtype, ums[ums_count].block_dev.devnum,
|
||||||
ums[ums_count].block_dev.hwpart,
|
ums[ums_count].block_dev.hwpart,
|
||||||
ums[ums_count].start_sector,
|
ums[ums_count].start_sector,
|
||||||
ums[ums_count].num_sectors);
|
ums[ums_count].num_sectors);
|
||||||
|
|
|
@ -98,6 +98,9 @@ int run_usb_dnl_gadget(int usbctrl_index, char *usb_dnl_gadget)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (dfu_reinit_needed)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
WATCHDOG_RESET();
|
WATCHDOG_RESET();
|
||||||
usb_gadget_handle_interrupts(usbctrl_index);
|
usb_gadget_handle_interrupts(usbctrl_index);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ Overview:
|
||||||
- The access to mediums is done in DFU backends (driver/dfu)
|
- The access to mediums is done in DFU backends (driver/dfu)
|
||||||
|
|
||||||
Today the supported DFU backends are:
|
Today the supported DFU backends are:
|
||||||
- MMC (RAW or FAT / EXT2 / EXT3 / EXT4 file system)
|
- MMC (RAW or FAT / EXT2 / EXT3 / EXT4 file system / SKIP / SCRIPT)
|
||||||
- NAND
|
- NAND
|
||||||
- RAM
|
- RAM
|
||||||
- SF (serial flash)
|
- SF (serial flash)
|
||||||
|
@ -91,6 +91,8 @@ Commands:
|
||||||
<name> part <dev> <part_id> [mmcpart <num>] raw access to partition
|
<name> part <dev> <part_id> [mmcpart <num>] raw access to partition
|
||||||
<name> fat <dev> <part_id> [mmcpart <num>] file in FAT partition
|
<name> fat <dev> <part_id> [mmcpart <num>] file in FAT partition
|
||||||
<name> ext4 <dev> <part_id> [mmcpart <num>] file in EXT4 partition
|
<name> ext4 <dev> <part_id> [mmcpart <num>] file in EXT4 partition
|
||||||
|
<name> skip 0 0 ignore flashed data
|
||||||
|
<name> script 0 0 execute commands in shell
|
||||||
|
|
||||||
with <partid> being the GPT or DOS partition index,
|
with <partid> being the GPT or DOS partition index,
|
||||||
with <num> being the eMMC hardware partition number.
|
with <num> being the eMMC hardware partition number.
|
||||||
|
@ -103,6 +105,32 @@ Commands:
|
||||||
|
|
||||||
"u-boot raw 0x80 0x800;uImage ext4 0 2"
|
"u-boot raw 0x80 0x800;uImage ext4 0 2"
|
||||||
|
|
||||||
|
If don't want to flash given image file to storage, use "skip" type
|
||||||
|
entity.
|
||||||
|
- It can be used to protect flashing wrong image for the specific board.
|
||||||
|
- Especailly, this layout will be useful when thor protocol is used,
|
||||||
|
which performs flashing in batch mode, where more than one file is
|
||||||
|
processed.
|
||||||
|
For example, if one makes a single tar file with support for the two
|
||||||
|
boards with u-boot-<board1>.bin and u-boot-<board2>.bin files, one
|
||||||
|
can use it to flash a proper u-boot image on both without a failure:
|
||||||
|
|
||||||
|
"u-boot-<board1>.bin raw 0x80 0x800; u-boot-<board2>.bin skip 0 0"
|
||||||
|
|
||||||
|
When flashing new system image requires do some more complex things
|
||||||
|
than just writing data to the storage medium, one can use 'script'
|
||||||
|
type. Data written to such entity will be executed as a command list
|
||||||
|
in the u-boot's shell. This for example allows to re-create partition
|
||||||
|
layout and even set new dfu_alt_info for the newly created paritions.
|
||||||
|
Such script would look like:
|
||||||
|
--->8---
|
||||||
|
setenv dfu_alt_info ...
|
||||||
|
setenv mbr_parts ...
|
||||||
|
mbr write ...
|
||||||
|
--->8---
|
||||||
|
Please note that this means that user will be able to execute any
|
||||||
|
arbitrary commands just like in the u-boot's shell.
|
||||||
|
|
||||||
"nand" (raw slc nand device)
|
"nand" (raw slc nand device)
|
||||||
cmd: dfu 0 nand <dev>
|
cmd: dfu 0 nand <dev>
|
||||||
each element in "dfu_alt_info" =
|
each element in "dfu_alt_info" =
|
||||||
|
|
|
@ -26,6 +26,8 @@ static struct hash_algo *dfu_hash_algo;
|
||||||
static unsigned long dfu_timeout = 0;
|
static unsigned long dfu_timeout = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool dfu_reinit_needed = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The purpose of the dfu_flush_callback() function is to
|
* The purpose of the dfu_flush_callback() function is to
|
||||||
* provide callback for dfu user
|
* provide callback for dfu user
|
||||||
|
@ -139,6 +141,8 @@ int dfu_init_env_entities(char *interface, char *devstr)
|
||||||
char *env_bkp;
|
char *env_bkp;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
dfu_reinit_needed = false;
|
||||||
|
|
||||||
#ifdef CONFIG_SET_DFU_ALT_INFO
|
#ifdef CONFIG_SET_DFU_ALT_INFO
|
||||||
set_dfu_alt_info(interface, devstr);
|
set_dfu_alt_info(interface, devstr);
|
||||||
#endif
|
#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 *dfu_get_layout(enum dfu_layout l)
|
||||||
{
|
{
|
||||||
const char *const dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2",
|
const char *const dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2",
|
||||||
"EXT3", "EXT4", "RAM_ADDR" };
|
"EXT3", "EXT4", "RAM_ADDR", "SKIP",
|
||||||
|
"SCRIPT" };
|
||||||
return dfu_layout[l];
|
return dfu_layout[l];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <fat.h>
|
#include <fat.h>
|
||||||
#include <mmc.h>
|
#include <mmc.h>
|
||||||
#include <part.h>
|
#include <part.h>
|
||||||
|
#include <command.h>
|
||||||
|
|
||||||
static unsigned char *dfu_file_buf;
|
static unsigned char *dfu_file_buf;
|
||||||
static u64 dfu_file_buf_len;
|
static u64 dfu_file_buf_len;
|
||||||
|
@ -108,6 +109,8 @@ static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu,
|
||||||
case DFU_FS_EXT4:
|
case DFU_FS_EXT4:
|
||||||
fstype = FS_TYPE_EXT;
|
fstype = FS_TYPE_EXT;
|
||||||
break;
|
break;
|
||||||
|
case DFU_SKIP:
|
||||||
|
return 0;
|
||||||
default:
|
default:
|
||||||
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
|
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
|
||||||
dfu_get_layout(dfu->layout));
|
dfu_get_layout(dfu->layout));
|
||||||
|
@ -204,6 +207,12 @@ int dfu_write_medium_mmc(struct dfu_entity *dfu,
|
||||||
case DFU_FS_EXT4:
|
case DFU_FS_EXT4:
|
||||||
ret = mmc_file_buf_write(dfu, offset, buf, len);
|
ret = mmc_file_buf_write(dfu, offset, buf, len);
|
||||||
break;
|
break;
|
||||||
|
case DFU_SCRIPT:
|
||||||
|
ret = run_command_list(buf, *len, 0);
|
||||||
|
break;
|
||||||
|
case DFU_SKIP:
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
|
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
|
||||||
dfu_get_layout(dfu->layout));
|
dfu_get_layout(dfu->layout));
|
||||||
|
@ -216,9 +225,21 @@ int dfu_flush_medium_mmc(struct dfu_entity *dfu)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (dfu->layout != DFU_RAW_ADDR) {
|
switch (dfu->layout) {
|
||||||
/* Do stuff here. */
|
case DFU_FS_FAT:
|
||||||
|
case DFU_FS_EXT4:
|
||||||
ret = mmc_file_buf_write_finish(dfu);
|
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;
|
return ret;
|
||||||
|
@ -238,6 +259,9 @@ int dfu_get_medium_size_mmc(struct dfu_entity *dfu, u64 *size)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
return 0;
|
return 0;
|
||||||
|
case DFU_SCRIPT:
|
||||||
|
case DFU_SKIP:
|
||||||
|
return 0;
|
||||||
default:
|
default:
|
||||||
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
|
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
|
||||||
dfu_get_layout(dfu->layout));
|
dfu_get_layout(dfu->layout));
|
||||||
|
@ -316,7 +340,7 @@ void dfu_free_entity_mmc(struct dfu_entity *dfu)
|
||||||
int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
|
int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
|
||||||
{
|
{
|
||||||
const char *entity_type;
|
const char *entity_type;
|
||||||
size_t second_arg;
|
ssize_t second_arg;
|
||||||
size_t third_arg;
|
size_t third_arg;
|
||||||
|
|
||||||
struct mmc *mmc;
|
struct mmc *mmc;
|
||||||
|
@ -339,7 +363,7 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
|
||||||
* Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8,
|
* Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8,
|
||||||
* with default 10.
|
* 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);
|
third_arg = simple_strtoul(argv[2], NULL, 0);
|
||||||
|
|
||||||
mmc = find_mmc_device(dfu->data.mmc.dev_num);
|
mmc = find_mmc_device(dfu->data.mmc.dev_num);
|
||||||
|
@ -399,6 +423,10 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
|
||||||
dfu->layout = DFU_FS_FAT;
|
dfu->layout = DFU_FS_FAT;
|
||||||
} else if (!strcmp(entity_type, "ext4")) {
|
} else if (!strcmp(entity_type, "ext4")) {
|
||||||
dfu->layout = DFU_FS_EXT4;
|
dfu->layout = DFU_FS_EXT4;
|
||||||
|
} else if (!strcmp(entity_type, "skip")) {
|
||||||
|
dfu->layout = DFU_SKIP;
|
||||||
|
} else if (!strcmp(entity_type, "script")) {
|
||||||
|
dfu->layout = DFU_SCRIPT;
|
||||||
} else {
|
} else {
|
||||||
pr_err("Memory layout (%s) not supported!\n", entity_type);
|
pr_err("Memory layout (%s) not supported!\n", entity_type);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -406,7 +434,8 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
|
||||||
|
|
||||||
/* if it's NOT a raw write */
|
/* if it's NOT a raw write */
|
||||||
if (strcmp(entity_type, "raw")) {
|
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;
|
dfu->data.mmc.part = third_arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,7 +204,7 @@ static int dfu_flush_medium_mtd(struct dfu_entity *dfu)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* in case of ubi partition, erase rest of the partition */
|
/* 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 = {};
|
struct erase_info erase_op = {};
|
||||||
|
|
||||||
erase_op.mtd = dfu->data.mtd.info;
|
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
|
* ubi partition, as sectors which are not used need
|
||||||
* to be erased
|
* to be erased
|
||||||
*/
|
*/
|
||||||
if (dfu->data.nand.ubi)
|
if (dfu->data.mtd.ubi)
|
||||||
return DFU_MANIFEST_POLL_TIMEOUT;
|
return DFU_MANIFEST_POLL_TIMEOUT;
|
||||||
|
|
||||||
return DFU_DEFAULT_POLL_TIMEOUT;
|
return DFU_DEFAULT_POLL_TIMEOUT;
|
||||||
|
|
|
@ -98,6 +98,15 @@ config USB_GADGET_DWC2_OTG_PHY_BUS_WIDTH_8
|
||||||
|
|
||||||
endif # USB_GADGET_DWC2_OTG
|
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
|
config CI_UDC
|
||||||
bool "ChipIdea device controller"
|
bool "ChipIdea device controller"
|
||||||
select USB_GADGET_DUALSPEED
|
select USB_GADGET_DUALSPEED
|
||||||
|
|
|
@ -145,6 +145,7 @@ static struct ci_drv controller = {
|
||||||
.name = "ci_udc",
|
.name = "ci_udc",
|
||||||
.ops = &ci_udc_ops,
|
.ops = &ci_udc_ops,
|
||||||
.is_dualspeed = 1,
|
.is_dualspeed = 1,
|
||||||
|
.max_speed = USB_SPEED_HIGH,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -335,6 +336,7 @@ static int ci_ep_enable(struct usb_ep *ep,
|
||||||
num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||||
in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
|
in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
|
||||||
ci_ep->desc = desc;
|
ci_ep->desc = desc;
|
||||||
|
ep->desc = desc;
|
||||||
|
|
||||||
if (num) {
|
if (num) {
|
||||||
int max = get_unaligned_le16(&desc->wMaxPacketSize);
|
int max = get_unaligned_le16(&desc->wMaxPacketSize);
|
||||||
|
@ -357,6 +359,7 @@ static int ci_ep_disable(struct usb_ep *ep)
|
||||||
struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
|
struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
|
||||||
|
|
||||||
ci_ep->desc = NULL;
|
ci_ep->desc = NULL;
|
||||||
|
ep->desc = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1015,8 +1018,6 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (!driver->bind || !driver->setup || !driver->disconnect)
|
if (!driver->bind || !driver->setup || !driver->disconnect)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
#if CONFIG_IS_ENABLED(DM_USB)
|
#if CONFIG_IS_ENABLED(DM_USB)
|
||||||
ret = usb_setup_ehci_gadget(&controller.ctrl);
|
ret = usb_setup_ehci_gadget(&controller.ctrl);
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
#include <linux/usb/composite.h>
|
#include <linux/usb/composite.h>
|
||||||
|
#include "u_os_desc.h"
|
||||||
|
|
||||||
#define USB_BUFSIZ 4096
|
#define USB_BUFSIZ 4096
|
||||||
|
|
||||||
|
@ -19,12 +20,32 @@
|
||||||
typedef struct { __le16 val; } __packed __le16_packed;
|
typedef struct { __le16 val; } __packed __le16_packed;
|
||||||
|
|
||||||
static struct usb_composite_driver *composite;
|
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)
|
static inline void le16_add_cpu_packed(__le16_packed *var, u16 val)
|
||||||
{
|
{
|
||||||
var->val = cpu_to_le16(le16_to_cpu(var->val) + 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
|
* usb_add_function() - add a function to a configuration
|
||||||
* @config: the configuration
|
* @config: the configuration
|
||||||
|
@ -67,6 +88,8 @@ int usb_add_function(struct usb_configuration *config,
|
||||||
config->fullspeed = 1;
|
config->fullspeed = 1;
|
||||||
if (!config->highspeed && function->hs_descriptors)
|
if (!config->highspeed && function->hs_descriptors)
|
||||||
config->highspeed = 1;
|
config->highspeed = 1;
|
||||||
|
if (!config->superspeed && function->ss_descriptors)
|
||||||
|
config->superspeed = 1;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (value)
|
if (value)
|
||||||
|
@ -202,7 +225,9 @@ static int config_buf(struct usb_configuration *config,
|
||||||
|
|
||||||
/* add each function's descriptors */
|
/* add each function's descriptors */
|
||||||
list_for_each_entry(f, &config->functions, list) {
|
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;
|
descriptors = f->hs_descriptors;
|
||||||
else
|
else
|
||||||
descriptors = f->descriptors;
|
descriptors = f->descriptors;
|
||||||
|
@ -228,8 +253,11 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
|
||||||
u8 type = w_value >> 8;
|
u8 type = w_value >> 8;
|
||||||
int hs = 0;
|
int hs = 0;
|
||||||
struct usb_configuration *c;
|
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)
|
if (gadget->speed == USB_SPEED_HIGH)
|
||||||
hs = 1;
|
hs = 1;
|
||||||
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
if (type == USB_DT_OTHER_SPEED_CONFIG)
|
||||||
|
@ -239,8 +267,24 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
w_value &= 0xff;
|
w_value &= 0xff;
|
||||||
list_for_each_entry(c, &cdev->configs, list) {
|
|
||||||
if (speed == USB_SPEED_HIGH) {
|
pos = &cdev->configs;
|
||||||
|
c = cdev->os_desc_config;
|
||||||
|
if (c)
|
||||||
|
goto check_config;
|
||||||
|
|
||||||
|
while ((pos = pos->next) != &cdev->configs) {
|
||||||
|
c = list_entry(pos, typeof(*c), list);
|
||||||
|
|
||||||
|
/* skip OS Descriptors config which is handled separately */
|
||||||
|
if (c == cdev->os_desc_config)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
check_config:
|
||||||
|
if (speed == USB_SPEED_SUPER) {
|
||||||
|
if (!c->superspeed)
|
||||||
|
continue;
|
||||||
|
} else if (speed == USB_SPEED_HIGH) {
|
||||||
if (!c->highspeed)
|
if (!c->highspeed)
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
@ -259,8 +303,12 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
|
||||||
struct usb_gadget *gadget = cdev->gadget;
|
struct usb_gadget *gadget = cdev->gadget;
|
||||||
unsigned count = 0;
|
unsigned count = 0;
|
||||||
int hs = 0;
|
int hs = 0;
|
||||||
|
int ss = 0;
|
||||||
struct usb_configuration *c;
|
struct usb_configuration *c;
|
||||||
|
|
||||||
|
if (gadget->speed == USB_SPEED_SUPER)
|
||||||
|
ss = 1;
|
||||||
|
|
||||||
if (gadget_is_dualspeed(gadget)) {
|
if (gadget_is_dualspeed(gadget)) {
|
||||||
if (gadget->speed == USB_SPEED_HIGH)
|
if (gadget->speed == USB_SPEED_HIGH)
|
||||||
hs = 1;
|
hs = 1;
|
||||||
|
@ -269,7 +317,10 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
|
||||||
}
|
}
|
||||||
list_for_each_entry(c, &cdev->configs, list) {
|
list_for_each_entry(c, &cdev->configs, list) {
|
||||||
/* ignore configs that won't work at this speed */
|
/* ignore configs that won't work at this speed */
|
||||||
if (hs) {
|
if (ss) {
|
||||||
|
if (!c->superspeed)
|
||||||
|
continue;
|
||||||
|
} else if (hs) {
|
||||||
if (!c->highspeed)
|
if (!c->highspeed)
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
@ -353,6 +404,9 @@ static int set_config(struct usb_composite_dev *cdev,
|
||||||
case USB_SPEED_HIGH:
|
case USB_SPEED_HIGH:
|
||||||
speed = "high";
|
speed = "high";
|
||||||
break;
|
break;
|
||||||
|
case USB_SPEED_SUPER:
|
||||||
|
speed = "super";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
speed = "?";
|
speed = "?";
|
||||||
break;
|
break;
|
||||||
|
@ -377,7 +431,9 @@ static int set_config(struct usb_composite_dev *cdev,
|
||||||
* function's setup callback instead of the current
|
* function's setup callback instead of the current
|
||||||
* configuration's setup callback.
|
* 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;
|
descriptors = f->hs_descriptors;
|
||||||
else
|
else
|
||||||
descriptors = f->descriptors;
|
descriptors = f->descriptors;
|
||||||
|
@ -457,8 +513,9 @@ int usb_add_config(struct usb_composite_dev *cdev,
|
||||||
list_del(&config->list);
|
list_del(&config->list);
|
||||||
config->cdev = NULL;
|
config->cdev = NULL;
|
||||||
} else {
|
} else {
|
||||||
debug("cfg %d/%p speeds:%s%s\n",
|
debug("cfg %d/%p speeds:%s%s%s\n",
|
||||||
config->bConfigurationValue, config,
|
config->bConfigurationValue, config,
|
||||||
|
config->superspeed ? " super" : "",
|
||||||
config->highspeed ? " high" : "",
|
config->highspeed ? " high" : "",
|
||||||
config->fullspeed
|
config->fullspeed
|
||||||
? (gadget_is_dualspeed(cdev->gadget)
|
? (gadget_is_dualspeed(cdev->gadget)
|
||||||
|
@ -475,8 +532,24 @@ int usb_add_config(struct usb_composite_dev *cdev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If one function of config is not super speed capable,
|
||||||
|
* force the gadget to be high speed so controller driver
|
||||||
|
* can init HW to be USB 2.0
|
||||||
|
*/
|
||||||
|
if (gadget_is_superspeed(cdev->gadget)) {
|
||||||
|
list_for_each_entry(f, &config->functions, list) {
|
||||||
|
if (!f->ss_descriptors)
|
||||||
|
cdev->gadget->max_speed =
|
||||||
|
USB_SPEED_HIGH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
usb_ep_autoconfig_reset(cdev->gadget);
|
usb_ep_autoconfig_reset(cdev->gadget);
|
||||||
|
|
||||||
|
os_desc_config = config;
|
||||||
|
cdev->os_desc_config = os_desc_config;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (status)
|
if (status)
|
||||||
debug("added config '%s'/%u --> %d\n", config->label,
|
debug("added config '%s'/%u --> %d\n", config->label,
|
||||||
|
@ -577,6 +650,16 @@ static int get_string(struct usb_composite_dev *cdev,
|
||||||
return s->bLength;
|
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
|
* Otherwise, look up and return a specified string. String IDs
|
||||||
* are device-scoped, so we look up each string table we're told
|
* are device-scoped, so we look up each string table we're told
|
||||||
|
@ -703,6 +786,7 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
|
||||||
static int bos_desc(struct usb_composite_dev *cdev)
|
static int bos_desc(struct usb_composite_dev *cdev)
|
||||||
{
|
{
|
||||||
struct usb_ext_cap_descriptor *usb_ext;
|
struct usb_ext_cap_descriptor *usb_ext;
|
||||||
|
struct usb_dcd_config_params dcd_config_params;
|
||||||
struct usb_bos_descriptor *bos = cdev->req->buf;
|
struct usb_bos_descriptor *bos = cdev->req->buf;
|
||||||
|
|
||||||
bos->bLength = USB_DT_BOS_SIZE;
|
bos->bLength = USB_DT_BOS_SIZE;
|
||||||
|
@ -746,13 +830,173 @@ static int bos_desc(struct usb_composite_dev *cdev)
|
||||||
USB_HIGH_SPEED_OPERATION |
|
USB_HIGH_SPEED_OPERATION |
|
||||||
USB_5GBPS_OPERATION);
|
USB_5GBPS_OPERATION);
|
||||||
ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
|
ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
|
||||||
ss_cap->bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT;
|
|
||||||
ss_cap->bU2DevExitLat =
|
/* Get Controller configuration */
|
||||||
cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
|
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);
|
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
|
* The setup() callback implements all the ep0 functionality that's
|
||||||
* not handled lower down, in hardware or the hardware driver(like
|
* not handled lower down, in hardware or the hardware driver(like
|
||||||
|
@ -801,32 +1045,28 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||||
cdev->desc.bNumConfigurations =
|
cdev->desc.bNumConfigurations =
|
||||||
count_configs(cdev, USB_DT_DEVICE);
|
count_configs(cdev, USB_DT_DEVICE);
|
||||||
|
|
||||||
/*
|
cdev->desc.bMaxPacketSize0 =
|
||||||
* If the speed is Super speed, then the supported
|
cdev->gadget->ep0->maxpacket;
|
||||||
* max packet size is 512 and it should be sent as
|
if (gadget->speed >= USB_SPEED_SUPER) {
|
||||||
* exponent of 2. So, 9(2^9=512) should be filled in
|
cdev->desc.bcdUSB = cpu_to_le16(0x0310);
|
||||||
* bMaxPacketSize0. Also fill USB version as 3.0
|
|
||||||
* if speed is Super speed.
|
|
||||||
*/
|
|
||||||
if (cdev->gadget->speed == USB_SPEED_SUPER) {
|
|
||||||
cdev->desc.bMaxPacketSize0 = 9;
|
cdev->desc.bMaxPacketSize0 = 9;
|
||||||
cdev->desc.bcdUSB = cpu_to_le16(0x0300);
|
|
||||||
} else {
|
} else {
|
||||||
cdev->desc.bMaxPacketSize0 =
|
cdev->desc.bcdUSB = cpu_to_le16(0x0200);
|
||||||
cdev->gadget->ep0->maxpacket;
|
|
||||||
}
|
}
|
||||||
value = min(w_length, (u16) sizeof cdev->desc);
|
value = min(w_length, (u16) sizeof cdev->desc);
|
||||||
memcpy(req->buf, &cdev->desc, value);
|
memcpy(req->buf, &cdev->desc, value);
|
||||||
break;
|
break;
|
||||||
case USB_DT_DEVICE_QUALIFIER:
|
case USB_DT_DEVICE_QUALIFIER:
|
||||||
if (!gadget_is_dualspeed(gadget))
|
if (!gadget_is_dualspeed(gadget) ||
|
||||||
|
gadget->speed >= USB_SPEED_SUPER)
|
||||||
break;
|
break;
|
||||||
device_qual(cdev);
|
device_qual(cdev);
|
||||||
value = min_t(int, w_length,
|
value = min_t(int, w_length,
|
||||||
sizeof(struct usb_qualifier_descriptor));
|
sizeof(struct usb_qualifier_descriptor));
|
||||||
break;
|
break;
|
||||||
case USB_DT_OTHER_SPEED_CONFIG:
|
case USB_DT_OTHER_SPEED_CONFIG:
|
||||||
if (!gadget_is_dualspeed(gadget))
|
if (!gadget_is_dualspeed(gadget) ||
|
||||||
|
gadget->speed >= USB_SPEED_SUPER)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case USB_DT_CONFIG:
|
case USB_DT_CONFIG:
|
||||||
|
@ -841,10 +1081,16 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||||
value = min(w_length, (u16) value);
|
value = min(w_length, (u16) value);
|
||||||
break;
|
break;
|
||||||
case USB_DT_BOS:
|
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);
|
value = bos_desc(cdev);
|
||||||
if (value >= 0)
|
|
||||||
value = min(w_length, (u16)value);
|
value = min(w_length, (u16)value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
goto unknown;
|
goto unknown;
|
||||||
|
@ -909,6 +1155,91 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
unknown:
|
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",
|
debug("non-core control req%02x.%02x v%04x i%04x l%d\n",
|
||||||
ctrl->bRequestType, ctrl->bRequest,
|
ctrl->bRequestType, ctrl->bRequest,
|
||||||
w_value, w_index, w_length);
|
w_value, w_index, w_length);
|
||||||
|
@ -1082,6 +1413,15 @@ static int composite_bind(struct usb_gadget *gadget)
|
||||||
sizeof(struct usb_device_descriptor));
|
sizeof(struct usb_device_descriptor));
|
||||||
cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
|
cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
|
||||||
|
|
||||||
|
if (cdev->use_os_string) {
|
||||||
|
/* TODO: Do we want to pass this via platform? */
|
||||||
|
cdev->b_vendor_code = 0x40;
|
||||||
|
|
||||||
|
/* Microsoft OS String Descriptor */
|
||||||
|
utf8_to_utf16le(qw_sign_buf, (__le16 *)cdev->qw_sign,
|
||||||
|
OS_STRING_QW_SIGN_LEN / 2);
|
||||||
|
}
|
||||||
|
|
||||||
debug("%s: ready\n", composite->name);
|
debug("%s: ready\n", composite->name);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -1129,7 +1469,7 @@ composite_resume(struct usb_gadget *gadget)
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct usb_gadget_driver composite_driver = {
|
static struct usb_gadget_driver composite_driver = {
|
||||||
.speed = USB_SPEED_HIGH,
|
.speed = USB_SPEED_SUPER,
|
||||||
|
|
||||||
.bind = composite_bind,
|
.bind = composite_bind,
|
||||||
.unbind = composite_unbind,
|
.unbind = composite_unbind,
|
||||||
|
|
|
@ -36,7 +36,7 @@ extern struct usb_function_driver ep0_driver;
|
||||||
int registered_functions;
|
int registered_functions;
|
||||||
int registered_devices;
|
int registered_devices;
|
||||||
|
|
||||||
char *usbd_device_events[] = {
|
__maybe_unused static char *usbd_device_events[] = {
|
||||||
"DEVICE_UNKNOWN",
|
"DEVICE_UNKNOWN",
|
||||||
"DEVICE_INIT",
|
"DEVICE_INIT",
|
||||||
"DEVICE_CREATE",
|
"DEVICE_CREATE",
|
||||||
|
@ -56,52 +56,15 @@ char *usbd_device_events[] = {
|
||||||
"DEVICE_FUNCTION_PRIVATE",
|
"DEVICE_FUNCTION_PRIVATE",
|
||||||
};
|
};
|
||||||
|
|
||||||
char *usbd_device_states[] = {
|
__maybe_unused static char *usbd_device_status[] = {
|
||||||
"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[] = {
|
|
||||||
"USBD_OPENING",
|
"USBD_OPENING",
|
||||||
"USBD_OK",
|
"USBD_OK",
|
||||||
"USBD_SUSPENDED",
|
"USBD_SUSPENDED",
|
||||||
"USBD_CLOSING",
|
"USBD_CLOSING",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define USBD_DEVICE_STATUS(x) (((unsigned int)x <= USBD_CLOSING) ? usbd_device_status[x] : "UNKNOWN")
|
||||||
|
|
||||||
|
|
||||||
/* Descriptor support functions ************************************************************** */
|
/* Descriptor support functions ************************************************************** */
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,52 @@
|
||||||
#define dbg_ep0(lvl,fmt,args...)
|
#define dbg_ep0(lvl,fmt,args...)
|
||||||
#endif
|
#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 ********************************************************************* */
|
/* EP0 Configuration Set ********************************************************************* */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,10 @@ static int ep_matches(
|
||||||
size = 64;
|
size = 64;
|
||||||
put_unaligned(cpu_to_le16(size), &desc->wMaxPacketSize);
|
put_unaligned(cpu_to_le16(size), &desc->wMaxPacketSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gadget->ops->ep_conf)
|
||||||
|
return gadget->ops->ep_conf(gadget, ep, desc);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,6 +262,7 @@ struct usb_ep *usb_ep_autoconfig(
|
||||||
ep = find_ep(gadget, "ep1-bulk");
|
ep = find_ep(gadget, "ep1-bulk");
|
||||||
if (ep && ep_matches(gadget, ep, desc))
|
if (ep && ep_matches(gadget, ep, desc))
|
||||||
return ep;
|
return ep;
|
||||||
|
#ifndef CONFIG_SPL_BUILD
|
||||||
} else if (gadget_is_dwc3(gadget)) {
|
} else if (gadget_is_dwc3(gadget)) {
|
||||||
const char *name = NULL;
|
const char *name = NULL;
|
||||||
/*
|
/*
|
||||||
|
@ -280,6 +285,7 @@ struct usb_ep *usb_ep_autoconfig(
|
||||||
ep = find_ep(gadget, name);
|
ep = find_ep(gadget, name);
|
||||||
if (ep && ep_matches(gadget, ep, desc))
|
if (ep && ep_matches(gadget, ep, desc))
|
||||||
return ep;
|
return ep;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gadget->ops->match_ep)
|
if (gadget->ops->match_ep)
|
||||||
|
|
|
@ -46,6 +46,25 @@ struct f_fastboot {
|
||||||
struct usb_request *in_req, *out_req;
|
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)
|
static inline struct f_fastboot *func_to_fastboot(struct usb_function *f)
|
||||||
{
|
{
|
||||||
return container_of(f, struct f_fastboot, usb_function);
|
return container_of(f, struct f_fastboot, usb_function);
|
||||||
|
@ -109,10 +128,45 @@ static struct usb_descriptor_header *fb_hs_function[] = {
|
||||||
NULL,
|
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 *
|
static struct usb_endpoint_descriptor *
|
||||||
fb_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
|
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)
|
if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
|
||||||
return hs;
|
return hs;
|
||||||
return fs;
|
return fs;
|
||||||
|
@ -161,6 +215,19 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
|
||||||
return id;
|
return id;
|
||||||
interface_desc.bInterfaceNumber = 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);
|
id = usb_string_id(c->cdev);
|
||||||
if (id < 0)
|
if (id < 0)
|
||||||
return id;
|
return id;
|
||||||
|
@ -187,6 +254,12 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
|
||||||
f->hs_descriptors = fb_hs_function;
|
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#");
|
s = env_get("serial#");
|
||||||
if (s)
|
if (s)
|
||||||
g_dnl_set_serialnumber((char *)s);
|
g_dnl_set_serialnumber((char *)s);
|
||||||
|
@ -196,6 +269,8 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
|
||||||
|
|
||||||
static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
|
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));
|
memset(fastboot_func, 0, sizeof(*fastboot_func));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,7 +324,7 @@ static int fastboot_set_alt(struct usb_function *f,
|
||||||
debug("%s: func: %s intf: %d alt: %d\n",
|
debug("%s: func: %s intf: %d alt: %d\n",
|
||||||
__func__, f->name, interface, alt);
|
__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);
|
ret = usb_ep_enable(f_fb->out_ep, d);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
puts("failed to enable out ep\n");
|
puts("failed to enable out ep\n");
|
||||||
|
@ -264,7 +339,7 @@ static int fastboot_set_alt(struct usb_function *f,
|
||||||
}
|
}
|
||||||
f_fb->out_req->complete = rx_handler_command;
|
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);
|
ret = usb_ep_enable(f_fb->in_ep, d);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
puts("failed to enable in ep\n");
|
puts("failed to enable in ep\n");
|
||||||
|
@ -315,7 +390,7 @@ static int fastboot_add(struct usb_configuration *c)
|
||||||
status = usb_add_function(c, &f_fb->usb_function);
|
status = usb_add_function(c, &f_fb->usb_function);
|
||||||
if (status) {
|
if (status) {
|
||||||
free(f_fb);
|
free(f_fb);
|
||||||
fastboot_func = f_fb;
|
fastboot_func = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
@ -352,7 +427,7 @@ static unsigned int rx_bytes_expected(struct usb_ep *ep)
|
||||||
{
|
{
|
||||||
int rx_remain = fastboot_data_remaining();
|
int rx_remain = fastboot_data_remaining();
|
||||||
unsigned int rem;
|
unsigned int rem;
|
||||||
unsigned int maxpacket = ep->maxpacket;
|
unsigned int maxpacket = usb_endpoint_maxp(ep->desc);
|
||||||
|
|
||||||
if (rx_remain <= 0)
|
if (rx_remain <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -110,7 +110,7 @@ struct f_rockusb *get_rkusb(void)
|
||||||
if (!f_rkusb) {
|
if (!f_rkusb) {
|
||||||
f_rkusb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_rkusb));
|
f_rkusb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_rkusb));
|
||||||
if (!f_rkusb)
|
if (!f_rkusb)
|
||||||
return 0;
|
return NULL;
|
||||||
|
|
||||||
rockusb_func = f_rkusb;
|
rockusb_func = f_rkusb;
|
||||||
memset(f_rkusb, 0, sizeof(*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,
|
f_rkusb->buf_head = memalign(CONFIG_SYS_CACHELINE_SIZE,
|
||||||
RKUSB_BUF_SIZE);
|
RKUSB_BUF_SIZE);
|
||||||
if (!f_rkusb->buf_head)
|
if (!f_rkusb->buf_head)
|
||||||
return 0;
|
return NULL;
|
||||||
|
|
||||||
f_rkusb->buf = f_rkusb->buf_head;
|
f_rkusb->buf = f_rkusb->buf_head;
|
||||||
memset(f_rkusb->buf_head, 0, RKUSB_BUF_SIZE);
|
memset(f_rkusb->buf_head, 0, RKUSB_BUF_SIZE);
|
||||||
|
@ -309,8 +309,9 @@ static int rockusb_add(struct usb_configuration *c)
|
||||||
|
|
||||||
status = usb_add_function(c, &f_rkusb->usb_function);
|
status = usb_add_function(c, &f_rkusb->usb_function);
|
||||||
if (status) {
|
if (status) {
|
||||||
|
free(f_rkusb->buf_head);
|
||||||
free(f_rkusb);
|
free(f_rkusb);
|
||||||
rockusb_func = f_rkusb;
|
rockusb_func = NULL;
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <linux/usb/cdc.h>
|
#include <linux/usb/cdc.h>
|
||||||
#include <g_dnl.h>
|
#include <g_dnl.h>
|
||||||
#include <dfu.h>
|
#include <dfu.h>
|
||||||
|
#include <thor.h>
|
||||||
|
|
||||||
#include "f_thor.h"
|
#include "f_thor.h"
|
||||||
|
|
||||||
|
@ -266,8 +267,8 @@ static long long int process_rqt_download(const struct rqt_box *rqt)
|
||||||
|
|
||||||
switch (rqt->rqt_data) {
|
switch (rqt->rqt_data) {
|
||||||
case RQT_DL_INIT:
|
case RQT_DL_INIT:
|
||||||
thor_file_size = (unsigned long long int)rqt->int_data[0] +
|
thor_file_size = (uint64_t)(uint32_t)rqt->int_data[0] +
|
||||||
(((unsigned long long int)rqt->int_data[1])
|
(((uint64_t)(uint32_t)rqt->int_data[1])
|
||||||
<< 32);
|
<< 32);
|
||||||
debug("INIT: total %llu bytes\n", thor_file_size);
|
debug("INIT: total %llu bytes\n", thor_file_size);
|
||||||
break;
|
break;
|
||||||
|
@ -280,8 +281,8 @@ static long long int process_rqt_download(const struct rqt_box *rqt)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
thor_file_size = (unsigned long long int)rqt->int_data[1] +
|
thor_file_size = (uint64_t)(uint32_t)rqt->int_data[1] +
|
||||||
(((unsigned long long int)rqt->int_data[2])
|
(((uint64_t)(uint32_t)rqt->int_data[2])
|
||||||
<< 32);
|
<< 32);
|
||||||
memcpy(f_name, rqt->str_data[0], F_NAME_BUF_SIZE);
|
memcpy(f_name, rqt->str_data[0], F_NAME_BUF_SIZE);
|
||||||
f_name[F_NAME_BUF_SIZE] = '\0';
|
f_name[F_NAME_BUF_SIZE] = '\0';
|
||||||
|
@ -735,6 +736,8 @@ int thor_handle(void)
|
||||||
printf("%s: No data received!\n", __func__);
|
printf("%s: No data received!\n", __func__);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (dfu_reinit_needed)
|
||||||
|
return THOR_DFU_REINIT_NEEDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -286,6 +286,7 @@ static struct usb_composite_driver g_dnl_driver = {
|
||||||
.name = NULL,
|
.name = NULL,
|
||||||
.dev = &device_desc,
|
.dev = &device_desc,
|
||||||
.strings = g_dnl_composite_strings,
|
.strings = g_dnl_composite_strings,
|
||||||
|
.max_speed = USB_SPEED_SUPER,
|
||||||
|
|
||||||
.bind = g_dnl_bind,
|
.bind = g_dnl_bind,
|
||||||
.unbind = g_dnl_unbind,
|
.unbind = g_dnl_unbind,
|
||||||
|
|
123
drivers/usb/gadget/u_os_desc.h
Normal file
123
drivers/usb/gadget/u_os_desc.h
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* u_os_desc.h
|
||||||
|
*
|
||||||
|
* Utility definitions for "OS Descriptors" support
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
||||||
|
* http://www.samsung.com
|
||||||
|
*
|
||||||
|
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __U_OS_DESC_H__
|
||||||
|
#define __U_OS_DESC_H__
|
||||||
|
|
||||||
|
#include <linux/utf.h>
|
||||||
|
|
||||||
|
#define USB_EXT_PROP_DW_SIZE 0
|
||||||
|
#define USB_EXT_PROP_DW_PROPERTY_DATA_TYPE 4
|
||||||
|
#define USB_EXT_PROP_W_PROPERTY_NAME_LENGTH 8
|
||||||
|
#define USB_EXT_PROP_B_PROPERTY_NAME 10
|
||||||
|
#define USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH 10
|
||||||
|
#define USB_EXT_PROP_B_PROPERTY_DATA 14
|
||||||
|
|
||||||
|
#define USB_EXT_PROP_RESERVED 0
|
||||||
|
#define USB_EXT_PROP_UNICODE 1
|
||||||
|
#define USB_EXT_PROP_UNICODE_ENV 2
|
||||||
|
#define USB_EXT_PROP_BINARY 3
|
||||||
|
#define USB_EXT_PROP_LE32 4
|
||||||
|
#define USB_EXT_PROP_BE32 5
|
||||||
|
#define USB_EXT_PROP_UNICODE_LINK 6
|
||||||
|
#define USB_EXT_PROP_UNICODE_MULTI 7
|
||||||
|
|
||||||
|
static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset)
|
||||||
|
{
|
||||||
|
return buf + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 *usb_ext_prop_size_ptr(u8 *buf)
|
||||||
|
{
|
||||||
|
return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 *usb_ext_prop_type_ptr(u8 *buf)
|
||||||
|
{
|
||||||
|
return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf)
|
||||||
|
{
|
||||||
|
return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 *usb_ext_prop_name_ptr(u8 *buf)
|
||||||
|
{
|
||||||
|
return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off)
|
||||||
|
{
|
||||||
|
return __usb_ext_prop_ptr(buf,
|
||||||
|
USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off)
|
||||||
|
{
|
||||||
|
return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void usb_ext_prop_put_size(u8 *buf, int dw_size)
|
||||||
|
{
|
||||||
|
put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void usb_ext_prop_put_type(u8 *buf, int type)
|
||||||
|
{
|
||||||
|
put_unaligned_le32(type, usb_ext_prop_type_ptr(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf));
|
||||||
|
memset(usb_ext_prop_name_ptr(buf), 0, 2 * strlen(name));
|
||||||
|
result = utf8_to_utf16le(name, (__le16 *)usb_ext_prop_name_ptr(buf),
|
||||||
|
strlen(name));
|
||||||
|
if (result < 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]);
|
||||||
|
|
||||||
|
return pnl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const char *data,
|
||||||
|
int data_len)
|
||||||
|
{
|
||||||
|
put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
|
||||||
|
memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string,
|
||||||
|
int data_len)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
|
||||||
|
memset(usb_ext_prop_data_ptr(buf, pnl), 0, 2 * (data_len >> 1));
|
||||||
|
result = utf8_to_utf16le(string, (__le16 *) usb_ext_prop_data_ptr(buf, pnl),
|
||||||
|
data_len >> 1);
|
||||||
|
if (result < 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
put_unaligned_le16(0,
|
||||||
|
&buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]);
|
||||||
|
|
||||||
|
return data_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __U_OS_DESC_H__ */
|
|
@ -10,79 +10,7 @@
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/usb/ch9.h>
|
#include <linux/usb/ch9.h>
|
||||||
#include <linux/usb/gadget.h>
|
#include <linux/usb/gadget.h>
|
||||||
|
#include <linux/utf.h>
|
||||||
#include <asm/unaligned.h>
|
|
||||||
|
|
||||||
|
|
||||||
static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
u8 c;
|
|
||||||
u16 uchar;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* this insists on correct encodings, though not minimal ones.
|
|
||||||
* BUT it currently rejects legit 4-byte UTF-8 code points,
|
|
||||||
* which need surrogate pairs. (Unicode 3.1 can use them.)
|
|
||||||
*/
|
|
||||||
while (len != 0 && (c = (u8) *s++) != 0) {
|
|
||||||
if ((c & 0x80)) {
|
|
||||||
/*
|
|
||||||
* 2-byte sequence:
|
|
||||||
* 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
|
|
||||||
*/
|
|
||||||
if ((c & 0xe0) == 0xc0) {
|
|
||||||
uchar = (c & 0x1f) << 6;
|
|
||||||
|
|
||||||
c = (u8) *s++;
|
|
||||||
if ((c & 0xc0) != 0x80)
|
|
||||||
goto fail;
|
|
||||||
c &= 0x3f;
|
|
||||||
uchar |= c;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 3-byte sequence (most CJKV characters):
|
|
||||||
* zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
|
|
||||||
*/
|
|
||||||
} else if ((c & 0xf0) == 0xe0) {
|
|
||||||
uchar = (c & 0x0f) << 12;
|
|
||||||
|
|
||||||
c = (u8) *s++;
|
|
||||||
if ((c & 0xc0) != 0x80)
|
|
||||||
goto fail;
|
|
||||||
c &= 0x3f;
|
|
||||||
uchar |= c << 6;
|
|
||||||
|
|
||||||
c = (u8) *s++;
|
|
||||||
if ((c & 0xc0) != 0x80)
|
|
||||||
goto fail;
|
|
||||||
c &= 0x3f;
|
|
||||||
uchar |= c;
|
|
||||||
|
|
||||||
/* no bogus surrogates */
|
|
||||||
if (0xd800 <= uchar && uchar <= 0xdfff)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 4-byte sequence (surrogate pairs, currently rare):
|
|
||||||
* 11101110wwwwzzzzyy + 110111yyyyxxxxxx
|
|
||||||
* = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
|
|
||||||
* (uuuuu = wwww + 1)
|
|
||||||
* FIXME accept the surrogate code points (only)
|
|
||||||
*/
|
|
||||||
} else
|
|
||||||
goto fail;
|
|
||||||
} else
|
|
||||||
uchar = c;
|
|
||||||
put_unaligned_le16(uchar, cp++);
|
|
||||||
count++;
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
fail:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* usb_gadget_get_string - fill out a string descriptor
|
* usb_gadget_get_string - fill out a string descriptor
|
||||||
|
|
21
include/bcb.h
Normal file
21
include/bcb.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Eugeniu Rosca <rosca.eugeniu@gmail.com>
|
||||||
|
*
|
||||||
|
* Android Bootloader Control Block Header
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __BCB_H__
|
||||||
|
#define __BCB_H__
|
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(CMD_BCB)
|
||||||
|
int bcb_write_reboot_reason(int devnum, char *partp, const char *reasonp);
|
||||||
|
#else
|
||||||
|
#include <linux/errno.h>
|
||||||
|
static inline int bcb_write_reboot_reason(int devnum, char *partp, const char *reasonp)
|
||||||
|
{
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __BCB_H__ */
|
|
@ -33,6 +33,8 @@ enum dfu_layout {
|
||||||
DFU_FS_EXT3,
|
DFU_FS_EXT3,
|
||||||
DFU_FS_EXT4,
|
DFU_FS_EXT4,
|
||||||
DFU_RAM_ADDR,
|
DFU_RAM_ADDR,
|
||||||
|
DFU_SKIP,
|
||||||
|
DFU_SCRIPT,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum dfu_op {
|
enum dfu_op {
|
||||||
|
@ -496,6 +498,8 @@ static inline int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern bool dfu_reinit_needed;
|
||||||
|
|
||||||
#if CONFIG_IS_ENABLED(DFU_WRITE_ALT)
|
#if CONFIG_IS_ENABLED(DFU_WRITE_ALT)
|
||||||
/**
|
/**
|
||||||
* dfu_write_by_name() - write data to DFU medium
|
* dfu_write_by_name() - write data to DFU medium
|
||||||
|
|
|
@ -37,6 +37,53 @@
|
||||||
|
|
||||||
struct usb_configuration;
|
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
|
* struct usb_function - describes one function of a configuration
|
||||||
* @name: For diagnostics, identifies the function.
|
* @name: For diagnostics, identifies the function.
|
||||||
|
@ -50,6 +97,10 @@ struct usb_configuration;
|
||||||
* the function will not be available at high speed.
|
* the function will not be available at high speed.
|
||||||
* @config: assigned when @usb_add_function() is called; this is the
|
* @config: assigned when @usb_add_function() is called; this is the
|
||||||
* configuration with which this function is associated.
|
* 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
|
* @bind: Before the gadget can register, all of its functions bind() to the
|
||||||
* available resources including string and interface identifiers used
|
* available resources including string and interface identifiers used
|
||||||
* in interface or class descriptors; endpoints; I/O buffers; and so on.
|
* in interface or class descriptors; endpoints; I/O buffers; and so on.
|
||||||
|
@ -95,9 +146,13 @@ struct usb_function {
|
||||||
struct usb_gadget_strings **strings;
|
struct usb_gadget_strings **strings;
|
||||||
struct usb_descriptor_header **descriptors;
|
struct usb_descriptor_header **descriptors;
|
||||||
struct usb_descriptor_header **hs_descriptors;
|
struct usb_descriptor_header **hs_descriptors;
|
||||||
|
struct usb_descriptor_header **ss_descriptors;
|
||||||
|
|
||||||
struct usb_configuration *config;
|
struct usb_configuration *config;
|
||||||
|
|
||||||
|
struct usb_os_desc_table *os_desc_table;
|
||||||
|
unsigned os_desc_n;
|
||||||
|
|
||||||
/* REVISIT: bind() functions can be marked __init, which
|
/* REVISIT: bind() functions can be marked __init, which
|
||||||
* makes trouble for section mismatch analysis. See if
|
* makes trouble for section mismatch analysis. See if
|
||||||
* we can't restructure things to avoid mismatching.
|
* we can't restructure things to avoid mismatching.
|
||||||
|
@ -225,6 +280,7 @@ struct usb_configuration {
|
||||||
u8 next_interface_id;
|
u8 next_interface_id;
|
||||||
unsigned highspeed:1;
|
unsigned highspeed:1;
|
||||||
unsigned fullspeed:1;
|
unsigned fullspeed:1;
|
||||||
|
unsigned superspeed:1;
|
||||||
struct usb_function *interface[MAX_CONFIG_INTERFACES];
|
struct usb_function *interface[MAX_CONFIG_INTERFACES];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -238,6 +294,7 @@ int usb_add_config(struct usb_composite_dev *,
|
||||||
* identifiers.
|
* identifiers.
|
||||||
* @strings: tables of strings, keyed by identifiers assigned during bind()
|
* @strings: tables of strings, keyed by identifiers assigned during bind()
|
||||||
* and language IDs provided in control requests
|
* 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
|
* @bind: (REQUIRED) Used to allocate resources that are shared across the
|
||||||
* whole device, such as string IDs, and add its configurations using
|
* whole device, such as string IDs, and add its configurations using
|
||||||
* @usb_add_config(). This may fail by returning a negative errno
|
* @usb_add_config(). This may fail by returning a negative errno
|
||||||
|
@ -265,6 +322,7 @@ struct usb_composite_driver {
|
||||||
const char *name;
|
const char *name;
|
||||||
const struct usb_device_descriptor *dev;
|
const struct usb_device_descriptor *dev;
|
||||||
struct usb_gadget_strings **strings;
|
struct usb_gadget_strings **strings;
|
||||||
|
enum usb_device_speed max_speed;
|
||||||
|
|
||||||
/* REVISIT: bind() functions can be marked __init, which
|
/* REVISIT: bind() functions can be marked __init, which
|
||||||
* makes trouble for section mismatch analysis. See if
|
* makes trouble for section mismatch analysis. See if
|
||||||
|
@ -284,13 +342,20 @@ struct usb_composite_driver {
|
||||||
extern int usb_composite_register(struct usb_composite_driver *);
|
extern int usb_composite_register(struct usb_composite_driver *);
|
||||||
extern void usb_composite_unregister(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
|
* struct usb_composite_device - represents one composite usb gadget
|
||||||
* @gadget: read-only, abstracts the gadget's usb peripheral controller
|
* @gadget: read-only, abstracts the gadget's usb peripheral controller
|
||||||
* @req: used for control responses; buffer is pre-allocated
|
* @req: used for control responses; buffer is pre-allocated
|
||||||
* @bufsiz: size of buffer pre-allocated in @req
|
* @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
|
* @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
|
* One of these devices is allocated and initialized before the
|
||||||
* associated device driver's bind() is called.
|
* associated device driver's bind() is called.
|
||||||
|
@ -324,6 +389,12 @@ struct usb_composite_dev {
|
||||||
|
|
||||||
struct usb_configuration *config;
|
struct usb_configuration *config;
|
||||||
|
|
||||||
|
/* OS String is a custom (yet popular) extension to the USB standard. */
|
||||||
|
u8 qw_sign[OS_STRING_QW_SIGN_LEN];
|
||||||
|
u8 b_vendor_code;
|
||||||
|
struct usb_configuration *os_desc_config;
|
||||||
|
unsigned int use_os_string:1;
|
||||||
|
|
||||||
/* private: */
|
/* private: */
|
||||||
/* internals */
|
/* internals */
|
||||||
unsigned int suspended:1;
|
unsigned int suspended:1;
|
||||||
|
|
|
@ -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;
|
||||||
struct usb_gadget_driver;
|
struct usb_gadget_driver;
|
||||||
|
|
||||||
|
@ -464,12 +469,16 @@ struct usb_gadget_ops {
|
||||||
int (*pullup) (struct usb_gadget *, int is_on);
|
int (*pullup) (struct usb_gadget *, int is_on);
|
||||||
int (*ioctl)(struct usb_gadget *,
|
int (*ioctl)(struct usb_gadget *,
|
||||||
unsigned code, unsigned long param);
|
unsigned code, unsigned long param);
|
||||||
|
void (*get_config_params)(struct usb_dcd_config_params *);
|
||||||
int (*udc_start)(struct usb_gadget *,
|
int (*udc_start)(struct usb_gadget *,
|
||||||
struct usb_gadget_driver *);
|
struct usb_gadget_driver *);
|
||||||
int (*udc_stop)(struct usb_gadget *);
|
int (*udc_stop)(struct usb_gadget *);
|
||||||
struct usb_ep *(*match_ep)(struct usb_gadget *,
|
struct usb_ep *(*match_ep)(struct usb_gadget *,
|
||||||
struct usb_endpoint_descriptor *,
|
struct usb_endpoint_descriptor *,
|
||||||
struct usb_ss_ep_comp_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,
|
void (*udc_set_speed)(struct usb_gadget *gadget,
|
||||||
enum usb_device_speed);
|
enum usb_device_speed);
|
||||||
};
|
};
|
||||||
|
|
75
include/linux/utf.h
Normal file
75
include/linux/utf.h
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#ifndef _LINUX_UTF_H
|
||||||
|
#define _LINUX_UTF_H
|
||||||
|
|
||||||
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
|
static inline int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
u8 c;
|
||||||
|
u16 uchar;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this insists on correct encodings, though not minimal ones.
|
||||||
|
* BUT it currently rejects legit 4-byte UTF-8 code points,
|
||||||
|
* which need surrogate pairs. (Unicode 3.1 can use them.)
|
||||||
|
*/
|
||||||
|
while (len != 0 && (c = (u8) *s++) != 0) {
|
||||||
|
if ((c & 0x80)) {
|
||||||
|
/*
|
||||||
|
* 2-byte sequence:
|
||||||
|
* 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
|
||||||
|
*/
|
||||||
|
if ((c & 0xe0) == 0xc0) {
|
||||||
|
uchar = (c & 0x1f) << 6;
|
||||||
|
|
||||||
|
c = (u8) *s++;
|
||||||
|
if ((c & 0xc0) != 0x80)
|
||||||
|
goto fail;
|
||||||
|
c &= 0x3f;
|
||||||
|
uchar |= c;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 3-byte sequence (most CJKV characters):
|
||||||
|
* zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
|
||||||
|
*/
|
||||||
|
} else if ((c & 0xf0) == 0xe0) {
|
||||||
|
uchar = (c & 0x0f) << 12;
|
||||||
|
|
||||||
|
c = (u8) *s++;
|
||||||
|
if ((c & 0xc0) != 0x80)
|
||||||
|
goto fail;
|
||||||
|
c &= 0x3f;
|
||||||
|
uchar |= c << 6;
|
||||||
|
|
||||||
|
c = (u8) *s++;
|
||||||
|
if ((c & 0xc0) != 0x80)
|
||||||
|
goto fail;
|
||||||
|
c &= 0x3f;
|
||||||
|
uchar |= c;
|
||||||
|
|
||||||
|
/* no bogus surrogates */
|
||||||
|
if (0xd800 <= uchar && uchar <= 0xdfff)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 4-byte sequence (surrogate pairs, currently rare):
|
||||||
|
* 11101110wwwwzzzzyy + 110111yyyyxxxxxx
|
||||||
|
* = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
|
||||||
|
* (uuuuu = wwww + 1)
|
||||||
|
* FIXME accept the surrogate code points (only)
|
||||||
|
*/
|
||||||
|
} else
|
||||||
|
goto fail;
|
||||||
|
} else
|
||||||
|
uchar = c;
|
||||||
|
put_unaligned_le16(uchar, cp++);
|
||||||
|
count++;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
fail:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _LINUX_UTF_H */
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
#include <linux/usb/composite.h>
|
#include <linux/usb/composite.h>
|
||||||
|
|
||||||
|
#define THOR_DFU_REINIT_NEEDED 0xFFFFFFFE
|
||||||
|
|
||||||
int thor_handle(void);
|
int thor_handle(void);
|
||||||
int thor_init(void);
|
int thor_init(void);
|
||||||
int thor_add(struct usb_configuration *c);
|
int thor_add(struct usb_configuration *c);
|
||||||
|
|
|
@ -264,8 +264,6 @@ struct usb_bus_instance;
|
||||||
#define USB_REQ_SET_INTERFACE 0x0B
|
#define USB_REQ_SET_INTERFACE 0x0B
|
||||||
#define USB_REQ_SYNCH_FRAME 0x0C
|
#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
|
* HID requests
|
||||||
*/
|
*/
|
||||||
|
@ -332,9 +330,6 @@ struct usb_bus_instance;
|
||||||
#define USB_DESCRIPTOR_TYPE_HID 0x21
|
#define USB_DESCRIPTOR_TYPE_HID 0x21
|
||||||
#define USB_DESCRIPTOR_TYPE_REPORT 0x22
|
#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
|
* standard feature selectors
|
||||||
*/
|
*/
|
||||||
|
@ -388,8 +383,6 @@ typedef enum usb_device_state {
|
||||||
STATE_UNKNOWN, /* destroyed */
|
STATE_UNKNOWN, /* destroyed */
|
||||||
} usb_device_state_t;
|
} usb_device_state_t;
|
||||||
|
|
||||||
#define USBD_DEVICE_STATE(x) (((unsigned int)x <= STATE_UNKNOWN) ? usbd_device_states[x] : "UNKNOWN")
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Device status
|
* Device status
|
||||||
*
|
*
|
||||||
|
@ -402,8 +395,6 @@ typedef enum usb_device_status {
|
||||||
USBD_CLOSING, /* we are currently closing */
|
USBD_CLOSING, /* we are currently closing */
|
||||||
} usb_device_status_t;
|
} usb_device_status_t;
|
||||||
|
|
||||||
#define USBD_DEVICE_STATUS(x) (((unsigned int)x <= USBD_CLOSING) ? usbd_device_status[x] : "UNKNOWN")
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Device Events
|
* 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_link_init (urb_link * ul);
|
||||||
void urb_detach (struct urb *urb);
|
void urb_detach (struct urb *urb);
|
||||||
urb_link *first_urb_link (urb_link * hd);
|
urb_link *first_urb_link (urb_link * hd);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user