/********************************************************************** * ********************************************************************** 0 1 2 3 4 :1000100084C083C082C081C080C07FC07EC07DC0DC ~~ データ長 ~~~~ アドレス ~~ レコードタイプ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~データ ~~チェックサム. */ int read_ihex(char *s) { int c; if(s[0] == ':') { read_hex_string(s+1); c = databuf[3]; parse_ihex(c,databuf,dataidx); } return 0; }
static int firmware_validate(const char *firmware_path, struct controller_param **params) { FILE *f = fopen(firmware_path, "r"); if(!f) { perror("Opening firmware file failed"); return 0; } int ret = 0; char buf[256]; uint32_t offset = 0; bool got_type = false; while(fgets(buf, 256, f)) { chomp(buf); if(buf[0] == '#') continue; else if(buf[0] == 'C') { const char *name = &buf[2]; size_t name_len = strlen(name); got_type = true; struct controller_param *p; *params = NULL; for(p = controller_params; p->name; p++) if(!strncasecmp(p->name, name, name_len)) { *params = p; break; } if(!*params) { log("Unknown controller type \"%s\"", name); ret = 0; goto out; } } else if(buf[0] == ':') { if(!got_type) { log("Got IHEX before type statement"); ret = 0; goto out; } uint32_t new_offset, length; enum ihex_record rtype = parse_ihex(buf, &new_offset, &length, NULL); switch(rtype) { case IHEX_INVALID: ret = 0; goto out; break; case IHEX_DATA: if(new_offset < offset) { log("IHEX is not monotonic"); ret = 0; goto out; } offset = new_offset; if(offset + length > PAGE_ROUND_DOWN((*params)->pagesize, (*params)->bootloader_offset)) { log("Data reaches into bootloader area"); ret = 0; goto out; } break; case IHEX_EOF: break; default: log("Unsupported IHEX record %02X", rtype); ret = 0; goto out; break; } } else { log("Got invalid line: %s", buf); ret = 0; goto out; } } if(!got_type) { log("Controller type needs to be specified"); ret = 0; goto out; } ret = 1; out: fclose(f); return ret; }
int firmware_flash(int cansock, const char *firmware_dir, canid_t addr) { const char *file = firmware_make_path(firmware_dir, addr); log("Using firmware file %s", file); struct controller_param *params; if(!firmware_validate(file, ¶ms)) { log("Firmware file validation failed, sending reset"); can_send_reset(cansock, addr); return -1; } FILE *f = fopen(file, "r"); char buf[256]; uint8_t *pagebuf = malloc(params->pagesize); uint32_t fillcnt = 0, base_addr; while(fgets(buf, 256, f)) { chomp(buf); if(buf[0] != ':') continue; uint32_t offset, length; uint8_t *data; if(parse_ihex(buf, &offset, &length, &data) == IHEX_EOF) break; while(length) { if(fillcnt == 0) { // We're starting a new page base_addr = PAGE_ROUND_DOWN(params->pagesize, offset); memset(pagebuf, 0, offset - base_addr); fillcnt = offset - base_addr; can_send_set_zpointer(cansock, addr, base_addr); } uint32_t to_write = MIN(length, params->pagesize - fillcnt); memcpy(pagebuf + fillcnt, data, to_write); fillcnt += to_write; offset += to_write; length -= to_write; if(fillcnt == params->pagesize) { // page complete, flash it if(do_flash(cansock, addr, fillcnt, pagebuf) < 0) { log("Flashing page 0x%X failed", base_addr / 2); free(data); free(pagebuf); return -1; } fillcnt = 0; } memmove(data, data + to_write, length); } free(data); } if(fillcnt) { // there's some data left if(do_flash(cansock, addr, fillcnt, pagebuf) < 0) { log("Flashing page 0x%X failed", base_addr / 2); free(pagebuf); return -1; } } free(pagebuf); log("Resetting device"); can_send_reset(cansock, addr); return 0; }
/* * Load a firmware file into target RAM. device is the open libusbx * device, and the path is the name of the source file. Open the file, * parse the bytes, and write them in one or two phases. * * If stage == 0, this uses the first stage loader, built into EZ-USB * hardware but limited to writing on-chip memory or CPUCS. Everything * is written during one stage, unless there's an error such as the image * holding data that needs to be written to external memory. * * Otherwise, things are written in two stages. First the external * memory is written, expecting a second stage loader to have already * been loaded. Then file is re-parsed and on-chip memory is written. */ int ezusb_load_ram(libusb_device_handle *device, const char *path, int fx_type, int img_type, int stage) { FILE *image; uint32_t cpucs_addr; bool (*is_external)(uint32_t off, size_t len); struct ram_poke_context ctx; int status; uint8_t iic_header[8] = { 0 }; if (fx_type == FX_TYPE_FX3) return fx3_load_ram(device, path); image = fopen(path, "rb"); if (image == NULL) { logerror("%s: unable to open for input.\n", path); return -2; } else if (verbose > 1) logerror("open firmware image %s for RAM upload\n", path); if (img_type == IMG_TYPE_IIC) { if ( (fread(iic_header, 1, sizeof(iic_header), image) != sizeof(iic_header)) || (((fx_type == FX_TYPE_FX2LP) || (fx_type == FX_TYPE_FX2)) && (iic_header[0] != 0xC2)) || ((fx_type == FX_TYPE_AN21) && (iic_header[0] != 0xB2)) || ((fx_type == FX_TYPE_FX1) && (iic_header[0] != 0xB6)) ) { logerror("IIC image does not contain executable code - cannot load to RAM.\n"); return -1; } } /* EZ-USB original/FX and FX2 devices differ, apart from the 8051 core */ switch(fx_type) { case FX_TYPE_FX2LP: cpucs_addr = 0xe600; is_external = fx2lp_is_external; break; case FX_TYPE_FX2: cpucs_addr = 0xe600; is_external = fx2_is_external; break; default: cpucs_addr = 0x7f92; is_external = fx_is_external; break; } /* use only first stage loader? */ if (stage == 0) { ctx.mode = internal_only; /* if required, halt the CPU while we overwrite its code/data */ if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, false)) return -1; /* 2nd stage, first part? loader was already uploaded */ } else { ctx.mode = skip_internal; /* let CPU run; overwrite the 2nd stage loader later */ if (verbose) logerror("2nd stage: write external memory\n"); } /* scan the image, first (maybe only) time */ ctx.device = device; ctx.total = ctx.count = 0; status = parse[img_type](image, &ctx, is_external, ram_poke); if (status < 0) { logerror("unable to upload %s\n", path); return status; } /* second part of 2nd stage: rescan */ // TODO: what should we do for non HEX images there? if (stage) { ctx.mode = skip_external; /* if needed, halt the CPU while we overwrite the 1st stage loader */ if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, false)) return -1; /* at least write the interrupt vectors (at 0x0000) for reset! */ rewind(image); if (verbose) logerror("2nd stage: write on-chip memory\n"); status = parse_ihex(image, &ctx, is_external, ram_poke); if (status < 0) { logerror("unable to completely upload %s\n", path); return status; } } if (verbose) logerror("... WROTE: %d bytes, %d segments, avg %d\n", (int)ctx.total, (int)ctx.count, (int)(ctx.total/ctx.count)); /* if required, reset the CPU so it runs what we just uploaded */ if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, true)) return -1; return 0; }
/* * Load an Intel HEX file into target RAM. The fd is the open "usbfs" * device, and the path is the name of the source file. Open the file, * parse the bytes, and write them in one or two phases. * * If stage == 0, this uses the first stage loader, built into EZ-USB * hardware but limited to writing on-chip memory or CPUCS. Everything * is written during one stage, unless there's an error such as the image * holding data that needs to be written to external memory. * * Otherwise, things are written in two stages. First the external * memory is written, expecting a second stage loader to have already * been loaded. Then file is re-parsed and on-chip memory is written. */ int ezusb_load_ram (int fd, const char *path, int fx2, int stage) { FILE *image; unsigned short cpucs_addr; int (*is_external)(unsigned short off, size_t len); struct ram_poke_context ctx; int status; image = fopen (path, "r"); if (image == 0) { fprintf (stderr, "%s: unable to open for input.\n", path); return -2; } else if (verbose) fprintf (stderr, "open RAM hexfile image %s\n", path); /* EZ-USB original/FX and FX2 devices differ, apart from the 8051 core */ if (fx2) { cpucs_addr = 0xe600; is_external = fx2_is_external; } else { cpucs_addr = 0x7f92; is_external = fx_is_external; } /* use only first stage loader? */ if (!stage) { ctx.mode = internal_only; /* don't let CPU run while we overwrite its code/data */ if (!ezusb_cpucs (fd, cpucs_addr, 0)) return -1; /* 2nd stage, first part? loader was already downloaded */ } else { ctx.mode = skip_internal; /* let CPU run; overwrite the 2nd stage loader later */ if (verbose) fprintf (stderr, "2nd stage: write external memory\n"); } /* scan the image, first (maybe only) time */ ctx.device = fd; ctx.total = ctx.count = 0; status = parse_ihex (image, &ctx, is_external, ram_poke); if (status < 0) { fprintf (stderr, "unable to download %s\n", path); return status; } /* second part of 2nd stage: rescan */ if (stage) { ctx.mode = skip_external; /* don't let CPU run while we overwrite the 1st stage loader */ if (!ezusb_cpucs (fd, cpucs_addr, 0)) return -1; /* at least write the interrupt vectors (at 0x0000) for reset! */ rewind (image); if (verbose) fprintf (stderr, "2nd stage: write on-chip memory\n"); status = parse_ihex (image, &ctx, is_external, ram_poke); if (status < 0) { fprintf (stderr, "unable to completely download %s\n", path); return status; } } if (verbose) fprintf (stderr, "... WROTE: %d bytes, %d segments, avg %d\n", ctx.total, ctx.count, ctx.total / ctx.count); /* now reset the CPU so it runs what we just downloaded */ if (!ezusb_cpucs (fd, cpucs_addr, 1)) return -1; return 0; }