/**** * flashburn.c Andrew Huang Xtreme Ideas * bunnie@mit.edu * * version 1.0 <12/30/98> * linux version of FLASH burner driver code, supports erasing, reading, * writing, and basic file manipulation. * * NOTE: when compiling this under linux, you must use the -O2 compiler flag. * for example, * * gcc -O2 flashburn.c -o flashburn * * This is because the I/O port instructions turn into in-line assembly. * Also, you must be super-user to run this program. ****/ /*** defines ***/ #if 0 /* dos compatibility commented out */ #undef LINUX 0 #define DOS 1 #endif #if 1 #define LINUX 1 #undef DOS #endif #undef SCANARGV /* scanargv library support; off for max compatibility */ /* open-loop wait parameters there is enough "play" in the loop such that this number does not really need to be adjusted even if this program is targetted for a variety of architectures */ #define WAIT 50 /*** includes ***/ #include "pport.h" #include "flashburn.h" /*** typedefs ***/ /*** globals ***/ char srcfname[256]; char dstfname[256]; FILE *srcfile; FILE *dstfile; unsigned char *srcdata; unsigned char *dstdata; unsigned long srcsize; unsigned long dstsize; /*** func protos ***/ void init_vars(); void init_ports(); void print_banner(); void set_FB_adr( unsigned char adr ); void set_FB_data( unsigned char data ); void set_PP_outmode(); void set_PP_inmode(); void FB_float(); void FB_engage(); void graceful_exit(); void do_writecycle( long wa, unsigned char wd ); void get_dstfname(); void get_srcfname(); void erase_sectors(); int get_ryby(); unsigned long sect_num2adr( int sector_number ); unsigned char doread_cycle( unsigned long wa ); void verify_flash(); void read_flash(); void program_flash(); void set_FB_reg( unsigned char data, unsigned char adr ); void graceful_exit(); void main( int argc, char *argv[] ) { /* main */ /* locals */ unsigned int i, count; long wait; FILE *bitstream = NULL; char c; int verbose = 1; char *fname; int choice; char data; /* body */ #if 0 #if SCANARGV scanargv( argc, argv, "%s [-v] [-f filename]", "[-v %+|-f %r]", &verbose, &bitstream ); #else fname = argv[1]; printf( "Using %s for FLASH image.\n", fname ); if( NULL IS (bitstream = fopen( fname, "r" ) ) ) { printf( "Can't open %s...\n", fname ); exit(0); } /* if */ verbose = 0; #endif #endif if( verbose ) print_banner(); /***** * init any machine-specific stuph *****/ init_vars(); init_ports(); /***** * initialize all registers to a known state *****/ /* set address to inactive */ set_FB_adr( FB_IDLE ); FB_float(); /* make sure that the outputs aren't driving */ set_PP_outmode(); /* set pport to output mode */ set_FB_reg( FB_CS | FB_OE | FB_WR, FB_CTL ); /* set all ctl sigs inactive */ set_FB_reg( 0, FB_DATA ); set_FB_reg( 0, FB_ALO ); set_FB_reg( 0, FB_AMID ); set_FB_reg( 0, FB_AHI ); /* we are all initialized */ printf( "Please insert device in programming socket...\n" ); getchar(); #if 0 printf( "test loop...\n" ); /* step 4: set pport to input mode */ outb( 0xFF, LP1_DR ); set_PP_inmode(); data = inb( LP1_BASE + 0x402 ); printf( "%02X\n", data & 0xFF ); outb( 0x35, LP1_BASE + 0x402 ); printf( "%02X\n", data & 0xFF ); while(1){ /* step 6: get the data! */ data = (unsigned char) inb( LP1_DR ); printf( "data: %02X\n", data & 0xFF ); fflush(stdout); } #endif FB_engage(); /* turn on the output drivers */ /***** * main interaction loop *****/ while( 1 ) { printf( "\nWhat is thy bidding, my master?\n" ); printf( "1) set source file for programming data\n" ); printf( "2) set destination file for readback data\n" ); printf( "3) program FLASH with data in source file\n" ); printf( "4) erase sectors\n" ); printf( "5) read FLASH and store in destination file\n" ); printf( "6) verify device matches source file\n" ); printf( "7) quit\n" ); printf( "flashburn> " ); scanf( "%d", &choice ); getchar(); /* eat CR/LF */ switch( choice ) { case 1: get_srcfname(); break; case 2: get_dstfname(); break; case 3: program_flash(); break; case 4: erase_sectors(); break; case 5: read_flash(); break; case 6: verify_flash(); break; case 7: graceful_exit(); break; default: printf( "Unrecognized command.\n" ); } /* switch choice */ } /* while */ } /* main */ void verify_flash() { /* verify_flash */ /* locals */ /* body */ printf( "For now, read contents to file and do a command line diff...\n" ); return; } /* verify_flash */ void read_flash() { /* read_flash */ /* locals */ unsigned long start, count, end; char startadrstr[256]; char countstr[256]; char response; unsigned long index; /* body */ /* reset device into read mode */ do_writecycle( 0x000, 0xF0 ); printf( "Please enter start address for reading, in hex: " ); scanf( "%s", startadrstr ); getchar(); start = strtoul( startadrstr, NULL, 16 ); if( start > FLASH_SIZE ) { printf( "Start address is out of range. Aborting read.\n" ); return; } printf( "Plesae enter how many bytes to read, in hex: " ); scanf( "%s", countstr ); getchar(); count = strtoul( countstr, NULL, 16 ); if( start + count > FLASH_SIZE ) { printf( "End of read region exceeds device capacity!\n" ); printf( "Continue? (y/n) " ); scanf( "%c", &response ); getchar(); if( response AINT 'y' ) { printf( "Aborting read...\n" ); return; } /* if */ printf( "Okay, read will be truncated!\n" ); end = FLASH_SIZE; } /* if */ else { end = start + count; } printf( "Reading..." ); /* always zero-align the readback data for convenience sake */ for( index = start; index < end; index++ ) { dstdata[index - start] = doread_cycle( index ); if( !(index % DOT_PERIOD) ) { putchar('.'); if( !(index % (DOT_PERIOD * 64) ) ) { putchar( '\n' ); } /* if */ fflush(stdout); } /* if */ } /* for */ printf( "\nRead completed, storing data in destination file.\n" ); for( index = start; index < end; index++ ) { fwrite( &(dstdata[index - start]), 1, 1, dstfile ); } fflush( dstfile ); /* flush the file, commit to disk */ return; } /* read_flash */ unsigned char doread_cycle( unsigned long wa ) { /* do_readcycle */ /* locals */ unsigned char data; /* body */ /* step 0: make sure ctl lines are clear */ set_FB_reg( FB_CS | FB_OE | FB_WR, FB_CTL ); /* step 1: set addresses */ set_FB_reg( wa & 0xFF, FB_ALO ); set_FB_reg( (wa >> 8) & 0xFF, FB_AMID ); set_FB_reg( (wa >> 16) & 0xFF, FB_AHI ); /* step 2: set PP_INIT to float */ FB_float(); /* step 3: enable OE, CS */ set_FB_reg( FB_WR, FB_CTL ); /* step 4: set pport to input mode */ outb( 0xFF, LP1_DR ); set_PP_inmode(); /* step 5: set PP_INIT to drive */ FB_engage(); /* step 6: get the data! */ data = (unsigned char) inb( LP1_DR ); /* step 7: float the PP pins again */ FB_float(); /* step 8: set pport to output mode */ set_PP_outmode(); /* step 9: disable OE and CS */ set_FB_reg( FB_WR | FB_CS | FB_OE, FB_CTL ); /* step 10: re-engage the device */ FB_engage(); /* step 11: return the data */ return( data ); } /* doread_cycle */ void program_flash() { /* locals */ unsigned long count; char startadrstr[255]; unsigned long start, end = 0; char response; /* body */ printf( "Please enter the start address in hex for programming: " ); scanf( "%s", startadrstr ); getchar(); start = strtoul( startadrstr, NULL, 16 ); if( start > (FLASH_SIZE - srcsize) ) { printf( "Start location %lX places content end beyond FLASH capacity.\n", start ); printf( "Continue? (y/n) " ); scanf( "%c", &response ); getchar(); if( response AINT 'y' ) { printf( "Aborting programming sequence...\n" ); return; } printf( "Okay, data will be truncated!\n" ); end = FLASH_SIZE - start; } /* if */ else { end = srcsize; } printf( "Programming %ld bytes starting at %ld...\n", end, start ); /* unlock bypass mode! */ do_writecycle( 0x555, 0xAA ); do_writecycle( 0x2AA, 0x55 ); do_writecycle( 0x555, 0x20 ); printf( "\nUnlock bypass mode engaged.\n" ); printf( "Programming..." ); for( count = 0; count < end; count++ ) { /* unlock bypass program cycles */ do_writecycle( 0x000, 0xA0 ); do_writecycle( start + count, srcdata[count] ); while( !get_ryby() ) { ; /* wait */ } /* while */ /* martching dots every 4K */ if( !(count % DOT_PERIOD) ) { putchar( '.' ); if( !( count % (DOT_PERIOD * 64) ) ) { putchar( '\n' ); } fflush(stdout); } /* if */ } /* for */ /* unlock bypass reset! */ do_writecycle( 0x000, 0x90 ); do_writecycle( 0x000, 0x00 ); printf( "\nUnlock bypass mode disengaged.\n" ); printf( "Finished programming FLASH; please perform verify operation to check programming.\n" ); } /* program_flash */ void erase_sectors() { /* erase_sectors */ /* locals */ int sector_number; unsigned long sector_address; /* body */ printf( "\nWhich sector would you like to erase (0-18, 19 is full chip)? " ); scanf( "%d", §or_number ); getchar(); if( sector_number < 0 OR sector_number > 19 ) { printf( "Sector number out of range.\n" ); return; } if( sector_number IS 19 ) { printf( "Erasing entire chip.\n" ); } else { printf( "Erasing sector %d.\n", sector_number ); } printf( "Press return to commence erase.\n" ); getchar(); if( sector_number IS 19 ) { do_writecycle( 0x555, 0xAA ); do_writecycle( 0x2AA, 0x55 ); do_writecycle( 0x555, 0x80 ); do_writecycle( 0x555, 0xAA ); do_writecycle( 0x2AA, 0x55 ); do_writecycle( 0x555, 0x10 ); while( !get_ryby() ) { ; /* wait */ } /* while */ printf( "Entire chip successfully erased.\n" ); } /* if */ else { do_writecycle( 0x555, 0xAA ); do_writecycle( 0x2AA, 0x55 ); do_writecycle( 0x555, 0x80 ); do_writecycle( 0x555, 0xAA ); do_writecycle( 0x2AA, 0x55 ); sector_address = sect_num2adr( sector_number ); do_writecycle( sector_address, 0x30 ); while( !get_ryby() ) { ; /* wait */ } /* while */ printf( "Sector %d successfully erased.\n", sector_number ); } /* else */ } /* erase_sectors */ unsigned long sect_num2adr( int sector_number ) { /* sect_num2adr */ /* locals */ unsigned long temp; /* body */ temp = 0L; if( sector_number >= 4 ) { temp = (sector_number - 3) << 16; return( temp ); } else { switch( sector_number ) { case 0: temp = 0x0; break; case 1: temp = 0x04000; break; case 2: temp = 0x06000; break; case 3: temp = 0x08000; break; default: printf( "sect_num2adr(): Internal error.\n" ); graceful_exit(); break; } /* switch */ return( temp ); } /* else */ } /* sect_num2adr */ int get_ryby() { /* get_ryby */ /* return( (FB_RYBY & inb( LP1_SR )) ? 1 : 0 ); */ return( 1 ); } /* get_ryby */ void get_srcfname() { /* get_srcfname */ /* locals */ long count; /* body */ printf( "\nSource file name (255 chars max): " ); scanf( "%s", srcfname ); printf( "Source file set to %s\n", srcfname ); if( NULL IS (srcfile = fopen( srcfname, "r" ) ) ) { printf( "Can't open %s...\n", srcfname ); graceful_exit(); } /* if */ printf( "Filehandle obtained.\n" ); count = 0; while( (count < FLASH_SIZE) AND !feof( srcfile ) ) { fread( &(srcdata[count]), 1, 1, srcfile ); count++; } /* while */ printf( "Read %ld bytes into buffer.\n", count ); srcsize = count; } /* get_srcfname */ void get_dstfname() { printf( "\nDestination file name (255 chars max): " ); scanf( "%s", dstfname ); printf( "Destination file set to %s\n", dstfname ); if( NULL IS (dstfile = fopen( dstfname, "w" ) ) ) { printf( "Can't open %s...\n", dstfname ); graceful_exit(); } /* if */ printf( "Filehandle obtained.\n" ); } void init_ports() { /* init_ports */ /* locals */ char data; /* body */ /* if in linux mode, enable access to the parallel port I/O space. you must be superuser to do this. */ #ifdef LINUX ioperm((unsigned long) LP1_BASE, (unsigned long) 0x3, 0x2 ); iopl( 3 ); #endif /* force into "byte mode" for PS/2 compatibility mode */ data = inb( LP1_BASE + 0x402 ); printf( "ECR mode: %02X\n", data & 0xFF); outb( (data & 0x1F) | 0x20, LP1_BASE + 0x402 ); /* ECP ports uses bits 7, 6, 5 of the ECR (LP1_BASE + 0x402 as follows: */ /* 000 SPP 001 Byte 010 fast centronics 011 ECP 100 EPP 101 Reserved 110 Test 111 Config */ } /* init_ports */ void do_writecycle( long wa, unsigned char wd ) { /* do_writecycle */ /* locals */ /* body */ /* step 0: make sure ctl lines are clear */ set_FB_reg( FB_CS | FB_OE | FB_WR, FB_CTL ); /* step 1: set addresses */ set_FB_reg( wa & 0xFF, FB_ALO ); set_FB_reg( (wa >> 8) & 0xFF, FB_AMID ); set_FB_reg( (wa >> 16) & 0xFF, FB_AHI ); /* step 2: set data */ set_FB_reg( wd & 0xFF, FB_DATA ); /* step 3: cycle the control pins */ set_FB_reg( FB_OE, FB_CTL ); set_FB_reg( FB_CS | FB_OE | FB_WR, FB_CTL ); /* that's it! */ } /* do_writecycle */ void set_FB_reg( unsigned char data, unsigned char adr ) { /* set_FB_reg */ /* locals */ /* body */ /*********** EXPECTATION **************/ /* FB_adr is idle, OE is high so we don't have to frotz with the FB_float thing and PP_outmode thing. */ outb( data & 0xFF, LP1_DR ); set_FB_adr( adr ); /* commit the reg value */ set_FB_adr( FB_IDLE ); } /* set_FB_reg */ void FB_float() { /* FB_float */ char CR_temp; CR_temp = (char) inb( LP1_CR ); CR_temp |= FB_FLOAT; /* set the float bit */ outb( CR_temp & 0xFF, LP1_CR ); /* commit change */ } /* FB_float */ void FB_engage() { /* FB_engage */ char CR_temp; CR_temp = (char) inb( LP1_CR ); CR_temp &= ~FB_FLOAT; /* clear the float bit */ outb( CR_temp, LP1_CR ); /* commit change */ } /* FB_engage */ void set_PP_outmode() { /* set_outmode */ char CR_temp; CR_temp = (char) inb( LP1_CR ); CR_temp &= ~CR_BID; /* clear the bidirectional bit */ outb( CR_temp, LP1_CR ); /* commit change */ } /* set_outmode */ void set_PP_inmode() { /* set_inmode */ char CR_temp; CR_temp = (char) inb( LP1_CR ); CR_temp |= CR_BID; /* set the bidirectional bit */ outb( CR_temp, LP1_CR ); /* commit change */ } /* set_inmode */ void set_FB_data( unsigned char data ) { outb( data & 0xFF, LP1_DR ); } /* set_FB_data */ void set_FB_adr( unsigned char adr ) { /* set_FB_adr */ char CR_state; CR_state = (char) inb( LP1_CR ); /* printf( "\nCR_state: %02X, ", CR_state & 0xFF ); */ CR_state = ~FB_A0 & ~FB_A1 & ~FB_A2 & CR_state; /* clear to one first */ /* A* lines are inverted by hardware */ /* now set bits to 0 if necessary */ if( !(adr & 1) ) { CR_state |= FB_A0; } /* if */ if( !(adr & 2) ) { CR_state |= FB_A1; } /* if */ if( !(adr & 4) ) { CR_state |= FB_A2; } /* if */ /* printf( "CR_state: %02X ", CR_state & 0xFF ); */ outb( CR_state & 0xFF, LP1_CR ); /* set the port state */ } /* set_FB_adr */ void print_banner() { /* print_banner */ /* locals */ /* body */ printf( "flashburn v0.8 (12/30/98)\n" ); } /* print_banner */ void graceful_exit() { FB_float(); /* float the FB pins */ exit(0); } void init_vars() { /* init_vars */ /* locals */ /* body */ if( NULL IS (dstdata = (unsigned char *) calloc( FLASH_SIZE, sizeof( unsigned char ) ) ) ) { printf( "Can't allocate I/O buffers\n" ); graceful_exit(); } if( NULL IS (srcdata = (unsigned char *) calloc( FLASH_SIZE, sizeof( unsigned char ) ) ) ) { printf( "Can't allocate I/O buffers\n" ); graceful_exit(); } srcsize = 0; dstsize = 0; } /* init_vars */