/*****************************************************************************/
/* Copyright 1990 to 1994, MBARI                                             */
/*****************************************************************************/
/* Summary  : MBARI Micro controller Board Driver for VxWorks                */
/* Filename : microCmd.c                                                     */
/* Author   : Andrew Pearce                                                  */
/* Project  : Tiburon                                                        */
/* Version  : Version 1.0                                                    */
/* Created  : 07/16/90                                                       */
/* Modified : 04/29/98                                                       */
/* Archived :                                                                */
/*****************************************************************************/
/* Modification History:                                                     */
/* $Header: microCmd.c,v 1.1 93/07/01 14:37:50 pean Exp $
 * $Log:        microCmd.c,v $
 * Revision 1.1  93/07/01  14:37:50  14:37:50  pean (Andrew Pearce)
 * Initial revision
 *
 * Revision 1.1  92/06/29  13:46:14  13:46:14  pean (Andrew Pearce 408-647-3746)
 * Initial revision
 *
 */
/*****************************************************************************/

#include <vxWorks.h>                /* vxWorks system declarations           */
#include <stdarg.h>                 /* vxWorks varargs declarations          */
#include <iosLib.h>                 /* vxWorks IO system declarations        */
#include <semLib.h>                 /* vxWorks Semaphore declarations        */
#include <wdLib.h>                  /* vxWorks watch dog timer support decls */
#include <msgQLib.h>                /* vxWorks Message Queue Library         */

#include "mbariTypes.h"             /* MBARI style guide type declarations   */
#include "mbariConst.h"             /* Miscellaneous constants               */

#include "sio32Drv.h"               /* SIO32 hardware and driver information */
#include "sio32Server.h"            /* Sio32 Client/Server Communication     */
#include "sio32Client.h"            /* Sio32 Client/Server Communication     */
#include "microCmd.h"               /* Microcontroller communications        */

#include "ibc_card.h"               /* IBC Card Definitions                  */
#include "ibc_cmd.h"                /* IBC CPU Applications                  */

#include "applic.h"                 /* Microcontroller applications          */
#include "microcmd.h"               /* Microcontroller commands              */

#define NUM_CPU_ADC_CHANS       8

/*****************************************************************************/
/* Function    : sendMicroCommand                                            */
/* Purpose     : Send command with arguments to serial port                  */
/* Inputs      : Sio32 channel, reply buffer, No of arguments, list of args  */
/* Outputs     : Returns result of readServerPacket                          */
/*****************************************************************************/
    STATUS
sendMicroCommand(FAST sio32Chan *sio32Channel, Void *reply, Int32 replyLen,
         Int32 nWords, ... )
{
    FAST Int32   arg;               /* Argument counter                      */
    FAST Int32   argVal;            /* Argument covnerted to an integer      */
    va_list argPtr;                 /* Argument list pointer                 */
    Byte buffer[MAX_PACKET_LEN];    /* Buffer containing date to send        */
    FAST Byte  *bufPtr = buffer;    /* Pointer to buffer input position      */
    STATUS  status;                 /* Return status of read call            */

    va_start(argPtr, nWords);       /* argPtr points to 1st unnamed argument */

    for (arg = 0; arg < nWords; arg++)
    {
        argVal = va_arg(argPtr, Int32);     /* Get next word argument        */

        *bufPtr++ = (Byte) (argVal >> 8);   /* store high byte               */
        *bufPtr++ = (Byte) (argVal & 0xff); /* store low byte                */
    } /* for */

    va_end(argPtr);                 /* Cleanup var args stuff                */

    if (semTake(sio32Channel->mutexSem, sio32Channel->timeout) == ERROR)
        return(ERROR);

    sio32ChanRxFlush(sio32Channel); /* Discard any old packets               */

                                    /**** Send Data Packet to Serial Port ****/
    writeServerPacket(sio32Channel, (Byte*) buffer, nWords * sizeof(Word));

    if (reply != NULL)              /* If reply buffer is NULL then exit     */
                                    /* Read received packet from serial port */
        status = readServerPacket(sio32Channel, (Byte *) reply, replyLen,
             sio32Channel->timeout);

    else
        status = OK;                /* No reply expected so return OK        */

    semGive(sio32Channel->mutexSem);/* Release access to serial channel      */

    return (status);                /* Return status from read call          */
} /* sendMicroCommand() */

/*****************************************************************************/
/* Function    : microCommand                                                */
/* Purpose     : Send a command without arguments to microcontroller         */
/* Inputs      : Sio32 channel, command word, argument word, reply word ptr  */
/* Outputs     : Return OK or ERROR if timeout or read error                 */
/*****************************************************************************/
    STATUS
microCommand(FAST sio32Chan *sio32Channel, Word command, Word *value)
{
    Int16   status;                 /* Command Status                        */
    Word    reply[2];               /* Command Result from microcontroller   */

                                    /* Send command to microcontroller       */
    if ( sendMicroCommand( sio32Channel, &reply, sizeof(Word) * 2, 1, command )
        == ERROR ) return(ERROR);

                                    /* Check if command result is CMD OK     */
    if (wordFromBuf(reply, 0) != CMD_OK)
        return(ERROR);              /* Command failed so return ERROR        */

    *value = wordFromBuf(reply, sizeof(Word));
    return(OK);
} /* microCommand() */

/*****************************************************************************/
/* Function    : wordToBuf                                                   */
/* Purpose     : Insert a word into a buffer using Big Endian format         */
/* Inputs      : buffer and word                                             */
/* Outputs     : None                                                        */
/*****************************************************************************/
    Void
wordToBuf(FAST Char *buffer, FAST Word data)
{
    *buffer++ = (Char) (data >> 8);     /* store high byte                   */
    *buffer = (Char) (data & 0xff);     /* store low byte                    */
} /* wordToBuf() */

/*****************************************************************************/
/* Function    : wordsToBuf                                                  */
/* Purpose     : Insert several words into a buffer using Big Endian format  */
/* Inputs      : Buffer, number of arguments, list of words                  */
/* Outputs     : Word                                                        */
/*****************************************************************************/
    Void
wordsToBuf(FAST Char *buffer, Int32 nWords, ... )
{
    FAST Int32   arg;               /* Argument counter                      */
    FAST Int32   argVal;            /* Argument converted to an integer      */
    FAST va_list argPtr;            /* Argument list pointer                 */

    va_start(argPtr, nWords);       /* argPtr points to 1st unnamed argument */

    for (arg = 0; arg < nWords; arg++)
    {
        argVal = va_arg(argPtr, Int32);     /* Get next word argument        */

        *buffer++ = (Char) (argVal >> 8);   /* store high byte               */
        *buffer++ = (Char) (argVal & 0xff); /* store low byte                */
    } /* for */

    va_end(argPtr);                 /* Cleanup var args stuff                */
} /* wordsToBuf() */

/*****************************************************************************/
/* Function    : wordFromBuf                                                 */
/* Purpose     : Remove a word into a buffer using Big Endian format         */
/* Inputs      : buffer offset in bytes                                      */
/* Outputs     : Word                                                        */
/*****************************************************************************/
    Word
wordFromBuf(FAST Void *buffer, FAST Int16 index)
{
                                    /* Get high and low bytes and build word*/
    return (( (* (((Byte*) buffer) + index)) << 8) |
              (* (((Byte*) buffer) + index + 1)) );
} /* wordFromBuf() */

/*****************************************************************************/
/* Function    : wordsFromBuf                                                */
/* Purpose     : Remove several words from a buffer using Big Endian format  */
/* Inputs      : Buffer, number of arguments, list of pointers to words      */
/* Outputs     : Word                                                        */
/*****************************************************************************/
    Void
wordsFromBuf(FAST Void *buffer, Int32 nWords, ... )
{
    FAST Int32   arg;               /* Argument counter                      */
    FAST Word    *ptr;              /* Copy of buffer pointer                */
    FAST Word    val;
    va_list   argPtr;               /* Argument list pointer                 */
    Byte      *bptr;

    bptr = (Byte *) buffer;
    va_start(argPtr, nWords);       /* argPtr points to 1st unnamed argument */

    for (arg = 0; arg < nWords; arg++)
    {
        ptr = va_arg(argPtr, Word *);     /* Get next word argument          */
                                    /* Get high and low bytes and build word*/
        val  =  *bptr << 8, bptr++;
        val +=  *bptr,      bptr++;
        *ptr = val;
    } /* for */

    va_end(argPtr);                 /* Cleanup var args stuff                */
} /* wordsFromBuf() */

/*****************************************************************************/
/* Function    : microCmdStringReply                                         */
/* Purpose     : Send command with no arguments to micro & get string reply  */
/* Inputs      : Sio32 socket, command, reply buffer and buffer length       */
/* Outputs     : Return number of bytes read or ERROR.                       */
/*****************************************************************************/
    STATUS
microCmdStringReply(FAST sio32Chan *sio32Channel, Word command,
                 Byte *reply, Int16 replyLen)
{
    STATUS len;                         /* Length of reply string            */

                                        /* Send the command and get back the */
                                        /* reply string                      */
    if ((len = sendMicroCommand( sio32Channel, reply, replyLen, 1, command))
        == ERROR) return(ERROR);

    len = min(len, replyLen - 1);       /* Make sure we don't overrun buffer */
    reply[len] = '\0';                  /* Add NULL terminator               */

    return(len);                        /* Return length of string           */
} /* microCmdStringReply() */

/*****************************************************************************/
/* Function    : microWriteBufReply                                          */
/* Purpose     : Sends a buffer to microcontroller, wait for & decode reply  */
/* Inputs      : Serial channel, Command Word, Buffer length & buffer        */
/* Outputs     : Returns OK or ERROR                                         */
/*****************************************************************************/
    STATUS
microWriteBufReply(sio32Chan *sio32DataChan, Word len, Byte *buffer)
{
    Word    cmdStatus;                  /* Result of microcontroller command */
    STATUS  status;                     /* Return status of read call        */

    if (len == 0)
        return (ERROR);

                                        /* Lock access to serial channel     */
    if (semTake(sio32DataChan->mutexSem, sio32DataChan->timeout)
         == ERROR)
        return(ERROR);
                                        /* Send the command to the micro     */
    writeServerPacket(sio32DataChan, buffer, (Word) min(len, MAX_PACKET_LEN));

                                        /* Read back the reply               */
    status = readServerPacket(sio32DataChan, (Byte *) &cmdStatus,
           sizeof(Word), sio32DataChan->timeout);

    semGive(sio32DataChan->mutexSem);   /* Release access to serial channel  */

    if (status == ERROR)
        return (ERROR);
                                        /* do byte ordering & extract value  */
    return (wordFromBuf(&cmdStatus, 0) == CMD_OK ? OK : ERROR);
} /* microWriteBufReply() */

/*****************************************************************************/
/* Function    : microResetCmd                                               */
/* Purpose     : Send Reset Command to Microcontroller                       */
/* Inputs      : Sio32 socket                                                */
/* Outputs     : None                                                        */
/*****************************************************************************/
    STATUS
microResetCmd( sio32Chan *sio32Channel )
{
    Word   cmdStatus;               /* Command Result from microcontroller   */

    microCmdEnable(sio32Channel);   /* Send Command Enable Command to Micro  */

                                    /* Send Reset command to micro           */
    if (sendMicroCommand(sio32Channel, &cmdStatus, sizeof(cmdStatus),
         1, MICRO_APP | MICRO_RESET) == ERROR) return(ERROR);

    return (wordFromBuf(&cmdStatus, 0) == CMD_OK ? OK : ERROR);
} /* microResetCmd() */

/*****************************************************************************/
/* Function    : microCmdEnable                                              */
/* Purpose     : Send Command Enable Command to Microcontroller              */
/* Inputs      : Sio32 socket                                                */
/* Outputs     : None                                                        */
/*****************************************************************************/
    STATUS
microCmdEnable( sio32Chan *sio32Channel )
{
    Word   cmdStatus;               /* Command Result from microcontroller   */

                                    /* Send command enable command to micro  */
    if (sendMicroCommand(sio32Channel, &cmdStatus, sizeof(cmdStatus),
         1, MICRO_APP | CMD_ENABLE) == ERROR) return(ERROR);

    return (wordFromBuf(&cmdStatus, 0) == CMD_OK ? OK : ERROR);
} /* microCmdEnable() */

/*****************************************************************************/
/* Function    : microGetIdent                                               */
/* Purpose     : returns identifcation string from microcontroller           */
/* Inputs      : Buffer for identification string                            */
/* Outputs     : Return OK or ERROR                                          */
/*****************************************************************************/
    STATUS
microGetIdent(sio32Chan *sio32Channel, Char *identString)
{
    if ( microCmdStringReply( sio32Channel, MICRO_APP | MICRO_IDENT,
         identString, MICRO_ID_LEN ) == ERROR ) return(ERROR);
    return(OK);
} /* microGetIdent() */

/*****************************************************************************/
/* Function    : microSerialNo                                               */
/* Purpose     : returns board serial number string from microcontroller     */
/* Inputs      : Buffer for serial number string                             */
/* Outputs     : Return OK or ERROR                                          */
/*****************************************************************************/
    STATUS
microSerialNo(sio32Chan *sio32Channel, Char *serialNo)
{
    if ( microCmdStringReply( sio32Channel, MICRO_APP | GET_MICRO_SER_NO,
            serialNo, MICRO_SERNO_LEN ) == ERROR ) return (ERROR);
    return(OK);
} /* microSerialNo() */

/*****************************************************************************/
/* Function    : microSetSerialNo                                            */
/* Purpose     : Sends Micro Serial Number String to microcontroller         */
/* Inputs      : None                                                        */
/* Outputs     : Returns OK or ERROR                                         */
/*****************************************************************************/
    STATUS
microSetSerialNo(sio32Chan *sio32DataChan, Char *serialNo )
{
    Byte    command[MICRO_SERNO_LEN + sizeof(Word)];

    wordToBuf(command, MICRO_APP | SET_MICRO_SER_NO); /* put cmd into buffer */

    bcopy(serialNo, command + sizeof(Word), min(strlen((char *) serialNo),
          MICRO_SERNO_LEN));

    return (microWriteBufReply(sio32DataChan, min(strlen((char *) serialNo),
          MICRO_SERNO_LEN) + sizeof(Word), command));
} /* microSetSerialNo() */

/*****************************************************************************/
/* Function    : microSoftwareRev                                            */
/* Purpose     : returns software revision string from microcontroller       */
/* Inputs      : Buffer for software revision string                         */
/* Outputs     : Return OK or ERROR                                          */
/*****************************************************************************/
    STATUS
microSoftwareRev(sio32Chan *sio32Channel, Char *softwareRev)
{
    if ( microCmdStringReply( sio32Channel, MICRO_APP | SOFTWARE_REV,
             softwareRev, SOFTWARE_REV_LEN ) == ERROR ) return (ERROR);
    return(OK);
} /* microSoftwareRev() */

/*****************************************************************************/
/* Function    : microGetRamTest                                             */
/* Purpose     : Read result of RAM test from microcontroller                */
/* Inputs      : Sio32 socket and pointer to ram test result                 */
/* Outputs     : Returns OK or ERROR                                         */
/*****************************************************************************/
    STATUS
microGetRamTest(sio32Chan *sio32Channel, MBool *ramTestStatus )
{
    Word boardStatus;

    if (sendMicroCommand(sio32Channel, &boardStatus, sizeof(boardStatus),
         1, MICRO_APP | GET_STATUS) == ERROR) return(ERROR);

    if (boardStatus & RAM_TEST_MASK)
        *ramTestStatus = TRUE;
    else
        *ramTestStatus = FALSE;

    return(OK);
} /* microGetRamTest() */

/*****************************************************************************/
/* Function    : microSetPacketRxTimeout                                     */
/* Purpose     :                                                             */
/* Inputs      : Sio32 channel,                                              */
/* Outputs     : Return OK or ERROR                                          */
/*****************************************************************************/
    STATUS
microSetPacketRxTimeout( FAST sio32Chan *sio32DataChan, Nat16 rxTimeout )
{
    Word    status;                 /* Command Result from microcontroller   */

                                    /* Send command to microcontroller       */
    if ( sendMicroCommand( sio32DataChan, &status, sizeof(status),
         2, MICRO_APP | SET_LINK_RX_TIME, rxTimeout )
         == ERROR ) return(ERROR);

                                    /* Check command result                  */
    return (wordFromBuf(&status, 0) == CMD_OK ? OK : ERROR);
} /* microSetPacketRxTimeout() */

/*****************************************************************************/
/* Function    : microGetPacketRxTimeout                                     */
/* Purpose     : Returns Packet Rx Timeout value from microcontroller        */
/* Inputs      : Sio32 channel, pointer to rx timeout value                  */
/* Outputs     : Returns OK or ERROR                                         */
/*****************************************************************************/
    STATUS
microGetPacketRxTimeout( sio32Chan *sio32DataChan, Nat16 *rxTimeout )
{
    return(microCommand(sio32DataChan, MICRO_APP | GET_LINK_RX_TIME,
                 rxTimeout));
} /* microGetPacketRxTimeout() */

/*****************************************************************************/
/* Function    : microPing                                                   */
/* Purpose     : Ping microcontroller board                                  */
/* Inputs      : Sio32 channel, Tx Sequence number, Rx Sequence number ptr   */
/* Outputs     : Returns OK or ERROR                                         */
/*****************************************************************************/
    STATUS
microPing( sio32Chan *sio32Channel, Word txSeqNo, Word *rxSeqNo )
{
    Word reply[2];

                                    /* Send command to microcontroller       */
    if (sendMicroCommand(sio32Channel, reply, sizeof(Word) * 2,
         2, MICRO_APP | MICRO_PING, txSeqNo) == ERROR) return(ERROR);

                                    /* Did command complete correctly        */
    if (wordFromBuf(reply, 0) != CMD_OK)
        return(ERROR);

                                    /* Yes, extract receive sequence no      */
    *rxSeqNo = wordFromBuf(reply, sizeof(Word));
    return (OK);                    /* Return OK                             */
} /* microPing() */


    STATUS
microSetGndFltTestState(sio32Chan *sio32Channel, MBool gfTestState)
{
    Word status;

    if (sendMicroCommand(sio32Channel, &status, sizeof(status),
         2, MICRO_APP | SET_GNDFLT_TEST, gfTestState) == ERROR)
        return(ERROR);
                                    /* Did command complete correctly        */
    return (wordFromBuf(&status, 0) == CMD_OK ? OK : ERROR);
} /* microSetGndFltTestState() */

    STATUS
microForwardPacket(sio32Chan *sio32Channel, Byte *packet, Word length)
{
    STATUS  status;
    Byte    buffer[MAX_PACKET_LEN];
    Word    boardAddr, channel;
    Word    cmdStatus;              /* Command Result from microcontroller   */

    if (length > (MAX_PACKET_LEN - sizeof(Word) * 3))
        return (ERROR);
                                    /* Extract Quad Serial Board Address and */
                                    /* Serial Channel Number                 */
    boardAddr = QUAD_SER_ADDR(sio32Channel->channel);
    channel   = QUAD_SER_CHAN(sio32Channel->channel);

                                    /* Insert QS Board Addr &  Channel Buffer*/
    wordsToBuf(buffer, 3, IBC_CPU_APP | QUAD_SER_FWD, boardAddr, channel);

    bcopy(packet, buffer + sizeof(Word) * 3, length);

    if (sendPacketToServer(sio32Channel, (Byte *) buffer,
             length + sizeof(Word) * 3) == ERROR)
        return (ERROR);
                                    /* Read back the reply                   */
    status = readServerPacket(sio32Channel, (Byte *) &cmdStatus,
           sizeof(Word), sio32Channel->timeout);

    if (status == ERROR)
        return (ERROR);
                                    /* Return result of Packet forwarding    */
    return (wordFromBuf(&cmdStatus, 0) == CMD_OK ? OK : ERROR);
} /* microForwardPacket() */

/*****************************************************************************/
/* Function    : microReadAtoDChangsCmd                                      */
/* Purpose     : Send Command to Read Microcontroller A to D's               */
/* Inputs      : Sio32 data struct                                           */
/* Outputs     : None                                                        */
/*****************************************************************************/
    STATUS
microReadAtoDChansCmd( sio32Chan *sio32Channel,
    Word atodValues[NUM_CPU_ADC_CHANS] )
{
    Word   cmdStatus;               /* Command Result from microcontroller   */
    Word   reply[NUM_CPU_ADC_CHANS];
    Int32  chan;

    if (sendMicroCommand(sio32Channel, reply, sizeof(reply),
         1, MICRO_APP | READ_ADC_CHANS) == ERROR) return(ERROR);

    for (chan = 0; chan < NUM_CPU_ADC_CHANS; chan++)
        atodValues[chan] = wordFromBuf(reply, chan + chan);

    return (OK);
} /* microReadAtoDChangsCmd() */


