/****************************************************************************/
/* Copyright 1991 MBARI                                                     */
/****************************************************************************/
/* $Header: /home/cvs/oasis3/src/test/RecipeCard/tick/drvr.c,v 1.1 2003/08/19 21:14:40 headley Exp $			    */
/* Summary  : Driver Management Routines for OASIS Mooring Controller	    */
/* Filename : drvr.c							    */
/* Author   : Robert Herlien (rah)					    */
/* Project  : OASIS Mooring						    */
/* $Revision: 1.1 $							    */
/* Created  : 02/14/91							    */
/*									    */
/* MBARI provides this documentation and code "as is", with no warranty,    */
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium  */
/* Research Institute to assist in its use, correction, modification, or    */
/* enhancement. This information should not be published or distributed to  */
/* third parties without specific written permission from MBARI.            */
/*									    */
/****************************************************************************/
/* Modification History:						    */
/* 14feb91 rah - created						    */
/* $Log: drvr.c,v $
/* Revision 1.1  2003/08/19 21:14:40  headley
/* no message
/*
 * Revision 4.4  2001/06/19  12:13:26  12:13:26  oasisa (Oasis users)
 * New Repository; 6/19/2001 (klh)
 * 
 * Revision 1.1  2001/06/19  11:43:21  11:43:21  oasisa (Oasis users)
 * Initial revision
 * 
 * Revision 4.3  99/06/16  10:21:33  10:21:33  bobh (Bob Herlien)
 * Mar/May '99 Deployments of M3/M2
 * 
 * Revision 4.2  98/09/09  10:48:01  10:48:01  bobh (Bob Herlien)
 * Sept/Oct '98 deployments of M1, Eqpac 1 & 2
 * 
 * Revision 3.8  97/09/12  10:50:50  10:50:50  bobh (Bob Herlien)
 * Redeploy M1
 * 
 * Revision 3.7  97/07/23  11:18:06  11:18:06  bobh (Bob Herlien)
 * July '97 M1 deployment, new shutter code
 * 
 * Revision 3.6  96/10/30  14:00:12  14:00:12  bobh (Bob Herlien)
 * Release for EqPac, M2 Test Replacement
 * 
 * Revision 3.5  96/07/17  14:38:43  14:38:43  bobh (Bob Herlien)
 * July '96 deployment of M2 with ARGOS code
 * 
 * Revision 3.4  96/06/18  15:24:19  15:24:19  bobh (Bob Herlien)
 * June '96 deployment of M1
 * 
 * Revision 3.3  95/04/13  13:46:53  13:46:53  hebo (Bob Herlien)
 * Drifter Deployment for Coop (flip) cruise
 * 
 * Revision 3.1  95/03/09  19:30:55  19:30:55  hebo (Bob Herlien)
 * March '95 Deployment of M1A
 * 
 * Revision 3.0  95/02/21  18:42:41  18:42:41  hebo (Bob Herlien)
 * February '95 Deployment
 * 
 * Revision 2.4  93/10/29  11:12:44  11:12:44  hebo (Bob Herlien)
 * November 1993 Deployment
 * 
 * Revision 2.0  92/08/21  14:45:03  14:45:03  hebo (Bob Herlien)
 * August 1992 deployment
 * 
 * Revision 1.3  92/03/05  14:22:29  14:22:29  hebo (Bob Herlien 408-647-3748)
 * New defaults, restart check, perm power stuff, analog command, gps changes
 * 
*/
/****************************************************************************/

#include <types.h>                      /* MBARI type definitions	    */
#include <const.h>                      /* MBARI constants		    */
#include <oasis.h>                      /* OASIS controller definitions	    */
#include <custom.h>			/* LOGGER definition		    */
#include <io.h>				/* OASIS I/O definitions	    */
#include <log.h>			/* Log record definitions	    */
#include <task.h>			/* OASIS Multitasking definitions   */
#include <80C196.h>			/* 80196 Register mapping           */
#include <string.h>			/* String library		    */
#include <limits.h>			/* Defines UINT_MAX		    */
#include <drvr.h>			/* Default driver headers	    */



/********************************/
/*	External Functions	*/
/********************************/

Extern Void	ser_init(Nat16 port, Nat16 baud); /* Serial port init	    */
Extern Void	io_relay(Word select0, Word select1); /* Set ser relay mux  */
Extern Void	io_pwron( Word pwrbits );	/* Turn power on	    */
Extern Void	io_pwroff( Word pwrbits );	/* Turn power off	    */
Extern Void	xdrain_ser( Nat16 tmout );	/* Drain serial output	    */
Extern char	*tmpMalloc( Nat16 size );	/* Alloc temp memory	    */
Extern Void	tmpFree( char *ptr );
Extern char	*permMalloc( Nat16 size );	/* Alloc permanent memory   */
Extern TaskDesc *task_create( Reg Driver *dp );	 /* Create driver task	    */
Extern Void	logPutRec( LogRecHdr *hdr, Byte *data );
						/* Log into logging memory  */
Extern Void	logError( Reg Word vect );	/* Log an error		    */
Extern char 	*cmp_ulc( char *s, char *cs );	/* Case insensitive strcmp  */
Extern Void	sensor_init( Void );		/* Normal init of sensor.c  */
Extern Void	bcopy( const Byte *src, Byte *dst, Nat16 len );
Extern Void	bzero( void *s, int n );	/* Zero memory		    */


/********************************/
/*	External Data		*/
/********************************/

Extern Reg TimeOfDay	tod;		/* Current time in TimeOfDay format */
Extern Reg Byte		localHour;	/* Hour of day (0 - 23) in local time*/
Extern Reg Nat16	port;		/* Default serial port		    */
Extern Nat16		pwr_perm;	/* Devices to leave powered on	    */
Extern TimeOfDay	radio_time;	/* Last time user connected on radio*/
					/*  (set in userif.c, used here)    */
Extern Nat16		sync_tmout;	/* Timeout for drvSync		    */


/********************************/
/*	Global Data		*/
/********************************/

Global LstHead		drv_list;		/* Driver list header	    */
Global Semaphore	ser_sem[NSER_PORTS];	/* Mutex sems for ser ports */
Global Semaphore	no3_sem;		/* Mutex sem for no3 driver */
Global Nat16		deploymentMode;		/* In deployment mode mode  */


/************************************************************************/
/* Function    : null_func						*/
/* Purpose     : Null function						*/
/* Inputs      : None							*/
/* Outputs     : TRUE							*/
/************************************************************************/
	MBool
null_func( Void )
{
    return( TRUE );

} /* null_func() */


/************************************************************************/
/* Function    : drv_pwron						*/
/* Purpose     : Turn on power for given driver				*/
/* Inputs      : Driver Pointer						*/
/* Outputs     : None							*/
/************************************************************************/
	Void
drv_pwron( Driver *dp )
{
    io_pwron( dp->drv_parms[PWR_CTRL] );

} /* drv_pwron() */


/************************************************************************/
/* Function    : drv_pwroff						*/
/* Purpose     : Turn off power for given driver			*/
/* Inputs      : Driver Pointer						*/
/* Outputs     : None							*/
/************************************************************************/
	Void
drv_pwroff( Driver *dp )
{
    Reg Word	pwrbits;

    pwrbits =  dp->drv_parms[PWR_CTRL];

    if ( (pwrbits & pwr_perm) == 0 )
	io_pwroff( pwrbits );

} /* drv_pwroff() */


/************************************************************************/
/* Function    : drv_ser_port						*/
/* Purpose     : Get access to serial port, init port			*/
/* Inputs      : Driver Pointer						*/
/* Outputs     : None							*/
/************************************************************************/
	Void
drv_ser_port( Driver *dp )
{
    if ( (port = dp->drv_parms[SER_PORT]) != NO_SERIAL )
    {
	sem_take( &ser_sem[port] );		/* Get access to ser port */
	drv_pwron( dp );			/* Turn on device	  */
	io_relay( dp->drv_parms[SER_RELAY0], dp->drv_parms[SER_RELAY1] );
	ser_init( port, dp->drv_parms[SER_SETUP] );
    }						 /* Init serial port	  */

} /* drv_ser_port() */


/************************************************************************/
/* Function    : drv_ser_release					*/
/* Purpose     : Release access to serial port				*/
/* Inputs      : Driver Pointer						*/
/* Outputs     : None							*/
/************************************************************************/
	Void
drv_ser_release( Driver *dp )
{
    if ( (port = dp->drv_parms[SER_PORT]) != NO_SERIAL )
    {
	xdrain_ser( 400 );			/* Wait for output to drain*/
	sem_give( &ser_sem[port] );		/* Give up semaphore	   */
    }

} /* drv_ser_release() */
			

/************************************************************************/
/* Function    : drvr_find						*/
/* Purpose     : Find a driver by name					*/
/* Inputs      : Pointer to name string					*/
/* Outputs     : Corresponding driver, or DRV_NULL if none		*/
/************************************************************************/
	Driver *
drvr_find( char *name )
{
    Reg Driver	*dp;

    for ( dp = (Driver *)(drv_list.lst_head); dp != DRV_NULL; 
          dp = dp->drv_next )
	if ( cmp_ulc(name, dp->drv_name) != NULL )
	    return( dp );

    return( DRV_NULL );

} /* drvr_find() */


/************************************************************************/
/* Function    : drvr_create						*/
/* Purpose     : Create a driver header					*/
/* Inputs      : Ptr to driver descriptor				*/
/* Outputs     : Driver pointer						*/
/************************************************************************/
	Driver *
drvr_create( const DrvDesc *ddp )
{
    Reg Driver	*dp;
    Reg Nat16	namelen;
    
    namelen = strlen( ddp->dd_name ) + 1;

    if ( (dp = (Driver *)permMalloc(sizeof(Driver) + namelen)) != DRV_NULL )
    {
	bzero( (Byte *)dp, sizeof(Driver) );
	bcopy( (Byte *)ddp, (Byte *)&(dp->drv_name), sizeof(DrvDesc) );
	dp->drv_name = (char *)(dp + 1);
	bcopy( (Byte *)ddp->dd_name, (Byte *)dp->drv_name, namelen );
	dp->drv_flags = DO_INIT;
    }
    return( dp );

} /* drvr_create() */


/************************************************************************/
/* Function    : drvr_init						*/
/* Purpose     : Initialize driver module				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
	Void
drvr_init( Void )
{
    Reg Driver	*dp;			/* Driver pointer		*/
    Reg Int16	i;			/* Scratch			*/
    LogRecHdr	rechdr;			/* Logging record header 	*/

    for ( i = 0; i < NSER_PORTS; i++ )
	sem_init( &ser_sem[i], 1 );	/* Init the serial semaphores	*/

    for (dp = (Driver *)drv_list.lst_head; dp != DRV_NULL; dp = dp->drv_next)
	dp->drv_td = NULLTID;		/* Init no driver tasks		*/

    sensor_init();			/* Init sensor module		*/
    sem_init( &no3_sem, 1 );		/* Init the no3 semaphore	*/

} /* drvr_init() */


/************************************************************************/
/* Function    : drvrColdInit						*/
/* Purpose     : Cold Initialize the driver module			*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/* Comments    : Called when system totally reset after power outage	*/
/************************************************************************/
	Void
drvrColdInit( Void )
{
    Reg Int16	i;
    Driver	*dp;

    list_init( &drv_list );		/* Init driver list		*/

    for ( i = 0; i < NUM_DRVRS; i++ )	/* Add default drivers		*/
	if ( (dp = drvr_create(&drv_default[i])) != DRV_NULL )
	{
	    list_add( &drv_list, (Node *)dp );	/* Add to driver list	*/
	    dp->drv_wakeup = tod;	/* Set to sample now		*/
	}

} /* drvrColdInit() */


/************************************************************************/
/* Function    : drvWakeupTime						*/
/* Purpose     : Determine time until driver needs to wake up		*/
/* Inputs      : Driver pointer						*/
/* Outputs     : Seconds until driver wakeup, or 0 if wakeup now	*/
/************************************************************************/
	Nat16
drvWakeupTime( Reg Driver *dp )
{
    Reg Nat16	nexthr;

    if ( tod < dp->drv_wakeup )			/* If not time to run,	*/
	return( (Nat16)(dp->drv_wakeup - tod) ); /* return time to wakeup*/

					/* Time to wakeup now. Check flags*/
    if ( dp->drv_flags & DO_SYNC )		/* If SYNC flag on, run	*/
	return( 0 );				/*   it now		*/

    if ( dp->drv_parms[INTERVAL] == 0 )		/* If driver turned off	*/
	return( NAT16_MAX );			/*   return Max Nat16	*/

    if ( (deploymentMode == 0) &&		/* Check TOD mask	*/
	(dp->drv_parms[TOD_MASK] & (1 << (localHour >> 1))) )
    {				/* If TOD mask on for this 2 hr period,	*/
				/*     go to next hour			*/
	nexthr = 3600 - (tod % 3600);
	dp->drv_wakeup = tod + nexthr;
	return( nexthr );
    }

    return( 0 );				/* If none above, run now*/

} /* drvWakeupTime() */


/************************************************************************/
/* Function    : drvr_sample						*/
/* Purpose     : (Re)start any driver not already running that should be*/
/* Inputs      : None							*/
/* Outputs     : Seconds until next driver wakeup			*/
/* Comments    : Will restart any driver not already running that has	*/
/*		 exipired its wakeup time.				*/
/*		 Returns 0 if any tasks still running, to keep main alive*/
/************************************************************************/
	Nat16
drvr_sample( Void )
{
    Reg Driver	*dp;			/* Cur drvr ptr			  */
    Reg Nat16	wtime, t;		/* Earliest wake time		  */

#ifdef SYNC_AT_MIDNIGHT
    wtime = 43200 - (tod % 43200);
#else
    wtime = NAT16_MAX;
#endif
    for ( dp = (Driver *)drv_list.lst_head; dp != DRV_NULL; dp = dp->drv_next )
    {
	if ( dp->drv_td != NULLTID )		/* If anybody running, 	   */
	    wtime = 0;				/*   next wake is now	   */

	else
	{
	    if ( (t = drvWakeupTime(dp)) == 0 )	/* If time to wake up	   */
	    {					/*  start the driver	   */
		dp->drv_td = (Tid)task_create(dp);
		dp->drv_wakeup = tod + dp->drv_parms[INTERVAL];
	    }

	    if ( t < wtime )			/* Save min wake time	   */
		wtime = t;
	}
    }

    return( wtime );

} /* drvr_sample() */


/************************************************************************/
/* Function    : drvLogError						*/
/* Purpose     : Log an error for a driver				*/
/* Inputs      : Driver ptr						*/
/* Outputs     : None							*/
/************************************************************************/
	Void
drvLogError( Driver *dp, Errno err )
{
    LogRecHdr	rechdr;		/* Logging record header 		*/
    Nat16	recbuff[2];

    rechdr.log_type = (Byte)LOG_ERROR;
    rechdr.log_len = sizeof( recbuff );
    recbuff[0] = dp->drv_parms[SAMPLE_TYPE];
    recbuff[1] = err;
    logPutRec( &rechdr, (Byte *)recbuff );

} /* drvLogError() */


/************************************************************************/
/* Function    : drvLog							*/
/* Purpose     : Default sample logging routine				*/
/* Inputs      : Driver ptr, sample ptr, number bytes			*/
/* Outputs     : None							*/
/************************************************************************/
	Void
drvLog( Driver *dp, Byte *samplep, Int16 len )
{
    LogRecHdr	rh;

    rh.log_type = dp->drv_parms[SAMPLE_TYPE];

    if ( (rh.log_len = len) > 0 )
	logPutRec( &rh, (Byte *)samplep );
    else
	drvLogError( dp, NO_DATA );


} /* drvLog() */


/************************************************************************/
/* Function    : drvLogWords						*/
/* Purpose     : Default sample logging routine				*/
/* Inputs      : Driver ptr, sample ptr, number words			*/
/* Outputs     : None							*/
/************************************************************************/
	Void
drvLogWords( Driver *dp, Nat16 *samplep, Int16 wrds )
{
    drvLog( dp, (Byte *)samplep, wrds * sizeof(Nat16) );

} /* drvLogWords() */


/************************************************************************/
/* Function    : drvSerPortAndMalloc					*/
/* Purpose     : Get serial port and malloc buffer			*/
/* Inputs      : Driver Pointer, buffer size				*/
/* Outputs     : Buffer ptr, or NULL if malloc fails			*/
/************************************************************************/
	char *
drvSerPortAndMalloc( Driver *dp, Nat16 size )
{
    char	*mallocPtr;

    drv_ser_port( dp );

    if ( (mallocPtr = tmpMalloc(size)) == NULL )
    {
	drv_ser_release( dp );
	drvLogError( dp, MALLOC_ERR );
    }

    return( mallocPtr );

} /* drvSerPortAndMalloc() */


/************************************************************************/
/* Function    : drvSerReleaseAndFree					*/
/* Purpose     : Release serial port and malloc buffer			*/
/* Inputs      : Driver Pointer, buffer ptr				*/
/* Outputs     : None							*/
/************************************************************************/
	Void
drvSerReleaseAndFree( Driver *dp, char *buffer )
{
    drv_ser_release( dp );
    tmpFree( buffer );
    
} /* drvSerReleaseAndFree() */


/************************************************************************/
/* Function    : drvSync						*/
/* Purpose     : Sync to an External driver				*/
/* Inputs      : Driver name						*/
/* Outputs     : None							*/
/************************************************************************/
	Void
drvSync( char *name )
{
    Driver	*dp;
    Nat16	cnt;

    if ( (dp = drvr_find(name)) == DRV_NULL )
	return;

    dp->drv_wakeup = tod;
    dp->drv_flags |= DO_SYNC;
    
    for ( cnt = sync_tmout;
	  (cnt > 0) && (dp->drv_flags & DO_SYNC); cnt-- )
	task_delay( TICKS_PER_SECOND );

} /* drvSync() */


/************************************************************************/
/* Function    : drvr_timechange					*/
/* Purpose     : Notify driver module that new time has been set	*/
/* Inputs      : Amount that time changed				*/
/* Outputs     : None							*/
/************************************************************************/
	Void
drvr_timechange( Int32 diff )
{
    Reg Driver	*dp;			/* Driver pointer		*/

    for (dp = (Driver *)drv_list.lst_head; dp != DRV_NULL; dp = dp->drv_next)
	dp->drv_wakeup += diff;

    radio_time += diff;

} /* drvr_timechange() */
