/****************************************************************************/
/* Copyright 2000 MBARI                                                     */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  : C Serial communication support for Tailcone                   */
/* Filename : serial.c                                                      */
/* Author   : Andrew Pearce                                                 */
/* Project  : Dorado AUV                                                    */
/* Version  : 2.0                                                           */
/* Created  : 02/24/92                                                      */
/* Modified : 03/03/00                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header: /data/altex/Master/auv/altex/onboard/mbariTailcone/micro/serial.c,v 1.1 2000/06/23 18:23:57 rob Exp $
 * $Log: serial.c,v $
 * Revision 1.1  2000/06/23 18:23:57  rob
 * Initial check-in of the tailcone source.  I'm sending this snapshot of
 * the tailcone directory to Don Green on 6/23.
 *
 * Revision 1.1.1.1  1997/05/02 17:16:00  pean
 * Initial release of the microcontroller software after Tiburon
 * Moolpool Dive to test IView, Lapboxes, modified Power can
 * GF/5V using bus capacitance mode.
 *
 * Revision 1.1  92/05/14  09:14:32  09:14:32  pean (Andrew Pearce 408-647-3746)
 * Initial revision
 *
*/
/****************************************************************************/
                                        /* Generate code for the 80C196KB   */
#pragma model(196)
#pragma nosignedchar                    /* Use unsigned char (Char)         */

#include "C:/C96/include/80C196.h"      /* 80196 Register mapping           */
#include "C:/C96/include/stddef.h"      /* C Standard Definitions           */
#include "c:/c96/include/string.h"      /* used for at least the memcpy command */
#include "const.h"                      /* Misc. constants - TRUE/FALSE etc */
#include "types.h"                      /* MBARI style guide declarations   */
#include "ring.h"                       /* Ring buffer support routines     */
#include "timer.h"                      /* Timer library definitions        */
#include "microasm.h"                   /* Assembly language functions      */
#include "microdef.h"                   /* Microcontroller definis      */
#include "microlib.h"                   /* Microcontroller Board decls      */

#include "malloc.h"              /*  mbari micro-lib version of malloc   */  

#include "/hardware/sio32/syslib.h"     /* system library hardware funcs    */
#include "serial.h"                     /* data link layer functions        */
#include "proto.h"                      /* protocol layer functions         */

#define CARRIAGE_RET    0x0D            /* Carriage Return Character        */
#define START_CHAR      0x24            /* Data Link Layer data structures  */
#define LINK_TIMEOUT 1000                /* 10 Seconds                     */

MLocal struct
{
    Nat16   rxTimeout;                  /* Frame Rx Timeout Period          */
    timerId rxTimer;                    /* Frame received timer             */
    Void (*disconnectHook) ();          /* Func. to call if link disconnects*/
} linkState;

MLocal Boolean receiverBusy;            /* Receiver activity detected       */
MLocal Ring serial_rx_ring;             /* Serial IO receive data structure */
MLocal Byte serial_rx_buf[SERIAL_BUF_SIZE];
MLocal Void  linkDisconnect( Void );

/****************************************************************************/
/* Function    : initLinkTimer                                              */
/* Purpose     : Initializes link timer data structure                      */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
initLinkTimer( Void )
{
    linkState.rxTimeout = LINK_TIMEOUT;
    linkState.rxTimer = (timerId) NULL; /* Initalize frame received timer   */
    linkState.disconnectHook = NULL;    /* Clear disconnect function pointer*/
} /* initLinkTimer() */

/****************************************************************************/
/* Function    : dataLinkDisconnectHook                                     */
/* Purpose     : set applic layer function to call when link disconnects    */
/* Inputs      : pointer to function to be called                           */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
dataLinkDisconnectHook( Void (*disconnectFunc) () )
{
                                        /* Save function pointer to call if */
                                        /* the link disconnects             */
    linkState.disconnectHook = disconnectFunc;

    if (disconnectFunc == NULL)
        return;
                                        /* Create link status timer         */
    if (linkState.rxTimer == (timerId) (NULL))
        linkState.rxTimer = timerCreate();

    linkState.rxTimeout = LINK_TIMEOUT;
} /* dataLinkDisconnectHook() */

/****************************************************************************/
/* Function    : setLinkRxTimeout                                           */
/* Purpose     : Sets link receive timeout period in 10 msec ticks          */
/* Inputs      : new Receive Timeout Value                                  */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
setLinkRxTimeout( Nat16 rxTimeout )
{
    linkState.rxTimeout = rxTimeout;
} /* setLinkRxTimeout() */

/****************************************************************************/
/* Function    : getLinkRxTimeout                                           */
/* Purpose     : Gets link receive timeout period in 10 msec ticks          */
/* Inputs      : None                                                       */
/* Outputs     : Link Receive Timeout                                       */
/****************************************************************************/
    Nat16
getLinkRxTimeout( Void )
{
    return (linkState.rxTimeout);
} /*getLinkRxTimeout() */

/****************************************************************************/
/* Function    : linkDisconnect                                             */
/* Purpose     : Disconnect serial communications link                      */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
     MLocal Void
linkDisconnect( Void )
{
    linkState.disconnectHook();        /* Call disconnect function         */
} /* linkDisconnect() */

/****************************************************************************/
/* Function    : waitForRxIdle                                              */
/* Purpose     : Carrier sense detector - waits for receiver to go idle     */
/* Inputs      : None                                                       */
/* Outputs     : None, but global receiverBusy flag is modified             */
/****************************************************************************/
    MLocal Void
waitForRxIdle( Void )
{
    Nat16 ticks;

    do
    {                                     /* Wait RX_IDLE_TIME             */
        receiverBusy = FALSE;             /* Clear receiver busy flag      */
                                          /* Short Delay                   */
        for (ticks = 0; ticks < RX_IDLE_TIME; ticks++);
    } while (receiverBusy != FALSE);
} /* waitForRxIdle() */


Void restartLinkTimer()
{

  if (linkState.rxTimer)     /* Re-start link timer, if enabled  */
  {
#if 0
    timerRestart(linkState.rxTimer, linkState.rxTimeout);
#endif    

    timerCancel(linkState.rxTimer);
    timerStart(linkState.rxTimer, linkState.rxTimeout, linkDisconnect, 0);

} /* if */

} /* restartLinkTimer() */

/****************************************************************************/
/* Function    : initSerialPort                                             */
/* Purpose     : Initializes serial port hardware                           */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
initSerialPort( Void )
{
    int1Disable(TI_INT_MASK);           /* Disable Txmit Interrupt          */
    int1Disable(RI_INT_MASK);           /* Disable Receive Interrupt        */

                                        /* Initialize receive ring buffer   */
    ring_init(&serial_rx_ring, serial_rx_buf, SERIAL_BUF_SIZE);

    transmitControl(OFF);               /* Disable RS485 Transmitter        */

    ioc1 = IOC1_SELECT_TXD;             /* Set P2.0 to Transmit Data mode   */

    setBaudRate((Nat16) BAUD_RATE);     /* Set Baud Rate                    */
                                        /* Mode 1, No Parity, Received On   */
    sp_con = SP_CON_MODE1 | SP_CON_RX_ENABLE;

    int1Enable(RI_INT_MASK);            /* Enable Receive Interrupt         */
    waitForRxIdle();                    /* Wait for RX line to go idle      */

                                        /* Flush receive ring buffer        */
    ring_init(&serial_rx_ring, serial_rx_buf, SERIAL_BUF_SIZE);

#ifdef RS232_MODE
    int1Enable(TI_INT_MASK);            /* Enable Transmit Interrupt        */
#endif
} /* initSerialPort() */

/****************************************************************************/
/* Function    : txStartUp                                                  */
/* Purpose     : Enables RS485 serial transmission                          */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
txStartUp( Void )
{
#ifndef RS232_MODE
    waitForRxIdle();                        /* Wait for receiver to go idle*/
    int1Disable(RI_INT_MASK);               /* Disable Receiver Interrupt  */
    transmitControl(ON);                    /* enable 485 transmitter      */
#endif
} /* txStartUp */

/****************************************************************************/
/* Function    : txShutDown                                                 */
/* Purpose     : Disables RS485 serial transmission                         */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
txShutDown( Void )
{
    Byte temp;

#ifndef RS232_MODE
    transmitControl(OFF);               /* Disable 485 transmitter        */
    temp = sbuf;                        /* Discard data in serial rx buf  */
    ipend1 &= ~RI_INT_MASK;             /* Clear any pending RI interrupt */
    int1Enable(RI_INT_MASK);            /* Enable Receiver Interrupt      */
#endif
} /* txShutDown */

/****************************************************************************/
/* Function    : transmitBuffer                                             */
/* Purpose     : Transmit a buffer to serial port                           */
/* Inputs      : buffer to send                                             */
/* Outputs     : Returns OK if frame sent, otherwise ERROR                  */
/****************************************************************************/
    Int16
transmitBuffer(char *buffer, Int16 nBytes)
{  
  /* This approach is very sub optimal as the processor is forced to */
  /* block inside this function until all bytes are sent. At 250 usec*/
  /* per byte, a typical packet takes 5 msec to send                 */
  /* A better approach is to use a transmit ring buffer and hook in  */
  /* a transmit_isr to config.s. This will allow bytes to be loaded  */
  /* into a buffer and sent using an interrupt from the UART         */

    if (nBytes == 0)
      return (OK);

    txStartUp();                        /* Enable RS485 transmitter         */

    while (nBytes--)
    {                                   /* Wait for transmitter to empty    */
      while ((sp_stat & TXE_BIT) == 0);
      sbuf = *(buffer++);               /* send a byte to serial port       */
    }

    while ((sp_stat & TXE_BIT) == 0);   /* Wait for last bit to be sent     */
    txShutDown();                       /* Disable RS485 transmitter        */
    
    return(OK);
} /* transmitBuffer() */

/****************************************************************************/
/* Function    : receiveByte  - Receiver Interrupt Service Routine          */
/* Purpose     : Reads byte from serial port and saves it in ring buffer    */
/* Inputs      : None                                                       */
/* Outputs     : None, but ring buffer is updated                           */
/****************************************************************************/
    Void
receiveByte( Void )
{
    Byte    rxByte;                     /* received byte                    */
    rxByte = sbuf; 
                                        /* Read received serial port byte   */
    receiverBusy = TRUE;                /* Set flag for waitForRxIdle       */
/*0x54*/
    if ((sp_stat & 0x40) == 0x40)       /* Check for Byte Received, Overrun */
    {                                   /* and framing error. No Errors so  */                                         /* process received byte            */
        ring_put(&serial_rx_ring, rxByte);
     
    } /* if */
    
    restartLinkTimer(); 

} /* receiveByte */

/****************************************************************************/
/* Function    : readMVCPacket                                              */
/* Purpose     : Extract packet from protocol layer                         */
/* Inputs      : Pointer to malloced packet buffer,  timeout in tics        */
/* Outputs     : Returns pointer packet buffer                              */
/*               Return status is ERROR or OK if packet placed in buffer    */
/****************************************************************************/
Int16
readMVCPacket(Byte *packet, Nat16 timeoutticks )
{
  Int16  rxByte = 0;         /* received byte     */
  Nat16  i = 0;
  Nat16  ticks = 0;
  Nat16  starttic = read_sysclock();
  static Byte tempbuf[22];
 
  do{  
    
    if((rxByte = ring_get(&serial_rx_ring)) != ERROR)
        tempbuf[i++] = (Byte)rxByte;

    if( (read_sysclock() - starttic) >= timeoutticks)
	return (ERROR);

  }while((rxByte != CARRIAGE_RET)&&(i<22));
 
  
  if(i!=22)
    return(ERROR); /* you've got some new problem if you end up here */ 
  
  
  for(i=0;i<20;i++)
    *(packet++) = tempbuf[i+1];
  
  return(OK);
  
} /* readMVCPacket() */
  
/****************************************************************************/
/* Function    : calcChecksum                                               */
/* Purpose     : Calculates a checksum. Value is 8 bit sum of all bytes     */
/* Inputs      : buffer with bytes and length of buffer                     */
/* Outputs     : Returns checksum                                           */
/****************************************************************************/
    MLocal Byte
calcChecksum(
    Reg Byte    *bytes,                 /* Pointer to the data bytes        */
    Reg Int16   len)                    /* number of bytes to checksum      */
{
    Reg Nat16   i;                      /* loop counter                     */
    Reg Int16   checksum;               /* calculated checksum              */

    checksum = 0;
    for (i = 0; i < len; i++)           /* checksum is the sum of all bytes */
        checksum += *bytes++;

    return((Byte) (checksum & 0xff));   /* truncate to 8 bit value          */
} /* calcChecksum */

/****************************************************************************/
/* Function    : initSerialProtocol                                         */
/* Purpose     : Application layer function to initialize serial protocol   */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
initSerialProtocol( Void (*disconnectFunc) () )
{   
    initLinkTimer();

    initSerialPort();                   /* Initialize micro Serial Port     */
                                        /* Add disconnect function hook     */
    dataLinkDisconnectHook(disconnectFunc);
} /* initSerialProtocol() */







