1cb383cd2SLukasz Majewski /* 2cb383cd2SLukasz Majewski * dfu.c -- DFU back-end routines 3cb383cd2SLukasz Majewski * 4cb383cd2SLukasz Majewski * Copyright (C) 2012 Samsung Electronics 5cb383cd2SLukasz Majewski * author: Lukasz Majewski <l.majewski@samsung.com> 6cb383cd2SLukasz Majewski * 71a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 8cb383cd2SLukasz Majewski */ 9cb383cd2SLukasz Majewski 10cb383cd2SLukasz Majewski #include <common.h> 11cb383cd2SLukasz Majewski #include <malloc.h> 121b6ca18bSPantelis Antoniou #include <errno.h> 13ea2453d5SPantelis Antoniou #include <div64.h> 14cb383cd2SLukasz Majewski #include <dfu.h> 157d0b605aSŁukasz Majewski #include <mmc.h> 16cb383cd2SLukasz Majewski 17ea2453d5SPantelis Antoniou static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) 18ea2453d5SPantelis Antoniou dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE]; 19ea2453d5SPantelis Antoniou static long dfu_file_buf_len; 20ea2453d5SPantelis Antoniou 215a127c84SAfzal Mohammed static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, 22ea2453d5SPantelis Antoniou u64 offset, void *buf, long *len) 23cb383cd2SLukasz Majewski { 247d0b605aSŁukasz Majewski struct mmc *mmc = find_mmc_device(dfu->dev_num); 257d0b605aSŁukasz Majewski u32 blk_start, blk_count, n = 0; 26cb383cd2SLukasz Majewski 27ea2453d5SPantelis Antoniou /* 28ea2453d5SPantelis Antoniou * We must ensure that we work in lba_blk_size chunks, so ALIGN 29ea2453d5SPantelis Antoniou * this value. 30ea2453d5SPantelis Antoniou */ 31ea2453d5SPantelis Antoniou *len = ALIGN(*len, dfu->data.mmc.lba_blk_size); 32ea2453d5SPantelis Antoniou 33ea2453d5SPantelis Antoniou blk_start = dfu->data.mmc.lba_start + 34ea2453d5SPantelis Antoniou (u32)lldiv(offset, dfu->data.mmc.lba_blk_size); 35ea2453d5SPantelis Antoniou blk_count = *len / dfu->data.mmc.lba_blk_size; 36ea2453d5SPantelis Antoniou if (blk_start + blk_count > 37ea2453d5SPantelis Antoniou dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) { 38ea2453d5SPantelis Antoniou puts("Request would exceed designated area!\n"); 39ea2453d5SPantelis Antoniou return -EINVAL; 40ea2453d5SPantelis Antoniou } 41ea2453d5SPantelis Antoniou 427d0b605aSŁukasz Majewski debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__, 437d0b605aSŁukasz Majewski op == DFU_OP_READ ? "MMC READ" : "MMC WRITE", dfu->dev_num, 447d0b605aSŁukasz Majewski blk_start, blk_count, buf); 457d0b605aSŁukasz Majewski switch (op) { 467d0b605aSŁukasz Majewski case DFU_OP_READ: 477d0b605aSŁukasz Majewski n = mmc->block_dev.block_read(dfu->dev_num, blk_start, 487d0b605aSŁukasz Majewski blk_count, buf); 497d0b605aSŁukasz Majewski break; 507d0b605aSŁukasz Majewski case DFU_OP_WRITE: 517d0b605aSŁukasz Majewski n = mmc->block_dev.block_write(dfu->dev_num, blk_start, 527d0b605aSŁukasz Majewski blk_count, buf); 537d0b605aSŁukasz Majewski break; 547d0b605aSŁukasz Majewski default: 557d0b605aSŁukasz Majewski error("Operation not supported\n"); 567d0b605aSŁukasz Majewski } 57cb383cd2SLukasz Majewski 587d0b605aSŁukasz Majewski if (n != blk_count) { 597d0b605aSŁukasz Majewski error("MMC operation failed"); 607d0b605aSŁukasz Majewski return -EIO; 617d0b605aSŁukasz Majewski } 627d0b605aSŁukasz Majewski 637d0b605aSŁukasz Majewski return 0; 64cb383cd2SLukasz Majewski } 65cb383cd2SLukasz Majewski 66ea2453d5SPantelis Antoniou static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len) 67cb383cd2SLukasz Majewski { 68ea2453d5SPantelis Antoniou if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) { 69ea2453d5SPantelis Antoniou dfu_file_buf_len = 0; 70ea2453d5SPantelis Antoniou return -EINVAL; 71cb383cd2SLukasz Majewski } 72cb383cd2SLukasz Majewski 73ea2453d5SPantelis Antoniou /* Add to the current buffer. */ 74ea2453d5SPantelis Antoniou memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len); 75ea2453d5SPantelis Antoniou dfu_file_buf_len += *len; 76ea2453d5SPantelis Antoniou 77ea2453d5SPantelis Antoniou return 0; 78cb383cd2SLukasz Majewski } 79cb383cd2SLukasz Majewski 805a127c84SAfzal Mohammed static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu, 81cb383cd2SLukasz Majewski void *buf, long *len) 82cb383cd2SLukasz Majewski { 83cb383cd2SLukasz Majewski char cmd_buf[DFU_CMD_BUF_SIZE]; 84cb383cd2SLukasz Majewski char *str_env; 85cb383cd2SLukasz Majewski int ret; 86cb383cd2SLukasz Majewski 8743e66272SŁukasz Majewski switch (dfu->layout) { 8843e66272SŁukasz Majewski case DFU_FS_FAT: 89ea2453d5SPantelis Antoniou sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s", 90cb383cd2SLukasz Majewski op == DFU_OP_READ ? "load" : "write", 91cb383cd2SLukasz Majewski dfu->data.mmc.dev, dfu->data.mmc.part, 92ea2453d5SPantelis Antoniou (unsigned int) buf, dfu->name); 9343e66272SŁukasz Majewski break; 9443e66272SŁukasz Majewski case DFU_FS_EXT4: 95ea2453d5SPantelis Antoniou sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s", 9643e66272SŁukasz Majewski op == DFU_OP_READ ? "load" : "write", 9743e66272SŁukasz Majewski dfu->data.mmc.dev, dfu->data.mmc.part, 98ea2453d5SPantelis Antoniou (unsigned int) buf, dfu->name); 9943e66272SŁukasz Majewski break; 10043e66272SŁukasz Majewski default: 10143e66272SŁukasz Majewski printf("%s: Layout (%s) not (yet) supported!\n", __func__, 10243e66272SŁukasz Majewski dfu_get_layout(dfu->layout)); 103ea2453d5SPantelis Antoniou return -1; 10443e66272SŁukasz Majewski } 105cb383cd2SLukasz Majewski 10617eb1d8fSLukasz Majewski if (op == DFU_OP_WRITE) 10717eb1d8fSLukasz Majewski sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len); 10817eb1d8fSLukasz Majewski 109cb383cd2SLukasz Majewski debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); 110cb383cd2SLukasz Majewski 111cb383cd2SLukasz Majewski ret = run_command(cmd_buf, 0); 112cb383cd2SLukasz Majewski if (ret) { 113cb383cd2SLukasz Majewski puts("dfu: Read error!\n"); 114cb383cd2SLukasz Majewski return ret; 115cb383cd2SLukasz Majewski } 116cb383cd2SLukasz Majewski 11781c1d7b6SŁukasz Majewski if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) { 118cb383cd2SLukasz Majewski str_env = getenv("filesize"); 119cb383cd2SLukasz Majewski if (str_env == NULL) { 120cb383cd2SLukasz Majewski puts("dfu: Wrong file size!\n"); 121cb383cd2SLukasz Majewski return -1; 122cb383cd2SLukasz Majewski } 123cb383cd2SLukasz Majewski *len = simple_strtoul(str_env, NULL, 16); 124cb383cd2SLukasz Majewski } 125cb383cd2SLukasz Majewski 126cb383cd2SLukasz Majewski return ret; 127cb383cd2SLukasz Majewski } 128cb383cd2SLukasz Majewski 129ea2453d5SPantelis Antoniou int dfu_write_medium_mmc(struct dfu_entity *dfu, 130ea2453d5SPantelis Antoniou u64 offset, void *buf, long *len) 131cb383cd2SLukasz Majewski { 132cb383cd2SLukasz Majewski int ret = -1; 133cb383cd2SLukasz Majewski 134cb383cd2SLukasz Majewski switch (dfu->layout) { 135cb383cd2SLukasz Majewski case DFU_RAW_ADDR: 136ea2453d5SPantelis Antoniou ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len); 137cb383cd2SLukasz Majewski break; 138cb383cd2SLukasz Majewski case DFU_FS_FAT: 13943e66272SŁukasz Majewski case DFU_FS_EXT4: 140ea2453d5SPantelis Antoniou ret = mmc_file_buffer(dfu, buf, len); 141cb383cd2SLukasz Majewski break; 142cb383cd2SLukasz Majewski default: 143cb383cd2SLukasz Majewski printf("%s: Layout (%s) not (yet) supported!\n", __func__, 144cb383cd2SLukasz Majewski dfu_get_layout(dfu->layout)); 145cb383cd2SLukasz Majewski } 146cb383cd2SLukasz Majewski 147cb383cd2SLukasz Majewski return ret; 148cb383cd2SLukasz Majewski } 149cb383cd2SLukasz Majewski 150ea2453d5SPantelis Antoniou int dfu_flush_medium_mmc(struct dfu_entity *dfu) 151ea2453d5SPantelis Antoniou { 152ea2453d5SPantelis Antoniou int ret = 0; 153ea2453d5SPantelis Antoniou 154ea2453d5SPantelis Antoniou if (dfu->layout != DFU_RAW_ADDR) { 155ea2453d5SPantelis Antoniou /* Do stuff here. */ 156ea2453d5SPantelis Antoniou ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf, 157ea2453d5SPantelis Antoniou &dfu_file_buf_len); 158ea2453d5SPantelis Antoniou 159ea2453d5SPantelis Antoniou /* Now that we're done */ 160ea2453d5SPantelis Antoniou dfu_file_buf_len = 0; 161ea2453d5SPantelis Antoniou } 162ea2453d5SPantelis Antoniou 163ea2453d5SPantelis Antoniou return ret; 164ea2453d5SPantelis Antoniou } 165ea2453d5SPantelis Antoniou 166ea2453d5SPantelis Antoniou int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf, 167ea2453d5SPantelis Antoniou long *len) 168cb383cd2SLukasz Majewski { 169cb383cd2SLukasz Majewski int ret = -1; 170cb383cd2SLukasz Majewski 171cb383cd2SLukasz Majewski switch (dfu->layout) { 172cb383cd2SLukasz Majewski case DFU_RAW_ADDR: 173ea2453d5SPantelis Antoniou ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len); 174cb383cd2SLukasz Majewski break; 175cb383cd2SLukasz Majewski case DFU_FS_FAT: 17643e66272SŁukasz Majewski case DFU_FS_EXT4: 177ea2453d5SPantelis Antoniou ret = mmc_file_op(DFU_OP_READ, dfu, buf, len); 178cb383cd2SLukasz Majewski break; 179cb383cd2SLukasz Majewski default: 180cb383cd2SLukasz Majewski printf("%s: Layout (%s) not (yet) supported!\n", __func__, 181cb383cd2SLukasz Majewski dfu_get_layout(dfu->layout)); 182cb383cd2SLukasz Majewski } 183cb383cd2SLukasz Majewski 184cb383cd2SLukasz Majewski return ret; 185cb383cd2SLukasz Majewski } 186cb383cd2SLukasz Majewski 187711b931fSMateusz Zalega /* 188711b931fSMateusz Zalega * @param s Parameter string containing space-separated arguments: 189711b931fSMateusz Zalega * 1st: 190711b931fSMateusz Zalega * raw (raw read/write) 191711b931fSMateusz Zalega * fat (files) 192711b931fSMateusz Zalega * ext4 (^) 193711b931fSMateusz Zalega * part (partition image) 194711b931fSMateusz Zalega * 2nd and 3rd: 195711b931fSMateusz Zalega * lba_start and lba_size, for raw write 196711b931fSMateusz Zalega * mmc_dev and mmc_part, for filesystems and part 197711b931fSMateusz Zalega */ 198cb383cd2SLukasz Majewski int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) 199cb383cd2SLukasz Majewski { 200711b931fSMateusz Zalega const char *entity_type; 201711b931fSMateusz Zalega size_t second_arg; 202711b931fSMateusz Zalega size_t third_arg; 203711b931fSMateusz Zalega 2041b6ca18bSPantelis Antoniou struct mmc *mmc; 205711b931fSMateusz Zalega 206711b931fSMateusz Zalega const char *argv[3]; 207711b931fSMateusz Zalega const char **parg = argv; 208711b931fSMateusz Zalega 209711b931fSMateusz Zalega for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) { 210711b931fSMateusz Zalega *parg = strsep(&s, " "); 211711b931fSMateusz Zalega if (*parg == NULL) { 212711b931fSMateusz Zalega error("Invalid number of arguments.\n"); 213711b931fSMateusz Zalega return -ENODEV; 214711b931fSMateusz Zalega } 215711b931fSMateusz Zalega } 216711b931fSMateusz Zalega 217711b931fSMateusz Zalega entity_type = argv[0]; 218*b7d4259aSMateusz Zalega /* 219*b7d4259aSMateusz Zalega * Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8, 220*b7d4259aSMateusz Zalega * with default 10. 221*b7d4259aSMateusz Zalega */ 222*b7d4259aSMateusz Zalega second_arg = simple_strtoul(argv[1], NULL, 0); 223*b7d4259aSMateusz Zalega third_arg = simple_strtoul(argv[2], NULL, 0); 224711b931fSMateusz Zalega 225711b931fSMateusz Zalega mmc = find_mmc_device(dfu->dev_num); 226711b931fSMateusz Zalega if (mmc == NULL) { 227711b931fSMateusz Zalega error("Couldn't find MMC device no. %d.\n", dfu->dev_num); 228711b931fSMateusz Zalega return -ENODEV; 229711b931fSMateusz Zalega } 230711b931fSMateusz Zalega 231711b931fSMateusz Zalega if (mmc_init(mmc)) { 232711b931fSMateusz Zalega error("Couldn't init MMC device.\n"); 233711b931fSMateusz Zalega return -ENODEV; 234711b931fSMateusz Zalega } 235711b931fSMateusz Zalega 236711b931fSMateusz Zalega if (!strcmp(entity_type, "raw")) { 237711b931fSMateusz Zalega dfu->layout = DFU_RAW_ADDR; 238711b931fSMateusz Zalega dfu->data.mmc.lba_start = second_arg; 239711b931fSMateusz Zalega dfu->data.mmc.lba_size = third_arg; 240711b931fSMateusz Zalega dfu->data.mmc.lba_blk_size = mmc->read_bl_len; 241711b931fSMateusz Zalega } else if (!strcmp(entity_type, "part")) { 2421b6ca18bSPantelis Antoniou disk_partition_t partinfo; 243711b931fSMateusz Zalega block_dev_desc_t *blk_dev = &mmc->block_dev; 244711b931fSMateusz Zalega int mmcdev = second_arg; 245711b931fSMateusz Zalega int mmcpart = third_arg; 246cb383cd2SLukasz Majewski 247711b931fSMateusz Zalega if (get_partition_info(blk_dev, mmcpart, &partinfo) != 0) { 248711b931fSMateusz Zalega error("Couldn't find part #%d on mmc device #%d\n", 249711b931fSMateusz Zalega mmcpart, mmcdev); 2501b6ca18bSPantelis Antoniou return -ENODEV; 2511b6ca18bSPantelis Antoniou } 2521b6ca18bSPantelis Antoniou 253711b931fSMateusz Zalega dfu->layout = DFU_RAW_ADDR; 2541b6ca18bSPantelis Antoniou dfu->data.mmc.lba_start = partinfo.start; 2551b6ca18bSPantelis Antoniou dfu->data.mmc.lba_size = partinfo.size; 2561b6ca18bSPantelis Antoniou dfu->data.mmc.lba_blk_size = partinfo.blksz; 257711b931fSMateusz Zalega } else if (!strcmp(entity_type, "fat")) { 258711b931fSMateusz Zalega dfu->layout = DFU_FS_FAT; 259711b931fSMateusz Zalega } else if (!strcmp(entity_type, "ext4")) { 260711b931fSMateusz Zalega dfu->layout = DFU_FS_EXT4; 261cb383cd2SLukasz Majewski } else { 262711b931fSMateusz Zalega error("Memory layout (%s) not supported!\n", entity_type); 2631b6ca18bSPantelis Antoniou return -ENODEV; 264cb383cd2SLukasz Majewski } 265cb383cd2SLukasz Majewski 266711b931fSMateusz Zalega /* if it's NOT a raw write */ 267711b931fSMateusz Zalega if (strcmp(entity_type, "raw")) { 268711b931fSMateusz Zalega dfu->data.mmc.dev = second_arg; 269711b931fSMateusz Zalega dfu->data.mmc.part = third_arg; 27043e66272SŁukasz Majewski } 27143e66272SŁukasz Majewski 272711b931fSMateusz Zalega dfu->dev_type = DFU_DEV_MMC; 273cb383cd2SLukasz Majewski dfu->read_medium = dfu_read_medium_mmc; 274cb383cd2SLukasz Majewski dfu->write_medium = dfu_write_medium_mmc; 275ea2453d5SPantelis Antoniou dfu->flush_medium = dfu_flush_medium_mmc; 276ea2453d5SPantelis Antoniou dfu->inited = 0; 277cb383cd2SLukasz Majewski 278cb383cd2SLukasz Majewski return 0; 279cb383cd2SLukasz Majewski } 280