/*
 *  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
 *
 *
 * i2c_api.c - I2C driver APIs.
 *
 */

/************************************************/

/* I2C driver                                   */

/************************************************/

/*ad*
 * The I2C driver provides an application programming interface to system I2C hardware.
 *
 * The driver has two states: uninitialized and initialized.  An initial call must first 
 * be made to @link NAI2CInit which includes setup of the I2C mode (slave or master) and the 
 * I2C wire speed: Normal (100khz) or Fast (400Khz).  
 *
 * Once NAI2CInit() has been successfully called, calls can then be made to 
 * functions @link NAI2COperation which initiates the I2C read/write process on the I2C bus.
 *
 * For systems with extremely loaded I2C buses, please see function @link NAI2CSetSclDelay  
 * to adjust for speed.
 *
 * @external
 * @overview I2C
 * @category BSP:Device_Drivers:I2C_DEVICE_API
 * @include "i2c_api.h"
 * @platform    ns9360_a,ns9750_a,connectcore9c_a,connectcorewi9c_a,connectcore9p9360_a,connectme9210,connectcore9p9215_a
 * @library     bsp
 * @since 6.1
 *
 *
*ad*/

#include <stddef.h>
#include "errno.h"
#include "tx_api.h"
#include "ll_api.h"
#include "Npttypes.h"
#include "sysClockApi.h"
#include "i2c_def.h"
#include "i2c_driver.h"
#include "i2c_api.h"
#include "mc_isr.h"
// #include "bsp.h"
#include "gpiomux_def.h"
#include <bsp_api.h>

sem_t *sem;
#define I2C_ADDRESS_MASK          0x7ff  /* 0 to 10 for a 10 bit addrss and 1 bit direction */
#define GET_SEMA(x)  if (sem_trywait(x) != TX_SUCCESS) return EBUSY;
#define PUT_SEMA(x)  sem_post(x);


TX_EVENT_FLAGS_GROUP i2cHostEvent;
NA_I2C_MESSAGE_TYPE * i2cWrMsg, *i2cRdMsg;    
TX_SEMAPHORE  i2cHostSemaphore;

static void NAI2CSetBusFree(void);

#ifdef I2C_DEBUG

static void NAI2CSetMRead(void)
{
    narm_write_reg(NARM_I2C_TX_CMD_DATA_REG, NA_I2C_TX_CMD_DATA_ADDRESS, cmd, NA_I2C_M_READ);
    return;
}

static void NAI2CSetMWrite(void)
{
    narm_write_reg(NARM_I2C_TX_CMD_DATA_REG, NA_I2C_TX_CMD_DATA_ADDRESS, cmd, NA_I2C_M_WRITE);
    return;
}

static void NAI2CSetSStop(void)
{
    narm_write_reg(NARM_I2C_TX_CMD_DATA_REG, NA_I2C_TX_CMD_DATA_ADDRESS, cmd, NA_I2C_S_STOP);
    return;
}

#endif

static void NAI2CSetSclRef(unsigned int clk)
{
    if (clk > 511) 
        return;
    narm_write_reg(NARM_I2C_CONFIG_REG, NA_I2C_CONFIG_REG_ADDRESS , clk_ref, clk);
    return;
}

static void NAI2CSetTimingMode(unsigned int mode)
{
    narm_write_reg(NARM_I2C_CONFIG_REG, NA_I2C_CONFIG_REG_ADDRESS , timing_mode, mode);  
    return;
}


static void NAI2CSetGcaIrqEnable(unsigned int mode)
{
    narm_write_reg(NARM_I2C_SLAVE_ADDRESS_REG, NA_I2C_SLAVE_DEV_ADDRESS , s_gca_irq_en, mode);  
    return;
}


static void NAI2CSetBusFree(void)
{
    unsigned long i2cConfigureValue;
    i2cConfigureValue = * NA_I2C_CONFIG_REG_ADDRESS;
#if (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
    narm_write_reg(NA_SCM_RSR_REG, NA_SCM_RSR_ADDRESS, i2c, SCM_RSR_RESET_MODULE);
    tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(20));
    narm_write_reg(NA_SCM_RSR_REG, NA_SCM_RSR_ADDRESS, i2c, SCM_RSR_ENABLE_MODULE);
#else
    narm_write_reg(NA_BUM_MRR_REG, NA_BUM_MRR_ADDRESS, i2c, 0x1);
    tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(20));
    narm_write_reg(NA_BUM_MRR_REG, NA_BUM_MRR_ADDRESS, i2c, 0x0);
#endif
    * NA_I2C_CONFIG_REG_ADDRESS = i2cConfigureValue;
    tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(20));
    
    return;
}

/*
 * This routine is called to initialize the NET+OS I2C driver.
 * This initializes the I2C related register in the NS9750/NS9360 ASIC.
 *
 * external
 * category I2C_DEVICE_API
 * since 6.1
 * param               channel          i2c channel number
 *
 * return 0         Success
 *
 * include i2c_api.h
 * note This is called automatically by the NET+OS BSP, by turning on
 * the BSP_INCLUDE_I2C_DRIVER option in bspconfq.h
 */

int i2c_dev_init(int channel)
{
    NA_UNUSED_PARAM(channel);
    NAI2CInitialize (0);

    return 0;

}

/*
 * This routine is called to flush all buffers passed by the user
 * application to the I2C driver.
 * This function will return all read and write buffers back to the 
 * application by way of call back functions.
 *
 * category I2C_DEVICE_API
 * since 6.1
 *
 * return EBADF                Channel is not opened
 * return ENOENT               No more buffer to be flushed
 * return I2C_SUCCESS       Success
 * return I2C_FAIL             Failure
 *
 * include "i2c_api.h"
 */

int NAI2CFlushBuffer(void)
{
    int retValue= I2C_SUCCESS;
    
    
    NA_I2C_GLOBAL_STRUCT_TYPE *i2c;

    i2c = (NA_I2C_GLOBAL_STRUCT_TYPE *) sysI2CDeviceGet ();

    if (i2c == NULL)            /* Something wrong with the I2C device struct */
        return I2C_FAIL;

    if (i2c -> opened != TRUE)
    {
        retValue = EBADF;
        goto done;
    }

    if ((i2c->wrQueueMsgCount == 0) && (i2c->rdQueueMsgCount ==0))
    {
        retValue = ENOENT; 
        goto done;
    }


    if (i2c->state != BUS_IDLE)
    {
        if (i2c->ptrWrMsg != NULL)
        {
            i2c->ptrWrMsg->state = I2C_ABORT;
            i2c->wrQueue.current = 0;
            i2c->wrQueue.end = 0;
            i2c->wrChannel = 0;
            if (i2c->writeReadyProc != NULL)
            {
                i2c->writeReadyProc (i2c->ptrWrMsg);
            }
        }
        if (i2c->ptrRdMsg != NULL) 
    {
            i2c->rdQueue.current = 0;
            i2c->rdQueue.end = 0;
            i2c->rdChannel = 0;
            i2c->ptrRdMsg->state = I2C_ABORT;
            if (i2c->readReadyProc != NULL)
            {
                i2c->readReadyProc (i2c->ptrRdMsg);
            }
        }
        
        i2c->state = BUS_IDLE;
        i2c->ptrWrMsg = NULL;
        i2c->ptrRdMsg = NULL;
        i2c->rdQueueMsgCount = 0;
        i2c->wrQueueMsgCount = 0;
        if (i2c->mode == I2C_MASTER)
            NAI2CSetBusFree();                    
    }
  done:
    if ((retValue == LL_SUCCESS) || (retValue == I2C_SUCCESS))
        return I2C_SUCCESS;
    else 
        return retValue;   /* could be other error code */
}

/* 
 * This routine is called when the user application calls the open function.
 * This routine is used to open an i2c receive or transmit channel for an I2C Master 
 * device or Slave device.
 *
 * The prototype for this function call is 
 *      int open(const char* filename, int mode);
 *
 * Presently, the BSP is configured to open 
 *      "/i2c/0"
 *
 * The mode field specifies how a given file descriptor is opened.  The BSP accepts 
 * these modes for this device:
 *
 *      I2C_MASTER      Open the I2C device in a Master device mode. 
 *      I2C_SLAVE       Open the I2C device in a Slave device mode.  
 *
 * This routine first verifies that the correct mode is passed into the routine.
 * After the correct parameters are verified, this routine installs the interrupt
 * service function for the specified I2C device.
 *
 * @note This function should not be called directly.  It should be called by the 
 * open function call in the form of fd = open("/i2c/0", I2C_MASTER) or 
 * fd = open("/i2c/0", I2C_SLAVE);
 *
 * @external
 * @category BSP:Device_Drivers:I2C_DEVICE_API
 * @since 6.1
 * @deprecated 7.0
 * @param channel     This is the I2C port.
 *                    <list type="unordered">
 *                    <item> /i2c/0   I2C Device String  </item>
 *                    </list>
 * 
 *                 
 * @param mode        This is the mode which the I2C is opened to.
 *                    <list type="unordered">
 *                    <item> I2C_MASTER        Open the I2C as a master.  </item>
 *                    <item> I2C_SLAVE         Open the I2C as a slave.   </item>
 *                    </list>
 *
 *
 * @return EBUSY                Channel already opened
 * @return EINVAL               Bad mode
 * @return I2C_SUCCESS      Success
 * @return I2C_FAIL     Failure
 *
 * @see @link BSP:Device_Drivers:I2C_DEVICE_API
 */

int i2c_dev_open(int channel, unsigned int mode)
{

    NA_I2C_GLOBAL_STRUCT_TYPE *ptrI2CGlobalStruct;
    int retValue = I2C_SUCCESS;
    NA_UNUSED_PARAM(channel);
    ptrI2CGlobalStruct = (NA_I2C_GLOBAL_STRUCT_TYPE *) sysI2CDeviceGet ();

    if (ptrI2CGlobalStruct == NULL)
    {
        retValue = I2C_FAIL;
        goto done;
    }

    if ((mode & (I2C_MASTER | I2C_SLAVE)) == 0)
    {
        retValue = EINVAL;
        goto done;
    }
  
    if (ptrI2CGlobalStruct->opened != TRUE)
    {
        sem = sem_open("I2C_SEM", O_EXCL | O_CREAT, 0, 1);
        if (sem == 0)
        {
      retValue = EBUSY;
          goto done;
        }
        GET_SEMA(sem);
        ptrI2CGlobalStruct->sem = sem;
        ptrI2CGlobalStruct->opened = TRUE;
        ptrI2CGlobalStruct->mode = mode & (I2C_MASTER | I2C_SLAVE);
        PUT_SEMA(sem);
        goto done;
    }
    else 
    {
        retValue = EBUSY;
        goto done;
    }

    done:
        return retValue;
}

/*
 * This routine is called when the user application calls the close function.
 * The user application uses this routine to close an i2c receive or transmit 
 * channel for an I2C Master device or Slave device.
 *
 * The prototype for this function call is 
 *      int close(int file_descriptor);
 *
 * This routine flushes all existing buffers and returns them to the 
 * application by calling the call back function.  The user application 
 * should handle those buffers that are returned appropriatly.
 *
 * @external
 * @category BSP:Device_Drivers:I2C_DEVICE_API
 * @since 6.1
 * @deprecated 7.0
 *
 * @param channel     An integer value given by the open function call
 *
 *
 * @return EBADF                Channel is not opened
 * @return I2C_SUCCESS      Success
 * @return I2C_FAIL     Failure
 *
 * @see @link BSP:Device_Drivers:I2C_DEVICE_API
 **/
int i2c_dev_close(int channel)
{
    NA_I2C_GLOBAL_STRUCT_TYPE *ptrI2CGlobalStruct;
    int retValue = I2C_SUCCESS;
    NA_UNUSED_PARAM(channel);
    ptrI2CGlobalStruct = (NA_I2C_GLOBAL_STRUCT_TYPE *) sysI2CDeviceGet ();

    if (ptrI2CGlobalStruct == NULL)
    {    
        retValue = I2C_FAIL;
        goto done;
    }

    if (ptrI2CGlobalStruct->opened == TRUE)
    {
        GET_SEMA(ptrI2CGlobalStruct->sem);
        NAI2CFlushBuffer ();
        ptrI2CGlobalStruct->opened = FALSE;
        sem_close(ptrI2CGlobalStruct->sem);
        ptrI2CGlobalStruct->sem = NULL;
    }
    else
    {
        retValue = EBADF;
        goto done;
    }


    done:
       return retValue;

}

int i2c_dev_enter (int channel)
{
    NA_UNUSED_PARAM(channel);
    return 0;
}

static unsigned int NAI2CSclDelay = 0;

/*ad*
 * NAI2CSetSclDelay is used to adjust the i2c clock rate for the i2c bus 
 * loading.
 *
 * The I2C bus master generates the clock ( SCL ) for the bus. The clock 
 * is generated in 3 parts.
 * <list type="ordered">
 * <item> 
 *      Create the low portion of the waveform using the I2c clock reference
 *      and the BBUS clock (@link NAgetBbusClkFreq) then stop driving SCL low.
 * </item>
 * <item> 
 *      Wait for the SCL signal on the bus to rise from low to input threshold.
 *      This step is required to allow slaves to stretch the SCL by holding it 
 *      low.  Even when the slave does not extend the clk there can still be a 
 *      short delay due to the capacitance of the bus.
 * </item>
 * <item> 
 *      Create the high portion of the waveform using clk_ref & clk/4 .
 * </item>
 * </list>
 *
 *  Step 2 will take a longer period of time when the bus is more heavily loaded. 
 *  To account for this additional delay there is a variable in the SCL clock formula 
 *  called scl_delay.
 *
 *  The proceedure for setting scl_delay, using Normal mode (10 us I2C clock period --  
 *  100khz) and a CPU clock of 176.99 Mhz (BBUS clock = 22.6 ns), is as follows:
 *
 * <list type="ordered">
 * <item> 
 *      Using the default scl value (count set to 0), measure period of scl.
 *      [Suppose we measured a period of scl to be 10.309 us  (97 kHz)]
 * </item>
 * <item> 
 *      Subtract target period from measured period.  [Thus, we would calculate the 
 *      delta between measured and desired value: 10.309 us - 10 us = 309 ns]
 * </item>
 * <item> 
 *      Divide the scl period delta by the BBUS clock period.  [We then therefore  
 *      compute: (309ns / 22.6ns) = 13.67.]
 * </item>
 * <item> 
 *      Round up the result to a whole munber.  [13.67 becomes 14]
 * </item>
 * <item> 
 *      Set scl_delay using the rounded up value.  [Call NAI2CSetSclDelay(14) and 
 *      remeasure.]
 * </item>
 * </list>
 *
 * @external
 * @category BSP:Device_Drivers:I2C_DEVICE_API
 * @since 7.0
 *
 * @param count                 I2C Host operation retry number
 *    
 * @note This value defaults to zero. 
 *
 * This call must be made prior to a call to NAI2CInit to affect 
 * the clock rate.
 *
 * @see @link BSP:Device_Drivers:I2C_DEVICE_API:NAI2CGetSclDelay
 * @see @link BSP:Device_Drivers:I2C_DEVICE_API:NAI2COperation
 * @see @link BSP:Device_Drivers:I2C_DEVICE_API:NAI2CInit
 *ad*/
void NAI2CSetSclDelay(unsigned int count)
{
    NAI2CSclDelay = count;
}

/*ad*
 * NAI2CGetSclDelay is used to return the current value for SCL delay.  This
 * value is set via a call to @link NAI2CSetSclDelay.
 *
 * @external
 * @category BSP:Device_Drivers:I2C_DEVICE_API
 * @since 7.0
 *
 * @return count        Returns the current count used to adjust the i2c 
 *                      clock rate.
 *
 * @note This value defaults to zero. 
 * 
 * @see @link BSP:Device_Drivers:I2C_DEVICE_API:NAI2CSetSclDelay
 * @see @link BSP:Device_Drivers:I2C_DEVICE_API:NAI2COperation
 * @see @link BSP:Device_Drivers:I2C_DEVICE_API:NAI2CInit
 *
 *ad*/
unsigned int NAI2CGetSclDelay(void)
{
    return NAI2CSclDelay;
}

int getSclClk(int mode)
{
    int val = 250;
    unsigned int clockInput;
    
    #if ((PROCESSOR == ns9210) || (PROCESSOR == ns9215))
        clockInput = NAgetAhbClkFreq();
    #else
        clockInput = NAgetBbusClkFreq();
    #endif
    
    switch (mode) 
    {
    case I2C_NORMAL_SPEED:  /* normal speed */
        val = (((clockInput/I2C_NORMAL_SPEED_SETTING) - 4 - NAI2CSclDelay) / 2);
        break;
    case I2C_FAST_SPEED:  /* fast speed -- Corrected per Vantive 18632   */
        val = ((((clockInput/I2C_FAST_SPEED_SETTING) - 4 - NAI2CSclDelay) * 2) / 3);
        break;
    default: 
        break;       
    }

    return val;
}



/*
 * This routine is called when the user application calls the ioctl function.
 * This routine is used to set the read and write call back functions for the i2c
 * devices. This routine can also be used to set or to get various hardware 
 * parameters that are associated with the i2c Device in the NS9750/NS9360 ASIC.
 *
 *
 * @external
 * @category BSP:Device_Drivers:I2C_DEVICE_API
 * @since 6.1
 * @deprecated 7.0
 *
 * @param channel     An integer value returned by open;
 * @param request     An integer option that the routine wants to set.
 * <list type="unordered">
 * <item> I2C_SET_READ_CALLBACK_FUNC  
 *          Sets the read callback so that 
 *          when a read buffer is filled or processed, the buffer can be returned
 *          to the user application by this callback function in an ISR routine.  
 *
 *          args is a pointer to a function with a pointer to the 
 *          NA_I2C_MESSAGE_TYPE type data structure as the only 
 *          function parameter. 
 *          </item>
 *
 * <item> I2C_SET_WRITE_CALLBACK_FUNC  
 *          Sets the write callback function so that when a write
 *          buffer is processed, the buffer can be returned to the user 
 *          application by this callback function in an ISR routine.  
 *
 *          args is a pointer to a function with a pointer to the 
 *          NA_I2C_MESSAGE_TYPE type data structure as the only function 
 *          parameter.
 *          </item>
 *
 * <item> I2C_SET_CLOCK_SPEED
 *          Sets the clock speed to normal or fast mode.
 *       
 *          When the args is a pointer to an integer with a value of 0, the 
 *          I2C bus will be set to normal speed.  If args is a pointer to 
 *          an integer with value of 1, the I2C bus will be set to high 
 *          speed: 400KHz.
 *          </item>
 *
 * <item> I2C_SET_GCA_IRQ_ENABLE
 *          Enables or disables the general call address
 *          feature.  
 *
 *          When the args is a pointer to an integer with a value of 0, the 
 *          general call address is off.  When the args is a pointer to an
 *          integer with a value of 1, the general call address option is on.
 *          </item>
 *
 * <item> I2C_FLUSH_BUFFER
 *          Causes the I2C lower level device driver to return all user
 *          buffer back to the application through either read callback or
 *          write callback functions.  After a buffer is passed to the I2C 
 *          driver, the user application cannot modify this buffer until it is
 *          returned from a callback function.          
 *                
 *          The args is ignored in this option.
 *          </item>
 * </list>
 * @param args        A pointer to a value that the routine wants to get or set.
 *
 * @note  This function should not be called directly.  This function will be 
 * called when the ioctl system call is issued.
 *
 * @return EBADF                Channel does not exist.
 * @return I2C_SUCCESS      Success
 * @return I2C_FAIL     Failure
 *
 * @see @link BSP:Device_Drivers:I2C_DEVICE_API
 */

int i2c_dev_ioctl(int channel, int request, char * args)
{

    unsigned int mode, sclRef;
    int retValue = I2C_SUCCESS;
    
 
    NA_I2C_GLOBAL_STRUCT_TYPE *ptrI2CGlobalStruct;

    NA_UNUSED_PARAM(channel);
    ptrI2CGlobalStruct = (NA_I2C_GLOBAL_STRUCT_TYPE *) sysI2CDeviceGet ();

    if (ptrI2CGlobalStruct == NULL)
    {
        retValue = I2C_FAIL;
        goto done;
    }
  
    if (ptrI2CGlobalStruct -> opened != TRUE)
    {
        retValue = EBADF;
        goto done;
    }

    GET_SEMA(ptrI2CGlobalStruct->sem);

    switch (request)
    {
    case I2C_SET_READ_CALLBACK_FUNC:
        ptrI2CGlobalStruct->readReadyProc = (void (*)(NA_I2C_MESSAGE_TYPE *)) args;
        break;
    case I2C_SET_WRITE_CALLBACK_FUNC:
        ptrI2CGlobalStruct->writeReadyProc = (void (*)(NA_I2C_MESSAGE_TYPE *)) args;
        break;
#if I2C_SEPERATE_ADDRESS_AND_MODE
    case I2C_SET_M_ADDRESS_MODE:
        mode = *(unsigned int *) args;
        NA_ASSERT((mode == 0) || (mode ==1));
        NAI2CSetMasterAddrMode(mode);
        break;
    case I2C_SET_S_ADDRESS_MODE:
        mode = *(unsigned int *) args;
        NA_ASSERT((mode == 0) || (mode ==1));
        NAI2CSetSlaveAddrMode(mode);
        break;
#endif
    case I2C_SET_CLOCK_SPEED:
        mode = *(unsigned int *) args;
        if (mode == 0)          /* normal mode */
        {
            NAI2CSetTimingMode (mode);
            sclRef = getSclClk(mode);
            NAI2CSetSclRef (sclRef);
        }
        if (mode == 1)          /* fast mode   */
        {
            NAI2CSetTimingMode (mode);
            sclRef = getSclClk(mode);
            NAI2CSetSclRef (sclRef);
        }
        break;
    case I2C_SET_GCA_IRQ_ENABLE:
        mode = *(unsigned int *) args;
        NA_ASSERT((mode == 0) || (mode == 1));
        NAI2CSetGcaIrqEnable(mode);
        break;
    case I2C_FLUSH_BUFFER:
        NAI2CFlushBuffer();
        break;
    default:
        break;
    }
    PUT_SEMA(ptrI2CGlobalStruct -> sem)
  
    done:
        return retValue;
}


static int NAI2CSetDataBuffer (I2C_MSG_TYPE msgType, NA_I2C_MESSAGE_TYPE * data)
{
    int retValue = 0;
    
    NA_I2C_GLOBAL_STRUCT_TYPE *ptrI2CGlobalStruct;


    ptrI2CGlobalStruct = (NA_I2C_GLOBAL_STRUCT_TYPE *) sysI2CDeviceGet ();

    if ((ptrI2CGlobalStruct == NULL) || (data == NULL))
        return I2C_FAIL;


    if (msgType == I2C_SEND_BUFFER_TYPE)
    {
        if (ptrI2CGlobalStruct->ptrWrMsg == NULL)
        {
            ptrI2CGlobalStruct->ptrWrMsg = data;
        }
        else
        {
            NAI2CFlushBuffer();
            retValue =  I2C_FAIL;
    }
    }
    if (msgType == I2C_READ_BUFFER_TYPE)
    {
        if (ptrI2CGlobalStruct->ptrRdMsg == NULL)
        {
            ptrI2CGlobalStruct->ptrRdMsg = data;        
        }
        else
        {
            NAI2CFlushBuffer();
            retValue =  I2C_FAIL;
        }
    }

    return retValue;
}
/*
 * This routine is called when the user application calls write function.
 * The user application uses this routine to pass a I2C message to the 
 * lower driver.   
 * 
 * The write function returns immediately with the I2C message being put into
 * the send buffer queue.  When this message is sent completely or is terminated,
 * the parameter in the message is updated and the message is  returned
 * to the application by calling the write call back function.
 *
 * A user application can only write one message at a time and this is changed
 * from previous version of APIs.
 *
 * When the I2C_FAIL is received, the user application can 
 * immediately reuse the data buffer which is associated with that i2cMsg; otherwise
 * the buffer can be used after it is returned from the write callback function.
 *
 *
 * @external
 * @category BSP:Device_Drivers:I2C_DEVICE_API
 * @since 6.1
 * @deprecated 7.0
 *
 * @param channel     An integer value returned by open
 * @param i2cMsg      A pointer to the i2cMsg type
 * @param length      Not used
 * @param szWritten   Not used
 *
 * @note  This function should not be called directly.  It should be called 
 *        by the write system call in the form of 
 *        write(fd, NA_I2C_MESSAGE_TYPE * i2cMsg, 0);
 *
 * @return EBADF                Channel is not opened
 * @return I2C_SUCCESS      Success
 * @return I2C_FAIL     Failure
 *
 * @see @link BSP:Device_Drivers:I2C_DEVICE_API
 */

int i2c_dev_write(int channel, void * i2cMsg, int length, int * szWritten)
{
    int retValue;
 
    NA_I2C_GLOBAL_STRUCT_TYPE *ptrI2CGlobalStruct;

    NA_UNUSED_PARAM(channel);
    NA_UNUSED_PARAM(length);
    NA_UNUSED_PARAM(szWritten);
    /* Create a structure.  Needs to be released later. */

    ptrI2CGlobalStruct = (NA_I2C_GLOBAL_STRUCT_TYPE *) sysI2CDeviceGet ();

    if (ptrI2CGlobalStruct == NULL)     /* Something wrong with the I2C device struct */
        return I2C_FAIL;

    GET_SEMA(ptrI2CGlobalStruct->sem);


    if (ptrI2CGlobalStruct -> opened != TRUE)
    {
        retValue = EBADF;
        goto done;
    }

    if ((retValue = NAI2CSetDataBuffer (I2C_SEND_BUFFER_TYPE, i2cMsg)) != I2C_SUCCESS)
    {
#ifdef I2C_DEBUG
        printf("NAI2CSetDataBuffer from write() failed.\n");
#endif
        goto done;
    }
    else if ((ptrI2CGlobalStruct->state == BUS_IDLE) && (ptrI2CGlobalStruct->mode == I2C_MASTER))
        NAI2CKickStartTx ();
    else if (ptrI2CGlobalStruct->mode == I2C_SLAVE)
    {
            NAI2CPopSlaveTxMsg();   /* MCI2CSlaveKickStartTx (); */
        }

    ptrI2CGlobalStruct->wrQueueMsgCount++;
  done:
    PUT_SEMA(ptrI2CGlobalStruct->sem);
    return retValue;

}

/*
 * This routine is called when the user application calls read function.
 * The user application uses this routine to receive a I2C message from the 
 * low level I2C driver.   
 * 
 * The read function returns immediately with the I2C message buffer being put into
 * the receive buffer queue.  When the message is received completely or is 
 * terminated, the parameter in the message is updated and the message is returned
 * to the application by calling the read call back function.
 *
 * A user application can only read one message at a time and this is different 
 * from the NETOS 6.1.
 * When the I2C_FAIL is received, the user application can immediately reuse the 
 * data buffer which is associated with that i2cMsg; otherwise the buffer can be 
 * used after it is returned from the read callback function.
 *
 *
 * @external
 * @category BSP:Device_Drivers:I2C_DEVICE_API
 * @since 6.1
 * @deprecated 7.0
 *
 * @param channel     An integer value returned by open
 * @param i2cMsg      A pointer to the i2cMsg type for data to be stored
 * @param length      Not used
 * @param szRead      Not used
 *
 *
 * @note  This function should not be called directly.  It should be called 
 *        by the read system call in the form of 
 *        read(fd, NA_I2C_MESSAGE_TYPE * i2cMsg, 0);
 *
 * @return I2C_SUCCESS      Success
 * @return I2C_FAIL         Failure
 *
 * @see @link BSP:Device_Drivers:I2C_DEVICE_API
 */

int i2c_dev_read(int channel, void * i2cMsg, int length, int * szRead)
{
    int retValue;

    NA_I2C_GLOBAL_STRUCT_TYPE *ptrI2CGlobalStruct;

    NA_UNUSED_PARAM(channel);
    NA_UNUSED_PARAM(length);
    NA_UNUSED_PARAM(szRead);
    ptrI2CGlobalStruct = (NA_I2C_GLOBAL_STRUCT_TYPE *) sysI2CDeviceGet ();

    if (ptrI2CGlobalStruct == NULL)     /* Something wrong with the I2C device struct */
        return I2C_FAIL;


    GET_SEMA(ptrI2CGlobalStruct->sem);

    if (ptrI2CGlobalStruct -> opened != TRUE)
    {
        retValue = EBADF;
        goto done;
    }

    if ((retValue = NAI2CSetDataBuffer (I2C_READ_BUFFER_TYPE, i2cMsg)) != I2C_SUCCESS)
    {
#ifdef I2C_DEBUG
        printf("NAI2CSetDataBuffer from read() failed.\n");
#endif
        goto done;
    }
    else if ((ptrI2CGlobalStruct->state == BUS_IDLE) && (ptrI2CGlobalStruct->mode == I2C_MASTER))
        NAI2CKickStartRx (I2C_DATA_NONAVAILABLE, 0);
    else if (ptrI2CGlobalStruct->mode == I2C_SLAVE)
        NAI2CSlaveKickStartRx (I2C_DATA_NONAVAILABLE, 0);

    ptrI2CGlobalStruct->rdQueueMsgCount++;
 
    done:
        PUT_SEMA(ptrI2CGlobalStruct->sem); 
        return retValue;

}

/*
 * This routine is used to construct a I2C message based on the character buffer 
 * pointer, an address in which the message is for, and the length of the buffer.
 *
 * After this message is constructed, it can be pass to the I2C driver through
 * the read and write functions.
 *
 *
 *
 * @external
 * @category BSP:Device_Drivers:I2C_DEVICE_API
 * @since 6.2
 *
 * @param i2cMsg      A pointer to a i2cMsg
 * @param buf         A pointer to a character buffer that the message uses
 * @param bufLength   The length of the character buffer
 * @param addr        The address for the I2C Master or slave in which the message 
 *                    is sent or received
 *
 * @see @link BSP:Device_Drivers:I2C_DEVICE_API
 */

void NAI2CBuildMsg(NA_I2C_MESSAGE_TYPE * i2cMsg, char * buf, unsigned long bufLength, unsigned short addr)
{
    if ((i2cMsg == NULL) || (buf == NULL))
        return;
    i2cMsg->buffer = buf;
    i2cMsg->address = (unsigned short)(addr & I2C_ADDRESS_MASK);
    i2cMsg->bufLength = bufLength;
    i2cMsg->availableDataLen = 0;
    i2cMsg->state = I2C_INIT;
    return;
}



void i2cHostWriteCallBackFunc(NA_I2C_MESSAGE_TYPE * ptrData)
{
    if (ptrData->state == I2C_PROCESSED)
    {
        tx_event_flags_set(&i2cHostEvent, I2C_WRITE_EVENT_FLAG, TX_OR);
    } 
    else 
        tx_event_flags_set(&i2cHostEvent, I2C_WRITE_ABORT_EVENT_FLAG, TX_OR);
    
    return;
}

void i2cHostReadCallBackFunc(NA_I2C_MESSAGE_TYPE * ptrData)
{
    if (ptrData->state == I2C_PROCESSED)
    {
        tx_event_flags_set(&i2cHostEvent, I2C_READ_EVENT_FLAG, TX_OR);
    }
    else
        tx_event_flags_set(&i2cHostEvent, I2C_READ_ABORT_EVENT_FLAG, TX_OR);

    return;
}

#if NS9360
/* Function sends the clock to flush the slave data out. This will not affect
   any other devices on the line, because master is not pointing to any address.
   When this function is called with stop parameter set, the function sends 
   the stop bit to the SDA line. 
*/
static int i2c_send_clock_pulse (unsigned int scl, unsigned int sdl, int stop)
{
    volatile unsigned int count;

    if (NAsetGPIOpin(scl, 1))
        return 1;
    for (count=0; count<0x100; count++);

    if (NAsetGPIOpin(scl, 0))
        return 1;
    for (count=0; count<0x100; count++);
    /* prepare to send stop */
    if (stop)
    {        
        if (NAconfigureGPIOpin(sdl, NA_GPIO_OUTPUT_STATE, 0))
            return 1;
    }
    for (count=0; count<0x100; count++);

    if (NAsetGPIOpin(scl, 1))
        return 1;
    for (count=0; count<0x100; count++);
    if (stop)
    {
        /* send stop */
        if (NAsetGPIOpin(sdl, 1))
           return 1;
    }

    return 0;
}

/* Function helps to come out of I2C lock at startup due to sdl line being 
   at logical low. This can be cause by CPU reset or I2C clock halt while 
   slave is transferring the data. Now slave waits for CPU clock from the 
   master and at startup master waits for the SDL line to be high to start 
   transmision. This will cause dead lock. 
   
   The recovery sequence here is
   1. Master detects the SDL line is low
   2. Sends one clock pulse (1-0-1)
   3. Checks the SDL line whether it is high, if it is still low then jumps to
      step 2
   4. If SDL is high, then master sends the STOP command. This will unlock the 
      line. 
*/
static void i2c_is_bus_locked(void)
{
    unsigned int state_orig, state;
    unsigned int scl_line, sda_line;

    /* check which pin is configured for SCL and SDL */
    if (NAgetGPIOpinConfig(I2C_GPIO_SCL_PRIMARY, &state_orig))
        return;
    else
    {
        if (state_orig == NA_GPIO_FUNC0_STATE)
        {
            scl_line = I2C_GPIO_SCL_PRIMARY;
            sda_line = I2C_GPIO_SDA_PRIMARY;    
        }
        else
        {
            if (NAgetGPIOpinConfig(I2C_GPIO_SCL_ALT, &state_orig))
                return;
            else
            {
                if (state_orig == NA_GPIO_FUNC2_STATE)
                {
                    scl_line = I2C_GPIO_SCL_ALT;
                    sda_line = I2C_GPIO_SDA_ALT;    
                }
                else /* not configured */
                    return;
            }
        }
    }

    if (NAconfigureGPIOpin(sda_line, NA_GPIO_INPUT_STATE, 0))
        goto bus_lock_done;

    /* is the SDA line high? */
    if (NAgetGPIOpin(sda_line, &state))
        goto bus_lock_done;

    if (state)  /* line is high, slave finished accessing the line */
        goto bus_lock_done;

    if (NAconfigureGPIOpin(scl_line, NA_GPIO_OUTPUT_STATE, 1))
        goto bus_lock_done;

#ifdef ANALOG_DEVICE_SOLUTION /* AN-686 */
    while (state == 0) 
    {
        /* send a clock pulse on scl */
        if (i2c_send_clock_pulse(scl_line, sda_line, 0))
            goto bus_lock_done;

        /* is the SDA line high? */
        if (NAgetGPIOpin(sda_line, &state))
            goto bus_lock_done;
    }
#else /* PHILIPS_SOLUTION AN10216-01 */
    /* send 9 clock pulse */
    for (state = 0; state < 9; state++)
    {
        /* send a clock pulse on scl */
        if (i2c_send_clock_pulse(scl_line, sda_line, 0))
            goto bus_lock_done;
    }            
#endif

    /* send stop */    
    if (i2c_send_clock_pulse(scl_line, sda_line, 1))
       goto bus_lock_done;

    bus_lock_done:
    /* put the GPIO pins back to I2C function mode */
    NAconfigureGPIOpin(scl_line, state_orig, 0);
    NAconfigureGPIOpin(sda_line, state_orig, 0);
}
#endif

/*ad*
 * Used to initialize the I2C hardware.  Sets up the hardware as an I2C master or slave, and 
 * if setup as a master, sets up the i2c bus clock to run either in a normal mode (100 Khz)
 * or a fast mode (400 Khz).
 *
 * After calling this function, applications can perform I2C sending and receiving using
 * the @link NAI2COperation call.
 *
 * Note that since release 6.3 this function replaces prior use of the
 * function i2c_dev_init.
 *
 * @external
 * @category BSP:Device_Drivers:I2C_DEVICE_API
 * @since 6.2
 *
 * @param masterMode  Initialize the I2C module as a host or slave.  (See @link I2CInitMode for details.)
 *                    <list type="unordered">
 *                    <item> I2C_MASTER
 *                                              Setup I2C hardware for I2C master mode  </item>
 *                    <item> I2C_SLAVE
 *                                              Setup I2C hardware for I2C slave mode   </item>
 *                    </list>
 *
 * @param speedMode   Initialize the I2C bus speed.  (See @link I2CInitSpeed for details)
 *                    <list type="unordered">
 *                    <item> I2C_NORMAL_SPEED       Setup the I2C master clock to normal mode at 100 Khz.  </item>
 *                    <item> I2C_FAST_SPEED         Setup the I2C master clock to run fast at 400 Khz. </item>
 *                    </list>
 *
 *
 * @return   I2C_ERROR_CREATING_SEMAPHORE    cannot create I2C host SEMAPHORE.
 * @return   I2C_ERROR_MALLOC_BUFFER         cannot create buffer needed for the API.
 * @return   I2C_ERROR_CREATING_EVENT        cannot create the EVENT control.
 * @return   I2C_FAIL                        cannot open I2C port for the host.
 * @return   I2C_SUCCESS                     success.
 *
 *
 * @note The I2C bus master generates the clock (SCL) for the bus. See function @link NAI2CSetSclDelay for
 * more details on adjusting the I2C clock on heavily loaded buses.
 *
 *
 * <bold>Sample usage:</bold>
 *
 * <example>
 * #include "i2c_api.h"
 * 
 * void someI2cFunction (void)
 * {
 *  int ret;
 *  unsigned int writeBufLen = 2;
 *  char writeBuffer[4] = {0x40, 0x88, 0, 0};
 * 
 *  ret = NAI2CInit(I2C_MASTER, I2C_NORMAL_SPEED);
 *  if (ret != I2C_SUCCESS)
 *  {
 *      return;
 *  }
 *
 *  ret = NAI2COperation(0x40, writeBuffer, &writeBufLen, NULL, NULL, 10, I2C_HOST_WRITE_ONCE);
 *  if (ret != I2C_SUCCESS)
 *  {
 *      return;
 *  }
 * }
 *
 * </example>
 *
 * @see @link BSP:Device_Drivers:I2C_DEVICE_API
 * @see @link I2CInitMode
 * @see @link I2CInitSpeed
 * @see @link NAI2CGetSclDelay
 * @see @link NAI2CSetSclDelay
*ad*/


int NAI2CInit(unsigned int masterMode, unsigned int speedMode )
{
    unsigned int sclRef;
    int i2cPort;
    int ret = I2C_SUCCESS ;
    unsigned int status;
    NA_I2C_GLOBAL_STRUCT_TYPE *ptrI2CGlobalStruct;

    /* enable the clock to this module */
    narm_write_reg(NA_SCM_CCR_REG, NA_SCM_CCR_ADDRESS, i2c, SCM_CCR_ENABLE_CLOCK); 
    narm_write_reg(NA_SCM_RSR_REG, NA_SCM_RSR_ADDRESS, i2c, SCM_RSR_ENABLE_MODULE); 

    status = tx_semaphore_create(&i2cHostSemaphore, "I2C_HOST_SEMAPHORE", 1);
    if (status != TX_SUCCESS)
    { 
        ret = I2C_ERROR_MALLOC_BUFFER;
        goto initEnd;
    }

    ptrI2CGlobalStruct = (NA_I2C_GLOBAL_STRUCT_TYPE *) sysI2CDeviceGet ();

#if NS9360
    /* Check if bus is locked and requires recovery */
    if (masterMode == I2C_MASTER)
        i2c_is_bus_locked();
#endif

    NAI2CInitialize(speedMode);

    i2cWrMsg = malloc(sizeof (NA_I2C_MESSAGE_TYPE));

    i2cRdMsg = malloc(sizeof (NA_I2C_MESSAGE_TYPE));

    NA_ASSERT((i2cWrMsg != NULL) && (i2cRdMsg != NULL));

    if ((i2cWrMsg == NULL) || (i2cRdMsg == NULL))
    {
        ret = I2C_ERROR_MALLOC_BUFFER;
        goto initEnd;
    }

    if (tx_event_flags_create(&i2cHostEvent, "i2cHostEvent") != TX_SUCCESS)
    {
        ret = I2C_ERROR_CREATING_EVENT;
        goto  initEnd;
    }

    i2cPort = i2c_dev_open(0, masterMode);

    if (i2cPort != I2C_SUCCESS)
    {
        printf("I2C open failed.\n");
        ret = I2C_FAIL;
        goto initEnd;
    }

    ptrI2CGlobalStruct->readReadyProc = (void (*)(NA_I2C_MESSAGE_TYPE *)) i2cHostReadCallBackFunc;
    ptrI2CGlobalStruct->writeReadyProc = (void (*)(NA_I2C_MESSAGE_TYPE *)) i2cHostWriteCallBackFunc;

    if ((speedMode == I2C_NORMAL_SPEED) || (speedMode == I2C_FAST_SPEED))
    {
        NAI2CSetTimingMode (speedMode);
        sclRef = getSclClk(speedMode);
        NAI2CSetSclRef (sclRef);
    }

    initEnd:
        return ret;
}


/*ad*
 *
 * Perform an I2C data read/write operation.  This function can be called 
 * after @link NAI2CInit initializes I2C hardware.  The function returns
 * after the operation completes or the timeout expires.
 *
 * Note that since release 6.3 this function replaces prior use of the
 * function i2c_dev_read and i2c_dev_write.
 *
 * @external
 * @category BSP:Device_Drivers:I2C_DEVICE_API
 * @since 6.2
 *
 * @param address              Address of the I2C device. This is the address that is already 
 *                             shifted 1 bit to the left with 0 at the 0 bit position.  In the 
 *                             7-bit address mode, the 0 bit is reserved for R/W.  For example,
 *                             to write to 0x21, you must use address 0x42 - which is 0x21 
 *                             moved over by 1 bit.  For the 10-bit address, the same rule applies.
 *                           
 * @param writeBuffer          A pointer to the send buffer.
 * @param writeBufferLength    A pointer to the length of the writeBuffer.  This value will
 *                             be updated after data is sent.
 * @param readBuffer           A pointer to the receive buffer.
 * @param readBufferLength     A pointer to the length of the readBuffer.  This value will be
 *                             updated after the data is received.
 * @param timeOut              Time out value in system ticks.  (For more inforamtion on system ticks, 
 *                             See @link NABspTicksPerSecond and @link BSP_TICKS_PER_SECOND.)
 *                             <list type="unordered">
 *                                  <item> 0xFFFFFFFF        Wait forever  </item>
 *                                  <item> 0x0               No Wait       </item>
 *                                  <item> 0x00000001 through 0xFFFFFFFE   </item>
 *                             </list>
 * 
 * @param flag                 I2C Host operation options.  (See @link I2Coperationflags for details.)
 *                             <list type="unordered">
 *                                  <item>  I2C_HOST_WRITE_ONCE      Host send once    </item>
 *                                  <item>  I2C_HOST_READ_ONCE       Host receive once </item>
 *                                  <item>  I2C_HOST_WRITE_THEN_READ Host first sends a data packet and then
 *                                                                   receives a data packet from a device. </item>
 *                                  <item>  I2C_HOST_READ_THEN_WRITE Host first receives a data packet and then
 *                                                                   sends a data packet to the device. </item>
 *                             </list>
 *    
 * @return     I2C_TIMEOUT          send or receive failed due to timeout.
 * @return     I2C_FAIL             operation failed.
 * @return     I2C_SUCCESS          operation successful.
 *
 *
 * @note 
 *
 * <bold>Sample usage:</bold>
 *
 * <example>
 * #include "i2c_api.h"
 * 
 * void someI2cFunction (void)
 * {
 *  int ret;
 *  unsigned int writeBufLen = 2;
 *  char writeBuffer[4] = {0x40, 0x88, 0, 0};
 * 
 *  ret = NAI2CInit(I2C_MASTER, I2C_NORMAL_SPEED);
 *  if (ret != I2C_SUCCESS)
 *  {
 *      return;
 *  }
 *
 *  ret = NAI2COperation(0x40, writeBuffer, &writeBufLen, NULL, NULL, 10, I2C_HOST_WRITE_ONCE);
 *  if (ret != I2C_SUCCESS)
 *  {
 *      return;
 *  }
 * }
 *
 * </example>
 *
 * @see @link NAI2CInit
 * @see @link I2Coperationflags
 * @see @link NABspTicksPerSecond
 * @see @link BSP_TICKS_PER_SECOND
*ad*/
int NAI2COperation(unsigned int address, char * writeBuffer, unsigned int * writeBufferLength, char * readBuffer,  unsigned int  * readBufferLength, unsigned int timeOut, unsigned int flag)
{
    int ret = I2C_SUCCESS, eventRet = -1, notUsed = 0;
    unsigned long volatile status;
    unsigned long actualEvent;
    static int isrNotInstall = 1;

    if (tx_semaphore_get(&i2cHostSemaphore, timeOut) != TX_SUCCESS)
    {
        ret = I2C_TIMEOUT;
        return ret;    /* have to do it here because no semaphore is created. */
    }

    if ((flag == I2C_HOST_WRITE_ONCE) || (flag == I2C_HOST_WRITE_THEN_READ) || (flag == I2C_HOST_READ_THEN_WRITE))
    {
        NA_ASSERT((writeBuffer != NULL) && (writeBufferLength != NULL));
        if ((writeBuffer == NULL) || (writeBufferLength == NULL))
        {
            ret = I2C_FAIL;
            goto I2C_END;
        }
    }

    if ((flag == I2C_HOST_READ_ONCE) || (flag == I2C_HOST_WRITE_THEN_READ) || (flag == I2C_HOST_READ_THEN_WRITE))
    {
        NA_ASSERT((readBuffer != NULL) && (readBufferLength != NULL));
        if ((readBuffer == NULL) || (readBufferLength == NULL))
        {
            ret = I2C_FAIL;
            goto I2C_END;
        }
    }

    if (isrNotInstall)
    {
        /* clear any pending interrupt */
        status = *NA_I2C_RX_STATUS_DATA_ADDRESS;
                
        ret = NAInstallIsr (I2C_INTERRUPT, (MC_ISR_HANDLER) NAI2CInterrupt, (void *) 0);
        if (ret == SUCCESS)
            isrNotInstall = 0;
        else
        {
            ret = I2C_FAIL;
            goto I2C_END;
        }

        /* enable interrrupt now */
        narm_write_reg(NARM_I2C_CONFIG_REG, NA_I2C_CONFIG_REG_ADDRESS, irq_dis, 0);
    }
     
    /* clear the event flag first */        
    tx_event_flags_get(&i2cHostEvent, I2C_WRITE_ABORT_EVENT_FLAG|I2C_WRITE_EVENT_FLAG|
                       I2C_READ_ABORT_EVENT_FLAG|I2C_READ_EVENT_FLAG, TX_OR_CLEAR, 
                       &actualEvent, TX_NO_WAIT );

    actualEvent = 0;

    switch (flag)
    {
        case I2C_HOST_WRITE_ONCE:
            NAI2CBuildMsg(i2cWrMsg, writeBuffer, *writeBufferLength, address);
            actualEvent = 0;
            ret = i2c_dev_write(0, i2cWrMsg, 0, &notUsed );
            if (ret != I2C_SUCCESS)
                goto I2C_END;
            eventRet = tx_event_flags_get(&i2cHostEvent, I2C_WRITE_ABORT_EVENT_FLAG|I2C_WRITE_EVENT_FLAG, TX_OR_CLEAR, &actualEvent, timeOut );
            break;
        
        case I2C_HOST_READ_ONCE:
            NAI2CBuildMsg(i2cRdMsg, readBuffer, *readBufferLength, address);
            actualEvent = 0;
            ret = i2c_dev_read(0 , i2cRdMsg, 0, &notUsed);
            if (ret != I2C_SUCCESS)
                goto I2C_END;
            eventRet = tx_event_flags_get(&i2cHostEvent, I2C_READ_ABORT_EVENT_FLAG|I2C_READ_EVENT_FLAG, TX_OR_CLEAR, &actualEvent, timeOut );
            break;
        
        case I2C_HOST_WRITE_THEN_READ:
            NAI2CBuildMsg(i2cWrMsg, writeBuffer, *writeBufferLength, address);
            NAI2CBuildMsg(i2cRdMsg, readBuffer, *readBufferLength, address);
            actualEvent = 0;
            ret = i2c_dev_write(0, i2cWrMsg, 0, &notUsed );
            if (ret != I2C_SUCCESS)
                goto I2C_END;
            ret = i2c_dev_read(0 , i2cRdMsg, 0, &notUsed);
            if (ret != I2C_SUCCESS)
                goto I2C_END;
            eventRet = tx_event_flags_get(&i2cHostEvent, I2C_WRITE_EVENT_FLAG|I2C_READ_EVENT_FLAG, TX_AND_CLEAR, &actualEvent, timeOut);
            /* ABORT event flags are left out for timeout condition. */
            break;
        
        case I2C_HOST_READ_THEN_WRITE:
            NAI2CBuildMsg(i2cRdMsg, readBuffer, *readBufferLength, address);
            actualEvent = 0;
            ret = i2c_dev_read(0 , i2cRdMsg, 0, &notUsed);
            if (ret != I2C_SUCCESS)
                goto I2C_END;

            NAI2CBuildMsg(i2cWrMsg, writeBuffer, *writeBufferLength, address);
            ret = i2c_dev_write(0, i2cWrMsg, 0, &notUsed );
            if (ret != I2C_SUCCESS)
                goto I2C_END;
            eventRet = tx_event_flags_get(&i2cHostEvent, I2C_WRITE_EVENT_FLAG|I2C_READ_EVENT_FLAG, TX_AND_CLEAR, &actualEvent, timeOut);
            /* ABORT event flags are left out for timeout condition. */
            break;      
        
        default:
            break;
    }

    if (eventRet == TX_SUCCESS)
    {
        if (actualEvent & I2C_WRITE_EVENT_FLAG)
        {
#if defined (I2C_DEBUG)
            printf("i2cHostWrMsg wr 0x%x %dB\n", i2cWrMsg->address, 
                   (int)i2cWrMsg->availableDataLen    );
#endif
            *writeBufferLength = (unsigned int)i2cWrMsg->availableDataLen; 
        }
     
        if (actualEvent & I2C_READ_EVENT_FLAG)
        {
#if defined (I2C_DEBUG)
            printf("i2cHostRdMsg wr 0x%x %dB\n", i2cRdMsg->address, 
                  (int)i2cRdMsg->availableDataLen     );
#endif
            *readBufferLength = (unsigned int)i2cRdMsg->availableDataLen ;
        }
        
        if ((actualEvent & I2C_READ_ABORT_EVENT_FLAG) || 
           (actualEvent & I2C_WRITE_ABORT_EVENT_FLAG))
            ret = I2C_FAIL;
        else
            ret = I2C_SUCCESS;
    }
    else
    {
#if defined (I2C_DEBUG)
        printf("I2C event receiving failed.\n");
#endif
        NAI2CFlushBuffer();
        ret = I2C_FAIL;
    }

    I2C_END:
        tx_semaphore_put(&i2cHostSemaphore);
        return ret;
}

  
