/*
 *  Copyright (c) 1996-2007 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 * Description.
 * =======================================================================
 * applicationStart() is the entry point for user applications.  This
 * This function will be called after the kernel has started and the
 * TCP/IP stack has loaded.  applicationTcpDown() is called periodically
 * after the kernel has started while the system is waiting for the
 * TCP/IP stack to start.  
 *                                                                        
 *                                                                        
 * Edit Date/Ver   Edit Description
 * ==============  ===================================================
 *
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tx_api.h"
#include "bsp_api.h"
#include "a2d.h"
#include "ns9215_a2d.h"
#include "nainvalidate.h"


    

#define BUFFER_LENGTH           (20*NA_A2D_MAX_INPUTS*sizeof(WORD32))
#define BUFFER_COUNT            (5)
#define BUFFER_NEXT_INDEX(i)    ((i + 1) % BUFFER_COUNT)

#define NUMBER_OF_ROWS          (10)

typedef struct
{
    WORD32 buffer[BUFFER_LENGTH / sizeof(WORD32)];
    unsigned length;
    unsigned reserved[3];
} bufferType;

static bufferType *autoBuffer[BUFFER_COUNT];
static unsigned volatile availBuffers = 0;


#define NA_IPSEC_FACILITY_CODE                      (0xB)

const char *errorLookupTable[] =
{
    "NA_SUCCESS",
    "NA_A2D_BAD_ARGUMENT",
    "NA_A2D_BAD_CLOCK_RATE",
    "NA_A2D_BAD_HANDLE",
    "NA_A2D_BAD_INPUT",
    "NA_A2D_BAD_LENGTH",
    "NA_A2D_BUFFER_Q_FULL",
    "NA_A2D_CHANNEL_IN_USE",
    "NA_A2D_DUPLICATE_CALLBACK",
    "NA_A2D_DUPLICATE_ID",
    "NA_A2D_INVALID_STATE",
    "NA_A2D_ISR_ALREADY_INSTALLED",
    "NA_A2D_ISR_NOT_INSTALLED",
    "NA_A2D_NO_BUFFERS",
    "NA_A2D_NO_SUCH_CALLBACK",
    "NA_A2D_NOT_AVAILABLE",
    "NA_A2D_NOT_CONFIGURED",
    "NA_A2D_NOT_RUNNING",
    "NA_A2D_NOT_SUPPORTED",
    "NA_A2D_OUT_OF_RESOURCES",
    "NA_A2D_UNKNOWN_IDENTIFIER"
};

const unsigned MAX_ERROR_CODE = asizeof(errorLookupTable);



/*
 * I use this macro to examine the return code from ipsec function calls.  If 
 * a function returns in error the macro will report the error and exit.  
 *
 * There is no reason why you have to use a macro like this.  I am doing it
 * only because I don't want to write code everywhere I make a function call
 * to check the return value.
 */
#define HANDLE_ERROR(fn, fnName)                                    \
        {                                                           \
            NaStatus ccode = fn;                                    \
            if (ccode != NA_SUCCESS)                                \
            {                                                       \
                reportError(fnName, ccode, __FILE__, __LINE__);     \
            }                                                       \
        }                                                           \

/*
 * Utility function to report an error and exit.  If the error is one of the 
 * codes in our lookup table above, then we will translate it into a string
 * to make it more readable.
 */
void reportError(char *functionName, NaStatus ccode, char *filename, int lineNumber)
{
    unsigned facilityCode = (ccode >> 16) & 0xfff;
    unsigned errorIndex = ccode & 0xffff;
    
    if ((facilityCode == NA_IPSEC_FACILITY_CODE) && (errorIndex < MAX_ERROR_CODE))
    {
        printf ("%s returned error %s at line %d in file %s.\n", functionName, 
                errorLookupTable[errorIndex], lineNumber, filename);
    }
    else
    {
        printf ("%s returned error 0x%8.8X at line %d in file %s.\n", functionName,
                ccode, ccode, lineNumber, filename);
    }
    
    tx_thread_suspend(tx_thread_identify());
}







/*
 * This function is registered by demonstrateAutomaticReads when it calls
 * naA2dAutoConfigure.  This function is called by the A/D API to return
 * buffers after they have been filled with data, or have been flushed.
 * This function updates the buffer length and the count of available 
 * buffers.
 *
 * This function is called from an ISR.
 */
void bufferReturnFn(void * context, void * buffer, unsigned int length)
{
    bufferType *bp = (bufferType *) buffer;
    
    if (bp != NULL)
    {
        bp->length = length;
        availBuffers++;
    }
}




/*
 * The NS9215 A/D converter can be used in two different modes.  In one mode software
 * reads the sampled values, in the other mode DMA is used to copy the sampled values
 * into memory buffers.  This function demonstrates how to use the DMA mode.  
 */
void demonstrateAutomaticReads(void)
{
    BYTE input = 0xff;
    NAA2dHandle_t handle;
    WORD64 desired, actual;
    unsigned scanIndex, inputIndex, bufferIndex;
    unsigned bufferQTail = 0;
    
    printf("\n\nStarting automatic read A/D demonstration\n");
    printf("This function will demonstrate how to set up the A/D converter\n");
    printf("to automatically sample inputs and store the sampled values\n");
    printf("in application buffers\n\n");
    /*
     * The NS9215 A/D can run from 600 KHz to 14 MHz.  We really don't care what 
     * speed we run at for this example, so we pick an in between speed.
     */
    desired.x = (NA_A2D_MIN_CLOCK_RATE + NA_A2D_MAX_CLOCK_RATE) / 2L;
    actual.x = 0; /* set to avoid compiler warning*/
    
    HANDLE_ERROR(naA2dOpen(NS9215_A2D_IDENTIFIER, &handle), "naA2dOpen");
    HANDLE_ERROR(naA2dConfigure(handle, &input, desired, &actual), "naA2dConfigure");
    printf("The A/D is now sampling %lu inputs per second.\n\n", actual.s.lsw);

    /*
     * At this point the A/D converter is continuously sampling its inputs at
     * about 7 MHz.  Now we set up the automatic read feature.  This will cause
     * the A/D converter to save the sampled values in buffers that we provide.
     * This next function call registers the function the API is to call to
     * return the buffers to us.
     */
    HANDLE_ERROR(naA2dAutoConfigure(handle, bufferReturnFn, NULL), "naA2dAutoConfigure");
    
    availBuffers = 0;
    /*
     * Now add the buffers into the A/D converter's queue of buffers.
     */
    for (bufferIndex = 0; bufferIndex < BUFFER_COUNT; bufferIndex++)
    {
        HANDLE_ERROR(naA2dAutoLoadBuffer(handle, autoBuffer[bufferQTail]->buffer, BUFFER_LENGTH), "naA2dAutoLoadBuffer");
        bufferQTail = BUFFER_NEXT_INDEX(bufferQTail);
    }

    HANDLE_ERROR(naA2dAutoStart(handle), "naA2dStart");
    
    printf("Pin 0   Pin 1   Pin 2   Pin 3   Pin 4   Pin 5   Pin 6   Pin 7\n");
    printf("=====   =====   =====   =====   =====   =====   =====   =====\n");
    for (bufferIndex = 0; bufferIndex < NUMBER_OF_ROWS; bufferIndex++)
    {
        unsigned samplesPerPin;
        
        /*
         * This while statement waits for a buffer to be returned to us.
         */
        while (availBuffers == 0);
        
        /*
         * Figure out how many samples are in the buffer.  The length value is
         * the total number of samples, we calculate the number of samples taken
         * for each pin.  The number of samples in the buffer will always be
         * evenly divisible by the number of active inputs.
         */
        samplesPerPin = autoBuffer[bufferQTail]->length / (NA_A2D_MAX_INPUTS * sizeof(unsigned));
        for (inputIndex = 0; inputIndex < NA_A2D_MAX_INPUTS; inputIndex++)
        {
            long long averageSample = 5LL;
            unsigned average = 0;
            
            for (scanIndex = 0; scanIndex < samplesPerPin; scanIndex++)
            {
                averageSample += 10 * autoBuffer[bufferQTail]->buffer[(NA_A2D_MAX_INPUTS * scanIndex) + inputIndex];
            }
            averageSample /= 10 * samplesPerPin;
            average = (unsigned) (averageSample & 0xffffffff);
            printf("%-4u    ", average);
            tx_thread_sleep(1);
        }
        /*
         * We have processed the buffer, so put it back into the API's queue.
         */
        HANDLE_ERROR(naA2dAutoLoadBuffer(handle, autoBuffer[bufferQTail]->buffer, BUFFER_LENGTH), "naA2dAutoLoadBuffer");
        availBuffers--;
        bufferQTail = BUFFER_NEXT_INDEX(bufferQTail);
        printf("\n");
    }
    HANDLE_ERROR(naA2dAutoStop(handle), "naA2dStop");
    HANDLE_ERROR(naA2dAutoFlushBuffers(handle), "naA2dAutoFlushBuffers");
    HANDLE_ERROR(naA2dClose(handle), "naA2dClose");
}
    
    

/*
 * The NS9215 A/D converter can be used in two different modes.  In one mode software
 * reads the sampled values, in the other mode DMA is used to copy the sampled values
 * into memory buffers.  This function demonstrates how to read the sampled values
 * using software, instead of DMA.
 */
void demonstrateSimpleReads(void)
{
    BYTE input = 0xff;
    NAA2dHandle_t handle;
    WORD64 desired, actual;
    unsigned rowIndex, inputIndex;
    unsigned value = 0;
    
    printf("Starting simple A/D demonstration\n");
    printf("This function will demonstrate how to set up the A/D converter\n");
    printf("and then read data from it one input at a time.\n\n");
    /*
     * The NS9215 A/D can run from 600 KHz to 14 MHz.  We really don't care what 
     * speed we run at for this example, so we pick an in between speed.
     */
    desired.x = (NA_A2D_MIN_CLOCK_RATE + NA_A2D_MAX_CLOCK_RATE) / 2L;
    actual.x = 0; /* set to avoid compiler warning*/
    
    HANDLE_ERROR(naA2dOpen(NS9215_A2D_IDENTIFIER, &handle), "naA2dOpen");
    HANDLE_ERROR(naA2dConfigure(handle, &input, desired, &actual), "naA2dConfigure");
    printf("The A/D is now sampling %lu inputs per second.\n\n", actual.s.lsw);
    
    /*
     * If we get here, then naA2dConfigure succeeded.  That means the A/D
     * converter is now running and is continously sampling the inputs and 
     * store the sampled values in internal registers.  We can read those
     * registers at any times.  Note that calling naA2dReadInput does not
     * cause the A/D converter to sample an input.  It is doing that 
     * continuously now.  The naA2dReadInput function just returns the 
     * last value sampled for a pin.
     */
    
    printf("Pin 0   Pin 1   Pin 2   Pin 3   Pin 4   Pin 5   Pin 6   Pin 7\n");
    printf("=====   =====   =====   =====   =====   =====   =====   =====\n");
    for (rowIndex = 0; rowIndex < NUMBER_OF_ROWS; rowIndex++)
    {
        /*
         * This sleep is not needed.  I just put it here to give users time
         * to change the voltage on a pin.
         */
        tx_thread_sleep(NABspTicksPerSecond);
        for (inputIndex = 0; inputIndex < NA_A2D_MAX_INPUTS; inputIndex++)
        {
            /*
             * This example reads all of the sampled values.  You don't really
             * need to do that.  If you are only using some pins, you can just
             * read the values for those and ignore the others.
             */
            HANDLE_ERROR(naA2dReadInput(handle, inputIndex, &value), "naA2dReadInput");
            printf("%-4u    ", value);
        }
        printf("\n");
    }
    
    HANDLE_ERROR(naA2dClose(handle), "naA2dClose");
}



/*
 *
 *  Function: void applicationStart (void)
 *
 *  Description:
 *
 *      This routine is responsible for starting the user application.  It should 
 *      create any threads or other resources the application needs.
 *
 *      ThreadX, the NET+OS device drivers, and the TCP/IP stack will be running
 *      when this function is called.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */
void applicationStart (void)

{
    #define BSP_SERIAL_NO_DRIVER    (0)
    #if (PROCESSOR == ns9215) || (PROCESSOR == ns9210)
        if (NABspSerialPortType(3) != BSP_SERIAL_NO_DRIVER)
        {
            printf ("Conflict with A/D converter and serial port D.  Both devices use the same\n");
            printf ("interrupt.  You must disable serial port D in order to use the A/D converter.\n");
            exit(1);
        }
    #endif
    int idx;
    
    /*
     * We need to make sure the DMA buffers are aligned on cache line boundaries, so we
     * allocate buffers that are big enough plus room for two extra cache lines.  Then
     * we adjust the buffer pointer so it starts on a cache line boundary.  At this 
     * point, the adjust pointer will be at the pointer returned by malloc plus some
     * offset n, which is less than or equal to the size of one cache line.  There
     * will also be extra padding after then end of the DMA buffer.  This extra 
     * padded will be equal to or greater than the size of a cache line.  These
     * adjustments and extra padding are there because the driver must invalidate the
     * buffer before starting DMA.  The region of memory invalidated starts and ends
     * on cache line boundaries.  So we need to make sure the DMA buffer starts on
     * a cache line boundary, and that there is extra room at the end so the invalidated
     * memory region does not effect someone else's buffer.
     *
     * We do not free these buffers.  If we did, we would have to make sure we freed
     * from the buffer pointer returned by malloc, not the adjusted one.
     */
    for (idx = 0; idx < BUFFER_COUNT; idx++)
    {
        unsigned bufferAddress = (unsigned) malloc(sizeof(bufferType) + 2*NA_CACHE_LINE_SIZE);
        
        if (bufferAddress == 0)
        {
            printf("Out of memory.\n");
            exit(1);
        }
        autoBuffer[idx] = (bufferType *) ((bufferAddress + NA_CACHE_LINE_SIZE) & ~(NA_CACHE_LINE_SIZE - 1));
    }
    
    demonstrateSimpleReads();
    demonstrateAutomaticReads();
}



/*
 *
 *  Function: void applicationTcpDown (void)
 *
 *  Description:
 *
 *      This routine will be called by the NET+OS root thread once every 
 *      clock tick while it is waiting for the TCP/IP stack to come up.  
 *      This function can increment a counter everytime it's called to 
 *      keep track of how long we've been waiting for the stack to start.
 *      If we've been waiting too long, then this function can do something
 *      to handle the error.  
 *
 *      This function will not be called once the stack has started.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */

void applicationTcpDown (void)

{
    static int ticksPassed = 0;

    ticksPassed++;
/*
 * Code to handle error condition if the stack doesn't come up goes here.
 */
}


