/*
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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
// #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 ...\n\n" \
" 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 <# of pages> : read N pages including spare\n" \
" read_data <# of pages> : read N pages, discard spare\n" \
" write_full <# of pages> : write N pages, including spare\n" \
" write_data <# of pages> : write N pages, discard spare\n" \
" erase_blocks <# 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;
}