Files
tinio/cylib/cyi2c.c
2017-10-21 16:30:05 +02:00

691 lines
29 KiB
C

/*
* 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;
}
}