/******************************************************************************
//	flash.c			handles the flash card read/writes	
//	
//*****************************************************************************
//
//==== Future Expansion =======================================================
//		last update: Feb2009
//
//  2. Debug menu for more extensive file testing
//  3. 
//
******************************************************************************/

#include	<cfxbios.h>		// Persistor BIOS and I/O Definitions
#include	<cfxpico.h>		// Persistor PicoDOS Definitions

#include	<assert.h>
#include	<ctype.h>
#include	<errno.h>
#include	<float.h>
#include	<limits.h>
#include	<locale.h>
#include	<math.h>
#include	<setjmp.h>
#include	<signal.h>
#include	<stdarg.h>
#include	<stddef.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<time.h>


#include    "cf2_qsm332.h"
#include    "spi_cmd.h"
#include    "nav.h"
#include    "lifo.h"
#include    "setup.h"

// globals
extern char   Verbose;  // switch for displaying more info
extern char   IRQ_Flag; // interrupt flag: says what caused the interrupt
extern short  iparam [ MAX_PARAM ]; // global parameter settings
extern struct all_param all;

// internal prototypes ================================================
short  flash_status   ( void );
void   flash_menu     ( void );
void   flash_debug    ( void );
void   flash_dbg_menu ( void );
//=====================================================================

// The ping-pong buffer is just used for the data file,
// which gets lots of short packets, and thus is less efficient.
// for writing out packet-by-packet
short write_ppbuf ( short n, uchar *b ) //*************************************
{ // write n  bytes of b to ping-pong buffer
  // returns status
  // write-status is in the upper-nibble
  // 0x0_ = no flash write (just stored to ppbuf)
  // 0x1_ = did flash write to lower half
  // 0x2_ = did flash write to upper half
  
  // lower-nibble contains error:
  // 1 = illegal file pointer
  // 2 = illegal request of lower/upper half
  // 4 = bad ping-pong pointer
  short err=0, e=0, f=0;
  short bhalf = PP_BMAX / 2; // half-buffer size
  short i, k, b0;
  uchar *pp;
  
  pp = all.ppb->ppbuf; // point to the buffer area
  k = all.ppb->ipp; // beginning buffer pointer
  if (k >= PP_BMAX  || k < 0 ) // ????
  {  err=0x04; // bad buffer pointer
     k=0;  // things are wonky, try to recover...
  }
  
  b0 = ( k< bhalf ); // true if starting in the lower half
  i=0; // input index
  while ( k< PP_BMAX && i<n ) //==============
  { // still room to fill 
      pp[k++] = b[i++]; // xfr to the buffer
  } //========================================
  
  if ( k == PP_BMAX ) //=========================================
  { // then the top half is full!
    k=0; // start filling from the bottom
    while (i<n )  pp[k++] = b[i++];
    // PP_BMAX=2048, n is already constrained to <256,
    // so don't have to worry about overflowing pp here.
    // All the data is now stored, but
    // the top half needs to be saved to flash
    e =write_pp( 1 ); // write the top half buffer to flash
    f = 0x20; // wrote to upper half
  } //===========================================================
  
  if ( b0 && k >=bhalf ) // we went past the lower transition
  {
     e = write_pp( 0 ); // write the lower half buffer to flash
     f = 0x10; //flag that wrote to lower half
  }
  
  all.ppb->ipp = k; // save the pointer for the next time
               // k should be range 0<=k< PP_BMAX
               
  //status report
  if (e>0) err += e;
  err += f; // add in bit if flash was written
    
  return (err );
} //***************************************************************************

short  write_pp ( short n ) //*************************************************
{ // if n = 0/1, dump the lower/upper half of the ppbuf to a flash file
  // returns an error if there is a problem
  // 1=illegal file pointer
  // 2=illegal n 
  FILE *fp;
  short err=0;  // error
  uchar *b;
  short bhalf = PP_BMAX / 2; // half-buffer size
  
  if (n<0 || n>1 ) return(2); // out of bounds!
  
  b = all.ppb->ppbuf; // point to the buffer, lower half
  if (n==1)
     b += bhalf; // point to the upper half
     
  fp = all.fp_dat; // points to the data file
  
  if ( fp != NULL )
  {   // file is ok to write to
      fwrite( b, bhalf, 1, fp );
  } else err=1; // error flag to bad file
  
  return( err );
} //***************************************************************************

short  flush_pp ( short allbuf    ) //*****************************************
{ // if allbuf==TRUE, flushes entire buffer (error dump),
  // else writes whatever is remaining in the present half-buffer (normal).
  // resets all.ppb->ipp for a clean start next time.
  
  // returns an error if there is a problem
  // 1=illegal file pointer
  // 2=bad ppbuf pointer

  FILE *fp;
  short err=1;  // error
  uchar *b;
  short  k, b0=0;
  short  bhalf = PP_BMAX / 2;
  // b0 = start offset address (0=1st half, =bhalf for 2nd half)
  
  k = all.ppb->ipp; // ending buffer pointer
  all.ppb->ipp = 0; // RESET=0 = has been flushed
  
  if (k >= PP_BMAX  || k < 0 ) // wonky #!
  {  err=2; // bad buffer pointer
     allbuf = 1; // dump all of it
     k=PP_BMAX;  // things are wonky, dump whatever is there
  }
  
  if (!allbuf && k>= bhalf) b0 = bhalf; // write upper half
  
  
  b = all.ppb->ppbuf + b0; // point to the buffer offset
  k = k - b0; //=# of bytes to write out
     
  fp = all.fp_dat; // points to the data file
  
  if ( fp != NULL )
  {   // file is ok to write to
      fwrite( b, k, 1, fp );
  } else err=1; // error flag to bad file
  
  return( err );
} //***************************************************************************

short flash_store(short num, uchar dat[], short pid ) // **********************
{ // send num bytes from dat array  to the flash ping-pong buffer
  // pid = packet id byte;  see setup.h for definitions
  // data packet format: SB, pid, num, <dat>, EB
  // returns status from writing to ppbuf

   short err, i, j;
   uchar b[260];

  if (num>251) num=251;  
  // above limits the length
  // ntot = num+ 4; //includes sb, eb, num, pid  : ntot <=255 always
  
  b[0] = SB;    // start-packet char
  b[1] = pid;   // packet id
  b[2] = num;   // #data bytes
  j=3;
  for (i=0; i<num; i++ )  b[j++] = dat[i];  //xfr the rest to tmp
  b[j++] = EB; // end-packet char
  //b[2]   = j;  // j =total #bytes
  
  err = write_ppbuf( j,  b ); // write to ping-pong buffer
  //cprintf("flash_store:  err=%d\n",err);
   return(err );  
} //***************************************************************************

void flash_dive_mark(  short ncyc ) //*****************************************
{ // write the dive marker packet to the flash card,
  // ncyc is the cycle # for this dive (range =0...n)
  // 0902, store the profile counter = #profiles since start-of-mission

  uchar b[12];
  short i=0, surf, pro;
  long ti0;
  
  surf = iparam [ EA_surf_num ];  // 0902 =surfacing #
  pro  = iparam [ EA_prof_num  ]; // 0902 =profile #
  i    = b2_store(i,b, surf); // store the surfacing # 
  i    = b2_store(i,b, ncyc); // store the cycle #
  i    = b2_store(i,b, pro ); // 0902 store the profile #
  
  ti0 = all.time->ti0; // UX time of start of this dive
  i = b4_store(i,b, ti0 ); // store the start-time
  ti0 = RTCGetTime(0,0); // time now
  i = b4_store(i,b, ti0 ); // and store
  all.time->ti_cyc = ti0; // 0902 save in struct for writing to SBD
  

  flash_store(i, b, CF_DIVE);
  
  return;
} //***************************************************************************

void flash_end( void ) //******************************************************
{ // write the end-file  packet to the cf1

  uchar b[4] = "END";

  flash_store(3,b,CF_END);
  
  return;
} //***************************************************************************

void  flash_eng ( void ) //****************************************************
{ // write the engineering structure to the cf1
  uchar *b;
  short nb;
  struct eng_param *eng, edum;
  
    eng = all.eng; // point to the structure
    nb = sizeof(edum)-2; //=#bytes to send : removes the 2 dummy bytes
    b = (uchar*) all.eng +1; // point to isu_type in array

    flash_store( nb, b, CF_ENG ); 
  
  return;
} //***************************************************************************


void  flash_gps ( short pid ) //***********************************************
{ // write the engineering structure to flash
  uchar *b;
  short nb;
  struct gps_param *gps, g ;
  
    gps = all.gps; // point to the structure
    gps->nsurf = iparam[ EA_surf_num ]; //present surface #
    nb  = sizeof(g ); //=#bytes to send 
    b   = (uchar*) gps; // recast as correct pointer type

    flash_store( nb, b, pid ); 
  
  return;
} //***************************************************************************


void  flash_hyd ( void  ) //***************************************************
{ // write the hydraulics structure to flash
  uchar *b;
  short nb;
  struct hyd_param *hyd, h ;
  
    hyd = all.hyd; // point to the structure
    nb  = sizeof(h ); //=#bytes to send 
    b   = (uchar*) hyd; // recast as correct pointer type

    flash_store( nb, b, CF_HYD ); 
  
  return;
} //***************************************************************************

void  flash_drift ( void  ) //*************************************************
{ // write the drift structure to flash
  uchar *b;
  short nb;
  struct drift_param *drift, d ;
  
    drift = all.drift; // point to the structure
    nb  = sizeof(d ); //=#bytes 
    b   = (uchar*) drift; // recast as correct pointer type

    flash_store( nb, b, CF_DRIFT ); 
  
  return;
} //***************************************************************************

#if ZOOG ==1 //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
void   flash_zoog ( void ) //**************************************************
{  // write the zooglider structure to flash
  uchar *b;
  short nb;
  struct zcam_param *dd, ddum;
  
  dd = all.zcam;
  nb = sizeof(ddum); //=#bytes 
  b  = (uchar*) dd; // point to the zoog structure
  
  flash_store( nb, b, CF_ZOOG ); 

  return;
} //***************************************************************************
#endif //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ


void  flash_surf ( void  ) //**************************************************
{ // write the GPS surface-drift structure to flash
  uchar *b;
  short nb;
  struct ublox_param *ubx, u ;
  
    ubx = all.ubx; // point to the structure
    nb  = sizeof( u ); //=#bytes 
    b   = (uchar*) ubx; // recast as correct pointer type

    flash_store( nb, b, CF_SURF ); 
  
  return;
} //***************************************************************************


void  flash_pwr ( void  ) //***************************************************
{ // write the avg power structure to flash
  uchar *b;
  short nb;
  struct pwr_param *pwr, p ;
  
    pwr = all.pwr; // point to the structure
    pwr->watt[3] = 0; // not used
    pwr->ti[3]   = 0; // not used
    nb  = sizeof( p ); //=#bytes to send 
    b   = (uchar*) pwr; // recast as correct pointer type

    flash_store( nb, b, CF_PWR ); 
  
  return;
} //***************************************************************************



void  flash_sbd ( void  ) //***************************************************
{ // write the avg power structure to flash
  uchar *b;
  short nb;
  struct sbd_param *sbd;
  
    sbd = all.sbd; // point to the structure
    nb  = 14; //=#bytes to send, includes start & end times
    b   = (uchar*) sbd; // recast as correct pointer type

    flash_store( nb, b, CF_SBD ); 
  
  return;
} //***************************************************************************


void  flash_motor ( void  ) //*************************************************
{ // write the last-motor ops structure to flash
  uchar *b;
  short nb;
  struct motor_stat *mot, m ;
  
    mot = all.mot; // point to the structure
    nb  = sizeof(m ); //=#bytes to send 
    b   = (uchar*) mot; // recast as correct pointer type

    flash_store( nb, b, CF_MOTOR ); 
  
  return;
} //***************************************************************************


void flash_spi_error (  short k, short rstat ) //******************************
{ // dec14: write out spi stats,
  // k = serr[k] index: k=0..4 = chip-select[GPIO,HYD,PITCH,ROLL, unknown]
  // rstat = error value
  // this will give us a better idea for when these errors occur.

  uchar b[12];
  short i;
  long ti0;
  
  i = 0;
  b[i++] = k; // offending index
  b[i++] = rstat; // the error
  b[i++] = all.exc->serr[k];  // accumulative error counter
  
  ti0 = RTCGetTime( 0, 0   ); // time now
  i   = b4_store(i, b, ti0 ); // and store  

  flash_store( i, b, CF_SPI );
  
  return;
} //***************************************************************************


short  flash_open_file ( char c ) //*******************************************
{  // open a flash file with first letter = c
   // returns:
   // 1 = erroneous dive #
   // 2 = open error
  
  FILE *fp;
  short surf, err=0;
  
  surf = iparam [ EA_surf_num ];
  if (surf<0 ) 
  {  
    surf=9999; //keep in bounds
    err = 1; // set flag
  }
  
  sprintf(all.fdat_name,"c:\\DATA\\%c%03d", c, surf);
  fp = fopen(all.fdat_name, "a+"); // appends if necessary
  
  if ( fp==NULL )
  { 
     cprintf("FILE OPEN ERROR, name= %s\n", all.fdat_name );
     err = 2;
  }
  
  all.fp_dat = fp; //no matter what, update the pointer
 
  return ( err );
} //***************************************************************************

short flash_update_FAT ( void ) //*********************************************
{ // open and close the file to update the FAT
  // NOTE this does nothing with the ping-pong buffer,
  // so the update should still reside on 512byte sector boundaries
  
  // returns 2 if a re-open error.
  
  short err=0;
  FILE *fp;
  
  err = fclose ( all.fp_dat ); // updates the FAT
  fp  = fopen( all.fdat_name, "a+" ); // re-open
  
  if ( fp==NULL )
  { 
     cprintf("FILE UPDATE ERROR, name= %s\n", all.fdat_name );
     err = 2;
  }
  
  all.fp_dat = fp; //no matter what, update the pointer
 
  return ( err );
} //***************************************************************************

void flash_open ( char c ) //**************************************************
{ // opens a new 'cxxxx' file where xxxx = dive num
  // and c is the first letter in the name, typically 'D'
  // the file pointer is stored to fp_dat.
  
  // If the ping-pong buffer is NOT zero, it is dumped to an 'Exxxx' file
  // and then the desired file is opened.
  
  // no error is returned, although a message is displayed
  
  
  if (all.ppb->ipp !=0 ) //=========================
  { // maybe spurious reset..try to recover what is there
     // dec14, display exception
     printf("dumping spurious data to E file: %d bytes\n", all.ppb->ipp );
     flash_open_file( 'E' ); // E=error dump file
     flush_pp(1); // dump ALL of the pp buffer
     flash_close();
  
  } //==============================================
  // else OK, open the type of file that we want
  
  flash_open_file ( c );
  flash_dive_mark(0); // start with a dive mark
  
  return;
} //***************************************************************************

short flash_close ( void ) //**************************************************
{ // close the present flash file,
  // clearing the ping-pong buffer beforehand
  short err=1;  
  
  if ( all.fp_dat  ) //================================================
  { // then there is something to close!
	   flash_end( ); // add end mark - goes to the pp-buffer
	   flush_pp( 0 ); // flushes the pp-buffer, resets all.ppb->ipp=0
       err = fclose ( all.fp_dat );
       if (err) cprintf("flash close err=%d\n",err);
       all.fp_dat = NULL; // update so we know the file is closed
  } //=================================================================
  else cprintf("No flash file ptr to close\n");
  
  return (err);
} //***************************************************************************

short check_flash_file ( void ) //*********************************************
{ // may16 check read/write to file, allows remount try
  // returns error
  // 0=ok
  // 0=ok
  // 2=could not open file
  // 1=could not close file
 short err;
 
   cprintf("Performing a test file check\n");
   err = file_test();
   if (err) //===============================
   { // may16: first try to re-mount
     cprintf("trying to mount the flash card\n");
     execstr("ccc 1");  // may16 try to re-mount
     err = file_test(); // try again
   } //======================================
   else { cprintf("Flash card is OK\n"); }
   
   if (err) //--------------------------------
   { // still failed
     cprintf("flash card FAILED test. \n");
   } //---------------------------------------
   
  return err;
} //***************************************************************************

short file_test ( void ) //****************************************************
{ // open a file, write a test packet, and close
  // returns error
  // 0=ok
  // 2=could not open file
  // 1=could not close file
  short err=0, e;
  uchar dat[256];
  short i, n;

  n = 28;
  for (i=0;i<n;i++) dat[i] = i + 48;  //initialize
  
  all.ppb->ipp = 0; // reet the ping-pong buffer
  
  err = flash_open_file( 'T' ); // test file

	if (err==0) //-----------------------------------------------
	{ // opened OK, 
	   // test ping-pong buffer

	   // write data to pp-buffer
	   all.time->ti0 = RTCGetTime (0, 0 ); // put marker into test
	   flash_dive_mark(0); // add a dive marker
	   // write out some test patterns
	   for (i=0; i<2; i++)
	   {
	     e = flash_store( n, dat, CF_TEST );
	     if (e) cprintf("flash_store status=%02x\n",e);
	   }
	   
	   err=flash_close(); // adds end-mark & flushes the buffer
	   // resets ppb->ipp=0 and fp_dat=NULL
	} //---------------------------------------------------------
	

	cprintf(" test file result ");
	if (err) cprintf("BAD\n");
	else     cprintf("OK\n");
  
  return ( err );
} //****************************************************************************


short flash_status(void ) // ***************************************************
// *** run through a sequence of tests of flash card status
{  // returns the file #, or -1 if error
  short err, dir_mode;
  char	Cdatadir[] = "C:data";
  char  Cdir[] = "C:\\";
  long mb=-1;
  float y;

   //--- do the file-check -------------------------------------------------
   cprintf("Performing a test file check\n");
   err = file_test();
   if (err) //===============================
   { // may16: first try to re-mount
     printf("trying to mount the flash card\n");
     execstr("ccc 1");  // may16 try to re-mount
     err = file_test(); // try again
   } //======================================
   
   if (err) //=========================================================
   { // file test failed
     cprintf("Try making data directory and try again? \n");
     if ( YesNo() ) //yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
     { // make the directory
		dir_mode = 0666; // directory attributes, via Unix
		err = pdx_mkdir(Cdatadir,dir_mode);
		if (err!=0) err=1; // ERROR
		cprintf("%d =mkdir stat, 1=error\n", err);
		if (err==0)
		{
		  cprintf("mkdir successful, trying test file\n");
		  err = file_test();
		} else  cprintf("mkdir failed!\n");
      } //yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
   
   } //================================================================
   
   if ( err==0) //=====================================================
   { // get flash info if requested
     cprintf("query flash for MBfree? (will take a minute) \n");
     if ( YesNo() )
     { 
        cprintf("Calculating...\n");
        mb = DIRTotalSpace( Cdir );
        y = mb*1e-6; // #Mb
         printf("Total Flash Memory = %6.1f Mb, ", y);
        mb = DIRFreeSpace( Cdir );
        y = mb*1e-6; // #Mb
         printf(" %6.1f Mb are free \n");
     }
   } //================================================================
   
   return ( err ); 
} //***************************************************************************

//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
void flash_test( void ) //*****************************************************
{
  char ch;  //input char
  short  done, tmo;
  long tnow;
  uchar dat[256];
  short i, ierr, n;

  n = 20;
  for (i=0;i<n;i++) dat[i] = i + 48;  //initialize

	cprintf("         Flash card test : %s \n", __DATE__);
	
	flash_menu();
  
	done = false;
	tnow = RTCGetTime(0,0);  // gets present time
	tmo = 0;
	while (!done) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	{  //cycle until we get a quit command

		
		while ( SCIRxQueuedCount() )
		    SCIRxFlush(); // delete any old stuff 
        if (ch !=' ' && ch>0 ) printf("F_1>\n"); // display directory prompt
	    ch = -1; // illegal value - means it timed out
	    ch = SCIRxGetCharWithTimeout( 1000 ); // get a new character
	    tmo = check_tmo ( &ch, tmo );

	    //================================================================
	    switch(ch) //====================================================
	    { // and depending upon the cmd, do the appropriate action =======
	    
        case 'Q' :  done=1; break; //quit function
        case '?' :  break; // causes directory prompt to be displayed
        case ' ' :  break; // wait for another character
        case 'D' :  flash_debug(); break;
        case 'U' :  flash_menu (); break;
        case  -1 :  break; // wait for another character
	    case '+' :  toggle_verbose(); // toggle verbose mode
				    break;



  
		case 'S' : 
			ierr = flash_status(); 
			cprintf("Flash Status = ");
			if (ierr) 
				cprintf("BAD!!\n");
			else
				cprintf("OK\n");
			break;


        default  : cprintf(" input unknown \n");
	} // end switch ------------------------------------------------
	
  }  //*** end while ++++++++++++++++++++++++++++++++++++++++++++++++++++
  
  return;
}  //**************************************************************************

void flash_menu(void) //*******************************************************
{ //print out menu selections 
 cprintf("                Flash Menu\n"
 		"Q.uit  test     :   U.pdate menu   :   D.ebug tests \n"
        "S.tatus         \n");
 return;
} //***************************************************************************

//*****************************************************************************
void flash_debug ( void ) //***************************************************
{
  char ch;  //input char
  short  done, tmo;
  long tnow;
  uchar dat[256];
  short i, n;

  n = 12;
  for (i=0;i<n;i++) dat[i] = i + 48;  //initialize

	cprintf("         Flash card debug : %s \n", __DATE__);
	
	flash_dbg_menu();
  
	done = false;
	tnow = RTCGetTime(0,0);  // gets present time
	tmo = 0;
	while (!done) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	{  //cycle until we get a quit command

		
		while ( SCIRxQueuedCount() )
		    SCIRxFlush(); // delete any old stuff 
        if (ch !=' ' && ch>0 ) printf("F_2>\n"); // display directory prompt
	    ch = -1; // illegal value - means it timed out
	    ch = SCIRxGetCharWithTimeout( 1000 ); // get a new character
	    tmo = check_tmo ( &ch, tmo );

	    //================================================================
	    switch(ch) //====================================================
	    { // and depending upon the cmd, do the appropriate action =======
	    
        case 'Q' :  done=1; break; //quit function
        case '?' :  break; // causes directory prompt to be displayed
        case ' ' :  break; // wait for another character
        case 'U' :  flash_dbg_menu(); break;
        case  -1 :  break; // wait for another character
	    case '+' :  toggle_verbose(); // toggle verbose mode
				    break;



  
		case 'F' : flash_update_FAT(); break;
		case 'C' : flash_close();      break;
		case 'O' : flash_open ('D');   break;
		
		case 'W' : /* write out all types */
		    flash_eng();
			flash_gps(1);
			flash_hyd();
			flash_motor(); /**/
			dat[0]++;
			i = flash_store(n,dat, CF_TEST );
			cprintf("Store packet %2d, status=%02x\n",dat[0]-48,i);
			break;


        default  : cprintf(" input unknown \n");
	} // end switch ------------------------------------------------
	
  }  //*** end while ++++++++++++++++++++++++++++++++++++++++++++++++++++
  
  return;
}  //**************************************************************************

void flash_dbg_menu ( void ) //************************************************
{ //print out menu selections 
 cprintf("              Flash Debug Menu\n"
 		"Q.uit  debug   : U.pdate menu   : F.AT update \n"
        "O.pen D file   : C.lose D file  : W.rite all structs\n");
 return;
} //***************************************************************************


//*** END *********************************************************************