cmd: usbdown: support the usbdown from usb storage to mmc 23/87623/2
authorJaehoon Chung <jh80.chung@samsung.com>
Tue, 16 Aug 2016 07:17:06 +0000 (16:17 +0900)
committerJaehoon Chung <jh80.chung@samsung.com>
Wed, 21 Sep 2016 09:38:12 +0000 (18:38 +0900)
This command is to support the download mode from usb storage to mmc
block device. XU4 didn't support the thor download, so this solution
might be replaced to thor download.

* How to use this command
1. Assume that  Block number of eMMC block device is "0"
2. If you want to flash the rootfs.img,
$update 0:0 mmc rootfs.img
- 0:0 -> usb block 0 devices and partitioin 0
  if you just enter "0" instead of "0:0", that check the
  partition 1 by default.
- mmc -> interface to flash the image
- rootfs.img -> filename to flash (directory is "/" by default.)
  If you want to use the directory, can use likes "/tizen/rootfs.img".
  (There is rootfs.img under "tizen" directory.)
3. Use "update all" command
$usbdown all
- directory is "/updateTizen/ by default.
*Recommendation
- Use "usbdown all"

Change-Id: I267c9b871741e9e1f6aef29965f1f846e19864f1
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
cmd/Makefile
cmd/usbdown.c [new file with mode: 0644]

index 40e9c1e..ee76a1d 100644 (file)
@@ -134,6 +134,7 @@ obj-$(CONFIG_CMD_LZMADEC) += lzmadec.o
 endif
 
 obj-$(CONFIG_CMD_USB) += usb.o
+obj-$(CONFIG_CMD_USB) += usbdown.o
 obj-$(CONFIG_CMD_FASTBOOT) += fastboot.o
 obj-$(CONFIG_CMD_FS_UUID) += fs_uuid.o
 
diff --git a/cmd/usbdown.c b/cmd/usbdown.c
new file mode 100644 (file)
index 0000000..6a91174
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * 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"
+);