--- /dev/null
+/*
+ * usbdown.c -- Download the image from USB storage
+ *
+ * Copyright (C) 2016 Jaehoon Chung <jh80.chung@samsung.com>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <command.h>
+#include <console.h>
+#include <dm.h>
+#include <memalign.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include <linux/list.h>
+#include <part.h>
+#include <usb.h>
+#include <mmc.h>
+#include <dfu.h>
+#include <fs.h>
+
+#define MAX_BLK_COUNT 0x2000000 /* 65536 * 512 */
+
+#ifdef CONFIG_USB_STORAGE
+static int usb_stor_curr_dev = -1; /* current device */
+#endif
+
+static LIST_HEAD(usbdown_list);
+
+static int alt_num_count;
+static int is_config;
+extern char usb_started;
+
+#define NAME_SIZE 32
+enum usbdown_layout {
+ RAW_ADDR = 1,
+ FS_FAT,
+ FS_EXT2,
+ FS_EXT3,
+ FS_EXT4,
+ RAM_ADDR,
+};
+
+struct usbdown_entity {
+ int dev_num;
+ char name[NAME_SIZE];
+ int layout;
+
+ /* RAW programming */
+ unsigned int lba_start;
+ unsigned int lba_size;
+ unsigned int lba_blk_size;
+
+ int alt_num;
+
+ /* eMMC HW partition access */
+ int hw_partition;
+
+ /* FAT/EXT */
+ unsigned int dev;
+ unsigned int part;
+
+ struct list_head list;
+};
+
+static int find_alt_num(const char *s)
+{
+ int i = 0;
+
+ for (; *s; s++)
+ if (*s == ';')
+ i++;
+
+ return ++i;
+}
+
+struct usbdown_entity *usbdown_get_entity(int alt)
+{
+ struct usbdown_entity *usb;
+
+ list_for_each_entry(usb, &usbdown_list, list) {
+ if (usb->alt_num == alt)
+ return usb;
+ }
+
+ return NULL;
+}
+
+int usbdown_get_alt(const char *name)
+{
+ struct usbdown_entity *usb;
+ const char *argv[3];
+ const char **parg = argv;
+ char *name_bkp;
+ const char *find_name;
+ char *str;
+
+ if (name[0] == '/') {
+ name_bkp = strdup(name);
+ for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) {
+ *parg = strsep(&name_bkp, "/");
+ if (*parg == NULL) {
+ error("Invalid number of arguments.\n");
+ return -ENODEV;
+ }
+ }
+
+ find_name = argv[2];
+ } else {
+ find_name = name;
+ }
+
+ list_for_each_entry(usb, &usbdown_list, list) {
+ if (usb->name[0] != '/') {
+ if (!strncmp(usb->name, find_name, strlen(usb->name)))
+ return usb->alt_num;
+ } else {
+ str = strstr(usb->name, find_name);
+ if (!str)
+ continue;
+ if (strlen(usb->name) ==
+ ((str - usb->name) + strlen(find_name)))
+ return usb->alt_num;
+ }
+ }
+
+ return -ENODEV;
+}
+
+static int usb_fill_entity(struct usbdown_entity *usb, char *s, int alt_num,
+ char *interface, char *devstr)
+{
+ const char *entity_type;
+ const char *argv[3];
+ const char **parg = argv;
+ size_t second_arg;
+ size_t third_arg;
+ struct mmc *mmc;
+
+ mmc = find_mmc_device(0);
+ if (mmc == NULL) {
+ error("Couldn't find MMC device no. 0.\n");
+ return -ENODEV;
+ }
+
+ if (mmc_init(mmc)) {
+ error("Couldn't init MMC device.\n");
+ return -ENODEV;
+ }
+
+ for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) {
+ *parg = strsep(&s, " ");
+ if (*parg == NULL) {
+ error("Invalid number of arguments.\n");
+ return -ENODEV;
+ }
+ }
+
+ entity_type = argv[0];
+ second_arg = simple_strtoul(argv[1], NULL, 0);
+ third_arg = simple_strtoul(argv[2], NULL, 0);
+ usb->alt_num = alt_num;
+
+ if (!strncmp(entity_type, "raw", 3)) {
+ usb->layout = RAW_ADDR;
+ usb->lba_start = second_arg;
+ usb->lba_size = third_arg;
+ usb->lba_blk_size = 512; /* Set to 512 bytes */
+
+ if (s && !strcmp(strsep(&s, " "), "mmcpart"))
+ usb->hw_partition = simple_strtoul(s, NULL, 0);
+
+ } else if (!strncmp(entity_type, "part", 4)) {
+ disk_partition_t partinfo;
+ block_dev_desc_t *blk_dev = &mmc->block_dev;
+ int mmcdev = second_arg;
+ int mmcpart = third_arg;
+ int offset = 0;
+
+ if (get_partition_info(blk_dev, mmcpart, &partinfo) != 0) {
+ error("Couldn't find part #%d on mmc device #%d\n",
+ mmcpart, mmcdev);
+ return -ENODEV;
+ }
+
+ /*
+ * Check for an extra entry at dfu_alt_info env variable
+ * specifying the mmc HW defined partition number
+ */
+ if (s && !strcmp(strsep(&s, " "), "offset"))
+ offset = simple_strtoul(s, NULL, 0);
+
+ usb->layout = RAW_ADDR;
+ usb->lba_start = partinfo.start + offset;
+ usb->lba_size = partinfo.size - offset;
+ usb->lba_blk_size = partinfo.blksz;
+
+ } else if (!strncmp(entity_type, "fat", 3)) {
+ usb->layout = FS_FAT;
+ usb->dev = second_arg;
+ usb->part = third_arg;
+ } else if (!strncmp(entity_type, "ext4", 4)) {
+ usb->layout = FS_EXT4;
+ usb->dev = second_arg;
+ usb->part = third_arg;
+ } else {
+ printf("layout (%s) not supported!\n", entity_type);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void fill_entity_usbdown(char *interface, char *devstr)
+{
+ struct usbdown_entity *usbdown;
+ int alt_num, i, ret, offset = 0;
+ char *alt_setting, *alt_sep, *s, *st, *setting;
+ size_t buf_size = CONFIG_SET_DFU_ALT_BUF_LEN;
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, buf_size);
+
+#ifdef CONFIG_SET_DFU_ALT_INFO
+ /* Reuse dfu information. Use the eMMC by default */
+ setenv("dfu_alt_boot", CONFIG_DFU_ALT_BOOT_EMMC);
+ offset = snprintf(buf, buf_size, "%s", CONFIG_DFU_ALT_BOOT_EMMC);
+
+ /* Reset the dfu_alt_boot environment for thor download */
+ setenv("dfu_alt_boot", "Not supported!");
+
+ alt_setting = getenv("dfu_alt_system");
+ if (alt_setting) {
+ if (offset)
+ alt_sep = ";";
+ else
+ alt_sep = "";
+
+ offset += snprintf(buf + offset, buf_size - offset,
+ "%s%s", alt_sep, alt_setting);
+ }
+
+ setenv("dfu_alt_info", buf);
+#endif
+ setting = getenv("dfu_alt_info");
+
+ alt_num = find_alt_num(setting);
+
+ usbdown = calloc(sizeof(*usbdown), alt_num);
+ if (!usbdown) {
+ printf("%s: Failed to allocate.\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < alt_num; i++) {
+ /* Parsing file name */
+ s = strsep(&setting, ";");
+ st = strsep(&s, " ");
+ strcpy(usbdown[i].name, st);
+
+ ret = usb_fill_entity(&usbdown[i], s, alt_num_count,
+ interface, devstr);
+ if (ret) {
+ printf("%s Failed.\n", __func__);
+ return;
+ }
+
+ list_add_tail(&usbdown[i].list, &usbdown_list);
+ alt_num_count++;
+ }
+}
+
+static void do_usb_start(void)
+{
+ bootstage_mark_name(BOOTSTAGE_ID_USB_START, "usb_start");
+
+ if (usb_init() < 0)
+ return;
+
+ /* Driver model will probe the devices as they are found */
+#ifndef CONFIG_DM_USB
+# ifdef CONFIG_USB_STORAGE
+ /* try to recognize storage devices immediately */
+ usb_stor_curr_dev = usb_stor_scan(1);
+# endif
+#endif /* !CONFIG_DM_USB */
+}
+
+int do_update(cmd_tbl_t *cmdtp, int flags, int argc, char * const argv[])
+{
+ struct usbdown_entity *usbdown;
+
+#ifdef CONFIG_USB_STORAGE
+ block_dev_desc_t *stor_dev;
+#endif
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ if (!usb_started) {
+ printf("\nUSB is stopped..starting USB first.\n\n");
+ do_usb_start();
+ }
+
+ if (argc == 4) {
+ int dev = (int)simple_strtoul(argv[1], NULL, 10);
+ char *interface = argv[2];
+ const char *filename = argv[3];
+ loff_t len_read = 0, size = 0, offset = 0;
+ unsigned long addr = CONFIG_SYS_SDRAM_BASE;
+ int alt_num, ret = 0;
+ unsigned long len = 0;
+ struct mmc *mmc;
+ const char *fsname, *opname;
+ char cmd_buf[128];
+ int part_num_bkp = 0;
+ const char *from = "usb";
+
+ /* Default MMC 0 */
+ mmc = find_mmc_device(0);
+ if (!mmc) {
+ error("Device MMC %d - not found!", 0);
+ return -ENODEV;
+ }
+
+ if (mmc_init(mmc)) {
+ error("Couldn't init MMC device.\n");
+ return -ENODEV;
+ }
+
+ /* Used the mmc 0 by default */
+ if (!is_config) {
+ fill_entity_usbdown(interface, "0");
+ is_config = 1;
+
+ printf("USB device %d: ", dev);
+ stor_dev = usb_stor_get_dev(dev);
+ if (stor_dev == NULL) {
+ printf("unknown device\n");
+ return -ENODEV;
+ }
+
+ printf("\n Device %d: ", dev);
+ dev_print(stor_dev);
+ if (stor_dev->type == DEV_TYPE_UNKNOWN)
+ return 1;
+ usb_stor_curr_dev = dev;
+ printf("... is now current device\n");
+ }
+
+ if (fs_set_blk_dev(from, argv[1], FS_TYPE_ANY)) {
+ printf("%d: Unknown block device..\n", __LINE__);
+ return -ENODEV;
+ }
+
+ alt_num = usbdown_get_alt(filename);
+ if (alt_num < 0) {
+ printf("There is no %s information\n", filename);
+ return -EINVAL;
+ }
+
+ ret = fs_size(filename, &size);
+ if (ret < 0) {
+ printf("There is no %s\n", filename);
+ return -EINVAL;
+ }
+
+ usbdown = usbdown_get_entity(alt_num);
+ if (!usbdown) {
+ printf("there is no information for %d\n", alt_num);
+ return -EINVAL;
+ }
+
+ if (usbdown->layout == FS_FAT) {
+ /* loading File */
+ fsname = "fat";
+ opname = "load";
+
+ sprintf(cmd_buf, "%s%s usb %s", fsname,
+ opname, argv[1]);
+ sprintf(cmd_buf + strlen(cmd_buf),
+ " %p", (void *)addr);
+ sprintf(cmd_buf + strlen(cmd_buf),
+ " %s", usbdown->name);
+
+ run_command(cmd_buf, 0);
+
+ /* Write file */
+ opname = "write";
+ sprintf(cmd_buf, "%s%s mmc %d:%d", fsname, opname,
+ usbdown->dev, usbdown->part);
+ sprintf(cmd_buf + strlen(cmd_buf),
+ " %p", (void *)addr);
+ sprintf(cmd_buf + strlen(cmd_buf),
+ " %s", usbdown->name);
+ sprintf(cmd_buf + strlen(cmd_buf),
+ " %llx", size);
+
+ run_command(cmd_buf, 0);
+
+ } else if (usbdown->layout == RAW_ADDR) {
+ if (usbdown->hw_partition >= 0) {
+ part_num_bkp = mmc->block_dev.hwpart;
+ ret = mmc_select_hwpart(usbdown->dev_num,
+ usbdown->hw_partition);
+ if (ret)
+ return ret;
+ }
+
+ while (size > 0) {
+ if (fs_set_blk_dev(from, argv[1],
+ FS_TYPE_ANY)) {
+ printf("Unknown block device..\n");
+ if (usbdown->hw_partition >= 0)
+ mmc_select_hwpart(usbdown->dev_num,
+ part_num_bkp);
+ return -ENODEV;
+ }
+ if (size < MAX_BLK_COUNT)
+ len = size;
+ else
+ len = MAX_BLK_COUNT;
+
+ ret = fs_read(filename, addr, offset, len,
+ &len_read);
+ if (ret < 0) {
+ printf("Failed to read %s file.\n", filename);
+ if (usbdown->hw_partition >= 0)
+ mmc_select_hwpart(usbdown->dev_num,
+ part_num_bkp);
+ return -EIO;
+ }
+
+ mmc->block_dev.block_write(&mmc->block_dev,
+ usbdown->lba_start + offset / 512,
+ len_read / 512, (void *)addr);
+
+ offset += len_read;
+ size -= len_read;
+ }
+ printf("Read %s Total %llu bytes\n", filename, offset);
+ }
+
+ if (usbdown->hw_partition >= 0) {
+ ret = mmc_select_hwpart(usbdown->dev_num, part_num_bkp);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+ }
+
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(update, 5, 1, do_update,
+ "Manually download Images with USB storage for Tizen",
+ "<dev:[part]> <interface> <filename>\n"
+ " (e,g update 0:0 mmc rootfs.img)\n"
+);
+
+/* Only release image files are described */
+static char *name[] = {
+ "u-boot-mmc.bin",
+ "params.bin",
+ "boot.img",
+ "rootfs.img",
+ "system-data.img",
+ "user.img",
+ "modules.img",
+};
+
+int do_usb_down(cmd_tbl_t *cmdtp, int flags, int argc, char * const argv[])
+{
+ if (argc < 1)
+ return CMD_RET_USAGE;
+
+ if (argc == 2) {
+ char cmd_buf[128];
+ const char *interface, *dir, *part, *cmd;
+ int ret, i;
+ int files = sizeof(name) / sizeof(name[0]);
+
+ if (!usb_started) {
+ printf("\nUSB is stopped..starting USB first.\n\n");
+ do_usb_start();
+ }
+
+ /* Use command "update" */
+ cmd = "update";
+
+ /* Fixed direcotry as /updateTizen */
+ dir = "/updateTizen";
+
+ /* interface is fixed "mmc" by default */
+ interface = "mmc";
+
+ /* Checking part 0:0 */
+ part = "0:0";
+ ret = file_exists("usb", part, dir, FS_TYPE_ANY);
+ if (!ret) {
+ part = "0:1";
+ ret = file_exists("usb", part, dir, FS_TYPE_ANY);
+ if (!ret) {
+ printf("Can't find partition or directory\n");
+ return CMD_RET_USAGE;
+ }
+ }
+
+ for (i = 0; i < files; i++) {
+ sprintf(cmd_buf, "%s %s %s %s",
+ cmd, part, interface, dir);
+ sprintf(cmd_buf + strlen(cmd_buf), "/%s", name[i]);
+ run_command(cmd_buf, 0);
+ printf("\n");
+ }
+
+ return 0;
+ }
+
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(usbdown, 2, 1, do_usb_down,
+ "Download automantically all image with USB storage for Tizen",
+ "all\n"
+ " (e,g usbdown all)\n"
+ "Note: All images have to be located in /updateTizen/ directory.\n"
+ " And part number is 0 or 1. - eg sdb or sdb1\n"
+);