/***********************************************  ***************************/
/* Summary  : Test Routines R16AU RecipeCard				    */
/* Filename : lpstop.c                                                      */
/* Author   : Robert Herlien (rah)					    */
/* Project  : BEDS (Benthic Event Detection System			    */
/* Revision : 1.0							    */
/* Created  : 01/14/2003 by Bob Herlien, adapted to BEDS 9/21/2011	    */
/*									    */
/* This is a modification of code originally written by Bob  Herlien for    */
/* OASIS3, further adapted for NOAA, and finally for BEDS		    */
/*									    */
/****************************************************************************/
/* Modification History:						    */
/* 14jan2003 rah - created						    */
/* 16jul2005 rah - borrowed and respun for NOAA EAR project		    */
/* 21sep2011 rah - borrowed and respun for BEDS				    */
/****************************************************************************/

#include <cfxbios.h>			/* Persistor BIOS definitions	    */
#include <cfxpico.h>			/* Persistor PicoDOS definitions    */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>


#define RTC_INT_LEVEL	2		/* Interrupt level for RTC	    */
#define SLEEP_51MS_TICKS 19		/* Sleep for 1 second at a time     */
#define ADSLOT		NMPCS3		/* SPI slot for ADC		    */

typedef short boolean;

#ifndef FALSE
#define FALSE	0
#endif

#ifndef TRUE
#define	TRUE	~FALSE
#endif

#define OK	 0
#define ERROR	-1

typedef enum				/****************************************/
{					/* Enum for wakeup from LPStop	   	*/
  WakeNone = 0,				/* Invalid, init'd to this before wake	*/
  WakeTmout,				/* Timeout from PIT			*/
  WakeIRQ2,				/* External /IRQ2			*/
  WakeIRQ5,				/* External /PBM pin (IRQ5)    		*/
  WakeIRQ7,				/* External /IRQ7			*/
  WakeConsoleChar,			/* Character from console port	   	*/
  WakeIRQOrConsoleChar			/* Used by int hndlr until we know which*/
} WhatWokeUs;				/****************************************/


/********************************/
/*	Global Data		*/
/********************************/

const char * const	signon = "LPStop test program\n\n";

static boolean		analogPwrOn = FALSE;
static boolean		wakeOnConsoleChar = TRUE;
static boolean		wakeOnIRQ7 = TRUE;
static boolean		wakeOnIRQ5 = TRUE;
static boolean		wakeOnIRQ2 = FALSE;
static boolean		fullStop = TRUE;
static volatile WhatWokeUs	wakeupStatus = WakeNone;

static char *wakeStr[] = {"WakeNone", "WakeTmout", "WakeIRQ2", "WakeIRQ5",
			  "WakeIRQ7", "WakeConsoleChar",
			  "WakeIRQOrConsoleChar"};

void		Irq5ISR(void);
void		lpPowerDown(void);
void		lpPowerUp(void);
WhatWokeUs	doLPStop(void);
WhatWokeUs	doTimedStop(ushort secs);
void		rtcChore(void);


/************************************************************************/
/* Function    : main							*/
/* Purpose     : Main entry point					*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/* Comments    : Never returns						*/
/************************************************************************/
int main( void )
{
  short		rtcIntLevel = 0;
  ushort	secs;
  boolean	rtn;
  ulong		now;
  struct tm	*nowtm;

	// Identify the progam and build
  printf("\nProgram: %s: %s %s \n", __FILE__, __DATE__, __TIME__);
	// Identify the device and its firmware
  cprintf("Persistor CF%d SN:%ld   BIOS:%d.%d   PicoDOS:%d.%d\n", CFX,
	 BIOSGVT.CFxSerNum, BIOSGVT.BIOSVersion, BIOSGVT.BIOSRelease, 
	 BIOSGVT.PICOVersion, BIOSGVT.PICORelease);

  TMGSetSpeed(16000000);
  RTCInit(RTC_INT_LEVEL);
  cprintf(signon);

  IEVInsertAsmFunct(Irq5ISR, level2InterruptAutovector);
  IEVInsertAsmFunct(Irq5ISR, level4InterruptAutovector);
  IEVInsertAsmFunct(Irq5ISR, level5InterruptAutovector);
  IEVInsertAsmFunct(Irq5ISR, level7InterruptAutovector);
  IEVInsertAsmFunct(Irq5ISR, spuriousInterrupt);

  while(1)
  {
    cprintf("\n");
    cprintf("Type 'T' to print time\n");
    cprintf("Type 'P' to go LPStop\n");
    cprintf("Type 'S' to stop for given time\n");
    cprintf("Type 'A' to %sable analog power during sleep\n",
		analogPwrOn ? "dis" : "en");
    cprintf("Type 'F' to sleep with %sStop mode\n",
	    fullStop ? "Fast" : "Full");
    cprintf("Type 'U' to %sable wakeup on serial char\n", 
		wakeOnConsoleChar ? "dis" : "en");
    cprintf("Type '2' to %sable wakeup on IRQ2\n", 
		wakeOnIRQ2 ? "dis" : "en");
    cprintf("Type '5' to %sable wakeup on IRQ5\n", 
		wakeOnIRQ5 ? "dis" : "en");
    cprintf("Type '7' to %sable wakeup on IRQ7\n", 
		wakeOnIRQ7 ? "dis" : "en");
    cprintf("Type 'X' to exit this program\n");

    switch(cgetc())
    {
      case 'x':
      case 'X':
      case 3:
	  cprintf("Goodbye\n");
	  SCITxWaitCompletion();
	  return(0);

      case 'T':
      case 't':
	  RTCtime(&now);
	  nowtm = gmtime(&now);

	  cprintf("%02d/%02d/%04d %02d:%02d:%02d\n", nowtm->tm_mon + 1,
		  nowtm->tm_mday, nowtm->tm_year + 1900, nowtm->tm_hour, 
		  nowtm->tm_min, nowtm->tm_sec);
	  break;

      case 'U':
      case 'u':
	  wakeOnConsoleChar = !wakeOnConsoleChar;
	  cprintf("Board will%s wake up on Rx\n", 
		  wakeOnConsoleChar ? "" : " not");
	  break;

      case '2':
	  wakeOnIRQ2 = !wakeOnIRQ2;
	  cprintf("Board will%s wake up on IRQ2\n", 
		  wakeOnIRQ2 ? "" : " not");
	  break;

      case '5':
	  wakeOnIRQ5 = !wakeOnIRQ5;
	  cprintf("Board will%s wake up on IRQ5\n", 
		  wakeOnIRQ5 ? "" : " not");
	  break;

      case '7':
	  wakeOnIRQ7 = !wakeOnIRQ7;
	  cprintf("Board will%s wake up on IRQ7\n", 
		  wakeOnIRQ7 ? "" : " not");
	  break;
		
      case 'A':
      case 'a':
	  analogPwrOn = !analogPwrOn;
	  cprintf("Board will%s leave analog power on during sleep\n", 
		  analogPwrOn ? "" : " not");
	  break;

      case 'F':
      case 'f':
	  fullStop = !fullStop;
	  cprintf("LPStop will use %s mode\n", 
		  fullStop ? "FullStop" : "FastStop");
	  break;

      case 'P':
      case 'p':
	  cprintf("Powering down\n");
	  lpPowerDown();
	  rtn = doLPStop();
	  lpPowerUp();
	  cprintf("Power up, Wakeup type = %s\n", wakeStr[rtn]);
	  if (kbhit())
	      cprintf("Keyboard char = %c\n", cgetc());
	  break;

      case 'S':
      case 's':
	  cprintf("Number of seconds in low power mode:  ");
	  if (scanf("%u", &secs) < 1)
	  {
	      cprintf("\nBad input");
	      break;
	  }
	  cprintf("\nPowering down for %u seconds\n", secs);
	  lpPowerDown();
	  rtn = doTimedStop(secs);
	  lpPowerUp();
	  cprintf("Power up, Wakeup type = %s\n", wakeStr[rtn]);
	  if (kbhit())
	      cprintf("Keyboard char = %c\n", cgetc());
	  break;
    }
  }

  return(0);

} /* main() */


/************************************************************************/
/* Function    : lpPowerDown						*/
/* Purpose     : Set CF2 into low-power mode for sleep			*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
void lpPowerDown(void)
{
    uchar		mirrorpins[] = { 1, 22, 23, 24, 25, 26, 31, 32, 33, 34, 0 };

//	FORCE FLOATING I/O PINS TO OUTPUTS TO REDUCE CURRENT
//		Sample each I/O pin, and set it back to an output at the same
//		level. This works for everything except driven signals that
//		may change and keeps floaters from wasting power.

	PIOMirrorList(mirrorpins);
	PIOSet(TPU15); PIOSet(TPU14);
//	PIOClear(TPU8);	PIOSet(TPU9);		// Turn off AUX RS-232s
	PIOSet(TPU8);	PIOClear(TPU9);		// AUX RS-232s: Tx Off, Rx On
	PIOClear(TPU7);
	PIOClear(TPU6);				// Turn off NAV power

//	if (analogPwrOn)
//	    PIOSet(28);				// A/D shutdown
//	else
//	    PIOClear(28);			// A/D shutdown

	PITSet51msPeriod(PITOff);		// disable timer (drops power)

	SCITxWaitCompletion();			// let any pending UART data finish
	EIAForceOff(true);			// turn off the RS232 driver
	CFEnable(false);			// turn off the CompactFlash card
	PIOSet(TPU8);	PIOClear(TPU9);
}


/************************************************************************/
/* Function    : lpPowerUp						*/
/* Purpose     : Restore CF2 after low-power sleep			*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
void lpPowerUp(void)
{
    EIAForceOff(false);				// turn on the RS232 driver
    CFEnable(true);				// turn on the CompactFlash card
//    ciflush();					// discard any garbage characters
//    PIOSet(28);					/* Turn on A/D reference voltage*/
}


/************************************************************************/
/* Function    : doLPStop						*/
/* Purpose     : Execute LPStop for one period, until interrupt wakes us*/
/* Inputs      : None							*/
/* Outputs     : Enum to tell what woke us				*/
/************************************************************************/
WhatWokeUs doLPStop(void)
{
    wakeupStatus = WakeNone;

    if (wakeOnIRQ7)
    {
	if (PinRead(IRQ7)==0)
	    return(WakeIRQ7);
	PinBus(IRQ7);
    }

    if (wakeOnIRQ5)
    {
	if (PinRead(IRQ5)==0)
	    return(WakeIRQ5);
	PinBus(IRQ5);
    }	

    if (wakeOnConsoleChar)
    {
	if (PinRead(IRQ4RXD)==0)
	    return(WakeConsoleChar);
	PinBus(IRQ4RXD);
    }

    if (wakeOnIRQ2)
    {
	if (PinRead(IRQ2)==0)
	    return(WakeIRQ2);
	PinBus(IRQ2);
    }

    LPStopCSE(fullStop ? FullStop : FastStop);

    PinIO(IRQ7);
    PinIO(IRQ5);
//    PinIO(IRQ4RXD);
    PinIO(IRQ2);

    if (wakeupStatus == WakeNone)
	return(WakeTmout);
    else if (wakeOnIRQ7 && (PinRead(IRQ7) == 0))
	return(WakeIRQ7);
    else if (wakeOnIRQ5 && (PinRead(IRQ5) == 0))
	return(WakeIRQ5);
    else if (wakeOnIRQ2 && (PinRead(IRQ2) == 0))
	return(WakeIRQ2);

    return(WakeConsoleChar);

} /* doLPStop() */


/************************************************************************/
/* Function    : doTimedStop						*/
/* Purpose     : Execute LPStop for number of seconds			*/
/* Inputs      : Number of seconds					*/
/* Outputs     : Enum to tell what woke us				*/
/************************************************************************/
WhatWokeUs doTimedStop(ushort secs)
{
    short	rtn;
    long	timeToStop;

    PITSet51msPeriod(SLEEP_51MS_TICKS);		/* Set PIT for 51 ms	*/

    for (timeToStop = 10000L*secs/512; timeToStop > SLEEP_51MS_TICKS;
	 timeToStop -= SLEEP_51MS_TICKS)
    {
	if ((rtn = doLPStop()) != WakeTmout)
	    return(rtn);
    }
    return(WakeTmout);

}


/*****************************************************************************\
**    Irq5ISR			Interrupt handler for IRQ4 & IRQ5
**	
**  This single interrrupt service routine handles the IRQ4 and IRQ5 interrupts
**  and the very likely spurious interrupts that my be generated by the
**  repeated asynchronous and non-acknowledged pulses of RS-232 input.
**	
**  The handler simply reverts IRQ5 back to input (to prevent further level
**  sensative interrupts) and returns. It's assumed the routine that set this
**  up is just looking for the side effect of breaking the CPU out of STOP
**  or LPSTOP mode.
**	
**  Note that this very simple handler is defined as a normal C function
**  rather than an IEV_C_PROTO/IEV_C_FUNCT. We can do this because we know
**  (and can verify by checking the disassembly) that is generates only
**  direct addressing instructions and will not modify any registers.
\*****************************************************************************/
void Irq5ISR(void)
{
    PinIO(IRQ4RXD);		// 31 // /IRQ4 (tied to Rx)
    PinIO(IRQ7);
    PinIO(IRQ5);
    PinIO(IRQ2);
    wakeupStatus = WakeIRQOrConsoleChar;
    RTE();
}
