basic functionality finished, sram on todo
authornotaz <notasas@gmail.com>
Wed, 11 Mar 2009 23:27:51 +0000 (23:27 +0000)
committernotaz <notasas@gmail.com>
Wed, 11 Mar 2009 23:27:51 +0000 (23:27 +0000)
mx/linux/Makefile
mx/linux/main.c

index dd2acce..d4fe5c7 100644 (file)
@@ -1,5 +1,11 @@
 
 CC ?= $(CROSS)gcc
 
-CFLAGS += -Wall -ggdb -Wno-unused-function
+CFLAGS += -Wall -O2 -ggdb
 LDFLAGS += -lusb
+TARGET = main
+
+all: $(TARGET)
+
+clean:
+       $(RM) $(TARGET)
index 951bc80..e5f77d0 100644 (file)
@@ -19,7 +19,9 @@ static const struct {
        { 0x03eb, 0x202d, "32MX+UF Game Device" },
 };
 
-/*****************************************************************************/
+#define VERSION                        "0.8"
+
+#define IO_BLK_SIZE            0x2000  /* 8K */
 
 #define CMD_ATM_READY          0x22
 #define CMD_SEC_GET_NAME       'G'     /* filename r/w */
@@ -28,6 +30,7 @@ static const struct {
 #define CMD_SEC_ERASE          'E'
 #define CMD_SEC_READY          'C'     /* is flash ready? */
 #define CMD_SEC_READ           'R'
+#define CMD_SEC_WRITE          'W'
 
 /* bus controllers */
 #define CTL_DATA_BUS   0x55
@@ -53,10 +56,11 @@ typedef struct {
                        u8 which_device;
                } dev_info;
                struct {
-                       u8 addrb2;      /* most significant */
+                       u8 addrb2;      /* most significant (BE) */
                        u8 addrb1;
                        u8 addrb0;
-                       u8 packets;     /* 64 byte usb packets */
+                       u8 param;       /* 64 byte usb packets for i/o */
+                       u8 param2;
                } rom_rw;
                struct {
                        u8 which;
@@ -64,7 +68,7 @@ typedef struct {
                struct {
                        u8 cmd;
                        u8 action;
-                       u8 b0;
+                       u8 b0;          /* LE */
                        u8 b1;
                        u8 b2;
                        u8 b3;
@@ -150,9 +154,11 @@ static int read_data(struct usb_dev_handle *dev, void *buff, int size)
        if (ret < 0) {
                fprintf(stderr, "failed to read:\n");
                fprintf(stderr, "%s (%d)\n", usb_strerror(), ret);
-       } else if (ret != size)
+       }
+/*
+       else if (ret != size)
                printf("read_data: read only %d of %d bytes\n", ret, size);
-
+*/
        return ret;
 }
 
@@ -248,7 +254,7 @@ static int write_filename(struct usb_dev_handle *dev, const char *fname, u8 whic
        return write_data(dev, buff, len + 1);
 }
 
-static int read_w_counter(struct usb_dev_handle *dev, u32 *val)
+static int read_erase_counter(struct usb_dev_handle *dev, u32 *val)
 {
        dev_info_t dummy_info;
        dev_cmd_t cmd;
@@ -339,23 +345,142 @@ static int get_page_size(const page_table_t *table, u32 addr, u32 *size)
        }
 
        if (addr == t[-1].end_addr + 1)
-               return 1;       /* last */
+               return 1;       /* no more */
        
        fprintf(stderr, "get_page_size: failed on addr %06x\n", addr);
        return -1;
 }
 
-static int erase_page(struct usb_dev_handle *dev, u32 addr)
+/* 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_rom_block(struct usb_dev_handle *dev, u32 addr, void *buffer, int bytes, int is_w)
+{
+       dev_cmd_t cmd;
+       int ret;
+
+       prepare_cmd(&cmd, is_w ? CMD_SEC_WRITE : CMD_SEC_READ);
+       cmd.write_flag = is_w ? 1 : 0;
+       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;
+       cmd.rom_rw.param2 = is_w ? 1 : 0; /* ? */
+
+       ret = write_cmd(dev, &cmd);
+       if (ret < 0)
+               return ret;
+
+       bytes &= ~63;
+
+       if (is_w)
+               ret = write_data(dev, buffer, bytes);
+       else
+               ret = read_data(dev, buffer, bytes);
+       if (ret < 0)
+               return ret;
+
+       if (ret != bytes)
+               fprintf(stderr, "rw_rom_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 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);
+
+       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_rom_block(dev, addr, buff, IO_BLK_SIZE, is_write);
+               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_rom_block(dev, addr, buff, bytes, is_write);
+               count++;
+               print_progress(total_bytes, total_bytes);
+       }
+
+       if (count & 1)
+               /* work around read_rom_block() limitation 3 */
+               rw_rom_block(dev, 0, dummy, sizeof(dummy), 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 0;
+}
+
+static int erase_page(struct usb_dev_handle *dev, u32 addr, int whole)
 {
        dev_cmd_t cmd;
-       u8 buff[10];
+       u8 buff[5];
        int i, ret;
 
        prepare_cmd(&cmd, CMD_SEC_ERASE);
        cmd.write_flag = 1;
-       cmd.rom_rw.addrb2 = addr >> 16;
-       cmd.rom_rw.addrb1 = addr >> 8;
-       cmd.rom_rw.addrb0 = addr;
+       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)
@@ -366,9 +491,9 @@ static int erase_page(struct usb_dev_handle *dev, u32 addr)
                return ret;
        
        prepare_cmd(&cmd, CMD_SEC_READY);
-       cmd.rom_rw.addrb2 = addr >> 16;
-       cmd.rom_rw.addrb1 = addr >> 8;
-       cmd.rom_rw.addrb0 = addr;
+       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);
@@ -382,88 +507,150 @@ static int erase_page(struct usb_dev_handle *dev, u32 addr)
                if (ret > 4 && buff[4] == 1)
                        break;
 
-               usleep(50);
+               usleep((whole ? 600 : 20) * 1000);
+       }
+
+       if (i == 100) {
+               fprintf(stderr, "\ntimeout waiting for erase to complete\n");
+               return -1;
        }
 
-       printf("i = %d\n", i);
        return 0;
 }
 
-/* limitations:
- * - bytes must be multiple of 64
- * - bytes must be less than 16k
- * - must perform even number of reads (firmware bug?) */
-static int read_rom_block(struct usb_dev_handle *dev, u32 addr, void *buffer, int bytes)
+static int erase_seq(struct usb_dev_handle *dev, u32 size)
 {
-       dev_cmd_t cmd;
-       int ret;
-
-       prepare_cmd(&cmd, CMD_SEC_READ);
-       cmd.rom_rw.addrb2 = addr >> (16 + 1);
-       cmd.rom_rw.addrb1 = addr >> (8 + 1);
-       cmd.rom_rw.addrb0 = addr >> 1;
-       cmd.rom_rw.packets = bytes / 64;
+       const page_table_t *table;
+       u32 addr, page_size = 0;
+       u32 rom0_id, rom1_id;
+       int count, ret;
 
-       ret = write_cmd(dev, &cmd);
+       ret = read_flash_rom_id(dev, 0, &rom0_id);
        if (ret < 0)
                return ret;
 
-       bytes &= ~63;
-       ret = read_data(dev, buffer, bytes);
+       ret = read_flash_rom_id(dev, 1, &rom1_id);
        if (ret < 0)
                return ret;
-       if (ret != bytes)
-               fprintf(stderr, "read_rom_block warning: read only %d/%d bytes\n", ret, bytes);
-
-       return ret;
-}
 
-#define READ_BLK_SIZE (0x2000) /* 8K */
+       if (rom0_id != rom1_id)
+               fprintf(stderr, "Warning: flash ROM ids differ: %08x %08x\n",
+                       rom0_id, rom1_id);
 
-static int read_rom(struct usb_dev_handle *dev, u32 addr, void *buffer, int bytes)
-{
-       int total_bytes = bytes;
-       u8 *buff = buffer;
-       u8 dummy[64 * 4];
-       int count, ret;
+       table = get_page_table(rom0_id);
+       if (table == NULL)
+               return -1;
 
-       if (addr & 1)
-               fprintf(stderr, "read_rom: can't handle odd address %06x, "
-                               "LSb will be ignored\n", addr);
-       if (bytes & 63)
-               fprintf(stderr, "read_rom: byte count must be multiple of 64, "
-                               "last %d bytes will not be read\n", bytes & 63);
+       printf("erasing flash...\n");
 
-       printf("reading flash ROM...\n");
+       ret = increment_erase_cnt(dev);
+       if (ret != 0)
+               fprintf(stderr, "warning: coun't increase erase counter\n");
 
-       /* read in blocks */
-       for (count = 0; bytes >= READ_BLK_SIZE; count++) {
-               print_progress(buff - (u8 *)buffer, total_bytes);
+       for (addr = 0, count = 0; addr < size; addr += page_size, count++) {
+               print_progress(addr, size);
 
-               ret = read_rom_block(dev, addr, buff, READ_BLK_SIZE);
+               ret = erase_page(dev, addr, 0);
                if (ret < 0)
                        return ret;
-               buff += READ_BLK_SIZE;
-               addr += READ_BLK_SIZE;
-               bytes -= READ_BLK_SIZE;
-       }
-       print_progress(buff - (u8 *)buffer, total_bytes);
 
-       ret = 0;
-       if (bytes != 0) {
-               ret = read_rom_block(dev, addr, buff, bytes);
-               count++;
-               print_progress(total_bytes, total_bytes);
+               ret = get_page_size(table, addr, &page_size);
+               if (ret != 0)
+                       break;
        }
 
        if (count & 1)
-               /* work around read_rom_block() limitation 3 */
-               read_rom_block(dev, 0, dummy, sizeof(dummy));
+               /* ??? */
+               /* 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;
+
+       printf("erasing flash0...");
+       fflush(stdout);
+
+       ret = increment_erase_cnt(dev);
+       if (ret != 0)
+               fprintf(stderr, "warning: couldn't increase erase counter\n");
+
+       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 usb_dev_handle *get_device(void)
 {
        struct usb_dev_handle *handle;
@@ -535,14 +722,115 @@ static void release_device(struct usb_dev_handle *device)
        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 <file>]\n"
+               "  -i         print some info about connected device\n"
+               "  -g         print some info about game ROM inside device\n"
+               "  -e         erase whole flash ROM in device\n"
+               "  -f         use different erase method\n"
+               "  -r [file]  copy game image from device to file; can autodetect filename\n"
+               "  -w <file>  write file to device\n"
+               "  -v         with -w: verify written file\n",
+               app_name);
+}
+
 int main(int argc, char *argv[])
 {
+       int pr_dev_info = 0, pr_rom_info = 0, do_erase_size = 0;
+       int erase_method = 0, do_read = 0, do_verify = 0;
        struct usb_dev_handle *device;
-       char fname[65];
-       u32 counter, rom0_id, rom1_id;
-       dev_info_t info;
-       char *buff;
-       int ret;
+       char *r_fname = NULL, *w_fname = NULL;
+       void *r_fdata = NULL, *w_fdata = NULL;
+       char r_fname_buff[65];
+       FILE *file = NULL;
+       int file_size = 0;
+       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;
+                       break;
+               case 'f':
+                       erase_method = 1;
+                       break;
+               case 'v':
+                       do_verify = 1;
+                       break;
+               case 'r':
+                       do_read = 1;
+                       if (argv[i+1] && (argv[i+1][0] != '-' || argv[i+1][2] != ' '))
+                               r_fname = argv[++i];
+                       break;
+               case 'w':
+                       if (argv[i+1] && (argv[i+1][0] != '-' || argv[i+1][2] != ' '))
+                               w_fname = argv[++i];
+                       else
+                               goto breakloop;
+                       break;
+               default:
+                       goto breakloop;
+               }
+       }
+
+breakloop:
+       if (i <= 1 || i < argc) {
+               usage(argv[0]);
+               return 1;
+       }
+
+       if (w_fname != NULL) {
+               file = fopen(w_fname, "rb");
+               if (file == NULL) {
+                       fprintf(stderr, "can't open file: %s\n", w_fname);
+                       return 1;
+               }
+               fseek(file, 0, SEEK_END);
+               file_size = ftell(file);
+               fseek(file, 0, SEEK_SET);
+               if (file_size > 0x400000)
+                       fprintf(stderr, "warning: input file too large\n");
+               if (file_size < 0) {
+                       fprintf(stderr, "bad/empty file: %s\n", w_fname);
+                       fclose(file);
+                       return 1;
+               }
+
+               w_fdata = malloc(file_size);
+               if (w_fdata == NULL) {
+                       fprintf(stderr, "low memory\n");
+                       fclose(file);
+                       return 1;
+               }
+
+               ret = fread(w_fdata, 1, file_size, file);
+               fclose(file);
+               if (ret != file_size) {
+                       fprintf(stderr, "failed to read file: %s\n", w_fname);
+                       return 1;
+               }
+
+               if (do_erase_size < file_size)
+                       do_erase_size = file_size;
+       } else if (do_verify) {
+               fprintf(stderr, "warning: -w not specified, -v ignored.\n");
+               do_verify = 0;
+       }
 
        usb_init();
 
@@ -550,60 +838,97 @@ int main(int argc, char *argv[])
        if (device == NULL)
                return 1;
 
-       printf("data bus controller:\n");
-       ret = read_info(device, CTL_DATA_BUS, &info);
-       if (ret < 0)
-               goto end;
-       printf_info(&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, &info);
-       if (ret < 0)
-               goto end;
-       printf_info(&info);
+       if (pr_rom_info) {
+               ret = print_game_info(device);
+               if (ret < 0)
+                       goto end;
+       }
 
-       ret = read_filename(device, fname, sizeof(fname), FILENAME_ROM0);
-       if (ret < 0)
-               goto end;
-       printf("ROM filename:  %s\n", fname);
+       /* 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;
+       }
 
-       ret = read_filename(device, fname, sizeof(fname), FILENAME_RAM);
-       if (ret < 0)
-               goto end;
-       printf("SRAM filename: %s\n", fname);
+       /* write */
+       if (w_fdata != NULL) {
+               char *p;
 
-       ret = read_w_counter(device, &counter);
-       if (ret < 0)
-               goto end;
-       printf("flash writes:  %u\n", counter);
+               ret = read_write_rom(device, 0, w_fdata, file_size, 1);
+               if (ret < 0)
+                       goto end;
 
-       ret = read_flash_rom_id(device, 0, &rom0_id);
-       if (ret < 0)
-               goto end;
-       printf("flash rom0 id: %08x\n", rom0_id);
+               p = strrchr(w_fname, '/');
+               if (p == NULL)
+                       p = w_fname;
+               else
+                       p++;
 
-       ret = read_flash_rom_id(device, 1, &rom1_id);
-       if (ret < 0)
-               goto end;
-       printf("flash rom1 id: %08x\n", rom1_id);
+               ret = write_filename(device, p, FILENAME_ROM0);
+               if (ret < 0)
+                       fprintf(stderr, "warning: failed to save ROM filename\n");
+       }
 
-       if (rom0_id != rom1_id)
-               fprintf(stderr, "Warning: flash ROM ids differ: %08x %08x\n",
-                       rom0_id, rom1_id);
-#define XSZ (0x400000)
-       buff = malloc(XSZ);
-       ret = read_rom(device, 0, buff, XSZ);
-       if (ret < 0)
-               goto end;
-       {
-               FILE *f = fopen("dump", "wb");
-               fwrite(buff, 1, XSZ, f);
-               fclose(f);
+       /* read, verify */
+       if (do_read && r_fname == NULL) {
+               ret = read_filename(device, r_fname_buff, sizeof(r_fname_buff), FILENAME_ROM0);
+               if (ret < 0)
+                       return ret;
+               r_fname = r_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 (do_verify) {
+               ret = memcmp(w_fdata, r_fdata, file_size);
+               if (ret == 0)
+                       printf("verification passed.\n");
+               else
+                       printf("verification failed!\n");
+       }
+
+       if (r_fname != NULL) {
+               file = fopen(r_fname, "wb");
+               if (file == NULL) {
+                       fprintf(stderr, "can't open for writing: %s\n", r_fname);
+                       goto end;
+               }
+               ret = fwrite(r_fdata, 1, 0x400000, file);
+               fclose(file);
+               if (ret != 0x400000)
+                       fprintf(stderr, "write failed to %s\n", r_fname);
+               else
+                       printf("saved to %s\n", r_fname);
+       }
 
 end:
+       if (w_fdata != NULL)
+               free(w_fdata);
+       if (r_fdata != NULL)
+               free(r_fdata);
+
        release_device(device);
 
        return ret;