/**************************************************************************** * * Copyright (c) 2006 Dave Hylands * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. * ****************************************************************************/ /** * * @file i2c-api.c * * @brief This file contains the implementation for performing I2C operations * on the gumstix. * ****************************************************************************/ // ---- Include Files ------------------------------------------------------- #include #include #include "i2c.h" #include "i2c-dev.h" #include "i2c-api.h" #include "Crc8.h" #include "DumpMem.h" #include "Log.h" // ---- Public Variables ---------------------------------------------------- // ---- Private Constants and Types ----------------------------------------- // ---- Private Variables --------------------------------------------------- static I2C_Addr_t gI2cAddr; static int gUseCrc; // ---- Private Function Prototypes ----------------------------------------- // ---- Functions ----------------------------------------------------------- //*************************************************************************** /** * * Sets the I2C address that we'll be communicating with, as well as whether * the device uses smbus PEC (CRC). */ void I2cSetSlaveAddress( int i2cDev, I2C_Addr_t i2cAddr, int useCrc ) { gI2cAddr = i2cAddr; gUseCrc = useCrc; LogDebug( "----- I2cSetSlaveAddress i2cAddr:0x%02x useCrc:%d -----\n", i2cAddr, useCrc ); // Indicate which slave we wish to speak to if ( ioctl( i2cDev, I2C_SLAVE, gI2cAddr ) < 0 ) { LogError( "I2cSetSlaveAddress: Error trying to set slave address to 0x%02x (%d %s)\n", gI2cAddr, errno, strerror( errno )); } // We do the CRC calculation ourself, so we don't need to tell the driver // that we're using it. #if 0 // Indicate that we use PEC (aka CRCs) if ( ioctl( i2cDev, I2C_PEC, 1 ) < 0 ) { LogError( "I2cSetSlaveAddress: Error trying to set PEC mode\n" ); } #endif } // I2cSetSlaveAddress //*************************************************************************** /** * Transfer data to/from an i2c device. * * This function implements the equivalent of the smbus functions using * I2C_RDWR. * * The PXA driver doesn't support the smbus transfers. * * This function can perform the following SMBUS transactions: * * Write Byte: wrLen == 1, rdLen == 0 * Read Byte: wrLen == 0, rdLen == 1 * Write Word: wrLen == 2, rdLen == 0 * Read Word: wrLen == 0, rdLen == 2 * Process Call: wrLen == 2, rdLen == 2 * Write Block: wrLen == 0x80 + numBytes, rdLen == 0 * Read Block: wrLen == 0, rdLen == 0x80 + numBytes * Process Block: wrLen == 0x80 + numBytes, rdLen == 0x80 + numBytes */ int I2cTransfer ( int i2cDev, ///< Handle to i2c-dev file uint8_t cmd, ///< Command to send const void *wrData, ///< Data to write uint8_t wrLen, ///< Number of bytes to write (or in 0x80 for a block write) void *rdData, ///< Place to store data read uint8_t rdLen, ///< Number of bytes to read (or in 0x80 for a block read) uint8_t *bytesReadp ///< Place to store number of bytes read ) { struct i2c_rdwr_ioctl_data rdwr; struct i2c_msg msg[ 2 ]; uint8_t wrBuf[ I2C_MAX_DATA_LEN + 3 ]; // +1 for cmd, +1 for len, +1 for CRC uint8_t rdBuf[ I2C_MAX_DATA_LEN + 2 ]; // +1 for len, +1 for CRC uint8_t crc = 0; uint8_t wrBlock = (( wrLen & 0x80 ) != 0 ); uint8_t rdBlock = (( rdLen & 0x80 ) != 0 ); int rc = 0; LogDebug( "----- I2cTransfer: cmd:0x%02x wrLen:0x%02x rdLen:0x%02x wrBlock:%d rdBlock:%d -----\n", cmd, wrLen, rdLen, wrBlock, rdBlock ); if ( wrData != NULL ) { LogDebug( "----- wrData:0x%08x *wrData:0x%02x -----\n", wrData, *(const uint8_t *)wrData ); } rdLen &= 0x7f; wrLen &= 0x7f; if ( bytesReadp != NULL ) { *bytesReadp = 0; } if ( wrLen > I2C_MAX_DATA_LEN ) { LogError( "I2cTransfer: wrLen too big: %d, max is %d\n", wrLen, I2C_MAX_DATA_LEN ); errno = ENOBUFS; return -1; } if ( rdLen > I2C_MAX_DATA_LEN ) { LogError( "I2cTransfer: rdLen too big: %d, max is %d\n", rdLen, I2C_MAX_DATA_LEN ); errno = ENOBUFS; return -1; } // Whether we're doing a read or a write, we always send // the command. msg[ 0 ].addr = gI2cAddr; msg[ 0 ].flags = 0; msg[ 0 ].len = wrLen + 1 + wrBlock; // +1 for cmd msg[ 0 ].buf = (char *)&wrBuf[ 0 ]; if ( gUseCrc ) { crc = Crc8( 0, gI2cAddr << 1 ); crc = Crc8( crc, cmd ); } wrBuf[ 0 ] = cmd; if ( wrLen > 0 ) { // We have some data to send down to the device if ( wrBlock ) { wrBuf[ 1 ] = wrLen; memcpy( &wrBuf[ 2 ], wrData, wrLen ); wrLen++; // Add in cmd to the length } else { memcpy( &wrBuf[ 1 ], wrData, wrLen ); } if ( gUseCrc ) { crc = Crc8Block( crc, &wrBuf[ 1 ], wrLen ); if ( rdLen == 0 ) { // This is a write-only, so we need to send the CRC wrBuf[ wrLen + 1 ] = crc; msg[ 0 ].len++; } } } if ( gDebug ) { Log( "msg[ 0 ].addr = 0x%02x\n", msg[ 0 ].addr ); Log( "msg[ 0 ].flags = 0x%04x\n", msg[ 0 ].flags ); Log( "msg[ 0 ].len = %d\n", msg[ 0 ].len ); DumpMem( "I2cTransfer W", 0, &wrBuf[ 0 ], msg[ 0 ].len ); } rdwr.msgs = msg; rdwr.nmsgs = 1; if ( rdLen > 0 ) { // We're expecting some data to come back msg[ 1 ].addr = gI2cAddr; msg[ 1 ].flags = I2C_M_RD; msg[ 1 ].len = rdLen + rdBlock + gUseCrc; msg[ 1 ].buf = (char *)&rdBuf[ 0 ]; rdwr.nmsgs = 2; if ( gUseCrc ) { crc = Crc8( crc, ( gI2cAddr << 1 ) | 1 ); } if ( gDebug ) { Log( "msg[ 1 ].addr = 0x%02x\n", msg[ 1 ].addr ); Log( "msg[ 1 ].flags = 0x%04x\n", msg[ 1 ].flags ); Log( "msg[ 1 ].len = %d\n", msg[ 1 ].len ); } } if ( ioctl( i2cDev, I2C_RDWR, &rdwr ) < 0 ) { LogError( "I2cTransfer: ioctl failed: %s (%d)\n", strerror( errno ), errno ); return -1; } if ( rdLen > 0 ) { if ( rdBlock ) { if ( rdBuf[ 0 ] > rdLen ) { LogError( "I2cTransfer: length is too big: %d max: %d\n", rdBuf[ 0 ], rdLen ); rc = EMSGSIZE; } else { rdLen = rdBuf[ 0 ]; } } if ( gUseCrc ) { crc = Crc8Block( crc, &rdBuf[ 0 ], rdLen + rdBlock ); if ( crc != rdBuf[ rdLen + rdBlock ] ) { LogError( "I2cTransfer: CRC failed: Rcvd: 0x%02x, expecting: 0x%02x\n", rdBuf[ rdLen + rdBlock ], crc ); rc = EBADMSG; } } if ( gDebug ) { DumpMem( "I2cTransfer R", 0, &rdBuf[ 0 ], msg[ 1 ].len ); } memcpy( rdData, &rdBuf[ rdBlock ], rdLen ); if ( bytesReadp != NULL ) { *bytesReadp = rdLen; } } return rc; } // I2cTransfer //*************************************************************************** /** * Uses the SMBUS Process-Block protocol to read data from a device. */ int I2cProcessBlock ( int i2cDev, ///< Handle to i2c-dev file uint8_t cmd, ///< Command to send const void *wrData, ///< Data to write uint8_t wrLen, ///< Number of bytes to write void *rdData, ///< Place to store data read uint8_t rdLen, ///< Number of bytes to read uint8_t *bytesReadp ///< Place to store number of bytes read ) { LogDebug( "----- I2cProcessBlock cmd: 0x%02x wrLen:0x%02x rdLen:0x%02x -----\n", cmd, wrLen, rdLen ); return I2cTransfer( i2cDev, cmd, wrData, 0x80 | wrLen, rdData, 0x80 | rdLen, bytesReadp ); } // I2cProcessBlock //*************************************************************************** /** * Uses the SMBUS Read-Block protocol to read data from a device. */ int I2cReadBlock ( int i2cDev, ///< Handle to i2c-dev file uint8_t cmd, ///< Command to send void *rdData, ///< Place to store data read uint8_t rdLen, ///< Number of bytes to read uint8_t *bytesReadp ///< Place to store number of bytes read ) { LogDebug( "----- I2cReadBlock cmd: 0x%02x rdLen:0x%02x -----\n", cmd, rdLen ); return I2cTransfer( i2cDev, cmd, NULL, 0, rdData, 0x80 | rdLen, bytesReadp ); } // I2cReadBlock //*************************************************************************** /** * Uses the SMBUS Read-Byte protocol to read a byte. */ int I2cReadByte ( int i2cDev, ///< Handle to i2c-dev file uint8_t cmd, ///< Command to send uint8_t *rdByte ///< Place to store byte read ) { LogDebug( "----- I2cReadByte cmd: 0x%02x -----\n", cmd ); return I2cTransfer( i2cDev, cmd, NULL, 0, rdByte, 1, NULL ); } // I2cReadByte //*************************************************************************** /** * Reads an array of bytes usinng i2c (not compatible with SMBUS) */ int I2cReadBytes ( int i2cDev, ///< Handle to i2c-dev file uint8_t cmd, ///< Command to send void *rdByte, ///< Place to store bytes read uint8_t rdLen ///< Number of bytes to read ) { LogDebug( "----- I2cReadBytes cmd: 0x%02x rdLen: 0x%02x -----\n", cmd, rdLen ); return I2cTransfer( i2cDev, cmd, NULL, 0, rdByte, rdLen, NULL ); } // I2cReadBytes //*************************************************************************** /** * Uses the SMBUS Write-Block protocol to write data from a device. */ int I2cWriteBlock ( int i2cDev, ///< Handle to i2c-dev file uint8_t cmd, ///< Command to send const void *wrData, ///< Data to write uint8_t wrLen ///< Number of bytes to write ) { LogDebug( "----- I2cWriteBlock cmd: 0x%02x wrLen:0x%02x -----\n", cmd, wrLen ); return I2cTransfer( i2cDev, cmd, wrData, 0x80 | wrLen, NULL, 0, NULL ); } // I2cWriteBlock //*************************************************************************** /** * Uses the SMBUS Write-Byte protocol to write a byte. */ int I2cWriteByte ( int i2cDev, ///< Handle to i2c-dev file uint8_t cmd, ///< Command to send uint8_t wrByte ///< Byte to write ) { LogDebug( "----- I2cWriteByte cmd: 0x%02x wrByte:0x%02x -----\n", cmd, wrByte ); LogDebug( "----- &wrByte = 0x%08x wrByte = 0x%02x -----\n", &wrByte, *&wrByte ); return I2cTransfer( i2cDev, cmd, &wrByte, 1, NULL, 0, NULL ); } // I2cWriteByte //*************************************************************************** /** * Writes an array of bytes using i2c (not compatible with SMBUS) */ int I2cWriteBytes ( int i2cDev, ///< Handle to i2c-dev file uint8_t cmd, ///< Command to send const void *wrByte, ///< Bytes to write uint8_t wrLen ///< Number of bytes to write ) { LogDebug( "----- I2cWriteBytes cmd: 0x%02x wrLen: 0x%02x -----\n", cmd, wrLen ); return I2cTransfer( i2cDev, cmd, wrByte, wrLen, NULL, 0, NULL ); } // I2cWriteBytes //*************************************************************************** /** * Uses the SMBUS Receive-Byte protocol to read a byte. */ int I2cReceiveByte ( int i2cDev, ///< Handle to i2c-dev file uint8_t *rdByte ///< Place to store byte read ) { return I2cReceiveBytes( i2cDev, rdByte, 1 ); } // I2cReceiveByte //*************************************************************************** /** * Uses the SMBUS Receive-Byte protocol to read multiple (or one or zero) bytes. */ int I2cReceiveBytes ( int i2cDev, ///< Handle to i2c-dev file uint8_t *rdData, ///< Place to store data read uint8_t rdLen ///< Number of bytes to read ) { struct i2c_rdwr_ioctl_data rdwr; struct i2c_msg msg; LogDebug( "----- I2cReceiveBytes -----\n" ); msg.addr = gI2cAddr; msg.flags = I2C_M_RD; msg.len = rdLen; msg.buf = (char *)rdData; rdwr.msgs = &msg; rdwr.nmsgs = 1; if ( ioctl( i2cDev, I2C_RDWR, &rdwr ) < 0 ) { LogError( "I2cReceiveBytes: ioctl failed: %s (%d)\n", strerror( errno ), errno ); return -1; } return 0; } // I2cReceiveBytes //*************************************************************************** /** * Uses the SMBUS Send-Byte protocol to write a byte. */ int I2cSendByte ( int i2cDev, ///< Handle to i2c-dev file uint8_t wrByte ///< Byte to write ) { return I2cSendBytes( i2cDev, &wrByte, 1 ); } // I2cSendByte //*************************************************************************** /** * Uses the SMBUS Send-Byte protocol to write multiple (or zero or one) bytes. */ int I2cSendBytes ( int i2cDev, ///< Handle to i2c-dev file uint8_t *wrData, ///< Pointer to data to write uint8_t wrLen ///< NUmber of bytes to write ) { struct i2c_rdwr_ioctl_data rdwr; struct i2c_msg msg; LogDebug( "----- I2cSendBytes wrLen = 0x%02x -----\n", wrLen ); msg.addr = gI2cAddr; msg.flags = 0; msg.len = wrLen; msg.buf = (char *)wrData; rdwr.msgs = &msg; rdwr.nmsgs = 1; if ( ioctl( i2cDev, I2C_RDWR, &rdwr ) < 0 ) { LogError( "I2cSendBytes: ioctl failed: %s (%d)\n", strerror( errno ), errno ); return -1; } return 0; } // I2cSendBytes