--- /dev/null
+/*
+ * cmd_nfsdown.c -- Tizen "nfs" Downloader
+ *
+ * Copyright (C) 2017 Jiho Chu <jiho.chu@samsung.com>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <malloc.h>
+#include <common.h>
+#include <errno.h>
+#include <command.h>
+#include <memalign.h>
+#include <net.h>
+#include <usb.h>
+#include <part.h>
+#include <mmc.h>
+
+#define LEN_BUF 128
+#define LEN_NAME 32
+
+#ifdef CONFIG_NFS_DOWNLOAD_ADDR
+#define NFS_DOWNLOAD_ADDR __stringify(CONFIG_NFS_DOWNLOAD_ADDR)
+#else
+#define NFS_DOWNLOAD_ADDR "0x40000000"
+#endif
+
+enum img_type {
+ IMG_TYPE_RAW,
+ IMG_TYPE_PART,
+};
+
+struct img_info {
+ char name[LEN_NAME];
+ enum img_type type;
+
+ /* mmc info */
+ uint hwpart;
+ uint part;
+
+ /* block info */
+ uint lba_start;
+ uint lba_size;
+ uint lba_blk_size;
+
+ struct list_head list;
+};
+
+struct img_comp {
+ char name[LEN_NAME];
+ uint size;
+
+ struct list_head list;
+};
+
+static char *g_update_image_names[] = {
+ "bl1.bin",
+ "bl2.bin",
+ "u-boot-mmc.bin",
+ "tzsw.bin",
+ "params.bin",
+ "boot.img",
+ "rootfs.img",
+ "system-data.img",
+ "user.img",
+ "modules.img"
+};
+
+#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_HOST_ETHER)
+extern char usb_started;
+
+static void do_usb_ether_start(void)
+{
+ bootstage_mark_name(BOOTSTAGE_ID_USB_START, "usb_start");
+ if (usb_init() < 0)
+ return;
+
+ bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start");
+ usb_host_eth_scan(1);
+}
+#endif
+
+#ifdef CONFIG_SET_DFU_ALT_INFO
+#ifdef CONFIG_SET_DFU_ALT_BUF_LEN
+#define DFU_ALT_BUF_LEN CONFIG_SET_DFU_ALT_BUF_LEN
+#else
+#define DFU_ALT_BUF_LEN (SZ_1K)
+#endif
+
+static void set_dfu_alt_info(void)
+{
+ int buf_used;
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, DFU_ALT_BUF_LEN);
+
+ buf_used = snprintf(buf, DFU_ALT_BUF_LEN, "%s",
+ CONFIG_DFU_ALT_BOOT_EMMC);
+
+ buf_used += snprintf(buf + buf_used, DFU_ALT_BUF_LEN, "%s%s",
+ buf_used ? ";" : "", CONFIG_DFU_ALT_SYSTEM);
+
+ if (buf_used <= 0)
+ return;
+
+ setenv("dfu_alt_info", buf);
+
+ return;
+}
+
+#else /* CONFIG_SET_DFU_ALT_INFO */
+
+static void set_dfu_alt_info(void)
+{
+ return;
+}
+
+#endif /* CONFIG_SET_DFU_ALT_INFO */
+
+static int do_mmc_init(struct mmc **mmc)
+{
+ int ret;
+
+ *mmc = find_mmc_device(0);
+ if (!mmc) {
+ error("Couldn't find MMC device no. 0.\n");
+ return -ENODEV;
+ }
+
+ ret = mmc_init(*mmc);
+ if (ret) {
+ error("Couldn't init MMC device.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int find_mmc_partition_info(struct mmc *mmc, uint hwpart, uint part,
+ disk_partition_t *partinfo)
+{
+ int ret;
+
+ if (!mmc)
+ return -ENODEV;
+
+ ret = mmc_select_hwpart(0, hwpart);
+ if (ret)
+ return ret;
+
+ ret = get_partition_info(&mmc->block_dev, part, partinfo);
+ if (ret) {
+ error("Couldn't find part #%d on mmc device #%d\n",
+ part, hwpart);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct img_info *create_img_info(struct mmc *mmc, char *str_info)
+{
+ struct img_info *info;
+ char *tok[6];
+ int i;
+ int ret;
+
+ info = calloc(1, sizeof(struct img_info));
+ if (!info) {
+ error("Cannot malloc!!\n");
+ return NULL;
+ }
+
+ for (i = 0; i < 6; i++)
+ tok[i] = strsep(&str_info, " \t");
+
+ if (strlen(tok[0]) >= LEN_NAME - 1) {
+ error("img name is too long: %s\n", tok[0]);
+ goto create_img_err;
+ }
+
+ for (i = 0; i < sizeof(g_update_image_names) / sizeof(char *); i++)
+ if (!strncmp(tok[0], g_update_image_names[i], strlen(tok[0])))
+ sprintf(info->name, "%s", g_update_image_names[i]);
+
+ if (!info->name[0])
+ goto create_img_err;
+
+ if (!strncmp(tok[1], "raw", 3)) {
+ info->type = IMG_TYPE_RAW;
+ info->lba_start = simple_strtoul(tok[2], NULL, 0);
+ info->lba_size = simple_strtoul(tok[3], NULL, 0);
+ info->lba_blk_size = 512;
+
+ if (tok[4] && !strncmp(tok[4], "mmcpart", 7))
+ info->hwpart = simple_strtoul(tok[5], NULL, 0);
+ else
+ info->hwpart = 1;
+ } else if (!strncmp(tok[1], "part", 4)) {
+ disk_partition_t partinfo;
+ uint offset = 0;
+
+ info->type = IMG_TYPE_PART;
+ info->hwpart = simple_strtoul(tok[2], NULL, 0);
+ info->part = simple_strtoul(tok[3], NULL, 0);
+
+ ret = find_mmc_partition_info(mmc, info->hwpart, info->part,
+ &partinfo);
+ if (ret) {
+ error("Cannot get partition info: %d\n", info->hwpart);
+ goto create_img_err;
+ }
+
+ if (tok[4] && !strcmp(tok[4], "offset"))
+ offset = simple_strtoul(tok[5], NULL, 0);
+
+ info->lba_start = partinfo.start + offset;
+ info->lba_size = partinfo.size - offset;
+ info->lba_blk_size = partinfo.blksz;
+ } else if (!strncmp(tok[1], "fat", 3)) {
+ error("nfsdown does not support fat update");
+ goto create_img_err;
+ } else {
+ error("Unrecognized img type: %s", tok[0]);
+ goto create_img_err;
+ }
+
+ return info;
+
+create_img_err:
+ free(info);
+
+ return NULL;
+}
+
+struct img_comp *create_img_comp(const char *name, uint size)
+{
+ struct img_comp *comp;
+
+ comp = malloc(sizeof(struct img_comp));
+ if (!comp) {
+ error("Failed to alloc\n");
+ return NULL;
+ }
+
+ strncpy(comp->name, name, LEN_NAME);
+ comp->size = size;
+
+ return comp;
+}
+
+/**
+ * Donwload from nfs file and write to mmc stroage
+ *
+ * @param buf_addr Download buffer address from NFS
+ * @param file_path Download file path from NFS
+ * @param part MMC partition
+ * @param offset img block offset
+ * @param size img block size
+ * @param blk_size MMC block size
+ * @return file_size written file size, return -1 if error occurs
+ */
+static int do_nfs_to_mmc(struct mmc *mmc, char *buf_addr, char *file_path,
+ uint part, uint offset, uint size, uint blk_size)
+{
+ char *_argv[3];
+ uint _size;
+ int repeatable;
+ int ret;
+ void *addr;
+
+ _argv[0] = "nfs";
+ _argv[1] = buf_addr;
+ _argv[2] = file_path;
+
+ ret = cmd_process(0, 3, _argv, &repeatable, NULL);
+ if (ret != CMD_RET_SUCCESS) {
+ puts("nfs download failed!!\n");
+ return -1;
+ }
+
+ _size = (net_boot_file_size % blk_size) ?
+ net_boot_file_size / blk_size + 1
+ : net_boot_file_size / blk_size;
+
+ ret = mmc_select_hwpart(0, part);
+ if (ret) {
+ error("Couldn't change to hwpart: %d\n", part);
+ return -1;
+ }
+
+ addr = (void *)simple_strtoul(buf_addr, NULL, 16);
+
+ printf("MMC write: dev # %d, block # %d, count %d ...\n\n",
+ part, offset, _size);
+ ret = mmc->block_dev.block_write(&mmc->block_dev, offset, _size, addr);
+ if (ret != _size) {
+ error("Failed to write MMC: part(%d), start(%d), size(%d)",
+ part, offset, _size);
+ return -1;
+ }
+
+ return (int)net_boot_file_size;
+}
+
+int do_nfs_down(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ char *nfs_path;
+ char *buf;
+ char *line;
+ char src_path[LEN_BUF];
+ int size;
+ int ret;
+
+ struct img_info *info, *tmp_info;
+ struct img_comp *comp, *tmp_comp;
+ struct mmc *mmc;
+
+ LIST_HEAD(img_info_list);
+ LIST_HEAD(img_comp_list);
+
+ puts("TIZEN \"nfs\" Downloader\n");
+
+ switch (argc) {
+ case 2:
+ nfs_path = argv[1];
+ break;
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ INIT_LIST_HEAD(&img_info_list);
+ INIT_LIST_HEAD(&img_comp_list);
+
+ set_dfu_alt_info();
+
+ buf = getenv("dfu_alt_info");
+ if (!buf) {
+ puts("No Default dfu_alt_info\n");
+ return CMD_RET_FAILURE;
+ }
+ printf("\ndfu_alt_info = %s\n", buf);
+
+ ret = do_mmc_init(&mmc);
+ if (ret) {
+ error("failed to init mmc\n");
+ ret = CMD_RET_FAILURE;
+ goto error_end;
+ }
+
+#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_HOST_ETHER)
+ if (!usb_started)
+ do_usb_ether_start();
+#endif
+
+ line = strtok(buf, ";\n\0");
+ while (line) {
+ struct img_info *new_info;
+
+ new_info = create_img_info(mmc, line);
+ if (new_info)
+ list_add_tail(&new_info->list, &img_info_list);
+
+ line = strtok(NULL, ";\n\0");
+ }
+
+ list_for_each_entry(info, &img_info_list, list) {
+ snprintf(src_path, LEN_BUF, "%s/%s", nfs_path, info->name);
+
+ size = do_nfs_to_mmc(mmc, NFS_DOWNLOAD_ADDR, src_path,
+ info->hwpart, info->lba_start,
+ info->lba_size, info->lba_blk_size);
+ if (size > 0) {
+ struct img_comp *new_comp;
+
+ new_comp = create_img_comp(info->name, size);
+ if (new_comp)
+ list_add_tail(&new_comp->list, &img_comp_list);
+ } else {
+ printf("SKIP %s\n\n", src_path);
+ }
+ }
+
+ puts("########################################\n");
+ list_for_each_entry(comp, &img_comp_list, list)
+ printf("%15s\t-\t%10d Byte\n", comp->name, comp->size);
+ puts("########################################\n");
+
+error_end:
+ free(buf);
+
+ list_for_each_entry_safe(comp, tmp_comp, &img_comp_list, list) {
+ list_del(&comp->list);
+ free(comp);
+ }
+
+ list_for_each_entry_safe(info, tmp_info, &img_info_list, list) {
+ list_del(&info->list);
+ free(info);
+ }
+
+ return ret;
+}
+
+U_BOOT_CMD(nfsdown, CONFIG_SYS_MAXARGS, 1, do_nfs_down,
+ "TIZEN \"nfs\" downloader",
+ "<IP_ADDR>:<IMG_PATH>\n"
+ " - device firmware upgrade via nfs for Tizen\n"
+ " fusing images from <IP_ADDR>:<IMG_PATH> to MMC.\n"
+ "\n"
+ "(e.g) nfsdown 192.168.0.1:/nfs\n"
+);