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 <errno.h> 12 #include <malloc.h> 13 #include <mmc.h> 14 #include <fat.h> 15 #include <dfu.h> 16 #include <linux/list.h> 17 #include <linux/compiler.h> 18 19 static bool dfu_reset_request; 20 static LIST_HEAD(dfu_list); 21 static int dfu_alt_num; 22 23 bool dfu_reset(void) 24 { 25 return dfu_reset_request; 26 } 27 28 void dfu_trigger_reset() 29 { 30 dfu_reset_request = true; 31 } 32 33 static int dfu_find_alt_num(const char *s) 34 { 35 int i = 0; 36 37 for (; *s; s++) 38 if (*s == ';') 39 i++; 40 41 return ++i; 42 } 43 44 int dfu_init_env_entities(char *interface, int dev) 45 { 46 const char *str_env; 47 char *env_bkp; 48 int ret; 49 50 str_env = getenv("dfu_alt_info"); 51 if (!str_env) { 52 error("\"dfu_alt_info\" env variable not defined!\n"); 53 return -EINVAL; 54 } 55 56 env_bkp = strdup(str_env); 57 ret = dfu_config_entities(env_bkp, interface, dev); 58 if (ret) { 59 error("DFU entities configuration failed!\n"); 60 return ret; 61 } 62 63 free(env_bkp); 64 return 0; 65 } 66 67 static unsigned char *dfu_buf; 68 static unsigned long dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE; 69 70 unsigned char *dfu_free_buf(void) 71 { 72 free(dfu_buf); 73 dfu_buf = NULL; 74 return dfu_buf; 75 } 76 77 unsigned char *dfu_get_buf(void) 78 { 79 char *s; 80 81 if (dfu_buf != NULL) 82 return dfu_buf; 83 84 s = getenv("dfu_bufsiz"); 85 dfu_buf_size = s ? (unsigned long)simple_strtol(s, NULL, 16) : 86 CONFIG_SYS_DFU_DATA_BUF_SIZE; 87 88 dfu_buf = memalign(CONFIG_SYS_CACHELINE_SIZE, dfu_buf_size); 89 if (dfu_buf == NULL) 90 printf("%s: Could not memalign 0x%lx bytes\n", 91 __func__, dfu_buf_size); 92 93 return dfu_buf; 94 } 95 96 static int dfu_write_buffer_drain(struct dfu_entity *dfu) 97 { 98 long w_size; 99 int ret; 100 101 /* flush size? */ 102 w_size = dfu->i_buf - dfu->i_buf_start; 103 if (w_size == 0) 104 return 0; 105 106 /* update CRC32 */ 107 dfu->crc = crc32(dfu->crc, dfu->i_buf_start, w_size); 108 109 ret = dfu->write_medium(dfu, dfu->offset, dfu->i_buf_start, &w_size); 110 if (ret) 111 debug("%s: Write error!\n", __func__); 112 113 /* point back */ 114 dfu->i_buf = dfu->i_buf_start; 115 116 /* update offset */ 117 dfu->offset += w_size; 118 119 puts("#"); 120 121 return ret; 122 } 123 124 int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) 125 { 126 int ret = 0; 127 int tret; 128 129 debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x offset: 0x%llx bufoffset: 0x%x\n", 130 __func__, dfu->name, buf, size, blk_seq_num, dfu->offset, 131 dfu->i_buf - dfu->i_buf_start); 132 133 if (!dfu->inited) { 134 /* initial state */ 135 dfu->crc = 0; 136 dfu->offset = 0; 137 dfu->bad_skip = 0; 138 dfu->i_blk_seq_num = 0; 139 dfu->i_buf_start = dfu_get_buf(); 140 if (dfu->i_buf_start == NULL) 141 return -ENOMEM; 142 dfu->i_buf_end = dfu_get_buf() + dfu_buf_size; 143 dfu->i_buf = dfu->i_buf_start; 144 145 dfu->inited = 1; 146 } 147 148 if (dfu->i_blk_seq_num != blk_seq_num) { 149 printf("%s: Wrong sequence number! [%d] [%d]\n", 150 __func__, dfu->i_blk_seq_num, blk_seq_num); 151 return -1; 152 } 153 154 /* DFU 1.1 standard says: 155 * The wBlockNum field is a block sequence number. It increments each 156 * time a block is transferred, wrapping to zero from 65,535. It is used 157 * to provide useful context to the DFU loader in the device." 158 * 159 * This means that it's a 16 bit counter that roll-overs at 160 * 0xffff -> 0x0000. By having a typical 4K transfer block 161 * we roll-over at exactly 256MB. Not very fun to debug. 162 * 163 * Handling rollover, and having an inited variable, 164 * makes things work. 165 */ 166 167 /* handle rollover */ 168 dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff; 169 170 /* flush buffer if overflow */ 171 if ((dfu->i_buf + size) > dfu->i_buf_end) { 172 tret = dfu_write_buffer_drain(dfu); 173 if (ret == 0) 174 ret = tret; 175 } 176 177 /* we should be in buffer now (if not then size too large) */ 178 if ((dfu->i_buf + size) > dfu->i_buf_end) { 179 error("Buffer overflow! (0x%p + 0x%x > 0x%p)\n", dfu->i_buf, 180 size, dfu->i_buf_end); 181 return -1; 182 } 183 184 memcpy(dfu->i_buf, buf, size); 185 dfu->i_buf += size; 186 187 /* if end or if buffer full flush */ 188 if (size == 0 || (dfu->i_buf + size) > dfu->i_buf_end) { 189 tret = dfu_write_buffer_drain(dfu); 190 if (ret == 0) 191 ret = tret; 192 } 193 194 /* end? */ 195 if (size == 0) { 196 /* Now try and flush to the medium if needed. */ 197 if (dfu->flush_medium) 198 ret = dfu->flush_medium(dfu); 199 printf("\nDFU complete CRC32: 0x%08x\n", dfu->crc); 200 201 /* clear everything */ 202 dfu_free_buf(); 203 dfu->crc = 0; 204 dfu->offset = 0; 205 dfu->i_blk_seq_num = 0; 206 dfu->i_buf_start = dfu_buf; 207 dfu->i_buf_end = dfu_buf; 208 dfu->i_buf = dfu->i_buf_start; 209 210 dfu->inited = 0; 211 212 } 213 214 return ret = 0 ? size : ret; 215 } 216 217 static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size) 218 { 219 long chunk; 220 int ret, readn; 221 222 readn = 0; 223 while (size > 0) { 224 /* get chunk that can be read */ 225 chunk = min(size, dfu->b_left); 226 /* consume */ 227 if (chunk > 0) { 228 memcpy(buf, dfu->i_buf, chunk); 229 dfu->crc = crc32(dfu->crc, buf, chunk); 230 dfu->i_buf += chunk; 231 dfu->b_left -= chunk; 232 dfu->r_left -= chunk; 233 size -= chunk; 234 buf += chunk; 235 readn += chunk; 236 } 237 238 /* all done */ 239 if (size > 0) { 240 /* no more to read */ 241 if (dfu->r_left == 0) 242 break; 243 244 dfu->i_buf = dfu->i_buf_start; 245 dfu->b_left = dfu->i_buf_end - dfu->i_buf_start; 246 247 /* got to read, but buffer is empty */ 248 if (dfu->b_left > dfu->r_left) 249 dfu->b_left = dfu->r_left; 250 ret = dfu->read_medium(dfu, dfu->offset, dfu->i_buf, 251 &dfu->b_left); 252 if (ret != 0) { 253 debug("%s: Read error!\n", __func__); 254 return ret; 255 } 256 dfu->offset += dfu->b_left; 257 dfu->r_left -= dfu->b_left; 258 259 puts("#"); 260 } 261 } 262 263 return readn; 264 } 265 266 int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) 267 { 268 int ret = 0; 269 270 debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", 271 __func__, dfu->name, buf, size, blk_seq_num, dfu->i_buf); 272 273 if (!dfu->inited) { 274 dfu->i_buf_start = dfu_get_buf(); 275 if (dfu->i_buf_start == NULL) 276 return -ENOMEM; 277 278 ret = dfu->read_medium(dfu, 0, dfu->i_buf_start, &dfu->r_left); 279 if (ret != 0) { 280 debug("%s: failed to get r_left\n", __func__); 281 return ret; 282 } 283 284 debug("%s: %s %ld [B]\n", __func__, dfu->name, dfu->r_left); 285 286 dfu->i_blk_seq_num = 0; 287 dfu->crc = 0; 288 dfu->offset = 0; 289 dfu->i_buf_end = dfu_get_buf() + dfu_buf_size; 290 dfu->i_buf = dfu->i_buf_start; 291 dfu->b_left = min(dfu_buf_size, dfu->r_left); 292 293 dfu->bad_skip = 0; 294 295 dfu->inited = 1; 296 } 297 298 if (dfu->i_blk_seq_num != blk_seq_num) { 299 printf("%s: Wrong sequence number! [%d] [%d]\n", 300 __func__, dfu->i_blk_seq_num, blk_seq_num); 301 return -1; 302 } 303 /* handle rollover */ 304 dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff; 305 306 ret = dfu_read_buffer_fill(dfu, buf, size); 307 if (ret < 0) { 308 printf("%s: Failed to fill buffer\n", __func__); 309 return -1; 310 } 311 312 if (ret < size) { 313 debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, dfu->crc); 314 puts("\nUPLOAD ... done\nCtrl+C to exit ...\n"); 315 316 dfu_free_buf(); 317 dfu->i_blk_seq_num = 0; 318 dfu->crc = 0; 319 dfu->offset = 0; 320 dfu->i_buf_start = dfu_buf; 321 dfu->i_buf_end = dfu_buf; 322 dfu->i_buf = dfu->i_buf_start; 323 dfu->b_left = 0; 324 325 dfu->bad_skip = 0; 326 327 dfu->inited = 0; 328 } 329 330 return ret; 331 } 332 333 static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt, 334 char *interface, int num) 335 { 336 char *st; 337 338 debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num); 339 st = strsep(&s, " "); 340 strcpy(dfu->name, st); 341 342 dfu->dev_num = num; 343 dfu->alt = alt; 344 345 /* Specific for mmc device */ 346 if (strcmp(interface, "mmc") == 0) { 347 if (dfu_fill_entity_mmc(dfu, s)) 348 return -1; 349 } else if (strcmp(interface, "nand") == 0) { 350 if (dfu_fill_entity_nand(dfu, s)) 351 return -1; 352 } else if (strcmp(interface, "ram") == 0) { 353 if (dfu_fill_entity_ram(dfu, s)) 354 return -1; 355 } else { 356 printf("%s: Device %s not (yet) supported!\n", 357 __func__, interface); 358 return -1; 359 } 360 361 return 0; 362 } 363 364 void dfu_free_entities(void) 365 { 366 struct dfu_entity *dfu, *p, *t = NULL; 367 368 list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) { 369 list_del(&dfu->list); 370 t = dfu; 371 } 372 if (t) 373 free(t); 374 INIT_LIST_HEAD(&dfu_list); 375 } 376 377 int dfu_config_entities(char *env, char *interface, int num) 378 { 379 struct dfu_entity *dfu; 380 int i, ret; 381 char *s; 382 383 dfu_alt_num = dfu_find_alt_num(env); 384 debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num); 385 386 dfu = calloc(sizeof(*dfu), dfu_alt_num); 387 if (!dfu) 388 return -1; 389 for (i = 0; i < dfu_alt_num; i++) { 390 391 s = strsep(&env, ";"); 392 ret = dfu_fill_entity(&dfu[i], s, i, interface, num); 393 if (ret) 394 return -1; 395 396 list_add_tail(&dfu[i].list, &dfu_list); 397 } 398 399 return 0; 400 } 401 402 const char *dfu_get_dev_type(enum dfu_device_type t) 403 { 404 const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM" }; 405 return dev_t[t]; 406 } 407 408 const char *dfu_get_layout(enum dfu_layout l) 409 { 410 const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2", 411 "EXT3", "EXT4", "RAM_ADDR" }; 412 return dfu_layout[l]; 413 } 414 415 void dfu_show_entities(void) 416 { 417 struct dfu_entity *dfu; 418 419 puts("DFU alt settings list:\n"); 420 421 list_for_each_entry(dfu, &dfu_list, list) { 422 printf("dev: %s alt: %d name: %s layout: %s\n", 423 dfu_get_dev_type(dfu->dev_type), dfu->alt, 424 dfu->name, dfu_get_layout(dfu->layout)); 425 } 426 } 427 428 int dfu_get_alt_number(void) 429 { 430 return dfu_alt_num; 431 } 432 433 struct dfu_entity *dfu_get_entity(int alt) 434 { 435 struct dfu_entity *dfu; 436 437 list_for_each_entry(dfu, &dfu_list, list) { 438 if (dfu->alt == alt) 439 return dfu; 440 } 441 442 return NULL; 443 } 444 445 int dfu_get_alt(char *name) 446 { 447 struct dfu_entity *dfu; 448 449 list_for_each_entry(dfu, &dfu_list, list) { 450 if (!strncmp(dfu->name, name, strlen(dfu->name))) 451 return dfu->alt; 452 } 453 454 return -ENODEV; 455 } 456