--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/drvAsynI2C.cpp Mon Jun 27 18:08:05 2016 +0200
@@ -0,0 +1,427 @@
+//******************************************************************************
+// Copyright (C) 2015 Florian Feldbauer <florian@ep1.ruhr-uni-bochum.de>
+// - University Mainz, Institute foer nuc
+//
+// This file is part of drvAsynCan
+//
+// drvAsynCan is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// drvAsynCan 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// brief AsynPortDriver for PANDA Raspberry Pi CAN interface
+//
+// version 3.0.0; Jul. 29, 2014
+//******************************************************************************
+
+//_____ I N C L U D E S _______________________________________________________
+
+// ANSI C includes
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cerrno>
+#include <fcntl.h>
+#include <linux/i2c-dev.h>
+//#include <linux/i2c.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+//#include <sys/select.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+// EPICS includes
+#include <epicsEvent.h>
+#include <epicsExport.h>
+#include <epicsMutex.h>
+#include <epicsString.h>
+#include <epicsThread.h>
+#include <epicsTime.h>
+#include <epicsTimer.h>
+#include <epicsTypes.h>
+#include <iocsh.h>
+
+// local includes
+#include "drvAsynI2C.h"
+
+//_____ D E F I N I T I O N S __________________________________________________
+
+//_____ G L O B A L S __________________________________________________________
+
+//_____ L O C A L S ____________________________________________________________
+static epicsTimerId i2c_timer;
+static epicsTimerQueueId i2c_timerQueue;
+
+//_____ F U N C T I O N S ______________________________________________________
+
+//------------------------------------------------------------------------------
+//! @brief Timeout handler for I2C communication
+//------------------------------------------------------------------------------
+static void timeoutHandler( void *ptr ) {
+ drvAsynI2C *pi2c = static_cast<drvAsynI2C*>( ptr );
+ pi2c->timeout();
+}
+
+//------------------------------------------------------------------------------
+//! @brief Called when asyn clients call pasynOctet->read().
+//! @param [in] pasynUser pasynUser structure that encodes the reason and address.
+//! @param [out] value Address of the string to read.
+//! @param [in] maxChars Maximum number of characters to read.
+//! @param [out] nActual Number of characters actually read.
+//! @param [out] eomReason Reason that read terminated.
+//! @return in case of no error occured asynSuccess is returned. Otherwise
+//! asynError or asynTimeout is returned. A error message is stored
+//! in pasynUser->errorMessage.
+//! @sa writeOctet
+//!
+//! Read a byte stream from the I2C bus.
+//! To set the correct slave address the pasynOctet->write() call has to be used.
+//! If the slave has multiple registers, use the write call to setup slave
+//! address and register address, followed by this read call.
+///------------------------------------------------------------------------------
+asynStatus drvAsynI2C::readOctet( asynUser *pasynUser, char *value, size_t maxChars,
+ size_t *nActual, int *eomReason ) {
+ if( _fd < 0 ) {
+ epicsSnprintf( pasynUser->errorMessage, pasynUser->errorMessageSize,
+ "%s: %s disconnected:", portName, _deviceName );
+ return asynError;
+ }
+ if( maxChars <= 0 ) {
+ epicsSnprintf( pasynUser->errorMessage, pasynUser->errorMessageSize,
+ "%s: %s maxchars %d Why <=0?", portName, _deviceName, (int)maxChars );
+ return asynError;
+ }
+
+ int thisRead = 0;
+ int nRead = 0;
+ bool timerStarted = false;
+ asynStatus status = asynSuccess;
+
+ _timeout = false;
+
+ if( eomReason ) *eomReason = 0;
+
+ for(;;) {
+ if( !timerStarted && pasynUser->timeout > 0 ) {
+ epicsTimerStartDelay( i2c_timer, pasynUser->timeout );
+ timerStarted = true;
+ }
+ thisRead = read( _fd, value, maxChars );
+ if( thisRead > 0 ) {
+ nRead = thisRead;
+ break;
+ } else {
+ if( thisRead < 0 && ( errno != EWOULDBLOCK )
+ && ( errno != EINTR )
+ && ( errno != EAGAIN ) ) {
+ epicsSnprintf( pasynUser->errorMessage, pasynUser->errorMessageSize,
+ "%s: %s read error: %s",
+ portName, _deviceName, strerror( errno ) );
+ status = asynError;
+ break;
+ }
+ }
+ if( _timeout ) break;
+ }
+ if( timerStarted ) epicsTimerCancel( i2c_timer );
+ if( _timeout && asynSuccess == status ) status = asynTimeout;
+
+/*
+ int mytimeout = (int)( pasynUser->timeout * 1.e6 );
+ if ( 0 > mytimeout ) {
+
+ nbytes = read( _fd, value, maxChars );
+ if ( 0 > nbytes ) {
+ epicsSnprintf( pasynUser->errorMessage, pasynUser->errorMessageSize,
+ "Error receiving message from device '%s': %d %s",
+ _deviceName, errno, strerror( errno ) );
+ return asynError;
+ }
+
+ } else {
+
+ fd_set fdRead;
+ struct timeval t;
+
+ // calculate timeout values
+ t.tv_sec = mytimeout / 1000000L;
+ t.tv_usec = mytimeout % 1000000L;
+
+ FD_ZERO( &fdRead );
+ FD_SET( _fd, &fdRead );
+
+ // wait until timeout or a message is ready to get read
+ int err = select( _fd + 1, &fdRead, NULL, NULL, &t );
+
+ // the only one file descriptor is ready for read
+ if ( 0 < err ) {
+ nbytes = read( _fd, value, maxChars );
+ if ( 0 > nbytes ) {
+ epicsSnprintf( pasynUser->errorMessage, pasynUser->errorMessageSize,
+ "Error receiving message from device '%s': %d %s",
+ _deviceName, errno, strerror( errno ) );
+ return asynError;
+ }
+ }
+
+ // nothing is ready, timeout occured
+ if ( 0 == err ) return asynTimeout;
+ if ( 0 > err ) {
+ epicsSnprintf( pasynUser->errorMessage, pasynUser->errorMessageSize,
+ "Error receiving message from device '%s': %d %s",
+ _deviceName, errno, strerror( errno ) );
+ return asynError;
+ }
+ }
+*/
+
+ *nActual = nRead;
+ if( eomReason && *nActual >= maxChars ) {
+ *eomReason = ASYN_EOM_CNT;
+ }
+
+ asynPrint( pasynUser, ASYN_TRACEIO_DRIVER,
+ "%s: read %lu bytes from %s, return %s\n",
+ portName, (unsigned long)*nActual, _deviceName,
+ pasynManager->strStatus( status ) );
+
+ return status;
+}
+
+//------------------------------------------------------------------------------
+//! @brief Called when asyn clients call pasynOctet->write().
+//! @param [in] pasynUser pasynUser structure that encodes the reason and address.
+//! @param [in] value Address of the string to write.
+//! @param [in] nChars Number of characters to write.
+//! @param [out] nActual Number of characters actually written.
+//! @return in case of no error occured asynSuccess is returned. Otherwise
+//! asynError or asynTimeout is returned. A error message is stored
+//! in pasynUser->errorMessage.
+//!
+//! Write a byte stream to the i2c bus. First byte holds the slave address, followed
+//! by the acutal data to send.
+//! Example: Setting the configuration register of the AD7998 to convert all
+//! 8 channels and use the BUSY output:
+//! value = 0x20 0x02 0x0ffa
+//! Depending on the slave device, multiple write commands can be concatenated
+///------------------------------------------------------------------------------
+asynStatus drvAsynI2C::writeOctet( asynUser *pasynUser, char const *value, size_t maxChars,
+ size_t *nActual ){
+
+ int thisWrite = 0;
+ bool timerStarted = false;
+ asynStatus status = asynSuccess;
+
+ if( _fd < 0 ) {
+ epicsSnprintf( pasynUser->errorMessage, pasynUser->errorMessageSize,
+ "%s: %s disconnected:", portName, _deviceName );
+ return asynError;
+ }
+ asynPrint( pasynUser, ASYN_TRACEIO_DRIVER,
+ "drvAsynI2C::writeOctet: trying to send %u bytes\n",
+ maxChars );
+
+ if( 0 == maxChars ) {
+ *nActual = 0;
+ return asynSuccess;
+ }
+
+ _timeout = false;
+
+ printf( "drvAsynI2C::writeOctet: sending: ");
+ for( size_t viech = 0; viech < maxChars; ++viech ) {
+ printf( "0x%02x", value[viech] );
+ }
+ printf( "\n" );
+
+ int addr = value[0];
+ if( addr != _slaveAddress ) {
+ // set slave address
+ if( ioctl( _fd, I2C_SLAVE, addr ) < 0 ) {
+ epicsSnprintf( pasynUser->errorMessage, pasynUser->errorMessageSize,
+ "%s: %s Can't set slave address: %s",
+ portName, _deviceName, strerror( errno ) );
+ return asynError;
+
+ _slaveAddress = addr;
+ }
+ }
+ ++value;
+ int nleft = maxChars - 1;
+ if( 0 < nleft ) {
+
+ if( pasynUser->timeout > 0 ) {
+ epicsTimerStartDelay( i2c_timer, pasynUser->timeout );
+ timerStarted = true;
+ }
+
+ for(;;) {
+ thisWrite = write( _fd, value, nleft );
+ if ( 0 < thisWrite ) {
+ nleft -= thisWrite;
+ if( 0 == nleft ) break;
+ value += thisWrite;
+ }
+ if( _timeout || 0 == pasynUser->timeout ) {
+ status = asynTimeout;
+ break;
+ }
+ if( 0 > thisWrite && ( errno != EWOULDBLOCK )
+ && ( errno != EINTR )
+ && ( errno != EAGAIN ) ) {
+ epicsSnprintf( pasynUser->errorMessage, pasynUser->errorMessageSize,
+ "%s: %s write error: %s",
+ portName, _deviceName, strerror( errno ) );
+ status = asynError;
+ break;
+ }
+ }
+ if( timerStarted ) epicsTimerCancel( i2c_timer );
+
+ }
+
+ *nActual = maxChars - nleft;
+
+ asynPrint( pasynUser, ASYN_TRACEIO_DRIVER,
+ "%s: wrote %lu bytes to %s, return %s\n",
+ portName, (unsigned long)*nActual, _deviceName,
+ pasynManager->strStatus( status ) );
+
+ return status;
+}
+
+//------------------------------------------------------------------------------
+//! @brief Connect driver to device
+//! @param [in] pasynUser pasynUser structure that encodes the reason and address.
+//! @return in case of no error occured asynSuccess is returned. Otherwise
+//! asynError or asynTimeout is returned. A error message is stored
+//! in pasynUser->errorMessage.
+///------------------------------------------------------------------------------
+asynStatus drvAsynI2C::connect( asynUser *pasynUser ) {
+ if( _fd >= 0 ) {
+ epicsSnprintf( pasynUser->errorMessage,pasynUser->errorMessageSize,
+ "%s: Link to %s already open!", portName, _deviceName );
+ return asynError;
+ }
+ asynPrint( pasynUser, ASYN_TRACEIO_DRIVER,
+ "%s: Open connection to %s\n", portName, _deviceName );
+
+ if( ( _fd = open( _deviceName, O_RDWR ) ) < 0 ) {
+ epicsSnprintf( pasynUser->errorMessage,pasynUser->errorMessageSize,
+ "%s: Can't open %s: %s", portName, _deviceName, strerror( errno ) );
+ return asynError;
+ }
+ if( ioctl( _fd, I2C_FUNCS, &_i2cfuncs ) < 0 ) {
+ epicsSnprintf( pasynUser->errorMessage,pasynUser->errorMessageSize,
+ "%s: Can't get functionality of %s: %s", portName, _deviceName, strerror( errno ) );
+ return asynError;
+ }
+ pasynManager->exceptionConnect( pasynUser );
+ return asynSuccess;
+}
+
+//------------------------------------------------------------------------------
+//! @brief Disconnect driver from device
+//! @param [in] pasynUser pasynUser structure that encodes the reason and address.
+//! @return in case of no error occured asynSuccess is returned. Otherwise
+//! asynError or asynTimeout is returned. A error message is stored
+//! in pasynUser->errorMessage.
+///------------------------------------------------------------------------------
+asynStatus drvAsynI2C::disconnect( asynUser *pasynUser ) {
+ asynPrint( pasynUser, ASYN_TRACEIO_DRIVER,
+ "%s: disconnect %s\n", portName, _deviceName );
+ epicsTimerCancel( i2c_timer );
+ if( _fd >= 0 ) {
+ close( _fd );
+ _fd = -1;
+ pasynManager->exceptionDisconnect(pasynUser);
+ }
+ return asynSuccess;
+}
+
+//------------------------------------------------------------------------------
+//! @brief Standard C'tor.
+//! @param [in] portName The name of the asynPortDriver to be created.
+//! @param [in] ttyName The name of the device
+//------------------------------------------------------------------------------
+drvAsynI2C::drvAsynI2C( const char *portName, const char *ttyName )
+ : asynPortDriver( portName,
+ 0, // maxAddr
+ 0, // paramTableSize
+ asynCommonMask | asynOctetMask | asynDrvUserMask, // Interface mask
+ asynCommonMask | asynOctetMask, // Interrupt mask
+ ASYN_CANBLOCK, // asynFlags
+ 1, // Autoconnect
+ 0, // Default priority
+ 0 ) // Default stack size
+{
+ _deviceName = epicsStrDup( ttyName );
+ _fd = -1;
+ _slaveAddress = 0;
+// if( ( _fd = open( _deviceName, O_RDWR ) ) < 0 ) {
+// std::cerr << "Cannot open port" << std::endl;
+// return;
+// }
+// if( ioctl( _fd, I2C_FUNCS, &_i2cfuncs ) < 0 ) {
+// std::cerr << "Cannont get I2C_FUNCS" << std::endl;
+// return;
+// }
+}
+
+// Configuration routines. Called directly, or from the iocsh function below
+extern "C" {
+ //----------------------------------------------------------------------------
+ //! @brief EPICS iocsh callable function to call constructor
+ //! for the drvAsynI2C class.
+ //! @param [in] portName The name of the asyn port driver to be created.
+ //! @param [in] ttyName The name of the interface
+ //----------------------------------------------------------------------------
+ int drvAsynI2CConfigure( const char *portName, const char *ttyName ) {
+ if( !portName ) {
+ printf( "Port name missing.\n" );
+ return -1;
+ }
+ if( !ttyName ) {
+ printf( "TTY name missing.\n" );
+ return -1;
+ }
+ drvAsynI2C* pi2c = new drvAsynI2C( portName, ttyName );
+ i2c_timerQueue = epicsTimerQueueAllocate( 1, epicsThreadPriorityScanLow );
+ i2c_timer = epicsTimerQueueCreateTimer( i2c_timerQueue, timeoutHandler, pi2c );
+ if( !i2c_timer ) {
+ printf( "drvAsynI2C: Can't create timer.\n");
+ return -1;
+ }
+ return( asynSuccess );
+ }
+ static const iocshArg initI2CArg0 = { "portName", iocshArgString };
+ static const iocshArg initI2CArg1 = { "ttyName", iocshArgString };
+ static const iocshArg * const initI2CArgs[] = { &initI2CArg0, &initI2CArg1 };
+ static const iocshFuncDef initI2CFuncDef = { "drvAsynI2CConfigure", 2, initI2CArgs };
+ static void initI2CCallFunc( const iocshArgBuf *args ) {
+ drvAsynI2CConfigure( args[0].sval, args[1].sval );
+ }
+
+ //----------------------------------------------------------------------------
+ //! @brief Register functions to EPICS
+ //----------------------------------------------------------------------------
+ void drvAsynI2CRegister( void ) {
+ static int firstTime = 1;
+ if ( firstTime ) {
+ iocshRegister( &initI2CFuncDef, initI2CCallFunc );
+ firstTime = 0;
+ }
+ }
+
+ epicsExportRegistrar( drvAsynI2CRegister );
+}
+