/*
 * I2C routines of Cypress USB Serial 
 * Copyright (C) 2013 Cypress Semiconductor
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "CyUSBCommon.h"
#pragma pack(1)
typedef struct
{
    UINT32 frequency;           /* Frequency of operation. Only valid values are
                                   100KHz and 400KHz. */
    UINT8 sAddress;             /* Slave address to be used when in slave mode. */
    BOOL isMsbFirst;            /* Whether to transmit most significant bit first. */
    BOOL isMaster;              /* Whether to block is to be configured as a master:
                                   CyTrue - The block functions as I2C master;
                                   CyFalse - The block functions as I2C slave. */
    BOOL sIgnore;               /* Ignore general call in slave mode. */
    BOOL clockStretch;          /* Wheteher to stretch clock in case of no FIFO availability. */
    BOOL isLoopback;            /* Whether to loop back TX data to RX. Valid only
                                   for debug purposes. */
    UCHAR reserved[6];          /*Reserved for future use*/
} CyUsI2cConfig_t;
#pragma pack()
#ifdef CY_I2C_ENABLE_PRECISE_TIMING
struct timeval startTimeWrite, endTimeWrite, startTimeRead, endTimeRead;
//Timer helper functions for proper timing 
void startI2cTick (bool isWrite) {
    if (isWrite)
        gettimeofday (&startTimeWrite, NULL);
    else 
        gettimeofday (&startTimeRead, NULL);   
}

UINT32 getI2cLapsedTime (bool isWrite){

    signed int currentTime_sec, currentTime_usec, currentTime;
    if (isWrite){
        gettimeofday (&endTimeWrite, NULL);
        currentTime_sec = (endTimeWrite.tv_sec - startTimeWrite.tv_sec) * 1000;
        currentTime_usec = ((endTimeWrite.tv_usec - startTimeWrite.tv_usec)) / 1000;
        currentTime = currentTime_sec + currentTime_usec;
        return (unsigned int)currentTime;
    }
    else{
        gettimeofday (&endTimeRead, NULL);
        currentTime_sec = (endTimeRead.tv_sec - startTimeRead.tv_sec) * 1000;
        currentTime_usec = ((endTimeRead.tv_usec - startTimeRead.tv_usec)) / 1000;
        currentTime = currentTime_sec + currentTime_usec;
        return (unsigned int)currentTime;
    }
}
#endif
CY_RETURN_STATUS handleI2cError (UINT8 i2cStatus){

    if (i2cStatus & CY_I2C_NAK_ERROR_BIT){
        CY_DEBUG_PRINT_ERROR ("CY:Error Nacked by device ...Function is %s\n", __func__);   
        return CY_ERROR_I2C_NAK_ERROR;
    }
    if (i2cStatus & CY_I2C_BUS_ERROR_BIT){
        CY_DEBUG_PRINT_ERROR ("CY:Error bus error occured... Function is %s\n", __func__);   
        return CY_ERROR_I2C_BUS_ERROR;
    }
    if (i2cStatus & CY_I2C_ARBITRATION_ERROR_BIT){
        CY_DEBUG_PRINT_ERROR ("CY:Error Arbitration error occured.. Function is %s\n", __func__);   
        return CY_ERROR_I2C_ARBITRATION_ERROR;
    }
    if (i2cStatus & CY_I2C_STOP_BIT_ERROR){
        CY_DEBUG_PRINT_ERROR ("CY:Error Stop bit set by master..Function is %s\n", __func__);   
        return CY_ERROR_I2C_STOP_BIT_SET;
    }
    else {
        //We should never hit this case!!!!
        CY_DEBUG_PRINT_ERROR ("CY:Unknown error..Function is %s\n", __func__);   
        return CY_ERROR_REQUEST_FAILED;
    }
}
//Used internally by read and write API to check if data is received at the I2C end.
CY_RETURN_STATUS CyI2cGetStatus (CY_HANDLE handle, bool mode, UCHAR *i2cStatus);
CY_RETURN_STATUS waitForNotification (CY_HANDLE handle, UINT16 *bytesPending, UINT32 ioTimeout);
/*
   This API gets the current I2C config
   for the particluar interface of the device
 */
CY_RETURN_STATUS CyGetI2cConfig (
        CY_HANDLE handle,
        CY_I2C_CONFIG *i2cConfig
        )	
{
    UINT16 wValue, wIndex, wLength;
    UINT8 bmRequestType, bmRequest; 
    int rStatus;
    CyUsI2cConfig_t localI2cConfig;
    CY_DEVICE *device;
    libusb_device_handle *devHandle;
    UINT16 scbIndex = 0;
    UINT32 ioTimeout = CY_USB_SERIAL_TIMEOUT;

    if (handle == NULL){
        CY_DEBUG_PRINT_ERROR ("CY:Error invalid handle.. Function is %s \n", __func__);
        return CY_ERROR_INVALID_HANDLE;
    }
    if (i2cConfig == NULL){    
        CY_DEBUG_PRINT_ERROR ("CY:Error invalid parameter.. Function is %s \n", __func__);
        return CY_ERROR_INVALID_PARAMETER;
    }
    device = (CY_DEVICE *)handle;
    devHandle = device->devHandle;
    if (device->deviceType != CY_TYPE_I2C) {
        CY_DEBUG_PRINT_ERROR ("CY:Error opened device is not i2c ..Function is %s \n", __func__);
        return CY_ERROR_REQUEST_FAILED;
    }
    scbIndex = device->interfaceNum;
    if (scbIndex > 0)
        scbIndex = 1;
    bmRequestType = CY_VENDOR_REQUEST_DEVICE_TO_HOST;
    bmRequest = CY_I2C_GET_CONFIG_CMD;
    wValue = (scbIndex << CY_SCB_INDEX_POS);
    wIndex = 0x00;
    wLength = CY_I2C_CONFIG_LENGTH;

    rStatus = libusb_control_transfer (devHandle, bmRequestType, bmRequest,
            wValue, wIndex, (unsigned char*)&localI2cConfig, wLength, ioTimeout);
    if (rStatus == CY_I2C_CONFIG_LENGTH){
        CY_DEBUG_PRINT_INFO ("CY: Read I2C config ...size is %d \n", rStatus);   
        i2cConfig->frequency = localI2cConfig.frequency;
        i2cConfig->slaveAddress = localI2cConfig.sAddress;
        i2cConfig->isMaster = localI2cConfig.isMaster;
        i2cConfig->isClockStretch = localI2cConfig.clockStretch;
        return CY_SUCCESS;
    }
    else if (rStatus == LIBUSB_ERROR_NO_DEVICE) {
        CY_DEBUG_PRINT_ERROR ("CY: Device Disconnected ....Function is %s\n", __func__);
        return CY_ERROR_DEVICE_NOT_FOUND;    
    }
    else if (rStatus == LIBUSB_ERROR_TIMEOUT){
        CY_DEBUG_PRINT_ERROR ("CY:Error time out ....Function is %s\n", __func__);
        return CY_ERROR_IO_TIMEOUT;    
    }
    else {
        CY_DEBUG_PRINT_ERROR ("CY: Error in doing I2C read ...libusb error is %d function is %s!\n", rStatus, __func__);
        return CY_ERROR_REQUEST_FAILED;    
    }
}
/*
   This API sets I2C config of the device for that 
   interface  
 */
CY_RETURN_STATUS CySetI2cConfig (
        CY_HANDLE handle,
        CY_I2C_CONFIG *i2cConfig
        )	
{
    UINT16 wValue, wIndex, wLength;
    UINT8 bmRequestType, bmRequest;
    CyUsI2cConfig_t localI2cConfig;
    int rStatus;
    CY_DEVICE *device = NULL;
    libusb_device_handle *devHandle;
    UINT16 scbIndex = 0;
    UINT32 ioTimeout = CY_USB_SERIAL_TIMEOUT;

    if (handle == NULL){
        CY_DEBUG_PRINT_ERROR ("CY:Error invalid handle.. Function is %s \n", __func__);
        return CY_ERROR_INVALID_HANDLE;
    }
    if (i2cConfig == NULL){    
        CY_DEBUG_PRINT_ERROR ("CY:Error invalid parameter.. Function is %s \n", __func__);
        return CY_ERROR_INVALID_PARAMETER;
    }
    if (i2cConfig->frequency < 1000 || i2cConfig->frequency > 400000){
        CY_DEBUG_PRINT_ERROR ("CY:Error frequency trying to set in out of ..range Function is %s \n", __func__);
        return CY_ERROR_INVALID_PARAMETER;            
    }
    if ((i2cConfig->slaveAddress % 2) != 0){
        CY_DEBUG_PRINT_ERROR ("CY:Error slave address needs to even..Function is %s \n", __func__);
        return CY_ERROR_INVALID_PARAMETER;            
    }
    device = (CY_DEVICE *)handle;
    devHandle = device->devHandle;
    scbIndex = device->interfaceNum;
    if (device->deviceType != CY_TYPE_I2C) {
        CY_DEBUG_PRINT_ERROR ("CY:Error opened device is not i2c ..Function is %s \n", __func__);
        return CY_ERROR_REQUEST_FAILED;
    }
    if (scbIndex > 0)
        scbIndex = 1;
    bmRequestType = CY_VENDOR_REQUEST_HOST_TO_DEVICE;
    bmRequest = CY_I2C_SET_CONFIG_CMD;
    wValue = (scbIndex << CY_SCB_INDEX_POS);
    wIndex = 0x00;
    wLength = CY_I2C_CONFIG_LENGTH;
    //We need to pass entire 16 bytes config structure to firmware
    //but we will not expose all the structure elements to user. 
    //so filling some of the values.
    memset (&localI2cConfig, 0, CY_I2C_CONFIG_LENGTH);
    localI2cConfig.frequency = i2cConfig->frequency;
    localI2cConfig.sAddress = i2cConfig->slaveAddress;
    localI2cConfig.isMaster = i2cConfig->isMaster;
    localI2cConfig.clockStretch = i2cConfig->isClockStretch;
    localI2cConfig.isMsbFirst = 1;
    rStatus = libusb_control_transfer (devHandle, bmRequestType, bmRequest,
            wValue, wIndex, (unsigned char*)&localI2cConfig, wLength, ioTimeout);
    if (rStatus == CY_I2C_CONFIG_LENGTH){
        CY_DEBUG_PRINT_INFO ("CY: Setting I2C config successful ...\n");   
        return CY_SUCCESS;
    }
    else if (rStatus == LIBUSB_ERROR_NO_DEVICE) {
        CY_DEBUG_PRINT_ERROR ("CY: Device Disconnected ....Function is %s\n", __func__);
        return CY_ERROR_DEVICE_NOT_FOUND;    
    }
    else if (rStatus == LIBUSB_ERROR_TIMEOUT){
        CY_DEBUG_PRINT_ERROR ("CY:Error time out ....Function is %s\n", __func__);
        return CY_ERROR_IO_TIMEOUT;    
    }
    else {
        CY_DEBUG_PRINT_ERROR ("CY: Error in doing I2C read ...libusb error is %d function is %s!\n", rStatus, __func__);
        return CY_ERROR_REQUEST_FAILED;    
    }
}
/*
   This API reads I2C data from the specified interface of the device 
   interface  
 */
CY_RETURN_STATUS CyI2cRead (
        CY_HANDLE handle,
        CY_I2C_DATA_CONFIG *i2cDataConfig,
        CY_DATA_BUFFER *readBuffer,
        UINT32 ioTimeout        
        )	
{
    int rStatus;
    CY_DEVICE *device = NULL;
    libusb_device_handle *devHandle;
    UINT16 wValue = 0, wIndex, wLength, bytesPending = 0;
    UINT8 bmRequestType, bmRequest;
    UCHAR i2cStatus[CY_I2C_GET_STATUS_LEN];
    UINT16 scbIndex = 0;
    bool mode = CY_I2C_MODE_READ;
    UINT32 elapsedTime;
    if (handle == NULL){
        CY_DEBUG_PRINT_ERROR ("CY:Error invalid handle.. Function is %s \n", __func__);
        return CY_ERROR_INVALID_HANDLE;
    }
    if ((readBuffer == NULL) || (readBuffer->buffer == NULL) || (readBuffer->length == 0)){    
        CY_DEBUG_PRINT_ERROR ("CY:Error invalid parameter.. Function is %s \n", __func__);
        return CY_ERROR_INVALID_PARAMETER;
    }
    readBuffer->transferCount = 0;
    device = (CY_DEVICE *)handle;
    devHandle = device->devHandle;
    if (device->deviceType != CY_TYPE_I2C) {
        CY_DEBUG_PRINT_ERROR ("CY:Error opened device is not i2c ..Function is %s \n", __func__);
        return CY_ERROR_REQUEST_FAILED;
    }
    if (pthread_mutex_trylock (&device->readLock) == 0){
        scbIndex = device->interfaceNum;
        if (scbIndex > 0)
            scbIndex = 1;
        i2cDataConfig->slaveAddress = ((i2cDataConfig->slaveAddress & 0x7F) | (scbIndex << 7));
        bmRequestType = CY_VENDOR_REQUEST_HOST_TO_DEVICE;
        bmRequest = CY_I2C_READ_CMD;
        wValue = ((i2cDataConfig->isStopBit) | (i2cDataConfig->isNakBit << 1));
        wValue |= (((i2cDataConfig->slaveAddress) << 8));
        wIndex = readBuffer->length;
        wLength = 0;
        rStatus = CyI2cGetStatus (handle, mode, (UCHAR *)i2cStatus);
        if (rStatus == CY_SUCCESS)
        {
            if ((i2cStatus[0] & CY_I2C_ERROR_BIT)){
                CY_DEBUG_PRINT_ERROR ("CY:Error device busy ... function is %s \n", __func__);
                pthread_mutex_unlock (&device->readLock);
                return CY_ERROR_I2C_DEVICE_BUSY;
            }
        }
        rStatus = libusb_control_transfer (devHandle, bmRequestType, bmRequest,wValue, wIndex, NULL, wLength, ioTimeout);
        if (rStatus == LIBUSB_ERROR_NO_DEVICE){
            CY_DEBUG_PRINT_ERROR ("CY:Error device disconnected ... \n");
            pthread_mutex_unlock (&device->readLock);
            return CY_ERROR_DEVICE_NOT_FOUND;
        }
        if (rStatus < 0){
            CY_DEBUG_PRINT_ERROR ("CY:Error in sending Read vendor command ... Libusb Error is %d .. Function is %s \n", rStatus, __func__);
            pthread_mutex_unlock (&device->readLock);
            return CY_ERROR_I2C_DEVICE_BUSY;
        }
        //Hoping that previous calls do not take much time!!
#ifdef CY_I2C_ENABLE_PRECISE_TIMING
        startI2cTick(false);
#endif
        rStatus = libusb_bulk_transfer (devHandle, device->inEndpoint, readBuffer->buffer, readBuffer->length, 
                (int*)&readBuffer->transferCount, ioTimeout);
#ifdef CY_I2C_ENABLE_PRECISE_TIMING
        elapsedTime = getI2cLapsedTime(false);
        //Giving an extra 10 msec to notification to findout the status
        ioTimeout = (ioTimeout - elapsedTime);
        if (ioTimeout == 0)
            ioTimeout = 10;
#endif
        if (rStatus == LIBUSB_SUCCESS){ 
            CY_DEBUG_PRINT_INFO ("CY: Successfully read i2c data.. %d bytes Read ...\n", readBuffer->transferCount);
            bytesPending = readBuffer->length;
            rStatus = waitForNotification (handle, &bytesPending, ioTimeout);
            if (rStatus)
                readBuffer->transferCount = (readBuffer->length - bytesPending);
            else
                readBuffer->transferCount = readBuffer->length;
            pthread_mutex_unlock (&device->readLock);
            return rStatus;
        }
        else if (rStatus == LIBUSB_ERROR_TIMEOUT){
            CY_DEBUG_PRINT_ERROR ("CY:Timeout error ..Function is %s\n", __func__);
            pthread_mutex_unlock (&device->readLock);
            return CY_ERROR_IO_TIMEOUT;
        }
        else if (rStatus == LIBUSB_ERROR_PIPE){
            CY_DEBUG_PRINT_INFO ("Pipe Error \n");
            rStatus = CyResetPipe (handle, device->outEndpoint);
            if (rStatus != CY_SUCCESS){
                CY_DEBUG_PRINT_ERROR ("Error in reseting the pipe \n");
            }
            else 
                CY_DEBUG_PRINT_INFO ("Reset pipe succeded \n");

            rStatus = CyI2cGetStatus (handle, mode, (UCHAR *)i2cStatus);
            if (rStatus == CY_SUCCESS)
            {
                CyI2cReset (handle, mode);
                rStatus = handleI2cError (i2cStatus[0]);
                pthread_mutex_unlock (&device->readLock);
                return rStatus;
            }
            else {
                pthread_mutex_unlock (&device->readLock);   
                return CY_ERROR_I2C_DEVICE_BUSY;
            }
        }
        else if (rStatus == LIBUSB_ERROR_NO_DEVICE) {
            pthread_mutex_unlock (&device->readLock);
            CY_DEBUG_PRINT_ERROR ("CY: Device Disconnected ....Function is %s\n", __func__);
            return CY_ERROR_DEVICE_NOT_FOUND;    
        }
        else if (rStatus == LIBUSB_ERROR_TIMEOUT){
            pthread_mutex_unlock (&device->readLock);
            CY_DEBUG_PRINT_ERROR ("CY:Error time out ....Function is %s\n", __func__);
            return CY_ERROR_IO_TIMEOUT;    
        }
        else {
            pthread_mutex_unlock (&device->readLock);
            CY_DEBUG_PRINT_ERROR ("CY: Error in doing I2C read ...libusb error is %d function is %s!\n", rStatus, __func__);
            return CY_ERROR_REQUEST_FAILED;    
        }
    }
    else{
        CY_DEBUG_PRINT_ERROR ("CY: Error API busy in servicing previous request... function is %s!\n", __func__);
        return CY_ERROR_REQUEST_FAILED;    
    }
}
/*
   This API writes I2C data into the specified interface of the device 
 */
CY_RETURN_STATUS CyI2cWrite (
        CY_HANDLE handle,
        CY_I2C_DATA_CONFIG *i2cDataConfig,
        CY_DATA_BUFFER *writeBuffer,
        UINT32 ioTimeout        
        )	
{
    int rStatus;
    UCHAR i2cStatus[CY_I2C_GET_STATUS_LEN];
    CY_DEVICE *device;
    libusb_device_handle *devHandle;
    UINT16 wValue = 0, wIndex, wLength, bytesPending = 0;
    UINT8 bmRequestType, bmRequest;
    UINT16 scbIndex = 0;
    BOOL mode = CY_I2C_MODE_WRITE;
    UINT32 elapsedTime;
    if (handle == NULL){
        CY_DEBUG_PRINT_ERROR ("CY:Error invalid handle.. Function is %s \n", __func__);
        return CY_ERROR_INVALID_HANDLE;
    }
    if ((writeBuffer == NULL) || (writeBuffer->buffer == NULL) || (writeBuffer->length == 0)){    
        CY_DEBUG_PRINT_ERROR ("CY:Error invalid parameter.. Function is %s \n", __func__);
        return CY_ERROR_INVALID_PARAMETER;
    }
    writeBuffer->transferCount = 0;
    device = (CY_DEVICE *)handle;
    devHandle = device->devHandle;
    scbIndex = device->interfaceNum;
    if (device->deviceType != CY_TYPE_I2C){
        CY_DEBUG_PRINT_ERROR ("CY:Error opened device is not i2c ..Function is %s \n", __func__);
        return CY_ERROR_REQUEST_FAILED;
    }
    if (pthread_mutex_trylock (&device->writeLock) == 0){
        if (scbIndex > 0)
            scbIndex = 1; 
        bmRequestType = CY_VENDOR_REQUEST_HOST_TO_DEVICE;
        bmRequest = CY_I2C_WRITE_CMD;
        i2cDataConfig->slaveAddress = ((i2cDataConfig->slaveAddress & 0x7F) | (scbIndex << 7));
        wValue = ((i2cDataConfig->isStopBit));
        wValue |= (((i2cDataConfig->slaveAddress) << 8));
        wIndex = (UINT16)(writeBuffer->length);
        wLength = 0;
        CY_DEBUG_PRINT_INFO ("wValue is %x \n", wValue);
        //Send I2C write vendor command before actually sending the data over bulk ep 
        rStatus = CyI2cGetStatus (handle, mode, (UCHAR *)i2cStatus);
        if (rStatus == CY_SUCCESS)
        {
            if ((i2cStatus[0] & CY_I2C_ERROR_BIT)){
                CY_DEBUG_PRINT_ERROR ("CY:Error ... Device busy ... function is %s \n", __func__);
                pthread_mutex_unlock (&device->writeLock);
                return CY_ERROR_I2C_DEVICE_BUSY;    
            }
        }
        else if (rStatus == LIBUSB_ERROR_NO_DEVICE){
            CY_DEBUG_PRINT_ERROR ("CY:Error device not found \n");
            pthread_mutex_unlock (&device->writeLock);
            return CY_ERROR_DEVICE_NOT_FOUND;
        }
        rStatus = libusb_control_transfer (devHandle, bmRequestType, bmRequest,wValue, wIndex, NULL, wLength, ioTimeout);
        if (rStatus == LIBUSB_ERROR_NO_DEVICE){
            CY_DEBUG_PRINT_ERROR ("CY:Error device not found \n");
            pthread_mutex_unlock (&device->writeLock);
            return CY_ERROR_DEVICE_NOT_FOUND;
        }
        if (rStatus < 0){
            CY_DEBUG_PRINT_ERROR ("CY:Error in sending write vendor command ... Libusb Error is %d \n", rStatus);
            pthread_mutex_unlock (&device->writeLock);
            return CY_ERROR_I2C_DEVICE_BUSY;
        }
        //After vendor command is sent send the actual data to be sent to i2c devic
#ifdef CY_I2C_ENABLE_PRECISE_TIMING
        startI2cTick(true);
#endif
        rStatus = libusb_bulk_transfer (devHandle, device->outEndpoint, writeBuffer->buffer, writeBuffer->length, 
                (int*)&(writeBuffer->transferCount), ioTimeout);
#ifdef CY_I2C_ENABLE_PRECISE_TIMING
        elapsedTime = getI2cLapsedTime(true);
        ioTimeout = (ioTimeout - elapsedTime);
        //Giving an extra 10 msec to notification to findout the status
        if (ioTimeout == 0)
            ioTimeout = 10;
#endif
        //Once the data is sent to usbserial, check if it was actually written to i2c device.
        if (rStatus == LIBUSB_SUCCESS){ 
            CY_DEBUG_PRINT_INFO ("CY: Successfully written i2c data.. %d bytes written ...\n", writeBuffer->transferCount);
            bytesPending = writeBuffer->length;
            rStatus = waitForNotification (handle, &bytesPending, ioTimeout);
            if (rStatus)
                writeBuffer->transferCount = (writeBuffer->length - bytesPending);
            else
                writeBuffer->transferCount = writeBuffer->length;
            pthread_mutex_unlock (&device->writeLock);    
            return rStatus;
        }
        //Transaction is stallled when we hit some I2C error while the transfer
        //was going on. After we hit this error clear stall and check why we hit this by 
        //CyGetStatus.
        else if (rStatus == LIBUSB_ERROR_PIPE){
            CY_DEBUG_PRINT_INFO ("CY:Pipe Error ... Function is %s\n", __func__);
            rStatus = CyResetPipe (handle, device->outEndpoint);
            if (rStatus != CY_SUCCESS){
                CY_DEBUG_PRINT_ERROR ("CY:Error in reseting the pipe ..Function is %s\n", __func__);
            }
            else 
                CY_DEBUG_PRINT_INFO ("Reset pipe succeded \n");

            rStatus = CyI2cGetStatus (handle, mode, (UCHAR *)i2cStatus);
            if (rStatus == CY_SUCCESS)
            {
                CyI2cReset (handle, mode);
                rStatus = handleI2cError (i2cStatus[0]);
                pthread_mutex_unlock (&device->writeLock);
                return rStatus;
            }
        }
        else if (rStatus == LIBUSB_ERROR_NO_DEVICE) {
            CY_DEBUG_PRINT_ERROR ("CY: Device Disconnected ....Function is %s\n", __func__);
            pthread_mutex_unlock (&device->writeLock);
            return CY_ERROR_DEVICE_NOT_FOUND;    
        }
        else if (rStatus == LIBUSB_ERROR_TIMEOUT){
            CY_DEBUG_PRINT_ERROR ("CY:Error time out ....Function is %s\n", __func__);
            pthread_mutex_unlock (&device->writeLock);
            return CY_ERROR_IO_TIMEOUT;    
        }
        else{
            CY_DEBUG_PRINT_ERROR ("CY: Error in doing I2C read ...libusb error is %d function is %s!\n", rStatus, __func__);
            pthread_mutex_unlock (&device->writeLock);
            return CY_ERROR_REQUEST_FAILED;    
        }
    }
    else{
        CY_DEBUG_PRINT_ERROR ("CY:API busy with servicing previous request... function is %s!\n", __func__);
        return CY_ERROR_REQUEST_FAILED;    
    }
    return CY_ERROR_REQUEST_FAILED;    
}
/*
   This Api gets the current status of the I2C data transaction
 */
CY_RETURN_STATUS CyI2cGetStatus (
        CY_HANDLE handle,
        bool mode,
        UCHAR *i2cStatus
        )
{
    int rStatus;
    CY_DEVICE *device;
    libusb_device_handle *devHandle;
    UINT16 wValue, wIndex, wLength, bmRequestType, bmRequest;;
    UINT16 scbIndex = 0;
    UINT32 ioTimeout = CY_USB_SERIAL_TIMEOUT;

    if (handle == NULL)
        return CY_ERROR_INVALID_HANDLE;
    if (i2cStatus == NULL)
        return CY_ERROR_INVALID_PARAMETER;
    device = (CY_DEVICE *)handle;
    devHandle = device->devHandle;
    if (device->deviceType != CY_TYPE_I2C) {
        CY_DEBUG_PRINT_ERROR ("CY:Error opened device is not i2c .. \n");
        return CY_ERROR_REQUEST_FAILED;
    }
    scbIndex = device->interfaceNum;
    if (scbIndex > 0)
        scbIndex = 1;
    bmRequestType = CY_VENDOR_REQUEST_DEVICE_TO_HOST;
    bmRequest = CY_I2C_GET_STATUS_CMD;
    wValue = ((scbIndex << CY_SCB_INDEX_POS) | mode);
    wIndex = 0;
    wLength = CY_I2C_GET_STATUS_LEN; 

    rStatus = libusb_control_transfer (devHandle, bmRequestType, bmRequest,wValue, wIndex, (UCHAR*)i2cStatus, wLength, ioTimeout);
    if (rStatus < CY_I2C_GET_STATUS_LEN){
        CY_DEBUG_PRINT_INFO ("CY:Error in sending I2C Get Status command...Libusb error is %d\n", rStatus);
        return rStatus;
    }
    return CY_SUCCESS;
}
/*
   This Api resets the I2C module
 */
CY_RETURN_STATUS CyI2cReset (
        CY_HANDLE handle,
        BOOL resetMode
        )
{
    int rStatus;
    CY_DEVICE *device;
    libusb_device_handle *devHandle;
    UINT16 wValue, wIndex, wLength, bmRequestType, bmRequest;
    UINT16 scbIndex = 0;
    UINT32 ioTimeout = CY_USB_SERIAL_TIMEOUT;

    if (handle == NULL)
        return CY_ERROR_INVALID_HANDLE;
    device = (CY_DEVICE *)handle;
    devHandle = device->devHandle;
    if (device->deviceType != CY_TYPE_I2C) {
        CY_DEBUG_PRINT_ERROR ("CY:Error opened device is not i2c .. \n");
        return CY_ERROR_REQUEST_FAILED;
    }
    scbIndex = device->interfaceNum;
    if (scbIndex > 0)
        scbIndex = 1;
    bmRequestType = CY_VENDOR_REQUEST_HOST_TO_DEVICE;
    bmRequest = CY_I2C_RESET_CMD;
    wValue = ((scbIndex << CY_SCB_INDEX_POS) | resetMode );
    wIndex = 0;
    wLength = 0; 
    rStatus = libusb_control_transfer (devHandle, bmRequestType, bmRequest,wValue, wIndex, NULL, wLength, ioTimeout);
    if (rStatus < 0){
        CY_DEBUG_PRINT_ERROR ("CY:Error in sending I2C Reset command ..libusb error is %d\n", rStatus);
        return CY_ERROR_REQUEST_FAILED;
    }
    return CY_SUCCESS;
}
static void LIBUSB_CALL i2c_notification_cb(struct libusb_transfer *transfer)
{
    UINT32 *completed = transfer->user_data;
    *completed = 1;
}

CY_RETURN_STATUS waitForNotification (CY_HANDLE handle, UINT16 *bytesPending, UINT32 ioTimeout){

    UINT32 transferCompleted = 0, length = CY_I2C_EVENT_NOTIFICATION_LEN;
    CY_DEVICE *device;
    libusb_device_handle *devHandle;
    struct libusb_transfer *transfer;
    CY_RETURN_STATUS errorStatus, rStatus;
    UCHAR i2cStatus[CY_I2C_EVENT_NOTIFICATION_LEN];
    struct timeval time;

    device = (CY_DEVICE *)handle;
    devHandle = device->devHandle;
    transfer = libusb_alloc_transfer(0);
    if (transfer == NULL){
        CY_DEBUG_PRINT_ERROR ("CY:Error in allocating trasnfer \n");
        errorStatus = CY_ERROR_ALLOCATION_FAILED;
        (*bytesPending) = 0;
        return errorStatus;
        //callbackFn (errorStatus, 0);
    }
    libusb_fill_interrupt_transfer (transfer, devHandle, device->interruptEndpoint, i2cStatus, length, 
            i2c_notification_cb, &transferCompleted, ioTimeout);
    if (libusb_submit_transfer (transfer)){
        CY_DEBUG_PRINT_ERROR ("CY:Error in submitting interrupt transfer ...\n");
        libusb_cancel_transfer (transfer);
        libusb_free_transfer (transfer);
        (*bytesPending) = 0;
        //callbackFn (CY_ERROR_REQUEST_FAILED, 0);
        return CY_ERROR_REQUEST_FAILED;
    }
    time.tv_sec = 0;
    time.tv_usec = 50;//polling timeout.
    while (transferCompleted == 0){
        libusb_handle_events_timeout (NULL, &time);
    }
    transferCompleted = 0;
    if (transfer->status == LIBUSB_TRANSFER_COMPLETED){
        CY_DEBUG_PRINT_INFO ("CY:Info successfully recieved data on interrupt pipe length is %d \n", transfer->actual_length);
        if (i2cStatus[0] & 0x80){ //Error notification is for write
            if ((i2cStatus[0] & CY_I2C_ERROR_BIT)){
                CY_DEBUG_PRINT_INFO ("Bytes pending is %x %x %x", i2cStatus[0], i2cStatus[1], i2cStatus[2]);
                if (i2cStatus[0] & 0x1E){
                    //There was some error, so reset the i2c module and usb module 
                    //of the device, so branch out of the loop(Check below for the errors reported).
                    rStatus = CyI2cReset (device, CY_I2C_MODE_WRITE);
                    if (rStatus != CY_SUCCESS)
                        CY_DEBUG_PRINT_INFO ("CY:i2c reset failed \n");
                    //Report the amount of byte that were actually written
                    memcpy(bytesPending, &i2cStatus[1], 2);
                    errorStatus = handleI2cError (i2cStatus[0]);
                }
            }
            else
                errorStatus = CY_SUCCESS;
        }
        else //Error notification is for read
        {
            if ((i2cStatus[0] & CY_I2C_ERROR_BIT)){
                CY_DEBUG_PRINT_INFO ("Bytes pending is %x %x %x", i2cStatus[0], i2cStatus[1], i2cStatus[2]);
                if (i2cStatus[0] & 0x1E){
                    rStatus = CyI2cReset (device, CY_I2C_MODE_READ);
                    if (rStatus != CY_SUCCESS)
                        CY_DEBUG_PRINT_INFO ("CY:i2c reset failed \n");
                    //Report the amount of byte that were actually written
                    memcpy(bytesPending, &i2cStatus[1], 2);
                    errorStatus = handleI2cError (i2cStatus[0]);
                }
            }
            else
                errorStatus = CY_SUCCESS;
        }
        libusb_free_transfer (transfer);
        return errorStatus;
    }
    else{
        libusb_cancel_transfer (transfer);
        if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT){
            CY_DEBUG_PRINT_ERROR ("CY:Error Timeout in getting i2c transfer status ....\n"); 
            CyI2cGetStatus (handle, 1, (UCHAR *)&errorStatus);
            errorStatus = CY_ERROR_IO_TIMEOUT;
        }
        if (transfer->status == LIBUSB_TRANSFER_OVERFLOW){
            CY_DEBUG_PRINT_ERROR ("CY:Error buffer overFlow in i2c transfer status ....\n");
            errorStatus = CY_ERROR_BUFFER_OVERFLOW;
        }
        if (transfer->status != LIBUSB_TRANSFER_COMPLETED || transfer->status != LIBUSB_TRANSFER_COMPLETED){
            CY_DEBUG_PRINT_ERROR ("CY:Error in i2c transfer status ... Libusb transfer error is %d \n", transfer->status);
            errorStatus = CY_ERROR_REQUEST_FAILED;
        }
        libusb_free_transfer (transfer);
        return CY_ERROR_REQUEST_FAILED;
    }
}
