/* * pulserAsynPar.hh * * Created on: April 12, 2013 * Author: Hovanes Egiyan * * This file defines a augmenting class for drvPulser to keep track of asyn parameters. * Some of the methods of this class expect to receive the pointer of the drvPulser object * for which the parameter instance is used for. So, this class is very much tied to the * drvPulser class and should not be considered as "an" independent class, these objects expect * to be members of drvPulser class object. */ #ifndef __PULSER_ASYNPAR_HH__ #define __PULSER_ASYNPAR_HH__ /************/ /* Includes */ /************/ #include #include #include #include #include #include /* EPICS includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "drvPulser.hh" #include "MtxLock.hh" // Base class for asyn parameters class pulserVAsynPar { protected: void* pvapReadFun; //! Address of the function to call for reading void* pvapWriteFun; //! Address of the function to call for writing int pvapParNum; //! Asyn driver parameter index string pvapParName; //! Asyn parameter name epicsUInt32 pvapMask; //! Mask for reading int pvapElmNum; //! Number of elements the driver will return bool pvapSingleChannel; //! Flag to indicate that this parameter is single channel pthread_mutex_t pvapMutex; //! Mutex for an object pthread_mutexattr_t pvapMtxAttr; //! Mutex attributes for an object static pthread_mutex_t pvapClassMutex; //! Class mutex for this class static pthread_mutexattr_t pvapClassMtxAttr; //! Class mutex attributes static epicsUInt32 pvapAllBitMask; //! Enable all 32 bits static int dummyInt; //! Initialize the mutex for the object void InitMutex(); //! Destroy the mutex for the object void CloseMutex(); //! Lock the class mutex for this class inline static int ClassLock() { return pthread_mutex_lock( &pvapClassMutex ); } //! Unlock the class mutex for this class inline static int ClassUnlock() { return pthread_mutex_unlock( &pvapClassMutex ); } public: pulserVAsynPar(): pvapReadFun(0), pvapWriteFun(0), pvapParNum(-1), pvapParName(""), pvapMask(pvapAllBitMask), pvapElmNum(0), pvapSingleChannel(true) { InitMutex(); } // This is the main constructor. It only initialized the parameters of the asyn parameter. // This method is expected to be called before the corresponding AsynDriver is created, therefore // actual creation of the parameter for an asyn driver should happen later from the AsynDriver's // constructor. This weird feature is because of the way AsynPortDriver class is organized. pulserVAsynPar( string parName, int parNum, void* readFun, void* writeFun, bool singleFlag, epicsUInt32 mask ) : pvapReadFun(readFun), pvapWriteFun(writeFun), pvapParNum(parNum), pvapParName(parName), pvapMask(mask), pvapElmNum(0), pvapSingleChannel(singleFlag) { InitMutex(); } pulserVAsynPar( const pulserVAsynPar& par ) : pvapReadFun(par.pvapReadFun), pvapWriteFun(par.pvapWriteFun), pvapParNum(par.pvapParNum), pvapParName(par.pvapParName), pvapMask(par.pvapMask), pvapElmNum(par.pvapElmNum), pvapSingleChannel(par.pvapSingleChannel) { InitMutex(); } virtual ~pulserVAsynPar() { CloseMutex(); } virtual pulserVAsynPar& operator=( const pulserVAsynPar& argPar ) { pulserVAsynPar& par = const_cast( argPar ); // Remove constness MtxLock selfObjLock(pvapMutex); MtxLock otherObjLock(par.pvapMutex); pvapReadFun = par.pvapReadFun; pvapWriteFun = par.pvapWriteFun; pvapParNum = par.pvapParNum; pvapParName = par.pvapParName; pvapMask = par.pvapMask; pvapElmNum = par.pvapElmNum; pvapSingleChannel=par.pvapSingleChannel; return *this; } virtual asynStatus Write( drvPulser* driver, unsigned channel = 0, void* value = 0, epicsUInt32 mask = pvapAllBitMask ) = 0; virtual asynStatus Read( drvPulser* driver, unsigned channel = 0, void* value = 0, epicsUInt32* mask = 0 ) = 0; inline string GetName() { MtxLock objLock(pvapMutex); return pvapParName; } inline string SetName( string name ) { MtxLock objLock(pvapMutex); return ( pvapParName = name ) ; } inline int GetNumber() { MtxLock objLock(pvapMutex); return pvapParNum; } inline int SetNumber( int num ) { MtxLock objLock(pvapMutex); return ( pvapParNum = num ); } inline epicsUInt32 GetMask() { MtxLock objLock(pvapMutex); return pvapMask; } inline epicsUInt32 SetMask( epicsUInt32 mask ) { MtxLock objLock(pvapMutex); return ( pvapMask = mask ); } inline bool IsSingleChannel() { MtxLock objLock(pvapMutex); return pvapSingleChannel; } // inline bool IsSingleChannel() { return pvapSingleChannel; } virtual void Connect2Driver( drvPulser* driver ) = 0; static epicsUInt32 GetAllBitMask() { MtxLock classLock(pvapClassMutex); return pvapAllBitMask; } //! Initialize global mutex static int InitGlobalMutex(); }; // Mutex manipulation functions //! Initialize global mutex int pulserVAsynPar::InitGlobalMutex() { pthread_mutexattr_init( &pvapClassMtxAttr ); pthread_mutexattr_setpshared( &pvapClassMtxAttr, PTHREAD_PROCESS_PRIVATE ); pthread_mutex_init( &pvapClassMutex, &pvapClassMtxAttr ); return 0; } //! Initialize mutex for individual objects void pulserVAsynPar::InitMutex() { pthread_mutexattr_init( &pvapMtxAttr ); pthread_mutexattr_setpshared( &pvapMtxAttr, PTHREAD_PROCESS_PRIVATE ); pthread_mutex_init( &pvapMutex, &pvapMtxAttr ); return; } //! Destroy mutex for individual objects void pulserVAsynPar::CloseMutex() { pthread_mutex_destroy( &pvapMutex ); pthread_mutexattr_destroy( &pvapMtxAttr ); return; } // Template class for asyn parameters. For different parameters types we will // use specialized methods for particular variable types. This class inherits // from the virtual base class. template class pulserAsynPar : public pulserVAsynPar { private: VarType papValue; // Parameter value public: // Default constructor pulserAsynPar() : papValue() {}; // Main constructor pulserAsynPar( string parName, int parNum, void* readFun, void* writeFun, bool singleFlag, epicsUInt32 mask = pvapAllBitMask ) : pulserVAsynPar( parName, parNum, readFun, writeFun, singleFlag, mask ), papValue() { cout << "Created pulserAsynPar " << this->GetName() << " with pvapSingleChannel flag " << this->IsSingleChannel() << endl; }; virtual ~pulserAsynPar() { ; } // Copy constructor pulserAsynPar( const pulserAsynPar& par ) : pulserVAsynPar(par), papValue( par.GetValue() ) {;} // Copy operator virtual pulserAsynPar& operator=( const pulserAsynPar& argPar ) { pulserAsynPar& par = const_cast( argPar ); pulserVAsynPar* baseClassPtr = dynamic_cast( this ); baseClassPtr->operator=( par ); MtxLock selfObjLock( pvapMutex ); MtxLock otherObjLock( par.pvapMutex ); papValue = par.papValue; return *this; } // Method to create the parameter for the driver passed by the argument. // This method is expected to be called from the AsynDriver constructor virtual void Connect2Driver( drvPulser* driver ) { cout << "Must use a specialized Connect2Driver method" << endl; exit(-1) ; } virtual asynStatus Write( drvPulser* driver, unsigned chan = 0, void* valPtr = 0, epicsUInt32 mask = pvapAllBitMask ) // Virtual Write asyn parameter { return this->WriteTmpl( driver, chan, valPtr, mask ) ;} virtual asynStatus Read( drvPulser* driver, unsigned chan = 0, void* valPtr = 0, epicsUInt32* mask = 0 ) // Virtual Read asyn parameter { return this->ReadTmpl ( driver, chan, valPtr, mask ) ; } virtual asynStatus WriteTmpl( drvPulser* driver, unsigned chan = 0, void* valPtr = 0, epicsUInt32 mask = pvapAllBitMask ) // Write asyn parameter { cout << "Must use a specialized Write method" << endl; exit(-1) ;} virtual asynStatus ReadTmpl( drvPulser* driver, unsigned chan = 0, void* valPtr = 0, epicsUInt32* mask = 0 ) // Read asyn parameter { cout << "Must use a specialized Read method" << endl; exit(-1) ;} inline VarType GetValue() { MtxLock objLock(pvapMutex); return papValue; } inline VarType SetValue( const VarType value ) { MtxLock objLock(pvapMutex); return (papValue = value); } }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Specialized method for UInt32 to create the parameter for the driver passed by the argument. // This method is expected to be called from the AsynDriver constructor template <> void pulserAsynPar::Connect2Driver( drvPulser* driver ) { MtxLock objLock(pvapMutex); driver->createParam( pvapParName.c_str(), asynParamInt32, &pvapParNum ) ; return; } // Specialized for Int32 method to write asyn parameter template <> asynStatus pulserAsynPar::WriteTmpl( drvPulser* driver, unsigned chan, void* valPtr, epicsUInt32 mask ) { char* bAddr = driver->getBaseAddress(); MtxLock objLock(pvapMutex); if( pvapWriteFun != 0 ) { asynStatus aStat = asynError; epicsUInt32 value = *static_cast( valPtr ); objLock.Unlock(); cout << "Setting Int32 parameter " << pvapParName << " for channel " << chan << " to " << value << endl; objLock.Lock(); int drvStat = pulser_OK; if( pvapSingleChannel ) { int16_t (*fun2callPtr)( char*, epicsUInt32 ) = ( int16_t (*)( char*, epicsUInt32 ) ) pvapWriteFun; drvStat = (*fun2callPtr)( bAddr, value ); } else { int16_t (*fun2callPtr)( char*, unsigned, epicsUInt32 ) = ( int16_t (*)( char*, unsigned, epicsUInt32 ) ) pvapWriteFun; drvStat = (*fun2callPtr)( bAddr, chan, value ); } if ( drvStat == pulser_OK ) { aStat = driver->setIntegerParam( chan, pvapParNum, value ); } return aStat; } return asynError; } // Specialized for Int32 to read asyn parameter template <> asynStatus pulserAsynPar::ReadTmpl( drvPulser* driver, unsigned chan, void* valPtr, epicsUInt32* mask ) { char* bAddr = driver->getBaseAddress(); MtxLock objLock(pvapMutex); if( pvapReadFun != 0 ) { asynStatus aStat = asynError; epicsUInt32 tmpValue; int drvStat = pulser_OK; if( pvapSingleChannel ) { int16_t (*fun2callPtr)( char*, epicsUInt32* ) = ( int16_t (*)( char*, epicsUInt32* ) ) pvapReadFun; // cout << "Calling read function for singlechannel parameter " << pvapParName << " channel " << chan << endl; drvStat = (*fun2callPtr)( bAddr, &tmpValue ); } else { int16_t (*fun2callPtr)( char*, unsigned, epicsUInt32* ) = ( int16_t (*)( char*, unsigned, epicsUInt32* ) ) pvapReadFun; // cout << "Calling read function for multichannel parameter " << pvapParName << " channel " << chan << endl; drvStat = (*fun2callPtr)( bAddr, chan, &tmpValue ); } if ( drvStat == 0 ) { papValue = *(reinterpret_cast(&tmpValue)); aStat = driver->setIntegerParam( chan, pvapParNum, papValue ); if( valPtr != 0 ) *(static_cast(valPtr)) = *reinterpret_cast(&tmpValue); } return aStat; } return asynError; } // Specialized method for UInt32 to create the parameter for the driver passed by the argument. // This method is expected to be called from the AsynDriver constructor template <> void pulserAsynPar::Connect2Driver( drvPulser* driver ) { cout << " Inside pulserAsynPar::Connect2Driver" << endl; MtxLock objLock(pvapMutex); driver->createParam( pvapParName.c_str(), asynParamUInt32Digital, &pvapParNum ) ; return; } // Specialized for UInt32 method to write asyn parameter template <> asynStatus pulserAsynPar::WriteTmpl( drvPulser* driver, unsigned chan, void* valPtr, epicsUInt32 mask ) { // cout << " Inside pulserAsynPar::WriteTmpl" << endl; char* bAddr = driver->getBaseAddress(); MtxLock objLock(pvapMutex); if( pvapWriteFun != 0 ) { asynStatus aStat = asynError; epicsUInt32 value = *static_cast( valPtr ); cout << "Setting UInt32 parameter " << pvapParName << " for channel " << chan << " to " << value << endl; int drvStat pulser_OK; if( pvapSingleChannel ) { int16_t (*fun2callPtr)( char*, epicsUInt32 ) = ( int16_t (*)( char*, epicsUInt32 ) ) pvapWriteFun; drvStat = (*fun2callPtr)( bAddr, value ); } else { int16_t (*fun2callPtr)( char*, unsigned, epicsUInt32 ) = ( int16_t (*)( char*, unsigned, epicsUInt32 ) ) pvapWriteFun; drvStat = (*fun2callPtr)( bAddr, chan, value ); } if ( drvStat == pulser_OK ) { aStat = driver->setUIntDigitalParam( chan, pvapParNum, value, mask, mask ); } return aStat; } return asynError; } // Specialized for UInt32 to read asyn parameter template <> asynStatus pulserAsynPar::ReadTmpl( drvPulser* driver, unsigned chan, void* valPtr, epicsUInt32* mask ) { // cout << " Inside pulserAsynPar::ReadTmpl" << endl; char* bAddr = driver->getBaseAddress(); MtxLock objLock(pvapMutex); if( pvapReadFun != 0 ) { asynStatus aStat = asynError; epicsUInt32 tmpValue; int drvStat = pulser_OK; if( pvapSingleChannel ) { int16_t (*fun2callPtr)( char*, epicsUInt32* ) = ( int16_t (*)( char*, epicsUInt32* ) ) pvapReadFun; // cout << "Calling read function for singlechannel parameter " << pvapParName << " channel " << chan << endl; drvStat = (*fun2callPtr)( bAddr, &tmpValue ); } else { int16_t (*fun2callPtr)( char*, unsigned, epicsUInt32* ) = ( int16_t (*)( char*, unsigned, epicsUInt32* ) ) pvapReadFun; // cout << "Calling read function for multichannel parameter " << pvapParName << " channel " << chan << endl; drvStat = (*fun2callPtr)( bAddr, chan, &tmpValue ); } if ( drvStat == 0 ) { papValue = tmpValue; aStat = driver->setUIntDigitalParam( chan, pvapParNum, papValue, *mask, *mask ); if( valPtr != 0 ) *(static_cast(valPtr)) = tmpValue; } return aStat; } return asynError; } #endif /* __PULSER_ASYNPAR_HH__ */