/*****************************************************************************
* Copyright (c) 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
*
*****************************************************************************/

#include "Npttypes.h"
#include "a2d.h"
#include "a2d_private.h"
#include "nastatus.h"


static unsigned isChannelOpen(NAA2dChannelTable_t *channel)
{
    unsigned result = FALSE;
    
    if (channel != NULL)
    {
        result = ((channel->state == A2D_CHANNEL_STATE_OPEN) && (channel->handleSignature == A2D_VALID_SIGNATURE));
    }
    
    return result;
}


/*ad*
 * Use this function to configure an A/D channel.  This function determines which
 * inputs the channel will scan and the scan rate. 
 *
 * Use the inputs parameter to select which A/D inputs to sample.  Each bit in
 * this parameter selects the corresponding input.  You set bit 0 to select input
 * 0, bit 1 to select input 1, and so on.  Note that there is a hardware limitation
 * in the NS9215 processor.  Input 0 must always be selected, and the enabled
 * inputs must be consecutive.  So you can select input 0, or inputs 0 and 1, or 
 * inputs 0, 1, and 2, etc., but you cannot select input 0 and 2 without selecting
 * input 1.
 *
 * Make sure you pass the address of the inputs argument, not the value of inputs.
 *
 * The sampling rate is the number of inputs sampled per second.  The function will 
 * set the channel's sampling rate as close to desiredClockRate as possible.  The 
 * actual clock rate is returned in *actualClockRate.  The caller can pass 
 * actualClockRate as NULL if it is not interested in it.
 *
 * The A/D converter will start sampling inputs before this function completes.  You
 * can use 
 * @link naA2dReadInput to retrieve the last sampled value for an input at any time.
 *
 * @since 7.3
 * @external   
 * @category BSP:Device_Drivers:a2d
 *
 * @param handle            handle acquired with naA2dOpen
 * @param inputs            pointer to byte which is a bit field that selects which 
 *                          inputs are to be enabled
 * @param desiredClockRate  desired scan rate in inputs sampled per second
 * @param actualClockRate   pointer to 64-bit word to load with the actual scan rate set
 *
 * @return NA_SUCCESS               channel has been configured
 * @return NA_A2D_BAD_HANDLE        the handle is invalid
 * @return NA_A2D_BAD_INPUT         numberOfInputs is too large
 * @return NA_A2D_INVALID_STATE     the channel is not in the correct state for this operation
 * @return NA_A2D_BAD_CLOCK_RATE    desiredClockRate is out of range
*ad*/
NaStatus naA2dConfigure(NAA2dHandle_t handle, BYTE * inputs, WORD64 desiredClockRate, WORD64 * actualClockRate)
{
    NAA2dChannelTable_t *channel = (NAA2dChannelTable_t *) handle;
    NaStatus result = NA_A2D_BAD_HANDLE;
    
    A2D_SEMAPHORE_GET;
    
    if (isChannelOpen(channel))
    {
        result = channel->configure(channel->context, inputs, desiredClockRate, actualClockRate);
    }
    
    A2D_SEMAPHORE_RELEASE;
    
    return result;
}




/*ad*
 * Read the most recently sampled value for one input.
 *
 * Once the A/D converter has been configured, it continuously samples the inputs
 * and stores the values in internal registers.  This function retrieves the last
 * value stored for the input you select.
 *
 * @since 7.3
 * @external    
 * @category BSP:Device_Drivers:a2d
 *
 * @param handle    handle acquired with naA2dOpen
 * @param input     indicates which input value to read
 * @param value     pointer to word to load with last sampled value for the input
 *
 * @return NA_SUCCESS               the function call succeeded
 * @return NA_A2D_BAD_HANDLE        the handle is invalid
 * @return NA_A2D_BAD_INPUT         input is larger than numberOfInputs passed to naA2dConfigure
 * @return NA_A2D_NOT_CONFIGURED    channel has not been configured
 * @return NA_A2D_BAD_ARGUMENT      value argument is NULL
*ad*/
NaStatus naA2dReadInput(NAA2dHandle_t handle, unsigned int input, unsigned int * value)
{
    NAA2dChannelTable_t *channel = (NAA2dChannelTable_t *) handle;
    NaStatus result = NA_A2D_BAD_HANDLE;
    
    A2D_SEMAPHORE_GET;
    
    if (value == NULL)
    {
        result = NA_A2D_BAD_ARGUMENT;
        goto naA2dReadInputEnd;
    }
    
    if (isChannelOpen(channel))
    {
        if (channel->maxInputs <= input)
        {
            result = NA_A2D_BAD_INPUT;
        }
        else
        {
            result = channel->readInput(channel->context, input, value);
        }
    }

naA2dReadInputEnd:    
    A2D_SEMAPHORE_RELEASE;

    return result;
}

    

/*ad*
 * Use this function to set the buffer callback function used in automatic scan mode.
 *
 * The A/D converter can be configured to write sampled values into memory buffers.
 * The API user must call 
 * @link naA2dAutoLoadBuffer to load empty buffers into a queue.  The driver uses the
 * callback registered with this function to return the buffers after they have been
 * filled, or after they have been flushed with
 * @link naA2dAutoFlushBuffers.
 *
 * @since 7.3
 * @external    
 * @category BSP:Device_Drivers:a2d
 *
 * @param handle            handle acquired with naA2dOpen
 * @param bufferReturnFn    callback function to be used to return buffers to the API user
 * @param bufferReturnContext   context for callback function
 *
 * @return NA_SUCCESS               the function call succeeded
 * @return NA_A2D_BAD_HANDLE        the handle is invalid
 * @return NA_A2D_CHANNEL_IN_USE    you must call naA2dAutoStop to stop the channel before configuring it
 * @return NA_A2D_NOT_SUPPORTED     the A/D channel does not support automatic reads
 * @return NA_A2D_NOT_CONFIGURED    channel has not been configured
 * @return NA_A2D_BAD_ARGUMENT      bufferReturnFn is NULL
 * @return NA_A2D_OUT_OF_RESOURCES    resources needed are in use by another process 
*ad*/
NaStatus naA2dAutoConfigure(NAA2dHandle_t handle, NAA2dAutoBufferReturnFn_t bufferReturnFn, void * bufferReturnContext)
{
    NAA2dChannelTable_t *channel = (NAA2dChannelTable_t *) handle;
    NaStatus result = NA_A2D_BAD_HANDLE;
    
    A2D_SEMAPHORE_GET;
    
    if (isChannelOpen(channel))
    {
        result = channel->autoConfigure(channel->context, bufferReturnFn, bufferReturnContext);
    }
    
    A2D_SEMAPHORE_RELEASE;
    
    return result;
}
    


/*ad*
 * Use this function to load a read buffer when using the auto read function.
 *
 * This function registers a buffer with an A/D channel.  The buffer will be 
 * placed in a queue of buffers, and data will be copied into it from 
 * the A/D converter.  The buffer must be aligned on a 32-bit boundary. 
 *
 * Each sample value is written into a 32-bit word.  The buffer length must be an even 
 * multiple of the number of selected inputs times four (4 bytes in each word).  
 * For example, if 3 inputs are selected, then the buffer length must be an 
 * even multiple of 12 (4 bytes per word times 3 inputs).
 *
 * Automatic reads are disabled until naA2dAutoStart is called.
 *
 * @since 7.3
 * @external    
 * @category BSP:Device_Drivers:a2d
 *
 * @param handle            handle acquired with naA2dOpen
 * @param buffer            buffer to place into queue
 * @param length            length of buffer in bytes
 *
 * @return NA_SUCCESS               the function call succeeded
 * @return NA_A2D_NOT_CONFIGURED    the A/D converter channel has not been configured for 
 *                                  auto scan mode
 * @return NA_A2D_BAD_HANDLE        the handle is invalid
 * @return NA_A2D_BAD_LENGTH        length is invalid
 * @return NA_A2D_BAD_ARGUMENT      the buffer is NULL or invalid
 * @return NA_A2D_NOT_SUPPORTED     the channel does not support automatic reads
 * @return NA_A2D_BUFFER_Q_FULL     the channel cannot accept anymore buffers
 * @return NA_A2D_OUT_OF_RESOURCES	resources needed are in use by another process 
*ad*/
NaStatus naA2dAutoLoadBuffer(NAA2dHandle_t handle, void * buffer, unsigned int length)
{
    NAA2dChannelTable_t *channel = (NAA2dChannelTable_t *) handle;
    NaStatus result = NA_A2D_BAD_HANDLE;
    
    A2D_SEMAPHORE_GET;
    
    if (buffer == NULL)
    {
        result = NA_A2D_BAD_ARGUMENT;
        goto naA2dAutoLoadBufferEnd;
    }
    
    if (isChannelOpen(channel))
    {
        result = channel->autoLoadBuffer(channel->context, buffer, length);
    }

naA2dAutoLoadBufferEnd:    
    A2D_SEMAPHORE_RELEASE;
    
    return result;
}




/*ad*
 * Use this function to start automatic scanning on the channel.
 *
 * This function enables automatic scanning on an A/D channel.  The channel 
 * must be configured for automatic scanning, and at least one buffer must 
 * have been loaded in the buffer queue.
 *
 * While the automatic scan feature is enabled, the channel continuously 
 * writes the sampled values into buffers registered with 
 * naA2dAutoLoadBuffer.  The API passes full buffers to the caller by 
 * calling the buffer return function specified in naA2dAutoConfigure.  
 *
 * Automatic reads continue until naAd2AutoStop is called.  If channel runs 
 * out of buffers, then data from inputs scanned while no buffers were 
 * available is lost.
 *
 * @since 7.3
 * @external 
 * @category BSP:Device_Drivers:a2d
 *
 * @param handle        handle acquired with naA2dOpen
 *
 * @return NA_SUCCESS               the function call succeeded
 * @return NA_A2D_NOT_CONFIGURED    the A/D converter channel has not been configured
 * @return NA_A2D_BAD_HANDLE        the handle is invalid
 * @return NA_A2D_NO_BUFFERS        no buffers have been loaded into the receive ring
 * @return NA_A2D_NOT_SUPPORTED     the channel does not support automatic reads
 * @return NA_A2D_OUT_OF_RESOURCES    required resource is in use by another process 
*ad*/
NaStatus naA2dAutoStart(NAA2dHandle_t handle)
{
    NAA2dChannelTable_t *channel = (NAA2dChannelTable_t *) handle;
    NaStatus result = NA_A2D_BAD_HANDLE;
    
    A2D_SEMAPHORE_GET;
    
    if (isChannelOpen(channel))
    {
        result = channel->autoStart(channel->context);
    }
    
    A2D_SEMAPHORE_RELEASE;
    
    return result;
}






/*ad*
 * Use this function to stop automatic scanning on an A/D channel.
 *
 * This function stops automatic scanning on an A/D channel.  If the operation
 * is stopped in the middle of a buffer, the buffer is closed and 
 * sent to the caller partially filled. 
 *
 * @since 7.3
 * @external 
 * @category BSP:Device_Drivers:a2d
 *
 * @param handle        handle acquired with naA2dOpen
 *
 * @return NA_SUCCESS               the function call succeeded
 * @return NA_A2D_NOT_RUNNING       automatic scan mode was not running
 * @return NA_A2D_BAD_HANDLE        the handle is invalid
 * @return NA_A2D_NOT_SUPPORTED     the channel does not support automatic reads
 * @return NA_A2D_OUT_OF_RESOURCES    DMA channel is reserved for use by another process 
*ad*/
NaStatus naA2dAutoStop(NAA2dHandle_t handle)
{
    NAA2dChannelTable_t *channel = (NAA2dChannelTable_t *) handle;
    NaStatus result = NA_A2D_BAD_HANDLE;
    
    A2D_SEMAPHORE_GET;
    
    if (isChannelOpen(channel))
    {
        result = channel->autoStop(channel->context);
    }
    
    A2D_SEMAPHORE_RELEASE;
    
    return result;
}




/*ad*
 * Use this function to flush all of the read buffers enqueued for a DMA channel.
 *
 * This function disables automatic reads and flushes all buffers in the receive 
 * queue.  The buffers are returned to the caller through the return function 
 * registered with naA2dAutoConfigure.
 *
 * @since 7.3
 * @external 
 * @category BSP:Device_Drivers:a2d
 *
 * @param handle        handle acquired with naA2dOpen
 *
 * @return NA_SUCCESS               the function call succeeded
 * @return NA_A2D_NOT_CONFIGURED    the A/D converter channel has not been configured
 * @return NA_A2D_BAD_HANDLE        the handle is invalid
 * @return NA_A2D_NOT_SUPPORTED     the channel does not support automatic reads
 * @return NA_A2D_OUT_OF_RESOURCES	DMA channel is reserved for use by another process 
*ad*/
NaStatus naA2dAutoFlushBuffers(NAA2dHandle_t handle)
{
    NAA2dChannelTable_t *channel = (NAA2dChannelTable_t *) handle;
    NaStatus result = NA_A2D_BAD_HANDLE;
    
    A2D_SEMAPHORE_GET;
    
    if (isChannelOpen(channel))
    {
        result = channel->autoFlushBuffers(channel->context);
    }
    
    A2D_SEMAPHORE_RELEASE;
    
    return result;
}



/*ad*
 * Use this function to install an interrupt service routine.
 *
 * This routine installs an interrupt service routine.  The A/D converter 
 * generates an interrupt every time it finishes sampling an input.  The 
 * ISR will be called with the context value set with this function, and 
 * the number of the input and its sampled value.
 *
 * This function does not enable the interrupt.  Call naA2dIsrEnable to 
 * enable it.
 *
 * Only one ISR callback can be registered at a time.
 *
 * @since 7.3
 * @external 
 * @category BSP:Device_Drivers:a2d
 *
 * @param handle        handle acquired with naA2dOpen
 * @param isr           pointer to interrupt service routine to install
 * @param isrContext    this parameter is passed to the ISR
 *
 * @return NA_SUCCESS               the ISR has been installed
 * @return NA_A2D_NOT_CONFIGURED    the A/D converter channel has not been configured
 * @return NA_A2D_BAD_HANDLE        the handle is invalid
 * @return NA_A2D_ISR_ALREADY_INSTALLED an ISR callback has already been installed
 * @return NA_A2D_NOT_SUPPORTED     the channel does not support ISRs
 * @return NA_A2D_OUT_OF_RESOURCES  cannot allocated buffers or other resources
 * @return NA_A2D_BAD_ARGUMENT      isr is NULL
*ad*/
NaStatus naA2dIsrInstall(NAA2dHandle_t handle, NAA2dIsr_t isr, void * isrContext)
{
    NAA2dChannelTable_t *channel = (NAA2dChannelTable_t *) handle;
    NaStatus result = NA_A2D_BAD_HANDLE;
    
    A2D_SEMAPHORE_GET;
    
    if (isr == NULL)
    {
        result = NA_A2D_BAD_ARGUMENT;
        goto naA2dIsrInstallEnd;
    }
    
    if (isChannelOpen(channel))
    {
        result = channel->isrInstall(channel->context, isr, isrContext);
    }

naA2dIsrInstallEnd:    
    A2D_SEMAPHORE_RELEASE;
    
    return result;
}





/*ad*
 * Use this routine to uninstall an interrupt service routine.
 *
 * This function disables the A/D interrupt and uninstalls 
 * the ISR previously installed by naA2dIsrInstall.
 *
 * @since 7.3
 * @external 
 * @category BSP:Device_Drivers:a2d
 *
 * @param handle        handle acquired with naA2dOpen
 *
 * @return NA_SUCCESS               the ISR has been uninstalled
 * @return NA_A2D_NOT_CONFIGURED    the A/D converter channel has not been configured
 * @return NA_A2D_BAD_HANDLE        the handle is invalid
 * @return NA_A2D_NOT_SUPPORTED     the channel does not support ISRs
*ad*/
NaStatus naA2dIsrUninstall(NAA2dHandle_t handle)
{
    NAA2dChannelTable_t *channel = (NAA2dChannelTable_t *) handle;
    NaStatus result = NA_A2D_BAD_HANDLE;
    
    A2D_SEMAPHORE_GET;
    
    if (isChannelOpen(channel))
    {
        result = channel->isrUninstall(channel->context);
    }

    A2D_SEMAPHORE_RELEASE;
    
    return result;
}






/*ad*
 * Use this function to enable or disable interrupts from a channel.
 *
 * @since 7.3
 * @external 
 * @category BSP:Device_Drivers:a2d
 *
 * @param handle        handle acquired with naA2dOpen
 * @param isEnabled     set to TRUE to enable interrupts, or FALSE to disable
 *
 * @return NA_SUCCESS               the new interrupt mask has been set
 * @return NA_A2D_NOT_CONFIGURED    the A/D converter channel has not been configured
 * @return NA_A2D_BAD_HANDLE        the handle is invalid
 * @return NA_A2D_NOT_SUPPORTED     the channel does not support ISRs
 * @return NA_A2D_ISR_NOT_INSTALLED use naA2dIsrInstall before calling this function
*ad*/
NaStatus naA2dInterruptEnable(NAA2dHandle_t handle, unsigned int isEnabled)
{
    NAA2dChannelTable_t *channel = (NAA2dChannelTable_t *) handle;
    NaStatus result = NA_A2D_BAD_HANDLE;
    
    A2D_SEMAPHORE_GET;
    
    if (isChannelOpen(channel))
    {
        result = channel->interruptEnable(channel->context, isEnabled);
    }

    A2D_SEMAPHORE_RELEASE;
    
    return result;
}





