X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=mx%2Flinux%2Fmain.c;h=9f9fdbe23304e2bcd27afa77dbe083684c795d98;hb=95122097e24114e884b48d432aa9e96d7c83fd28;hp=6b95bc089302ba9bc0048608f808f045f5124a1a;hpb=726b85c8e9047aa8584ad1a8ab8d7a6b0595bb46;p=megadrive.git diff --git a/mx/linux/main.c b/mx/linux/main.c index 6b95bc0..9f9fdbe 100644 --- a/mx/linux/main.c +++ b/mx/linux/main.c @@ -1,3 +1,4 @@ +/* FIXME: RAM odd */ #include #include #include @@ -19,39 +20,78 @@ static const struct { { 0x03eb, 0x202d, "32MX+UF Game Device" }, }; -/*****************************************************************************/ +#define VERSION "0.8" + +#define IO_BLK_SIZE 0x2000 /* 8K */ +#define IO_RAM_BLK_SIZE 256 + +#define CMD_ATM_READY 0x22 +#define CMD_SEC_GET_NAME 'G' /* filename r/w */ +#define CMD_SEC_PUT_NAME 'P' +#define CMD_SEC_DEVID 'L' /* read flash device ID */ +#define CMD_SEC_ERASE 'E' +#define CMD_SEC_READY 'C' /* is flash ready? */ +#define CMD_SEC_READ 'R' +#define CMD_SEC_WRITE 'W' +#define CMD_SEC_RAM_READ 'D' /* not implemented? */ +#define CMD_SEC_RAM_WRITE 'U' +#define CMD_SEC_COMPAT '$' /* set RAM mode */ + +/* bus controllers */ +#define CTL_DATA_BUS 0x55 +#define CTL_ADDR_BUS 0xAA -#define CMD_ATM_READY 0x22 +#define W_COUNTER 0xA0 +#define W_CNT_WRITE 0x01 +#define W_CNT_READ 0x00 -#define CTL_DATA_BUS 0x55 /* refers to data bus controller */ -#define CTL_ADDR_BUS 0xAA /* .. address bus .. */ +#define FILENAME_ROM0 0 +#define FILENAME_ROM1 1 +#define FILENAME_RAM 2 + +/* windows app sets to 0x80 on init + * checkboxes use 0x41 0x42 0x43 (?) + * r,w Ram/ROM uses 0x23/0x21 + */ +#define C_MODE_4M_NORAM 0x41 /* RAM always off */ +#define C_MODE_4M_RAM 0x42 /* RAM switched by game */ +#define C_MODE_2M_RAM 0x43 +#define C_RAM_TMP_OFF 0x21 +#define C_RAM_TMP_ON 0x23 typedef struct { u8 magic[4]; - u8 reserved[11]; + u8 reserved[8]; + u8 write_flag; + u8 reserved2[2]; u8 magic2; u8 mx_cmd; - union { + union { /* 0x11 */ struct { u8 which_device; } dev_info; struct { - u8 addrb2; /* most significant */ + u8 addrb2; /* most significant (BE) */ u8 addrb1; u8 addrb0; - u8 num_pages; + u8 param; /* 64 byte usb packets for i/o */ + u8 param2; } rom_rw; struct { - u8 position; - } filename; + u8 which; + } filename, mode; struct { - u8 times_cmd; + u8 cmd; u8 action; - u8 cntb3; - u8 cntb2; - u8 cntb1; - u8 cntb0; - } times_write; + u8 b0; /* LE */ + u8 b1; + u8 b2; + u8 b3; + } write_cnt; + struct { + u8 which; + u8 dev_id; + } rom_id; }; u8 pad[8]; } dev_cmd_t; @@ -62,39 +102,703 @@ typedef struct { char names[56]; } dev_info_t; +typedef struct { + u32 start_addr; + u32 end_addr; + u32 page_size; +} page_table_t; + +static const page_table_t p_AM29LV320DB[] = +{ + { 0x000000, 0x00ffff, 0x002000 }, + { 0x010000, 0x3fffff, 0x010000 }, + { 0x000000, 0x000000, 0x000000 }, +}; + +static const page_table_t p_AM29LV320DT[] = +{ + { 0x000000, 0x3effff, 0x010000 }, + { 0x3f0000, 0x3fffff, 0x002000 }, + { 0x000000, 0x000000, 0x000000 }, +}; + +static const page_table_t p_2x_16[] = +{ + { 0x000000, 0x003fff, 0x004000 }, + { 0x004000, 0x007fff, 0x002000 }, + { 0x008000, 0x00ffff, 0x008000 }, + { 0x010000, 0x1fffff, 0x010000 }, + { 0x200000, 0x203fff, 0x004000 }, + { 0x204000, 0x207fff, 0x002000 }, + { 0x208000, 0x20ffff, 0x008000 }, + { 0x210000, 0x3fffff, 0x010000 }, + { 0x000000, 0x000000, 0x000000 }, +}; + +/*****************************************************************************/ + static void prepare_cmd(dev_cmd_t *dev_cmd, u8 cmd) { memset(dev_cmd, 0, sizeof(*dev_cmd)); memcpy(dev_cmd->magic, "USBC", 4); - dev_cmd->magic2 = 0x67; /* "MySCSICommand" */ + dev_cmd->magic2 = 0x67; /* MySCSICommand, EXCOMMAND */ dev_cmd->mx_cmd = cmd; } -static int write_cmd(struct usb_dev_handle *dev, dev_cmd_t *cmd) +static int write_data(struct usb_dev_handle *dev, void *data, int len) { - int ret = usb_bulk_write(dev, 0x03, (char *)cmd, sizeof(*cmd), 2000); + int ret = usb_bulk_write(dev, 0x03, data, len, 2000); if (ret < 0) { fprintf(stderr, "failed to write:\n"); fprintf(stderr, "%s (%d)\n", usb_strerror(), ret); - } else if (ret != sizeof(*cmd)) - printf("write_cmd: wrote only %d of %d bytes\n", ret, sizeof(*cmd)); + } else if (ret != len) + printf("write_cmd: wrote only %d of %d bytes\n", ret, len); return ret; } -static int read_response(struct usb_dev_handle *dev, void *buff, int size) +static int write_cmd(struct usb_dev_handle *dev, dev_cmd_t *cmd) +{ + return write_data(dev, cmd, sizeof(*cmd)); +} + +static int read_data(struct usb_dev_handle *dev, void *buff, int size) { int ret = usb_bulk_read(dev, 0x82, buff, size, 2000); if (ret < 0) { fprintf(stderr, "failed to read:\n"); fprintf(stderr, "%s (%d)\n", usb_strerror(), ret); - } else if (ret != size) - printf("read_response: read only %d of %d bytes\n", ret, size); + } +/* + else if (ret != size) + printf("read_data: read only %d of %d bytes\n", ret, size); +*/ + return ret; +} + +static int read_info(struct usb_dev_handle *device, u8 ctl_id, dev_info_t *info) +{ + dev_cmd_t cmd; + int ret; + + prepare_cmd(&cmd, CMD_ATM_READY); + cmd.dev_info.which_device = ctl_id; + memset(info, 0, sizeof(*info)); + + ret = write_cmd(device, &cmd); + if (ret < 0) + return ret; + + ret = read_data(device, info, sizeof(*info)); + if (ret < 0) + return ret; + + return 0; +} + +static void printf_info(dev_info_t *info) +{ + printf(" firmware version: %X.%X.%X%c\n", info->firmware_ver[0], + info->firmware_ver[1], info->firmware_ver[2], info->firmware_ver[3]); + printf(" bootloader version: %X.%X.%X%c\n", info->bootloader_ver[0], + info->bootloader_ver[1], info->bootloader_ver[2], info->bootloader_ver[3]); + info->names[sizeof(info->names) - 1] = 0; + printf(" device name: %s\n", info->names); +} + +static void print_progress(u32 done, u32 total) +{ + int i, step; + + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); /* 20 */ + printf("\b\b\b\b\b\b"); + printf("%06x/%06x |", done, total); + + step = total / 20; + for (i = step; i <= total; i += step) + printf("%c", done >= i ? '=' : '-'); + printf("| %3d%%", done * 100 / total); + fflush(stdout); +} + +static int read_filename(struct usb_dev_handle *dev, char *dst, int len, u8 which) +{ + char buff[65]; + dev_cmd_t cmd; + int ret; + + prepare_cmd(&cmd, CMD_SEC_GET_NAME); + cmd.filename.which = which; + memset(buff, 0, sizeof(buff)); + + ret = write_cmd(dev, &cmd); + if (ret < 0) + return ret; + + ret = read_data(dev, buff, 64); + if (ret < 0) + return ret; + + strncpy(dst, buff, len); + dst[len - 1] = 0; + + return 0; +} + +static int write_filename(struct usb_dev_handle *dev, const char *fname, u8 which) +{ + dev_cmd_t cmd; + char buff[64]; + int ret, len; + + len = strlen(fname); + if (len > 63) + len = 63; + strncpy(buff, fname, len); + buff[len] = 0; + + prepare_cmd(&cmd, CMD_SEC_PUT_NAME); + cmd.filename.which = which; + + ret = write_cmd(dev, &cmd); + if (ret < 0) + return ret; + + return write_data(dev, buff, len + 1); +} + +static int read_erase_counter(struct usb_dev_handle *dev, u32 *val) +{ + dev_info_t dummy_info; + dev_cmd_t cmd; + u8 buff[4]; + int ret; + + /* must perform dummy info read here, + * or else device hangs after close (firmware bug?) */ + ret = read_info(dev, CTL_DATA_BUS, &dummy_info); + if (ret < 0) + return ret; + + prepare_cmd(&cmd, CMD_ATM_READY); + cmd.write_cnt.cmd = W_COUNTER; + cmd.write_cnt.action = W_CNT_READ; + + ret = write_cmd(dev, &cmd); + if (ret < 0) + return ret; + + ret = read_data(dev, buff, sizeof(buff)); + if (ret < 0) + return ret; + + *val = *(u32 *)buff; + return 0; +} + +static int read_flash_rom_id(struct usb_dev_handle *dev, int is_second, u32 *val) +{ + dev_cmd_t cmd; + u8 buff[2]; + int ret; + + prepare_cmd(&cmd, CMD_SEC_DEVID); + cmd.rom_id.which = is_second ? 0x10 : 0; + cmd.rom_id.dev_id = 0; + + ret = write_cmd(dev, &cmd); + if (ret < 0) + return ret; + + ret = read_data(dev, buff, sizeof(buff)); + if (ret < 0) + return ret; + + *val = *(u16 *)buff << 16; + + cmd.rom_id.dev_id = 1; + ret = write_cmd(dev, &cmd); + if (ret < 0) + return ret; + + ret = read_data(dev, buff, sizeof(buff)); + if (ret < 0) + return ret; + + *val |= *(u16 *)buff; + return 0; +} + +static const page_table_t *get_page_table(u32 rom_id) +{ + switch (rom_id) { + case 0x0100F922: + return p_AM29LV320DB; + case 0x0100F422: + return p_AM29LV320DT; + case 0x01004922: + case 0xC2004922: + return p_2x_16; + default: + fprintf(stderr, "unrecognized ROM id: %08x\n", rom_id); + } + + return NULL; +} + +static int get_page_size(const page_table_t *table, u32 addr, u32 *size) +{ + const page_table_t *t; + + for (t = table; t->end_addr != 0; t++) { + if (addr >= t->start_addr && addr <= t->end_addr) { + *size = t->page_size; + return 0; + } + } + + if (addr == t[-1].end_addr + 1) + return 1; /* no more */ + + fprintf(stderr, "get_page_size: failed on addr %06x\n", addr); + return -1; +} + +static int set_ram_mode(struct usb_dev_handle *dev, u8 mode) +{ + dev_cmd_t cmd; + u8 buff[2]; + int ret; + + prepare_cmd(&cmd, CMD_SEC_COMPAT); + cmd.write_flag = 1; + cmd.mode.which = mode; + + ret = write_cmd(dev, &cmd); + if (ret < 0) + goto end; + + ret = read_data(dev, buff, sizeof(buff)); + +end: + if (ret < 0) + fprintf(stderr, "warning: failed to set RAM mode\n"); + return ret; +} + +/* limitations: + * - bytes must be multiple of 64 + * - bytes must be less than 16k + * - must perform even number of reads, or dev hangs on exit (firmware bug?) */ +static int rw_dev_block(struct usb_dev_handle *dev, u32 addr, void *buffer, int bytes, int mx_cmd) +{ + dev_cmd_t cmd; + int ret; + + prepare_cmd(&cmd, mx_cmd); + if (mx_cmd == CMD_SEC_WRITE || mx_cmd == CMD_SEC_RAM_WRITE) + cmd.write_flag = 1; + cmd.rom_rw.addrb2 = addr >> (16 + 1); + cmd.rom_rw.addrb1 = addr >> (8 + 1); + cmd.rom_rw.addrb0 = addr >> 1; + cmd.rom_rw.param = bytes / 64; + if (mx_cmd == CMD_SEC_WRITE || mx_cmd == CMD_SEC_RAM_WRITE) + cmd.rom_rw.param2 = 1; /* ? */ + + ret = write_cmd(dev, &cmd); + if (ret < 0) + return ret; + + bytes &= ~63; + + if (mx_cmd == CMD_SEC_WRITE || mx_cmd == CMD_SEC_RAM_WRITE) + ret = write_data(dev, buffer, bytes); + else + ret = read_data(dev, buffer, bytes); + if (ret < 0) + return ret; + + if (ret != bytes) + fprintf(stderr, "rw_dev_block warning: done only %d/%d bytes\n", ret, bytes); + + return ret; +} + +static int read_write_rom(struct usb_dev_handle *dev, u32 addr, void *buffer, int bytes, int is_write) +{ + int mx_cmd = is_write ? CMD_SEC_WRITE : CMD_SEC_READ; + int total_bytes = bytes; + u8 *buff = buffer; + u8 dummy[64 * 4]; + int count, ret; + + if (addr & 1) + fprintf(stderr, "read_write_rom: can't handle odd address %06x, " + "LSb will be ignored\n", addr); + if (bytes & 63) + fprintf(stderr, "read_write_rom: byte count must be multiple of 64, " + "last %d bytes will not be handled\n", bytes & 63); + + set_ram_mode(dev, C_RAM_TMP_OFF); + + printf("%s flash ROM...\n", is_write ? "writing to" : "reading"); + + /* do i/o in blocks */ + for (count = 0; bytes >= IO_BLK_SIZE; count++) { + print_progress(buff - (u8 *)buffer, total_bytes); + + ret = rw_dev_block(dev, addr, buff, IO_BLK_SIZE, mx_cmd); + if (ret < 0) + return ret; + buff += IO_BLK_SIZE; + addr += IO_BLK_SIZE; + bytes -= IO_BLK_SIZE; + } + print_progress(buff - (u8 *)buffer, total_bytes); + + ret = 0; + if (bytes != 0) { + ret = rw_dev_block(dev, addr, buff, bytes, mx_cmd); + count++; + print_progress(total_bytes, total_bytes); + } + + if (count & 1) + /* work around rw_dev_block() limitation 3 (works for reads only?) */ + rw_dev_block(dev, 0, dummy, sizeof(dummy), 0); + + printf("\n"); + return ret; +} + +static int read_write_ram(struct usb_dev_handle *dev, void *buffer, int bytes, int is_write) +{ + int mx_cmd = is_write ? CMD_SEC_RAM_WRITE : CMD_SEC_READ; + int total_bytes = bytes; + u8 *buff = buffer; + u32 addr = 0x200000; + int i, ret = 0; + + if (bytes % IO_RAM_BLK_SIZE) + fprintf(stderr, "read_write_ram: byte count must be multiple of %d, " + "last %d bytes will not be handled\n", IO_RAM_BLK_SIZE, + bytes % IO_RAM_BLK_SIZE); + + set_ram_mode(dev, C_RAM_TMP_ON); + + printf("%s RAM...\n", is_write ? "writing to" : "reading"); + + /* do i/o in blocks */ + while (bytes >= IO_RAM_BLK_SIZE) { + print_progress(buff - (u8 *)buffer, total_bytes); + + ret = rw_dev_block(dev, addr, buff, IO_RAM_BLK_SIZE, mx_cmd); + if (ret < 0) + return ret; + buff += IO_RAM_BLK_SIZE; + addr += IO_RAM_BLK_SIZE; + bytes -= IO_RAM_BLK_SIZE; + } + print_progress(buff - (u8 *)buffer, total_bytes); + + /* only D0-D7 connected.. */ + for (i = 0; i < total_bytes; i += 2) + ((u8 *)buffer)[i] = 0; + + printf("\n"); + return ret; + +} + +static int increment_erase_cnt(struct usb_dev_handle *dev) +{ + dev_cmd_t cmd; + u8 buff[4]; + u32 cnt; + int ret; + + ret = read_erase_counter(dev, &cnt); + if (ret != 0) + return ret; + + if (cnt == (u32)-1) { + fprintf(stderr, "flash erase counter maxed out!\n"); + fprintf(stderr, "(wow, did you really erase so many times?)\n"); + return -1; + } + + cnt++; + + prepare_cmd(&cmd, CMD_ATM_READY); + cmd.write_cnt.cmd = W_COUNTER; + cmd.write_cnt.action = W_CNT_WRITE; + cmd.write_cnt.b3 = cnt >> 24; + cmd.write_cnt.b2 = cnt >> 16; + cmd.write_cnt.b1 = cnt >> 8; + cmd.write_cnt.b0 = cnt; + + ret = write_cmd(dev, &cmd); + if (ret < 0) + return ret; + + ret = read_data(dev, buff, sizeof(buff)); + if (ret < 0) + return ret; + + return cnt; +} + +static int erase_page(struct usb_dev_handle *dev, u32 addr, int whole) +{ + dev_cmd_t cmd; + u8 buff[5]; + int i, ret; + + prepare_cmd(&cmd, CMD_SEC_ERASE); + cmd.write_flag = 1; + cmd.rom_rw.addrb2 = addr >> (16 + 1); + cmd.rom_rw.addrb1 = addr >> (8 + 1); + cmd.rom_rw.addrb0 = addr >> 1; + cmd.rom_rw.param = whole ? 0x10 : 0; + + ret = write_cmd(dev, &cmd); + if (ret < 0) + return ret; + + ret = read_data(dev, buff, sizeof(buff)); + if (ret < 0) + return ret; + + prepare_cmd(&cmd, CMD_SEC_READY); + cmd.rom_rw.addrb2 = addr >> (16 + 1); + cmd.rom_rw.addrb1 = addr >> (8 + 1); + cmd.rom_rw.addrb0 = addr >> 1; + + for (i = 0; i < 100; i++) { + ret = write_cmd(dev, &cmd); + if (ret < 0) + return ret; + + ret = read_data(dev, buff, sizeof(buff)); + if (ret < 0) + return ret; + + if (ret > 4 && buff[4] == 1) + break; + + usleep((whole ? 600 : 20) * 1000); + } + + if (i == 100) { + fprintf(stderr, "\ntimeout waiting for erase to complete\n"); + return -1; + } + + return 0; +} + +static int erase_seq(struct usb_dev_handle *dev, u32 size) +{ + const page_table_t *table; + u32 addr, page_size = 0; + u32 rom0_id, rom1_id; + int count, ret; + + ret = read_flash_rom_id(dev, 0, &rom0_id); + if (ret < 0) + return ret; + + ret = read_flash_rom_id(dev, 1, &rom1_id); + if (ret < 0) + return ret; + + if (rom0_id != rom1_id) + fprintf(stderr, "Warning: flash ROM ids differ: %08x %08x\n", + rom0_id, rom1_id); + + table = get_page_table(rom0_id); + if (table == NULL) + return -1; + + ret = increment_erase_cnt(dev); + if (ret < 0) + fprintf(stderr, "warning: coun't increase erase counter\n"); + + printf("erasing flash... (erase count=%u)\n", ret); + + for (addr = 0, count = 0; addr < size; addr += page_size, count++) { + print_progress(addr, size); + + ret = erase_page(dev, addr, 0); + if (ret < 0) + return ret; + + ret = get_page_size(table, addr, &page_size); + if (ret != 0) + break; + } + + if (count & 1) + /* ??? */ + /* must submit even number of erase commands (fw bug?) */ + erase_page(dev, 0, 0); + + print_progress(addr, size); + printf("\n"); + + return ret; +} + +static int erase_all(struct usb_dev_handle *dev, u32 size) +{ + int ret; + + ret = increment_erase_cnt(dev); + if (ret < 0) + fprintf(stderr, "warning: couldn't increase erase counter\n"); + + printf("erasing flash0, count=%u ...", ret); + fflush(stdout); + + ret = erase_page(dev, 0xaaa, 1); + if (ret != 0) + return ret; + + if (size > 0x200000) { + printf(" done.\n"); + printf("erasing flash1..."); + fflush(stdout); + + ret = erase_page(dev, 0x200aaa, 1); + } + printf(" done.\n"); return ret; } +static int print_device_info(struct usb_dev_handle *dev) +{ + u32 counter, rom0_id, rom1_id; + dev_info_t info; + int ret; + + printf("data bus controller:\n"); + ret = read_info(dev, CTL_DATA_BUS, &info); + if (ret < 0) + return ret; + printf_info(&info); + + printf("address bus controller:\n"); + ret = read_info(dev, CTL_ADDR_BUS, &info); + if (ret < 0) + return ret; + printf_info(&info); + + ret = read_erase_counter(dev, &counter); + if (ret < 0) + return ret; + printf("flash erase count: %u\n", counter); + + ret = read_flash_rom_id(dev, 0, &rom0_id); + if (ret < 0) + return ret; + printf("flash rom0 id: %08x\n", rom0_id); + + ret = read_flash_rom_id(dev, 1, &rom1_id); + if (ret < 0) + return ret; + printf("flash rom1 id: %08x\n", rom1_id); + + return 0; +} + +static int print_game_info(struct usb_dev_handle *dev) +{ + char fname[65]; + int ret; + + ret = read_filename(dev, fname, sizeof(fname), FILENAME_ROM0); + if (ret < 0) + return ret; + printf("ROM filename: %s\n", fname); + + ret = read_filename(dev, fname, sizeof(fname), FILENAME_RAM); + if (ret < 0) + return ret; + printf("SRAM filename: %s\n", fname); + + return 0; +} + +static int read_file(const char *fname, void **buff_out, int *size, int limit) +{ + int file_size, ret; + void *data; + FILE *file; + + file = fopen(fname, "rb"); + if (file == NULL) { + fprintf(stderr, "can't open file: %s\n", fname); + return -1; + } + + fseek(file, 0, SEEK_END); + file_size = ftell(file); + fseek(file, 0, SEEK_SET); + if (file_size > limit) + fprintf(stderr, "warning: input file \"%s\" too large\n", fname); + if (file_size < 0) { + fprintf(stderr, "bad/empty file: %s\n", fname); + goto fail; + } + + data = malloc(file_size); + if (data == NULL) { + fprintf(stderr, "low memory\n"); + goto fail; + } + + ret = fread(data, 1, file_size, file); + if (ret != file_size) { + fprintf(stderr, "failed to read file: %s", fname); + perror(""); + goto fail; + } + + *buff_out = data; + *size = file_size; + fclose(file); + return 0; + +fail: + fclose(file); + return -1; +} + +static int write_file(const char *fname, void *buff, int size) +{ + FILE *file; + int ret; + + file = fopen(fname, "wb"); + if (file == NULL) { + fprintf(stderr, "can't open for writing: %s\n", fname); + return -1; + } + + ret = fwrite(buff, 1, size, file); + if (ret != size) { + fprintf(stderr, "write failed to %s", fname); + perror(""); + } else + printf("saved to \"%s\".\n", fname); + fclose(file); + + return 0; +} + static usb_dev_handle *get_device(void) { struct usb_dev_handle *handle; @@ -160,62 +864,327 @@ found: return handle; } -static int read_info(struct usb_dev_handle *device, u8 ctl_id) -{ - dev_cmd_t cmd; - dev_info_t info; - int ret; - - prepare_cmd(&cmd, CMD_ATM_READY); - cmd.dev_info.which_device = ctl_id; - - ret = write_cmd(device, &cmd); - if (ret < 0) - return ret; - - ret = read_response(device, &info, sizeof(info)); - if (ret < 0) - return ret; - - printf("firmware version: %X.%X.%X%c\n", info.firmware_ver[0], - info.firmware_ver[1], info.firmware_ver[2], info.firmware_ver[3]); - printf("bootloader version: %X.%X.%X%c\n", info.bootloader_ver[0], - info.bootloader_ver[1], info.bootloader_ver[2], info.bootloader_ver[3]); - info.names[sizeof(info.names) - 1] = 0; - printf("device name: %s\n", info.names); - - return 0; -} - static void release_device(struct usb_dev_handle *device) { usb_release_interface(device, 0); usb_close(device); } +static void usage(const char *app_name) +{ + printf("Flasher tool for MX game devices\n" + "written by Grazvydas \"notaz\" Ignotas\n"); + printf("v" VERSION " (" __DATE__ ")\n\n"); + printf("Usage:\n" + "%s [-i] [-g] [-e] [-r [file]] [-w ] ...\n" + " -i print some info about connected device\n" + " -g print some info about game ROM inside device\n" + " -e[1] erase whole flash ROM in device, '1' uses different erase method\n" + " -m[1-3] set MX mode: 2M+RAM, 4M no RAM, 4M+RAM\n" + " -f skip file check\n" + " -r [file] copy game image from device to file; can autodetect filename\n" + " -w write file to device; also does erase\n" + " -sr [file] read save RAM to file\n" + " -sw write save RAM file to device\n" + " -sc clear save RAM\n" + " -v with -w or -sw: verify written file\n", + app_name); +} + int main(int argc, char *argv[]) { + char *r_fname = NULL, *w_fname = NULL, *sr_fname = NULL, *sw_fname = NULL; + void *r_fdata = NULL, *w_fdata = NULL, *sr_fdata = NULL, *sw_fdata = NULL; + int do_read_ram = 0, do_clear_ram = 0, do_verify = 0, do_check = 1; + int pr_dev_info = 0, pr_rom_info = 0, do_read = 0, mx_mode = 0; + int erase_method = 0, do_erase_size = 0; + int w_fsize = 0, sw_fsize = 0; struct usb_dev_handle *device; - int ret; + char fname_buff[65]; + int i, ret = 0; + for (i = 1; i < argc; i++) + { + if (argv[i][0] != '-') + break; + + switch (argv[i][1]) { + case 'i': + pr_dev_info = 1; + break; + case 'g': + pr_rom_info = 1; + break; + case 'e': + do_erase_size = 0x400000; + if (argv[i][2] == '1') + erase_method = 1; + break; + case 'f': + do_check = 0; + break; + case 'v': + do_verify = 1; + break; + case 'm': + mx_mode = argv[i][2]; + break; + case 'r': + do_read = 1; + if (argv[i+1] && argv[i+1][0] != '-') + r_fname = argv[++i]; + break; + case 'w': + if (argv[i+1] && argv[i+1][0] != '-') + w_fname = argv[++i]; + else + goto breakloop; + break; + case 's': + switch (argv[i][2]) { + case 'r': + do_read_ram = 1; + if (argv[i+1] && argv[i+1][0] != '-') + sr_fname = argv[++i]; + break; + case 'w': + if (argv[i+1] && argv[i+1][0] != '-') + sw_fname = argv[++i]; + else + goto breakloop; + break; + case 'c': + do_clear_ram = 1; + break; + default: + goto breakloop; + } + break; + default: + goto breakloop; + } + } + +breakloop: + if (i <= 1 || i < argc) { + usage(argv[0]); + return 1; + } + + /* preparations */ + if (w_fname != NULL) { + /* check extension */ + ret = strlen(w_fname); + if (do_check && (w_fname[ret - 4] == '.' || w_fname[ret - 3] == '.' || + w_fname[ret - 2] == '.') && + strcasecmp(&w_fname[ret - 4], ".gen") != 0 && + strcasecmp(&w_fname[ret - 4], ".bin") != 0) { + fprintf(stderr, "\"%s\" doesn't look like a game ROM, aborting " + "(use -f to disable this check)\n", w_fname); + return 1; + } + + ret = read_file(w_fname, &w_fdata, &w_fsize, 0x400000); + if (ret < 0) + return 1; + + if (do_erase_size < w_fsize) + do_erase_size = w_fsize; + } + if (sw_fname != NULL) { + ret = read_file(sw_fname, &sw_fdata, &sw_fsize, 0x8000*2); + if (ret < 0) + return 1; + } + if (sw_fdata != NULL || do_clear_ram) { + if (sw_fsize < 0x8000*2) { + sw_fdata = realloc(sw_fdata, 0x8000*2); + if (sw_fdata == NULL) { + fprintf(stderr, "low mem\n"); + return 1; + } + memset((u8 *)sw_fdata + sw_fsize, 0, 0x8000*2 - sw_fsize); + } + sw_fsize = 0x8000*2; + } + if (w_fname == NULL && sw_fname == NULL && do_verify) { + fprintf(stderr, "warning: -w or -sw not specified, -v ignored.\n"); + do_verify = 0; + } + + /* init */ usb_init(); device = get_device(); if (device == NULL) return 1; - printf("data bus controller:\n"); - ret = read_info(device, CTL_DATA_BUS); - if (ret < 0) - goto end; + /* info */ + if (pr_dev_info) { + ret = print_device_info(device); + if (ret < 0) + goto end; + } - printf("address bus controller:\n"); - ret = read_info(device, CTL_ADDR_BUS); - if (ret < 0) - goto end; + if (pr_rom_info) { + ret = print_game_info(device); + if (ret < 0) + goto end; + } + + /* set mode */ + if (mx_mode || w_fsize > 0x200000) { + if (mx_mode == 0) + mx_mode = '3'; + printf("MX mode set to "); + switch (mx_mode) { + case '1': + printf("2M with RAM.\n"); + mx_mode = C_MODE_2M_RAM; + break; + case '2': + printf("4M, no RAM.\n"); + mx_mode = C_MODE_4M_NORAM; + break; + default: + printf("4M with RAM.\n"); + mx_mode = C_MODE_4M_RAM; + break; + } + set_ram_mode(device, mx_mode); + } + + /* erase */ + if (do_erase_size != 0) { + if (erase_method) + ret = erase_all(device, do_erase_size); + else + ret = erase_seq(device, do_erase_size); + if (ret < 0) + goto end; + } + + /* write flash */ + if (w_fdata != NULL) { + char *p; + + ret = read_write_rom(device, 0, w_fdata, w_fsize, 1); + if (ret < 0) + goto end; + + p = strrchr(w_fname, '/'); + p = (p == NULL) ? w_fname : p + 1; + + ret = write_filename(device, p, FILENAME_ROM0); + if (ret < 0) + fprintf(stderr, "warning: failed to save ROM filename\n"); + } + + /* write ram */ + if (sw_fdata != NULL) { + char *p, *t; + ret = read_write_ram(device, sw_fdata, sw_fsize, 1); + if (ret < 0) + goto end; + + memset(fname_buff, 0, sizeof(fname_buff)); + p = fname_buff; + if (sw_fname != NULL) { + p = strrchr(sw_fname, '/'); + p = (p == NULL) ? sw_fname : p + 1; + } else if (w_fname != NULL) { + t = strrchr(w_fname, '/'); + t = (t == NULL) ? w_fname : t + 1; + + strncpy(fname_buff, t, sizeof(fname_buff)); + fname_buff[sizeof(fname_buff) - 1] = 0; + ret = strlen(fname_buff); + if (ret > 4 && fname_buff[ret - 4] == '.') + strcpy(&fname_buff[ret - 4], ".srm"); + } + + ret = write_filename(device, p, FILENAME_RAM); + if (ret < 0) + fprintf(stderr, "warning: failed to save RAM filename\n"); + } + + /* read flash */ + if (do_read && r_fname == NULL) { + ret = read_filename(device, fname_buff, sizeof(fname_buff), FILENAME_ROM0); + if (ret < 0) + return ret; + r_fname = fname_buff; + if (r_fname[0] == 0) + r_fname = "rom.gen"; + } + + if (r_fname != NULL || do_verify) { + r_fdata = malloc(0x400000); + if (r_fdata == NULL) { + fprintf(stderr, "low mem\n"); + goto end; + } + + ret = read_write_rom(device, 0, r_fdata, 0x400000, 0); + if (ret < 0) + goto end; + } + + if (r_fname != NULL) + write_file(r_fname, r_fdata, 0x400000); + + /* read ram */ + if (do_read_ram && sr_fname == NULL) { + ret = read_filename(device, fname_buff, sizeof(fname_buff), FILENAME_RAM); + if (ret < 0) + return ret; + sr_fname = fname_buff; + if (sr_fname[0] == 0) + sr_fname = "rom.srm"; + } + + if (sr_fname != NULL || do_verify) { + sr_fdata = malloc(0x8000*2); + if (sr_fdata == NULL) { + fprintf(stderr, "low mem\n"); + goto end; + } + + ret = read_write_ram(device, sr_fdata, 0x8000*2, 0); + if (ret < 0) + goto end; + } + + if (sr_fname != NULL) + write_file(sr_fname, sr_fdata, 0x8000*2); + + /* verify */ + if (do_verify && w_fdata != NULL && r_fdata != NULL) { + ret = memcmp(w_fdata, r_fdata, w_fsize); + if (ret == 0) + printf("flash verification passed.\n"); + else + printf("flash verification FAILED!\n"); + } + + if (do_verify && sw_fdata != NULL && sr_fdata != NULL) { + ret = memcmp(sw_fdata, sr_fdata, 0x8000*2); + if (ret == 0) + printf("RAM verification passed.\n"); + else + printf("RAM verification FAILED!\n"); + } + + printf("all done.\n"); + ret = 0; end: + if (w_fdata != NULL) + free(w_fdata); + if (r_fdata != NULL) + free(r_fdata); + release_device(device); return ret;