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 * SPDX-License-Identifier: GPL-2.0+ 8 */ 9 10 #include <common.h> 11 #include <malloc.h> 12 #include <errno.h> 13 #include <div64.h> 14 #include <dfu.h> 15 16 enum dfu_mmc_op { 17 DFU_OP_READ = 1, 18 DFU_OP_WRITE, 19 }; 20 21 static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) 22 dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE]; 23 static long dfu_file_buf_len; 24 25 static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu, 26 u64 offset, void *buf, long *len) 27 { 28 char cmd_buf[DFU_CMD_BUF_SIZE]; 29 u32 blk_start, blk_count; 30 31 /* 32 * We must ensure that we work in lba_blk_size chunks, so ALIGN 33 * this value. 34 */ 35 *len = ALIGN(*len, dfu->data.mmc.lba_blk_size); 36 37 blk_start = dfu->data.mmc.lba_start + 38 (u32)lldiv(offset, dfu->data.mmc.lba_blk_size); 39 blk_count = *len / dfu->data.mmc.lba_blk_size; 40 if (blk_start + blk_count > 41 dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) { 42 puts("Request would exceed designated area!\n"); 43 return -EINVAL; 44 } 45 46 sprintf(cmd_buf, "mmc %s %p %x %x", 47 op == DFU_OP_READ ? "read" : "write", 48 buf, blk_start, blk_count); 49 50 debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); 51 return run_command(cmd_buf, 0); 52 } 53 54 static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len) 55 { 56 if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) { 57 dfu_file_buf_len = 0; 58 return -EINVAL; 59 } 60 61 /* Add to the current buffer. */ 62 memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len); 63 dfu_file_buf_len += *len; 64 65 return 0; 66 } 67 68 static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu, 69 void *buf, long *len) 70 { 71 char cmd_buf[DFU_CMD_BUF_SIZE]; 72 char *str_env; 73 int ret; 74 75 switch (dfu->layout) { 76 case DFU_FS_FAT: 77 sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s", 78 op == DFU_OP_READ ? "load" : "write", 79 dfu->data.mmc.dev, dfu->data.mmc.part, 80 (unsigned int) buf, dfu->name); 81 if (op == DFU_OP_WRITE) 82 sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len); 83 break; 84 case DFU_FS_EXT4: 85 sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s", 86 op == DFU_OP_READ ? "load" : "write", 87 dfu->data.mmc.dev, dfu->data.mmc.part, 88 (unsigned int) buf, dfu->name); 89 if (op == DFU_OP_WRITE) 90 sprintf(cmd_buf + strlen(cmd_buf), " %ld", *len); 91 break; 92 default: 93 printf("%s: Layout (%s) not (yet) supported!\n", __func__, 94 dfu_get_layout(dfu->layout)); 95 return -1; 96 } 97 98 debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); 99 100 ret = run_command(cmd_buf, 0); 101 if (ret) { 102 puts("dfu: Read error!\n"); 103 return ret; 104 } 105 106 if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) { 107 str_env = getenv("filesize"); 108 if (str_env == NULL) { 109 puts("dfu: Wrong file size!\n"); 110 return -1; 111 } 112 *len = simple_strtoul(str_env, NULL, 16); 113 } 114 115 return ret; 116 } 117 118 int dfu_write_medium_mmc(struct dfu_entity *dfu, 119 u64 offset, void *buf, long *len) 120 { 121 int ret = -1; 122 123 switch (dfu->layout) { 124 case DFU_RAW_ADDR: 125 ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len); 126 break; 127 case DFU_FS_FAT: 128 case DFU_FS_EXT4: 129 ret = mmc_file_buffer(dfu, buf, len); 130 break; 131 default: 132 printf("%s: Layout (%s) not (yet) supported!\n", __func__, 133 dfu_get_layout(dfu->layout)); 134 } 135 136 return ret; 137 } 138 139 int dfu_flush_medium_mmc(struct dfu_entity *dfu) 140 { 141 int ret = 0; 142 143 if (dfu->layout != DFU_RAW_ADDR) { 144 /* Do stuff here. */ 145 ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf, 146 &dfu_file_buf_len); 147 148 /* Now that we're done */ 149 dfu_file_buf_len = 0; 150 } 151 152 return ret; 153 } 154 155 int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf, 156 long *len) 157 { 158 int ret = -1; 159 160 switch (dfu->layout) { 161 case DFU_RAW_ADDR: 162 ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len); 163 break; 164 case DFU_FS_FAT: 165 case DFU_FS_EXT4: 166 ret = mmc_file_op(DFU_OP_READ, dfu, buf, len); 167 break; 168 default: 169 printf("%s: Layout (%s) not (yet) supported!\n", __func__, 170 dfu_get_layout(dfu->layout)); 171 } 172 173 return ret; 174 } 175 176 int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) 177 { 178 int dev, part; 179 struct mmc *mmc; 180 block_dev_desc_t *blk_dev; 181 disk_partition_t partinfo; 182 char *st; 183 184 dfu->dev_type = DFU_DEV_MMC; 185 st = strsep(&s, " "); 186 if (!strcmp(st, "mmc")) { 187 dfu->layout = DFU_RAW_ADDR; 188 dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16); 189 dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16); 190 dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num); 191 } else if (!strcmp(st, "fat")) { 192 dfu->layout = DFU_FS_FAT; 193 } else if (!strcmp(st, "ext4")) { 194 dfu->layout = DFU_FS_EXT4; 195 } else if (!strcmp(st, "part")) { 196 197 dfu->layout = DFU_RAW_ADDR; 198 199 dev = simple_strtoul(s, &s, 10); 200 s++; 201 part = simple_strtoul(s, &s, 10); 202 203 mmc = find_mmc_device(dev); 204 if (mmc == NULL || mmc_init(mmc)) { 205 printf("%s: could not find mmc device #%d!\n", 206 __func__, dev); 207 return -ENODEV; 208 } 209 210 blk_dev = &mmc->block_dev; 211 if (get_partition_info(blk_dev, part, &partinfo) != 0) { 212 printf("%s: could not find partition #%d on mmc device #%d!\n", 213 __func__, part, dev); 214 return -ENODEV; 215 } 216 217 dfu->data.mmc.lba_start = partinfo.start; 218 dfu->data.mmc.lba_size = partinfo.size; 219 dfu->data.mmc.lba_blk_size = partinfo.blksz; 220 221 } else { 222 printf("%s: Memory layout (%s) not supported!\n", __func__, st); 223 return -ENODEV; 224 } 225 226 if (dfu->layout == DFU_FS_EXT4 || dfu->layout == DFU_FS_FAT) { 227 dfu->data.mmc.dev = simple_strtoul(s, &s, 10); 228 dfu->data.mmc.part = simple_strtoul(++s, &s, 10); 229 } 230 231 dfu->read_medium = dfu_read_medium_mmc; 232 dfu->write_medium = dfu_write_medium_mmc; 233 dfu->flush_medium = dfu_flush_medium_mmc; 234 235 /* initial state */ 236 dfu->inited = 0; 237 238 return 0; 239 } 240