/** DPF access library for AX206 based HW * * 12/2010 * * This is an ugly hack, because we use existing SCSI vendor specific * extensions to pass on our requests to the DPF. * * One day we might use a proper protocol like netpp. * */ // FIXME: Put all those SCSI commands in one (wrapped) place. #include "dpf.h" #include #include #include #include #ifdef DEBUG #define DEB(x) x #else #define DEB(x) #endif extern AccessMethods scsi_methods; extern AccessMethods hid_methods; /* static int dpf_query(DPFHANDLE h) { int ret; unsigned char buf[64]; // Do not change size static unsigned char cmd[16] = { 0xcd, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return wrap_scsi(h, cmd, sizeof(cmd), DIR_IN, buf, sizeof(buf)); } */ int dpf_open(const char *dev, DPFHANDLE *h) { int error = 0; DPFContext *dpf; int i; usb_dev_handle *u; int fd; if (!dev) { fprintf(stderr, "Please specify a string like 'usb0' or a sg device\n"); return DEVERR_OPEN; } if (strncmp(dev, "usb", 3) == 0) { i = dev[3] - '0'; error = dpf_usb_open(i, &u); if (error < 0) return error; if (!u) return DEVERR_OPEN; i = error; // USB mode error = 0; } else { fprintf(stderr, "Opening generic SCSI device '%s'\n", dev); if (sgdev_open(dev, &fd) < 0) return DEVERR_OPEN; i = MODE_SG; } dpf = (DPFHANDLE) malloc(sizeof(DPFContext)); if (!dpf) return DEVERR_MALLOC; dpf->flags = 0; dpf->mode = i; switch (dpf->mode) { case MODE_USB: dpf->dev.udev = u; error = probe(dpf); fprintf(stderr, "Got LCD dimensions: %dx%d\n", dpf->width, dpf->height); dpf->methods = scsi_methods; break; case MODE_USBHID: dpf->dev.udev = u; dpf->methods = hid_methods; break; case MODE_SG: dpf->dev.fd = fd; dpf->methods = scsi_methods; break; default: fprintf(stderr, "Unknown interface mode\n"); error = -1; } *h = dpf; return error; } void dpf_close(DPFContext *h) { switch (h->mode) { case MODE_USBHID: case MODE_SG: close(h->dev.fd); break; case MODE_USB: usb_release_interface(h->dev.udev, 0); usb_close(h->dev.udev); break; } free(h); } const char *dev_errstr(int err) { switch (err) { case DEVERR_FILE: return "File I/O error"; case DEVERR_OPEN: return "File open error"; case DEVERR_HEX: return "Hex file error"; case DEVERR_CHK: return "Checksum error"; case DEVERR_IO: return "Common I/O error"; case DEVERR_UNSUPP: return "Unsupported feature"; default: return "Unknown error"; } } int flash_probe(DPFContext *h, unsigned char *id) { return h->methods.flash_probe(h, id); } int flash_cmd(DPFContext *h, int command, int cmdlen, ADDR addr) { return h->methods.flash_cmd(h, command, cmdlen, addr); } /* Flash functions, API */ int flash_read(DPFContext *h, unsigned char *buf, ADDR offset, int len) { return h->methods.flash_read(h, buf, offset, len); } int flash_status_wait(DPFContext *h, uint8_t mask) { int error; uint8_t status; do { error = h->methods.flash_status(h, &status); } while ((status & mask) && !error); return error; } int flash_write(DPFContext *h, const unsigned char *buf, ADDR offset, int len) { int n; int error = 0; while (len > 0) { error = h->methods.flash_cmd(h, SPM_WREN, 1, 0); DEB(printf("Write pages at %06x\n", offset)); n = h->methods.flash_writechunk(h, buf, offset, len); error = flash_status_wait(h, SPS_WIP); if (n < 0) break; len -= n; buf += n; offset += n; } return error; } /* Mid level flash */ int load_ihx(DPFContext *h, const char *fname, unsigned char *data, unsigned int *buflen, unsigned int reloc) { unsigned char csum_is, csum_need; int ret; FILE *f; static char line[512]; static unsigned char buf[0x100]; int count; unsigned int addr, len, type; unsigned short b; unsigned int total = 0; DEB(printf("Opening %s...\n", fname)); f = fopen(fname, "r"); if (f == NULL) { return DEVERR_OPEN; } while(1) { fgets(line, sizeof(line), f); if (feof(f) || ferror(f)) break; if ((line[0] != ':') || (strlen(line) < 9)) { fprintf(stderr, "invalid line in ihx\n"); break; } ret = sscanf(&line[1], "%02x", &len); if (ret != 1) { ret = DEVERR_HEX; break; } ret = sscanf(&line[3], "%04x", &addr); if (ret != 1) { ret = DEVERR_HEX; break; } ret = sscanf(&line[7], "%02x", &type); if (ret != 1) { ret = DEVERR_HEX; break; } #ifdef DEBUG printf("len %u addr %04x type %u\n", len, addr, type); #endif if (type == 1) break; if (type != 0) { fprintf(stderr, "ihx: unknown type %u\n", type); continue; } csum_need = len + (addr & 0xff) + (addr >> 8) + type; total += len; if (total > *buflen) { fprintf(stderr, "Buffer length exceeded. IHX file too big.\n"); ret = DEVERR_HEX; break; } if (len > sizeof(buf)) { fprintf(stderr, "Buffer length exceeded. Too long lines.\n"); ret = DEVERR_HEX; break; } for(count = 0; count < len; count++) { ret = sscanf(&line[9 + count * 2], "%02hx", &b); if (ret != 1) { fprintf(stderr, "hex file: could not parse data!\n"); break; } buf[count] = b; csum_need += buf[count]; } if (ret != 1) { ret = DEVERR_HEX; break; } ret = sscanf(&line[9 + len * 2], "%02hx", &b); if (ret != 1) { ret = DEVERR_HEX; break; } csum_is = b; if (((csum_need+csum_is) & 0xff) != 0x00) { fprintf(stderr, "ihx: checksum failure! is: %02x should be:%02x\n", csum_is, csum_need); ret = DEVERR_CHK; break; } if (addr < reloc) { fprintf(stderr, "Bad relocation value\n"); ret = DEVERR_HEX; break; } // Copy to data buffer at relocated address if (data) { DEB(printf("Patching at offset %08x, chunk size %d\n", addr - reloc, len)); memcpy(&data[addr - reloc], buf, len); } else { DEB(printf("Writing to %04x (CODE: %04x), chunk size %d\n", addr - reloc, addr, len)); h->methods.mem_write(h, addr - reloc, buf, len); } } *buflen = total; fclose(f); return ret; } int flash_erase_full(DPFContext *h) { flash_cmd(h, SPM_RES, 1, 0); flash_cmd(h, SPM_WRSR, 2, 0); // clr status flash_cmd(h, SPM_WREN, 1, 0); flash_cmd(h, SPM_FLASH_BE, 1, 0); printf("Erase full flash...\n"); return flash_status_wait(h, SPS_WIP); } int flash_erase(DPFContext *h, ADDR addr) { int error; flash_cmd(h, SPM_RES, 1, 0); flash_cmd(h, SPM_WREN, 1, 0); flash_cmd(h, SPM_WRSR, 2, 0); // clr status // now erase flash sector: flash_cmd(h, SPM_WREN, 1, 0); flash_cmd(h, SPM_FLASH_SE, 4, addr); error = flash_status_wait(h, SPS_WIP); flash_cmd(h, SPM_WRDI, 1, 0); return error; } int dpf_flash_lock(DPFContext *h, char enable) { if (h->methods.flash_lock) return h->methods.flash_lock(h, enable); else return DEVERR_UNSUPP; } int patch_sector(DPFContext *h, ADDR reloc, unsigned long addr, const char *hexfile) { int error; unsigned short offset; static unsigned char readbuf[0x10000]; unsigned int len = sizeof(readbuf); offset = addr & 0xffff; addr -= offset; error = flash_read(h, readbuf, addr, 0x10000); if (error < 0) { perror("Reading flash"); return error; } error = load_ihx(h, hexfile, &readbuf[offset], &len, reloc); if (error < 0) { fprintf(stderr, "Failed to load HEX file\n"); return error; } // Lock DPF handler so nothing can interfere with the flashing (in case // we flash ourselves) dpf_flash_lock(h, 1); error = flash_cmd(h, SPM_WREN, 1, 0); error = flash_cmd(h, SPM_WRSR, 2, 0); // clr status error = flash_erase(h, addr); if (error < 0) return error; error = flash_write(h, readbuf, addr, 0x10000); // clr status dpf_flash_lock(h, 0); return error; } //////////////////////////////////////////////////////////////////////////// // High level functions, generic // These require a hacked command handler with extended command set. int load_hexfile(DPFContext *h, const char *hexfile) { unsigned int len = 0xc000; int error; error = load_ihx(h, hexfile, 0, &len, 0x800); return error; } #define CHUNK_SIZE 1024 int read_mem(DPFContext *h, unsigned char *buf, ADDR src, unsigned short len) { int error = 0; if (!h->methods.mem_read) return DEVERR_UNSUPP; while (len > CHUNK_SIZE && error >= 0) { error = h->methods.mem_read(h, buf, src, CHUNK_SIZE); src += CHUNK_SIZE; len -= CHUNK_SIZE; buf += CHUNK_SIZE; } error = h->methods.mem_read(h, buf, src, len); return error; } int write_mem(DPFContext *h, ADDR dst, const unsigned char *buf, unsigned short len) { return h->methods.mem_write(h, dst, buf, len); } int code_go(DPFContext *h, ADDR start) { printf("Executing applet..\n"); if (h->methods.go) return h->methods.go(h, start); return DEVERR_UNSUPP; } //////////////////////////////////////////////////////////////////////////// // High level functions, DPF specific /* Bootstrap mode: Expects contiguous memory block to download, then jumps * into start address */ int dpf_bootstrap(DPFContext *h, ADDR dest, unsigned char *src, unsigned short n, ADDR start) { if (h->methods.bootstrap) return h->methods.bootstrap(h, dest, src, n, start); else return DEVERR_UNSUPP; }