/*
 *  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
 *
 * =======================================================================
 * This module contains APIs for 1-wire driver
 *
 * Edit Date/Ver   Edit Description
 * ==============  =======================================================
 *
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <errno.h>

#include "netosIo.h"
#include "onewire.h"
#include "fim_onewire.h"
#include "sysClock.h"
#include "NA_ISR.h"
#include <bsp_iop.h>


/*ad*
 * 1-Wire (R) is a registered trademark of Dallas Semiconductor Corp for a 
 * device communications bus system that provides low-speed data, signaling 
 * and power over a single signal, albeit using two wires, one for ground, one
 * for power and data. 1-Wire is similar in concept to  
 * @link BSP:Device_Drivers:I2C_DEVICE_API, but with lower data rates, 
 * longer range and a much lower cost. It is typically used to communicate 
 * with small inexpensive devices such as digital thermometers, weather 
 * instruments and 1-Wire memory devices.
 *
 * Legacy 1-Wire products support a standard communication speed of 16.3 kbps 
 * and overdrive speed of 142 kbps. 
 *
 * This 1-Wire driver uses (Flexible Interface Module) to communicate with the
 * slave device. The driver "/1-wire/0" uses FIM0 and "/1-wire/1" uses FIM0. 
 * Each driver use one FIM general IO line as data line. Which FIM IO line 
 * to use is configurable in bsp_iop.h. The variable @link BSP_IOP_FIM0_1WIRE 
 * is used for "1-wire/0" and the varible @link BSP_IOP_FIM1_1WIRE is used for 
 * "1-wire/1". User can use @link ioctl function to override this default IO
 * pin value.
 *  
 * This driver provide three basic operations which are Reset, Write data 
 * bytes and Read data bytes. The driver uses ioctl to send reset, configure 
 * timings (regular/standard or overdrive) and configure FIM IO line to be 
 * used as 1-Wire data line.
 *
 * @external
 * @overview 1-Wire
 * @category BSP:Device_Drivers:1WIRE_API
 * @include "netosIo.h"
 * @platform    All_9210_Platforms, All_9215_Platforms 
 * @library    bsp
 * @since 7.4
 *
 * @see @link  NA_1WIRE_SET_CNFG
 * @see @link  ioctl
 * @see @link  Na1wireConfig_t
 *
*ad*/


static Na1wireConfig_t onewireConf[NA_IOP_MAX_PICS] = 
{   /* mode, slot, low1, low0, rec, lowR, rdv, release, resetLow, genIo */
    {    0,    0,    0,    0,   0,    0,   0,       0,        0,     BSP_IOP_FIM0_1WIRE },
    {    0,    0,    0,    0,   0,    0,   0,       0,        0,     BSP_IOP_FIM1_1WIRE }
};

static Na1wireConfig_t regularDefault = 
{   /* mode,  slot, low1, low0, rec, lowR, rdv, release, resetLow, genIo */
    O_STANDARD, 66,   6,   60,   10,    5,  15,    5,        480,     BSP_IOP_GEN_IO_0 
};

static Na1wireConfig_t overdriveDefault = 
{   /* mode,  slot, low1, low0, rec, lowR, rdv, release, resetLow, genIo */
    O_OVERDRIVE, 8,   1,    6,    1,    1,   2,    1,        50,     BSP_IOP_GEN_IO_0 
};

/*
 * - Downloads FIM program
 * - Configures register for direct access (FIFO)
 *
 */
static int setupFim(NaIopProcessor_t channel)
{
    NaStatus ccode;
    int ret = SUCCESS;

    /* stop the processor so we can download its program */
    ccode = naIopProcessorStopAndReset(channel);
    if (ccode != NASTATUS_SUCCESS)
    {
        ret = EINVAL;
        goto done_setup;
    }

    /* download the code to FIM */
    ccode = naIopProcessorDownloadPicProgram(channel, &naOnewireProgram);
    if (ccode != NASTATUS_SUCCESS)
    {
        ret = EINVAL;
        goto done_setup;
    }

    /* set the registers for direct FIFO access */
    narm_write_reg (NA_IOHUB_DMA_RX_CONTROL, NA_IOHUB_DMA_RX_CONTROL_getAddress(channel), 
                                     direct, NA_IOHUB_DMA_RX_CONTROL_DIRECT_ACCESS_MODE);
    narm_write_reg (NA_IOHUB_DMA_TX_CONTROL, NA_IOHUB_DMA_TX_CONTROL_getAddress(channel), 
                                     direct, NA_IOHUB_DMA_TX_CONTROL_DIRECT_ACCESS_MODE);

    /* disable everything until get going */
    *NA_IOHUB_CONTROL_getAddress(channel, 6) = 0;

    /* start the FIM */
    ccode = naIopProcessorStartAtZero(channel);
    if (ccode != NASTATUS_SUCCESS)
    {
        ret = EINVAL;
        goto done_setup;
    }

    tx_thread_sleep(2);  /* let it get going */

    done_setup:
    return ret;
}


/*
 * - Calculates the time in FIM clock
 * - Generates the interrupt to FIM to read the timing
 *
 */
static int setTimeInFim(int channel, unsigned code, unsigned short tval)
{
    
    unsigned long val = ((NAgetPllOutputClkFreq()/1000000) * tval)/10;

    if (val > 0xFFFF)
        return EINVAL;

    *NA_IOHUB_CONTROL_getAddress(channel, 0) = val & 0xFF;
    *NA_IOHUB_CONTROL_getAddress(channel, 1) = (val >> 8) & 0xFF;

    if (naIopInterruptProcessor((NaIopProcessor_t)channel, code) != NASTATUS_SUCCESS)
        return EINVAL;
    else
        return SUCCESS;
}

/*
 * - updates the timings in FIM
 * - updates the IO line to be used
 * - updates the private FIM data
 */
static int setConfigTime(int channel, Na1wireConfig_t *cnfg, Na1wireConfig_t *value)
{
    int ret = SUCCESS;
    
    assert(cnfg != NULL);
    assert(value != NULL);

    /* set tSLOT - tLOW1 and send INTR_TSLOT interrupt code */
    if ((cnfg->tSlot != value->tSlot) || (cnfg->tLow1 != value->tLow1))
    {
        if ((ret = setTimeInFim(channel, INTR_TSLOT, value->tSlot - value->tLow1)) != SUCCESS)
            goto doneConfig;
    }

    /* set tLOW1 and send INTR_TLOW1 interrupt code */
    if (cnfg->tLow1 != value->tLow1)
    {
        if ((ret = setTimeInFim(channel, INTR_TLOW1, value->tLow1)) != SUCCESS)
            goto doneConfig;
    }

    /* set tLOW0 and send INTR_TLOW0 interrupt code */
    if (cnfg->tLow0 != value->tLow0)
    {
        if ((ret = setTimeInFim(channel, INTR_TLOW0, value->tLow0)) != SUCCESS)
            goto doneConfig;
    }

    /* set tREC and send INTR_TREC interrupt code */
    if (cnfg->tRec != value->tRec)
    {
        if ((ret = setTimeInFim(channel, INTR_TREC, value->tRec)) != SUCCESS)
            goto doneConfig;
    }

    /* set tLOWR and send INTR_TLOWR interrupt code */
    if (cnfg->tLowR != value->tLowR)
    {
        if ((ret = setTimeInFim(channel, INTR_TLOWR, value->tLowR)) != SUCCESS)
            goto doneConfig;
    }

    /* set read sample time and send INTR_TSAMPL interrupt code */
    if ((cnfg->tLowR != value->tLowR) || ((cnfg->tRdv != value->tRdv)))
    {
        if ((ret = setTimeInFim(channel, INTR_TSAMPL, value->tRdv - value->tLowR)) != SUCCESS)
            goto doneConfig;
    }

    /* set tRELEASE and send INTR_TRELEASE interrupt code */
    if (cnfg->tRelease != value->tRelease)
    {
        if ((ret = setTimeInFim(channel, INTR_TRELEASE, value->tRelease)) != SUCCESS)
            goto doneConfig;
    }

    /* set tRESET and send INTR_TRESET interrupt code */
    if (cnfg->tResetLow != value->tResetLow)
    {
        if ((ret = setTimeInFim(channel, INTR_TRESET, value->tResetLow)) != SUCCESS)
            goto doneConfig;
    }

    doneConfig:
    return ret;    
}

/*
 * - Sends command to FIM to issue reset signal
 * - Reads the RX FIFO and calculates the presence width in microseconds
 */
static unsigned sendReset(int channel)
{
    volatile char *wrPtr;
    unsigned presence = 0;
    int count = NA_1WIRE_RSTWAIT;

    wrPtr = (char *)NA_IOHUB_DIRECT_MODE_TX_LAST_getAddress(channel);
    while (*NA_IOHUB_IFS_getAddress(channel) & NA_IOHUB_IFS_TX_FIFO_FULL);
    *wrPtr = NA_1WIRE_RST; 

    do {
        if (count-- > 0)
            tx_thread_sleep(1);
        else 
            return presence;
        
    } while (*NA_IOHUB_IFS_getAddress(channel) & NA_IOHUB_IFS_RX_FIFO_EMPTY);


    if (narm_read_reg(NA_IOHUB_DIRECT_MODE_RX_STATUS, NA_IOHUB_DIRECT_MODE_RX_STATUS_getAddress(channel), byte))
    {                     
        presence = narm_read_reg(NA_IOHUB_DIRECT_MODE_RX_FIFO, NA_IOHUB_DIRECT_MODE_RX_FIFO_getAddress(channel), reg);
        presence = ((presence & 0xFFFFFF) * 12)/(NAgetPllOutputClkFreq()/1000000);
    }

    return presence;
}


/*ad*
 *
 * This is the 1-Wire DDI ioctl routine is used to control 1-Wire IO options.
 *
 * This can be used to issue reset, to configure standard or overdrive timings
 * and to configure FIM IO line to be used for 1-Wire communication. 
 *
 * @external
 * @category BSP:Device_Drivers:1WIRE_API
 * @since 7.4
 *
 * @param channel   This is the channel that was opened from the initial
 *                  open call
 * @param request   Command requested by the user
 *                  <list type="ordered">
 *                  <item>NA_1WIRE_GET_DFLT: Returns the current default
 *                      values used either for regular mode or for overdrive 
 *                      mode based on (Na1wireConfig_t *)arg->curMode passed 
 *                      by the user </item>
 *                  <item>NA_1WIRE_SET_DFLT: Configures the timings and IO 
 *                      line to use with the default value for O_STANDARD or 
 *                      O_OVERDRIVE mode passed by the user in arg. The 
 *                      1wire_open will request this command to set standard 
 *                      timings </item>
 *                  <item>NA_1WIRE_GET_CNFG: Returns the current 
 *                      configuration values used by the driver for 1-Wire 
 *                      communication in arg, user data space of size 
 *                      Na1wireConfig_t</item>
 *                  <item>NA_1WIRE_SET_CNFG: Configures the timings and IO 
 *                      line to use with values passed in arg </item>
 *                  <item>NA_1WIRE_RESET: Sends reset signal based on the 
 *                      current mode and returns the presence signal width in
 *                      micro seconds in arg, 4 bytes user data space </item>
 *                  </list> 
 * @param arg       user data space required to process the request. Refer
 *                  previous parameter for more details
 *    
 * @return     >=0/SUCCESS  Completed successfully
 * @return     -1/EBADF     Bad channel number
 * @return     -1/EINVAL    Invalid parameter or failed to configure FIM     
 *
 * @note This routine should not be directly called.  Instead, it should be 
 * called by a call to ioctl which in turn calls this function.
 * 
 * <example>
 * #include "netosIo.h"
 * 
 * void someSampleFunction (void)
 * {
 *     int fd;
 *     unsigned presence;
 * 
 *     fd = open ("/1-wire/0", O_RDWR);
 *     if (fd < 0)
 *     {
 *         return;
 *     }
 *
 *     if (ioctl(fd, NA_1WIRE_RESET, (char *)&presence) < 0)
 *     {
 *         return;
 *     }
 *
 * }
 *
 * </example>
 *
 * @see @link  ioctl
 * @see @link  NA_1WIRE_GET_DFLT
 * @see @link  NA_1WIRE_SET_DFLT
 * @see @link  NA_1WIRE_GET_CNFG
 * @see @link  NA_1WIRE_SET_CNFG
 * @see @link  NA_1WIRE_RESET
 * @see @link  open
 * @see @link  na1wireOpen
 * @see @link  Na1wireConfig_t
 *
*ad*/
int na1wireIoctl(int channel, int request, char * arg)
{
    int ret = SUCCESS;
    Na1wireConfig_t *cnfg = (Na1wireConfig_t *)arg;

    /* is channel maps to valid FIM? */    
    if ((unsigned)channel >= NA_IOP_MAX_PICS)   
        return EBADF;

    if (cnfg == NULL)
        return EINVAL;

    switch (request)
    {
        case NA_1WIRE_GET_DFLT:
            if (cnfg->curMode & O_OVERDRIVE)
                memcpy(cnfg, &overdriveDefault, sizeof *cnfg);
            else 
                memcpy(cnfg, &regularDefault, sizeof *cnfg);
            break;

        case NA_1WIRE_SET_DFLT:
        {
            unsigned mode = *(unsigned *)arg;
            cnfg = &onewireConf[channel];

            /* set speed mode */
            if (mode & O_OVERDRIVE)
            {
                cnfg->curMode &= ~O_STANDARD;                            
                cnfg->curMode |= O_OVERDRIVE;                            
                ret = setConfigTime(channel, cnfg, &overdriveDefault);
            }
            else
            {
                cnfg->curMode &= ~O_OVERDRIVE;                            
                cnfg->curMode |= O_STANDARD;                            
                ret = setConfigTime(channel, cnfg, &regularDefault);
            }

            /* configure general IO pin */
            if (ret == SUCCESS)
            {
                unsigned short pin = cnfg->genIoPin;
                
                memcpy(cnfg, &regularDefault, sizeof(Na1wireConfig_t));
                cnfg->genIoPin = (pin > BSP_IOP_GEN_IO_7) ? BSP_IOP_GEN_IO_0 : pin;
                    
                *NA_IOHUB_CONTROL_getAddress(channel, 7) = 0x01 << cnfg->genIoPin;
            }

            break;
        }

        case NA_1WIRE_GET_CNFG:
            memcpy(cnfg, &onewireConf[channel], sizeof *cnfg);
            break;

        case NA_1WIRE_SET_CNFG:
            ret = setConfigTime(channel, &onewireConf[channel], cnfg);
            if (ret == SUCCESS)
            {
                unsigned short pin = onewireConf[channel].genIoPin;

                /* set speed mode */
                if (cnfg->curMode & O_OVERDRIVE)
                {
                    onewireConf[channel].curMode &= ~O_STANDARD;                            
                    onewireConf[channel].curMode |= O_OVERDRIVE;                            
                }
                else
                {
                    onewireConf[channel].curMode &= ~O_OVERDRIVE;                            
                    onewireConf[channel].curMode |= O_STANDARD;                            
                }
                
                memcpy(&onewireConf[channel], cnfg, sizeof(Na1wireConfig_t));
                
                /* configure general IO pin */
                if (cnfg->genIoPin > BSP_IOP_GEN_IO_7)
                    onewireConf[channel].genIoPin = pin;
                else
                    pin = cnfg->genIoPin;
                    
                *NA_IOHUB_CONTROL_getAddress(channel, 7) = 0x01 << pin;                
            }
            
            break;

        case NA_1WIRE_RESET:
            *(unsigned *)arg = sendReset(channel);
            break;

        default:
            ret = EINVAL;
            break;        
    }

    return ret;
}


/*ad*
 * This is the 1-Wire DDI open routine to open 1-Wire master device.
 *
 * This routine configures the standard 1-Wire timings and FIM gpio line 
 * selected. User need to call @link ioctl (which call @link na1wireIoctl) 
 * for any subsequent changes either in timings or GPIO line to use. 
 *
 * The 1-Wire Driver devices: "/1-wire/0" and "/1-wire/1" default 
 * to device number 0 and 1 respectively.   
 *
 * @param   channel     The following 1-Wire channels can be opened:
 *                      <list type="unordered">
 *                      <item> "/1-wire/0"    to open channel 0    </item>
 *                      <item> "/1-wire/1"    to open channel 1    </item>
 *                      </list>
 *
 * @param   mode        Always opens in O_RDWR mode
 *
 * @return >=0/SUCCESS    Opened successfully
 * @return -1/EBADF       Bad channel number
 * @return -1/EBUSY       Device is already open
 * @return -1/EINVAL      Failed to configure the device
 *
 * @note This routine should not be directly called.  Instead, it should be 
 * called by a call to open which in turn calls this function.
 * 
 * <example>
 * #include "netosIo.h"
 * 
 * void someSampleFunction (void)
 * {
 *     int fd;
 * 
 *     fd = open ("/1-wire/0", O_RDWR);
 *     if (fd < 0)
 *     {
 *         return;
 *     }
 *
 * }
 *
 * </example>
 *
 * @see @link open
 *
 * @external
 * @category BSP:Device_Drivers:1WIRE_API
 * @since 7.4
*ad*/
int na1wireOpen(int channel, unsigned mode)
{
    int ret = SUCCESS;
    Na1wireConfig_t *cnfg;

    /* is channel maps to valid FIM? */    
    if ((unsigned)channel >= NA_IOP_MAX_PICS)   
    {
        ret = EBADF;
        goto doneOpen;
    }

    cnfg = &onewireConf[channel];

    /* is channel already opened? */
    if (cnfg->curMode & O_1WIRE)
    {
        ret = EBUSY;
        goto doneOpen;
    }

    cnfg->curMode = mode & ~O_1WIRE;
    if ((ret = setupFim((NaIopProcessor_t)channel)) != SUCCESS)
        goto doneOpen;

    mode = O_STANDARD; /* always start with standard mode */
    if ((ret = na1wireIoctl(channel, NA_1WIRE_SET_DFLT, (char *)&mode)))
        goto doneOpen;

    /* start watching wire */
    if (ret == SUCCESS)
    {
        cnfg->curMode |= O_1WIRE;
        *NA_IOHUB_CONTROL_getAddress(channel, 6) = NA_1WIRE_INIT_COMPLETE;
    }
        
    doneOpen:
    return ret;

}


/*ad*
 * This is the 1-Wire DDI close routine to close 1-Wire master device.
 *
 * This routine also resets all the configuration values. 
 *
 * @param   channel     This is the channel that was opened from the initial
 *                      open call 
 *
 * @return >=0/SUCCESS    Opened successfully
 * @return -1/EBADF       Bad device name
 * @return -1/EINVAL      Failed to close the device
 *
 * @note This routine should not be directly called.  Instead, it should be 
 * called by a call to close which in turn calls this function.
 * 
 * <example>
 * #include "netosIo.h"
 * 
 * void someSampleFunction (void)
 * {
 *     int fd;
 * 
 *     fd = open ("/1-wire/0", O_RDWR);
 *     if (fd < 0)
 *     {
 *         return;
 *     }
 *
 *     if (close(fd) < 0)
 *     {
 *         return;
 *     }
 * }
 *
 *
 * </example>
 *
 * @see @link open
 * @see @link na1wireOpen
 * @see @link close
 *
 * @external
 * @category BSP:Device_Drivers:1WIRE_API
 * @since 7.4
*ad*/
int na1wireClose(int channel)
{
    int ret = SUCCESS;
    NaStatus ccode;

    /* is channel maps to valid FIM? */    
    if ((unsigned)channel >= NA_IOP_MAX_PICS)   
    {
        ret = EBADF;
        goto doneClose;
    }

    /*
     * Stop the processor and reset
     */
    ccode = naIopProcessorStopAndReset((NaIopProcessor_t)channel);
    if (ccode != NASTATUS_SUCCESS)
        ret = EINVAL;

    /* clear all the data anyway */
    memset(&onewireConf[channel], 0, sizeof (Na1wireConfig_t));

    doneClose:
    return ret;    
}


/*ad*
 * This is the 1-Wire DDI read routine to read data from 1-Wire device(s).
 *
 * This routine, on success, always reads number of specified bytes. Reads 
 * all 0xFF for no response. 
 *
 * @param   channel     This is the channel that was opened from the initial
 *                      open call 
 * @param   buffer      Pointer to the user data buffer to which device data
 *                      is read
 * @param   length      Number of bytes to be read, make sure user buffer data
 *                      can hold length bytes of data
 * @param   bytesRead   Number of bytes read by the driver
 *
 * @return >=0/SUCCESS    Read success
 * @return -1/EBADF       Bad device name
 * @return -1/EINVAL      Failed to read the data
 * @return -1/EINTR       Interrupted prematurely due to unexpected status
 *
 * @note This routine should not be directly called.  Instead, it should be 
 * called by a call to read which in turn calls this function.
 * 
 * <example>
 * #include "netosIo.h"
 * 
 * void someSampleFunction (void)
 * {
 *     int fd;
 *     unsigned presence;
 *     char buff[8];
 * 
 *     fd = open ("/1-wire/0", O_RDWR);
 *     if (fd < 0)
 *         return;
 *
 *     if (ioctl(fd, NA_1WIRE_RESET, (char *)&presence) < 0)
 *         return;
 *    
 *     buff[0] = 0xCC;  // skip rom
 *     buff[1] = 0xF0;  // read memory
 *     buff[2] = 0x20;  // from address 0x0020
 *     buff[3] = 0;
 *     if (write(fd, buff, 4) != 4)
 *         return;
 *
 *     // to read 8 bytes data from memory address 0x0020
 *     if (read(fd, buff, 8) != 8)
 *         return;
 * }
 *
 *
 * </example>
 *
 * @see @link open
 * @see @link na1wireOpen
 * @see @link read
 * @see @link write
 *
 * @external
 * @category BSP:Device_Drivers:1WIRE_API
 * @since 7.4
*ad*/
int na1wireRead(int channel, void * buffer, int length, int * bytesRead)
{
    volatile char *wrPtr;
    unsigned char *data = (unsigned char *)buffer;
    unsigned long value;

    /* is channel maps to valid FIM? */    
    if ((unsigned)channel >= NA_IOP_MAX_PICS)   
        return EBADF;

    if (!(*NA_IOHUB_IFS_getAddress(channel) & NA_IOHUB_IFS_RX_FIFO_EMPTY))
        return EINTR;

    wrPtr = (char *)NA_IOHUB_DIRECT_MODE_TX_LAST_getAddress(channel);
    for (*bytesRead = 0; *bytesRead < length; (*bytesRead)++)
    {
        while (*NA_IOHUB_IFS_getAddress(channel) & NA_IOHUB_IFS_TX_FIFO_FULL);
        *wrPtr = NA_1WIRE_READ; 

        while (*NA_IOHUB_IFS_getAddress(channel) & NA_IOHUB_IFS_RX_FIFO_EMPTY);

        if (narm_read_reg(NA_IOHUB_DIRECT_MODE_RX_STATUS, NA_IOHUB_DIRECT_MODE_RX_STATUS_getAddress(channel), byte))
        {
            /* read a byte at a time */
            value = narm_read_reg(NA_IOHUB_DIRECT_MODE_RX_FIFO, NA_IOHUB_DIRECT_MODE_RX_FIFO_getAddress(channel), reg);
            *data++ = (unsigned char)value;
        }
        else
            return EINVAL;
    }    

    return SUCCESS;
}


/*ad*
 * This is the 1-Wire DDI write routine to write data to 1-Wire device(s).
 *
 * This routine, on success, always writes the number of specified bytes.  
 *
 * @param   channel     This is the channel that was opened from the initial
 *                      open call 
 * @param   buffer      Pointer to the user data to transmit to 1-Wire device
 * @param   length      Number of bytes to be transmitted
 * @param   bytesWritten   Number of bytes written to 1-Wire
 *
 * @return >=0/SUCCESS    Write success
 * @return -1/EBADF       Bad device name
 * @return -1/EINTR       Interrupted prematurely due to unexpected status
 *
 * @note This routine should not be directly called.  Instead, it should be 
 * called by a call to write which in turn calls this function.
 * 
 * <example>
 * #include "netosIo.h"
 * 
 * void someSampleFunction (void)
 * {
 *     int fd;
 *     unsigned presence;
 *     char buff[8];
 * 
 *     fd = open ("/1-wire/0", O_RDWR);
 *     if (fd < 0)
 *         return;
 *
 *     if (ioctl(fd, NA_1WIRE_RESET, (char *)&presence) < 0)
 *         return;
 *    
 *     buff[0] = 0xCC;  // skip rom
 *     buff[1] = 0xF0;  // read memory
 *     buff[2] = 0x20;  // from address 0x0020
 *     buff[3] = 0;
 *     if (write(fd, buff, 4) != 4)
 *         return;
 *
 *     // to read 8 bytes data from memory address 0x0020
 *     if (read(fd, buff, 8) != 8)
 *         return;
 * }
 *
 *
 * </example>
 *
 * @see @link open
 * @see @link na1wireOpen
 * @see @link read
 * @see @link write
 *
 * @external
 * @category BSP:Device_Drivers:1WIRE_API
 * @since 7.4
*ad*/
int na1wireWrite(int channel, void * buffer, int length, int * bytesWritten)
{
    volatile unsigned short *wrPtr;
    unsigned char *data = (unsigned char *)buffer;
    
    /* is channel maps to valid FIM? */    
    if ((unsigned)channel >= NA_IOP_MAX_PICS)   
        return EBADF;

    if (!(*NA_IOHUB_IFS_getAddress(channel) & NA_IOHUB_IFS_RX_FIFO_EMPTY))
        return EINTR;

    wrPtr = (unsigned short *)NA_IOHUB_DIRECT_MODE_TX_LAST_getAddress(channel);
    for (*bytesWritten = 0; *bytesWritten < length; (*bytesWritten)++)
    {
        while (*NA_IOHUB_IFS_getAddress(channel) & NA_IOHUB_IFS_TX_FIFO_FULL);
        *wrPtr = (*data++ << 8) | NA_1WIRE_WRITE; 
    }

    return SUCCESS;
}


