/** This function runs as a polling task at the system clock rate if there is no interrupt driver (such as Ip-Unidig) being used */ void drvAPS_EM::pollerThread() { while(1) { /* Do forever */ if (acquiring_) callbackFunc(0); epicsThreadSleep(epicsThreadSleepQuantum()); } }
/* * Ensure func() is run only once. */ void epicsThreadOnce(epicsThreadOnceId *id, void(*func)(void *), void *arg) { #define EPICS_THREAD_ONCE_DONE (epicsThreadId) 1 if (!initialized) epicsThreadInit(); epicsMutexMustLock(onceMutex); if (*id != EPICS_THREAD_ONCE_DONE) { if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */ *id = epicsThreadGetIdSelf(); /* mark active */ epicsMutexUnlock(onceMutex); func(arg); epicsMutexMustLock(onceMutex); *id = EPICS_THREAD_ONCE_DONE; /* mark done */ } else if (*id == epicsThreadGetIdSelf()) { epicsMutexUnlock(onceMutex); cantProceed("Recursive epicsThreadOnce() initialization\n"); } else while (*id != EPICS_THREAD_ONCE_DONE) { /* Another thread is in the above func(arg) call. */ epicsMutexUnlock(onceMutex); epicsThreadSleep(epicsThreadSleepQuantum()); epicsMutexMustLock(onceMutex); } } epicsMutexUnlock(onceMutex); }
static void initPeriodic(void) { dbMenu *pmenu = dbFindMenu(pdbbase, "menuScan"); double quantum = epicsThreadSleepQuantum(); int i; if (!pmenu) { errlogPrintf("initPeriodic: menuScan not present\n"); return; } nPeriodic = pmenu->nChoice - SCAN_1ST_PERIODIC; papPeriodic = dbCalloc(nPeriodic, sizeof(periodic_scan_list*)); periodicTaskId = dbCalloc(nPeriodic, sizeof(void *)); for (i = 0; i < nPeriodic; i++) { periodic_scan_list *ppsl = dbCalloc(1, sizeof(periodic_scan_list)); const char *choice = pmenu->papChoiceValue[i + SCAN_1ST_PERIODIC]; double number; char *unit; int status = epicsParseDouble(choice, &number, &unit); ppsl->scan_list.lock = epicsMutexMustCreate(); ellInit(&ppsl->scan_list.list); ppsl->name = choice; if (status || number == 0) { errlogPrintf("initPeriodic: Bad menuScan choice '%s'\n", choice); ppsl->period = i; } else if (!*unit || !epicsStrCaseCmp(unit, "second") || !epicsStrCaseCmp(unit, "seconds")) { ppsl->period = number; } else if (!epicsStrCaseCmp(unit, "minute") || !epicsStrCaseCmp(unit, "minutes")) { ppsl->period = number * 60; } else if (!epicsStrCaseCmp(unit, "hour") || !epicsStrCaseCmp(unit, "hours")) { ppsl->period = number * 60 * 60; } else if (!epicsStrCaseCmp(unit, "Hz") || !epicsStrCaseCmp(unit, "Hertz")) { ppsl->period = 1 / number; } else { errlogPrintf("initPeriodic: Bad menuScan choice '%s'\n", choice); ppsl->period = i; } number = ppsl->period / quantum; if ((ppsl->period < 2 * quantum) || (number / floor(number) > 1.1)) { errlogPrintf("initPeriodic: Scan rate '%s' is not achievable.\n", choice); } ppsl->scanCtl = ctlPause; ppsl->loopEvent = epicsEventMustCreate(epicsEventEmpty); papPeriodic[i] = ppsl; } }
timerQueueActive :: timerQueueActive ( RefMgr & refMgr, bool okToShareIn, unsigned priority ) : _refMgr ( refMgr ), queue ( *this ), thread ( *this, "timerQueue", epicsThreadGetStackSize ( epicsThreadStackMedium ), priority ), sleepQuantum ( epicsThreadSleepQuantum() ), okToShare ( okToShareIn ), exitFlag ( false ), terminateFlag ( false ) { }
/** Report parameters * \param[in] fp The file pointer to write to * \param[in] details The level of detail requested */ void drvAPS_EM::report(FILE *fp, int details) { fprintf(fp, "Port: %s, address %p\n", portName, baseAddress_); if (details >= 1) { if (pUInt32DigitalPvt_) { fprintf(fp, " Using digital I/O interrupts\n"); } else { fprintf(fp, " Not using interrupts, scan time=%f\n", epicsThreadSleepQuantum()); } } drvQuadEM::report(fp, details); }
/* Some sample loads on MVME167: * * -> sp jbk_artificial_load, 100000000, 10000, 1 * Load average: 69% * -> sp jbk_artificial_load, 100000000, 100000, 1 * Load average: 95% * -> sp jbk_artificial_load, 100000000, 25000, 1 * Load average: 88% */ int jbk_artificial_load(unsigned long iter,unsigned long often,unsigned long tick_delay) { volatile unsigned long i; double delay = (double)tick_delay * epicsThreadSleepQuantum(); if(iter==0) { printf("Usage: jbk_artificial_load(num_iterations,iter_betwn_delays,tick_delay)\n"); return 0; } for(i=0; i<iter; i++) { if(i%often==0) epicsThreadSleep(delay); } return 0; }
void SimADC::run() { const double min_sleep = epicsThreadSleepQuantum(); sim_global_type::guard_t G(mutex); while(true) { { double zzz = rate.value>0.0 ? 1.0/rate.value : min_sleep; sim_global_type::unguard_t U(G); epicsThreadSleep(zzz); } if(runner_stop) break; cycle(); } printf("SimADC shutdown\n"); }
epicsShareFunc void epicsShareAPI epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg) { static struct epicsThreadOSD threadOnceComplete; #define EPICS_THREAD_ONCE_DONE &threadOnceComplete int status; epicsThreadInit(); status = mutexLock(&onceLock); if(status) { fprintf(stderr,"epicsThreadOnce: pthread_mutex_lock returned %s.\n", strerror(status)); exit(-1); } if (*id != EPICS_THREAD_ONCE_DONE) { if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */ *id = epicsThreadGetIdSelf(); /* mark active */ status = pthread_mutex_unlock(&onceLock); checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce"); func(arg); status = mutexLock(&onceLock); checkStatusQuit(status,"pthread_mutex_lock", "epicsThreadOnce"); *id = EPICS_THREAD_ONCE_DONE; /* mark done */ } else if (*id == epicsThreadGetIdSelf()) { status = pthread_mutex_unlock(&onceLock); checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce"); cantProceed("Recursive epicsThreadOnce() initialization\n"); } else while (*id != EPICS_THREAD_ONCE_DONE) { /* Another thread is in the above func(arg) call. */ status = pthread_mutex_unlock(&onceLock); checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce"); epicsThreadSleep(epicsThreadSleepQuantum()); status = mutexLock(&onceLock); checkStatusQuit(status,"pthread_mutex_lock", "epicsThreadOnce"); } } status = pthread_mutex_unlock(&onceLock); checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnce"); }
/** Reads the settings back from the electrometer. * On the APS_EM this just computes the SampleTime based on the IntegrationTime and valuesPerRead. */ asynStatus drvAPS_EM::readStatus() { double integrationTime, sampleTime, averagingTime; int numAverage; getDoubleParam(P_IntegrationTime, &integrationTime); /* If we are using the interrupts then this is the scan rate * except that we only get interrupts after every other cycle * because of ping/pong, so we multiply by 2. */ if (pUInt32DigitalPvt_ != NULL) { sampleTime = 2. * integrationTime; } else { sampleTime = epicsThreadSleepQuantum(); } sampleTime = sampleTime * valuesPerRead_; setDoubleParam(P_SampleTime, sampleTime); // Compute the number of values that will be accumulated in the ring buffer before averaging getDoubleParam(P_AveragingTime, &averagingTime); numAverage = (int)((averagingTime / sampleTime) + 0.5); setIntegerParam(P_NumAverage, numAverage); return asynSuccess; }
/** This thread is woken up by an interrupt or a request to read status * It loops calling readFIFO until acquiring_ goes to false. * readFIFO only reads a limited amount of FIFO data at once in order * to avoid blocking the device support threads. */ void drvSIS3801::readFIFOThread() { int count; int signal; int chan; int nChans; int status; int i; bool acquiring; // We have a separate flag because we need to continue processing one last // time even if acquiring_ goes to false because acquisition was manually stopped epicsUInt32 scalerPresets[SIS38XX_MAX_SIGNALS]; epicsUInt32 *pOut=NULL; epicsTimeStamp t1, t2; static const char* functionName="readFIFOThread"; while(true) { epicsEventWait(readFIFOEventId_); // We got an event, which can come from acquisition starting, or FIFO full interrupt asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: got readFIFOEvent, eventType=%d, interrupt status=0x%8.8x\n", driverName, functionName, eventType_, registers_->csr_reg & 0xFFF00000); lock(); acquiring = acquiring_; unlock(); while (acquiring) { lock(); for (i=0; i<maxSignals_; i++) getIntegerParam(i, scalerPresets_, (int *)&scalerPresets[i]); getIntegerParam(mcaNumChannels_, &nChans); asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: scaler presets[0]=%d, scalerData[0]=%d\n", driverName, functionName, scalerPresets[0], scalerData_[0]); signal = nextSignal_; chan = nextChan_; count = 0; // This block of code can be slow and does not require the asynPortDriver lock because we are not // accessing object data that could change. // It does require the FIFO lock so no one resets the FIFO while it executes epicsMutexLock(fifoLockId_); unlock(); epicsTimeGetCurrent(&t1); /* Read out FIFO. It would be more efficient not to check the empty * flag on each transfer, using the almost empty flag. But this has gotten * too complex, and is unlikely to save time on new boards with lots of * memory. */ if (acquireMode_== ACQUIRE_MODE_MCS) { // Copy the data from the FIFO to the mcsBuffer pOut = mcsData_ + signal*maxChans_ + chan; asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: pOut=%p, signal=%d, chan=%d\n", driverName, functionName, pOut, signal, chan); while (((registers_->csr_reg & STATUS_M_FIFO_FLAG_EMPTY)==0) && (chan < nChans) && acquiring_) { *pOut = registers_->fifo_reg; signal++; count++; if (signal >= maxSignals_) { signal = 0; chan++; pOut = mcsData_ + chan; } else { pOut += maxChans_; } } } else if (acquireMode_ == ACQUIRE_MODE_SCALER) { while ((registers_->csr_reg & STATUS_M_FIFO_FLAG_EMPTY)==0 && acquiring_) { scalerData_[signal] += registers_->fifo_reg; signal++; count++; if (signal >= maxSignals_) { for (i=0; i<maxSignals_; i++) { if ((scalerPresets[i] != 0) && (scalerData_[i] >= scalerPresets[i])) acquiring = false; } asynPrintIO(pasynUserSelf, ASYN_TRACEIO_DRIVER, (const char*)scalerData_, maxSignals_*sizeof(epicsUInt32), "%s:%s:\n", driverName, functionName); if (!acquiring) break; signal = 0; } } } epicsTimeGetCurrent(&t2); asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: read FIFO (%d) in %fs, acquiring=%d\n", driverName, functionName, count, epicsTimeDiffInSeconds(&t2, &t1), acquiring_); // Release the FIFO lock, we are done accessing the FIFO epicsMutexUnlock(fifoLockId_); // Take the lock since we are now changing object data lock(); nextChan_ = chan; nextSignal_ = signal; if (acquireMode_ == ACQUIRE_MODE_MCS) { asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: pOut=%p,signal=%d, chan=%d\n", driverName, functionName, pOut, signal, chan); checkMCSDone(); } else if (acquireMode_ == ACQUIRE_MODE_SCALER) { if (!acquiring) acquiring_ = false; if (!acquiring_) { asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: scaler done, doing callbacks\n", driverName, functionName); stopScaler(); setIntegerParam(scalerDone_, 1); callParamCallbacks(); } } /* Reenable interrupts in case we were woken up by an interrupt for FIFO almost full */ enableInterrupts(); acquiring = acquiring_; // Release the lock unlock(); // If we are still acquiring then sleep for a short time, but wake up if there is an interrupt if (acquiring) { status = epicsEventWaitWithTimeout(readFIFOEventId_, epicsThreadSleepQuantum()); if (status == epicsEventWaitOK) asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: got interrupt in epicsEventWaitWithTimeout, eventType=%d\n", driverName, functionName, eventType_); } } } }
/** * read just one line of input */ asynStatus omsMAXv::sendReceive(const char *outputBuff, char *inputBuff, unsigned int inputSize) { static const char* functionName = "sendReceive"; STATUS1 flag1; epicsUInt16 getIndex, putIndex; size_t bufsize; size_t usedSpace = 0; char *start, *end; int itera = 0; asynStatus status; if (!enabled) return asynError; status = sendOnly(outputBuff); if (status != asynSuccess) return status; if (inputSize <= 0) return status; *inputBuff = '\0'; double time = 0.0; double timeout = 0.1; // skip busy-waiting for small epicsThreadSleepQuantum if (epicsThreadSleepQuantum() <= 0.01) itera = 2001; while ((pmotor->status1_flag.Bits.text_response == 0) && (time < timeout)){ Debug(32, "%s:%s:%s: Waiting for reponse, itera:%d\n", driverName, functionName, portName, itera); // busy-waiting but not more than 2000 times if (itera > 2000){ time += epicsThreadSleepQuantum(); epicsThreadSleep(epicsThreadSleepQuantum()); } itera++; } if (pmotor->status1_flag.Bits.text_response == 0) { Debug(1, "%s:%s:%s: Timeout occurred , %s\n", driverName, functionName, portName, outputBuff); return asynTimeout; } getIndex = (epicsUInt16) pmotor->inGetIndex; putIndex = (epicsUInt16) pmotor->inPutIndex; bufsize = putIndex - getIndex; start = (char *) &pmotor->inBuffer[getIndex]; end = (char *) &pmotor->inBuffer[putIndex]; if (start < end) { /* Test for message wraparound in buffer. */ usedSpace = MIN(bufsize, inputSize); memcpy(inputBuff, start, usedSpace); } else { bufsize += BUFFER_SIZE; size_t firstPart = ((char *) &pmotor->inBuffer[BUFFER_SIZE]) - start; usedSpace = MIN(firstPart, inputSize); memcpy(inputBuff, start, usedSpace); size_t copySize = MIN(bufsize - firstPart, inputSize - usedSpace); memcpy((inputBuff + usedSpace), (const char *) &pmotor->inBuffer[0], copySize); usedSpace += copySize; } inputBuff[usedSpace - 1]= '\0'; getIndex += bufsize; if (getIndex >= BUFFER_SIZE) getIndex -= BUFFER_SIZE; while (getIndex != (epicsUInt16)pmotor->inPutIndex) { Debug(2, "readbuf(): flushed - %d\n", pmotor->inBuffer[getIndex]); if (++getIndex > BUFFER_SIZE) getIndex = 0; } pmotor->status1_flag.Bits.text_response = 0; pmotor->inGetIndex = (epicsUInt32) getIndex; flag1.All = pmotor->status1_flag.All; pmotor->status1_flag.All = flag1.All; pmotor->msg_semaphore=0; Debug(16, "omsMAXv::sendReceive: received %s\n", inputBuff); return asynSuccess; }
asynStatus omsMAXv::sendOnly(const char *outputBuff) { STATUS1 flag1; const char* functionName = "sendOnly"; int len = strlen(outputBuff); double timeout = 0.01; epicsUInt16 getIndex, putIndex; if (!enabled) return asynError; Debug(16, "omsMAXv::send: sending: %s \n", outputBuff); if (len > (BUFFER_SIZE-1)) { errlogPrintf("%s:%s:%s: message too long: %d character\n", driverName, functionName, portName, len); return asynError; } /* see if junk at input port - should not be any data available */ int flushTime = 0; while (((epicsUInt16) pmotor->inGetIndex != (epicsUInt16) pmotor->inPutIndex) && (flushTime < 100)) { // flush cards response Buffer #ifdef DEBUG int deltaIndex = ((epicsUInt16)pmotor->inPutIndex) - ((epicsUInt16)pmotor->inGetIndex); Debug(32, "%s:%s:%s: flushing %d characters\n", driverName, functionName, portName, (((deltaIndex < 0) ? BUFFER_SIZE + deltaIndex : deltaIndex))); #endif putIndex = (epicsUInt16) pmotor->inPutIndex; pmotor->inGetIndex = putIndex; pmotor->status1_flag.Bits.text_response = 0; flag1.All = pmotor->status1_flag.All; pmotor->status1_flag.All = flag1.All; pmotor->msg_semaphore=0; epicsThreadSleep(timeout); flushTime++; if (flushTime == 100 ) { Debug(1, "%s:%s:%s: unable to flush more than 100 strings\n", driverName, functionName, portName); return asynError; } } putIndex = (epicsUInt16) pmotor->outPutIndex; getIndex = (epicsUInt16) pmotor->outGetIndex; for (int i = 0; (i < len); i++) { pmotor->outBuffer[putIndex++]= outputBuff[i]; if (putIndex >= BUFFER_SIZE) putIndex = 0; } pmotor->outPutIndex = putIndex; /* Message Sent */ int count = 0, prevdeltaIndex = 0, index = 0; int maxcount = (int)(0.1 / epicsThreadSleepQuantum()); // skip busy-waiting for small epicsThreadSleepQuantum if (epicsThreadSleepQuantum() <= 0.01) index = 100; int deltaIndex = ((epicsUInt16)pmotor->outPutIndex) - ((epicsUInt16)pmotor->outGetIndex); while ((deltaIndex != 0) && (count <= maxcount)) { deltaIndex = ((epicsUInt16)pmotor->outPutIndex) - ((epicsUInt16)pmotor->outGetIndex); // do busy-waiting but not more than 100 times while ((index < 100) && (deltaIndex != 0)){ deltaIndex = ((epicsUInt16)pmotor->outPutIndex) - ((epicsUInt16)pmotor->outGetIndex); ++index; } if ((index >= 100) && (deltaIndex != 0)) epicsThreadSleep(timeout); if (deltaIndex == prevdeltaIndex) ++count; else count = 0; prevdeltaIndex = deltaIndex; }; if (deltaIndex != 0) { Debug(1, "%s:%s:%s: Timeout\n", driverName, functionName, portName); return asynTimeout; } Debug(64, "omsMAXv::send: done\n"); return asynSuccess; }
/* Scan task, one per PLC */ static void PLC_scan_task(PLC *plc) { ScanList *list; epicsTimeStamp next_schedule, start_time, end_time; double timeout, delay, quantum; eip_bool transfer_ok, reset_next_schedule; quantum = epicsThreadSleepQuantum(); timeout = (double)ETHERIP_TIMEOUT/1000.0; scan_loop: /* --------- The Scan Loop for one PLC -------- */ if (epicsMutexLock(plc->lock) != epicsMutexLockOK) { EIP_printf_time(1, "drvEtherIP scan task for PLC '%s'" " cannot take plc->lock\n", plc->name); return; } if (!assert_PLC_connect(plc)) { /* don't rush since connection takes network bandwidth */ epicsMutexUnlock(plc->lock); EIP_printf_time(2, "drvEtherIP: PLC '%s' is disconnected\n", plc->name); epicsThreadSleep(timeout); goto scan_loop; } EIP_printf_time(10, "drvEtherIP scan PLC '%s'\n", plc->name); reset_next_schedule = true; epicsTimeGetCurrent(&start_time); for (list = DLL_first(ScanList,&plc->scanlists); list; list = DLL_next(ScanList,list)) { if (! list->enabled) continue; if (epicsTimeLessThanEqual(&list->scheduled_time, &start_time)) { epicsTimeGetCurrent(&list->scan_time); transfer_ok = process_ScanList(plc->connection, list); epicsTimeGetCurrent(&end_time); list->last_scan_time = epicsTimeDiffInSeconds(&end_time, &list->scan_time); /* update statistics */ if (list->last_scan_time > list->max_scan_time) list->max_scan_time = list->last_scan_time; if (list->last_scan_time < list->min_scan_time || list->min_scan_time == 0.0) list->min_scan_time = list->last_scan_time; if (transfer_ok) /* re-schedule exactly */ { list->scheduled_time = list->scan_time; epicsTimeAddSeconds(&list->scheduled_time, list->period); } else { /* end_time+fixed delay, ignore extra due to error */ list->scheduled_time = end_time; epicsTimeAddSeconds(&list->scheduled_time, timeout); ++list->list_errors; ++plc->plc_errors; disconnect_PLC(plc); epicsMutexUnlock(plc->lock); goto scan_loop; } } /* Update time for list that's due next */ if (reset_next_schedule || epicsTimeLessThan(&list->scheduled_time, &next_schedule)) { reset_next_schedule = false; next_schedule = list->scheduled_time; } } epicsMutexUnlock(plc->lock); /* fallback for empty/degenerate scan list */ if (reset_next_schedule) delay = EIP_MIN_TIMEOUT; else { epicsTimeGetCurrent(&start_time); delay = epicsTimeDiffInSeconds(&next_schedule, &start_time); if (delay > 60.0) { char tsString[50]; printf("Scanlist %g secs has scheduling problem, delay = %g sec\n", list->period, delay); epicsTimeToStrftime(tsString, sizeof(tsString), "%Y/%m/%d %H:%M:%S.%04f", &list->scan_time); printf(" 'Scan time' : %s\n", tsString); epicsTimeToStrftime(tsString, sizeof(tsString), "%Y/%m/%d %H:%M:%S.%04f", &start_time); printf(" 'Current time' : %s\n", tsString); epicsTimeToStrftime(tsString, sizeof(tsString), "%Y/%m/%d %H:%M:%S.%04f", &next_schedule); printf(" 'Next time' : %s\n", tsString); /* Attempt to hack around this by waiting a minute, * hoping that the clock looks better by then. * Also resetting the scheduled time to 'now'. */ delay = 60.0; list->scheduled_time = start_time; ++list->sched_errors; } } /* Sleep until next turn. */ if (delay > 0.0) epicsThreadSleep(delay); else if (delay <= -quantum) { EIP_printf(8, "drvEtherIP scan task slow, %g sec delay\n", delay); ++plc->slow_scans; /* hmm, "plc" not locked... */ } goto scan_loop; }
/** This thread is woken up by an interrupt or a request to read status * It loops calling readFIFO until acquiring_ goes to false. * readFIFO only reads a limited amount of FIFO data at once in order * to avoid blocking the device support threads. */ void drvSIS3820::readFIFOThread() { int status; int count; int signal; int chan; int i; bool acquiring; epicsUInt32 *pIn, *pOut; epicsTimeStamp t1, t2, t3; static const char* functionName="readFIFOThread"; while(true) { asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: waiting for readFIFOEvent\n", driverName, functionName); enableInterrupts(); (void)epicsEventWait(readFIFOEventId_); asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: got readFIFOEvent, eventType=%d\n", driverName, functionName, eventType_); // We got an event. This can come from: // Acquisition complete in scaler mode // FIFO full in MCS mode // Acquisition start in MCS mode // For scaler mode we just do callbacks lock(); // Do callbacks on acquiring status when acquisition completes if ((acquiring_ == false) && (acquireMode_ == ACQUIRE_MODE_SCALER)) { asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: doing callbacks on scalerDone\n", driverName, functionName); setIntegerParam(scalerDone_, 1); callParamCallbacks(); unlock(); continue; } acquiring = acquiring_; unlock(); // MCS mode while (acquiring && (acquireMode_ == ACQUIRE_MODE_MCS)) { lock(); signal = nextSignal_; chan = nextChan_; // This block of code can be slow and does not require the asynPortDriver lock because we are not // accessing object data that could change. // It does require the FIFO lock so no one resets the FIFO while it executes epicsMutexLock(fifoLockId_); unlock(); count = registers_->fifo_word_count_reg; if (count > fifoBufferWords_) count = fifoBufferWords_; epicsTimeGetCurrent(&t1); if (useDma_ && (count >= MIN_DMA_TRANSFERS)) { asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: doing DMA transfer, fifoBuffer=%p, fifoBaseVME_=%p, count=%d\n", driverName, functionName, fifoBuffer_, fifoBaseVME_, count); status = sysDmaFromVme(dmaId_, fifoBuffer_, (int)fifoBaseVME_, VME_AM_EXT_SUP_D64BLT, (count)*sizeof(int), 8); if (status) { asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: doing DMA transfer, error calling sysDmaFromVme, status=%d, error=%d, buff=%p, fifoBaseVME_=%p, count=%d\n", driverName, functionName, status, errno, fifoBuffer_, fifoBaseVME_, count); } else { (void)epicsEventWait(dmaDoneEventId_); status = sysDmaStatus(dmaId_); if (status) asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: DMA error, errno=%d, message=%s\n", driverName, functionName, errno, strerror(errno)); } } else { asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: programmed transfer, count=%d\n", driverName, functionName, count); // Note: we were previously using memcpy here. But that is not guaranteed to do a word transfer, which the // SIS3820 requires for reading the FIFO. In fact if the word count was 1 then a memcpy of 4 bytes was clearly // not doing a word transfer on vxWorks, and was generating bus errors. for (i=0; i<count; i++) fifoBuffer_[i] = fifoBaseCPU_[i]; } epicsTimeGetCurrent(&t2); asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: read FIFO (%d) in %fs, fifo word count after=%d, fifoBuffer_=%p, fifoBaseCPU_=%p\n", driverName, functionName, count, epicsTimeDiffInSeconds(&t2, &t1), registers_->fifo_word_count_reg, fifoBuffer_, fifoBaseCPU_); // Release the FIFO lock, we are done accessing the FIFO epicsMutexUnlock(fifoLockId_); // Copy the data from the FIFO buffer to the mcsBuffer pOut = mcsData_ + signal*maxChans_ + chan; pIn = fifoBuffer_; for (i=0; i<count; i++) { *pOut = *pIn++; signal++; if (signal == maxSignals_) { signal = 0; chan++; pOut = mcsData_ + chan; } else { pOut += maxChans_; } } epicsTimeGetCurrent(&t2); // Take the lock since we are now changing object data lock(); nextChan_ = chan; nextSignal_ = signal; asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: copied data to mcsBuffer in %fs, nextChan=%d, nextSignal=%d\n", driverName, functionName, epicsTimeDiffInSeconds(&t3, &t3), nextChan_, nextSignal_); checkMCSDone(); acquiring = acquiring_; /* Re-enable FIFO threshold and FIFO almost full interrupts */ /* NOTE: WE ARE NOT USING FIFO THRESHOLD INTERRUPTS FOR NOW */ /* registers_->irq_control_status_reg = SIS3820_IRQ_SOURCE1_ENABLE; */ registers_->irq_control_status_reg = SIS3820_IRQ_SOURCE4_ENABLE; // Release the lock unlock(); enableInterrupts(); // If we are still acquiring sleep for a short time but wake up on interrupt if (acquiring) { status = epicsEventWaitWithTimeout(readFIFOEventId_, epicsThreadSleepQuantum()); if (status == epicsEventWaitOK) asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: got interrupt in epicsEventWaitWithTimeout, eventType=%d\n", driverName, functionName, eventType_); } } } }