uboot/drivers/dfu/dfu_mmc.c
<<
>>
Prefs
   1/*
   2 * dfu.c -- DFU back-end routines
   3 *
   4 * Copyright (C) 2012 Samsung Electronics
   5 * author: Lukasz Majewski <l.majewski@samsung.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20 */
  21
  22#include <common.h>
  23#include <malloc.h>
  24#include <errno.h>
  25#include <div64.h>
  26#include <dfu.h>
  27
  28enum dfu_mmc_op {
  29        DFU_OP_READ = 1,
  30        DFU_OP_WRITE,
  31};
  32
  33static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
  34                                dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE];
  35static long dfu_file_buf_len;
  36
  37static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
  38                        u64 offset, void *buf, long *len)
  39{
  40        char cmd_buf[DFU_CMD_BUF_SIZE];
  41        u32 blk_start, blk_count;
  42
  43        /*
  44         * We must ensure that we work in lba_blk_size chunks, so ALIGN
  45         * this value.
  46         */
  47        *len = ALIGN(*len, dfu->data.mmc.lba_blk_size);
  48
  49        blk_start = dfu->data.mmc.lba_start +
  50                        (u32)lldiv(offset, dfu->data.mmc.lba_blk_size);
  51        blk_count = *len / dfu->data.mmc.lba_blk_size;
  52        if (blk_start + blk_count >
  53                        dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) {
  54                puts("Request would exceed designated area!\n");
  55                return -EINVAL;
  56        }
  57
  58        sprintf(cmd_buf, "mmc %s %p %x %x",
  59                op == DFU_OP_READ ? "read" : "write",
  60                 buf, blk_start, blk_count);
  61
  62        debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
  63        return run_command(cmd_buf, 0);
  64}
  65
  66static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len)
  67{
  68        if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) {
  69                dfu_file_buf_len = 0;
  70                return -EINVAL;
  71        }
  72
  73        /* Add to the current buffer. */
  74        memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len);
  75        dfu_file_buf_len += *len;
  76
  77        return 0;
  78}
  79
  80static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
  81                        void *buf, long *len)
  82{
  83        char cmd_buf[DFU_CMD_BUF_SIZE];
  84        char *str_env;
  85        int ret;
  86
  87        switch (dfu->layout) {
  88        case DFU_FS_FAT:
  89                sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s",
  90                        op == DFU_OP_READ ? "load" : "write",
  91                        dfu->data.mmc.dev, dfu->data.mmc.part,
  92                        (unsigned int) buf, dfu->name);
  93                if (op == DFU_OP_WRITE)
  94                        sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len);
  95                break;
  96        case DFU_FS_EXT4:
  97                sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s",
  98                        op == DFU_OP_READ ? "load" : "write",
  99                        dfu->data.mmc.dev, dfu->data.mmc.part,
 100                        (unsigned int) buf, dfu->name);
 101                if (op == DFU_OP_WRITE)
 102                        sprintf(cmd_buf + strlen(cmd_buf), " %ld", *len);
 103                break;
 104        default:
 105                printf("%s: Layout (%s) not (yet) supported!\n", __func__,
 106                       dfu_get_layout(dfu->layout));
 107                return -1;
 108        }
 109
 110        debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
 111
 112        ret = run_command(cmd_buf, 0);
 113        if (ret) {
 114                puts("dfu: Read error!\n");
 115                return ret;
 116        }
 117
 118        if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) {
 119                str_env = getenv("filesize");
 120                if (str_env == NULL) {
 121                        puts("dfu: Wrong file size!\n");
 122                        return -1;
 123                }
 124                *len = simple_strtoul(str_env, NULL, 16);
 125        }
 126
 127        return ret;
 128}
 129
 130int dfu_write_medium_mmc(struct dfu_entity *dfu,
 131                u64 offset, void *buf, long *len)
 132{
 133        int ret = -1;
 134
 135        switch (dfu->layout) {
 136        case DFU_RAW_ADDR:
 137                ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
 138                break;
 139        case DFU_FS_FAT:
 140        case DFU_FS_EXT4:
 141                ret = mmc_file_buffer(dfu, buf, len);
 142                break;
 143        default:
 144                printf("%s: Layout (%s) not (yet) supported!\n", __func__,
 145                       dfu_get_layout(dfu->layout));
 146        }
 147
 148        return ret;
 149}
 150
 151int dfu_flush_medium_mmc(struct dfu_entity *dfu)
 152{
 153        int ret = 0;
 154
 155        if (dfu->layout != DFU_RAW_ADDR) {
 156                /* Do stuff here. */
 157                ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf,
 158                                &dfu_file_buf_len);
 159
 160                /* Now that we're done */
 161                dfu_file_buf_len = 0;
 162        }
 163
 164        return ret;
 165}
 166
 167int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
 168                long *len)
 169{
 170        int ret = -1;
 171
 172        switch (dfu->layout) {
 173        case DFU_RAW_ADDR:
 174                ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
 175                break;
 176        case DFU_FS_FAT:
 177        case DFU_FS_EXT4:
 178                ret = mmc_file_op(DFU_OP_READ, dfu, buf, len);
 179                break;
 180        default:
 181                printf("%s: Layout (%s) not (yet) supported!\n", __func__,
 182                       dfu_get_layout(dfu->layout));
 183        }
 184
 185        return ret;
 186}
 187
 188int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
 189{
 190        int dev, part;
 191        struct mmc *mmc;
 192        block_dev_desc_t *blk_dev;
 193        disk_partition_t partinfo;
 194        char *st;
 195
 196        dfu->dev_type = DFU_DEV_MMC;
 197        st = strsep(&s, " ");
 198        if (!strcmp(st, "mmc")) {
 199                dfu->layout = DFU_RAW_ADDR;
 200                dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16);
 201                dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16);
 202                dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num);
 203        } else if (!strcmp(st, "fat")) {
 204                dfu->layout = DFU_FS_FAT;
 205        } else if (!strcmp(st, "ext4")) {
 206                dfu->layout = DFU_FS_EXT4;
 207        } else if (!strcmp(st, "part")) {
 208
 209                dfu->layout = DFU_RAW_ADDR;
 210
 211                dev = simple_strtoul(s, &s, 10);
 212                s++;
 213                part = simple_strtoul(s, &s, 10);
 214
 215                mmc = find_mmc_device(dev);
 216                if (mmc == NULL || mmc_init(mmc)) {
 217                        printf("%s: could not find mmc device #%d!\n",
 218                               __func__, dev);
 219                        return -ENODEV;
 220                }
 221
 222                blk_dev = &mmc->block_dev;
 223                if (get_partition_info(blk_dev, part, &partinfo) != 0) {
 224                        printf("%s: could not find partition #%d on mmc device #%d!\n",
 225                               __func__, part, dev);
 226                        return -ENODEV;
 227                }
 228
 229                dfu->data.mmc.lba_start = partinfo.start;
 230                dfu->data.mmc.lba_size = partinfo.size;
 231                dfu->data.mmc.lba_blk_size = partinfo.blksz;
 232
 233        } else {
 234                printf("%s: Memory layout (%s) not supported!\n", __func__, st);
 235                return -ENODEV;
 236        }
 237
 238        if (dfu->layout == DFU_FS_EXT4 || dfu->layout == DFU_FS_FAT) {
 239                dfu->data.mmc.dev = simple_strtoul(s, &s, 10);
 240                dfu->data.mmc.part = simple_strtoul(++s, &s, 10);
 241        }
 242
 243        dfu->read_medium = dfu_read_medium_mmc;
 244        dfu->write_medium = dfu_write_medium_mmc;
 245        dfu->flush_medium = dfu_flush_medium_mmc;
 246
 247        /* initial state */
 248        dfu->inited = 0;
 249
 250        return 0;
 251}
 252