LCOV - code coverage report
Current view: top level - mu2e-pcie-utils/dtcInterfaceLib - mu2edev.cpp (source / functions) Coverage Total Hit
Test: mu2edaq.info.cleaned Lines: 0.0 % 477 0
Test Date: 2026-07-30 01:56:44 Functions: 0.0 % 56 0

            Line data    Source code
       1              : // This file (mu2edev.cc) was created by Ron Rechenmacher <ron@fnal.gov> on
       2              : // Feb 13, 2014. "TERMS AND CONDITIONS" governing this file are in the README
       3              : // or COPYING file. If you do not have such a file, one can be obtained by
       4              : // contacting Ron or Fermi Lab in Batavia IL, 60510, phone: 630-840-3000.
       5              : // $RCSfile: .emacs.gnu,v $
       6              : // rev="$Revision: 1.23 $$Date: 2012/01/23 15:32:40 $";
       7              : 
       8              : /*
       9              :  *    make mu2edev.o CFLAGS='-g -Wall -std=c++0x'
      10              :  */
      11              : 
      12              : #include <signal.h>
      13              : #include <chrono>
      14              : #include <errno.h>
      15              : 
      16              : #include "TRACE/tracemf.h"
      17              : 
      18              : #include "mu2edev.h"
      19              : 
      20              : #include "dtcInterfaceLib/otsStyleCoutMacros.h"
      21              : 
      22              : typedef unsigned long dma_addr_t;
      23              : #include "mu2e_driver/mu2e_proto_globals.h"  // MU2E_NUM_RECV_BUFFS
      24              : 
      25              : #define DEV_TLOG(lvl) TLOG(lvl) << "DEVICE " << this->getDeviceUID() << ": "
      26              : 
      27              : static constexpr int TLVL_INIT_DMA_ENGINE = TLVL_DEBUG + 15;
      28              : static constexpr int TLVL_READ_DATA = TLVL_DEBUG + 16;
      29              : static constexpr int TLVL_READ_DATA_2 = TLVL_DEBUG + 17;
      30              : static constexpr int TLVL_READ_DATA_3 = TLVL_DEBUG + 18;
      31              : static constexpr int TLVL_READ_RELEASE = TLVL_DEBUG + 19;
      32              : static constexpr int TLVL_READ_REGISTER = TLVL_DEBUG + 20;
      33              : static constexpr int TLVL_WRITE_REGISTER = TLVL_DEBUG + 21;
      34              : static constexpr int TLVL_WRITE_REGISTER_CHECKED = TLVL_DEBUG + 22;
      35              : static constexpr int TLVL_META_DUMP = TLVL_DEBUG + 23;
      36              : static constexpr int TLVL_WRITE_DATA = TLVL_DEBUG + 24;
      37              : static constexpr int TLVL_RELEASE_ALL = TLVL_DEBUG + 25;
      38              : static constexpr int TLVL_BEGIN_DCS_TRANSACTION = TLVL_DEBUG + 26;
      39              : static constexpr int TLVL_BEGIN_DCS_TRANSACTION_VERBOSE = TLVL_DEBUG + 27;
      40              : static constexpr int TLVL_END_DCS_TRANSACTION = TLVL_DEBUG + 28;
      41              : static constexpr int TLVL_GET_DRIVER_VERSION = TLVL_DEBUG + 29;
      42              : 
      43              : static const std::thread::id NULL_TID = std::thread::id();
      44              : 
      45              : std::array<mu2edev::DCSLock, MU2E_MAX_NUM_DTCS> mu2edev::dcs_locks_{};
      46              : 
      47            0 : mu2edev::mu2edev()
      48            0 :         : devfd_(0), buffers_held_(0), simulator_(nullptr), activeDeviceIndex_(0), deviceTime_(0LL), writeSize_(0), readSize_(0), UID_("")
      49              : {
      50              :         // TRACE_CNTL( "lvlmskM", 0x3 );
      51              :         // TRACE_CNTL( "lvlmskS", 0x3 );
      52            0 :         TLOG(TLVL_INFO) << "CONSTRUCTOR";
      53            0 : }  // end constructor
      54              : 
      55            0 : mu2edev::~mu2edev()
      56              : {
      57            0 :         TLOG(TLVL_INFO) << "DESTRUCTOR " << UID_;
      58              : 
      59            0 :         end_dcs_transaction(false /* mustHaveLock */);
      60            0 :         if (debugFp_) fclose(debugFp_);
      61              : 
      62            0 :         close();
      63            0 :         TLOG(TLVL_INFO) << "DESTRUCTOR end";
      64            0 : }  // end destructor
      65              : 
      66            0 : int mu2edev::init(DTCLib::DTC_SimMode simMode, int deviceIndex, std::string simMemoryFileName, const std::string& uid)
      67              : {
      68            0 :         UID_ = uid;
      69              : 
      70            0 :         auto debugWriteFilePath = getenv("DTCLIB_DEBUG_WRITE_FILE_PATH");
      71            0 :         if (debugWriteFilePath != nullptr)
      72              :         {
      73            0 :                 auto debugWriteFileStr = std::string(debugWriteFilePath) + "/Write_debug_" + uid + "_" + std::to_string(time(0)) + ".txt";
      74            0 :                 if (debugFp_) fclose(debugFp_);
      75            0 :                 debugFp_ = fopen(debugWriteFileStr.c_str(), "w");
      76            0 :                 if (!debugFp_)
      77              :                 {
      78            0 :                         __SS__ << "mu2e Device write debug file could not be opened at path DTCLIB_DEBUG_WRITE_FILE_PATH! Exiting.\n"
      79            0 :                                    << "open " << debugWriteFileStr << __E__;
      80            0 :                         perror(ss.str().c_str());
      81            0 :                         __SS_THROW__;
      82              :                         // exit(1);
      83            0 :                 }
      84            0 :         }
      85              : 
      86            0 :         auto start = std::chrono::steady_clock::now();
      87            0 :         lastWriteTime_ = start;  // init time
      88              : 
      89            0 :         if (simMode != DTCLib::DTC_SimMode_Disabled && simMode != DTCLib::DTC_SimMode_NoCFO &&
      90            0 :                 simMode != DTCLib::DTC_SimMode_ROCEmulator && simMode != DTCLib::DTC_SimMode_Loopback)
      91              :         {
      92            0 :                 simulator_ = new mu2esim(simMemoryFileName);
      93            0 :                 simulator_->init(simMode);
      94              :         }
      95              :         else
      96              :         {
      97            0 :                 if (simulator_ != nullptr)
      98              :                 {
      99            0 :                         delete simulator_;
     100            0 :                         simulator_ = nullptr;
     101              :                 }
     102              : 
     103            0 :                 activeDeviceIndex_ = deviceIndex;
     104            0 :                 initDMAEngine();
     105              :         }
     106              : 
     107              :         // init to free dcs locks
     108            0 :         end_dcs_transaction(true /* force */);  // force unlock
     109              : 
     110            0 :         deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
     111            0 :         return simMode;
     112              : }
     113              : 
     114              : //==============================================================================
     115              : // exec
     116              : //      run linux command and get result back in string
     117            0 : std::string exec(const char* cmd)
     118              : {
     119            0 :         __COUTV__(cmd);
     120              : 
     121              :         std::array<char, 128> buffer;
     122            0 :         std::string result;
     123            0 :         std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
     124            0 :         if (!pipe)
     125              :         {
     126            0 :                 __COUT_ERR__ << "popen() failed!" << __E__;
     127            0 :                 return "popen() failed!";
     128              :         }
     129            0 :         while (!feof(pipe.get()))
     130              :         {
     131            0 :                 if (fgets(buffer.data(), 128, pipe.get()) != nullptr)
     132            0 :                         result += buffer.data();
     133              :         }
     134              :         //__COUTV__(result);
     135            0 :         return result;
     136            0 : }  // end exec()
     137              : 
     138              : /*****************************
     139              :    initDMAEngine
     140              :    set up DMA engines
     141              :    */
     142            0 : void mu2edev::initDMAEngine()
     143              : {
     144            0 :         if (simulator_ != nullptr)
     145              :         {
     146            0 :                 __COUT__ << "Using simulator, so no need to init DMA Engine..." << __E__;
     147            0 :                 return;  // do nothing if using simulator
     148              :         }
     149            0 :         __COUT__ << "Initializing DMA engine..." << __E__;
     150              : 
     151              :         char devfile[11];
     152            0 :         snprintf(devfile, 11, "/dev/" MU2E_DEV_FILE, activeDeviceIndex_);
     153              :         int sts;
     154            0 :         if (devfd_) ::close(devfd_);
     155            0 :         devfd_ = open(devfile, O_RDWR);
     156            0 :         if (devfd_ == -1 || devfd_ == 0)
     157              :         {
     158            0 :                 __SS__ << "mu2e Device file not found (or DTCLIB_SIM_ENABLE not set)! Exiting.\n"
     159            0 :                            << "Attempt to open '" << devfile << "' and received error: " << errno << " - " << strerror(errno) << __E__;
     160              : 
     161              :                 ss << "Who owns it? (ls -l /dev/mu2e*)\n"
     162            0 :                    << exec("ls -l /dev/mu2e*") << __E__;
     163              :                 ss << "\nWhat process is using it (lsof /dev/mu2e* 2>/dev/null, will only show processes owned by the current user)?\n"
     164            0 :                    << exec("lsof /dev/mu2e* 2>/dev/null") << __E__;
     165            0 :                 perror(ss.str().c_str());
     166            0 :                 __SS_THROW__;
     167              :                 // exit(1);
     168            0 :         }
     169            0 :         for (unsigned chn = 0; chn < MU2E_MAX_CHANNELS; ++chn)
     170            0 :                 for (unsigned dir = 0; dir < 2; ++dir)
     171              :                 {
     172              :                         m_ioc_get_info_t get_info;
     173            0 :                         get_info.chn = chn;
     174            0 :                         get_info.dir = dir;
     175            0 :                         get_info.tmo_ms = 1000;
     176            0 :                         TRACE(TLVL_INIT_DMA_ENGINE, UID_ + " - mu2edev::init before ioctl( devfd_, M_IOC_GET_INFO, &get_info ) chn=%u dir=%u", chn, dir);
     177            0 :                         sts = ioctl(devfd_, M_IOC_GET_INFO, &get_info);
     178            0 :                         if (sts != 0)
     179              :                         {
     180            0 :                                 __SS__ << "Failed mu2edev::init before ioctl( devfd_, M_IOC_GET_INFO, &get_info)." << __E__;
     181            0 :                                 perror(ss.str().c_str());
     182            0 :                                 __SS_THROW__;
     183              :                                 // exit(1);
     184            0 :                         }
     185            0 :                         mu2e_channel_info_[activeDeviceIndex_][chn][dir] = get_info;
     186            0 :                         TRACE(TLVL_INIT_DMA_ENGINE, UID_ + " - mu2edev::init %d %u:%u - num=%u size=%u hwIdx=%u, swIdx=%u delta=%u", activeDeviceIndex_, chn, dir,
     187              :                                   get_info.num_buffs, get_info.buff_size, get_info.hwIdx, get_info.swIdx,
     188              :                                   mu2e_chn_info_delta_(activeDeviceIndex_, chn, dir, &mu2e_channel_info_));
     189            0 :                         for (unsigned map = 0; map < 2; ++map)
     190              :                         {
     191            0 :                                 size_t length = get_info.num_buffs * ((map == MU2E_MAP_BUFF) ? get_info.buff_size : sizeof(int));
     192              :                                 // int prot = (((dir == S2C) && (map == MU2E_MAP_BUFF))? PROT_WRITE : PROT_READ);
     193            0 :                                 int prot = (((map == MU2E_MAP_BUFF)) ? PROT_WRITE : PROT_READ);
     194            0 :                                 off64_t offset = chnDirMap2offset(chn, dir, map);
     195            0 :                                 mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map] = mmap(0 /* hint address */
     196              :                                                                                                                                                   ,
     197              :                                                                                                                                                   length, prot, MAP_SHARED, devfd_, offset);
     198            0 :                                 if (mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map] == MAP_FAILED)
     199              :                                 {
     200            0 :                                         __SS__ << "mu2e Device mmap error! Exiting." << __E__;
     201            0 :                                         perror(ss.str().c_str());
     202            0 :                                         __SS_THROW__;
     203              :                                         // exit(1);
     204            0 :                                 }
     205            0 :                                 TRACE(TLVL_INIT_DMA_ENGINE, UID_ + " - mu2edev::init chnDirMap2offset=%lu mu2e_mmap_ptrs_[%d][%d][%d][%d]=%p p=%c l=%lu", offset, activeDeviceIndex_, chn,
     206              :                                           dir, map, mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map], prot == PROT_READ ? 'R' : 'W', length);
     207              :                         }
     208              : 
     209              :                         // do not want to release all from constructor, in case this instance of the device is not the 'owner' of a DMA-channel
     210              :                         //  if (dir == DTC_DMA_Direction_C2S)
     211              :                         //  {
     212              :                         //      release_all(static_cast<DTC_DMA_Engine>(chn));
     213              :                         //  }
     214              : 
     215              :                         // Reset the DTC
     216              :                         //{
     217              :                         //      write_register(0x9100, 0, 0xa0000000);
     218              :                         //      write_register(0x9118, 0, 0x0000003f);
     219              :                         //      write_register(0x9100, 0, 0x00000000);
     220              :                         //      write_register(0x9100, 0, 0x10000000);
     221              :                         //      write_register(0x9100, 0, 0x30000000);
     222              :                         //      write_register(0x9100, 0, 0x10000000);
     223              :                         //      write_register(0x9118, 0, 0x00000000);
     224              :                         //}
     225              : 
     226              :                         // Enable DMA Engines
     227              :                         {
     228              :                                 // uint16_t addr = DTC_Register_Engine_Control(chn, dir);
     229              :                                 // TRACE(TLVL_INIT_DMA_ENGINE, UID_ + " - mu2edev::init write Engine_Control reg 0x%x", addr);
     230              :                                 // write_register(addr, 0, 0x100);//bit 8 enable=1
     231              :                         }
     232              :                 }
     233              : }  // end initDMAEngine()
     234              : 
     235              : /*****************************
     236              :    read_data
     237              :    returns number of bytes read; negative value indicates an error
     238              :    */
     239            0 : int mu2edev::read_data(DTC_DMA_Engine const& chn, void** buffer, int tmo_ms)
     240              : {
     241              :         // WARNING NOTE: if there is existing data still sitting in unreleased buffer, the timeout will not add any delay
     242              :         int retsts;
     243            0 :         TRACE_EXIT { TRACE(TLVL_READ_DATA, UID_ + " - mu2edev::read_data returning retsts(bytes)=%d", retsts); };
     244              : 
     245            0 :         if (chn == DTC_DMA_Engine_DCS && !thread_owns_dcs_lock())
     246              :         {
     247            0 :                 TRACE(TLVL_ERROR, UID_ + " - read_data dcs lock not held!");
     248            0 :                 return retsts = -2;
     249              :         }
     250              : 
     251            0 :         auto start = std::chrono::steady_clock::now();
     252            0 :         if (simulator_ != nullptr)
     253              :         {
     254            0 :                 retsts = simulator_->read_data(chn, buffer, tmo_ms);
     255              :         }
     256              :         else
     257              :         {
     258            0 :                 retsts = 0;
     259              :                 unsigned has_recv_data;
     260            0 :                 TRACE(TLVL_READ_DATA, UID_ + " - mu2edev::read_data before (mu2e_mmap_ptrs_[%d][0][0][0]!=NULL) || ((retsts=init())==0) tmo_ms=%d", activeDeviceIndex_, tmo_ms);
     261            0 :                 if ((mu2e_mmap_ptrs_[activeDeviceIndex_][0][0][0] != NULL) ||
     262            0 :                         ((retsts = init(DTCLib::DTC_SimMode_Disabled, 0)) == 0))  // Default-init mu2edev if not given guidance
     263              :                 {
     264            0 :                         has_recv_data = mu2e_chn_info_delta_(activeDeviceIndex_, chn, C2S, &mu2e_channel_info_);
     265            0 :                         TRACE(TLVL_READ_DATA, UID_ + " - mu2edev::read_data after %u=has_recv_data = delta_( chn=%d, C2S ), held=%u", has_recv_data, chn, buffers_held_);
     266            0 :                         mu2e_channel_info_[activeDeviceIndex_][chn][C2S].tmo_ms = tmo_ms;  // in case GET_INFO is called
     267            0 :                         if ((has_recv_data > buffers_held_) ||
     268            0 :                                 ((retsts = ioctl(devfd_, M_IOC_GET_INFO, &mu2e_channel_info_[activeDeviceIndex_][chn][C2S])) == 0 &&
     269            0 :                                  (has_recv_data = mu2e_chn_info_delta_(activeDeviceIndex_, chn, C2S, &mu2e_channel_info_)) >
     270            0 :                                          buffers_held_))
     271              :                         {  // have data
     272              :                                 // get byte count from new/next
     273              :                                 unsigned newNxtIdx =
     274            0 :                                         idx_add(mu2e_channel_info_[activeDeviceIndex_][chn][C2S].swIdx, (int)buffers_held_ + 1, activeDeviceIndex_, chn, C2S);
     275            0 :                                 int* BC_p = (int*)mu2e_mmap_ptrs_[activeDeviceIndex_][chn][C2S][MU2E_MAP_META];
     276            0 :                                 retsts = BC_p[newNxtIdx];
     277            0 :                                 *buffer = ((mu2e_databuff_t*)(mu2e_mmap_ptrs_[activeDeviceIndex_][chn][C2S][MU2E_MAP_BUFF]))[newNxtIdx];
     278            0 :                                 TRACE(TLVL_READ_DATA_2,
     279              :                                           "mu2edev::read_data chn%d hIdx=%u, sIdx=%u num_buffs=%u hasRcvDat=%u %p(BC_p)[newNxtIdx=%d]=retsts=%d %p(buf)[0]=0x%08x",
     280              :                                           chn,
     281              :                                           mu2e_channel_info_[activeDeviceIndex_][chn][C2S].hwIdx,
     282              :                                           mu2e_channel_info_[activeDeviceIndex_][chn][C2S].swIdx,
     283              :                                           mu2e_channel_info_[activeDeviceIndex_][chn][C2S].num_buffs,
     284              :                                           has_recv_data, (void*)BC_p, newNxtIdx, retsts,
     285              :                                           *buffer, *(uint32_t*)*buffer);
     286            0 :                                 TRACE(TLVL_READ_DATA_3, "first 80 bytes: %016lx %016lx %016lx %016lx %016lx %016lx %016lx %016lx %016lx %016lx",
     287              :                                           *(((uint64_t*)*buffer) + 0), *(((uint64_t*)*buffer) + 1), *(((uint64_t*)*buffer) + 2), *(((uint64_t*)*buffer) + 3),
     288              :                                           *(((uint64_t*)*buffer) + 4), *(((uint64_t*)*buffer) + 5), *(((uint64_t*)*buffer) + 6), *(((uint64_t*)*buffer) + 7),
     289              :                                           *(((uint64_t*)*buffer) + 8), *(((uint64_t*)*buffer) + 9));
     290              : 
     291              :                                 /// increment buffers held if allowing multiple buffers to be held by user space (e.g. for Data DMA channel, which is different for CFO vs DTC)</param>
     292            0 :                                 if (chn == DTC_DMA_Engine_DAQ)
     293            0 :                                         ++buffers_held_;
     294              :                         }
     295              :                         else
     296              :                         {  // was it a tmo or error
     297            0 :                                 if (retsts != 0)
     298              :                                 {
     299            0 :                                         __SS__ << "Failed mu2edev::read_data with M_IOC_GET_INFO... return not 0." << __E__;
     300            0 :                                         perror(ss.str().c_str());
     301            0 :                                         __SS_THROW__;
     302              :                                         // exit(1);
     303            0 :                                 }
     304            0 :                                 TRACE(TLVL_READ_DATA_2, UID_ + " - mu2edev::read_data not error... return %d status", retsts);
     305              :                         }
     306              :                 }
     307              :         }
     308            0 :         deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
     309            0 :         if (retsts > 0) readSize_ += retsts;
     310            0 :         return retsts;
     311            0 : }  // end read_data()
     312              : 
     313            0 : int mu2edev::GetBufferIndex(DTC_DMA_Engine const& chn, const void* ptr) const
     314              : {
     315            0 :         const volatile void* mmapBase = mu2e_mmap_ptrs_[activeDeviceIndex_][chn][C2S][MU2E_MAP_BUFF];
     316            0 :         if (mmapBase == nullptr || ptr == nullptr) return -1;
     317            0 :         const auto bytesFromBase = reinterpret_cast<const volatile uint8_t*>(ptr) - reinterpret_cast<const volatile uint8_t*>(mmapBase);
     318            0 :         if (bytesFromBase < 0) return -1;
     319            0 :         const auto idx = static_cast<size_t>(bytesFromBase) / sizeof(mu2e_databuff_t);
     320            0 :         if (idx >= MU2E_NUM_RECV_BUFFS) return -1;
     321            0 :         return static_cast<int>(idx);
     322              : }
     323              : 
     324              : /* read_release
     325              :    release a number of buffers (usually 1)
     326              :    */
     327            0 : int mu2edev::read_release(DTC_DMA_Engine const& chn, unsigned num)
     328              : {
     329            0 :         if (chn == DTC_DMA_Engine_DCS && !thread_owns_dcs_lock())
     330              :         {
     331            0 :                 TRACE(TLVL_ERROR, UID_ + " - read_release dcs lock not held!");
     332            0 :                 return -2;
     333              :         }
     334              : 
     335            0 :         auto start = std::chrono::steady_clock::now();
     336            0 :         auto retsts = -1;
     337            0 :         if (simulator_ != nullptr)
     338              :         {
     339            0 :                 retsts = simulator_->read_release(chn, num);
     340              :         }
     341              :         else
     342              :         {
     343            0 :                 retsts = 0;
     344              :                 unsigned long arg;
     345              :                 unsigned has_recv_data;
     346            0 :                 has_recv_data = mu2e_chn_info_delta_(activeDeviceIndex_, chn, C2S, &mu2e_channel_info_);
     347              : 
     348            0 :                 TLOG(TLVL_READ_RELEASE) << "read_release(chn=" << chn << " num=" << num << ") dtc="
     349            0 :                                                                 << activeDeviceIndex_ << " has_recv_data=" << has_recv_data;
     350              : 
     351              :                 // if (num > has_recv_data) num = has_recv_data;
     352            0 :                 if (num <= has_recv_data)
     353              :                 {
     354            0 :                         arg = (chn << 24) | (C2S << 16) | (num & 0xffff);  // THIS OBIVOUSLY SHOULD BE A MACRO
     355            0 :                         retsts = ioctl(devfd_, M_IOC_BUF_GIVE, arg);
     356            0 :                         if (retsts != 0)
     357              :                         {
     358            0 :                                 __SS__ << "ioctl(devfd_, M_IOC_BUF_GIVE, arg)... return not 0." << __E__;
     359            0 :                                 perror(ss.str().c_str());
     360            0 :                                 DEV_TLOG(TLVL_ERROR) << ss.str();
     361              :                                 // __SS_THROW__;
     362            0 :                         }  // exit(1); } // Don't exit for now
     363              : 
     364              :                         // increment our cached info
     365            0 :                         mu2e_channel_info_[activeDeviceIndex_][chn][C2S].swIdx =
     366            0 :                                 idx_add(mu2e_channel_info_[activeDeviceIndex_][chn][C2S].swIdx, (int)num, activeDeviceIndex_, chn, C2S);
     367            0 :                         if (num <= buffers_held_)
     368            0 :                                 buffers_held_ -= num;
     369              :                         else
     370            0 :                                 buffers_held_ = 0;
     371              :                 }
     372              :                 else
     373            0 :                         TLOG(TLVL_WARN) << "read_release num=" << num << " > has_recv_data=" << has_recv_data;
     374              :         }
     375            0 :         deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
     376            0 :         return retsts;
     377              : }  // end read_release()
     378              : 
     379            0 : int mu2edev::read_register(uint16_t address, int tmo_ms, uint32_t* output)
     380              : {
     381            0 :         auto start = std::chrono::steady_clock::now();
     382            0 :         if (simulator_ != nullptr)
     383              :         {
     384            0 :                 return simulator_->read_register(address, tmo_ms, output);
     385              :         }
     386              :         m_ioc_reg_access_t reg;
     387            0 :         reg.reg_offset = address;
     388            0 :         reg.access_type = 0;
     389              : 
     390            0 :         int counter = 0;
     391            0 :         int errorCode = -99;
     392              : 
     393            0 :         while (counter < 5 && errorCode < 0)
     394              :         {
     395            0 :                 errorCode = ioctl(devfd_, M_IOC_REG_ACCESS, &reg);
     396            0 :                 counter++;
     397            0 :                 if (errorCode < 0) usleep(10000);
     398              :         }
     399            0 :         *output = reg.val;
     400            0 :         TRACE(TLVL_READ_REGISTER, UID_ + " - Read value 0x%x from register 0x%x errorcode %d", reg.val, address, errorCode);
     401            0 :         deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
     402            0 :         return errorCode;
     403              : }  // end read_register()
     404              : 
     405            0 : int mu2edev::write_register(uint16_t address, int tmo_ms, uint32_t data)
     406              : {
     407            0 :         auto start = std::chrono::steady_clock::now();
     408            0 :         auto retsts = -1;
     409            0 :         if (simulator_ != nullptr)
     410              :         {
     411            0 :                 retsts = simulator_->write_register(address, tmo_ms, data);
     412              :         }
     413              :         else
     414              :         {
     415              :                 m_ioc_reg_access_t reg;
     416            0 :                 reg.reg_offset = address;
     417            0 :                 reg.access_type = 1;
     418            0 :                 reg.val = data;
     419            0 :                 TRACE(TLVL_WRITE_REGISTER, UID_ + " - Writing value 0x%x to register 0x%x", data, address);
     420            0 :                 if (debugFp_) fprintf(debugFp_, (UID_ + " - Writing value 0x%x to register 0x%x - time delta %ld\n").c_str(), data, address,
     421            0 :                                                           std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - lastWriteTime_).count());
     422            0 :                 retsts = ioctl(devfd_, M_IOC_REG_ACCESS, &reg);
     423            0 :                 lastWriteTime_ = start;
     424              :         }
     425            0 :         deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
     426            0 :         return retsts;
     427              : }  // end write_register()
     428              : 
     429            0 : int mu2edev::write_register_checked(uint16_t address, int tmo_ms, uint32_t data, uint32_t* output)
     430              : {
     431            0 :         auto start = std::chrono::steady_clock::now();
     432            0 :         auto retsts = -1;
     433            0 :         if (simulator_ != nullptr)
     434              :         {
     435            0 :                 retsts = simulator_->write_register(address, tmo_ms, data);
     436            0 :                 *output = data;
     437              :         }
     438              :         else
     439              :         {
     440              :                 m_ioc_reg_access_t reg;
     441            0 :                 reg.reg_offset = address;
     442            0 :                 reg.access_type = 2;
     443            0 :                 reg.val = data;
     444            0 :                 if (debugFp_) fprintf(debugFp_, (UID_ + " - Writing value 0x%x to register 0x%x with readback - time delta %ld\n").c_str(), data, address,
     445            0 :                                                           std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - lastWriteTime_).count());
     446            0 :                 retsts = ioctl(devfd_, M_IOC_REG_ACCESS, &reg);
     447            0 :                 *output = reg.val;
     448            0 :                 lastWriteTime_ = start;
     449              : 
     450            0 :                 TRACE(TLVL_WRITE_REGISTER_CHECKED, UID_ + " - Writing value 0x%x to register 0x%x with readback=0x%x", data, address, *output);
     451              :         }
     452            0 :         deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
     453            0 :         return retsts;
     454              : }  // end write_register_checked()
     455              : 
     456            0 : void mu2edev::meta_dump()
     457              : {
     458            0 :         TRACE(TLVL_META_DUMP, UID_ + " - mu2edev::meta_dump");
     459            0 :         auto start = std::chrono::steady_clock::now();
     460            0 :         if (simulator_ == nullptr)
     461              :         {
     462            0 :                 int retsts = 0;
     463            0 :                 for (int chn = 0; chn < MU2E_MAX_CHANNELS; ++chn)
     464            0 :                         for (int dir = 0; dir < 2; ++dir)
     465            0 :                                 if ((mu2e_mmap_ptrs_[activeDeviceIndex_][0][0][0] != NULL) ||
     466            0 :                                         ((retsts = init(DTCLib::DTC_SimMode_Disabled, 0)) == 0))  // Default-init mu2edev if not given guidance
     467              :                                 {
     468            0 :                                         for (unsigned buf = 0; buf < mu2e_channel_info_[activeDeviceIndex_][chn][dir].num_buffs; ++buf)
     469              :                                         {
     470            0 :                                                 int* BC_p = (int*)mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][MU2E_MAP_META];
     471            0 :                                                 printf("buf_%02d: %u\n", buf, BC_p[buf]);
     472              :                                         }
     473              :                                 }
     474            0 :                 retsts = ioctl(devfd_, M_IOC_DUMP);
     475              :         }
     476            0 :         deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
     477            0 : }
     478              : 
     479            0 : int mu2edev::write_data(DTC_DMA_Engine const& chn, void* buffer, size_t bytes)
     480              : {
     481            0 :         if (chn == DTC_DMA_Engine_DCS && !thread_owns_dcs_lock())
     482              :         {
     483            0 :                 __SS__ << "write_data failed - dcs lock not held!" << __E__;
     484            0 :                 __SS_THROW__;
     485              :                 // return -2;
     486            0 :         }
     487              : 
     488            0 :         auto start = std::chrono::steady_clock::now();
     489            0 :         auto retsts = -1;
     490            0 :         if (simulator_ != nullptr)
     491              :         {
     492            0 :                 retsts = simulator_->write_data(chn, buffer, bytes);
     493              :         }
     494              :         else
     495              :         {
     496            0 :                 int dir = S2C;
     497            0 :                 retsts = 0;
     498            0 :                 unsigned delta = mu2e_chn_info_delta_(activeDeviceIndex_, chn, dir, &mu2e_channel_info_);  // check cached info
     499            0 :                 TRACE(TLVL_WRITE_DATA, UID_ + " - write_data delta=%u chn=%d dir=S2C, sz=%zu", delta, chn, bytes);
     500            0 :                 while (delta <= 1 &&
     501            0 :                            std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count() <
     502              :                                    1000)
     503              :                 {
     504              :                         m_ioc_get_info_t get_info;
     505            0 :                         get_info.chn = chn;
     506            0 :                         get_info.dir = dir;
     507            0 :                         get_info.tmo_ms = 1000;
     508            0 :                         int sts = ioctl(devfd_, M_IOC_GET_INFO, &get_info);
     509            0 :                         if (sts != 0)
     510              :                         {
     511            0 :                                 __SS__ << "Failed mu2edev::write_data with M_IOC_GET_INFO... return not 0." << __E__;
     512            0 :                                 perror(ss.str().c_str());
     513            0 :                                 __SS_THROW__;
     514              :                                 // exit(1);
     515            0 :                         }
     516            0 :                         mu2e_channel_info_[activeDeviceIndex_][chn][dir] = get_info;  // copy info struct
     517            0 :                         delta = mu2e_chn_info_delta_(activeDeviceIndex_, chn, dir, &mu2e_channel_info_);
     518            0 :                         usleep(1000);
     519              :                 }
     520              : 
     521            0 :                 if (delta <= 1)
     522              :                 {
     523            0 :                         __SS__ << "Failed mu2edev::write_data with HW_NOT_READING_BUFS." << __E__;
     524            0 :                         perror(ss.str().c_str());
     525            0 :                         __SS_THROW__;
     526              :                         // kill(0, SIGUSR2);
     527              :                         // exit(2);
     528            0 :                 }
     529              : 
     530            0 :                 unsigned idx = mu2e_channel_info_[activeDeviceIndex_][chn][dir].swIdx;
     531            0 :                 void* data = ((mu2e_databuff_t*)(mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][MU2E_MAP_BUFF]))[idx];
     532            0 :                 memcpy(data, buffer, bytes);
     533            0 :                 unsigned long arg = (chn << 24) | (bytes & 0xffffff);  // THIS OBIVOUSLY SHOULD BE A MACRO
     534              : 
     535            0 :                 int retry = 15;
     536              :                 do
     537              :                 {
     538            0 :                         retsts = ioctl(devfd_, M_IOC_BUF_XMIT, arg);
     539            0 :                         if (retsts != 0)
     540              :                         {
     541            0 :                                 TRACE(TLVL_WRITE_DATA, UID_ + " - write_data ioctl returned %d, errno=%d (%s), retrying.", retsts, errno, strerror(errno));
     542              :                                 // perror("M_IOC_BUF_XMIT");
     543            0 :                                 usleep(50000);
     544              :                         }  // exit(1); } // Take out the exit call for now
     545            0 :                         retry--;
     546            0 :                 } while (retry > 0 && retsts != 0);
     547              :                 // increment our cached info
     548            0 :                 if (retsts == 0)
     549              :                 {
     550            0 :                         mu2e_channel_info_[activeDeviceIndex_][chn][dir].swIdx =
     551            0 :                                 idx_add(mu2e_channel_info_[activeDeviceIndex_][chn][dir].swIdx, 1, activeDeviceIndex_, chn, dir);
     552              :                 }
     553              :         }
     554            0 :         deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
     555            0 :         if (retsts >= 0) writeSize_ += bytes;
     556            0 :         return retsts;
     557              : }  // end write_data()
     558              : 
     559            0 : int mu2edev::release_all(DTC_DMA_Engine const& chn)
     560              : {
     561            0 :         TLOG_DEBUG(TLVL_RELEASE_ALL) << __PRETTY_FUNCTION__ << " called from\n"
     562            0 :                                                                  << otsStyleStackTrace();
     563            0 :         auto retsts = 0;
     564            0 :         TRACE_EXIT { TLOG_DEBUG(TLVL_RELEASE_ALL) << "Exit - retsts=" << retsts; };
     565            0 :         if (chn == DTC_DMA_Engine_DCS && !thread_owns_dcs_lock())
     566              :         {
     567            0 :                 TRACE(TLVL_WARN, UID_ + " - release_all dcs lock not held!");
     568            0 :                 retsts = -2;
     569            0 :                 return retsts;
     570              :         }
     571            0 :         auto start = std::chrono::steady_clock::now();
     572            0 :         auto time_last_data = start;
     573              : 
     574            0 :         if (simulator_ != nullptr)
     575              :         {
     576            0 :                 TRACE(TLVL_RELEASE_ALL, UID_ + " - release all!");
     577            0 :                 retsts = simulator_->release_all(chn);
     578              :         }
     579              :         else
     580              :         {
     581            0 :                 constexpr size_t kTimeCheckIntervalLoops = 100;  // Recheck elapsed time every 100 loop iterations to avoid hot-spin clock polling.
     582            0 :                 size_t time_check_counter = 0;
     583              :                 while (1)
     584              :                 {
     585            0 :                         auto _tmo_ms = mu2e_channel_info_[activeDeviceIndex_][chn][C2S].tmo_ms;
     586            0 :                         mu2e_channel_info_[activeDeviceIndex_][chn][C2S].tmo_ms = 0;
     587            0 :                         int sts = ioctl(devfd_, M_IOC_GET_INFO, &mu2e_channel_info_[activeDeviceIndex_][chn][C2S]);
     588            0 :                         mu2e_channel_info_[activeDeviceIndex_][chn][C2S].tmo_ms = _tmo_ms;  // restore
     589            0 :                         if (sts != 0)
     590              :                         {
     591            0 :                                 __SS__ << "Failed mu2edev::release_all of chn=" << chn << " with M_IOC_GET_INFO... return " << sts << " which is not 0. " << strerror(errno) << __E__;
     592            0 :                                 perror(ss.str().c_str());
     593            0 :                                 __SS_THROW__;
     594            0 :                         }
     595            0 :                         auto has_recv_data = mu2e_chn_info_delta_(activeDeviceIndex_, chn, C2S, &mu2e_channel_info_);  // reads cached value, need M_IOC_GET_INFO before to update
     596              : 
     597            0 :                         if (has_recv_data)
     598              :                         {
     599            0 :                                 TRACE(TLVL_RELEASE_ALL, UID_ + " - release_all calling read_release(chn=%d has_recv_data=%d", chn, has_recv_data);
     600            0 :                                 read_release(chn, has_recv_data);
     601            0 :                                 time_last_data = std::chrono::steady_clock::now();
     602              :                         }
     603              : 
     604              :                         // do not worry about silence on release_all for DCS (since we have lock)
     605            0 :                         if (chn == DTC_DMA_Engine_DCS)
     606              :                         {
     607            0 :                                 if (has_recv_data) continue;
     608              : 
     609            0 :                                 TRACE(TLVL_RELEASE_ALL, UID_ + " - release_all no data to release for chn=%d", chn);
     610            0 :                                 break;
     611              :                         }
     612              : 
     613            0 :                         if (++time_check_counter >= kTimeCheckIntervalLoops)
     614              :                         {
     615            0 :                                 time_check_counter = 0;
     616            0 :                                 auto now = std::chrono::steady_clock::now();
     617            0 :                                 auto nano_since_last_data = std::chrono::duration_cast<std::chrono::nanoseconds>(now - time_last_data).count();
     618            0 :                                 if (!has_recv_data && nano_since_last_data > 1000000000LL)  // require a full 1 second of buffer idle before declaring release_all complete
     619              :                                 {
     620            0 :                                         TRACE(TLVL_RELEASE_ALL, UID_ + " - release_all done after buffers idle for >= 1s...");
     621            0 :                                         break;
     622              :                                 }
     623              : 
     624            0 :                                 auto nano_since_start = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start).count();
     625            0 :                                 if (nano_since_start > 5000000000LL)  // 5 seconds overall cap
     626              :                                 {
     627            0 :                                         __SS__ << "mu2edev::release_all of chn=" << chn << " timed out after 5 seconds while attempting to release buffers (data never went idle for the required 1 second)." << __E__;
     628            0 :                                         __SS_THROW__;
     629            0 :                                 }
     630              :                         }
     631            0 :                 }
     632              : 
     633              :                 // releaseBuffersHeld if allowing multiple buffers to be held by user space (e.g. for Data DMA channel, which is different for CFO vs DTC)
     634            0 :                 if (chn == DTC_DMA_Engine_DAQ)
     635            0 :                         buffers_held_ = 0;
     636              : 
     637              :                 // Stamp first qword of every receive buffer with 0xdeadbeef so the next
     638              :                 // consumer can tell which buffers have been freshly filled by hardware
     639              :                 // (anything still reading 0xdeadbeef at qword[0] has not been touched
     640              :                 // since this release_all completed).
     641              :                 {
     642            0 :                         auto* bufArray = (mu2e_databuff_t*)(mu2e_mmap_ptrs_[activeDeviceIndex_][chn][C2S][MU2E_MAP_BUFF]);
     643            0 :                         for (auto bufIdx = 0; bufIdx < MU2E_NUM_RECV_BUFFS; ++bufIdx)
     644              :                         {
     645            0 :                                 *reinterpret_cast<uint64_t*>(bufArray[bufIdx]) = 0xdeadbeefULL;
     646              :                         }
     647            0 :                         unsigned lastReleasedIdx = idx_add(mu2e_channel_info_[activeDeviceIndex_][chn][C2S].swIdx, -1, activeDeviceIndex_, chn, C2S);
     648            0 :                         TRACE(TLVL_RELEASE_ALL, UID_ + " - release_all stamped 0xdeadbeef on first qword of all %d receive buffers (chn=%d) last cleared buffer index #%02u",
     649              :                                   MU2E_NUM_RECV_BUFFS, chn, lastReleasedIdx);
     650              :                 }
     651              :         }
     652            0 :         deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
     653            0 :         return retsts;
     654            0 : }  // end release_all()
     655              : 
     656            0 : void mu2edev::close()
     657              : {
     658            0 :         if (simulator_ != nullptr)
     659              :         {
     660            0 :                 delete simulator_;
     661            0 :                 simulator_ = nullptr;
     662            0 :                 TLOG(TLVL_INFO) << UID_ << " " << __PRETTY_FUNCTION__ << " - mu2edev::close() closed simulated device";
     663              :         }
     664            0 :         else if (devfd_ != -1)
     665              :         {
     666              :                 // mmap also keeps handle to the device! so must unmap
     667            0 :                 for (int chn = 0; chn < MU2E_MAX_CHANNELS; ++chn)
     668            0 :                         for (unsigned dir = 0; dir < 2; ++dir)
     669            0 :                                 for (unsigned map = 0; map < 2; ++map)
     670              :                                 {
     671            0 :                                         if (mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map] &&
     672            0 :                                                 mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map] != MAP_FAILED)
     673              :                                         {
     674            0 :                                                 size_t length = mu2e_channel_info_[activeDeviceIndex_][chn][dir].num_buffs *
     675            0 :                                                                                 ((map == MU2E_MAP_BUFF) ? mu2e_channel_info_[activeDeviceIndex_][chn][dir].buff_size : sizeof(int));
     676              : 
     677            0 :                                                 TRACE(TLVL_INIT_DMA_ENGINE, UID_ + " - mu2edev::init chnDirMap2offset=%lu mu2e_mmap_ptrs_[offset][%d][%d][%d]=%p p=prot l=%lu",
     678              :                                                           // offset,
     679              :                                                           activeDeviceIndex_, chn,
     680              :                                                           dir, map, mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map],
     681              :                                                           // prot == PROT_READ ? 'R' : 'W',
     682              :                                                           length);
     683              : 
     684            0 :                                                 munmap((void*)mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map],
     685              :                                                            length);
     686            0 :                                                 mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map] = nullptr;
     687              :                                         }
     688              :                                 }
     689              : 
     690              :                 // after unmap, then close device
     691            0 :                 ::close(devfd_);
     692            0 :                 devfd_ = -1;
     693            0 :                 TLOG(TLVL_INFO) << UID_ << " " << __PRETTY_FUNCTION__ << " - mu2edev::close() closed device file descriptor";
     694              :         }
     695            0 :         TLOG(TLVL_DEBUG) << UID_ << " " << __PRETTY_FUNCTION__ << " - mu2edev::close() done: " << otsStyleStackTrace() << __E__;
     696            0 : }  // end close()
     697              : 
     698            0 : void mu2edev::begin_dcs_transaction()
     699              : {
     700            0 :         TLOG_DEBUG(TLVL_BEGIN_DCS_TRANSACTION) << UID_ << " " << __PRETTY_FUNCTION__ << " called from\n"
     701            0 :                                                                                    << otsStyleStackTrace();
     702            0 :         TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transation: who has lock? dcs_lock_held_=%llu, threadid=%llu", dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
     703            0 :         if (thread_owns_dcs_lock())
     704              :         {
     705            0 :                 TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transation: device lock already held by this thread. counter=%d, threadid=%llu", dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
     706            0 :                 dcs_locks_[activeDeviceIndex_].lock_count++;
     707            0 :                 return;
     708              :         }
     709            0 :         if (!dcs_lock_free())
     710            0 :                 TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: device lock for this instance held by another thread! Waiting... counter=%d, threadid=%llu", dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
     711              : 
     712            0 :         int tmo_ms = 1000;  // 1s timeout
     713            0 :         auto start = std::chrono::steady_clock::now();
     714            0 :         TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: waiting for library thread lock");
     715            0 :         while (!dcs_lock_free() && !thread_owns_dcs_lock() && (tmo_ms <= 0 || std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count() < tmo_ms))
     716              :         {
     717            0 :                 std::this_thread::sleep_for(std::chrono::microseconds(100));
     718              :         }
     719              : 
     720            0 :         if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count() >= tmo_ms)
     721              :         {
     722            0 :                 TRACE(TLVL_ERROR, UID_ + " begin_dcs_transaction: timed out waiting for library thread lock");
     723            0 :                 std::string file = __FILE__;
     724              :                 throw std::runtime_error(
     725            0 :                         std::string(file.find("/srcs/") != std::string::npos ? file.substr(file.find("/srcs") + 6) : file) + ":" +
     726            0 :                         std::to_string(__LINE__) + " | " +
     727            0 :                         UID_ + " - mu2e Device could not take lock - library-internal lock error. Throwing exception.");
     728            0 :         }
     729              : 
     730            0 :         if (simulator_ != nullptr)
     731              :         {
     732            0 :                 dcs_locks_[activeDeviceIndex_].thread_id = std::this_thread::get_id();
     733            0 :                 dcs_locks_[activeDeviceIndex_].lock_count++;
     734            0 :                 TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: sim mode, taking library thread lock and returning, counter=%d, threadid=%llu", dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
     735            0 :                 return;
     736              :         }
     737              : 
     738            0 :         TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: waiting for driver lock");
     739            0 :         int retsts = -1;
     740            0 :         while (retsts == -1 && (tmo_ms <= 0 || std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count() < tmo_ms))
     741              :         {
     742            0 :                 retsts = ioctl(devfd_, M_IOC_DCS_LOCK);
     743            0 :                 if ((retsts == -EAGAIN) || ((retsts == -1) && (errno == EAGAIN)))
     744              :                 {
     745            0 :                         TRACE(TLVL_BEGIN_DCS_TRANSACTION_VERBOSE, UID_ + " begin_dcs_transaction: ioctl returned %d (errno %d), waiting and retrying, counter=%d, threadid=%llu", retsts, errno, dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
     746            0 :                         std::this_thread::sleep_for(std::chrono::microseconds(100));
     747            0 :                 }
     748            0 :                 else if (retsts != 0)
     749              :                 {
     750            0 :                         TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: Method not supported by driver, taking library lock and returning. ioctl returned %d, errno %d", retsts, errno);
     751            0 :                         dcs_locks_[activeDeviceIndex_].thread_id = std::this_thread::get_id();
     752            0 :                         dcs_locks_[activeDeviceIndex_].lock_count++;
     753            0 :                         TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: Method not supported by driver, taking library lock and returning. ioctl returned %d, errno %d, counter=%d, threadid=%llu", retsts, errno, dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
     754            0 :                         return;
     755              :                 }
     756              :                 else
     757              :                 {
     758            0 :                         dcs_locks_[activeDeviceIndex_].thread_id = std::this_thread::get_id();
     759            0 :                         dcs_locks_[activeDeviceIndex_].lock_count++;
     760            0 :                         TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: have driver lock, setting library lock and returning true, counter=%d, threadid=%llu", dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
     761            0 :                         return;
     762              :                 }
     763              :         }
     764              : 
     765            0 :         TRACE(TLVL_ERROR, UID_ + " begin_dcs_transaction: timed out waiting for driver lock");
     766              :         // force unlock
     767            0 :         end_dcs_transaction(true /* force */);
     768            0 :         std::string file = __FILE__;
     769              :         throw std::runtime_error(
     770            0 :                 std::string(file.find("/srcs/") != std::string::npos ? file.substr(file.find("/srcs") + 6) : file) + ":" +
     771            0 :                 std::to_string(__LINE__) + " | " +
     772            0 :                 UID_ + " - mu2e Device could not take lock - M_IOC_DCS_LOCK error. Attempting to force unlock and throwing exception.");
     773            0 : }  // end begin_dcs_transaction()
     774              : 
     775            0 : void mu2edev::end_dcs_transaction(bool force)
     776              : {
     777            0 :         TLOG_DEBUG(TLVL_END_DCS_TRANSACTION) << UID_ << " " << __PRETTY_FUNCTION__ << " called from\n"
     778            0 :                                                                                  << otsStyleStackTrace();
     779            0 :         TRACE(TLVL_END_DCS_TRANSACTION, UID_ + " end_dcs_transaction: checking for ability to release lock force=%d, counter=%d", force, dcs_locks_[activeDeviceIndex_].lock_count.load());
     780            0 :         if (force || thread_owns_dcs_lock())
     781              :         {
     782            0 :                 dcs_locks_[activeDeviceIndex_].lock_count--;
     783            0 :                 TRACE(TLVL_END_DCS_TRANSACTION, UID_ + " end_dcs_transaction: after decrement lock counter, force=%d, counter=%d", force, dcs_locks_[activeDeviceIndex_].lock_count.load());
     784            0 :                 if (dcs_locks_[activeDeviceIndex_].lock_count.load() <= 0 || force)
     785              :                 {
     786            0 :                         dcs_locks_[activeDeviceIndex_].lock_count = 0;
     787            0 :                         if (simulator_ == nullptr)
     788              :                         {
     789            0 :                                 TRACE(TLVL_END_DCS_TRANSACTION, UID_ + " end_dcs_transaction: releasing driver lock");
     790            0 :                                 int retsts = ioctl(devfd_, M_IOC_DCS_RELEASE);
     791            0 :                                 if (retsts != 0)
     792              :                                 {
     793            0 :                                         TRACE(TLVL_END_DCS_TRANSACTION, UID_ + " end_dcs_transaction: IOCTL returned %d!", retsts);
     794            0 :                                         perror("M_IOC_DCS_RELEASE");
     795              :                                 }
     796              :                         }
     797            0 :                         TRACE(TLVL_END_DCS_TRANSACTION, UID_ + " end_dcs_transaction: releasing library lock");
     798            0 :                         dcs_locks_[activeDeviceIndex_].thread_id = NULL_TID;
     799              :                 }
     800              :         }
     801              : 
     802            0 : }  // end end_dcs_transaction()
     803              : 
     804            0 : bool mu2edev::thread_owns_dcs_lock()
     805              : {
     806            0 :         return dcs_locks_[activeDeviceIndex_].thread_id.load() == std::this_thread::get_id();
     807              : }
     808            0 : bool mu2edev::dcs_lock_free()
     809              : {
     810            0 :         return dcs_locks_[activeDeviceIndex_].thread_id.load() == NULL_TID;
     811              : }
     812              : 
     813            0 : std::string mu2edev::get_driver_version()
     814              : {
     815            0 :         TRACE(TLVL_GET_DRIVER_VERSION, UID_ + " get_driver_version BEGIN");
     816            0 :         if (simulator_ != nullptr) { return "SIMULATED"; }
     817              :         mu2e_string_t output;
     818            0 :         int retsts = ioctl(devfd_, M_IOC_GET_VERSION, &output);
     819              : 
     820            0 :         if (retsts != 0)
     821              :         {
     822            0 :                 TRACE(TLVL_GET_DRIVER_VERSION, UID_ + " get_driver_version: IOCTL returned %d!", retsts);
     823            0 :                 perror("M_IOC_GET_VERSION");
     824              :         }
     825              : 
     826            0 :         std::string outstr = std::string(output);
     827            0 :         if (outstr == "")
     828              :         {
     829            0 :                 return "UNSUPPORTED_DRIVER";
     830              :         }
     831              : 
     832            0 :         return outstr;
     833            0 : }  // end get_driver_version
     834              : 
     835              : /// @brief Dump contents of receive buffers in a loop with various options for controlling format and behavior. Intended for debugging and development use, not for production use (can cause log-file chaos if used excessively).
     836              : /// @param chn
     837              : /// @param optsmsk - bitmask of options controlling behavior and format.
     838              : ///             Bit 0 (1) to suppress the initial screen clear; when this bit is not set, the screen is cleared once before the loop starts,
     839              : ///             bit 1 (2) to run only one iteration,
     840              : ///             bit 2 (4) reserved/legacy screen-clear option (current implementation does not clear between iterations),
     841              : ///             bit 3 (8) to dump all 8192 bytes of each buffer instead of just first 4 qwords,
     842              : ///             bit 4 (16) to include stack trace at end of output,
     843              : ///             bit 28 (268435456) to force spy to run even if it has already been called once for this instance (which is usually enough and more can cause log-file chaos).
     844              : ///                     spyHasOccurred_ can also be reset to force spy to trigger once again, by calling mu2edev::resetSpyHasOccurred()
     845              : /// @param out
     846            0 : void mu2edev::spy(int chn, unsigned optsmsk, std::ostream& out /* = std::cout */)
     847              : {
     848            0 :         if (!(optsmsk & (1 << 28)) &&       // set bit 28 to force spy to happen
     849            0 :                 spyHasOccurred_ && !TTEST(50))  // set debug+50 to re-enable if you know what you're doing and want to see more than the first spy() call for a given instance in the logs (which is usually enough and more can cause log-file chaos)
     850              :         {
     851            0 :                 TLOG(TLVL_WARNING) << "spy() already executed for this mu2edev instance (" << UID_ << "); skipping to avoid log-file chaos. Call resetSpyHasOccurred() to re-enable.";
     852            0 :                 return;
     853              :         }
     854            0 :         if (!(optsmsk & (1 << 28)))  // only flag spy occurring if not forcing it (with bit 28)
     855              :         {
     856            0 :                 TLOG_INFO() << "Flagging that spy() has occurred.";
     857            0 :                 spyHasOccurred_ = true;
     858              :         }
     859              : 
     860            0 :         TLOG_INFO() << "spy";
     861              :         void* buffer;
     862              :         uint64_t* datap;
     863            0 :         out << "optsmsk=" << optsmsk << '\n';
     864            0 :         if (!(optsmsk & 1)) out << "\033[0;0H\033[J";
     865            0 :         while (++spyIteration_)
     866              :         {  // watch out (when using integer type) for compiler error: iteration 2147483647 invokes undefined behavior [-Werror=aggressive-loop-optimizations]
     867            0 :                 unsigned lastReleasedIdx = idx_add(mu2e_channel_info_[activeDeviceIndex_][chn][C2S].swIdx, -1, activeDeviceIndex_, chn, C2S);
     868            0 :                 out << "spy iteration: " << std::dec << std::setw(7) << std::setfill(' ') << spyIteration_
     869            0 :                         << "  -- last released buffer index #" << std::setw(2) << std::setfill('0') << lastReleasedIdx << '\n';
     870            0 :                 for (auto bufIdx = 0; bufIdx < MU2E_NUM_RECV_BUFFS;)
     871              :                 {
     872            0 :                         out << std::dec << std::setw(3) << std::setfill(' ') << bufIdx << " ";
     873            0 :                         for (auto buf = 0; buf < ((optsmsk & 8) ? 1 : 3); ++buf)
     874              :                         {
     875            0 :                                 buffer = ((mu2e_databuff_t*)(mu2e_mmap_ptrs_[activeDeviceIndex_][chn][C2S][MU2E_MAP_BUFF]))[bufIdx++];
     876            0 :                                 datap = (uint64_t*)buffer;
     877            0 :                                 out << "0x" << std::hex << std::setw(16) << std::setfill('0') << datap[0];
     878            0 :                                 for (auto dd = 1; dd < ((optsmsk & 8) ? 13 : 4); ++dd)
     879              :                                 {
     880            0 :                                         out << " " << std::hex << std::setw(16) << std::setfill('0') << datap[dd];
     881              :                                 }
     882            0 :                                 if (optsmsk & 8)
     883              :                                 {
     884            0 :                                         constexpr size_t totalQwords = sizeof(mu2e_databuff_t) / sizeof(uint64_t);  // 8192
     885            0 :                                         out << " ...";
     886            0 :                                         for (size_t dd = totalQwords - 13; dd < totalQwords; ++dd)
     887              :                                         {
     888            0 :                                                 out << " " << std::hex << std::setw(16) << std::setfill('0') << datap[dd];
     889              :                                         }
     890              :                                 }
     891            0 :                                 if (!(bufIdx < MU2E_NUM_RECV_BUFFS)) break;
     892            0 :                                 if (buf < 3) out << "   ";
     893              :                         }
     894            0 :                         out << '\n';
     895              :                 }
     896            0 :                 if (optsmsk & 2) break;
     897            0 :                 sleep(1);  // user should control-C here :)
     898            0 :                 if (optsmsk & 4) out << "\033[0;0H";
     899              :         }
     900            0 :         if (optsmsk & 16)
     901              :                 out << "\n\n"
     902            0 :                         << otsStyleStackTrace() << std::flush;
     903              : }  // end spy()
        

Generated by: LCOV version 2.0-1