/* File: drvPulser.h * Author: Hovanes Egiyan, Jefferson Lab * Date: 14-Apr-2013 * * Purpose: * This module provides the driver support for the asyn device support layer * for the CAEN V1495 board with Ben Raydo's (JLAB) FPGA firmware. * * */ #include "drvPulser.hh" #include "pulserAsynPar.hh" //! Initialize global mutex and its attributes for the parameter class volatile static int dummyInt1 = pulserVAsynPar::InitGlobalMutex(); //! Declare global mutex and its attributes pthread_mutex_t pulserVAsynPar::pvapClassMutex; pthread_mutexattr_t pulserVAsynPar::pvapClassMtxAttr; // All 32 bits are set to 1 for the parameter class static variable epicsUInt32 pulserVAsynPar::pvapAllBitMask = ~static_cast(0); //! Initialize global mutex and its attributes volatile static int dummyInt2 = drvPulser::initGlobalMutex(); //! Declare global mutex and its attributes pthread_mutex_t drvPulser::dpClassMutex; pthread_mutexattr_t drvPulser::dpClassMtxAttr; // Driver name string drvPulser::dpDriverName = "asynPulser"; // Open VME window at the loading time //static int dummyInt3 = vmeOpenDefaultWindows(); static int dummyInt3 = drvPulser::initVME(); /***************/ /* Definitions */ /***************/ vector drvPulser::createParVector() { string functionName = "drvPulser::createParVector"; cout << "In function " << functionName << endl; vector tempVec; tempVec.push_back( new pulserAsynPar ( "PULSER_PULSER_ID" , tempVec.size() , (void*)pulser_getPulserId , (void*)0 , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_FIRMWARE" , tempVec.size() , (void*)pulser_getFirmwareRev , (void*)0 , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_BOARD_IDS" , tempVec.size() , (void*)pulser_getDaughterBoardId , (void*)0 , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_JUMPERS" , tempVec.size() , (void*)pulser_getJumpers , (void*)0 , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_STATUS_H" , tempVec.size() , (void*)pulser_getPulserStatusH , (void*)0 , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_STATUS_L" , tempVec.size() , (void*)pulser_getPulserStatusL , (void*)0 , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_START_MASK_H" , tempVec.size() , (void*)pulser_getPulserStartMaskH , (void*)pulser_setPulserStartMaskH , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_START_MASK_L" , tempVec.size() , (void*)pulser_getPulserStartMaskL , (void*)pulser_setPulserStartMaskL , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_STOP_MASK_H" , tempVec.size() , (void*)pulser_getPulserStopMaskH , (void*)pulser_setPulserStopMaskH , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_STOP_MASK_L" , tempVec.size() , (void*)pulser_getPulserStopMaskL , (void*)pulser_setPulserStopMaskL , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_GIN_MASK_H" , tempVec.size() , (void*)pulser_getPulserGinMaskH , (void*)pulser_setPulserGinMaskH , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_GIN_MASK_L" , tempVec.size() , (void*)pulser_getPulserGinMaskL , (void*)pulser_setPulserGinMaskL , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_START_STOP" , tempVec.size() , (void*)pulser_getPulserStartStop , (void*)pulser_setPulserStartStop , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_MOR_DELAY" , tempVec.size() , (void*)pulser_getMasterOrDelay , (void*)pulser_setMasterOrDelay , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_MOR_WIDTH" , tempVec.size() , (void*)pulser_getMasterOrWidth , (void*)pulser_setMasterOrWidth , true ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_PERIOD" , tempVec.size() , (void*)pulser_getPeriod , (void*)pulser_setPeriod , false ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_WIDTH" , tempVec.size() , (void*)pulser_getWidth , (void*)pulser_setWidth , false ) ); tempVec.push_back( new pulserAsynPar ( "PULSER_NPULSES" , tempVec.size() , (void*)pulser_getNPulses , (void*)pulser_setNPulses , false ) ); return tempVec; } /*Constructor */ drvPulser::drvPulser( const char* asynPortName, char* baseAddr, vector& parVec ) : asynPortDriver( asynPortName, PULSER_NUM, parVec.size(), asynUInt32DigitalMask | asynInt32Mask | asynDrvUserMask , asynUInt32DigitalMask | asynInt32Mask , ASYN_CANBLOCK | ASYN_MULTIDEVICE, 1, 0, 0 ), dpParVec(parVec), dpExists(false), dpBaseAddr(baseAddr) { initMutex(); cout << "Starting driver for " << asynPortName << " at base address " << (unsigned)baseAddr << endl; string functionName = "drvPulser::drvPulser"; unsigned bAddr = reinterpret_cast( baseAddr ); { MtxLock objLock(dpMutex); this->dpEventID = epicsEventCreate(epicsEventEmpty); } // Uncomment this line to enable asynTraceFlow during the constructor // pasynTrace->setTraceMask( pasynUserSelf, 0x11 ); pasynTrace->setTraceMask(pasynUserSelf, 0x255); cout << "Will connect the parameters for ASYN port " << asynPortName << endl; unsigned nPars = 0; { MtxLock objLock( dpMutex ); nPars = dpParVec.size(); } cout << "Parameter vector size is " << nPars << endl; // Connect all parameters in the parameter vector to this driver for( unsigned ix = 0; ix < nPars; ix++ ) { pulserVAsynPar* parPtr = 0; { MtxLock objLock( dpMutex ); parPtr = dpParVec[ix]; } cout << "At parameter number " << ix << endl; cout << functionName << ": Connecting parameter " << parPtr->GetName() << endl; parPtr->Connect2Driver(this); usleep(10000); cout << "Parameter " << parPtr->GetName() << " is connected " << endl; } cout << "Checking address " << hex << bAddr << dec << endl; // Check if there is a pulser at that base address stringstream ssMessage; pulser_REGS* pulserRegs = 0; if( pulser_getRegs( reinterpret_cast(bAddr), &pulserRegs ) == pulser_OK ) { if( pulserRegs != 0 ) { epicsUInt32 idVal; if( pulser_getPulserId( reinterpret_cast(bAddr), &idVal ) == pulser_OK ) { cout << "ID is " << hex << showbase << idVal << endl; if( idVal != TRUE_PULSER_ID ) { ssMessage << "Did not find a pulser board with base address " << hex << showbase << bAddr ; throw std::runtime_error( ssMessage.str() ); } } else { ssMessage << "Could not execute pulser_getPulserId with base address " << hex << showbase << bAddr; throw runtime_error( ssMessage.str() ); } } else { ssMessage << "Pulser Registers with base address " << hex << showbase << bAddr << " are at bad address " << pulserRegs; throw runtime_error( ssMessage.str() ); } } else { ssMessage << "Bad base address " << hex << showbase << bAddr ; throw runtime_error( ssMessage.str() ); } cout << "Launching thread" << endl; int status = 0; /* Create the thread that reads the values from the registers in the background */ status = (asynStatus)( epicsThreadCreate(portName, epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), (EPICSTHREADFUNC) ::readPulserRegisters, this) == NULL); if ( status ) { printf( "%s: epicsThreadCreate failure\n", functionName.c_str() ); ssMessage << "Could not start thread from " << drvPulser::getDriverName() << " for asyn port " << portName ; throw runtime_error( ssMessage.str() ); } { MtxLock objLock( dpMutex ); dpExists = true; } return; } asynStatus drvPulser::readInt32( asynUser* pasynUser, epicsInt32* value ) { string functionName = "drvPulser::readInt32"; int function = pasynUser->reason; int chanNum; getAddress( pasynUser, &chanNum ); printf( "Now inside %s , command is %d, value will be written to 0x%x\n", functionName.c_str(), function, (unsigned)value ); asynStatus aStat = asynError; unsigned nPars = 0; { MtxLock objLock( dpMutex ); nPars = dpParVec.size(); } // Find the parameter in the list and read the parameter value for( unsigned ix = 0; ix < nPars; ix++ ) { pulserVAsynPar* parPtr = 0; { MtxLock objLock( dpMutex ); parPtr = dpParVec[ix]; } if( function == parPtr->GetNumber() ) { aStat = parPtr->Read( this, chanNum, value ); if( aStat == asynError ) { asynPrint( pasynUser, ASYN_TRACE_ERROR, "%s:%s error, status=%d address=%d, function=%d, value=%d\n", drvPulser::getDriverName().c_str(), functionName.c_str(), aStat, chanNum, function, value ); return aStat; } else { asynPrint( pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s:: address=%d, function=%d, value=%d\n", drvPulser::getDriverName().c_str(), functionName.c_str(), chanNum, function, value ); callParamCallbacks( chanNum ); return aStat; } } } return aStat; } // Override the method for writing unsigned 32-bit integers asynStatus drvPulser::writeInt32( asynUser* pasynUser, epicsInt32 value ) { int command = pasynUser->reason; int channel; getAddress( pasynUser, &channel ); string functionName = "drvPulser::writeInt32"; printf( "Now inside %s , command is %d, value is %d \n", functionName.c_str(), command, value ); pulserVAsynPar* parPtr = 0; { MtxLock objLock( dpMutex ); if ( !dpExists ) return asynError; parPtr = dpParVec[command]; } asynStatus aStat = parPtr->Write( this, static_cast(channel), static_cast(&value) ); if ( aStat == asynSuccess ) { printf("Calling callbacks after setting parameter %d named %s for channel %d \n ", command, parPtr->GetName().c_str(), channel ); aStat = callParamCallbacks( channel ); } return aStat; } asynStatus drvPulser::readUInt32Digital( asynUser* pasynUser, epicsUInt32* value, epicsUInt32 mask ) { string functionName = "drvPulser::readUInt32Digital"; // cout << "In function " << functionName << endl; int function = pasynUser->reason; int chanNum; getAddress( pasynUser, &chanNum ); epicsUInt32 tempMask = mask; asynStatus aStat = asynError; unsigned nPars = 0; { MtxLock objLock( dpMutex ); nPars = dpParVec.size(); } // Find the parameter in the list and read the parameter value for( unsigned ix = 0; ix < nPars; ix++ ) { pulserVAsynPar* parPtr = 0; { MtxLock objLock( dpMutex ); parPtr = dpParVec[ix]; } if( function == parPtr->GetNumber() ) { aStat = parPtr->Read( this, chanNum, value, &tempMask ); if( aStat == asynError ) { asynPrint( pasynUser, ASYN_TRACE_ERROR, "%s:%s error, status=%d address=%d, function=%d, value=%d\n", drvPulser::getDriverName().c_str(), functionName.c_str(), aStat, chanNum, function, value ); return aStat; } else { asynPrint( pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s:: address=%d, function=%d, value=%d\n", drvPulser::getDriverName().c_str(), functionName.c_str(), chanNum, function, value ); callParamCallbacks( chanNum ); return aStat; } } } return aStat; } // Override the method for writing unsigned 32-bit integers asynStatus drvPulser::writeUInt32Digital( asynUser* pasynUser, epicsUInt32 value, epicsUInt32 mask ) { int command = pasynUser->reason; int channel; getAddress( pasynUser, &channel ); string functionName = "drvPulser::writeUInt32Digital"; printf( "Now inside %s , command is %d, value is %d \n", functionName.c_str(), command, value ); pulserVAsynPar* parPtr = 0; { MtxLock objLock( dpMutex ); if ( !dpExists ) return asynError; parPtr = dpParVec[command]; } asynStatus aStat = parPtr->Write( this, static_cast(channel), static_cast(&value), mask ); if ( aStat == asynSuccess ) { printf("Calling callbacks after setting parameter %d named %s for channel %d \n ", command, parPtr->GetName().c_str(), channel ); aStat = callParamCallbacks( channel ); } return aStat; } /* Report parameters */ void drvPulser::report( FILE *fp, int details ) { if ( details > 0 ) { // fprintf(fp, " Scratch Content = 0x%x\n", PulserSratch_ ); } // Call the base class method asynPortDriver::report( fp, details ); return; } // This function reads the registers and calls the callbacks // It is supposed to run in a separate thread for each board // Because the registers are read here we do not need to read // in our new readUInt32Digital function, just call the base version in // case the SCAN field is set to some fixed rate scanning. // The values read here will be moved to th appropriate records. void drvPulser::readRegisters( void ) { string functionName = "drvPulser::readRegisters"; cout << "In function " << functionName << endl; asynStatus aStat = asynError; // Set updating time to value in seconds double updateTime = 2.5; // while( taskDelay(sysClkRateGet()) ) { while( 1 ) { epicsEventId eventID; { MtxLock objLock( dpMutex ); eventID = this->dpEventID; } epicsEventWaitWithTimeout( eventID, updateTime ); // usleep( 100000 ); // if (run) epicsEventWaitWithTimeout(this->eventId, updateTime); // else epicsEventWait(this->eventId); // /* run could have changed while we were waiting */ // getIntegerParam(P_Run, &run); // if (!run) continue; // Loop over all asyn parameters and read them out for( unsigned iChan = 0; iChan < static_cast(this->maxAddr); iChan++ ) { unsigned nPars = 0; { MtxLock objLock( dpMutex ); nPars = dpParVec.size(); } for( unsigned iPar = 0; iPar < nPars; iPar++ ) { pulserVAsynPar* parPtr = 0; { MtxLock objLock( dpMutex ); parPtr = dpParVec[iPar]; } epicsUInt32 tmpVal = 0; epicsUInt32 tmpMask = pulserVAsynPar::GetAllBitMask(); aStat = parPtr->Read( this, iChan, &tmpVal, &tmpMask ); } callParamCallbacks( iChan ); } } } // Mutex manipulation functions //! Initialize global mutex int drvPulser::initGlobalMutex() { pthread_mutexattr_init( &dpClassMtxAttr ); pthread_mutexattr_setpshared( &dpClassMtxAttr, PTHREAD_PROCESS_PRIVATE ); pthread_mutex_init( &dpClassMutex, &dpClassMtxAttr ); return 0; } //! Initialize mutex for individual objects void drvPulser::initMutex() { pthread_mutexattr_init( &dpMtxAttr ); pthread_mutexattr_setpshared( &dpMtxAttr, PTHREAD_PROCESS_PRIVATE ); pthread_mutex_init( &dpMutex, &dpMtxAttr ); return; } //! Destroy mutex for individual objects void drvPulser::closeMutex() { pthread_mutex_destroy( &dpMutex ); pthread_mutexattr_destroy( &dpMtxAttr ); return; } int drvPulser::initVME() { int vmeOpenStat = vmeOpenDefaultWindows(); // if( vmeOpenStat == OK ) { // vmeBusKillLockShm( 1 ); // } return vmeOpenStat; } // This function simply calls the member function of the driver whose pointer // is passed as the argument. This function is to be called from a separate // thread and is specified in the argument when launching that thread void readPulserRegisters( void *drvPvt ) { drvPulser* pPvt = (drvPulser *)drvPvt; pPvt->readRegisters(); } extern "C" { char* drvPulserConfig( const char *portName, int baseAddr ) { // First create the vector of parameters for the driver to be started cout << "Creating vector of ASYN parameters " << endl; vector parVec = drvPulser::createParVector(); // Now create the asyn driver cout << "Creating the driver for " << portName << " for VME base address " << hex << baseAddr << dec << endl; drvPulser* pPulser = new drvPulser( portName, reinterpret_cast(baseAddr), parVec ); cout << "Driver has been created at address " << hex << pPulser << dec << endl; return reinterpret_cast( pPulser ); } /* iocsh config function */ static const iocshArg drvPulserConfigArg0 = { "Asyn port name", iocshArgString }; static const iocshArg drvPulserConfigArg1 = { "Base address", iocshArgInt }; static const iocshArg * const drvPulserConfigArgs[] = { &drvPulserConfigArg0, &drvPulserConfigArg1 }; static const iocshFuncDef drvPulserConfigFuncDef = {"drvPulserConfig", 2, drvPulserConfigArgs}; static void drvPulserConfigCallFunc(const iocshArgBuf *args) { drvPulserConfig( args[0].sval, args[1].ival ); } void drvPulserRegister(void) { iocshRegister( &drvPulserConfigFuncDef,drvPulserConfigCallFunc ); } epicsExportRegistrar( drvPulserRegister ); } // extern "C"