/* Raspberry Pi / GPIO RAW NAND flasher (made out of "360-Clip based 8-bit NAND reader" by pharos) Copyright (C) 2016 littlebalup 2019 skypiece This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <time.h> // #define DEBUG 1 #define PAGE_SIZE 2112 // (2K + 64)Byte #define BLOCK_SIZE 135168 // 64 pages (128K + 4K)Byte #define MAX_WAIT_READ_BUSY 1000000 /* For Raspberry B+ :*/ // #define BCM2708_PERI_BASE 0x20000000 // #define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* For Raspberry 2B and 3B(+) :*/ #define BCM2736_PERI_BASE 0x3F000000 #define GPIO_BASE (BCM2736_PERI_BASE + 0x200000) /* GPIO controller */ // IMPORTANT: BE VERY CAREFUL TO CONNECT VCC TO P1-01 (3.3V) AND *NOT* P1-02 (5V) !! // IMPORTANT: MAY BE YOU NEED EXTERNAL 1.8V for modern NANDs // GPIO pins have been chose to compitable Waveshare NandFlash Board and lost RPi SMI NAND driver #define N_WRITE_PROTECT 2 #define N_READ_BUSY 3 #define ADDRESS_LATCH_ENABLE 4 #define COMMAND_LATCH_ENABLE 5 #define N_READ_ENABLE 6 #define N_WRITE_ENABLE 7 // #define N_CHIP_ENABLE 22 int data_to_gpio_map[8] = { 8, 9, 10, 11, 12, 13, 14, 15 }; // 8 is NAND IO 0, etc. volatile unsigned int *gpio; int read_id(unsigned char id[5]); int read_pages(int first_page_number, int number_of_pages, char *outfile, int write_spare); int write_pages(int first_page_number, int number_of_pages, char *infile); int erase_blocks(int first_block_number, int number_of_blocks); inline void INP_GPIO(int g) { #ifdef DEBUG printf("setting direction of GPIO#%d to input\n", g); #endif (*(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))); } inline void OUT_GPIO(int g) { INP_GPIO(g); #ifdef DEBUG printf("setting direction of GPIO#%d to output\n", g); #endif *(gpio+((g)/10)) |= (1<<(((g)%10)*3)); } inline void GPIO_SET_1(int g) { #ifdef DEBUG printf("setting GPIO#%d to 1\n", g); #endif *(gpio + 7) = 1 << g; } inline void GPIO_SET_0(int g) { #ifdef DEBUG printf("setting GPIO#%d to 0\n", g); #endif *(gpio + 10) = 1 << g; } inline int GPIO_READ(int g) { int x = (*(gpio + 13) & (1 << g)) >> g; #ifdef DEBUG printf("GPIO#%d reads as %d\n", g, x); #endif return x; } inline void set_data_direction_in(void) { int i; #ifdef DEBUG printf("data direction => IN\n"); #endif for (i = 0; i < 8; i++) INP_GPIO(data_to_gpio_map[i]); } inline void set_data_direction_out(void) { int i; #ifdef DEBUG printf("data direction => OUT\n"); #endif for (i = 0; i < 8; i++) OUT_GPIO(data_to_gpio_map[i]); } inline int GPIO_DATA8_IN(void) { int i, data; for (i = data = 0; i < 8; i++, data = data << 1) { data |= GPIO_READ(data_to_gpio_map[7 - i]); } data >>= 1; #ifdef DEBUG printf("GPIO_DATA8_IN: data=%02x\n", data); #endif return data; } inline void GPIO_DATA8_OUT(int data) { int i; #ifdef DEBUG printf("GPIO_DATA8_OUT: data=%02x\n", data); #endif for (i = 0; i < 8; i++, data >>= 1) { if (data & 1) GPIO_SET_1(data_to_gpio_map[i]); else GPIO_SET_0(data_to_gpio_map[i]); } } int delay = 1; void shortpause() { int i; volatile static int dontcare = 0; for (i = 0; i < delay; i++) { dontcare++; } } // void shortpause() // { // struct timespec ts; // ts.tv_sec = delay / 1000; // ts.tv_nsec = (delay % 1000) * 1000000; // nanosleep(&ts, NULL); // } int main(int argc, char **argv) { int mem_fd; printf("Raspberry GPIO raw NAND flasher by pharos, littlebalup, skypiece\n\n"); if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC)) < 0) { perror("open /dev/mem, are you root?"); return -1; } if ((gpio = (volatile unsigned int *) mmap((caddr_t) 0x13370000, 4096, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, mem_fd, GPIO_BASE)) == MAP_FAILED) { perror("mmap GPIO_BASE"); close(mem_fd); return -1; } INP_GPIO(N_READ_BUSY); OUT_GPIO(N_WRITE_PROTECT); GPIO_SET_1(N_WRITE_PROTECT); OUT_GPIO(N_READ_ENABLE); GPIO_SET_1(N_READ_ENABLE); OUT_GPIO(N_WRITE_ENABLE); GPIO_SET_1(N_WRITE_ENABLE); OUT_GPIO(COMMAND_LATCH_ENABLE); GPIO_SET_0(COMMAND_LATCH_ENABLE); OUT_GPIO(ADDRESS_LATCH_ENABLE); GPIO_SET_0(ADDRESS_LATCH_ENABLE); //OUT_GPIO(N_CHIP_ENABLE); //GPIO_SET_0(N_CHIP_ENABLE); if (argc < 3) { usage: //GPIO_SET_1(N_CHIP_ENABLE); printf("usage: sudo %s <delay> <command> ...\n\n" \ " <delay> used to slow down operations (50 should work, increase if bad reads)\n\n" \ "Commands:\n" \ " read_id (no arguments) : read and decrypt chip ID\n" \ " read_full <page #> <# of pages> <output file> : read N pages including spare\n" \ " read_data <page #> <# of pages> <output file> : read N pages, discard spare\n" \ " write_full <page #> <# of pages> <input file> : write N pages, including spare\n" \ " write_data <page #> <# of pages> <input file> : write N pages, discard spare\n" \ " erase_blocks <block number> <# of blocks> : erase N blocks\n\n" \ "Notes:\n" \ " This program assumes PAGE_SIZE == %d\n" \ " Run as root (sudo) required (for /dev/mem access)\n\n", argv[0], PAGE_SIZE); close(mem_fd); return -1; } delay = atoi(argv[1]); if (strcmp(argv[2], "read_id") == 0) { return read_id(NULL); } if (strcmp(argv[2], "read_full") == 0) { if (argc != 6) goto usage; if (atoi(argv[4]) <= 0) { printf("# of pages must be > 0\n"); return -1; } return read_pages(atoi(argv[3]), atoi(argv[4]), argv[5], 1); } if (strcmp(argv[2], "read_data") == 0) { if (argc != 6) goto usage; if (atoi(argv[4]) <= 0) { printf("# of pages must be > 0\n"); return -1; } return read_pages(atoi(argv[3]), atoi(argv[4]), argv[5], 0); } if (strcmp(argv[2], "write_full") == 0) { if (argc != 6) goto usage; if (atoi(argv[4]) <= 0) { printf("# of pages must be > 0\n"); return -1; } return write_pages(atoi(argv[3]), atoi(argv[4]), argv[5]); } if (strcmp(argv[2], "erase_blocks") == 0) { if (argc != 5) goto usage; if (atoi(argv[4]) <= 0) { printf("# of blocks must be > 0\n"); return -1; } return erase_blocks(atoi(argv[3]), atoi(argv[4])); } printf("unknown command '%s'\n", argv[2]); goto usage; return 0; } void error_msg(char *msg) { printf("%s\nBe sure to check wiring, and check that pressure is applied on clip (if used)\n", msg); } void print_id(unsigned char id[5]) { unsigned int i, bit, page_size, ras_size, orga, plane_number; unsigned long block_size, plane_size, nand_size, nandras_size; char maker[16], device[16], serial_access[20]; unsigned *thirdbits = (unsigned*)malloc(sizeof(unsigned) * 8); unsigned *fourthbits = (unsigned*)malloc(sizeof(unsigned) * 8); unsigned *fifthbits = (unsigned*)malloc(sizeof(unsigned) * 8); printf("Raw ID data: "); for (i = 0; i < 5; i++) printf("0x%02X ", id[i]); printf("\n"); switch(id[0]) { case 0xEC: { strcpy(maker, "Samsung"); switch(id[1]) { case 0xA1: strcpy(device, "K9F1G08R0A"); break; case 0xD5: strcpy(device, "K9GAG08U0M"); break; case 0xF1: strcpy(device, "K9F1G08U0A/B"); break; case 0xDC: strcpy(device, "K9F4G08U0A/B"); break; default: strcpy(device, "unknown"); } break; } case 0xAD: { strcpy(maker, "Hynix"); switch(id[1]) { case 0x73: strcpy(device, "HY27US08281A"); break; case 0xD7: strcpy(device, "H27UBG8T2A"); break; case 0xDA: strcpy(device, "HY27UF082G2B"); break; case 0xDC: strcpy(device, "H27U4G8F2D"); break; default: strcpy(device, "unknown"); } break; } case 0x2C: { strcpy(maker, "Micron"); switch(id[1]) { default: strcpy(device, "unknown"); } break; } default: strcpy(maker, "unknown"); strcpy(device, "unknown"); } /* all sizes in bytes */ for(bit = 0; bit < 8; ++bit) thirdbits[bit] = (id[2] >> bit) & 1; for(bit = 0; bit < 8; ++bit) fourthbits[bit] = (id[3] >> bit) & 1; switch(fourthbits[1] * 10 + fourthbits[0]) { case 00: page_size = 1024; break; case 01: page_size = 2048; break; case 10: page_size = 4096; break; case 11: page_size = 8192; break; } switch(fourthbits[5] * 10 + fourthbits[4]) { case 00: block_size = 64 * 1024; break; case 01: block_size = 128 * 1024; break; case 10: block_size = 256 * 1024; break; case 11: block_size = 521 * 1024; break; } switch(fourthbits[2]) { case 0: ras_size = 8; break; // for 512 bytes case 1: ras_size = 16; break; // for 512 bytes } switch(fourthbits[6]) { case 0: orga = 8; break; // bits case 1: orga = 16; break; // bits } switch(fourthbits[7] * 10 + fourthbits[3]) { case 00: strcpy(serial_access, "50ns/30ns minimum"); break; case 10: strcpy(serial_access, "25ns minimum"); break; case 01: strcpy(serial_access, "unknown (reserved)"); break; case 11: strcpy(serial_access, "unknown (reserved)"); break; } for(bit = 0; bit < 8; ++bit) fifthbits[bit] = (id[4] >> bit) & 1; switch(fifthbits[3] * 10 + fifthbits[2]) { case 00: plane_number = 1; break; case 01: plane_number = 2; break; case 10: plane_number = 4; break; case 11: plane_number = 8; break; } switch(fifthbits[6] * 100 + fifthbits[5] * 10 + fifthbits[4]) { case 000: plane_size = 64 / 8 * 1024 * 1024; break; // 64 megabits case 001: plane_size = 128 / 8 * 1024 * 1024; break; // 128 megabits case 010: plane_size = 256 / 8 * 1024 * 1024; break; // 256 megabits case 011: plane_size = 512 / 8 * 1024 * 1024; break; // 512 megabits case 100: plane_size = 1024 / 8 * 1024 * 1024; break; // 1 gigabit case 101: plane_size = 2048 / 8 * 1024 * 1024; break; // 2 gigabits case 110: plane_size = 4096 / 8 * 1024 * 1024; break; // 4 gigabits case 111: plane_size = 8192 / 8 * 1024 * 1024; break; // 8 gigabits } nand_size = plane_number * plane_size; nandras_size = nand_size + ras_size * nand_size / 512; printf("\n"); printf("NAND manufacturer: %s (0x%02X)\n", maker, id[0]); printf("NAND model: %s (0x%02X)\n", device, id[1]); printf("\n"); printf(" I/O|7|6|5|4|3|2|1|0|\n"); printf("3rd ID data: |"); for(bit = 8; bit--;) printf("%u|", thirdbits[bit]); printf(" (0x%02X)\n", id[2]); printf("4th ID data: |"); for(bit = 8; bit--;) printf("%u|", fourthbits[bit]); printf(" (0x%02X)\n", id[3]); printf("5th ID data: |"); for(bit = 8; bit--;) printf("%u|", fifthbits[bit]); printf(" (0x%02X)\n", id[4]); printf("\n"); printf("Page size: %d bytes\n", page_size); printf("Block size: %lu bytes\n", block_size); printf("RAS (/512 bytes): %d bytes\n", ras_size); // printf("RAS (per page): %d bytes\n", ras_size * page_size / 512); // printf("RAS (per block): %d bytes\n", ras_size * block_size / 512); printf("Organisation: %d bit\n", orga); printf("Serial access: %s\n", serial_access); printf("Number of planes: %d\n", plane_number); printf("Plane size: %lu bytes\n", plane_size); printf("\n"); printf("NAND size: %lu MB\n", nand_size / (1024 * 1024)); printf("NAND size + RAS: %lu MB\n", nandras_size / (1024 * 1024)); printf("Number of blocks: %lu\n", nand_size / block_size); printf("Number of pages: %lu\n", nand_size / page_size); } int read_id(unsigned char id[5]) { int i; unsigned char buf[5]; GPIO_SET_1(COMMAND_LATCH_ENABLE); shortpause(); GPIO_SET_0(N_WRITE_ENABLE); set_data_direction_out(); GPIO_DATA8_OUT(0x90); // Read ID byte 1 shortpause(); GPIO_SET_1(N_WRITE_ENABLE); shortpause();set_data_direction_in(); GPIO_SET_0(COMMAND_LATCH_ENABLE); shortpause(); GPIO_SET_1(ADDRESS_LATCH_ENABLE); GPIO_SET_0(N_WRITE_ENABLE); set_data_direction_out(); GPIO_DATA8_OUT(0x00); // Read ID byte 2 shortpause(); GPIO_SET_1(N_WRITE_ENABLE); shortpause(); set_data_direction_in(); GPIO_SET_0(ADDRESS_LATCH_ENABLE); shortpause(); for (i = 0; i < 5; i++) { GPIO_SET_0(N_READ_ENABLE); shortpause(); buf[i] = GPIO_DATA8_IN(); // GPIO_SET_1(N_READ_ENABLE); shortpause(); } if (id != NULL) memcpy(id, buf, 5); else print_id(buf); if (buf[0] == buf[1] && buf[1] == buf[2] && buf[2] == buf[3] && buf[3] == buf[4]) { error_msg((char*)"all five ID bytes are identical, this is not normal"); return -1; } return 0; } inline int page_to_address(int page, int address_byte_index) { switch(address_byte_index) { case 2: return page & 0xff; case 3: return (page >> 8) & 0xff; case 4: return (page >> 16) & 0xff; default: return 0; } } int send_read_command(int page) { int i; set_data_direction_out(); GPIO_SET_1(COMMAND_LATCH_ENABLE); shortpause(); GPIO_SET_0(N_WRITE_ENABLE); shortpause(); GPIO_DATA8_OUT(0x00); shortpause(); GPIO_SET_1(N_WRITE_ENABLE); shortpause(); GPIO_SET_0(COMMAND_LATCH_ENABLE); shortpause(); GPIO_SET_1(ADDRESS_LATCH_ENABLE); for (i = 0; i < 5; i++) { GPIO_SET_0(N_WRITE_ENABLE); shortpause(); // if (i < 2) { // printf("Col Add%d = %d\n", i + 1, page_to_address(page, i)); // } // else { // printf("Row Add%d = %d\n", i - 1, page_to_address(page, i)); // } GPIO_DATA8_OUT(page_to_address(page, i)); shortpause(); GPIO_SET_1(N_WRITE_ENABLE); shortpause(); } GPIO_SET_0(ADDRESS_LATCH_ENABLE); shortpause(); GPIO_SET_1(COMMAND_LATCH_ENABLE); shortpause(); GPIO_SET_0(N_WRITE_ENABLE); shortpause(); GPIO_DATA8_OUT(0x30); shortpause(); GPIO_SET_1(N_WRITE_ENABLE); shortpause(); GPIO_SET_0(COMMAND_LATCH_ENABLE); shortpause(); return 0; } int send_write_command(int page, unsigned char data[PAGE_SIZE]) { int i; set_data_direction_out(); GPIO_SET_1(COMMAND_LATCH_ENABLE); shortpause(); GPIO_SET_0(N_WRITE_ENABLE); shortpause(); GPIO_DATA8_OUT(0x80); shortpause(); GPIO_SET_1(N_WRITE_ENABLE); shortpause(); GPIO_SET_0(COMMAND_LATCH_ENABLE); shortpause(); GPIO_SET_1(ADDRESS_LATCH_ENABLE); for (i = 0; i < 5; i++) { GPIO_SET_0(N_WRITE_ENABLE); // if (i < 2) { // printf("Col Add%d = %d\n", i + 1, page_to_address(page, i)); // } // else { // printf("Row Add%d = %d\n", i - 1, page_to_address(page, i)); // } GPIO_DATA8_OUT(page_to_address(page, i)); shortpause(); GPIO_SET_1(N_WRITE_ENABLE); shortpause(); } GPIO_SET_0(ADDRESS_LATCH_ENABLE); shortpause(); for (i = 0; i < PAGE_SIZE; i++) { GPIO_SET_0(N_WRITE_ENABLE); shortpause(); GPIO_DATA8_OUT(data[i]); // shortpause(); GPIO_SET_1(N_WRITE_ENABLE); shortpause(); } GPIO_SET_1(COMMAND_LATCH_ENABLE); shortpause(); GPIO_SET_0(N_WRITE_ENABLE); GPIO_DATA8_OUT(0x10); shortpause(); GPIO_SET_1(N_WRITE_ENABLE); shortpause(); GPIO_SET_0(COMMAND_LATCH_ENABLE); shortpause(); return 0; } int send_eraseblock_command(int block) { int i; set_data_direction_out(); GPIO_SET_1(COMMAND_LATCH_ENABLE); shortpause(); GPIO_SET_0(N_WRITE_ENABLE); shortpause(); GPIO_DATA8_OUT(0x60); shortpause(); GPIO_SET_1(N_WRITE_ENABLE); shortpause(); GPIO_SET_0(COMMAND_LATCH_ENABLE); shortpause(); GPIO_SET_1(ADDRESS_LATCH_ENABLE); for (i = 2; i < 5; i++) { GPIO_SET_0(N_WRITE_ENABLE); shortpause(); // printf("Row Add%d = %d\n", i - 1, page_to_address(block, i)); GPIO_DATA8_OUT(page_to_address(block, i)); shortpause(); GPIO_SET_1(N_WRITE_ENABLE); shortpause(); } GPIO_SET_0(ADDRESS_LATCH_ENABLE); shortpause(); GPIO_SET_1(COMMAND_LATCH_ENABLE); shortpause(); GPIO_SET_0(N_WRITE_ENABLE); shortpause(); GPIO_DATA8_OUT(0xD0); shortpause(); GPIO_SET_1(N_WRITE_ENABLE); shortpause(); GPIO_SET_0(COMMAND_LATCH_ENABLE); shortpause(); return 0; } int read_status() { int i, data; unsigned char buf[5]; set_data_direction_out(); GPIO_SET_1(COMMAND_LATCH_ENABLE); shortpause(); GPIO_SET_0(N_WRITE_ENABLE); GPIO_DATA8_OUT(0x70); shortpause(); GPIO_SET_1(N_WRITE_ENABLE); shortpause(); GPIO_SET_0(COMMAND_LATCH_ENABLE); shortpause(); set_data_direction_in(); GPIO_SET_0(N_READ_ENABLE); shortpause(); data = GPIO_DATA8_IN(); // shortpause(); GPIO_SET_1(N_READ_ENABLE); shortpause(); // printf("Status data = %d\n", data); return data & 1; // I/O0=0 success , I/O0=1 error } int read_pages(int first_page_number, int number_of_pages, char *outfile, int write_spare) { int page, page_no, block_no, page_nbr, percent, i, n, retry_count; unsigned char id[5], id2[5]; unsigned char buf[PAGE_SIZE * 2]; FILE *badlog, *f = fopen(outfile, "w+"); if (f == NULL) { perror("fopen output file"); return -1; } if ((badlog = fopen("bad.log", "w+")) == NULL) { perror("fopen bad.log"); return -1; } if (GPIO_READ(N_READ_BUSY) == 0) { error_msg((char*)"N_READ_BUSY should be 1 (pulled up), but reads as 0. make sure the NAND is powered on"); return -1; } if (read_id(id) < 0) return -1; print_id(id); printf("if this ID is incorrect, press Ctrl-C NOW to abort (3s timeout)\n"); sleep(3); printf("\nStart reading...\n"); clock_t start = clock(); for (retry_count = 0, page = first_page_number*2; page < (first_page_number + number_of_pages)*2; page++) { retry_all: page_no = page >> 1; // printf("page = %d, n = %d\n",page, n); if (page % 2 == 0 && retry_count == 0) { // page_no = page / 2; page_nbr = page_no - first_page_number + 1; percent = (100 * page_nbr) / number_of_pages; block_no = page_no / 64; printf("Reading page n° %d in block n° %d (page %d of %d), %d%%\r", page_no, block_no, page_nbr, number_of_pages, percent); fflush(stdout); } // else { // printf("Reading the page again to ensure correct operation\n"); // } retry: read_id(id2); if (memcmp(id, id2, 5) != 0) { printf("\nNAND ID has changed! retrying"); goto retry; } send_read_command(page_no); //for (i = 0; i < MAX_WAIT_READ_BUSY; i++) { // if (GPIO_READ(N_READ_BUSY) == 0) // break; //} while (GPIO_READ(N_READ_BUSY) == 0) { // printf("Busy\n"); shortpause(); } // if (i == MAX_WAIT_READ_BUSY) { // // #ifdef DEBUG // printf("N_READ_BUSY was not brought to 0 by NAND in time, retrying\n"); // // #endif // goto retry; // } set_data_direction_in(); // for (i = 0; i < MAX_WAIT_READ_BUSY; i++) { // if (GPIO_READ(N_READ_BUSY) == 1) // break; // } // if (i == MAX_WAIT_READ_BUSY) { // // #ifdef DEBUG // printf("N_READ_BUSY was not brought to 1 by NAND in time, retrying\n"); // // #endif // goto retry; // } n = PAGE_SIZE*(page & 1); for (i = 0; i < PAGE_SIZE; i++) { GPIO_SET_0(N_READ_ENABLE); shortpause(); buf[i + n] = GPIO_DATA8_IN(); // GPIO_SET_1(N_READ_ENABLE); shortpause(); } if (!n) // read the page again to ensure correct operation, bit 0 in page used for this purpose // printf("RE LOOP | page = %d, n = %d\n",page, n); // printf("Reading the page n° %d again to ensure correct operation\n", page_no); continue; if (memcmp(buf, buf + PAGE_SIZE, PAGE_SIZE) != 0) { if (retry_count == 0) printf("\n"); if (retry_count < 5) { printf("Page failed to read correctly! retrying\n"); retry_count++; page = page & ~1; goto retry_all; } printf("Too many retries. Perhaps bad block?\n"); fprintf(badlog, "Page %d seems to be bad\n", page_no); } if (write_spare) { if (fwrite(buf, PAGE_SIZE, 1, f) != 1) { perror("fwrite"); return -1; } } else { if (fwrite(buf, 512 * (PAGE_SIZE / 512), 1, f) != 1) { perror("fwrite"); return -1; } } retry_count = 0; } fcloseall(); clock_t end = clock(); printf("\n\nReading done in %f seconds\n", (float)(end - start) / CLOCKS_PER_SEC); //show cursor // printf("\e[?25h"); // fflush(stdout) ; return 0; } /*int read_pages(int first_page_number, int number_of_pages, char *outfile, int write_spare) { int page, block_no, page_nbr, percent, i; unsigned char buf[PAGE_SIZE], id[5], id2[5];; FILE *f = fopen(outfile, "w+"); if (f == NULL) { perror("fopen output file"); return -1; } if (GPIO_READ(N_READ_BUSY) == 0) { error_msg((char*)"N_READ_BUSY should be 1 (pulled up), but reads as 0. make sure the NAND is powered on"); return -1; } if (read_id(id) < 0) return -1; print_id(id); printf("if this ID is incorrect, press Ctrl-C NOW to abort (3s timeout)\n"); sleep(3); printf("\nStart reading...\n\n"); clock_t start = clock(); for (page = first_page_number; page < first_page_number + number_of_pages; page++) { // printf("page = %d, n = %d\n",page, n); // page_nbr = page - first_page_number + 1; // percent = (100 * page_nbr) / number_of_pages; // block_no = page / 64; // printf("Reading page n° %d in block n° %d (page %d of %d), %d%%\n", page, block_no, page_nbr, number_of_pages, percent); printf("\nReading page n° %d\n", page); send_read_command(page); while (GPIO_READ(N_READ_BUSY) == 0) { // printf("Busy\n"); shortpause(); } set_data_direction_in(); for (i = 0; i < PAGE_SIZE; i++) { GPIO_SET_0(N_READ_ENABLE); shortpause(); buf[i] = GPIO_DATA8_IN(); // shortpause(); GPIO_SET_1(N_READ_ENABLE); shortpause(); } if (write_spare) { if (fwrite(buf, PAGE_SIZE, 1, f) != 1) { perror("fwrite"); return -1; } } else { if (fwrite(buf, 512 * (PAGE_SIZE / 512), 1, f) != 1) { perror("fwrite"); return -1; } } } fcloseall(); clock_t end = clock(); printf("\nReading done in %f seconds\n", (float)(end - start) / CLOCKS_PER_SEC); } */ int write_pages(int first_page_number, int number_of_pages, char *infile) { int page, block_no, page_nbr, percent, retry_count; unsigned char buf[PAGE_SIZE], id[5], id2[5];; if (read_id(id) < 0) return -1; print_id(id); printf("if this ID is incorrect, press Ctrl-C NOW to abort (3s timeout)\n"); sleep(3); printf("\nStart writing...\n"); clock_t start = clock(); FILE *f = fopen(infile, "rb"); if (f == NULL) { perror("fopen input file"); return -1; } // printf("first_page_number = %d\n", first_page_number); // printf("number of pages = %d\n", number_of_pages); for (retry_count = 0, page = first_page_number; page < first_page_number + number_of_pages; page++) { retry_all: if (retry_count == 0) { // page_no = page / 2; page_nbr = page - first_page_number + 1; percent = (100 * page_nbr) / number_of_pages; block_no = page / 64; printf("Writing page n° %d in block n° %d (page %d of %d), %d%%\r", page, block_no, page_nbr, number_of_pages, percent); fflush(stdout); } fseek(f, page * PAGE_SIZE, SEEK_SET); fread(buf, PAGE_SIZE, 1, f); // printf("\nwriting page n°%d\n", page); retry: read_id(id2); if (memcmp(id, id2, 5) != 0) { printf("\nNAND ID has changed! retrying"); goto retry; } send_write_command(page, buf); while (GPIO_READ(N_READ_BUSY) == 0) { // printf("Busy\n"); shortpause(); } // read_status(); if (read_status()) { if (retry_count == 0) printf("\n"); if (retry_count < 5) { printf("Failed to write page correctly! retrying\n"); retry_count++; goto retry_all; } printf("Too many retries. Perhaps bad block?\n"); // retry_count = 0; } retry_count = 0; } fcloseall(); clock_t end = clock(); printf("\nWrite done in %f seconds\n", (float)(end - start) / CLOCKS_PER_SEC); return 0; } int erase_blocks(int first_block_number, int number_of_blocks) { int block, block_no, block_nbr, percent, i, n, retry_count; unsigned char id[5], id2[5]; if (read_id(id) < 0) return -1; print_id(id); printf("if this ID is incorrect, press Ctrl-C NOW to abort (3s timeout)\n"); sleep(3); printf("\nStart erasing...\n"); clock_t start = clock(); for (retry_count = 0, block = first_block_number; block < (first_block_number + number_of_blocks); block++) { retry_all: block_nbr = block - first_block_number + 1; percent = (100 * block_nbr) / number_of_blocks; if (retry_count == 0) { printf("Erasing block n° %d at adress 0x%02X (block %d of %d), %d%%\r", block, block * BLOCK_SIZE, block_nbr, number_of_blocks, percent); fflush(stdout); // printf("Block address : %d (0x%02X)\n", block * BLOCK_SIZE, block * BLOCK_SIZE); } retry: read_id(id2); if (memcmp(id, id2, 5) != 0) { printf("\nNAND ID has changed! retrying"); goto retry; } send_eraseblock_command(block * 64); // 64 = pages per block while (GPIO_READ(N_READ_BUSY) == 0) { // printf("Busy\n"); shortpause(); } if (read_status()) { if (retry_count == 0) printf("\n"); if (retry_count < 5) { printf("Failed to erase block correctly! retrying\n"); retry_count++; goto retry_all; } printf("Too many retries. Perhaps bad block?\n"); // retry_count = 0; } retry_count = 0; } clock_t end = clock(); printf("\nErasing done in %f seconds\n", (float)(end - start) / CLOCKS_PER_SEC); return 0; }