Index: /fact/drsdaq/DAQReadout.cc
===================================================================
--- /fact/drsdaq/DAQReadout.cc	(revision 9833)
+++ /fact/drsdaq/DAQReadout.cc	(revision 9833)
@@ -0,0 +1,1414 @@
+/********************************************************************\
+
+  DAQReadout.cc
+
+  Main DAQ routines.
+   
+  Sebastian Commichau, Oliver Grimm
+  
+\********************************************************************/
+
+#include "DAQReadout.h"
+
+static const char* daq_state_str[] = {"active", "stopped"};
+static const char* daq_runtype_str[] = {"data", "pedestal", "reserved", "test"};
+
+static const struct CL_Struct { const char *Name;    
+                	  void (DAQReadout::*CommandPointer)();
+			  bool NeedNotBusy;
+			  const char *Parameters;
+			  const char *Help;
+  } CommandList[] = 
+  {{"board", &DAQReadout::cmd_board, true, "<i> [j] | <all>" ,"Address board i, boards i-j, all boards"},
+   {"status", &DAQReadout::cmd_status, false, "[daq|drs]", "Show DAQ/DRS status information"},
+   {"frequency", &DAQReadout::cmd_freq, true, "<GHz> [reg]", "Set DRS sampling frequency (regulated)"},
+   {"acalib", &DAQReadout::cmd_acalib, true, "<trig rate> ", "Amplitude calibration"},
+   {"tcalib", &DAQReadout::cmd_tcalib, true, "<trig rate> ", "Amplitude calibration"},
+   {"trigger", &DAQReadout::cmd_trigger, true, "<on|off>", "Hardware trigger on or off"},
+   {"wmode", &DAQReadout::cmd_wmode, true, "<run|stop>", "Domino wave running or stopped during read out"},
+   {"rmode", &DAQReadout::cmd_rmode, true, "<first|stop>", "Readout start at first bin or stop position (DRS4)"},
+   {"dmode", &DAQReadout::cmd_dmode, true, "<single|continuous>", "Domino wave single shot or continuous"},
+   {"centre", &DAQReadout::cmd_centre, true, "<V>", "Set centre of input range (DRS4)"},
+   {"refclk", &DAQReadout::cmd_refclk, true, "<FPGA|extern>", "Set reference clock to FPGA or external (DRS4)"},
+   {"read", &DAQReadout::cmd_read, false, "<brd chip chan> [res]", "Read current data (and restart if DAQ not active)"},
+   {"take", &DAQReadout::cmd_take, false, "<d|p|t> [n] [source]", "Start run (data, pedestal or test) with n events"},
+   {"events", &DAQReadout::cmd_events, false, "", "Number of events in current run"},
+   {"start", &DAQReadout::cmd_take, false, "", "Start run without disk writing"},
+   {"stop", &DAQReadout::cmd_stop, false, "", "Issue soft trigger and stop DAQ"},
+   {"regtest", &DAQReadout::cmd_regtest, true, "", "DRS register test"},
+   {"ramtest", &DAQReadout::cmd_ramtest, true, "", "DRS RAM integrity and speed test"},
+   {"chiptest", &DAQReadout::cmd_chiptest, true, "", "Chip test"},
+   {"eepromtest", &DAQReadout::cmd_eepromtest, true, "", "EEPROM test"},
+   {"led", &DAQReadout::cmd_led, true, "<on|off>", "Turn LED on or off"},
+   {"config", &DAQReadout::cmd_config, false, "", "Print drsdaq configuration"},
+   {"serial", &DAQReadout::cmd_serial, true, "<i> <n>", "Set serial# of board <i> to <n> (experts only)"},
+   {"disk", &DAQReadout::cmd_disk, false, "" ,"Remaining disk space"},
+   {"uptime", &DAQReadout::cmd_uptime, false, "", "Get DAQ uptime"},		  
+   {"update", &DAQReadout::cmd_update, false, "<sec>", "Minimum delay between updates to DIM event service"},		  
+   {"exit", &DAQReadout::cmd_exit, false, "", "Exit program"},
+   {"help", &DAQReadout::cmd_help, false, "", "Print help"}};
+
+
+// Global pointer for thread entry routines
+class DAQReadout *ThisClass;
+
+// -----------------------------------------------
+// *****  Constructor: Class initialisation  *****
+// -----------------------------------------------
+//
+ 
+DAQReadout::DAQReadout(): EvidenceServer(SERVER_NAME) {
+
+  // Initialization
+  ThisClass = this;
+  MainThread = pthread_self();
+  ConsoleText = NULL;
+  time(&StartTime);
+
+  // DIM console service used in PrintMessage()
+  ConsoleOut = new DimService(SERVER_NAME"/ConsoleOut", (char *) "");
+
+  // Initialize status structure
+  daq_state	     		= stopped;
+  daq_runtype	    	= data;
+  NumEvents	     		= 0;
+  NumEventsRequested 	= 0;
+  NumBoards	     		= 0;
+  FirstBoard	     	= 0;
+  LastBoard	     		= -1;
+  MinDelay  			= 1;
+  RunNumber 			= -1;
+
+  // Get configuration data (static needed for c_str() pointers to remain valid after constructor finished)
+  static std::string _RawDataPath = GetConfig("RawDataPath");
+  static std::string _CalibDataPath = GetConfig("CalibDataPath");
+  fRawDataPath = _RawDataPath.c_str();
+  fCalibDataPath = _CalibDataPath.c_str();
+  fFirstSample = atoi(GetConfig("FirstSample").c_str());
+  fSamples = atoi(GetConfig("Samples").c_str());
+  fMinDiskSpaceMB = atoi(GetConfig("MinDiskSpaceMB").c_str());
+  fMaxFileSizeMB = atoi(GetConfig("MaxFileSizeMB").c_str());
+  fDefaultFrequency = atof(GetConfig("DefaultFrequency").c_str());
+
+  fLedTrigBoard = atoi(GetConfig("TrigBoard").c_str());
+  fLedTrigChannel = atoi(GetConfig("TrigChannel").c_str());
+  fLedTrigChip = atoi(GetConfig("TrigChip").c_str());
+  fLedTrigSample = atoi(GetConfig("TrigSample").c_str());
+  fLedTrigThreshold = atoi(GetConfig("TrigThreshold").c_str());
+  fLedSignalSample = atoi(GetConfig("SignalSample").c_str());
+  fLedBaselineSample = atoi(GetConfig("BaselineSample").c_str());
+  fIntHalfWidth = atoi(GetConfig("IntHalfWidth").c_str());
+
+  if (fFirstSample < 0 || fFirstSample > kNumberOfBins || fSamples > kNumberOfBins) {
+    PrintMessage("Warning: Sample range in configuration beyond limits, setting to full range\n");
+    fFirstSample = kNumberOfBins;
+    fSamples = kNumberOfBins;
+  }
+  snprintf(CalibInfoFilename,sizeof(CalibInfoFilename), "%s/CalibInfo", fCalibDataPath);
+  
+  // Allocate headers and initialise to zero
+  RHeader = new RunHeader;
+  memset(RHeader, 0, sizeof(RunHeader));
+  EHeader = new EventHeader;
+  memset(EHeader, 0, sizeof(EventHeader));
+
+  // Scan for DRS boards
+  NumBoards = GetNumberOfBoards();
+  DRSFreq = new float [NumBoards];
+  ACalib = new bool [NumBoards];
+  ACalibTemp = new double [NumBoards];
+  TCalib = new bool [NumBoards];
+
+  if (NumBoards == 0) PrintMessage("No DRS boards found\n");
+
+  for (int i=0; i<NumBoards; i++) {
+    LastBoard++;
+    GetBoard(i)->Init();
+    DRSFreq[i] = 0;
+    ACalib[i] = false;
+    ACalibTemp[i] = -999;
+    TCalib[i] = false;      
+  }
+  BStruct  = new BoardStructure [NumBoards == 0 ? 1:NumBoards];
+  memset(BStruct, 0, sizeof(BoardStructure)*(NumBoards == 0 ? 1:NumBoards));
+
+  WaveForm = new short [NumBoards == 0 ? 1:NumBoards][kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins];
+  TriggerCell = new int [NumBoards == 0 ? 1:NumBoards][kNumberOfChipsMax] ();  // Zero initialised
+
+  DIMEventData = new char [sizeof(RunHeader) + sizeof(EventHeader) +
+  						   sizeof(BoardStructure)*(NumBoards == 0 ? 1:NumBoards) +
+						   sizeof(int)*(NumBoards == 0 ? 1:NumBoards)*kNumberOfChipsMax +
+						   sizeof(short)*(NumBoards == 0 ? 1:NumBoards)*kNumberOfChipsMax*kNumberOfChannelsMax*kNumberOfBins];
+
+  // Create DIM event data service
+  EventService = new DimService (SERVER_NAME"/EventData", (char *) "C", DIMEventData, 0);
+  RunNumService = new DimService (SERVER_NAME"/RunNumber", RunNumber);
+						   
+  // Install DIM command (after all initialized)
+  Command = new DimCommand((char *) SERVER_NAME"/Command", (char *) "C", this);
+}
+
+// ------------------------
+// *****  Destructor  *****
+// ------------------------
+
+DAQReadout::~DAQReadout() {
+
+  delete Command;
+
+  delete RunNumService;
+  delete EventService;	delete[] DIMEventData;
+  delete RHeader;		delete EHeader;
+  delete[] ACalibTemp;
+  delete[] ACalib;		delete[] TCalib;
+  delete[] DRSFreq; 	delete[] BStruct;
+  delete[] WaveForm;	delete[] TriggerCell;
+
+  delete ConsoleOut;
+  free(ConsoleText);
+}
+
+// --------------------------------
+// *****  Command evaluation  *****
+// --------------------------------
+
+void DAQReadout::Execute(char *Command) {
+
+  if (Command[0]=='.') {   // Shell command
+    system(&(Command[1]));
+    return;
+  }
+
+  for (int i=0; i<MAX_NUM_TOKEN; i++) Param[i] = "";  // All pointers point initially to empty string
+  NParam = ParseInput(Command, Param);
+  
+  // Search for command
+  unsigned int Count;
+  for (Count=0; Count<sizeof(CommandList)/sizeof(CL_Struct); Count++) {
+    if (Match(Param[0], CommandList[Count].Name)) break;
+  }
+  
+  // Command not found?
+  if (Count == sizeof(CommandList)/sizeof(CL_Struct)) {
+	PrintMessage("Unknown command '%s'\n", Param[0]);
+	return;
+  }
+
+  if(CommandList[Count].NeedNotBusy && daq_state==active) PrintMessage("DAQ is busy\n");
+  else if(CommandList[Count].NeedNotBusy && NumBoards==0) PrintMessage("No boards available\n");
+  else {
+	CmdNumber = Count;
+	(this->*CommandList[CmdNumber].CommandPointer)();
+  }
+}
+  	  
+// Get uptime
+void DAQReadout::cmd_uptime() {
+  time_t ActualT;
+  time (&ActualT);
+  PrintMessage("%02d:%02d:%02d\n", (int) difftime(ActualT, StartTime)/3600, ((int) difftime(ActualT, StartTime)/60)%60, (int) difftime(ActualT, StartTime)%60);
+} 
+
+// Print disk space
+void DAQReadout::cmd_disk() {
+  PrintMessage("Free disk space (%s) [MB]: %lu\n", fRawDataPath, CheckDisk(fRawDataPath));
+} 
+
+// Print current number of events
+void DAQReadout::cmd_events() {
+  if(daq_state != active) PrintMessage("DAQ not active\n");
+  else PrintMessage("Current number of events: %d (of %d requested)\n", NumEvents, NumEventsRequested);
+} 
+
+// Print DAQ configuration
+void DAQReadout::cmd_config() {
+
+  PrintMessage("RawDataPath: %s\n"
+		"DefaultFrequency: %.2f\tFirstSample: %d\tSamples: %u\n"
+		"MinDiskSpaceMB: %u\tMaxFileSizeMB: %d\n"
+		"CalibDataPath: %s\n\n"
+		"LedTrigBoard: %d\t\tLedTrigChip: %d\t\tLedTrigChannel: %d\n"
+        "LedTrigSample: %d\tLedTrigThreshold: %.2f\n"
+        "LedSignalSample: %d\tLedBaselineSample: %d\tIntHalfWidth:%u\n",
+    fRawDataPath,fDefaultFrequency,fFirstSample,fSamples,fMinDiskSpaceMB,
+    fMaxFileSizeMB,fCalibDataPath,
+    fLedTrigBoard, fLedTrigChip, fLedTrigChannel, fLedTrigSample,
+    fLedTrigThreshold, fLedSignalSample, fLedBaselineSample, fIntHalfWidth);
+}
+
+// Start DAQ 
+void DAQReadout::cmd_take() {
+  
+  // Check conditions if this is not a test run
+  if(!Match(Param[1],"test")) {
+    if (daq_state==active || NumBoards==0) {
+      PrintMessage("DAQ is busy or no boards available\n");
+      return;
+    }
+    // Check if all boards have the same number of DRS chips and channels
+    // (restriction of current data format)
+    for (int i=FirstBoard; i<=LastBoard-1; i++) {
+      if ((GetBoard(i)->GetNumberOfChannels() != GetBoard(i+1)->GetNumberOfChannels()) || (GetBoard(i)->GetNumberOfChips() != GetBoard(i+1)->GetNumberOfChips())) {
+	PrintMessage("Cannot take data if not all boards have the same number of DRS chips and channels due to data format restriction\n");
+	return;
+      }
+    }
+    if (!IsDRSFreqSet()) {
+      PrintMessage("Setting default frequency\n");
+      SetDRSFrequency(fDefaultFrequency, true);
+    }
+    if (!ReadCalibration()) {
+      PrintMessage("Cannot start run if response calibration not read\n");
+      //return;
+    }
+    // Readout from domino wave stop position (ignored if not DRS4)
+    SetDOMINOReadMode(1); 
+  }
+      
+  if (Match(Param[1],"data")) {
+    HWTrigger(1);
+    daq_runtype = data;
+  } 
+  else if (Match(Param[1],"pedestal")) {
+    HWTrigger(0);
+    daq_runtype = pedestal;	
+  } 
+  else if (Match(Param[1],"test")) {
+    daq_runtype = test;	
+  }
+  else {
+    PrintUsage();
+    return;
+  }
+
+  if (NParam==3) NumEventsRequested = atoi(Param[2]);
+  if (NParam==4) {
+    NumEventsRequested = atoi(Param[2]);
+    strncpy(RHeader->Description, Param[3], sizeof(RHeader->Description));
+  }
+  else snprintf(RHeader->Description,sizeof(RHeader->Description),"DUMMY");
+
+  // Request new run number (if command was 'start', set run number to -1 --> no disk writing)
+  if (Match(Param[0], "take")) {
+	DimRpcInfo RunNumRPC((char *) "NextRunNumber", -1);
+
+	RunNumRPC.setData((char *) "");printf("and least here\n");
+
+	RunNumber = RunNumRPC.getInt();
+	if(RunNumber < 1) {
+      PrintMessage("Error: No connection to run number dispatcher or received number smaller than 1\n");
+      return;
+	}
+  }
+  else RunNumber = -1;
+  RunNumService->updateService();
+
+  // Create DAQ thread
+  pthread_t Thread;
+  int Code;
+  if ((Code = pthread_create(&Thread, NULL, (void * (*)(void *)) ::DAQ,(void *) this)) != 0) {
+    PrintMessage("pthread_create() failed with DAQ thread (error code %d)\n", Code);
+  }
+  else {
+    daq_state = active;
+    Stop = false;
+    if ((Code = pthread_detach(Thread)) != 0) {
+	  PrintMessage("pthread_detach() failed with DAQ thread (error code %d)\n", Code);
+	}
+  }  
+}
+  
+// EEPROM test
+void DAQReadout::cmd_eepromtest() {
+
+  unsigned short buf[16384],rbuf[16384];
+  int n = 0;
+
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    PrintMessage("EEPROM test of board #%d:\n\r",i);
+    for (int j=0; j<16384; j++) buf[j] = rand();
+    GetBoard(i)->WriteEEPROM(1, buf, sizeof(buf));
+    memset(rbuf, 0, sizeof(rbuf));
+    GetBoard(i)->Write(T_RAM, 0, rbuf, sizeof(rbuf));
+    GetBoard(i)->ReadEEPROM(1, rbuf, sizeof(rbuf));
+    
+    for (int j=0; j<16384; j++) if (buf[j] != rbuf[j]) n++;
+    printf("32 kByte written, %d errors\n", n);
+  }
+}
+
+// RAM test
+void DAQReadout::cmd_ramtest() {
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    PrintMessage("RAM integrity and speed test of board #%d:\n",i);
+    GetBoard(i)->RAMTest(3);
+  }
+} 
+
+// Register test
+void DAQReadout::cmd_regtest() {
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    PrintMessage("Register test of board #%d:\n\r",i);
+    GetBoard(i)->RegisterTest();
+  }
+}
+
+// Chip test
+void DAQReadout::cmd_chiptest() {
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    if (GetBoard(i)->ChipTest()) PrintMessage("Chip test of board %i successful\n", i);
+    else PrintMessage("Chip test of board %i failed\n", i);
+  }
+}
+
+// Stop DAQ 
+void DAQReadout::cmd_stop() {
+  if(!daq_state==active && !IsDRSBusy()) PrintMessage("Nothing to stop\n");
+  if (daq_state==active) StopRun();
+  if (IsDRSBusy()) {
+    StopDRS();
+    PrintMessage("Domino wave stopped\n");
+  }
+} 
+  
+// Read current data
+// For socket transmission: all numbers must be separated by exactly one
+// whitespace; the first number is the number of numbers that follow, the
+// second number the sampling frequency in GHz, the third the conversion factor 
+
+void DAQReadout::cmd_read() {
+  if(NumBoards==0) {
+    PrintMessage("No mezzanine boards available\n");
+    return;
+  }
+  if (NParam<4) {
+    PrintUsage();
+    return;
+  }
+  int Board = atoi(Param[1]), Chip = atoi(Param[2]), Channel = atoi(Param[3]);
+        
+  if (Board>LastBoard || Board<FirstBoard) {
+    PrintMessage("Error: Board number out of range\n");
+    return;
+  }
+  if (Channel<0 || Channel>=GetBoard(Board)->GetNumberOfChannels()) {
+    PrintMessage("Error: Channel number out of range\n");
+    return;
+  }
+  if (Chip<0 || Chip>=GetBoard(Board)->GetNumberOfChips()) {
+    PrintMessage("Error: Chip number out of range\n");
+    return;
+  }
+
+  if (daq_state != active) {
+    ReadCalibration();
+    if(NParam==5) StopDRS();
+    ReadCalibratedDRSData();
+    if(NParam==5) StartDRS();
+  } 
+
+  PrintMessage("==START== %d %.2f %.2f ",kNumberOfBins+2,DRSFreq[Board],GetBoard(Board)->GetPrecision());
+  for (int k=0; k<kNumberOfBins; k++) {
+    PrintMessage("%.1f ", (float) WaveForm[Board][Chip][Channel][k]);
+  }
+  PrintMessage("==END==");
+  PrintMessage("\n");
+  PrintMessage("Trigger cell: %d\n", TriggerCell[Board][Chip]);  
+} 
+
+// Set Domino mode
+void DAQReadout::cmd_dmode() {
+  if (Match(Param[1],"continuous")) SetDOMINOMode(1);
+  else if (Match(Param[1],"single")) SetDOMINOMode(0);
+  else PrintUsage();
+} 
+
+// Set Domino readout mode
+void DAQReadout::cmd_rmode() {
+  if (Match(Param[1],"first")) SetDOMINOReadMode(0);
+  else if (Match(Param[1],"stop")) SetDOMINOReadMode(1);
+  else PrintUsage();
+} 
+
+// Set Domino wave mode
+void DAQReadout::cmd_wmode() {
+  if (Match(Param[1],"run")) SetDOMINOWaveMode(1);
+  else if (Match(Param[1],"stop")) SetDOMINOWaveMode(0);
+  else PrintUsage();
+} 
+
+// Set input range
+void DAQReadout::cmd_centre() {
+
+  if (NParam!=2) {
+    PrintUsage();
+    return;
+  }
+  
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    if (GetBoard(i)->SetInputRange(atof(Param[1])) == 1) {
+      PrintMessage("Range of board %d: %.2f to %.2f Volt (centre %.2f V)\n",i,atof(Param[1])-0.5, atof(Param[1])+0.5, atof(Param[1]));
+    }
+    else PrintMessage("Centre voltage of board %d out of range\n", i);
+  }
+}
+
+// Set refclock source
+void DAQReadout::cmd_refclk() {
+
+  if (NParam!=2) {
+    PrintUsage();
+    return;
+  }
+  
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    if (Match(Param[1], "extern")) GetBoard(i)->SetRefclk(1);
+	else GetBoard(i)->SetRefclk(0);
+  }
+}
+
+// Set trigger mode
+void DAQReadout::cmd_trigger() {
+  if (Match(Param[1],"on")) HWTrigger(1);
+  else if (Match(Param[1],"off")) HWTrigger(0);
+  else PrintUsage();
+} 
+
+// Set serial number of board
+void DAQReadout::cmd_serial() {    
+  if (NParam==4 && Match(Param[3], "expert")) {
+    if ((atoi(Param[1]) < FirstBoard) || (atoi(Param[1]) > LastBoard))
+      PrintMessage("Board number out of range (%d...%d)!\n",FirstBoard,LastBoard);
+    else if (atoi(Param[2]) < 100 || atoi(Param[2]) >= 1000) 
+      PrintMessage("Serial number out of range (100...999)!\n");
+    else {
+      PrintMessage("Flashing EEPROM of board %d...\n", atoi(Param[1]));
+      (GetBoard(atoi(Param[1])))->SetBoardSerialNumber(atoi(Param[2]));
+    }
+  }
+  else PrintMessage("You are not allowed to change the serial number!\n");
+} 
+
+// Do amplitude calibration
+void DAQReadout::cmd_acalib() {
+
+  if (!IsDRSFreqSet()) {
+    PrintMessage("Set sampling frequency for all boards first\n");
+    return;
+  }
+      
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    if (GetBoard(i)->GetDRSType()==2 && (NParam!=2 || !atof(Param[1]))) {
+      PrintUsage();
+      return;
+    }
+
+    PrintMessage("Creating amplitude calibration of board %d (serial #%04d)\n  Note: No input signals should be connected\n", i, GetBoard(i)->GetBoardSerialNumber());
+		
+    GetBoard(i)->EnableTcal(0);
+    GetBoard(i)->SelectClockSource(0);
+    if (GetBoard(i)->GetDRSType() == 4) {
+      GetBoard(i)->SetFrequency(DRSFreq[i], true);
+      GetBoard(i)->CalibrateVolt(this);
+    } else {
+      GetBoard(i)->Init();
+      GetBoard(i)->SetFrequency(DRSFreq[i], true);
+      GetBoard(i)->SoftTrigger();
+
+      if (GetBoard(i)->GetDRSType() == 3) {
+        GetBoard(i)->GetResponseCalibration()->SetCalibrationParameters(1,11,0,20,0,0,0,0,0);
+      }
+      else GetBoard(i)->GetResponseCalibration()->SetCalibrationParameters(1,36,110,20,19,40,15,atof(Param[1]),0);
+ 
+      GetBoard(i)->SetCalibrationDirectory(fCalibDataPath);
+      for (int j=0; j<2; j++) {
+        GetBoard(i)->GetResponseCalibration()->ResetCalibration();
+        while (!GetBoard(i)->GetResponseCalibration()->RecordCalibrationPoints(j)) {}
+	PrintMessage("Calibration points recorded.\n");
+        while (!GetBoard(i)->GetResponseCalibration()->FitCalibrationPoints(j)) {}
+	PrintMessage("Calibration points fitted.\n");
+        while (!GetBoard(i)->GetResponseCalibration()->OffsetCalibration(j)) {}
+	PrintMessage("Offset calibration done.\n");
+        if (!GetBoard(i)->GetResponseCalibration()->WriteCalibration(j)) break;
+      }
+    } // else for DRS2/3
+    ACalib[i] = true;
+    ACalibTemp[i] = GetBoard(i)->GetTemperature();
+	
+  } // Loop over boards
+  PrintMessage("Amplitude calibration finished\n");
+  
+  // Write short calibration information
+  time_t Time = time(NULL);
+  FILE *InfoFile = fopen(CalibInfoFilename, "w");
+  if (InfoFile != NULL) {
+	fprintf(InfoFile, "# Calibration information as of %s\n", ctime(&Time));
+	for (int i=0; i<GetNumberOfBoards(); i++) {
+	  fprintf(InfoFile, "%d %d %.1f %d %.2f\n", GetBoard(i)->GetBoardSerialNumber(), ACalib[i], ACalibTemp[i], TCalib[i], DRSFreq[i]);
+	}
+	fclose(InfoFile);
+  }
+  else PrintMessage("Could not write calibration information to file '%s'\n", CalibInfoFilename);
+
+}
+
+// Do time calibration
+void DAQReadout::cmd_tcalib() {
+  
+  if (!IsDRSFreqSet()) {
+    PrintMessage("Set sampling frequency for all boards first\n");
+    return;
+  }
+  if (!ReadCalibration()) {
+    PrintMessage("Amplitude calibration has to be done first\n");
+    return;
+  }
+      
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    if (GetBoard(i)->GetDRSType() != 4 || GetBoard(i)->GetFirmwareVersion() < 13279) {
+      PrintMessage("Time calibration needs DRS4 and minimum firmware version 13279, skipping board %d\n", i);
+      continue;
+    }
+    PrintMessage("Creating time calibration of board %d (serial #%04d)\n  Note: No input signals should be connected\n", i, GetBoard(i)->GetBoardSerialNumber());
+    
+    GetBoard(i)->SetFrequency(DRSFreq[i], true);
+    if (GetBoard(i)->CalibrateTiming(this) != 1) {
+      PrintMessage("Time calibration method returned error status, stopping calibration\n");
+      return;
+    }
+
+    TCalib[i] = true;
+
+    // Write calibration data to file
+    float Time[kNumberOfChipsMax][kNumberOfBins];
+    char *Filename;
+    FILE *Calibfile;
+	bool WriteOK = true;
+	
+    // Copy calibration data into array
+	for (int Chip=0; Chip<GetBoard(i)->GetNumberOfChips(); Chip++) {
+      GetBoard(i)->GetTime(Chip, Time[Chip], true, false);
+	}
+
+	// Write calibration data to file
+    if (asprintf(&Filename, "%s/TCalib_%d_%.2fGHz.txt", fCalibDataPath, GetBoard(i)->GetBoardSerialNumber(), DRSFreq[i]) == -1) {
+      PrintMessage("Error: asprintf() failed, cannot generate filename (%s)\n", strerror(errno));
+      return; 
+    }
+    if ((Calibfile=fopen(Filename,"w")) == NULL) {
+      PrintMessage("Error: Could not open file '%s' \n", Filename);      
+    }
+    else {
+      if(fprintf(Calibfile, "# DRS time calibration\n") == -1) WriteOK = false;
+
+      for (int Bin=0; Bin<kNumberOfBins; Bin++) {
+		for (int Chip=0; Chip<GetBoard(i)->GetNumberOfChips(); Chip++) {
+          if(fprintf(Calibfile, "%.2f ", Time[Chip][Bin]) == -1) WriteOK = false;
+		}
+        if(fprintf(Calibfile, "\n") == -1) WriteOK = false;
+	  }
+	  if (fclose(Calibfile) != 0) PrintMessage("Error closing file '%s'\n", Filename);
+	}
+	if (!WriteOK) PrintMessage("Error writing to file '%s'\n", Filename);
+	else PrintMessage("Calibration written to file '%s'\n", Filename);
+	
+    free(Filename);
+  }
+  PrintMessage("Time calibration finished\n");
+}
+
+// Set DRS sampling frequency
+void DAQReadout::cmd_freq() {    
+  if (NParam>=2 && atof(Param[1])) {
+    SetDRSFrequency(atof(Param[1]), NParam==2 ? false : true);
+  }
+  else PrintUsage();
+} 
+
+// Set LED
+void DAQReadout::cmd_led() {
+  if (Match(Param[1], "on") || Match(Param[1], "off"))
+    for (int i=FirstBoard; i<=LastBoard; i++)
+      (GetBoard(i))->SetLED(Match(Param[1], "on") ? 1 : 0);     
+  else PrintUsage();
+}
+
+// Print status
+void DAQReadout::cmd_status() {
+  
+  double freq;
+  
+  if(NParam==1 || Match(Param[1],"daq")) {
+    PrintMessage("********** DAQ STATUS **********\n"
+ 	        " DAQ: %s\n"
+  	        " Run number: %d\n"
+ 	        " Run type: %s\n"
+	        " Event: %d\n"
+      	        " Requested events per run: %d\n"
+ 	        " Storage directory: %s\n"
+ 	        " Disk space: %lu MByte\n"
+              	" Total number of DRS boards: %d\n"
+	        " Active DRS boards: %d\n",
+      daq_state_str[daq_state], daq_state==active ? (int) RunNumber:-1,
+      daq_state==active ? daq_runtype_str[daq_runtype]:"n/a", NumEvents,
+      NumEventsRequested, fRawDataPath,
+      CheckDisk(fRawDataPath), NumBoards, LastBoard - FirstBoard + 1);
+
+    for (int i=FirstBoard;i<=LastBoard;i++)
+      PrintMessage(" Frequency of board %d set: %s\n",i,(DRSFreq[i]!=0 ? "yes":"no"));
+  }
+  
+  if(NParam==1 || Match(Param[1],"drs")) {
+    PrintMessage("\n********** DRS STATUS **********\n");
+    if (NumBoards) {
+      for (int i=FirstBoard; i<=LastBoard; i++) {
+
+	PrintMessage("Board #%d in slot %d %s\n",i,((GetBoard(i))->GetSlotNumber() >> 1)+2,((GetBoard(i))->GetSlotNumber() & 1)==0 ? "upper":"lower");
+	PrintMessage(" DRS%d   serial %d, firmware %d\n"
+                    " Actual temperature:   %1.1lf C\n"
+		    " Calibration temp.:    %1.1lf C\n"
+                    " Status reg.:          0x%08X\n", 
+		    GetBoard(i)->GetDRSType(),
+		    GetBoard(i)->GetBoardSerialNumber(),
+		    GetBoard(i)->GetFirmwareVersion(),
+		    GetBoard(i)->GetTemperature(),
+		    ACalibTemp[i],
+		    GetBoard(i)->GetStatusReg());
+
+
+	if (GetBoard(i)->GetStatusReg() & BIT_RUNNING)
+	  PrintMessage("   Domino wave running\n");
+	if (GetBoard(i)->GetStatusReg() & BIT_NEW_FREQ1)
+	  PrintMessage("   New Freq1 ready\n");
+	if (GetBoard(i)->GetStatusReg() & BIT_NEW_FREQ2)
+	  PrintMessage("   New Freq2 ready\n");
+
+	PrintMessage(" Control reg.:         0x%08X\n", (GetBoard(i))->GetCtrlReg());
+	if (GetBoard(i)->GetCtrlReg() & BIT_AUTOSTART)
+	  PrintMessage("   AUTOSTART enabled\n");
+	if (GetBoard(i)->GetCtrlReg() & BIT_DMODE)
+	  PrintMessage("   DMODE circular\n");
+	else
+	  PrintMessage("   DMODE single shot\n");
+	if (GetBoard(i)->GetCtrlReg() & BIT_LED)
+          PrintMessage("   LED\n");
+	if (GetBoard(i)->GetCtrlReg() & BIT_TCAL_EN)
+	  PrintMessage("   TCAL enabled\n");
+	if (GetBoard(i)->GetCtrlReg() & BIT_TCAL_SOURCE)
+	  PrintMessage("   TCAL_SOURCE enabled\n");
+	if (GetBoard(i)->GetCtrlReg() & BIT_FREQ_AUTO_ADJ)
+	  PrintMessage("   FREQ_AUTO_ADJ enabled\n");
+	if (GetBoard(i)->GetCtrlReg() & BIT_ENABLE_TRIGGER1)
+	  PrintMessage("   ENABLE_TRIGGER\n");
+	if (GetBoard(i)->GetCtrlReg() & BIT_LONG_START_PULSE)
+	  PrintMessage("   LONG_START_PULSE\n");
+	if (GetBoard(i)->GetCtrlReg() & BIT_ACAL_EN)
+	  PrintMessage("   ACAL enabled\n");
+	PrintMessage(" Trigger bus:          0x%08X\n", GetBoard(i)->GetTriggerBus());
+	if (GetBoard(i)->IsBusy()) {
+	  GetBoard(i)->ReadFrequency(0, &freq);
+	  PrintMessage(" Frequency0:           %1.4lf GHz\n", freq);
+	  GetBoard(i)->ReadFrequency(1, &freq);
+	  PrintMessage(" Frequency1:           %1.4lf GHz\n", freq);
+	} 
+	else PrintMessage(" Domino wave stopped\n");
+      }
+    }
+    else PrintMessage("No DRS boards available!\n\n");
+  }
+}
+
+// Adress DRS boards
+void DAQReadout::cmd_board() {
+  if (Match(Param[1],"all")) {
+    FirstBoard = 0;
+    LastBoard = GetNumberOfBoards()-1;
+  } 
+  else if (NParam==2 && atoi(Param[1])>=0 && atoi(Param[1])<NumBoards) {
+    FirstBoard = atoi(Param[1]);
+    LastBoard = FirstBoard;
+  }
+  else if (NParam==3 && atoi(Param[1])>=0 && atoi(Param[1])<NumBoards && 
+           atoi(Param[2])>0 && atoi(Param[2])<NumBoards) {
+    FirstBoard = atoi(Param[1]);
+    LastBoard = atoi(Param[2]);
+  }
+  else PrintMessage("Cannot address board(s), out of range.\n");
+} 
+
+// Set input range
+void DAQReadout::cmd_update() {
+
+  if (NParam != 2 || atoi(Param[1]) < 0) {
+    PrintUsage();
+    return;
+  }
+  MinDelay = atoi(Param[1]);  
+}
+
+// Print help
+void DAQReadout::cmd_help() {
+
+  char Buffer[MAX_COM_SIZE];
+
+  for(unsigned int i=0; i<sizeof(CommandList)/sizeof(CL_Struct); i++) {
+    snprintf(Buffer, sizeof(Buffer), "%s %s", CommandList[i].Name, CommandList[i].Parameters);
+    PrintMessage("%-28s%s\n", Buffer, CommandList[i].Help);
+  }     
+  PrintMessage(".<command>                  Execute shell command\n\n"
+   "Items in <> are mandatory, in [] optional, | indicates mutual exclusive or.\n"
+   "Test data can also be written if no DRS boards are available.\n"
+   "Strings containing spaces have to be enclosed in \"double quotes\".\n"); 
+}
+
+// Exit programm - SIGTERM sets ExitRequest flag
+// If command comes from DimCommand thread, SIGTERM also makes readline() return
+void DAQReadout::cmd_exit() {
+
+  if (daq_state == active) PrintMessage("Issue 'stop' first to stop daq\n");
+  else pthread_kill(MainThread, SIGTERM);  
+}
+
+
+// ----------------------------------------------
+// *****  Utility function for DRS control  *****
+// ----------------------------------------------
+
+// Start domino wave
+void DAQReadout::StartDRS() {
+  for (int i=FirstBoard; i<=LastBoard; i++) GetBoard(i)->StartDomino();
+}
+
+// Stop domino wave
+void DAQReadout::StopDRS() {
+  for (int i=FirstBoard; i<=LastBoard; i++) GetBoard(i)->SoftTrigger();
+}
+
+// Transfer amplitude-calibrated waveforms to memory
+void DAQReadout::ReadCalibratedDRSData() {
+ 
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    GetBoard(i)->TransferWaves(GetBoard(i)->GetNumberOfChannels()*GetBoard(i)->GetNumberOfChips()); 
+	
+    for (int k=0; k<GetBoard(i)->GetNumberOfChips(); k++) {
+	  TriggerCell[i][k] = GetBoard(i)->GetTriggerCell(k);
+	  for (int j=0; j<GetBoard(i)->GetNumberOfChannels(); j++) {
+        GetBoard(i)->GetWave(k, j, WaveForm[i][k][j], true, TriggerCell[i][k]);
+      }
+    }
+  }
+}
+
+// Read calibration data
+bool DAQReadout::ReadCalibration() {
+
+  static char Buffer[MAX_COM_SIZE];
+  int Serial, Calib;
+  float Temp, Freq;
+
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    if (GetBoard(i)->GetDRSType() == 4) {
+      if (ACalib[i] == false) {
+	    // Check calibration info file if EEPROM data on DRS board still valild
+		FILE *CalibInfo = fopen(CalibInfoFilename, "r");
+		if (CalibInfo == NULL) return false;
+		fgets(Buffer, sizeof(Buffer), CalibInfo); // skip first two lines
+		fgets(Buffer, sizeof(Buffer), CalibInfo);
+
+		while (fgets(Buffer, sizeof(Buffer), CalibInfo) != NULL) {
+		  if (sscanf(Buffer, "%d %d %f %*d %f", &Serial, &Calib, &Temp, &Freq) != 4) {
+			fclose(CalibInfo);
+			return false;
+		  }
+
+		  if (Serial==GetBoard(i)->GetBoardSerialNumber() && int(Freq*100)==int(DRSFreq[i]*100) && Calib==1) {
+			ACalib[i] = true;
+			ACalibTemp[i] = Temp;
+			break;
+		  }
+		}
+		fclose(CalibInfo);
+	  }
+    }
+    else {
+      if (!ACalib[i]) {
+        GetBoard(i)->SetCalibrationDirectory(fCalibDataPath);
+        PrintMessage("Reading response calibration file for board %d from: \"%s\"\n", i, fCalibDataPath);
+        for (int Chip=0; Chip<GetBoard(i)->GetNumberOfChips(); Chip++) {
+          if (GetBoard(i)->GetResponseCalibration()->ReadCalibration(Chip) == false) return false;
+        }
+        ACalib[i] = true;
+      }
+    }
+	if (fabs(ACalibTemp[i]-GetBoard(i)->GetTemperature())>2) PrintMessage("Warning: Large difference to calibration temperature for board %d\n", i);
+  } // Loop over boards
+  return true;
+}
+
+// Stop DAQ
+void DAQReadout::StopRun() {
+
+  if(daq_state != active) PrintMessage("DAQ is not active.\n");
+  else {
+    Stop = true;
+    PrintMessage("DAQ will stop.\n");
+  }
+}
+
+// Set DOMINO mode 
+void DAQReadout::SetDOMINOMode(int mode) {
+ 
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    GetBoard(i)->SetDominoMode(mode==1 ? 1:0);
+    PrintMessage("Domino mode of board %d switched to %s.\n",i,mode==1 ? "continuous":"single shot");
+  } 
+}
+
+// Set DOMINO readout mode 
+void DAQReadout::SetDOMINOReadMode(int mode) {
+
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    GetBoard(i)->SetReadoutMode(mode);
+    PrintMessage("Start readout of board %d from %s.\n",i,mode==0 ? "first bin":"stop position");
+  } 
+}
+
+// Set DOMINO wave mode 
+void DAQReadout::SetDOMINOWaveMode(int mode) {
+
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    GetBoard(i)->SetDominoActive(mode);
+    PrintMessage("Domino wave of board %d is %s during readout\n",i,mode==1 ? "running":"stopped");
+  } 
+}
+
+// Enable hardware trigger of all boards 
+void DAQReadout::HWTrigger(int mode) {
+
+  if (NumBoards) 
+    for (int i=FirstBoard; i<=LastBoard; i++) {
+      GetBoard(i)->EnableTrigger(mode, 0);
+      PrintMessage("Hardware trigger of board %d %s\n",i,mode==1 ? "enabled":"disabled");
+    }
+  else PrintMessage("No DRS boards available\n");
+}
+
+// Set DRS sampling frequency
+void DAQReadout::SetDRSFrequency(double freq, bool Regulation) {
+
+  PrintMessage("Setting frequency %s regulation\n",Regulation ? "with":"without");
+  
+  for (int i=FirstBoard; i<=LastBoard; i++) { 
+    if ((Regulation ? GetBoard(i)->RegulateFrequency(freq) :
+    GetBoard(i)->SetFrequency(freq, true)) == 0) {
+      PrintMessage("Warning: Board %d has not reached the requested value\n",i);
+    }
+
+    double f;
+    GetBoard(i)->ReadFrequency(0, &f); 
+    DRSFreq[i] = f;
+    ACalib[i] = false;
+    TCalib[i] = false;
+    PrintMessage("Board %d is running at %1.3lf GHz\n",i,f);
+  }
+}
+
+// Check if DRS is sampling
+bool DAQReadout::IsDRSBusy() {
+
+  for (int i=FirstBoard; i<=LastBoard; i++)     
+    if ((GetBoard(i))->IsBusy()) return true;
+  return false;
+}
+
+// Check if DRS frequency is set
+bool DAQReadout::IsDRSFreqSet() {
+
+  for (int i=FirstBoard; i<=LastBoard; i++)
+    if (DRSFreq[i] == 0) {
+      PrintMessage("Sampling frequency of DRS board %d not set\n", i);
+      return false;
+    }
+  return true;
+}
+
+// Open new raw data file (if RunNumber == -1, data will be written to /dev/null)
+bool DAQReadout::OpenRawFile() {
+
+  time_t rawtime;
+  struct tm *timeinfo;
+  char RunDate[MAX_COM_SIZE], Buffer[MAX_COM_SIZE];
+  
+  // Write run date to status structure (if after 13:00, use next day)
+  time(&rawtime);
+  timeinfo = gmtime(&rawtime);
+  if(timeinfo->tm_hour>=13) rawtime += 12*60*60;
+  timeinfo = gmtime(&rawtime);
+  snprintf(RunDate,sizeof(RunDate),"%d%02d%02d",timeinfo->tm_year+1900,timeinfo->tm_mon + 1,timeinfo->tm_mday);
+
+  // Create direcory if not existing (ignore error if already existing) and change to it
+  if (RunNumber != -1) {
+	snprintf(Buffer, sizeof(Buffer), "%s/%s", fRawDataPath, RunDate);
+	if(mkdir(Buffer, S_IRWXU|S_IRWXG)==-1 && errno!=EEXIST) {
+	  PrintMessage("\rError: Could not create direcory \"%s\" (%s)\n", Buffer, strerror(errno));
+	  return false;
+	}
+  }
+  
+  // Generate filename
+  if (RunNumber == -1) snprintf(FileName, sizeof(FileName), "/dev/null");
+  else snprintf(FileName, sizeof(FileName),"%s/%s/%s_D1_%.8u.%.3u_%c_%s.raw", fRawDataPath, RunDate,
+    RunDate, RunNumber, FileNumber, toupper(daq_runtype_str[daq_runtype][0]), RHeader->Description);
+ 
+  //  Open file with rwx right for owner and group, never overwrite file
+  Rawfile = open(FileName,O_WRONLY|O_CREAT|(RunNumber==-1?0:O_EXCL), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
+  if(Rawfile==-1) {
+    PrintMessage("\rError: Could not open file \"%s\" (%s)\n", FileName, strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+// Write run header and board structures (revision number is zero for svn modified working copy)
+bool DAQReadout::WriteRunHeader() {
+
+  struct timeval Time;
+
+  RHeader->MagicNum = MAGICNUM_OPEN;
+  RHeader->DataFormat = DATA_FORMAT;
+  RHeader->SoftwareRevision = atoi(REVISION) * (strchr(REVISION, 'M')==NULL ? 1:-1);
+
+  RHeader->RunHeaderSize = sizeof(RunHeader);
+  RHeader->EventHeaderSize = sizeof(EventHeader);
+  RHeader->BoardStructureSize = sizeof(BoardStructure);
+
+  RHeader->Identification = IDENTIFICATION;
+  RHeader->Type = daq_runtype;
+  RHeader->RunNumber  = RunNumber;
+  RHeader->FileNumber = FileNumber;
+
+  gettimeofday(&Time, NULL);
+  RHeader->StartSecond = Time.tv_sec;
+  RHeader->StartMicrosecond = Time.tv_usec;
+  
+  RHeader->NBoards   = NumBoards==0 && daq_runtype==test ? 1 : (LastBoard - FirstBoard) + 1;  
+  RHeader->NChips    = NumBoards==0 ? kNumberOfChipsMax : GetBoard(0)->GetNumberOfChips();
+  RHeader->NChannels = NumBoards==0 ? kNumberOfChannelsMax : GetBoard(0)->GetNumberOfChannels();
+  RHeader->NBytes    = sizeof(short);
+
+  RHeader->Offset  = fFirstSample;
+  RHeader->Samples = fSamples;
+
+  if(write(Rawfile, RHeader, sizeof(RunHeader)) != sizeof(RunHeader)) {
+    PrintMessage("Error: Could not write run header, terminating run (%s)\n", strerror(errno));
+    return false;
+  }
+  
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    BStruct[i].SerialNo    = GetBoard(i)->GetBoardSerialNumber();	  
+    BStruct[i].BoardTemp   = GetBoard(i)->GetTemperature();
+    BStruct[i].NomFreq     = DRSFreq[i];
+    BStruct[i].ScaleFactor = GetBoard(i)->GetPrecision();
+  }
+
+  // In case no boards are available, dummy data is written for one board structure   
+  if (NumBoards == 0) {
+    BStruct[0].NomFreq     = 1;
+    BStruct[0].ScaleFactor = 0.1;
+  }    
+
+  if(write(Rawfile, &BStruct[FirstBoard], sizeof(BoardStructure)*(LastBoard-FirstBoard+1+(NumBoards==0))) != (ssize_t) sizeof(BoardStructure)*(LastBoard-FirstBoard+1+(NumBoards==0))) {
+    PrintMessage("Error: Could not write (all) board structures, terminating run (%s)\n", strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+// Update run header before closing file
+bool DAQReadout::UpdateRunHeader(unsigned int Events, bool Error) {
+
+  struct timeval Time;
+  
+  RHeader->MagicNum = Error==false ? MAGICNUM_CLOSED:MAGICNUM_ERROR;
+  RHeader->Events   = Events;
+
+  gettimeofday(&Time, NULL);
+  RHeader->EndSecond = Time.tv_sec;
+  RHeader->EndMicrosecond = Time.tv_usec;
+
+  if(lseek(Rawfile,0,SEEK_SET)==-1) {
+    PrintMessage("Error: Could not rewind file to write updated run header, terminating run (%s)\n", strerror(errno));
+    return false;
+  }
+
+  if(write(Rawfile, RHeader, sizeof(RunHeader)) != sizeof(RunHeader)) {
+    PrintMessage("Error: Could not write updated run header, terminating run (%s)\n", strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+// Write event
+bool DAQReadout::WriteEvent() {
+
+  // Event header
+  struct timeval Time;
+
+  gettimeofday(&Time, NULL);
+  
+  EHeader->EventNumber = NumEvents;
+  EHeader->TriggerType = daq_runtype==data ? 0 : 1;
+  EHeader->Second = Time.tv_sec;
+  EHeader->Microsecond = Time.tv_usec;
+  EHeader->EventSize = sizeof(short)*RHeader->NBoards*RHeader->NChips*RHeader->NChannels*RHeader->Samples +
+    	    	       sizeof(int)*RHeader->NBoards*RHeader->NChips;
+
+  if(write(Rawfile, EHeader, sizeof(EventHeader)) != sizeof(EventHeader)) {
+    PrintMessage("Error: Could not write event header, terminating run (%s)\n", strerror(errno));
+    return false;
+  }
+
+  unsigned int Start, Count = 0;
+  ssize_t WriteResult, Size = 0;
+  struct iovec DataPart[IOV_MAX];
+
+  // Write trigger cells
+  for (int i=FirstBoard; (i<=LastBoard + (NumBoards==0)); i++) {
+    for (unsigned int k=0; k<RHeader->NChips; k++) {
+	  if ((WriteResult=write(Rawfile, &TriggerCell[i][k], sizeof(int))) != sizeof(int)) {
+		if (WriteResult == -1) PrintMessage("Error: Could not write trigger cells, terminating run (%s)\n", strerror(errno));
+		else PrintMessage("Error: Could only write %u out of %u bytes of event data, terminating run\n", WriteResult, sizeof(int));
+	    return false;
+	  }
+	}
+  }
+  
+  // ADC data (two chucks per channel if wrap around of pipeline occurred, therefore
+  // IOV_MAX>=2 is checked at startup
+  for (int i=FirstBoard; (i<=LastBoard + (NumBoards==0)); i++) {
+    for (unsigned int k=0; k<RHeader->NChips; k++) {
+      // Start bin
+      if (GetBoard(i)->GetDRSType() == 4) Start = 0;
+      else Start = (TriggerCell[i][k]-fFirstSample+kNumberOfBins) % kNumberOfBins;
+      for (unsigned int l=0; l<RHeader->NChannels; l++) {
+	DataPart[Count].iov_base = &WaveForm[i][k][l][Start];
+	DataPart[Count++].iov_len = (Start+fSamples<kNumberOfBins ? fSamples:(kNumberOfBins-Start)) * sizeof(short);
+	Size += DataPart[Count-1].iov_len;
+	// In case second part of waveform still missing, write now
+	if(DataPart[Count-1].iov_len < fSamples * sizeof(short)) {
+    	  DataPart[Count].iov_base = &WaveForm[i][k][l][0];
+	  DataPart[Count++].iov_len = (fSamples-(kNumberOfBins-Start)) * sizeof(short);
+    	  Size += DataPart[Count-1].iov_len;
+	}
+
+	// Write to disk if either maximum size of DataPart[] array or last loop interation is reached 
+	// Possibly 2 chunks are entered in array in the previous lines of code, therefore IOV_MAX-1
+	if (Count>=IOV_MAX-1 || (l==(RHeader->NChannels-1) && k==(RHeader->NChips-1) && i==(LastBoard+(NumBoards==0)))) {
+	  if ((WriteResult=writev(Rawfile, DataPart, Count)) != (int) Size) {
+            if (WriteResult==-1) PrintMessage("Error: Could not write event data, terminating run (%s)\n", strerror(errno));
+            else PrintMessage("Error: Could only write %u out of %u bytes of event data, terminating run\n", WriteResult,Count*DataPart[0].iov_len);
+	    return false;
+	  }
+	  Count = 0;   Size = 0;
+	}
+      } // Channels
+    } // Chips
+  } // Boards
+  
+  return true;
+}
+
+// Print usage text for command
+void DAQReadout::PrintUsage() {
+  PrintMessage("Usage: %s %s\n", CommandList[CmdNumber].Name, CommandList[CmdNumber].Parameters);
+}
+
+// Print progress (used in DRS class)
+void DAQReadout::Progress(int Progress) {
+  PrintMessage("\rProgress: %d%%              ", Progress);
+  fflush(stdout);
+};
+
+// Print message to console only
+void DAQReadout::PrintMessage(const char *Format, ...) {
+
+  static char Error[] = "vasprintf() failed in PrintMessage()";
+  char *Text;
+
+  // Evaluate arguments
+  va_list ArgumentPointer;
+  va_start(ArgumentPointer, Format);
+  if (vasprintf(&Text, Format, ArgumentPointer) == -1) Text = Error;
+  va_end(ArgumentPointer);
+  
+  // Print to console
+  if(strlen(Text)>0 && Text[strlen(Text)-1]=='\n') printf("\r%s%s", Text, Prompt); // New prompt
+  else printf("%s", Text);
+  fflush(stdout);
+
+  // Send to DIM text service
+  ConsoleOut->updateService(Text); 
+
+  // Free old text
+  if (ConsoleText != Error) free(ConsoleText);
+  ConsoleText = Text; 
+}
+
+// DIM command handler (must be non-blocking, otherwise a DIM rpc would dead-lock)
+void DAQReadout::commandHandler() {
+
+  // Ignore empty or illegal strings
+  if (getCommand()->getSize() == 0 ||
+  	  *((char *) getCommand()->getData()+getCommand()->getSize()-1) != '\0' ||
+	  strlen(getCommand()->getString()) == 0) return;
+
+  // Copy command to new buffer (will be freed by global Execute() function)
+  char *Command;
+  if (asprintf(&Command, "%s", getCommand()->getString()) == -1) {
+	PrintMessage("asprintf() failed in DRSReadout::commandHandler() (%s)\n", strerror(errno));
+	return;
+  }
+
+  // Create detached command handling thread
+  pthread_t Thread;
+  int Code;
+  if ((Code = pthread_create(&Thread, NULL, (void * (*)(void *)) ::Execute,(void *) Command)) != 0) {
+    PrintMessage("pthread_create() failed in DRSReadout::commandHandler() (%s)\n", strerror(Code));
+  }
+  else if ((Code = pthread_detach(Thread)) != 0) {
+	PrintMessage("pthread_detach() failed in DRSReadout::commandHandler() (%s)\n", strerror(Code));
+  }  
+}
+
+/********************************************************************\
+
+  DAQ Thread
+
+  This thread takes data until the requested number of events is reached,
+  until no more disk space is available or until data taking is stopped.
+  No mutex mechanism is used since variables will never be written 
+  simultaneoously by this and the main thread.
+  
+\********************************************************************/
+
+void DAQReadout::DAQ() {
+
+  struct timeval StartTime, StopTime;
+  unsigned int EventsInFile;
+  unsigned long long RunSize = 0;
+  bool WriteError = false;
+  time_t LastDIMUpdate = 0;
+  off_t FileSize;
+  int DIMSize;
+  float RunSizeMB=0, FileSizeMB=0;
+
+  // Initialize run
+  NumEvents = 0;
+  FileNumber = 0;
+  DimClient::sendCommandNB("Feedback/Command", "clear");
+
+  DimService RunSizeService(SERVER_NAME"/RunSizeMB", RunSizeMB);
+  DimService FileSizeService(SERVER_NAME"/FileSizeMB", FileSizeMB);
+  DimService EventNumService(SERVER_NAME"/EventNumber", NumEvents);
+  DimService FilenameService(SERVER_NAME"/FileName", FileName);
+    
+  gettimeofday(&StartTime, NULL);
+  PrintMessage("\rStarting run #%d (%s) with %u event(s)\n", RunNumber, daq_runtype_str[daq_runtype], NumEventsRequested);
+
+  do {
+    // Check if enough disk space is left
+    if (CheckDisk(fRawDataPath) <= fMinDiskSpaceMB+fMaxFileSizeMB) {	  
+      PrintMessage("\rError: Disk space after next file (max. %d MByte) below %d MByte\n", fMaxFileSizeMB, fMinDiskSpaceMB);
+      break;
+    }
+
+    // Init run header, open raw file, write run header
+    if (!OpenRawFile()) break;
+    PrintMessage("\rData file \"%s\" opened.\n", FileName);
+    EventsInFile = 0;
+    FileSize = 0;
+	FilenameService.updateService();
+
+    WriteError |= !WriteRunHeader();
+    StartDRS(); 
+
+	// Copy run header and board structures to buffer for DIM event service
+	memcpy(DIMEventData, RHeader, sizeof(RunHeader));
+	((RunHeader *) DIMEventData)->Events = 1; // always contains 1 event
+    memcpy(DIMEventData+sizeof(RunHeader), &BStruct[FirstBoard], sizeof(BoardStructure)*(LastBoard-FirstBoard+1+(NumBoards==0)));
+
+	// Service size for DIM
+	DIMSize = sizeof(RunHeader) + sizeof(EventHeader) +
+  			  sizeof(BoardStructure)*RHeader->NBoards +
+			  sizeof(int)*RHeader->NBoards*RHeader->NChips +
+			  sizeof(short)*RHeader->NBoards*RHeader->NChips*RHeader->NChannels*RHeader->Samples;
+
+    // Take data until finished, stopped or file too large   
+    while ((NumEvents<NumEventsRequested || NumEventsRequested==0) &&
+      !Stop && FileSize/1024/1024<fMaxFileSizeMB && !WriteError) {
+
+      if (daq_runtype == data) while (IsDRSBusy());  // Wait for hardware trigger (if DAQ stopped, DRS will not be busy anymore)
+      else if (daq_runtype == pedestal) StopDRS();   // ..or for software trigger
+
+      // Read event data and restart (reduces dead-time because waiting for next trigger while writing)
+	  ReadCalibratedDRSData();
+	  StartDRS();
+
+      // Write event to disk and update file size
+      EventsInFile++;
+	  NumEvents++;	
+      WriteError |= !WriteEvent();
+
+      if((FileSize = lseek(Rawfile, 0, SEEK_CUR)) == -1) {
+    	PrintMessage("Error: Could not determine file size, terminating run (%s)\n", strerror(errno));
+		WriteError = true;
+      }
+
+	  // Check for LED trigger
+	  if (WaveForm[fLedTrigBoard][fLedTrigChip][fLedTrigChannel][fLedTrigSample] > fLedTrigThreshold) {
+		std::stringstream Cmd;
+		float Integral;
+
+		// Calculate feedback signal
+		for (int i=FirstBoard; i<=LastBoard; i++) {
+		  for (unsigned int j=0; j<RHeader->NChips; j++) {
+			for (unsigned int k=0; k<RHeader->NChannels; k++) {
+			  Integral = 0.0;
+			  for (int q=-fIntHalfWidth; q<=(int) fIntHalfWidth; q++) { 
+        		Integral += (WaveForm[i][j][k][fLedSignalSample+q] - WaveForm[i][j][k][fLedBaselineSample+q])*GetBoard(i)->GetPrecision();
+			  }
+        	  Integral /= 2*fIntHalfWidth+1;
+			  Cmd << Integral << " ";
+    		}
+		  }
+		}
+
+		// Send data to feedback
+		DimClient::sendCommandNB("Feedback/Command", (char *) ("data "+Cmd.str()).c_str());
+	  }
+
+      // Update DIM services (update rate is limited)
+	  if (time(NULL) - LastDIMUpdate < MinDelay) continue;
+	  LastDIMUpdate = time(NULL);
+	  
+	  RunSizeMB = (RunSize+FileSize)/1024.0/1024.0;
+	  RunSizeService.updateService();
+	  FileSizeMB = FileSize/1024.0/1024.0;
+	  FileSizeService.updateService();
+	  EventNumService.updateService();
+	  
+	  // Copy new event header
+	  char *Pnt = DIMEventData+sizeof(RunHeader)+RHeader->NBoards*sizeof(BoardStructure);
+	  memcpy(Pnt, EHeader, sizeof(EventHeader));
+	  Pnt += sizeof(EventHeader); 
+
+	  // Copy new trigger cells
+	  for (int i=FirstBoard; (i<=LastBoard + (NumBoards==0)); i++) {
+    	for (unsigned int k=0; k<RHeader->NChips; k++) {
+		   *((int *) Pnt) = TriggerCell[i][k];
+		   Pnt += sizeof(int);
+		}
+	  }
+
+	  // Copy event data
+	  unsigned int Start;
+	  for (int i=FirstBoard; (i<=LastBoard + (NumBoards==0)); i++) {
+		for (unsigned int k=0; k<RHeader->NChips; k++) {
+		  // Start bin
+		  if (GetBoard(i)->GetDRSType() == 4) Start = 0;
+		  else Start = (TriggerCell[i][k]-fFirstSample+kNumberOfBins) % kNumberOfBins;
+
+		  for (unsigned int l=0; l<RHeader->NChannels; l++) {
+			memcpy(Pnt, &WaveForm[i][k][l][Start], (Start+fSamples<kNumberOfBins ? fSamples:(kNumberOfBins-Start)) * sizeof(short));
+			Pnt += (Start+fSamples<kNumberOfBins ? fSamples:(kNumberOfBins-Start)) * sizeof(short);
+			// In case second part of waveform still missing, write now
+			if((Start+fSamples<kNumberOfBins ? fSamples:(kNumberOfBins-Start)) * sizeof(short) < fSamples * sizeof(short)) {
+			  memcpy(Pnt, &WaveForm[i][k][l][0], (fSamples-(kNumberOfBins-Start)) * sizeof(short));
+			}
+		  }
+		}
+	  }
+	  EventService->updateService((void *) DIMEventData, DIMSize);
+    }
+
+    // Write updated run header, close file
+    RunSize += FileSize;
+    WriteError |= !UpdateRunHeader(EventsInFile, WriteError);
+    if(close(Rawfile) == -1) PrintMessage("Error: Could not close data file (%s)\n", strerror(errno));
+    else PrintMessage("Data file closed (size %lu MByte).\n", FileSize/1024/1024);
+
+    FileNumber += 1; 
+  } while((NumEvents<NumEventsRequested || NumEventsRequested==0) && !Stop && !WriteError);
+
+  StopDRS();
+  
+  // Print run summary to screen
+  if(!WriteError) PrintMessage("\rRun #%d (%s) %s, %d events\n", RunNumber, daq_runtype_str[daq_runtype], (NumEvents == NumEventsRequested) ? "completed":"stopped", NumEvents);
+  else PrintMessage("\rRun #%d (%s) aborted due to error after %d events\n", RunNumber, daq_runtype_str[daq_runtype], NumEvents);
+
+  // Print run statistics
+  if (NumEvents>0 && !WriteError) {
+    gettimeofday(&StopTime, NULL);
+    float RunTime = StopTime.tv_sec-StartTime.tv_sec + (StopTime.tv_usec-StartTime.tv_usec)*1e-6;
+    PrintMessage("Time for run %.2f seconds, trigger rate %.2f Hz.\n", RunTime, NumEvents/RunTime);
+    PrintMessage("Run size %llu MByte, data rate %.1f MByte/s.\n", RunSize/1024/1024, RunSize/1024.0/1024/RunTime);
+  }
+  
+  RunNumber = -1;
+  RunNumService->updateService();
+
+  daq_state = stopped;
+}
+
+// ---------------------------------------
+// *****  Various utility functions  *****
+// ---------------------------------------
+
+// Check if two strings match (min 1 character must match)
+bool Match(const char *str, const char *cmd) {
+  return strncasecmp(str,cmd,strlen(str)==0 ? 1:strlen(str)) ? false:true;
+}
+
+// Return current available storage space in given directory
+int CheckDisk(const char *Directory) {
+  struct statfs FileSystemStats;
+
+  statfs(Directory, &FileSystemStats);
+  return FileSystemStats.f_bavail / 1024 * (FileSystemStats.f_bsize / 1024);
+}
+
+// Parse command line for white space and double-quote separated tokens 
+int ParseInput(char* Command, const char *Param[]) {
+  int Count=0;
+   
+  while(Count<MAX_NUM_TOKEN) {
+    while (isspace(*Command)) Command++; // Ignore initial white spaces
+    if(*Command=='\0') break;
+    if (*Command == '\"') {
+      Param[Count] = ++Command;
+      while(*Command!='\"' && *Command!='\0') Command++;
+    }
+    else {
+      Param[Count] = Command;
+      while(!isspace(*Command) && *Command!='\0') Command++;
+    }
+    if(*Command != '\0') *Command++ = '\0';
+    Count++;
+  }
+  return Count;
+}
+
+// Thread entry routines (non-static class methods cannot be directly executed as thread)
+void DAQ(DAQReadout *m) {
+
+ m->DAQ();
+}
+
+// Stub to call Execute() metho of class and free command memory
+void Execute(char *Command) {
+
+  ThisClass->Lock();
+  ThisClass->Execute(Command);
+  free(Command);
+  ThisClass->Unlock();
+}
Index: /fact/drsdaq/DAQReadout.h
===================================================================
--- /fact/drsdaq/DAQReadout.h	(revision 9833)
+++ /fact/drsdaq/DAQReadout.h	(revision 9833)
@@ -0,0 +1,144 @@
+#ifndef DAQREADOUT_H_SEEN
+#define DAQREADOUT_H_SEEN
+
+#define SERVER_NAME "drsdaq"       // Name to use in DIM
+#include "Evidence.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/vfs.h>
+#include <sys/time.h>
+
+#include "RawDataCTX.h"
+#include "DRS.h"
+
+#define MAX_PATH 256		// also used for filename length
+#define MAX_COM_SIZE 10000
+#define MAX_NUM_TOKEN 10
+
+enum state_enum {active, stopped};
+enum runtype_enum {data, pedestal, reserved, test};
+
+class DAQReadout : public DRS, public DRSCallback, public EvidenceServer {
+
+	time_t StartTime;
+	pid_t MainThread;
+	DimService *EventService;
+	DimService *RunNumService;
+	int MinDelay;
+    unsigned int CmdNumber;
+	DimCommand *Command;
+	DimService *ConsoleOut;
+	char *ConsoleText;
+    int Rawfile;
+
+    void PrintUsage();
+	void commandHandler();
+
+    // Configuration data
+    const char *fCalibDataPath;
+    const char *fRawDataPath;
+    int fMinDiskSpaceMB;   // Minimum required disk space in MBytes
+    int fMaxFileSizeMB;    // Maximum File size in Bytes 
+    int fFirstSample;
+    unsigned int fSamples;
+    double fDefaultFrequency;
+
+    int fLedTrigBoard;
+    int fLedTrigChip;
+    int fLedTrigChannel;
+    int fLedTrigSample;
+    float fLedTrigThreshold;
+    int fLedSignalSample;
+    int fLedBaselineSample;
+    unsigned int fIntHalfWidth;
+
+    state_enum   daq_state;
+    runtype_enum daq_runtype;
+    bool Stop;	      	      	// Set to true to stop run
+    int NumEvents;		// Number of event taken            
+    int NumEventsRequested;	// Number of events requested
+    int RunNumber; 
+    unsigned int FileNumber; 
+    char FileName[MAX_PATH];
+    char CalibInfoFilename[MAX_PATH];
+	
+  public:
+    RunHeader*   RHeader;
+    EventHeader* EHeader;
+    BoardStructure *BStruct;
+    
+    short (*WaveForm)[kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins];
+    int (*TriggerCell)[kNumberOfChipsMax];
+	char *DIMEventData;
+    
+    char Prompt[MAX_COM_SIZE];
+
+    int NParam;       	      	// Number of command parameters
+    const char *Param[MAX_NUM_TOKEN]; // Pointers to parameters
+    int NumBoards;
+    int FirstBoard;
+    int LastBoard;
+    float *DRSFreq;   	      	// DRS sampling frequency [GHz]
+    bool *ACalib;
+    double *ACalibTemp;
+    bool *TCalib;
+
+    DAQReadout();
+    ~DAQReadout();
+
+    void cmd_exit();	   void cmd_help();
+    void cmd_board();	   void cmd_status();
+    void cmd_led(); 	   void cmd_freq();
+    void cmd_acalib();	   void cmd_serial();
+    void cmd_trigger();	   void cmd_centre();
+	void cmd_refclk();
+    void cmd_wmode();	   void cmd_rmode();
+    void cmd_dmode();	   void cmd_read();
+    void cmd_stop();	   void cmd_chiptest();
+    void cmd_eepromtest(); void cmd_tcalib();
+    void cmd_regtest();	   void cmd_ramtest();
+	void cmd_take();	   void cmd_update();
+    void cmd_config();	   void cmd_events();
+    void cmd_disk();	   void cmd_uptime();
+      
+    void Execute(char*);  
+    void StartDRS();
+    void StopDRS();
+    void StopRun();
+    bool IsDRSBusy();
+    bool IsDRSFreqSet();
+    void SetDRSFrequency(double, bool);
+    void SetDOMINOMode(int);
+    void SetDOMINOReadMode(int);
+    void SetDOMINOWaveMode(int);
+    void SetDelayedStart(int);
+    void HWTrigger(int); 
+    bool ReadCalibration();
+    void ReadCalibratedDRSData();
+    void PrintMessage(const char*, ...);
+    bool OpenRawFile();
+    bool WriteRunHeader();
+    bool UpdateRunHeader(unsigned int, bool);
+    bool WriteEvent();
+	void DAQ();
+    
+    void Progress(int);
+};
+
+void DAQ(DAQReadout *);
+void Execute(char *);
+
+bool Match(const char*, const char*);
+int ParseInput(char*, const char *Param[]);
+int CheckDisk(const char*);
+
+#endif
Index: /fact/drsdaq/DRS/DRS.cc
===================================================================
--- /fact/drsdaq/DRS/DRS.cc	(revision 9833)
+++ /fact/drsdaq/DRS/DRS.cc	(revision 9833)
@@ -0,0 +1,7004 @@
+/********************************************************************
+
+  Name:         DRS.cpp
+  Created by:   Stefan Ritt, Matthias Schneebeli
+
+  Contents:     Library functions for DRS mezzanine and USB boards
+
+  $Id: DRS.cpp 14453 2009-10-22 10:51:29Z ritt $
+
+\********************************************************************/
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <assert.h>
+#include <algorithm>
+#include <sys/stat.h>
+#include "strlcpy.h"
+
+#ifdef CT_VME
+  #define MEM_SEGMENT 150000 // Size of the memory segment
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(disable:4996)
+#   include <windows.h>
+#   include <direct.h>
+#else
+#   include <unistd.h>
+#   include <sys/time.h>
+inline void Sleep(useconds_t x)
+{
+   usleep(x * 1000);
+}
+#endif
+
+#ifdef _MSC_VER
+#include <conio.h>
+#define drs_kbhit() kbhit()
+#else
+#include <sys/ioctl.h>
+int drs_kbhit()
+{
+   int n;
+
+   ioctl(0, FIONREAD, &n);
+   return (n > 0);
+}
+static inline int getch()
+{
+   return getchar();
+}
+#endif
+
+#include <DRS.h>
+
+#ifdef _MSC_VER
+extern "C" {
+#endif
+
+#include <mxml.h>
+
+#ifdef _MSC_VER
+}
+#endif
+
+/*---- minimal FPGA firmvare version required for this library -----*/
+const int REQUIRED_FIRMWARE_VERSION_DRS2 = 5268;
+const int REQUIRED_FIRMWARE_VERSION_DRS3 = 6981;
+const int REQUIRED_FIRMWARE_VERSION_DRS4 = 13191;
+
+/*---- calibration methods to be stored in EEPROMs -----------------*/
+
+#define VCALIB_METHOD  1
+#define TCALIB_METHOD  1
+
+/*---- VME addresses -----------------------------------------------*/
+#if defined(HAVE_VME) || defined(CT_VME)
+
+/* assuming following DIP Switch settings:
+
+   SW1-1: 1 (off)       use geographical addressing (1=left, 21=right)
+   SW1-2: 1 (off)       \
+   SW1-3: 1 (off)        >  VME_WINSIZE = 8MB, subwindow = 1MB
+   SW1-4: 0 (on)        /
+   SW1-5: 0 (on)        reserverd
+   SW1-6: 0 (on)        reserverd
+   SW1-7: 0 (on)        reserverd
+   SW1-8: 0 (on)       \
+                        |
+   SW2-1: 0 (on)        |
+   SW2-2: 0 (on)        |
+   SW2-3: 0 (on)        |
+   SW2-4: 0 (on)        > VME_ADDR_OFFSET = 0
+   SW2-5: 0 (on)        |
+   SW2-6: 0 (on)        |
+   SW2-7: 0 (on)        |
+   SW2-8: 0 (on)       /
+
+   which gives
+     VME base address = SlotNo * VME_WINSIZE + VME_ADDR_OFFSET
+                      = SlotNo * 0x80'0000
+*/
+#define GEVPC_BASE_ADDR           0x00000000
+#define GEVPC_WINSIZE               0x800000
+#define GEVPC_USER_FPGA   (GEVPC_WINSIZE*2/8)
+#define PMC1_OFFSET                  0x00000
+#define PMC2_OFFSET                  0x80000
+#define PMC_CTRL_OFFSET              0x00000    /* all registers 32 bit */
+#define PMC_STATUS_OFFSET            0x10000
+#define PMC_FIFO_OFFSET              0x20000
+#define PMC_RAM_OFFSET               0x40000
+#endif                          // HAVE_VME
+/*---- USB addresses -----------------------------------------------*/
+#define USB_TIMEOUT                     1000    // one second
+#ifdef HAVE_USB
+#define USB_CTRL_OFFSET                 0x00    /* all registers 32 bit */
+#define USB_STATUS_OFFSET               0x40
+#define USB_RAM_OFFSET                  0x80
+#define USB_CMD_IDENT                      0    // Query identification
+#define USB_CMD_ADDR                       1    // Address cycle
+#define USB_CMD_READ                       2    // "VME" read <addr><size>
+#define USB_CMD_WRITE                      3    // "VME" write <addr><size>
+#define USB_CMD_READ12                     4    // 12-bit read <LSB><MSB>
+#define USB_CMD_WRITE12                    5    // 12-bit write <LSB><MSB>
+
+#define USB2_CMD_READ                      1
+#define USB2_CMD_WRITE                     2
+#define USB2_CTRL_OFFSET             0x00000    /* all registers 32 bit */
+#define USB2_STATUS_OFFSET           0x10000
+#define USB2_FIFO_OFFSET             0x20000
+#define USB2_RAM_OFFSET              0x40000
+#endif                          // HAVE_USB
+
+/*------------------------------------------------------------------*/
+
+using namespace std;
+
+#ifdef HAVE_USB
+#define USB2_BUFFER_SIZE (1024*1024+10)
+unsigned char static *usb2_buffer = NULL;
+#endif
+
+/*------------------------------------------------------------------*/
+
+DRS::DRS()
+:  fNumberOfBoards(0)
+#ifdef HAVE_VME
+    , fVmeInterface(0)
+#endif
+{
+#ifdef HAVE_USB
+   MUSB_INTERFACE *usb_interface;
+#endif
+
+  int index = 0, i = 0;
+  memset(fError, 0, sizeof(fError));
+
+#if defined(HAVE_VME) || defined(CT_VME)
+   unsigned short type, fw, magic, serial, temperature;
+
+#ifdef HAVE_VME
+   mvme_addr_t addr;
+
+   if (mvme_open(&fVmeInterface, 0) == MVME_SUCCESS) {
+      mvme_set_am(fVmeInterface, MVME_AM_A32);
+      mvme_set_dmode(fVmeInterface, MVME_DMODE_D16);      
+#endif
+#ifdef CT_VME 
+  unsigned int addr;
+    
+  if (OpenVME() == VME_SUCCESS) {    
+    if (OpenCMEM() != VME_SUCCESS) {
+      printf("Error with OpenCMEM()\n");
+      return;
+    }
+    // Set master mapping input information
+    MasterMap.window_size	= GEVPC_WINSIZE;
+    MasterMap.address_modifier	= VME_A32;  
+    MasterMap.options		= 0;
+#endif
+      /* check all VME slave slots */
+      for (index = 2; index <= 9; index++) {      
+#ifdef CT_VME 
+	MasterMap.vmebus_address	= GEVPC_BASE_ADDR + index * GEVPC_WINSIZE; // VME board base address
+	if (MasterMapVME(&MasterMapping[index]) != VME_SUCCESS) continue;
+#endif
+      
+        /* check PMC1 and PMC2 */
+	for (int pmc=0; pmc<2; pmc++) {
+
+#ifdef HAVE_VME
+         addr = GEVPC_BASE_ADDR + index * GEVPC_WINSIZE;        // VME board base address
+         addr += GEVPC_USER_FPGA;       // UsrFPGA base address
+         addr += (pmc == 0) ? PMC1_OFFSET : PMC2_OFFSET;   // offset
+
+         mvme_set_dmode(fVmeInterface, MVME_DMODE_D16);
+         i = mvme_read(fVmeInterface, &magic, addr + PMC_STATUS_OFFSET + REG_MAGIC, 2);
+#endif
+#ifdef CT_VME
+         addr = 0;        // VME board base address
+         addr += GEVPC_USER_FPGA;       // UsrFPGA base address
+         addr += (pmc == 0) ? PMC1_OFFSET : PMC2_OFFSET;   // offset
+         ErrorCode = VME_ReadSafeUShort(MasterMapping[index], addr + PMC_STATUS_OFFSET + REG_MAGIC, &magic);
+	 if (ErrorCode == VME_SUCCESS) i = 2;
+	 else i = 0;
+#endif
+         if (i == 2) {
+            if (magic != 0xC0DE) {
+               printf("Found old firmware, please upgrade immediately!\n");
+#ifdef HAVE_VME
+               fBoard[fNumberOfBoards] = new DRSBoard(fVmeInterface, addr, (index - 2) << 1 | pmc);
+#endif
+#ifdef CT_VME
+	       fBoard[fNumberOfBoards] = new DRSBoard(MasterMapping[index], MasterMap.vmebus_address, addr, (index-2) << 1 | pmc);	  
+#endif
+               fNumberOfBoards++;
+            } else {
+
+#ifdef HAVE_VME
+               /* read board type */
+               mvme_read(fVmeInterface, &type, addr + PMC_STATUS_OFFSET + REG_BOARD_TYPE, 2);
+               /* read firmware number */
+               mvme_read(fVmeInterface, &fw, addr + PMC_STATUS_OFFSET + REG_VERSION_FW, 2);
+               /* read serial number */
+               mvme_read(fVmeInterface, &serial, addr + PMC_STATUS_OFFSET + REG_SERIAL_BOARD, 2);
+               /* read temperature register to see if CMC card is present */
+               mvme_read(fVmeInterface, &temperature, addr + PMC_STATUS_OFFSET + REG_TEMPERATURE, 2);
+
+#endif
+#ifdef CT_VME
+               /* read board type */
+	       VME_ReadFastUShort(MasterMapping[index], addr + PMC_STATUS_OFFSET + REG_BOARD_TYPE, &type);
+     	       // Read firmware
+	       VME_ReadFastUShort(MasterMapping[index], addr + PMC_STATUS_OFFSET + REG_VERSION_FW, &fw);
+	       // Read serial number
+	       VME_ReadFastUShort(MasterMapping[index], addr + PMC_STATUS_OFFSET + REG_SERIAL_BOARD, &serial);
+      	       // Read temperature register to see if CMC card is present 
+      	       VME_ReadFastUShort(MasterMapping[index], addr + PMC_STATUS_OFFSET + REG_TEMPERATURE, &temperature);
+#endif
+               type &= 0xFF;
+               if (type == 2 || type == 3 || type == 4) {    // DRS2 or DRS3 or DRS4
+                  if (temperature == 0xFFFF) {
+                     printf("Found VME board in slot %d, fw %d, but no CMC board in %s slot\n", index, fw, (pmc==0) ? "upper" : "lower");
+                  } 
+		  else {
+                     printf("Found DRS%d board %2d in %s VME slot %2d, serial #%d, firmware revision %d\n", type, fNumberOfBoards, (pmc==0) ? "upper" : "lower", index, serial, fw);
+
+#ifdef HAVE_VME
+                     fBoard[fNumberOfBoards] = new DRSBoard(fVmeInterface, addr, (index - 2) << 1 | pmc);
+#endif
+#ifdef CT_VME
+		     fBoard[fNumberOfBoards] = new DRSBoard(MasterMapping[index], MasterMap.vmebus_address, addr, (index-2) << 1 | pmc);	  
+#endif
+                     if (fBoard[fNumberOfBoards]->HasCorrectFirmware()) fNumberOfBoards++;
+                     else {
+                       sprintf(fError, "Wrong firmware version: board has %d, required is %d\n",
+                                fBoard[fNumberOfBoards]->GetFirmwareVersion(),
+                                fBoard[fNumberOfBoards]->GetRequiredFirmwareVersion());
+		       delete fBoard[fNumberOfBoards];
+		     }
+                  } // Temperature check
+               } // Type check
+            } // Magic check
+         }
+	 } // pmc
+      }
+   } else printf("Cannot access VME crate, check driver, power and connection\n");
+#endif                          // HAVE_VME
+
+#ifdef HAVE_USB
+   unsigned char buffer[512];
+   int found, one_found, usb_slot;
+
+   one_found = 0;
+   usb_slot = 0;
+   for (index = 0; index < 127; index++) {
+      found = 0;
+
+      /* check for USB-Mezzanine test board */
+      if (musb_open(&usb_interface, 0x10C4, 0x1175, index, 1, 0) == MUSB_SUCCESS) {
+
+         /* check ID */
+         buffer[0] = USB_CMD_IDENT;
+         musb_write(usb_interface, 2, buffer, 1, USB_TIMEOUT);
+
+         i = musb_read(usb_interface, 1, (char *) buffer, sizeof(buffer), USB_TIMEOUT);
+         if (strcmp((char *) buffer, "USB_MEZZ2 V1.0") != 0) {
+            /* no USB-Mezzanine board found */
+            musb_close(usb_interface);
+         } else {
+            usb_interface->usb_type = 1;        // USB 1.1
+            fBoard[fNumberOfBoards] = new DRSBoard(usb_interface, usb_slot++);
+            if (fBoard[fNumberOfBoards]->HasCorrectFirmware()) {
+               fNumberOfBoards++;
+               found = 1;
+               one_found = 1;
+            } else
+               sprintf(fError, "Wrong firmware version: board has %d, required is %d\n",
+                       fBoard[fNumberOfBoards]->GetFirmwareVersion(),
+                       fBoard[fNumberOfBoards]->GetRequiredFirmwareVersion());
+         }
+      }
+
+      /* check for DRS4 evaluation board */
+      if (musb_open(&usb_interface, 0x04B4, 0x1175, index, 1, 0) == MUSB_SUCCESS) {
+
+         /* check ID */
+         struct usb_device_descriptor d;
+         usb_get_descriptor(usb_interface->dev, USB_DT_DEVICE, 0, &d, sizeof(d));
+         if (d.bcdDevice != 1) {
+            /* no DRS evaluation board found */
+            musb_close(usb_interface);
+         } else {
+
+            /* drain any data from Cy7C68013 FIFO if FPGA startup caused erratic write */
+            do {
+               i = musb_read(usb_interface, 8, buffer, sizeof(buffer), 100);
+               if (i > 0)
+                  printf("%d bytes stuck in buffer\n", i);
+            } while (i > 0);
+
+            usb_interface->usb_type = 2;        // USB 2.0
+            fBoard[fNumberOfBoards] = new DRSBoard(usb_interface, usb_slot++);
+            if (fBoard[fNumberOfBoards]->HasCorrectFirmware()) {
+               fNumberOfBoards++;
+               found = 1;
+               one_found = 1;
+            } else {
+               sprintf(fError, "Wrong firmware version: board has %d, required is %d\n",
+                      fBoard[fNumberOfBoards]->GetFirmwareVersion(),
+                      fBoard[fNumberOfBoards]->GetRequiredFirmwareVersion());
+            }
+         }
+      }
+
+      if (!found) {
+         if (!one_found)
+            printf("USB successfully scanned, but no boards found\n");
+         break;
+      }
+   }
+#endif                          // HAVE_USB
+
+   return;
+}
+
+/*------------------------------------------------------------------*/
+
+DRS::~DRS()
+{
+   int i;
+   for (i = 0; i < fNumberOfBoards; i++) {
+      delete fBoard[i];
+   }
+
+#ifdef HAVE_USB
+   if (usb2_buffer) {
+      free(usb2_buffer);
+      usb2_buffer = NULL;
+   }
+#endif
+
+#ifdef HAVE_VME
+   mvme_close(fVmeInterface);
+#endif
+
+#ifdef CT_VME 
+  if (CloseVME() != VME_SUCCESS) printf("Error with CloseVME()\n");
+  if (CloseCMEM()!= VME_SUCCESS) printf("Error with CloseCMEM()\n");
+#endif
+}
+
+/*------------------------------------------------------------------*/
+
+bool DRS::GetError(char *str, int size)
+{
+   if (fError[0])
+      strlcpy(str, fError, size);
+
+   return fError[0] > 0;
+}
+
+/*------------------------------------------------------------------*/
+
+#ifdef HAVE_USB
+DRSBoard::DRSBoard(MUSB_INTERFACE * musb_interface, int usb_slot)
+:  fDAC_COFSA(0)
+    , fDAC_COFSB(0)
+    , fDAC_DRA(0)
+    , fDAC_DSA(0)
+    , fDAC_TLEVEL(0)
+    , fDAC_ACALIB(0)
+    , fDAC_DSB(0)
+    , fDAC_DRB(0)
+    , fDAC_COFS(0)
+    , fDAC_ADCOFS(0)
+    , fDAC_CLKOFS(0)
+    , fDAC_ROFS_1(0)
+    , fDAC_ROFS_2(0)
+    , fDAC_INOFS(0)
+    , fDAC_BIAS(0)
+    , fDRSType(0)
+    , fBoardType(0)
+    , fRequiredFirmwareVersion(0)
+    , fFirmwareVersion(0)
+    , fBoardSerialNumber(0)
+    , fCtrlBits(0)
+    , fNumberOfReadoutChannels(0)
+    , fReadoutChannelConfig(0)
+    , fADCClkPhase(0)
+    , fADCClkInvert(0)
+    , fExternalClockFrequency(0)
+    , fUsbInterface(musb_interface)
+#ifdef HAVE_VME
+    , fVmeInterface(0)
+    , fBaseAddress(0)
+#endif
+    , fSlotNumber(usb_slot)
+    , fFrequency(0)
+    , fDominoMode(0)
+    , fDominoActive(0)
+    , fChannelConfig(0)
+    , fWSRLoop(0)
+    , fReadoutMode(0)
+    , fTriggerEnable1(0)
+    , fTriggerEnable2(0)
+    , fTriggerSource(0)
+    , fTriggerDelay(0)
+    , fDelayedStart(0)
+    , fRange(0)
+    , fCommonMode(0.8)
+    , fAcalMode(0)
+    , fAcalVolt(0)
+    , fTcalFreq(0)
+    , fTcalLevel(0)
+    , fTcalPhase(0)
+    , fMaxChips(0)
+    , fResponseCalibration(0)
+    , fCellCalibrationValid(false)
+    , fCellCalibratedRange(0)
+    , fTimingCalibrationValid(false)
+    , fTimeData(0)
+    , fNumberOfTimeData(0)
+    , fDebug(0)
+    , fTriggerStartBin(0)
+{
+   if (musb_interface->usb_type == 1)
+      fTransport = TR_USB;
+   else
+      fTransport = TR_USB2;
+   memset(fStopCell, 0, sizeof(fStopCell));
+   ConstructBoard();
+}
+
+#endif
+
+#ifdef HAVE_VME
+/*------------------------------------------------------------------*/
+
+DRSBoard::DRSBoard(MVME_INTERFACE * mvme_interface, mvme_addr_t base_address, int slot_number)
+:fDAC_COFSA(0)
+, fDAC_COFSB(0)
+, fDAC_DRA(0)
+, fDAC_DSA(0)
+, fDAC_TLEVEL(0)
+, fDAC_ACALIB(0)
+, fDAC_DSB(0)
+, fDAC_DRB(0)
+, fDAC_COFS(0)
+, fDAC_ADCOFS(0)
+, fDAC_CLKOFS(0)
+, fDAC_ROFS_1(0)
+, fDAC_ROFS_2(0)
+, fDAC_INOFS(0)
+, fDAC_BIAS(0)
+, fDRSType(0)
+, fBoardType(0)
+, fRequiredFirmwareVersion(0)
+, fFirmwareVersion(0)
+, fBoardSerialNumber(0)
+, fTransport(TR_VME)
+, fCtrlBits(0)
+, fNumberOfReadoutChannels(0)
+, fReadoutChannelConfig(0)
+, fADCClkPhase(0)
+, fADCClkInvert(0)
+, fExternalClockFrequency(0)
+#ifdef HAVE_USB
+, fUsbInterface(0)
+#endif
+#ifdef HAVE_VME
+, fVmeInterface(mvme_interface)
+, fBaseAddress(base_address)
+, fSlotNumber(slot_number)
+#endif
+, fFrequency(0)
+, fRefClock(0)
+, fDominoMode(1)
+, fDominoActive(1)
+, fChannelConfig(0)
+, fWSRLoop(1)
+, fReadoutMode(0)
+, fTriggerEnable1(0)
+, fTriggerEnable2(0)
+, fTriggerSource(0)
+, fTriggerDelay(0)
+, fDelayedStart(0)
+, fRange(0)
+, fCommonMode(0.8)
+, fAcalMode(0)
+, fAcalVolt(0)
+, fTcalFreq(0)
+, fTcalLevel(0)
+, fTcalPhase(0)
+, fMaxChips(0)
+, fResponseCalibration(0)
+, fTimeData(0)
+, fNumberOfTimeData(0)
+, fDebug(0)
+, fTriggerStartBin(0)
+{
+   ConstructBoard();
+}
+
+#endif
+
+#ifdef CT_VME
+/*------------------------------------------------------------------*/
+
+DRSBoard::DRSBoard(int MasterMapping, unsigned int base_address, unsigned int BoardAddress, int slot_number)
+:fDAC_COFSA(0)
+, fDAC_COFSB(0)
+, fDAC_DRA(0)
+, fDAC_DSA(0)
+, fDAC_TLEVEL(0)
+, fDAC_ACALIB(0)
+, fDAC_DSB(0)
+, fDAC_DRB(0)
+, fDAC_COFS(0)
+, fDAC_ADCOFS(0)
+, fDAC_CLKOFS(0)
+, fDAC_ROFS_1(0)
+, fDAC_ROFS_2(0)
+, fDAC_INOFS(0)
+, fDAC_BIAS(0)
+, fDRSType(0)
+, fBoardType(0)
+, fRequiredFirmwareVersion(0)
+, fFirmwareVersion(0)
+, fBoardSerialNumber(0)
+, fTransport(TR_VME)
+, fCtrlBits(0)
+, fNumberOfReadoutChannels(0)
+, fReadoutChannelConfig(0)
+, fADCClkPhase(0)
+, fADCClkInvert(0)
+, fExternalClockFrequency(0)
+, fBaseAddress(base_address)
+, fBoardAddress(BoardAddress)
+, fMasterMapping(MasterMapping)
+, fSlotNumber(slot_number)
+, fFrequency(0)
+, fRefClock(0)
+, fDominoMode(1)
+, fDominoActive(1)
+, fChannelConfig(0)
+, fWSRLoop(1)
+, fReadoutMode(0)
+, fTriggerEnable1(0)
+, fTriggerEnable2(0)
+, fTriggerSource(0)
+, fTriggerDelay(0)
+, fDelayedStart(0)
+, fRange(0)
+, fCommonMode(0.8)
+, fAcalMode(0)
+, fAcalVolt(0)
+, fTcalFreq(0)
+, fTcalLevel(0)
+, fTcalPhase(0)
+, fMaxChips(0)
+, fResponseCalibration(0)
+, fTimeData(0)
+, fNumberOfTimeData(0)
+, fDebug(0)
+, fTriggerStartBin(0)
+{
+  // Allocate contiguous memory for BLT
+  AllocateSegmentCMEM(MEM_SEGMENT,&CMEM_SegIdentifier);
+  AssignPhysicalSegAddressCMEM(CMEM_SegIdentifier, &PCIAddress);
+  AssignVirtualSegAddressCMEM(CMEM_SegIdentifier, &VirtualAddress);
+  ConstructBoard();
+}
+#endif
+
+/*------------------------------------------------------------------*/
+
+DRSBoard::~DRSBoard()
+{
+   int i;
+#ifdef HAVE_USB
+   if (fTransport == TR_USB || fTransport == TR_USB2)
+      musb_close(fUsbInterface);
+#endif
+
+   // Response Calibration
+   delete fResponseCalibration;
+
+   // Time Calibration
+   for (i = 0; i < fNumberOfTimeData; i++) {
+      delete fTimeData[i];
+   }
+   delete[]fTimeData;
+   
+#ifdef CT_VME 
+  FreeSegmentCMEM(CMEM_SegIdentifier);
+#endif
+
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::ConstructBoard()
+{
+   unsigned char buffer[2];
+
+   fDebug = 0;
+   fWSRLoop = 1;
+   fCtrlBits = 0;
+
+   fExternalClockFrequency = 1000. / 30.;
+   strcpy(fCalibDirectory, ".");
+
+   /* check board communication */
+   if (Read(T_STATUS, buffer, REG_MAGIC, 2) < 0) {
+      InitFPGA();
+      if (Read(T_STATUS, buffer, REG_MAGIC, 2) < 0)
+         return;
+   }
+
+   ReadSerialNumber();
+
+   /* check for required firmware version */
+   if (!HasCorrectFirmware())
+      return;
+
+   /* set correct reference clock */
+   if (fBoardType == 5)
+      fRefClock = 60;
+   else
+      fRefClock = 33;
+
+   /* get mode from hardware */
+   if (fDRSType == 4) {
+      fDominoMode = (GetConfigReg() & BIT_CONFIG_DMODE) > 0;
+   } else {
+      fDominoMode = (GetCtrlReg() & BIT_DMODE) > 0;
+   }
+   fTriggerEnable1 = (GetConfigReg() & BIT_ENABLE_TRIGGER1) > 0;
+   fTriggerEnable2 = (GetConfigReg() & BIT_ENABLE_TRIGGER2) > 0;
+   fTriggerSource = ((GetConfigReg() & BIT_TR_SOURCE1) > 0) | (((GetConfigReg() & BIT_TR_SOURCE2) > 0) << 1);
+   fReadoutMode = (GetConfigReg() & BIT_READOUT_MODE) > 0;
+   fADCClkInvert = (GetConfigReg() & BIT_ADCCLK_INVERT) > 0;
+   fDominoActive = (GetConfigReg() & BIT_DACTIVE) > 0;
+   ReadFrequency(0, &fFrequency);
+   if (fFrequency < 0.1 || fFrequency > 6)
+      fFrequency = 1;
+
+   /* initialize number of channels */
+   if (fDRSType == 4) {
+      if (fBoardType == 6) {
+         unsigned short d;
+         Read(T_CTRL, &d, REG_CHANNEL_MODE, 2);
+         fReadoutChannelConfig = d & 0xFF;
+         if (d == 7)
+            fNumberOfReadoutChannels = 9;
+         else
+            fNumberOfReadoutChannels = 5;
+      } else
+         fNumberOfReadoutChannels = 9;
+   } else
+      fNumberOfReadoutChannels = 10;
+
+   if (fBoardType == 1) {
+      fDAC_COFSA = 0;
+      fDAC_COFSB = 1;
+      fDAC_DRA = 2;
+      fDAC_DSA = 3;
+      fDAC_TLEVEL = 4;
+      fDAC_ACALIB = 5;
+      fDAC_DSB = 6;
+      fDAC_DRB = 7;
+   } else if (fBoardType == 2 || fBoardType == 3) {
+      fDAC_COFS = 0;
+      fDAC_DSA = 1;
+      fDAC_DSB = 2;
+      fDAC_TLEVEL = 3;
+      fDAC_CLKOFS = 5;
+      fDAC_ACALIB = 6;
+      fDAC_ADCOFS = 7;
+   } else if (fBoardType == 4) {
+      fDAC_ROFS_1 = 0;
+      fDAC_DSA = 1;
+      fDAC_DSB = 2;
+      fDAC_ROFS_2 = 3;
+      fDAC_BIAS = 4;
+      fDAC_INOFS = 5;
+      fDAC_ACALIB = 6;
+      fDAC_ADCOFS = 7;
+   } else if (fBoardType == 5) {
+      fDAC_ROFS_1 = 0;
+      fDAC_CMOFS = 1;
+      fDAC_CALN = 2;
+      fDAC_CALP = 3;
+      fDAC_BIAS = 4;
+      fDAC_TLEVEL = 5;
+      fDAC_ONOFS = 6;
+   } else if (fBoardType == 6) {
+      fDAC_ONOFS = 0;
+      fDAC_CMOFSP = 1;
+      fDAC_CALN = 2;
+      fDAC_CALP = 3;
+      fDAC_CMOFSN = 5;
+      fDAC_ROFS_1 = 6;
+      fDAC_BIAS = 7;
+   }
+
+   if (fDRSType == 4) {
+      ReadCalibration();
+   } else {
+      // Response Calibration
+      fResponseCalibration = new ResponseCalibration(this);
+
+      // Time Calibration
+      fTimeData = new DRSBoard::TimeData *[kNumberOfChipsMax];
+      fNumberOfTimeData = 0;
+   }
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::ReadSerialNumber()
+{
+   unsigned char buffer[2];
+   int number;
+
+   // check magic number
+   if (Read(T_STATUS, buffer, REG_MAGIC, 2) < 0) {
+      printf("Cannot read from board\n");
+      return;
+   }
+
+   number = (static_cast < int >(buffer[1]) << 8) +buffer[0];
+   if (number != 0xC0DE) {
+      printf("Invalid magic number: %04X\n", number);
+      return;
+   }
+   
+   // read board type
+   Read(T_STATUS, buffer, REG_BOARD_TYPE, 2);
+   fDRSType = buffer[0];
+   fBoardType = buffer[1];
+
+   // read firmware version
+   Read(T_STATUS, buffer, REG_VERSION_FW, 2);
+   fFirmwareVersion = (static_cast < int >(buffer[1]) << 8) +buffer[0];
+
+   // retrieve board serial number
+   Read(T_STATUS, buffer, REG_SERIAL_BOARD, 2);
+   number = (static_cast < int >(buffer[1]) << 8) +buffer[0];
+   fBoardSerialNumber = number;
+
+   // determine DRS type and board type for old boards from setial number
+   if (fBoardType == 0) {
+      // determine board version from serial number
+      if (number >= 2000 && number < 5000) {
+         fBoardType = 6;
+         fDRSType = 4;
+      } else if (number >= 1000) {
+         fBoardType = 4;
+         fDRSType = 3;
+      } else if (number >= 100)
+         fBoardType = 3;
+      else if (number > 0)
+         fBoardType = 2;
+      else {
+         fBoardType = 3;
+         fDRSType = 2;
+         fRequiredFirmwareVersion = REQUIRED_FIRMWARE_VERSION_DRS2;
+      }
+   }
+
+   // set constants according to board type
+   if (fBoardType == 6)
+      fNumberOfChips = 4;
+   else
+      fNumberOfChips = 2;
+
+   if (fDRSType == 4)
+      fNumberOfChannels = 9;
+   else
+      fNumberOfChannels = 10;
+
+   // retrieve firmware version
+   if (fDRSType == 2)
+      fRequiredFirmwareVersion = REQUIRED_FIRMWARE_VERSION_DRS2;
+   if (fDRSType == 3)
+      fRequiredFirmwareVersion = REQUIRED_FIRMWARE_VERSION_DRS3;
+   if (fDRSType == 4)
+      fRequiredFirmwareVersion = REQUIRED_FIRMWARE_VERSION_DRS4;
+
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::ReadCalibration(void)
+{
+   unsigned short buf[1024*9*2];
+   int i, j, chip;
+
+   fCellCalibrationValid = false;
+   fTimingCalibrationValid = false;
+
+   memset(fCellOffset,  0, sizeof(fCellOffset));
+   memset(fCellGain,    0, sizeof(fCellGain));
+   memset(fCellOffset2, 0, sizeof(fCellOffset2));
+   memset(fCellT,       0, sizeof(fCellT));
+
+   /* read offsets and gain from eeprom */
+   if (fBoardType == 5) {
+      ReadEEPROM(0, buf, 16);
+
+      /* check voltage calibration method */
+      if ((buf[2] & 0xFF) == VCALIB_METHOD)
+         fCellCalibrationValid = true;
+      else {
+         fCellCalibratedRange = 0;
+         return;
+      }
+
+      /* check timing calibration method */
+      if ((buf[4] & 0xFF) == TCALIB_METHOD)
+         fTimingCalibrationValid = true;
+      else {
+         fTimingCalibratedFrequency = 0;
+      }
+
+      fCellCalibratedRange = ((int) (buf[2] >> 8)) / 100.0; // -50 ... +50 => -0.5 V ... +0.5 V
+      fTimingCalibratedFrequency = buf[6] / 65525.0 * 6; // 0 ... 65535 => 0 ... 6 GHz
+
+      ReadEEPROM(1, buf, 1024*32);
+      for (i=0 ; i<8 ; i++)
+         for (j=0 ; j<1024; j++) {
+            fCellOffset[i][j] = buf[(i*1024+j)*2];
+            fCellGain[i][j]   = buf[(i*1024+j)*2 + 1]/65535.0*0.4+0.7;
+         }
+
+      ReadEEPROM(2, buf, 1024*5*4);
+      for (i=0 ; i<1 ; i++)
+         for (j=0 ; j<1024; j++) {
+            fCellOffset[i+8][j] = buf[(i*1024+j)*2];
+            fCellGain[i+8][j]   = buf[(i*1024+j)*2 + 1]/65535.0*0.4+0.7;
+         }
+
+      for (i=0 ; i<4 ; i++)
+         for (j=0 ; j<1024; j++) {
+            fCellOffset2[i*2][j]   = buf[2*1024+((i*2)*1024+j)*2];
+            fCellOffset2[i*2+1][j] = buf[2*1024+((i*2)*1024+j)*2+1];
+         }
+
+   } else if (fBoardType == 6) {
+      ReadEEPROM(0, buf, 16);
+
+      /* check voltage calibration method */
+      if ((buf[2] & 0xFF) == VCALIB_METHOD)
+         fCellCalibrationValid = true;
+      else {
+         fCellCalibratedRange = 0;
+         return;
+      }
+
+      /* check timing calibration method */
+      if ((buf[4] & 0xFF) == TCALIB_METHOD)
+         fTimingCalibrationValid = true;
+      else {
+         fTimingCalibratedFrequency = 0;
+      }
+
+      fCellCalibratedRange = ((int) (buf[2] >> 8)) / 100.0; // -50 ... +50 => -0.5 V ... +0.5 V
+      fTimingCalibratedFrequency = buf[6] / 65525.0 * 6; // 0 ... 65535 => 0 ... 6 GHz
+
+      for (chip=0 ; chip<4 ; chip++) {
+         ReadEEPROM(1+chip, buf, 1024*32);
+         for (i=0 ; i<8 ; i++)
+            for (j=0 ; j<1024; j++) {
+               fCellOffset[i+chip*9][j] = buf[(i*1024+j)*2];
+               fCellGain[i+chip*9][j]   = buf[(i*1024+j)*2 + 1]/65535.0*0.4+0.7;
+            }
+      }
+
+      ReadEEPROM(5, buf, 1024*4*4);
+      for (chip=0 ; chip<4 ; chip++)
+         for (j=0 ; j<1024; j++) {
+            fCellOffset[8+chip*9][j] = buf[j*2+chip*0x0800];
+            fCellGain[8+chip*9][j]   = buf[j*2+1+chip*0x0800]/65535.0*0.4+0.7;
+         }
+
+      ReadEEPROM(7, buf, 1024*32);
+      for (i=0 ; i<8 ; i++) {
+         for (j=0 ; j<1024; j++) {
+            fCellOffset2[i][j]   = buf[i*0x800 + j*2];
+            fCellOffset2[i+9][j] = buf[i*0x800 + j*2+1];
+         }
+      }
+
+      ReadEEPROM(8, buf, 1024*32);
+      for (i=0 ; i<8 ; i++) {
+         for (j=0 ; j<1024; j++) {
+            fCellOffset2[i+18][j] = buf[i*0x800 + j*2];
+            fCellOffset2[i+27][j] = buf[i*0x800 + j*2+1];
+         }
+      }
+
+   }
+   else return;
+
+   /* read timing calibration from eeprom */
+   if (fBoardType == 5) {
+      if (!fTimingCalibrationValid) {
+         for (i=0 ; i<1024 ; i++)
+            fCellT[0][i] = 1/fFrequency*i;
+      } else {
+         ReadEEPROM(0, buf, 1024*sizeof(short)*2);
+         fCellT[0][0] = 0;
+         for (i=1 ; i<1024; i++)
+            fCellT[0][i] = fCellT[0][i-1] + buf[i*2+1]/10000.0;
+      }
+   } else if (fBoardType == 6) {
+      if (!fTimingCalibrationValid) {
+         for (i=0 ; i<1024 ; i++)
+            for (j=0 ; j<4 ; j++)
+               fCellT[j][i] = 1/fFrequency*i;
+      } else {
+         ReadEEPROM(6, buf, 1024*sizeof(short)*4);
+         for (j=0 ; j<4 ; j++)
+            fCellT[j][0] = 0;
+         for (i=1 ; i<1024; i++) {
+            fCellT[0][i] = fCellT[0][i-1] + buf[i*2]/10000.0;
+            fCellT[1][i] = fCellT[1][i-1] + buf[i*2+1]/10000.0;
+            fCellT[2][i] = fCellT[2][i-1] + buf[i*2+0x800]/10000.0;
+            fCellT[3][i] = fCellT[3][i-1] + buf[i*2+0x800+1]/10000.0;
+         }
+      }
+   }
+}
+
+/*------------------------------------------------------------------*/
+
+bool DRSBoard::HasCorrectFirmware()
+{
+   /* check for required firmware version */
+   return (fFirmwareVersion >= fRequiredFirmwareVersion);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::InitFPGA(void)
+{
+
+#ifdef HAVE_USB
+   if (fTransport == TR_USB2) {
+      unsigned char buffer[1];
+      int i, status;
+
+      /* blink Cy7C68013A LED and issue an FPGA reset */
+      buffer[0] = 0;            // LED off
+      musb_write(fUsbInterface, 1, buffer, 1, 100);
+      Sleep(50);
+
+      buffer[0] = 1;            // LED on
+      musb_write(fUsbInterface, 1, buffer, 1, 100);
+
+      /* wait until EEPROM page #0 has been read */
+      for (i=0 ; i<100 ; i++) {
+         Read(T_STATUS, &status, REG_STATUS, 4);
+         if ((status & BIT_SERIAL_BUSY) == 0)
+            break;
+         Sleep(10);
+      }
+   }
+#endif
+
+   return 1;
+}
+
+int DRSBoard::Write(int type, unsigned int addr, void *data, int size)
+{
+   // Generic write function
+
+#ifdef CT_VME 
+  if (size > MEM_SEGMENT) size = MEM_SEGMENT;
+  
+  if (type == T_CTRL) addr += PMC_CTRL_OFFSET;
+  else if (type == T_STATUS) addr += PMC_STATUS_OFFSET;
+  else if (type == T_RAM) addr += PMC_RAM_OFFSET;
+  else return 0;
+  
+  if (size == 1) {
+    ErrorCode = VME_WriteSafeUChar(fMasterMapping, fBoardAddress + addr, *(static_cast<unsigned char*>(data)));    
+  } 
+  else if (size == 2) {  
+    ErrorCode = VME_WriteSafeUShort(fMasterMapping, fBoardAddress + addr, *(static_cast<unsigned short*>(data)));
+  }
+  else if (size == 4) {  
+    ErrorCode = VME_WriteSafeUInt(fMasterMapping, fBoardAddress + addr, *(static_cast<unsigned int*>(data)));
+  } 
+  else {  
+    // Copy to contiguous block of memory at VirtualAddress
+    memcpy((void *) VirtualAddress, data, size);
+
+    // Assign fields for BLT and perform BLT
+    BLT_List.list_of_items[0].vmebus_address       = fBaseAddress + fBoardAddress + PMC_RAM_OFFSET;
+    BLT_List.list_of_items[0].system_iobus_address = PCIAddress;
+    BLT_List.list_of_items[0].size_requested       = size; 
+    BLT_List.list_of_items[0].control_word         = VME_DMA_D64W | VME_A32;
+    BLT_List.number_of_items = 1;
+    ErrorCode = VME_BlockTransfer(&BLT_List,-1);
+  }
+  if (ErrorCode != VME_SUCCESS) {
+    VME_ErrorPrint(ErrorCode);
+    return 0;
+  }
+  else return size;
+#endif
+
+   if (fTransport == TR_VME) {
+
+#ifdef HAVE_VME
+      unsigned int base_addr;
+
+      base_addr = fBaseAddress;
+
+      if (type == T_CTRL)
+         base_addr += PMC_CTRL_OFFSET;
+      else if (type == T_STATUS)
+         base_addr += PMC_STATUS_OFFSET;
+      else if (type == T_RAM)
+         base_addr += PMC_RAM_OFFSET;
+
+      if (size == 1) {
+         /* 8-bit write access */
+         mvme_set_dmode(fVmeInterface, MVME_DMODE_D8);
+         mvme_write(fVmeInterface, base_addr + addr, static_cast < mvme_locaddr_t * >(data), size);
+      } else if (size == 2) {
+         /* 16-bit write access */
+         mvme_set_dmode(fVmeInterface, MVME_DMODE_D16);
+         mvme_write(fVmeInterface, base_addr + addr, static_cast < mvme_locaddr_t * >(data), size);
+      } else {
+         mvme_set_dmode(fVmeInterface, MVME_DMODE_D32);
+
+         /* as long as no block transfer is supported, do pseudo block transfer */
+         mvme_set_blt(fVmeInterface, MVME_BLT_NONE);
+
+         mvme_write(fVmeInterface, base_addr + addr, static_cast < mvme_locaddr_t * >(data), size);
+      }
+
+      return size;
+#endif                          // HAVE_VME
+
+   } else if (fTransport == TR_USB) {
+#ifdef HAVE_USB
+      unsigned char buffer[64], ack;
+      unsigned int base_addr;
+      int i, j, n;
+
+      if (type == T_CTRL)
+         base_addr = USB_CTRL_OFFSET;
+      else if (type == T_STATUS)
+         base_addr = USB_STATUS_OFFSET;
+      else if (type == T_RAM)
+         base_addr = USB_RAM_OFFSET;
+      else
+         base_addr = 0;
+
+      if (type != T_RAM) {
+
+         /*---- register access ----*/
+
+         if (size == 2) {
+            /* word swapping: first 16 bit sit at upper address */
+            if ((addr % 4) == 0)
+               addr = addr + 2;
+            else
+               addr = addr - 2;
+         }
+
+         buffer[0] = USB_CMD_WRITE;
+         buffer[1] = base_addr + addr;
+         buffer[2] = size;
+
+         for (i = 0; i < size; i++)
+            buffer[3 + i] = *((unsigned char *) data + i);
+
+         /* try 10 times */
+         ack = 0;
+         for (i = 0; i < 10; i++) {
+            n = musb_write(fUsbInterface, 2, buffer, 3 + size, USB_TIMEOUT);
+            if (n == 3 + size) {
+               for (j = 0; j < 10; j++) {
+                  /* wait for acknowledge */
+                  n = musb_read(fUsbInterface, 1, &ack, 1, USB_TIMEOUT);
+                  if (n == 1 && ack == 1)
+                     break;
+
+                  printf("Redo receive\n");
+               }
+            }
+
+            if (ack == 1)
+               return size;
+
+            printf("Redo send\n");
+         }
+      } else {
+
+         /*---- RAM access ----*/
+
+         buffer[0] = USB_CMD_ADDR;
+         buffer[1] = base_addr + addr;
+         musb_write(fUsbInterface, 2, buffer, 2, USB_TIMEOUT);
+
+         /* chop buffer into 60-byte packets */
+         for (i = 0; i <= (size - 1) / 60; i++) {
+            n = size - i * 60;
+            if (n > 60)
+               n = 60;
+            buffer[0] = USB_CMD_WRITE12;
+            buffer[1] = n;
+
+            for (j = 0; j < n; j++)
+               buffer[2 + j] = *((unsigned char *) data + j + i * 60);
+
+            musb_write(fUsbInterface, 2, buffer, 2 + n, USB_TIMEOUT);
+
+            for (j = 0; j < 10; j++) {
+               /* wait for acknowledge */
+               n = musb_read(fUsbInterface, 1, &ack, 1, USB_TIMEOUT);
+               if (n == 1 && ack == 1)
+                  break;
+
+               printf("Redo receive acknowledge\n");
+            }
+         }
+
+         return size;
+      }
+#endif                          // HAVE_USB
+   } else if (fTransport == TR_USB2) {
+#ifdef HAVE_USB
+      unsigned int base_addr;
+      int i;
+
+      if (usb2_buffer == NULL)
+         usb2_buffer = (unsigned char *) malloc(USB2_BUFFER_SIZE);
+      assert(usb2_buffer);
+
+      /* only accept even address and number of bytes */
+      assert(addr % 2 == 0);
+      assert(size % 2 == 0);
+
+      /* check for maximum size */
+      assert(size <= USB2_BUFFER_SIZE - 10);
+
+      if (type == T_CTRL)
+         base_addr = USB2_CTRL_OFFSET;
+      else if (type == T_STATUS)
+         base_addr = USB2_STATUS_OFFSET;
+      else if (type == T_FIFO)
+         base_addr = USB2_FIFO_OFFSET;
+      else if (type == T_RAM)
+         base_addr = USB2_RAM_OFFSET;
+      else
+         base_addr = 0;
+
+      if (type != T_RAM && size == 2) {
+         /* word swapping: first 16 bit sit at upper address */
+         if ((addr % 4) == 0)
+            addr = addr + 2;
+         else
+            addr = addr - 2;
+      }
+
+      addr += base_addr;
+
+      usb2_buffer[0] = USB2_CMD_WRITE;
+      usb2_buffer[1] = 0;
+
+      usb2_buffer[2] = (addr >> 0) & 0xFF;
+      usb2_buffer[3] = (addr >> 8) & 0xFF;
+      usb2_buffer[4] = (addr >> 16) & 0xFF;
+      usb2_buffer[5] = (addr >> 24) & 0xFF;
+
+      usb2_buffer[6] = (size >> 0) & 0xFF;
+      usb2_buffer[7] = (size >> 8) & 0xFF;
+      usb2_buffer[8] = (size >> 16) & 0xFF;
+      usb2_buffer[9] = (size >> 24) & 0xFF;
+
+      for (i = 0; i < size; i++)
+         usb2_buffer[10 + i] = *((unsigned char *) data + i);
+
+      i = musb_write(fUsbInterface, 4, usb2_buffer, 10 + size, USB_TIMEOUT);
+      if (i != 10 + size)
+         printf("musb_write error: %d\n", i);
+
+      return i;
+#endif                          // HAVE_USB
+   }
+
+   return 0;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::Read(int type, void *data, unsigned int addr, int size)
+{
+   // Generic read function
+
+#ifdef CT_VME
+  if (size > MEM_SEGMENT) size = MEM_SEGMENT;
+
+  if (type == T_CTRL) addr += PMC_CTRL_OFFSET;
+  else if (type == T_STATUS) addr += PMC_STATUS_OFFSET;
+  else if (type == T_RAM) addr += PMC_RAM_OFFSET;
+  else if (type == T_FIFO) addr += PMC_FIFO_OFFSET;
+  else return 0;
+  
+  if (size == 1) {
+    ErrorCode = VME_ReadSafeUChar(fMasterMapping, fBoardAddress + addr, static_cast<unsigned char*>(data)); 
+  } else if (size == 2) {   
+    ErrorCode = VME_ReadSafeUShort(fMasterMapping, fBoardAddress + addr, static_cast<unsigned short*>(data)); 
+  } else if (size == 4) {
+    ErrorCode = VME_ReadSafeUInt(fMasterMapping, fBoardAddress + addr, static_cast<unsigned int*>(data)); 
+  } else {
+
+    // Assign fields and perform BLT
+    BLT_List.list_of_items[0].vmebus_address       = fBaseAddress + fBoardAddress + PMC_RAM_OFFSET;
+    BLT_List.list_of_items[0].system_iobus_address = PCIAddress;
+    BLT_List.list_of_items[0].size_requested       = size; 
+    BLT_List.list_of_items[0].control_word         = VME_DMA_D64R | VME_A32;
+    BLT_List.number_of_items = 1;
+    ErrorCode = VME_BlockTransfer(&BLT_List, -1);
+    
+    // Copy contiguous block of memory starting from VirtualAddress
+    memcpy(data, (void *) VirtualAddress, size);
+  }
+
+  if (ErrorCode != VME_SUCCESS) {
+    VME_ErrorPrint(ErrorCode);
+    return 0;
+  }
+  else return size;
+#endif
+
+   if (fTransport == TR_VME) {
+
+#ifdef HAVE_VME
+      unsigned int base_addr;
+      int n, i;
+
+      base_addr = fBaseAddress;
+
+      if (type == T_CTRL)
+         base_addr += PMC_CTRL_OFFSET;
+      else if (type == T_STATUS)
+         base_addr += PMC_STATUS_OFFSET;
+      else if (type == T_RAM)
+         base_addr += PMC_RAM_OFFSET;
+      else if (type == T_FIFO)
+         base_addr += PMC_FIFO_OFFSET;
+
+      mvme_set_dmode(fVmeInterface, MVME_DMODE_D32);
+
+      n = 0;
+      if (size == 1) {
+         /* 8-bit read access */
+         mvme_set_dmode(fVmeInterface, MVME_DMODE_D8);
+         n = mvme_read(fVmeInterface, static_cast < mvme_locaddr_t * >(data), base_addr + addr, size);
+      } else if (size == 2) {
+         /* 16-bit read access */
+         mvme_set_dmode(fVmeInterface, MVME_DMODE_D16);
+         n = mvme_read(fVmeInterface, static_cast < mvme_locaddr_t * >(data), base_addr + addr, size);
+      } else {
+         mvme_set_dmode(fVmeInterface, MVME_DMODE_D32);
+
+         //mvme_set_blt(fVmeInterface, MVME_BLT_NONE); // pseudo block transfer
+         mvme_set_blt(fVmeInterface, MVME_BLT_2EVME);   // 2eVME if implemented
+         n = mvme_read(fVmeInterface, static_cast < mvme_locaddr_t * >(data), base_addr + addr, size);
+         while (n != size) {
+            printf("Only read %d out of %d, retry with %d: ", n, size, size - n);
+            i = mvme_read(fVmeInterface, static_cast < mvme_locaddr_t * >(data) + n / 4, base_addr + addr + n,
+                          size - n);
+            printf("read %d\n", i);
+            if (i == 0) {
+               printf("Error reading VME\n");
+               return n;
+            }
+            n += i;
+         }
+
+         //for (i = 0; i < size; i += 4)
+         //   mvme_read(fVmeInterface, (mvme_locaddr_t *)((char *)data+i), base_addr + addr+i, 4);
+      }
+      return n;
+
+#endif                          // HAVE_VME
+   } else if (fTransport == TR_USB) {
+#ifdef HAVE_USB
+      unsigned char buffer[64];
+      unsigned int base_addr;
+      int i, j, ret, n;
+
+      if (type == T_CTRL)
+         base_addr = USB_CTRL_OFFSET;
+      else if (type == T_STATUS)
+         base_addr = USB_STATUS_OFFSET;
+      else if (type == T_RAM)
+         base_addr = USB_RAM_OFFSET;
+      else
+         assert(0);             // FIFO not implemented
+
+      if (type != T_RAM) {
+
+         /*---- register access ----*/
+
+         if (size == 2) {
+            /* word swapping: first 16 bit sit at uppder address */
+            if ((addr % 4) == 0)
+               addr = addr + 2;
+            else
+               addr = addr - 2;
+         }
+
+         buffer[0] = USB_CMD_READ;
+         buffer[1] = base_addr + addr;
+         buffer[2] = size;
+
+         musb_write(fUsbInterface, 2, buffer, 2 + size, USB_TIMEOUT);
+         i = musb_read(fUsbInterface, 1, data, size, USB_TIMEOUT);
+
+         if (i != size)
+            return 0;
+
+         return size;
+      } else {
+
+         /*---- RAM access ----*/
+
+         /* in RAM mode, only the 2048-byte page can be selected */
+         buffer[0] = USB_CMD_ADDR;
+         buffer[1] = base_addr + (addr >> 11);
+         musb_write(fUsbInterface, 2, buffer, 2, USB_TIMEOUT);
+
+         /* receive data in 60-byte packets */
+         for (i = 0; i <= (size - 1) / 60; i++) {
+            n = size - i * 60;
+            if (n > 60)
+               n = 60;
+            buffer[0] = USB_CMD_READ12;
+            buffer[1] = n;
+            musb_write(fUsbInterface, 2, buffer, 2, USB_TIMEOUT);
+
+            ret = musb_read(fUsbInterface, 1, buffer, n, USB_TIMEOUT);
+
+            if (ret != n) {
+               /* try again */
+               ret = musb_read(fUsbInterface, 1, buffer, n, USB_TIMEOUT);
+               if (ret != n)
+                  return 0;
+            }
+
+            for (j = 0; j < ret; j++)
+               *((unsigned char *) data + j + i * 60) = buffer[j];
+         }
+
+         return size;
+      }
+#endif                          // HAVE_USB
+   } else if (fTransport == TR_USB2) {
+#ifdef HAVE_USB
+      unsigned char buffer[10];
+      unsigned int base_addr;
+      int i;
+
+      /* only accept even address and number of bytes */
+      assert(addr % 2 == 0);
+      assert(size % 2 == 0);
+
+      /* check for maximum size */
+      assert(size <= USB2_BUFFER_SIZE - 10);
+
+      if (type == T_CTRL)
+         base_addr = USB2_CTRL_OFFSET;
+      else if (type == T_STATUS)
+         base_addr = USB2_STATUS_OFFSET;
+      else if (type == T_FIFO)
+         base_addr = USB2_FIFO_OFFSET;
+      else if (type == T_RAM)
+         base_addr = USB2_RAM_OFFSET;
+      else
+         base_addr = 0;
+
+      if (type != T_RAM && size == 2) {
+         /* word swapping: first 16 bit sit at upper address */
+         if ((addr % 4) == 0)
+            addr = addr + 2;
+         else
+            addr = addr - 2;
+      }
+
+      addr += base_addr;
+
+      buffer[0] = USB2_CMD_READ;
+      buffer[1] = 0;
+
+      buffer[2] = (addr >> 0) & 0xFF;
+      buffer[3] = (addr >> 8) & 0xFF;
+      buffer[4] = (addr >> 16) & 0xFF;
+      buffer[5] = (addr >> 24) & 0xFF;
+
+      buffer[6] = (size >> 0) & 0xFF;
+      buffer[7] = (size >> 8) & 0xFF;
+      buffer[8] = (size >> 16) & 0xFF;
+      buffer[9] = (size >> 24) & 0xFF;
+
+      i = musb_write(fUsbInterface, 4, buffer, 10, USB_TIMEOUT);
+      if (i != 10)
+         printf("musb_read error %d\n", i);
+
+      i = musb_read(fUsbInterface, 8, data, size, USB_TIMEOUT);
+      return i;
+#endif                          // HAVE_USB
+   }
+
+   return 0;
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::SetLED(int state)
+{
+   // Set LED state
+   if (state)
+      fCtrlBits |= BIT_LED;
+   else
+      fCtrlBits &= ~BIT_LED;
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::SetChannelConfig(int firstChannel, int lastChannel, int nConfigChannels)
+{
+   // Set number of channels
+   unsigned short d;
+
+   if (lastChannel < 0 || lastChannel > 10) {
+      printf("Invalid number of channels: %d (must be between 0 and 10)\n", lastChannel);
+      return;
+   }
+
+   if (fDRSType == 2) {
+      // register must contain last channel to read out starting from 9
+      d = 9 - lastChannel;
+      Write(T_CTRL, REG_CHANNEL_CONFIG, &d, 2);
+   } else if (fDRSType == 3) {
+      // upper four bits of register must contain last channel to read out starting from 9
+      d = (firstChannel << 4) | lastChannel;
+      Write(T_CTRL, REG_CHANNEL_MODE, &d, 2);
+
+      // set bit pattern for write shift register
+      switch (nConfigChannels) {
+      case 1:
+         d = 0x001;
+         break;
+      case 2:
+         d = 0x041;
+         break;
+      case 3:
+         d = 0x111;
+         break;
+      case 4:
+         d = 0x249;
+         break;
+      case 6:
+         d = 0x555;
+         break;
+      case 12:
+         d = 0xFFF;
+         break;
+      default:
+         printf("Invalid channel configuration\n");
+      }
+      Write(T_CTRL, REG_CHANNEL_CONFIG, &d, 2);
+   } else if (fDRSType == 4) {
+      if (fBoardType == 6) {
+         // determined channel readout mode A/C[even/odd], B/D[even/odd] or A/B/C/D
+         fReadoutChannelConfig = firstChannel;
+         Read(T_CTRL, &d, REG_CHANNEL_MODE, 2);
+         d = (d & 0xFF00) | firstChannel; // keep higher 8 bits which are ADClkPhase
+         Write(T_CTRL, REG_CHANNEL_MODE, &d, 2);
+      } else {
+         // upper four bits of register must contain last channel to read out starting from 9
+         Read(T_CTRL, &d, REG_CHANNEL_MODE, 2);
+         d = (d & 0xFF00) | (firstChannel << 4) | lastChannel; // keep higher 8 bits which are ADClkPhase
+         Write(T_CTRL, REG_CHANNEL_MODE, &d, 2);
+      }
+
+      // set bit pattern for write shift register
+      fChannelConfig = 0;
+      switch (nConfigChannels) {
+      case 1:
+         fChannelConfig = 0x01;
+         break;
+      case 2:
+         fChannelConfig = 0x11;
+         break;
+      case 4:
+         fChannelConfig = 0x55;
+         break;
+      case 8:
+         fChannelConfig = 0xFF;
+         break;
+      default:
+         printf("Invalid channel configuration\n");
+      }
+      d = fChannelConfig | (fDominoMode << 8) | (1 << 9) | (fWSRLoop << 10) | (0xF8 << 8);
+
+      Write(T_CTRL, REG_CHANNEL_CONFIG, &d, 2);
+   }
+
+   if (fBoardType == 6) {
+      if (fReadoutChannelConfig == 7)
+         fNumberOfReadoutChannels = 9;
+      else
+         fNumberOfReadoutChannels = 5;
+   } else {
+      fNumberOfReadoutChannels = lastChannel - firstChannel + 1;
+   }
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::SetNumberOfChannels(int nChannels)
+{
+   SetChannelConfig(0, nChannels - 1, 12);
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::SetADCClkPhase(int phase, bool invert)
+{
+   unsigned short d;
+
+   /* Set the clock phase of the ADC via the variable phase shift
+      in the Xilinx DCM. One unit is equal to the clock period / 256,
+      so at 30 MHz this is about 130ps. The possible range at 30 MHz
+      is -87 ... +87 */
+
+   // keep lower 8 bits which are the channel mode
+   Read(T_CTRL, &d, REG_ADCCLK_PHASE, 2);
+   d = (d & 0x00FF) | (phase << 8);
+   Write(T_CTRL, REG_ADCCLK_PHASE, &d, 2);
+
+   if (invert)
+      fCtrlBits |= BIT_ADCCLK_INVERT;
+   else
+      fCtrlBits &= ~BIT_ADCCLK_INVERT;
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+   fADCClkPhase = phase;
+   fADCClkInvert = invert;
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::SetWarmup(unsigned int microseconds)
+{
+   /* Set the "warmup" time. When starting the domino wave, the DRS4
+      chip together with its power supply need some time to stabilize
+      before high resolution data can be taken (jumping baseline
+      problem). This sets the time in ticks of 900ns before triggers
+      are accepted */
+
+   unsigned short ticks;
+
+   if (microseconds == 0)
+      ticks = 0;
+   else
+      ticks = (unsigned short) (microseconds / 0.9 + 0.5) - 1;
+   Write(T_CTRL, REG_WARMUP, &ticks, 2);
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::SetCooldown(unsigned int microseconds)
+{
+   /* Set the "cooldown" time. When stopping the domino wave, the 
+      power supply needs some time to stabilize before high resolution 
+      data can read out (slanted baseline problem). This sets the 
+      time in ticks of 900 ns before the readout is started */
+
+   unsigned short ticks;
+
+   ticks = (unsigned short) (microseconds / 0.9 + 0.5) - 1;
+   Write(T_CTRL, REG_COOLDOWN, &ticks, 2);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetDAC(unsigned char channel, double value)
+{
+   // Set DAC value
+   unsigned short d;
+
+   /* normalize to 2.5V for 16 bit */
+   if (value < 0)
+      value = 0;
+   if (value > 2.5)
+      value = 2.5;
+   d = static_cast < unsigned short >(value / 2.5 * 0xFFFF + 0.5);
+
+   Write(T_CTRL, REG_DAC_OFS + (channel * 2), &d, 2);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::ReadDAC(unsigned char channel, double *value)
+{
+   // Readback DAC value from control register
+   unsigned char buffer[2];
+
+   /* map 0->1, 1->0, 2->3, 3->2, etc. */
+   //ofs = channel + 1 - 2*(channel % 2);
+
+   Read(T_CTRL, buffer, REG_DAC_OFS + (channel * 2), 2);
+
+   /* normalize to 2.5V for 16 bit */
+   *value = 2.5 * (buffer[0] + (buffer[1] << 8)) / 0xFFFF;
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetRegulationDAC(double *value)
+{
+   // Get DAC value from status register (-> freq. regulation)
+   unsigned char buffer[2];
+
+   if (fBoardType == 1)
+      Read(T_STATUS, buffer, REG_RDAC3, 2);
+   else if (fBoardType == 2 || fBoardType == 3 || fBoardType == 4)
+      Read(T_STATUS, buffer, REG_RDAC1, 2);
+
+   /* normalize to 2.5V for 16 bit */
+   *value = 2.5 * (buffer[0] + (buffer[1] << 8)) / 0xFFFF;
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::StartDomino()
+{
+   // Start domino sampling
+   fCtrlBits |= BIT_START_TRIG;
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+   fCtrlBits &= ~BIT_START_TRIG;
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::Reinit()
+{
+   // Stop domino sampling
+   // reset readout state machine
+   // reset FIFO counters
+   fCtrlBits |= BIT_REINIT_TRIG;
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+   fCtrlBits &= ~BIT_REINIT_TRIG;
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::Init()
+{
+   // Init FPGA on USB2 board
+   InitFPGA();
+
+   // Default values
+
+   // Reinitialize
+   fCtrlBits |= BIT_REINIT_TRIG;        // reset readout state machine
+   if (fDRSType == 2)
+      fCtrlBits &= ~BIT_FREQ_AUTO_ADJ;  // turn auto. freq regul. off
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+   fCtrlBits &= ~BIT_REINIT_TRIG;
+
+   if (fBoardType == 1) {
+      // set max. domino speed
+      SetDAC(fDAC_DRA, 2.5);
+      SetDAC(fDAC_DSA, 2.5);
+      SetDAC(fDAC_DRB, 2.5);
+      SetDAC(fDAC_DSB, 2.5);
+      // set readout offset
+      SetDAC(fDAC_COFSA, 0.9);
+      SetDAC(fDAC_COFSB, 0.9);
+      SetDAC(fDAC_TLEVEL, 1.7);
+   } else if (fBoardType == 2 || fBoardType == 3) {
+      // set max. domino speed
+      SetDAC(fDAC_DSA, 2.5);
+      SetDAC(fDAC_DSB, 2.5);
+
+      // set readout offset
+      SetDAC(fDAC_COFS, 0.9);
+      SetDAC(fDAC_TLEVEL, 1.7);
+      SetDAC(fDAC_ADCOFS, 1.7); // 1.7 for DC coupling, 1.25 for AC
+      SetDAC(fDAC_CLKOFS, 1);
+   } else if (fBoardType == 4) {
+      // set max. domino speed
+      SetDAC(fDAC_DSA, 2.5);
+      SetDAC(fDAC_DSB, 2.5);
+
+      // set readout offset
+      SetDAC(fDAC_ROFS_1, 1.25);        // LVDS level
+      //SetDAC(fDAC_ROFS_2, 0.85);   // linear range  0.1V ... 1.1V
+      SetDAC(fDAC_ROFS_2, 1.05);        // differential input from Lecce splitter
+
+      SetDAC(fDAC_ADCOFS, 1.25);
+      SetDAC(fDAC_ACALIB, 0.5);
+      SetDAC(fDAC_INOFS, 0.6);
+      SetDAC(fDAC_BIAS, 0.70);  // a bit above the internal bias of 0.68V
+   
+   } else if (fBoardType == 5) {
+      // DRS4 USB Evaluation Board 1.1 + 2.0
+
+      // set max. domino speed
+      SetDAC(fDAC_DSA, 2.5);
+
+      // set readout offset
+      fROFS = 1.6;              // differential input range -0.5V ... +0.5V
+      fRange = 0;
+      SetDAC(fDAC_ROFS_1, fROFS);
+
+      // set common mode offset
+      fCommonMode = 0.8;        // 0.8V +- 0.5V inside NMOS range
+      SetDAC(fDAC_CMOFS, fCommonMode);
+
+      // calibration voltage
+      SetDAC(fDAC_CALP, fCommonMode);
+      SetDAC(fDAC_CALN, fCommonMode);
+
+      // OUT- offset
+      SetDAC(fDAC_ONOFS, 1.25);
+
+      SetDAC(fDAC_BIAS, 0.70);
+
+   } else if (fBoardType == 6) {
+      // DRS4 Mezzanine Board 1.0
+      
+      // set readout offset
+      fROFS = 1.6;              // differential input range -0.5V ... +0.5V
+      fRange = 0;
+      SetDAC(fDAC_ROFS_1, fROFS);
+
+      // set common mode offset
+      fCommonMode = 0.8;        // 0.8V +- 0.5V inside NMOS range
+      SetDAC(fDAC_CMOFSP, fCommonMode);
+      SetDAC(fDAC_CMOFSN, fCommonMode);
+
+      // calibration voltage
+      SetDAC(fDAC_CALN, fCommonMode);
+      SetDAC(fDAC_CALP, fCommonMode);
+
+      // OUT- offset
+      SetDAC(fDAC_ONOFS, 1.25);
+
+      SetDAC(fDAC_BIAS, 0.70);
+   }
+
+   /* set default number of channels per chip */
+   if (fDRSType == 4) {
+      if (fTransport == TR_USB2)
+         SetChannelConfig(0, fNumberOfReadoutChannels - 1, 8);
+      else
+         SetChannelConfig(7, fNumberOfReadoutChannels - 1, 8);
+   } else
+      SetChannelConfig(0, fNumberOfReadoutChannels - 1, 12);
+
+   // set ADC clock phase
+   if (fBoardType == 5) {
+      fADCClkPhase = 0;
+      fADCClkInvert = 0;
+   } else if (fBoardType == 6) {
+      fADCClkPhase = 60;
+      fADCClkInvert = 0;
+   }
+
+   // default settings
+   fDominoMode = 1;
+   fReadoutMode = 1;
+   fTriggerEnable1 = 0;
+   fTriggerEnable2 = 0;
+   fTriggerSource = 0;
+   fTriggerDelay = 0;
+   fFrequency = 1;
+   fDominoActive = 1;
+
+   // get some settings from hardware
+   fRange = GetCalibratedInputRange();
+   if (fRange < 0 || fRange > 0.5)
+      fRange = 0;
+   fFrequency = GetCalibratedFrequency();
+   if (fFrequency < 0.1 || fFrequency > 6)
+      fFrequency = 1;
+
+   SetDominoMode(fDominoMode);
+   SetReadoutMode(fReadoutMode);
+   EnableTrigger(fTriggerEnable1, fTriggerEnable2);
+   SetTriggerSource(fTriggerSource);
+   SetTriggerDelay(fTriggerDelay);
+   SetDominoActive(fDominoActive);
+   SetFrequency(fFrequency, true);
+   SetInputRange(fRange);
+   if (fBoardType == 5)
+      SelectClockSource(0); // FPGA clock
+   if (fBoardType == 6) {
+      SetADCClkPhase(fADCClkPhase, fADCClkInvert);
+      SetWarmup(0);
+      SetCooldown(100);
+   }
+
+   /* disable calibration signals */
+   EnableAcal(0, 0);
+   SetCalibTiming(0, 0);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetDominoMode(unsigned char mode)
+{
+   // Set domino mode
+   // mode == 0: single sweep
+   // mode == 1: run continously
+   //
+   fDominoMode = mode;
+
+   if (fDRSType == 4) {
+      unsigned short d;
+      Read(T_CTRL, &d, REG_CONFIG, 2);
+      fChannelConfig = d & 0xFF;
+
+      d = fChannelConfig | (fDominoMode << 8) | (1 << 9) | (fWSRLoop << 10) | (0xF8 << 8);
+      Write(T_CTRL, REG_CONFIG, &d, 2);
+   } else {
+      if (mode)
+         fCtrlBits |= BIT_DMODE;
+      else
+         fCtrlBits &= ~BIT_DMODE;
+
+      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+   }
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetDominoActive(unsigned char mode)
+{
+   // Set domino activity
+   // mode == 0: stop during readout
+   // mode == 1: keep domino wave running
+   //
+   fDominoActive = mode;
+   if (mode)
+      fCtrlBits |= BIT_DACTIVE;
+   else
+      fCtrlBits &= ~BIT_DACTIVE;
+
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetReadoutMode(unsigned char mode)
+{
+   // Set readout mode
+   // mode == 0: start from first bin
+   // mode == 1: start from domino stop
+   //
+   fReadoutMode = mode;
+   if (mode)
+      fCtrlBits |= BIT_READOUT_MODE;
+   else
+      fCtrlBits &= ~BIT_READOUT_MODE;
+
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SoftTrigger(void)
+{
+   // Send a software trigger
+   fCtrlBits |= BIT_SOFT_TRIG;
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+   fCtrlBits &= ~BIT_SOFT_TRIG;
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::EnableTrigger(int flag1, int flag2)
+{
+   // Enable external trigger
+   fTriggerEnable1 = flag1;
+   fTriggerEnable2 = flag2;
+   if (flag1)
+      fCtrlBits |= BIT_ENABLE_TRIGGER1;
+   else
+      fCtrlBits &= ~BIT_ENABLE_TRIGGER1;
+
+   if (flag2)
+      fCtrlBits |= BIT_ENABLE_TRIGGER2;
+   else
+      fCtrlBits &= ~BIT_ENABLE_TRIGGER2;
+
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetDelayedTrigger(int flag)
+{
+   // Select delayed trigger from trigger bus
+   if (flag)
+      fCtrlBits |= BIT_TRIGGER_DELAYED;
+   else
+      fCtrlBits &= ~BIT_TRIGGER_DELAYED;
+
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetTriggerLevel(double voltage, bool negative)
+{
+   if (fBoardType == 5) {
+      fTcalLevel = negative;
+
+      if (negative)
+         fCtrlBits |= BIT_NEG_TRIGGER;
+      else
+         fCtrlBits &= ~BIT_NEG_TRIGGER;
+
+      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+      return SetDAC(fDAC_TLEVEL, voltage/2 + 0.8);
+   }
+
+   return 0;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetTriggerDelay(int delay)
+{
+   short ticks;
+   int delay0;
+
+   if (fBoardType == 5 || fBoardType == 6) {
+      fTriggerDelay = delay;
+
+      if (fBoardType == 5) {
+         // Adjust trigger delay to middle of window
+         delay0 = (int) ((kNumberOfBins/fFrequency) / 2);
+         delay0 -= 33; // internal trigger delay is about 33 ns
+      } else
+         delay0 = 0; // treat delay as addition to minimal delay
+
+      // convert delay in ns into ticks, ~4*580 ps per quad LUT
+      ticks = (unsigned short) ((delay0 + delay) / 2.3 + 0.5);
+      if (ticks > 255)
+         ticks = 255;
+      if (ticks < 0)
+         ticks = 0;
+      Write(T_CTRL, REG_TRG_DELAY, &ticks, 2);
+
+      return 1;
+   }
+
+   return 0;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetTriggerSource(int source)
+{
+   // Set trigger source 
+   // 0=CH1, 1=CH2, 2=CH3, 3=CH4
+   if (source & 1)
+      fCtrlBits |= BIT_TR_SOURCE1;
+   else
+      fCtrlBits &= ~BIT_TR_SOURCE1;
+   if (source & 2)
+      fCtrlBits |= BIT_TR_SOURCE2;
+   else
+      fCtrlBits &= ~BIT_TR_SOURCE2;
+
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetDelayedStart(int flag)
+{
+   // Enable external trigger
+   fDelayedStart = flag;
+   if (flag)
+      fCtrlBits |= BIT_DELAYED_START;
+   else
+      fCtrlBits &= ~BIT_DELAYED_START;
+
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetTranspMode(int flag)
+{
+   // Enable/disable transparent mode
+   fTranspMode = flag;
+   if (flag)
+      fCtrlBits |= BIT_TRANSP_MODE;
+   else
+      fCtrlBits &= ~BIT_TRANSP_MODE;
+
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetStandbyMode(int flag)
+{
+   // Enable/disable standby mode
+   fTranspMode = flag;
+   if (flag)
+      fCtrlBits |= BIT_STANDBY_MODE;
+   else
+      fCtrlBits &= ~BIT_STANDBY_MODE;
+
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::IsBusy()
+{
+   // Get running flag
+   unsigned int status;
+
+   Read(T_STATUS, &status, REG_STATUS, 4);
+   return (status & BIT_RUNNING) > 0;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::IsPLLLocked()
+{
+   // Get running flag
+   unsigned int status;
+
+   Read(T_STATUS, &status, REG_STATUS, 4);
+   if (GetBoardType() == 6)
+      return ((status >> 1) & 0x0F) == 0x0F;
+   return (status & BIT_PLL_LOCKED0) > 0;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::IsLMKLocked()
+{
+   // Get running flag
+   unsigned int status;
+
+   Read(T_STATUS, &status, REG_STATUS, 4);
+   if (GetBoardType() == 6)
+      return (status & BIT_LMK_LOCKED) > 0;
+   return 0;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::IsNewFreq(unsigned char chipIndex)
+{
+   unsigned int status;
+
+   Read(T_STATUS, &status, REG_STATUS, 4);
+   if (chipIndex == 0)
+      return (status & BIT_NEW_FREQ1) > 0;
+   return (status & BIT_NEW_FREQ2) > 0;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::ReadFrequency(unsigned char chipIndex, double *f)
+{
+   if (fDRSType == 4) {
+
+      if (fBoardType == 6) {
+         *f = fFrequency;
+         return 1;
+      }
+
+      unsigned short ticks;
+
+      Read(T_CTRL, &ticks, REG_FREQ_SET, 2);
+      ticks += 2;
+
+      /* convert rounded ticks back to frequency */
+      if (ticks > 2)
+         *f = 1.024 / ticks * fRefClock;
+      else
+         *f = 0;
+   } else {
+      // Read domino sampling frequency
+      unsigned char buffer[2];
+
+      if (chipIndex == 0)
+         Read(T_STATUS, buffer, REG_FREQ1, 2);
+      else
+         Read(T_STATUS, buffer, REG_FREQ2, 2);
+
+      *f = (static_cast < unsigned int >(buffer[1]) << 8) +buffer[0];
+
+      /* convert counts to frequency */
+      if (*f != 0)
+         *f = 1024 * 200 * (32.768E6 * 4) / (*f) / 1E9;
+   }
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+double DRSBoard::VoltToFreq(double volt)
+{
+   if (fDRSType == 3) {
+      if (volt <= 1.2001)
+         return (volt - 0.6) / 0.2;
+      else
+         return 0.73 / 0.28 + sqrt((0.73 / 0.28) * (0.73 / 0.28) - 2.2 / 0.14 + volt / 0.14);
+   } else
+      return (volt - 0.5) / 0.2;
+}
+
+/*------------------------------------------------------------------*/
+
+double DRSBoard::FreqToVolt(double freq)
+{
+   if (fDRSType == 3) {
+      if (freq <= 3)
+         return 0.6 + 0.2 * freq;
+      else
+         return 2.2 - 0.73 * freq + 0.14 * freq * freq;
+   } else
+      return 0.55 + 0.25 * freq;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::ConfigureLMK(double sampFreq, bool freqChange, int calFreq, int calPhase)
+{
+   unsigned int data[] = { 0x80000100,   // RESET=1
+                           0x0007FF00,   // CLKOUT0: EN=1, DIV=FF (=510) MUX=Div&Delay
+                           0x00000101,   // CLKOUT1: Disabled
+                           0x0082000B,   // R11: DIV4=0
+                           0x028780AD,   // R13: VCO settings
+                           0x0830000E,   // R14: PLL settings
+                           0xC000000F }; // R15: PLL settings
+
+   /* calculate dividing ratio */
+   int divider, vco_divider, n_counter, r_counter;
+   unsigned int status;
+   double clk, vco;
+
+   if (fTransport == TR_USB2) {
+      /* 30 MHz clock */
+      data[4]     = 0x028780AD;  // R13 according to CodeLoader 4
+      clk         = 30;
+      if (sampFreq < 1) {
+         r_counter   = 1;
+         vco_divider = 8;
+         n_counter   = 5;
+      } else {
+         r_counter   = 1;
+         vco_divider = 5;
+         n_counter   = 8;
+      }
+   } else {
+      
+      if (fCtrlBits & BIT_REFCLK_SOURCE) {
+         /* 19.44 MHz clock */
+         data[4]     = 0x0284C0AD;  // R13 according to CodeLoader 4
+         clk         = 19.44; // global clock through P2
+
+         r_counter   = 2;
+         vco_divider = 8;
+         n_counter   = 16;
+      } else {
+         /* 33 MHz clock */
+         data[4]     = 0x028840AD;  // R13 according to CodeLoader 4
+         clk         = 33; // FPGA clock
+
+         r_counter   = 2;
+         vco_divider = 8;
+         n_counter   = 9;
+      }
+   }
+
+   vco = clk/r_counter*n_counter*vco_divider;
+   divider = (int) ((vco / vco_divider / (sampFreq/2.048) / 2.0) + 0.5);
+
+   /* return exact frequency */
+   fFrequency = vco/vco_divider/(divider*2)*2.048;
+
+   /* return exact timing calibration frequency */
+   fTCALFrequency = vco/vco_divider;
+
+   /* change registers accordingly */
+   data[1] = 0x00070000 | (divider << 8);   // R0
+   data[5] = 0x0830000E | (r_counter << 8); // R14
+   data[6] = 0xC000000F | (n_counter << 8) | (vco_divider << 26); // R15
+
+   /* enable TCA output if requested */
+   if (calFreq) {
+      if (calFreq == 1)
+         data[2] = 0x00050001 | (  1<<8) ; // 148.5 MHz  (33 MHz PLL)
+                                           // 150 MHz    (30 MHz PLL)
+                                           // 155.52 MHz (19.44 MHz PLL)
+      else if (calFreq == 2)
+         data[2] = 0x00070001 | (  4<<8);  // above values divided by 8
+      else if (calFreq == 3)
+         data[2] = 0x00070001 | (255<<8);  // above values divided by 510
+   }
+
+   /* set delay to adjsut phase */
+   if (calPhase > 0)
+      data[2] |= (( calPhase & 0x0F) << 4);
+   else if (calPhase < 0)
+      data[1] |= ((-calPhase & 0x0F) << 4);
+
+   if (freqChange) {
+      /* set all registers */    
+      for (int i=0 ; i<(int)(sizeof(data)/sizeof(unsigned int)) ; i++) {
+         Write(T_CTRL, REG_LMK_LSB, &data[i], 2);
+         Write(T_CTRL, REG_LMK_MSB, ((char *)&data[i])+2, 2);
+         // poll on serial_busy flag
+         for (int j=0 ; j<100 ; j++) {
+            Read(T_STATUS, &status, REG_STATUS, 4);
+            if ((status & BIT_SERIAL_BUSY) == 0)
+               break;
+         }
+      }
+   } else {
+      /* only enable/disable timing calibration frequency */
+      Write(T_CTRL, REG_LMK_LSB, &data[1], 2);
+      Write(T_CTRL, REG_LMK_MSB, ((char *)&data[1])+2, 2);
+
+      /* poll on serial_busy flag */
+      for (int j=0 ; j<100 ; j++) {
+         Read(T_STATUS, &status, REG_STATUS, 4);
+         if ((status & BIT_SERIAL_BUSY) == 0)
+            break;
+      }
+
+      Write(T_CTRL, REG_LMK_LSB, &data[2], 2);
+      Write(T_CTRL, REG_LMK_MSB, ((char *)&data[2])+2, 2);
+
+      /* poll on serial_busy flag */
+      for (int j=0 ; j<100 ; j++) {
+         Read(T_STATUS, &status, REG_STATUS, 4);
+         if ((status & BIT_SERIAL_BUSY) == 0)
+            break;
+      }
+   }
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetFrequency(double demand, bool wait)
+{
+   // Set domino sampling frequency
+   double freq, voltage, delta_voltage;
+   unsigned short ticks;
+   int i, index, timeout;
+   int dominoModeSave = fDominoMode;
+   int triggerEnableSave1 = fTriggerEnable1;
+   int triggerEnableSave2 = fTriggerEnable2;
+
+   if (fDRSType == 4) {
+      /* allowed range is 100 MHz to 6 GHz */
+      if (demand > 6 || demand < 0.1)
+         return 0;
+
+      if (fBoardType == 6)
+         return ConfigureLMK(demand, true, fTcalFreq, fTcalPhase);
+
+      /* convert frequency in GHz into ticks counted by reference clock */
+      if (demand == 0)
+         ticks = 0;             // turn off frequency generation
+      else
+         ticks = static_cast < unsigned short >(1.024 / demand * fRefClock + 0.5);
+
+      ticks -= 2;               // firmware counter need two additional clock cycles
+      Write(T_CTRL, REG_FREQ_SET, &ticks, 2);
+      ticks += 2;
+
+      /* convert rounded ticks back to frequency */
+      if (demand > 0)
+         demand = 1.024 / ticks * fRefClock;
+      fFrequency = demand;
+
+      /* wait for PLL lock if asekd */
+      if (wait) {
+         StartDomino();
+         for (i=0 ; i<1000 ; i++)
+         if (GetStatusReg() & BIT_PLL_LOCKED0)
+            break;
+         if (i==100) {
+            printf("PLL did not lock for frequency %lf\n", demand);
+            return 0;
+         }
+      }
+   } else {                     // fDRSType == 4
+      SetDominoMode(1);
+      EnableTrigger(0, 0);
+      EnableAcal(0, 0);
+
+      fFrequency = demand;
+
+      /* turn automatic adjustment off */
+      fCtrlBits &= ~BIT_FREQ_AUTO_ADJ;
+
+      /* disable external trigger */
+      fCtrlBits &= ~BIT_ENABLE_TRIGGER1;
+      fCtrlBits &= ~BIT_ENABLE_TRIGGER2;
+
+      /* set start pulse length for future DRSBoard_domino_start() */
+      if (fDRSType == 2) {
+         if (demand < 0.8)
+            fCtrlBits |= BIT_LONG_START_PULSE;
+         else
+            fCtrlBits &= ~BIT_LONG_START_PULSE;
+
+         Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+      }
+
+      /* stop any running domino wave */
+      Reinit();
+
+      /* estimate DAC setting */
+      voltage = FreqToVolt(demand);
+
+      SetDAC(fDAC_DSA, voltage);
+      SetDAC(fDAC_DSB, voltage);
+
+      /* wait until new DAC value has settled */
+      Sleep(10);
+
+      /* restart domino wave */
+      StartDomino();
+
+      ticks = static_cast < unsigned short >(1024 * 200 * (32.768E6 * 4) / demand / 1E9);
+
+      /* iterate over both DRS chips */
+      for (index = 0; index < 2; index++) {
+
+         /* starting voltage */
+         voltage = FreqToVolt(demand);
+
+         for (i = 0; i < 100; i++) {
+
+            /* wait until measurement finished */
+            for (timeout = 0; timeout < 1000; timeout++)
+               if (IsNewFreq(index))
+                  break;
+
+            freq = 0;
+            if (timeout == 1000)
+               break;
+
+            ReadFrequency(index, &freq);
+
+            delta_voltage = FreqToVolt(demand) - FreqToVolt(freq);
+
+            if (fDebug) {
+               if (fabs(freq - demand) < 0.001)
+                  printf("CHIP-%d, iter%3d: %1.5lf(%05d) %7.5lf\n", index, i, voltage,
+                         static_cast < int >(voltage / 2.5 * 65535 + 0.5), freq);
+               else
+                  printf("CHIP-%d, iter%3d: %1.5lf(%05d) %7.5lf %+5d\n", index, i, voltage,
+                         static_cast < int >(voltage / 2.5 * 65535 + 0.5), freq,
+                         static_cast < int >(delta_voltage / 2.5 * 65535 + 0.5));
+            }
+
+            if (fabs(freq - demand) < 0.001)
+               break;
+
+            voltage += delta_voltage;
+            if (voltage > 2.5)
+               voltage = 2.5;
+            if (voltage < 0)
+               voltage = 0;
+
+            if (freq == 0)
+               break;
+
+            if (index == 0)
+               SetDAC(fDAC_DSA, voltage);
+            else
+               SetDAC(fDAC_DSB, voltage);
+
+            Sleep(10);
+         }
+         if (i == 100 || freq == 0 || timeout == 1000) {
+            printf("Board %d --> Could not set frequency of CHIP-#%d to %1.3f GHz\n", GetBoardSerialNumber(),
+                   index, demand);
+            return 0;
+         }
+      }
+
+      SetDominoMode(dominoModeSave);
+      EnableTrigger(triggerEnableSave1, triggerEnableSave2);
+   }
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::RegulateFrequency(double demand)
+{
+   // Set frequency regulation
+   unsigned short target, target_hi, target_lo;
+
+   if (demand < 0.42 || demand > 5.2)
+      return 0;
+
+   fFrequency = demand;
+
+   /* first iterate DAC value from host */
+   if (!SetFrequency(demand, true))
+      return 0;
+
+   /* convert frequency in GHz into counts for 200 cycles */
+   target = static_cast < unsigned short >(1024 * 200 * (32.768E6 * 4) / demand / 1E9);
+   target_hi = target + 6;
+   target_lo = target - 6;
+   Write(T_CTRL, REG_FREQ_SET_HI, &target_hi, 2);
+   Write(T_CTRL, REG_FREQ_SET_LO, &target_lo, 2);
+
+   /* turn on regulation */
+   fCtrlBits |= BIT_FREQ_AUTO_ADJ;
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+   /* optional monitoring code ... */
+#if 0
+   do {
+      double freq;
+      unsigned short dac, cnt;
+
+      ReadFrequency(0, &freq);
+
+      if (fBoardType == 1)
+         Read(T_STATUS, &dac, REG_RDAC3, 2);
+      else if (fBoardType == 2 || fBoardType == 3)
+         Read(T_STATUS, &dac, REG_RDAC1, 2);
+
+      Read(T_STATUS, &cnt, REG_FREQ1, 2);
+
+      if (cnt < 65535)
+         printf("%5d %5d %5d %1.5lf\n", dac, target, cnt, freq);
+
+      Sleep(500);
+   } while (1);
+#endif
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::RegisterTest()
+{
+   // Register test
+#define N_REG 8
+
+   int i, n, n_err;
+   unsigned int buffer[N_REG], ret[N_REG];
+
+   /* test single register */
+   buffer[0] = 0x12345678;
+   Write(T_CTRL, 0, buffer, 4);
+   memset(ret, 0, sizeof(ret));
+   i = Read(T_CTRL, ret, 0, 4);
+   while (i != 4)
+      printf("Read error single register!\n");
+
+   printf("Reg.0: %08X - %08X\n", buffer[0], ret[0]);
+
+   n_err = 0;
+   for (n = 0; n < 100; n++) {
+      for (i = 0; i < N_REG; i++)
+         buffer[i] = (rand() << 16) | rand();
+      Write(T_CTRL, 0, buffer, sizeof(buffer));
+
+      memset(ret, 0, sizeof(ret));
+      i = Read(T_CTRL, ret, 0, sizeof(ret));
+      while (i != sizeof(ret)) {
+         printf("Read error!\n");
+         return;
+      }
+
+      for (i = 0; i < N_REG; i++) {
+         if (n == 0)
+            printf("Reg.%d: %08X - %08X\n", i, buffer[i], ret[i]);
+         if (buffer[i] != ret[i]) {
+            n_err++;
+         }
+      }
+   }
+
+   printf("Register test: %d errors\n", n_err);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::RAMTest(int flag)
+{
+#define MAX_N_BYTES  128*1024   // 128 kB
+
+   int i, j, n, bits, n_bytes, n_words, n_dwords;
+   unsigned int buffer[MAX_N_BYTES/4], ret[MAX_N_BYTES/4];
+   time_t now;
+
+   if (fBoardType == 6 && fTransport == TR_VME) {
+      bits = 32;
+      n_bytes = 128*1024; // test full 128 kB
+      n_words = n_bytes/2;
+      n_dwords = n_words/2;
+   }  else {
+      bits = 24;
+      n_words = 9*1024;
+      n_bytes = n_words * 2;
+      n_dwords = n_words/2;
+   }
+
+   if (flag & 1) {
+      /* integrety test */
+      printf("Buffer size: %d (%1.1lfk)\n", n_words * 2, n_words * 2 / 1024.0);
+      if (flag & 1) {
+         for (i = 0; i < n_dwords; i++) {
+            if (bits == 24)
+               buffer[i] = (rand() | rand() << 16) & 0x00FFFFFF;   // random 24-bit values
+            else
+               buffer[i] = (rand() | rand() << 16);                // random 32-bit values
+         }
+
+         Reinit();
+         Write(T_RAM, 0, buffer, n_bytes);
+         memset(ret, 0, n_bytes);
+         Read(T_RAM, ret, 0, n_bytes);
+         Reinit();
+
+         for (i = n = 0; i < n_dwords; i++) {
+            if (buffer[i] != ret[i]) {
+               n++;
+            }
+            if (i < 10)
+               printf("written: %08X   read: %08X\n", buffer[i], ret[i]);
+         }
+
+         printf("RAM test: %d errors\n", n);
+      }
+   }
+
+   /* speed test */
+   if (flag & 2) {
+      /* read continously to determine speed */
+      time(&now);
+      while (now == time(NULL));
+      time(&now);
+      i = n = 0;
+      do {
+         memset(ret, 0, n_bytes);
+
+         for (j = 0; j < 10; j++) {
+            Read(T_RAM, ret, 0, n_bytes);
+            i += n_bytes;
+         }
+
+         if (flag & 1) {
+            for (j = 0; j < n_dwords; j++)
+               if (buffer[j] != ret[j])
+                  n++;
+         }
+
+         if (now != time(NULL)) {
+            if (flag & 1)
+               printf("%d read/sec, %1.2lf MB/sec, %d errors\n", static_cast < int >(i / n_bytes),
+                      i / 1024.0 / 1024.0, n);
+            else
+               printf("%d read/sec, %1.2lf MB/sec\n", static_cast < int >(i / n_bytes),
+                      i / 1024.0 / 1024.0);
+            time(&now);
+            i = 0;
+         }
+
+         if (drs_kbhit())
+            break;
+
+      } while (1);
+
+      while (drs_kbhit())
+         getch();
+   }
+
+   return 0;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::ChipTest()
+{
+   int i, j, t;
+   double freq, old_freq, min, max, mean, rms;
+   float  waveform[1024];
+
+   Init();
+   SetChannelConfig(0, 8, 8);
+   SetDominoMode(1);
+   SetReadoutMode(1);
+   SetDominoActive(1);
+   SetTranspMode(0);
+   EnableTrigger(0, 0);
+   EnableTcal(1, 0);
+   SelectClockSource(0);
+   EnableAcal(1, 0);
+
+   /* test 1 GHz */
+   SetFrequency(1, true);
+   StartDomino();
+   Sleep(100);
+   if (!(GetStatusReg() & BIT_PLL_LOCKED0)) {
+      puts("PLL did not lock at 1 GHz");
+      return 0;
+   }
+
+   /* test up to 6 GHz */
+   for (freq = 5 ; freq < 6 ; freq += 0.1) {
+      SetFrequency(freq, false);
+      Sleep(10);
+      if (!(GetStatusReg() & BIT_PLL_LOCKED0)) {
+         printf("Max. frequency is %1.1lf GHz\n", old_freq);
+         break;
+      }
+      ReadFrequency(0, &old_freq);
+   }
+
+   /* read and check at 0 calibration voltage */
+   SetFrequency(5, true);
+   Sleep(10);
+   SoftTrigger();
+   while (IsBusy());
+   TransferWaves(0, 8);
+
+   for (i=0 ; i<8 ; i++) {
+      t = GetStopCell(0);
+      GetWave(0, i, waveform, false, t, false);
+      for (j=0 ; j<1024; j++)
+         if (waveform[j] < -100 || waveform[j] > 100) {
+            if (j<5) {
+               /* skip this cells */
+            } else {
+               printf("Cell error on channel %d, cell %d: %1.1lf mV instead 0 mV\n", i, j, waveform[j]);
+               return 0;
+            }
+         }
+   }
+
+   /* read and check at +0.5V calibration voltage */
+   EnableAcal(1, 0.5);
+   StartDomino();
+   SoftTrigger();
+   while (IsBusy());
+   TransferWaves(0, 8);
+
+   for (i=0 ; i<8 ; i++) {
+      t = GetStopCell(0);
+      GetWave(0, i, waveform, false, t, false);
+      for (j=0 ; j<1024; j++)
+         if (waveform[j] < 350) {
+            if (j<5) {
+               /* skip this cell */
+            } else {
+               printf("Cell error on channel %d, cell %d: %1.1lf mV instead 400 mV\n", i, j, waveform[j]);
+               return 0;
+            }
+         }
+   }
+
+   /* read and check at -0.5V calibration voltage */
+   EnableAcal(1, -0.5);
+   StartDomino();
+   Sleep(10);
+   SoftTrigger();
+   while (IsBusy());
+   TransferWaves(0, 8);
+
+   for (i=0 ; i<8 ; i++) {
+      t = GetStopCell(0);
+      GetWave(0, i, waveform, false, t, false);
+      for (j=0 ; j<1024; j++)
+         if (waveform[j] > -350) {
+            if (j<5) {
+               /* skip this cell */
+            } else {
+               printf("Cell error on channel %d, cell %d: %1.1lf mV instead -400mV\n", i, j, waveform[j]);
+               return 0;
+            }
+         }
+   }
+
+   /* check clock channel */
+   GetWave(0, 8, waveform, false, 0);
+   min = max = mean = rms = 0;
+   for (j=0 ; j<1024 ; j++) {
+      if (waveform[j] > max)
+         max = waveform[j];
+      if (waveform[j] < min)
+         min = waveform[j];
+      mean += waveform[j];
+   }
+   mean /= 1024.0;
+   for (j=0 ; j<1024 ; j++)
+      rms += (waveform[j] - mean) * (waveform[j] - mean);
+   rms = sqrt(rms/1024);
+
+   if (max - min < 400) {
+      printf("Error on clock channel amplitude: %1.1lf mV\n", max-min);
+      return 0;
+   }
+
+   if (rms < 100 || rms > 300) {
+      printf("Error on clock channel RMS: %1.1lf mV\n", rms);
+      return 0;
+   }
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::SetVoltageOffset(double offset1, double offset2)
+{
+   if (fDRSType == 3) {
+      SetDAC(fDAC_ROFS_1, 0.95 - offset1);
+      SetDAC(fDAC_ROFS_2, 0.95 - offset2);
+   } else if (fDRSType == 2)
+      SetDAC(fDAC_COFS, 0.9 - offset1);
+
+   // let DAC settle
+   Sleep(100);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetInputRange(double center)
+{
+   if (fBoardType == 5 || fBoardType == 6) {
+      // DRS4 USB Evaluation Board 1.1 + Mezzanine Board
+
+      // only allow -0.5...0.5 to 0...1.0
+      if (center < 0 || center > 0.5)
+         return 0;
+
+      // remember range
+      fRange = center;
+
+      // correct for sampling cell charge injection
+      center *= 1.125;
+
+      // set readout offset
+      fROFS = 1.6 - center;
+      SetDAC(fDAC_ROFS_1, fROFS);
+   }
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetExternalClockFrequency(double frequencyMHz)
+{
+   // Set the frequency of the external clock
+   fExternalClockFrequency = frequencyMHz;
+   return 0;
+}
+
+/*------------------------------------------------------------------*/
+
+double DRSBoard::GetExternalClockFrequency()
+{
+   // Return the frequency of the external clock
+   return fExternalClockFrequency;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::TransferWaves(int numberOfChannels)
+{
+   return TransferWaves(fWaveforms, numberOfChannels);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::TransferWaves(unsigned char *p, int numberOfChannels)
+{
+   return TransferWaves(p, 0, numberOfChannels - 1);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::TransferWaves(int firstChannel, int lastChannel)
+{
+   int offset;
+
+   if (fTransport == TR_USB)
+      offset = firstChannel * sizeof(short int) * kNumberOfBins;
+   else
+      offset = 0;               //in VME and USB2, always start from zero
+
+   return TransferWaves(fWaveforms + offset, firstChannel, lastChannel);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::TransferWaves(unsigned char *p, int firstChannel, int lastChannel)
+{
+   // Transfer all waveforms at once from VME or USB to location
+   int n, offset, n_requested;
+
+   if (lastChannel >= fNumberOfChips * fNumberOfChannels)
+      lastChannel = fNumberOfChips * fNumberOfChannels - 1;
+   if (lastChannel < 0) {
+      printf("Error: Invalid channel index %d\n", lastChannel);
+      return 0;
+   }
+
+   if (firstChannel < 0 || firstChannel > fNumberOfChips * fNumberOfChannels) {
+      printf("Error: Invalid channel index %d\n", firstChannel);
+      return 0;
+   }
+
+   if (fTransport == TR_VME) {
+      /* in VME, always transfer all waveforms, since channels sit 'next' to each other */
+      firstChannel = 0;
+      lastChannel = fNumberOfChips * fNumberOfChannels - 1;
+      if (fReadoutChannelConfig == 4)
+         lastChannel = fNumberOfChips * 5 - 1; // special mode to read only even channels + clock
+   }
+
+   else if (fTransport == TR_USB2) {
+      /* USB2 FPGA contains 9 (Eval) or 10 (Mezz) channels */
+      firstChannel = 0;
+      if (fBoardType == 5)
+         lastChannel = 8;
+      else if (fBoardType == 6)
+         lastChannel = 9;
+   }
+
+   else if (fTransport == TR_USB) {
+      /* USB1 FPGA contains only 16 channels */
+      if (lastChannel > 15)
+         lastChannel = 15;
+   }
+
+   n_requested = (lastChannel - firstChannel + 1) * sizeof(short int) * kNumberOfBins;
+   offset = firstChannel * sizeof(short int) * kNumberOfBins;
+
+   n = Read(T_RAM, p, offset, n_requested);
+   if (n != n_requested) {
+      printf("Error: only %d bytes out of %d read\n", n, n_requested);
+      return n;
+   }
+
+   // read trigger cells
+   if (fDRSType == 4) {
+      if (fBoardType == 5)
+         Read(T_STATUS, fStopCell, REG_STOP_CELL0, 2);
+      else {
+         Read(T_STATUS, fStopCell,   REG_STOP_CELL0, 2);
+         Read(T_STATUS, fStopCell+1, REG_STOP_CELL1, 2);
+         Read(T_STATUS, fStopCell+2, REG_STOP_CELL2, 2);
+         Read(T_STATUS, fStopCell+3, REG_STOP_CELL3, 2);
+      }
+   }
+  
+   return n;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::DecodeWave(unsigned int chipIndex, unsigned char channel, unsigned short *waveform)
+{
+   return DecodeWave(fWaveforms, chipIndex, channel, waveform);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::DecodeWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel,
+                         unsigned short *waveform)
+{
+   // Get waveform
+   int i, offset=0, ind;
+
+   /* check valid parameters */
+   assert((int)channel < fNumberOfChannels);
+   assert((int)chipIndex < fNumberOfChips);
+
+   /* remap channel */
+   if (fBoardType == 1) {
+      if (channel < 8)
+         channel = 7 - channel;
+      else
+         channel = 16 - channel;
+   } else if (fBoardType == 6) {
+      if (fReadoutChannelConfig == 7) {
+         if (channel < 8)
+            channel = 7-channel;
+      } else if (fReadoutChannelConfig == 4) {
+         if (channel == 8)
+            channel = 4;
+         else
+            channel = 3 - channel/2;
+      } else {
+         channel = channel / 2;
+         if (channel != 4)
+           channel = 3-channel;
+      }
+   } else
+      channel = channel;
+
+   // Read channel
+   if (fTransport == TR_USB) {
+      offset = kNumberOfBins * 2 * (chipIndex * 16 + channel);
+      for (i = 0; i < kNumberOfBins; i++) {
+         // 12-bit data
+         waveform[i] = ((waveforms[i * 2 + 1 + offset] & 0x0f) << 8) + waveforms[i * 2 + offset];
+      }
+   } else if (fTransport == TR_USB2) {
+
+      if (fBoardType == 5)
+         // see dpram_map_eval1.xls
+         offset = kNumberOfBins * 2 * (chipIndex * 16 + channel);
+      else if (fBoardType == 6) {
+         // see dpram_map_mezz1.xls mode 0-3
+         offset = (kNumberOfBins * 4) * (channel % 9) + 2 * (chipIndex/2);
+      }
+      for (i = 0; i < kNumberOfBins; i++) {
+         // 16-bit data
+         if (fBoardType == 5)
+            waveform[i] = ((waveforms[i * 2 + 1 + offset] & 0xff) << 8) + waveforms[i * 2 + offset];
+         else if (fBoardType == 6)
+            waveform[i] = ((waveforms[i * 4 + 1 + offset] & 0xff) << 8) + waveforms[i * 4 + offset];
+      }
+   } else if (fTransport == TR_VME) {
+
+      if (fBoardType == 6) {
+         if (fReadoutChannelConfig == 7)       // see dpram_map_mezz1.xls mode 7
+            offset = (kNumberOfBins * 4) * (channel % 9 + 9*(chipIndex % 2)) + 2 * (chipIndex/2);
+         else if (fReadoutChannelConfig == 4)  // see dpram_map_mezz1.xls mode 4
+            offset = (kNumberOfBins * 4) * (channel % 5 + 5*(chipIndex % 2)) + 2 * (chipIndex/2);
+         for (i = 0; i < kNumberOfBins; i++)
+            waveform[i] = ((waveforms[i * 4 + 1 + offset] & 0xff) << 8) + waveforms[i * 4 + offset];
+      } else {
+         offset = (kNumberOfBins * 4) * channel;
+         for (i = 0; i < kNumberOfBins; i++) {
+            ind = i * 4 + offset;
+            if (chipIndex == 0)
+               // lower 12 bit
+               waveform[i] = ((waveforms[ind + 1] & 0x0f) << 8) | waveforms[ind];
+            else
+               // upper 12 bit
+               waveform[i] = (waveforms[ind + 2] << 4) | (waveforms[ind + 1] >> 4);
+         }
+      }
+   } else {
+      printf("Error: invalid transport %d\n", fTransport);
+      return kInvalidTransport;
+   }
+   return kSuccess;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetWave(unsigned int chipIndex, unsigned char channel, float *waveform)
+{
+   return GetWave(chipIndex, channel, waveform, true, fStopCell[chipIndex], false, 0, true);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetWave(unsigned int chipIndex, unsigned char channel, short *waveform, bool responseCalib,
+                      int triggerCell, bool adjustToClock, float threshold, bool offsetCalib)
+{
+   return GetWave(fWaveforms, chipIndex, channel, waveform, responseCalib, triggerCell, adjustToClock,
+                  threshold, offsetCalib);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetWave(unsigned int chipIndex, unsigned char channel, float *waveform, bool responseCalib,
+                      int triggerCell, bool adjustToClock, float threshold, bool offsetCalib)
+{
+   int ret, i;
+   short waveS[kNumberOfBins];
+   ret =
+       GetWave(fWaveforms, chipIndex, channel, waveS, responseCalib, triggerCell, adjustToClock, threshold,
+               offsetCalib);
+   if (responseCalib)
+      for (i = 0; i < kNumberOfBins; i++)
+         waveform[i] = static_cast < float >(static_cast <short> (waveS[i]) * GetPrecision());
+   else {
+      for (i = 0; i < kNumberOfBins; i++) {
+         if (fBoardType == 4 || fBoardType == 5 || fBoardType == 6) {
+            waveform[i] = static_cast < float >(waveS[i] * GetPrecision());
+         } else
+            waveform[i] = static_cast < float >(waveS[i]);
+      }
+   }
+   return ret;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel,
+                      float *waveform, bool responseCalib, int triggerCell, bool adjustToClock,
+                      float threshold, bool offsetCalib)
+{
+   int ret, i;
+   short waveS[kNumberOfBins];
+   ret =
+       GetWave(waveforms, chipIndex, channel, waveS, responseCalib, triggerCell, adjustToClock, threshold,
+               offsetCalib);
+
+   if (fBoardType == 4) {
+      for (i = 0; i < kNumberOfBins; i++)
+         waveform[i] = static_cast < float >(waveS[i] / 65.535); // 16-bit corresponding to 1V
+   } else {
+      if (responseCalib) {
+         for (i = 0; i < kNumberOfBins; i++)
+            waveform[i] = static_cast < float >(waveS[i] * GetPrecision());
+      } else {
+         for (i = 0; i < kNumberOfBins; i++) {
+            waveform[i] = static_cast < float >(waveS[i]);
+         }
+      }
+   }
+
+   return ret;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel,
+                      short *waveform, bool responseCalib, int triggerCell, bool adjustToClock,
+                      float threshold, bool offsetCalib)
+{
+   unsigned short adcWaveform[kNumberOfBins];
+   int ret = DecodeWave(waveforms, chipIndex, channel, adcWaveform);
+   if (ret != kSuccess)
+      return ret;
+
+   return CalibrateWaveform(chipIndex, channel, adcWaveform, waveform, responseCalib,
+                            triggerCell, adjustToClock, threshold, offsetCalib);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetRawWave(unsigned int chipIndex, unsigned char channel, unsigned short *waveform,
+                         bool adjustToClock)
+{
+   return GetRawWave(fWaveforms, chipIndex, channel, waveform, adjustToClock);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetRawWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel, 
+                         unsigned short *waveform, bool adjustToClock)
+{
+   int i, status, tc;
+   unsigned short wf[kNumberOfBins];
+
+   status = DecodeWave(waveforms, chipIndex, channel, wf);
+
+   if (adjustToClock) {
+      tc = GetTriggerCell(chipIndex);
+      for (i = 0 ; i < kNumberOfBins; i++)
+         waveform[(i + tc) % kNumberOfBins] = wf[i];
+   } else {
+      for (i = 0 ; i < kNumberOfBins; i++)
+         waveform[i] = wf[i];
+   }
+
+   return status;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::CalibrateWaveform(unsigned int chipIndex, unsigned char channel, unsigned short *adcWaveform,
+                                short *waveform, bool responseCalib,
+                                int triggerCell, bool adjustToClock, float threshold, bool offsetCalib)
+{
+   int j;
+   double value;
+   short left, right;
+
+   // calibrate waveform
+   if (responseCalib) {
+      if (GetDRSType() == 4) {
+         // if Mezz though USB2 -> select correct calibration channel
+         if (fBoardType == 6 && (fReadoutChannelConfig == 0 || fReadoutChannelConfig == 2) &&
+             channel != 8)
+            channel++;
+
+         // Channel readout mode #4 -> select correct calibration channel
+         if (fBoardType == 6 && fReadoutChannelConfig == 4 && channel % 2 == 0 && channel != 8)
+            channel++;
+
+         for (j = 0 ; j < kNumberOfBins; j++) {
+            if (fCellCalibrationValid) {
+               value = adcWaveform[j] - fCellOffset[channel+chipIndex*9][(j + triggerCell) % kNumberOfBins];
+               value = value / fCellGain[channel+chipIndex*9][(j + triggerCell) % kNumberOfBins];
+               if (offsetCalib && channel != 8)
+                  value = value - fCellOffset2[channel+chipIndex*9][j] + 32768;
+            } else {
+               value = adcWaveform[j];
+            }
+            /* convert to units of 0.1 mV */
+            value = value / 65536.0 * 1000 * 10; 
+
+            /* apply clipping */
+            if (channel != 8) {
+               if (adcWaveform[j] >= 0xFFF0 || value > (fRange * 1000 + 500) * 10)
+                  value = (fRange * 1000 + 500) * 10;
+               if (adcWaveform[j] <  0x0010 || value < (fRange * 1000 - 500) * 10)
+                  value = (fRange * 1000 - 500) * 10;
+            }
+
+            if (adjustToClock)          
+               waveform[(j + triggerCell) % kNumberOfBins] = (short) (value + 0.5);
+            else
+               waveform[j] = (short) (value + 0.5); 
+         }
+
+         // check for stuck pixels and replace by average of neighbors
+         if (fCellCalibrationValid) {
+            for (j = 0 ; j < kNumberOfBins; j++) {
+               if (adjustToClock) {
+                  if (fCellOffset[channel+chipIndex*9][j] == 0) {
+                     left = waveform[(j-1+kNumberOfBins) % kNumberOfBins];
+                     right = waveform[(j+1) % kNumberOfBins];
+                     waveform[j] = (short) ((left+right)/2);
+                  }
+               } else {
+                  if (fCellOffset[channel+chipIndex*9][(j + triggerCell) % kNumberOfBins] == 0) {
+                     left = waveform[(j-1+kNumberOfBins) % kNumberOfBins];
+                     right = waveform[(j+1) % kNumberOfBins];
+                     waveform[j] = (short) ((left+right)/2);
+                  }
+               }
+            }
+         }
+
+      } else {
+         if (!fResponseCalibration->
+             Calibrate(chipIndex, channel % 10, adcWaveform, waveform, triggerCell, threshold, offsetCalib))
+            return kZeroSuppression;       // return immediately if below threshold
+      }
+   } else {
+      if (GetDRSType() == 4) {
+         // if Mezz though USB2 -> select correct calibration channel
+         if (fBoardType == 6 && (fReadoutChannelConfig == 0 || fReadoutChannelConfig == 2) &&
+             channel != 8)
+            channel++;
+         for (j = 0 ; j < kNumberOfBins; j++) {
+            value = adcWaveform[j];
+
+            /* convert to units of 0.1 mV */
+            value = (value - 32768) / 65536.0 * 1000 * 10; 
+
+            /* correct for range */
+            value += fRange * 1000 * 10;
+
+            if (adjustToClock)          
+               waveform[(j + triggerCell) % kNumberOfBins] = (short) (value + 0.5);
+            else
+               waveform[j] = (short) (value + 0.5); 
+         }
+      } else {
+         for (j = 0; j < kNumberOfBins; j++) {
+            if (adjustToClock) {
+               // rotate waveform such that waveform[0] corresponds to bin #0 on the chip
+               waveform[j] = adcWaveform[(kNumberOfBins-triggerCell+j) % kNumberOfBins];
+            } else {
+               waveform[j] = adcWaveform[j];
+            }
+         }
+      }
+   }
+
+   // fix bad cells for single turn mode
+   if (GetDRSType() == 2) {
+      if (fDominoMode == 0 && triggerCell == -1) {
+         waveform[0] = 2 * waveform[1] - waveform[2];
+         short m1 = (waveform[kNumberOfBins - 5] + waveform[kNumberOfBins - 6]) / 2;
+         short m2 = (waveform[kNumberOfBins - 6] + waveform[kNumberOfBins - 7]) / 2;
+         waveform[kNumberOfBins - 4] = m1 - 1 * (m2 - m1);
+         waveform[kNumberOfBins - 3] = m1 - 2 * (m2 - m1);
+         waveform[kNumberOfBins - 2] = m1 - 3 * (m2 - m1);
+         waveform[kNumberOfBins - 1] = m1 - 4 * (m2 - m1);
+      }
+   }
+
+   return kSuccess;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetStretchedTime(float *time, float *measurement, int numberOfMeasurements, float period)
+{
+   int j;
+   if (*time >= measurement[numberOfMeasurements - 1]) {
+      *time -= measurement[numberOfMeasurements - 1];
+      return 1;
+   }
+   if (*time < measurement[0]) {
+      *time = *time - measurement[0] - (numberOfMeasurements - 1) * period / 2;
+      return 1;
+   }
+   for (j = 0; j < numberOfMeasurements - 1; j++) {
+      if (*time > measurement[j] && *time <= measurement[j + 1]) {
+         *time =
+             (period / 2) / (measurement[j + 1] - measurement[j]) * (*time - measurement[j + 1]) -
+             (numberOfMeasurements - 2 - j) * period / 2;
+         return 1;
+      }
+   }
+   return 0;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetTriggerCell(unsigned int chipIndex)
+{
+   if (fDRSType == 4)
+      return GetStopCell(chipIndex);
+
+   return GetTriggerCell(fWaveforms, chipIndex);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetTriggerCell(unsigned char *waveforms, unsigned int chipIndex)
+{
+   int j, triggerCell;
+   bool calib;
+   unsigned short baseLevel = 1000;
+   unsigned short triggerChannel[1024];
+
+   if (fDRSType == 4)
+      return GetStopCell(chipIndex);
+
+   GetRawWave(waveforms, chipIndex, 8, triggerChannel);
+   calib = fResponseCalibration->SubtractADCOffset(chipIndex, 8, triggerChannel, triggerChannel, baseLevel);
+
+   triggerCell = -1;
+   for (j = 0; j < kNumberOfBins; j++) {
+      if (calib) {
+         if (triggerChannel[j] <= baseLevel + 200
+             && triggerChannel[(j + 1) % kNumberOfBins] > baseLevel + 200) {
+            triggerCell = j;
+            break;
+         }
+      } else {
+         if (fDRSType == 3) {
+            if (triggerChannel[j] <= 2000 && triggerChannel[(j + 1) % kNumberOfBins] > 2000) {
+               triggerCell = j;
+               break;
+            }
+         } else {
+            if (triggerChannel[j] >= 2000 && triggerChannel[(j + 1) % kNumberOfBins] < 2000) {
+               triggerCell = j;
+               break;
+            }
+         }
+      }
+   }
+   if (triggerCell == -1) {
+      return kInvalidTriggerSignal;
+   }
+   fStopCell[0] = triggerCell;
+   return triggerCell;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetStopCell(unsigned int chipIndex)
+{
+   return fStopCell[chipIndex];
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::TestDAC(int channel)
+{
+   // Test DAC
+   int status;
+
+   do {
+      status = SetDAC(channel, 0);
+      Sleep(1000);
+      status = SetDAC(channel, 0.5);
+      Sleep(1000);
+      status = SetDAC(channel, 1);
+      Sleep(1000);
+      status = SetDAC(channel, 1.5);
+      Sleep(1000);
+      status = SetDAC(channel, 2);
+      Sleep(1000);
+      status = SetDAC(channel, 2.5);
+      Sleep(1000);
+   } while (status);
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::MeasureSpeed()
+{
+   // Measure domino sampling speed
+   FILE *f;
+   double vdr, vds, freq;
+
+   f = fopen("speed.txt", "wt");
+   fprintf(f, "\t");
+   printf("\t");
+   for (vdr = 0.5; vdr <= 2.501; vdr += 0.05) {
+      fprintf(f, "%1.2lf\t", vdr);
+      printf("%1.2lf\t", vdr);
+   }
+   fprintf(f, "\n");
+   printf("\n");
+
+   for (vds = 0.5; vds <= 2.501; vds += 0.05) {
+      fprintf(f, "%1.2lf\t", vds);
+      printf("%1.2lf\t", vds);
+
+      SetDAC(fDAC_DSA, vds);
+      StartDomino();
+      Sleep(1000);
+      ReadFrequency(0, &freq);
+
+      fprintf(f, "%1.3lf\t", freq);
+      printf("%1.3lf\t", freq);
+
+      fprintf(f, "\n");
+      printf("\n");
+      fflush(f);
+   }
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::InteractSpeed()
+{
+   int status, i;
+   double freq, vds;
+
+   do {
+      printf("DS: ");
+      scanf("%lf", &vds);
+      if (vds == 0)
+         break;
+
+      SetDAC(fDAC_DSA, vds);
+      SetDAC(fDAC_DSB, vds);
+
+      StartDomino();
+      for (i = 0; i < 4; i++) {
+         Sleep(1000);
+
+         status = ReadFrequency(0, &freq);
+         if (!status)
+            break;
+         printf("%1.6lf GHz\n", freq);
+      }
+
+      /* turn BOARD_LED off */
+      SetLED(0);
+
+   } while (1);
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::MonitorFrequency()
+{
+   // Monitor domino sampling frequency
+   int status;
+   unsigned int data;
+   double freq, dac;
+   FILE *f;
+   time_t now;
+   char str[256];
+
+   f = fopen("DRSBoard.log", "w");
+
+   do {
+      Sleep(1000);
+
+      status = ReadFrequency(0, &freq);
+      if (!status)
+         break;
+
+      data = 0;
+      if (fBoardType == 1)
+         Read(T_STATUS, &data, REG_RDAC3, 2);
+      else if (fBoardType == 2 || fBoardType == 3)
+         Read(T_STATUS, &data, REG_RDAC1, 2);
+
+      dac = data / 65536.0 * 2.5;
+      printf("%1.6lf GHz, %1.4lf V\n", freq, dac);
+      time(&now);
+      strcpy(str, ctime(&now) + 11);
+      str[8] = 0;
+
+      fprintf(f, "%s %1.6lf GHz, %1.4lf V\n", str, freq, dac);
+      fflush(f);
+
+   } while (!drs_kbhit());
+
+   fclose(f);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::TestShift(int n)
+{
+   // Test shift register
+   unsigned char buffer[3];
+
+   memset(buffer, 0, sizeof(buffer));
+
+#if 0
+   buffer[0] = CMD_TESTSHIFT;
+   buffer[1] = n;
+
+   status = msend_usb(buffer, 2);
+   if (status != 2)
+      return status;
+
+   status = mrecv_usb(buffer, sizeof(buffer));
+   if (status != 1)
+      return status;
+#endif
+
+   if (buffer[0] == 1)
+      printf("Shift register %c works correctly\n", 'A' + n);
+   else if (buffer[0] == 2)
+      printf("SROUT%c does hot go high after reset\n", 'A' + n);
+   else if (buffer[0] == 3)
+      printf("SROUT%c does hot go low after 1024 clocks\n", 'A' + n);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+unsigned int DRSBoard::GetCtrlReg()
+{
+   unsigned int status;
+
+   Read(T_CTRL, &status, REG_CTRL, 4);
+   return status;
+}
+
+/*------------------------------------------------------------------*/
+
+unsigned short DRSBoard::GetConfigReg()
+{
+   unsigned short status;
+
+   Read(T_CTRL, &status, REG_CONFIG, 2);
+   return status;
+}
+
+/*------------------------------------------------------------------*/
+
+unsigned int DRSBoard::GetStatusReg()
+{
+   unsigned int status;
+
+   Read(T_STATUS, &status, REG_STATUS, 4);
+   return status;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::EnableTcal(int freq, int level, int phase)
+{
+   fTcalFreq = freq;
+   fTcalLevel = level;
+   fTcalPhase = phase;
+
+   if (fBoardType == 6) {
+      ConfigureLMK(fFrequency, false, freq, phase);
+   } else {
+      // Enable clock channel
+      if (freq)
+         fCtrlBits |= BIT_TCAL_EN;
+      else
+         fCtrlBits &= ~BIT_TCAL_EN;
+
+      // Set output level, needed for gain calibration
+      if (fDRSType == 4) {
+         if (level)
+            fCtrlBits |= BIT_NEG_TRIGGER;
+         else
+            fCtrlBits &= ~BIT_NEG_TRIGGER;
+      }
+
+      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+   }
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SelectClockSource(int source)
+{
+   fTcalSource = source;
+
+   // Select clock source:
+   // EVAL1: synchronous (0) or asynchronous (1) (2nd quartz)
+   if (source) 
+      fCtrlBits |= BIT_TCAL_SOURCE;
+   else
+      fCtrlBits &= ~BIT_TCAL_SOURCE;
+
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetRefclk(int source)
+{
+   // Select reference clock source to internal FPGA (0) or external P2 (1)
+   if (source) 
+      fCtrlBits |= BIT_REFCLK_SOURCE;
+   else
+      fCtrlBits &= ~BIT_REFCLK_SOURCE;
+
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::EnableAcal(int mode, double voltage)
+{
+   double t1, t2;
+
+   fAcalMode = mode;
+   fAcalVolt = voltage;
+
+   if (mode == 0) {
+      /* turn calibration off */
+      SetCalibTiming(0, 0);
+      if (fBoardType == 5 || fBoardType == 6) {
+         /* turn voltages off (50 Ohm analog switch!) */
+         SetDAC(fDAC_CALP, 0);
+         SetDAC(fDAC_CALN, 0);
+      }
+      fCtrlBits &= ~BIT_ACAL_EN;
+      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+   } else if (mode == 1) {
+      /* static calibration */
+      SetCalibVoltage(voltage);
+      SetCalibTiming(0, 0);
+      fCtrlBits |= BIT_ACAL_EN;
+      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+   } else if (mode == 2) {
+      /* first part calibration:
+         stop domino wave after 1.2 revolutions
+         turn on calibration voltage after 0.1 revolutions */
+
+      /* ensure circulating domino wave */
+      SetDominoMode(1);
+
+      /* set calibration voltage but do not turn it on now */
+      SetCalibVoltage(voltage);
+      fCtrlBits &= ~BIT_ACAL_EN;
+      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+      /* calculate duration of DENABLE signal as 1.2 revolutions */
+      t1 = 1 / fFrequency * 1024 * 1.2; // ns
+      t1 = static_cast < int >((t1 - 30) / 30 + 1);     // 30 ns offset, 30 ns units, rounded up
+      t2 = 1 / fFrequency * 1024 * 0.1; // ns
+      t2 = static_cast < int >((t2 - 30) / 30 + 1);     // 30 ns offset, 30 ns units, rounded up
+      SetCalibTiming(static_cast < int >(t1), static_cast < int >(t2));
+
+   } else if (mode == 3) {
+      /* second part calibration:
+         stop domino wave after 1.05 revolutions */
+
+      /* ensure circulating domino wave */
+      SetDominoMode(1);
+
+      /* turn on and let settle calibration voltage */
+      SetCalibVoltage(voltage);
+      fCtrlBits |= BIT_ACAL_EN;
+      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+
+      /* calculate duration of DENABLE signal as 1.1 revolutions */
+      t1 = 1 / fFrequency * 1024 * 1.05;        // ns
+      t1 = static_cast < int >((t1 - 30) / 30 + 1);     // 30 ns offset, 30 ns units, rounded up
+      SetCalibTiming(static_cast < int >(t1), 0);
+   }
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetCalibTiming(int t_enable, int t_cal)
+{
+   unsigned short d;
+
+   if (fDRSType == 2) {
+      d = t_cal | (t_enable << 8);
+      Write(T_CTRL, REG_CALIB_TIMING, &d, 2);
+   }
+
+   if (fDRSType == 3) {
+      d = t_cal;
+      Write(T_CTRL, REG_CALIB_TIMING, &d, 2);
+   }
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetCalibVoltage(double value)
+{
+   // Set Calibration Voltage
+   if (fBoardType == 5 || fBoardType == 6) {
+      if (fBoardType == 5)
+         value = value * (1+fFrequency/65); // rough correction factor for input current
+      SetDAC(fDAC_CALP, fCommonMode + value / 2);
+      SetDAC(fDAC_CALN, fCommonMode - value / 2);
+   } else
+      SetDAC(fDAC_ACALIB, value);
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::StartClearCycle()
+{
+   /* clear cycle is necessary for DRS4 to reduce noise */
+
+   fbkAcalVolt  = fAcalVolt;
+   fbkAcalMode  = fAcalMode;
+   fbkTcalFreq  = fTcalFreq;
+   fbkTcalLevel = fTcalLevel;
+
+   /* switch all inputs to zero */
+   EnableAcal(1, 0);
+
+   /* start, stop and readout of zero */
+   StartDomino();
+   SoftTrigger();
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::FinishClearCycle()
+{
+   while (IsBusy());
+
+   /* restore old values */
+   EnableAcal(fbkAcalMode, fbkAcalVolt);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+double DRSBoard::GetTemperature()
+{
+   // Read Out Temperature Sensor
+   unsigned char buffer[2];
+   unsigned short d;
+   double temperature;
+
+   Read(T_STATUS, buffer, REG_TEMPERATURE, 2);
+
+   d = (static_cast < unsigned int >(buffer[1]) << 8) +buffer[0];
+   temperature = ((d >> 3) & 0x0FFF) * 0.0625;
+
+   return temperature;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetTriggerBus()
+{
+   unsigned short status;
+
+   Read(T_STATUS, &status, REG_TRIGGER_BUS, 2);
+   return static_cast < int >(status);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::SetBoardSerialNumber(unsigned short serialNumber)
+{
+   unsigned char buf[32768];
+
+   unsigned short dac;
+
+   if (fDRSType < 4) {
+      // read current DAC register
+      Read(T_CTRL, &dac, REG_DAC0, 2);
+
+      // put serial in DAC register
+      Write(T_CTRL, REG_DAC0, &serialNumber, 2);
+
+      // execute flash
+      fCtrlBits |= BIT_EEPROM_WRITE_TRIG;
+      Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+      fCtrlBits &= ~BIT_EEPROM_WRITE_TRIG;
+
+      // wait 6ms per word
+      Sleep(20);
+
+      // write back old DAC registers
+      Write(T_CTRL, REG_DAC0, &dac, 2);
+
+      // read back serial number
+      ReadSerialNumber();
+
+   } else if (fDRSType == 4) {
+      /* merge serial number into eeprom page #0 */
+      ReadEEPROM(0, buf, sizeof(buf));
+      buf[0] = serialNumber & 0xFF;
+      buf[1] = serialNumber >> 8;
+      WriteEEPROM(0, buf, sizeof(buf));
+
+      /* erase DPRAM */
+      memset(buf, 0, sizeof(buf));
+      Write(T_RAM, 0, buf, sizeof(buf));
+
+      /* read back EEPROM */
+      ReadEEPROM(0, buf, sizeof(buf));
+
+      /* check if correctly set */
+      if (((buf[1] << 8) | buf[0]) != serialNumber)
+         return 0;
+
+      fBoardSerialNumber = serialNumber;
+   }
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::ReadEEPROM(unsigned short page, void *buffer, int size)
+{
+   int i;
+   unsigned long status;
+   // write eeprom page number
+   if (fBoardType == 5)
+      Write(T_CTRL, REG_EEPROM_PAGE_EVAL, &page, 2);
+   else if (fBoardType == 6)
+      Write(T_CTRL, REG_EEPROM_PAGE_MEZZ, &page, 2);
+   else return -1;
+        
+   // execute eeprom read
+   fCtrlBits |= BIT_EEPROM_READ_TRIG;
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+   fCtrlBits &= ~BIT_EEPROM_READ_TRIG;
+
+   // poll on serial_busy flag
+   for (i=0 ; i<100 ; i++) {
+      Read(T_STATUS, &status, REG_STATUS, 4);
+      if ((status & BIT_SERIAL_BUSY) == 0) break;
+      Sleep(10);
+   }
+
+   return Read(T_RAM, buffer, 0, size);
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::WriteEEPROM(unsigned short page, void *buffer, int size)
+{
+   int i;
+   unsigned long status;
+
+   // write eeprom page number
+   if (fBoardType == 5)
+      Write(T_CTRL, REG_EEPROM_PAGE_EVAL, &page, 2);
+   else if (fBoardType == 6)
+      Write(T_CTRL, REG_EEPROM_PAGE_MEZZ, &page, 2);
+   else 
+      return -1;
+
+   // write eeprom page to RAM
+   Write(T_RAM, 0, buffer, size);
+
+   // execute eeprom write
+   fCtrlBits |= BIT_EEPROM_WRITE_TRIG;
+   Write(T_CTRL, REG_CTRL, &fCtrlBits, 4);
+   fCtrlBits &= ~BIT_EEPROM_WRITE_TRIG;
+
+   // poll on serail_busy flag
+   for (i=0 ; i<500 ; i++) {
+      Read(T_STATUS, &status, REG_STATUS, 4);
+      if ((status & BIT_SERIAL_BUSY) == 0)
+         break;
+      Sleep(10);
+   }
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetTime(unsigned int chipIndex, float *time, bool tcalibrated, bool rotated)
+{
+   int i;
+
+   /* for DRS2, please use function below */
+   if (fDRSType < 4)
+      return GetTime(chipIndex, fFrequency, time, tcalibrated, rotated);
+
+   if (!fTimingCalibrationValid || !tcalibrated || fabs(fTimingCalibratedFrequency - fFrequency)>0.01) {
+      double t0 = fStopCell[chipIndex] / fFrequency;
+      for (i = 0; i < kNumberOfBins; i++) {
+         if (rotated)
+            time[i] = static_cast < float >(((i+fStopCell[chipIndex]) % kNumberOfBins) / fFrequency - t0);
+         else
+            time[i] = static_cast < float >(i / fFrequency);
+         if (time[i] < 0)
+            time[i] += static_cast < float > (kNumberOfBins / fFrequency);
+      }
+      return 1;
+   }
+
+   double t0 = fCellT[chipIndex][fStopCell[chipIndex]];
+
+   for (i=0 ; i<kNumberOfBins ; i++) {
+      if (rotated)
+         time[i] = static_cast < float > (fCellT[chipIndex][(i+fStopCell[chipIndex]) % kNumberOfBins] - t0);
+      else
+         time[i] = static_cast < float > (fCellT[chipIndex][i]);
+      if (time[i] < 0)
+         time[i] += static_cast < float > (kNumberOfBins / fFrequency);
+   }
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::GetTime(unsigned int chipIndex, double freqGHz, float *time, bool tcalibrated, bool rotated)
+{
+   /* for DRS4, use function above */
+   if (fDRSType == 4)
+      return GetTime(chipIndex, time, tcalibrated, rotated);
+
+   int i, irot;
+   DRSBoard::TimeData * init;
+   DRSBoard::TimeData::FrequencyData * freq;
+   int frequencyMHz = (int)(freqGHz*1000);
+
+   init = GetTimeCalibration(chipIndex);
+
+   if (init == NULL) {
+      for (i = 0; i < kNumberOfBins; i++)
+         time[i] = static_cast < float >(i / fFrequency);
+      return 1;
+   }
+   freq = NULL;
+   for (i = 0; i < init->fNumberOfFrequencies; i++) {
+      if (init->fFrequency[i]->fFrequency == frequencyMHz) {
+         freq = init->fFrequency[i];
+         break;
+      }
+   }
+   if (freq == NULL) {
+      for (i = 0; i < kNumberOfBins; i++)
+         time[i] = static_cast < float >(i / fFrequency);
+      return 1;
+   }
+   for (i = 0; i < kNumberOfBins; i++) {
+      irot = (fStopCell[chipIndex] + i) % kNumberOfBins;
+      if (fStopCell[chipIndex] + i < kNumberOfBins)
+         time[i] = static_cast < float >((freq->fBin[irot] - freq->fBin[fStopCell[chipIndex]]) / fFrequency);
+      else
+      time[i] =
+          static_cast <
+          float
+          >((freq->fBin[irot] - freq->fBin[fStopCell[chipIndex]] + freq->fBin[kNumberOfBins - 1] - 2 * freq->fBin[0] +
+             freq->fBin[1]) / fFrequency);
+   }
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+bool DRSBoard::InitTimeCalibration(unsigned int chipIndex)
+{
+   return GetTimeCalibration(chipIndex, true) != NULL;
+}
+
+/*------------------------------------------------------------------*/
+
+DRSBoard::TimeData * DRSBoard::GetTimeCalibration(unsigned int chipIndex, bool reinit)
+{
+   int i, l, index;
+   char *cstop;
+   char fileName[500];
+   char error[240];
+   PMXML_NODE node, rootNode, mainNode;
+
+   index = fNumberOfTimeData;
+   for (i = 0; i < fNumberOfTimeData; i++) {
+      if (fTimeData[i]->fChip == static_cast < int >(chipIndex)) {
+         if (!reinit)
+            return fTimeData[i];
+         else {
+            index = i;
+            break;
+         }
+      }
+   }
+
+   fTimeData[index] = new DRSBoard::TimeData();
+   DRSBoard::TimeData * init = fTimeData[index];
+
+   init->fChip = chipIndex;
+
+   for (i = 0; i < init->kMaxNumberOfFrequencies; i++) {
+      if (i <= 499 || (i >= 501 && i <= 999) || (i >= 1001 && i <= 1499) || (i >= 1501 && i <= 1999) || 
+          (i >= 2001 && i <= 2499) || i >= 2501)
+         continue;
+      sprintf(fileName, "%s/board%d/TimeCalib_board%d_chip%d_%dMHz.xml", fCalibDirectory, fBoardSerialNumber,
+              fBoardSerialNumber, chipIndex, i);
+      rootNode = mxml_parse_file(fileName, error, sizeof(error));
+      if (rootNode == NULL)
+         continue;
+
+      init->fFrequency[init->fNumberOfFrequencies] = new DRSBoard::TimeData::FrequencyData();
+      init->fFrequency[init->fNumberOfFrequencies]->fFrequency = i;
+
+      mainNode = mxml_find_node(rootNode, "/DRSTimeCalibration");
+
+      for (l = 0; l < kNumberOfBins; l++) {
+         node = mxml_subnode(mainNode, l + 2);
+         init->fFrequency[init->fNumberOfFrequencies]->fBin[l] = strtod(mxml_get_value(node), &cstop);
+      }
+      mxml_free_tree(rootNode);
+      init->fNumberOfFrequencies++;
+   }
+   if (init->fNumberOfFrequencies == 0) {
+      printf("Board %d --> Could not find time calibration file\n", GetBoardSerialNumber());
+   }
+
+   if (index == fNumberOfTimeData)
+      fNumberOfTimeData++;
+
+   return fTimeData[index];
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::SetCalibrationDirectory(const char *calibrationDirectoryPath)
+{
+   strncpy(fCalibDirectory, calibrationDirectoryPath, strlen(calibrationDirectoryPath));
+   fCalibDirectory[strlen(calibrationDirectoryPath)] = 0;
+};
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::GetCalibrationDirectory(char *calibrationDirectoryPath)
+{
+   strncpy(calibrationDirectoryPath, fCalibDirectory, strlen(fCalibDirectory));
+   calibrationDirectoryPath[strlen(fCalibDirectory)] = 0;
+};
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::LinearRegression(double *x, double *y, int n, double *a, double *b)
+{
+   int i;
+   double sx, sxx, sy, sxy;
+
+   sx = sxx = sy = sxy = 0;
+   for (i = 0; i < n; i++) {
+      sx += x[i];
+      sxx += x[i] * x[i];
+      sy += y[i];
+      sxy += x[i] * y[i];
+   }
+
+   *a = (n * sxy - sx * sy) / (n * sxx - sx * sx);
+   *b = (sy - *a * sx) / n;
+}
+
+/*------------------------------------------------------------------*/
+
+void DRSBoard::ReadSingleWaveform(int nChip, int nChan, 
+                                  unsigned short wf[kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins],
+                                  bool rotated)
+{
+   int i, j, k, tc;
+
+   StartDomino();
+   SoftTrigger();
+   while (IsBusy());
+   TransferWaves();
+
+   for (i=0 ; i<nChip ; i++) {
+      tc = GetTriggerCell(i);
+
+      for (j=0 ; j<nChan ; j++) {
+         GetRawWave(i, j, wf[i][j], rotated);
+         if (!rotated) {
+            for (k=0 ; k<kNumberOfBins ; k++) {
+               /* do primary offset calibration */
+               wf[i][j][k] = wf[i][j][k] - fCellOffset[j+i*9][(k + tc) % kNumberOfBins] + 32768;
+            }
+         }
+      }
+   }
+}
+      
+#define WFH_SIZE 100
+
+static unsigned short wfh[kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins][WFH_SIZE];
+static float weight[] = { 
+   0.1f,
+   0.2f,
+   0.4f,
+   0.6f,
+   0.8f,
+   1.0f,
+   1.0f,
+   1.0f,
+   0.8f,
+   0.6f,
+   0.4f,
+   0.2f,
+   0.1f,
+};
+static unsigned short swf[kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins];
+static unsigned short htmp[WFH_SIZE];
+static float          center[kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins];
+static int            icenter[kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins];
+
+int DRSBoard::AverageWaveforms(DRSCallback *pcb, int nChip, int nChan, 
+                               int prog1, int prog2, unsigned short *awf, int n, bool rotated)
+{
+   int i, j, k, l, prog, old_prog = 0;
+   float cm;
+
+   if (pcb != NULL)
+      pcb->Progress(prog1);
+
+   memset(center, 0, sizeof(center));
+
+   for (i=0 ; i<n; i++) {
+      ReadSingleWaveform(nChip, nChan, swf, rotated);
+
+      for (j=0 ; j<nChip ; j++) {
+         for (k=0 ; k<nChan ; k++) {
+            if (i > 5) {
+               /* calculate and subtract common mode */
+               for (l=0,cm=0 ; l<kNumberOfBins ; l++)
+                  cm += swf[j][k][l] - 32768;
+               cm /= kNumberOfBins;
+               for (l=0 ; l<kNumberOfBins ; l++)
+                  center[j][k][l] += swf[j][k][l]- cm;
+            }
+         }
+      }
+
+      prog = (int)(((double)i/n)*(prog2-prog1)+prog1);
+      if (prog > old_prog) {
+         old_prog = prog;
+         if (pcb != NULL)
+            pcb->Progress(prog);
+      }
+   }
+
+   for (i=0 ; i<nChip ; i++)
+      for (j=0 ; j<nChan ; j++)
+         for (k=0 ; k<kNumberOfBins ; k++)
+            awf[(i*nChan+j)*kNumberOfBins+k] = (unsigned short)(center[i][j][k]/(n-6) + 0.5);
+   
+   return 1;
+}
+
+int DRSBoard::RobustAverageWaveforms(DRSCallback *pcb, int nChip, int nChan, 
+                               int prog1, int prog2, unsigned short *awf, int n, bool rotated)
+{
+   int i, j, k, l, prog, old_prog = 0;
+   int nw, bin, max, imax;
+   float mean, norm;
+
+   if (pcb != NULL)
+      pcb->Progress(prog1);
+
+   memset(wfh, 0, sizeof(wfh));
+   memset(center, 0, sizeof(center));
+
+   /* obtain center of histograms */
+   for (i=0 ; i<10 ; i++) {
+      ReadSingleWaveform(nChip, nChan, swf, rotated);
+      for (j=0 ; j<nChip ; j++)
+         for (k=0 ; k<nChan ; k++)
+            for (l=0 ; l<kNumberOfBins ; l++) {
+               center[j][k][l] += swf[j][k][l]/10.0f;
+            }
+
+      /* update progress bar */
+      prog = (int)(((double)i/(n+10))*(prog2-prog1)+prog1);
+      if (prog > old_prog) {
+         old_prog = prog;
+         if (pcb != NULL)
+            pcb->Progress(prog);
+      }
+   }
+   for (j=0 ; j<nChip ; j++)
+      for (k=0 ; k<nChan ; k++)
+         for (l=0 ; l<kNumberOfBins ; l++)
+            icenter[j][k][l] = (int)(center[j][k][l]/16+0.5)*16;
+
+   /* fill histograms */
+   for (i=0 ; i<n ; i++) {
+      ReadSingleWaveform(nChip, nChan, swf, rotated);
+      for (j=0 ; j<nChip ; j++)
+         for (k=0 ; k<nChan ; k++)
+            for (l=0 ; l<kNumberOfBins ; l++) {
+               bin = (swf[j][k][l]-icenter[j][k][l])/16+WFH_SIZE/2;
+               if (bin < 0)
+                  bin = 0;
+               if (bin > WFH_SIZE-1)
+                  bin = WFH_SIZE-1;
+               wfh[j][k][l][bin]++;
+            }
+
+      /* update progress bar */
+      prog = (int)(((double)(i+10)/(n+10))*(prog2-prog1)+prog1);
+      if (prog > old_prog) {
+         old_prog = prog;
+         if (pcb != NULL)
+            pcb->Progress(prog);
+      }
+   }
+
+   /*
+   FILE *fh = fopen("calib.csv", "wt");
+   for (i=40 ; i<60 ; i++) {
+      for (j=0 ; j<WFH_SIZE ; j++)
+         fprintf(fh, "%d;", wfh[0][0][i][j]);
+      fprintf(fh, "\n");
+   }
+   fclose(fh);
+   */
+
+   /* shift histograms to center */
+   for (i=0 ; i<nChip ; i++) {
+      for (j=0 ; j<nChan ; j++) {
+         for (k=0 ; k<kNumberOfBins ; k++) {
+            max = imax = 0;
+            for (l=0 ; l<WFH_SIZE ; l++) {
+               if (wfh[i][j][k][l] > max) {
+                  max = wfh[i][j][k][l];
+                  imax = l;
+               }
+            }
+            for (l=0 ; l<WFH_SIZE ; l++) {
+               bin = l+imax-WFH_SIZE/2;
+               if (bin < 0 || bin > WFH_SIZE-1)
+                  htmp[l] = 0;
+               else
+                  htmp[l] = wfh[i][j][k][bin];
+            }
+            for (l=0 ; l<WFH_SIZE ; l++)
+               wfh[i][j][k][l] = htmp[l];
+            icenter[i][j][k] += (imax-WFH_SIZE/2)*16;
+         }
+      }
+   }
+
+   /* do a weighted average */
+   nw = sizeof(weight)/sizeof(float);
+   for (i=0 ; i<nChip ; i++) {
+      for (j=0 ; j<nChan ; j++) {
+         for (k=0 ; k<kNumberOfBins ; k++) {
+            mean = norm = 0;
+            for (l=0 ; l<nw ; l++) {
+               mean += wfh[i][j][k][WFH_SIZE/2 + l-nw/2] * weight[l] * (icenter[i][j][k] + (l-nw/2)*16);
+               norm += wfh[i][j][k][WFH_SIZE/2 + l-nw/2] * weight[l];
+            }
+            if (norm == 0)
+               awf[(i*nChan+j)*kNumberOfBins+k] = 0;
+            else
+               awf[(i*nChan+j)*kNumberOfBins+k] = (unsigned short) (mean/norm+0.5);
+         }
+      }
+   }
+
+   /*
+   FILE *fh = fopen("calib.csv", "wt");
+   for (i=40 ; i<60 ; i++) {
+      fprintf(fh, "%d;", icenter[0][0][0] + (i - WFH_SIZE/2)*16);
+      fprintf(fh, "%d;", wfh[0][0][0][i]);
+      if (i == 50)
+         fprintf(fh, "%d;", awf[0]);
+      fprintf(fh, "\n");
+   }
+   fclose(fh);
+   */
+
+   if (pcb != NULL)
+      pcb->Progress(prog2);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int idx[4][10] = {
+   {  0,  2,  4,  6,  8, 18, 20, 22, 24, 26 },
+   {  1,  3,  5,  7, 39, 19, 21, 23, 25, 39 },
+   {  9, 11, 13, 15, 17, 27, 29, 31, 33, 35 },
+   { 10, 12, 14, 16, 39, 28, 30, 32, 34, 39 },
+};
+
+#define F1(x) ((int) (84.0/24 * (x)))
+#define F2(x) ((int) (92.0/8 * (x)))
+
+static unsigned short wft[kNumberOfChipsMax*kNumberOfChannelsMax*kNumberOfChipsMax][1024], 
+                      wf1[kNumberOfChipsMax*kNumberOfChannelsMax*kNumberOfChipsMax][1024], 
+                      wf2[kNumberOfChipsMax*kNumberOfChannelsMax*kNumberOfChipsMax][1024],
+                      wf3[kNumberOfChipsMax*kNumberOfChannelsMax*kNumberOfChipsMax][1024];
+
+int DRSBoard::CalibrateVolt(DRSCallback *pcb)
+{
+int    i, j, nChan, timingChan=0, chip, config, p, clkon, refclk, trg1, trg2, n_stuck;
+double f, r;
+unsigned short buf[kNumberOfBins*kNumberOfCalibChannelsV4*2];
+   
+   f       = fFrequency;
+   r       = fRange;
+   clkon   = (GetCtrlReg() & BIT_TCAL_EN) > 0;
+   refclk  = (GetCtrlReg() & BIT_REFCLK_SOURCE) > 0;
+   trg1    = fTriggerEnable1;
+   trg2    = fTriggerEnable2;
+
+   Init();
+   fFrequency = f;
+   SetRefclk(refclk);
+   SetFrequency(fFrequency, true);
+   SetDominoMode(1);
+   SetDominoActive(1);
+   SetReadoutMode(1);
+   SetInputRange(r);
+   if (fBoardType == 5)
+      SelectClockSource(0);
+   else if (fBoardType == 6)
+      SetRefclk(refclk);
+   EnableTrigger(0, 0);
+
+   StartDomino();
+
+   nChan = 0;
+
+   if (fBoardType == 5) {
+      nChan = 9;
+      timingChan = 8;
+
+      /* measure offset */
+      EnableAcal(0, 0); // no inputs signal is allowed during calibration!
+      EnableTcal(0, 0);
+      Sleep(100);
+      RobustAverageWaveforms(pcb, 1, nChan, 0, 33, wf1[0], 500, true);
+
+      /* measure gain at upper range */
+      EnableAcal(1, fRange+0.4);
+      EnableTcal(0, 1);
+      Sleep(100);
+      RobustAverageWaveforms(pcb, 1, nChan, 33, 66, wf2[0], 500, true);
+
+   } else if (fBoardType == 6) {
+      if (fTransport == TR_USB2) {
+         nChan = 36;
+         timingChan = 8;
+         memset(wf1, 0, sizeof(wf1));
+         memset(wf2, 0, sizeof(wf2));
+         memset(wf3, 0, sizeof(wf3));
+         for (config=p=0 ; config<4 ; config++) {
+            SetChannelConfig(config, 8, 8);
+
+            /* measure offset */
+            EnableAcal(1, 0);
+            EnableTcal(0, 0);
+            Sleep(100);
+            RobustAverageWaveforms(pcb, 0, 10, F1(p), F1(p+1), wft[0], 500, true); p++;
+            for (i=0 ; i<5 ; i++)
+               memcpy(wf1[idx[config][i]], wft[i*2], sizeof(float)*kNumberOfBins);
+            RobustAverageWaveforms(pcb, 2, 10, F1(p), F1(p+1), wft[0], 500, true); p++;
+            for (i=0 ; i<5 ; i++)
+               memcpy(wf1[idx[config][i+5]], wft[i*2], sizeof(float)*kNumberOfBins);
+
+            /* measure gain at +400 mV */
+            EnableAcal(1, 0.4);
+            EnableTcal(0, 0);
+            Sleep(100);
+            RobustAverageWaveforms(pcb, 0, 8, F1(p), F1(p+1), wft[0], 500, true); p++;
+            for (i=0 ; i<4 ; i++)
+               memcpy(wf2[idx[config][i]], wft[i*2], sizeof(float)*kNumberOfBins);
+            RobustAverageWaveforms(pcb, 2, 8, F1(p), F1(p+1), wft[0], 500, true); p++;
+            for (i=0 ; i<4 ; i++)
+               memcpy(wf2[idx[config][i+5]], wft[i*2], sizeof(float)*kNumberOfBins);
+
+            /* measure gain at -400 mV */
+            EnableAcal(1, -0.4);
+            EnableTcal(0, 1);
+            Sleep(100);
+            RobustAverageWaveforms(pcb, 0, 8, F1(p), F1(p+1), wft[0], 500, true); p++;
+            for (i=0 ; i<4 ; i++)
+               memcpy(wf3[idx[config][i]], wft[i], sizeof(float)*kNumberOfBins);
+            RobustAverageWaveforms(pcb, 2, 8, F1(p), F1(p+1), wft[0], 500, true); p++;
+            for (i=0 ; i<4 ; i++)
+               memcpy(wf3[idx[config][i+5]], wft[i], sizeof(float)*kNumberOfBins);
+         }
+      } else {
+         nChan = 36;
+         timingChan = 8;
+
+         /* measure offset */
+         EnableAcal(0, 0); // no inputs signal is allowed during calibration!
+         EnableTcal(0, 0);
+         Sleep(100);
+         RobustAverageWaveforms(pcb, 4, 9, 0, 25, wf1[0], 500, true);
+
+         /* measure gain at upper range */
+         EnableAcal(1, fRange+0.4);
+         EnableTcal(0, 0);
+         Sleep(100);
+         RobustAverageWaveforms(pcb, 4, 9, 25, 50, wf2[0], 500, true);
+      }
+   }
+
+   /* convert offsets and gains to 16-bit values */
+   memset(fCellOffset, 0, sizeof(fCellOffset));
+   n_stuck = 0;
+   for (i=0 ; i<nChan ; i++) {
+      for (j=0 ; j<kNumberOfBins; j++) {
+         if (i % 9 == timingChan) {
+            /* calculate offset and gain for timing channel */
+            if (fBoardType == 5) {
+               /* we have a +325mV and a -325mV value */
+               fCellOffset[i][j] = (unsigned short) ((wf1[i][j]+wf2[i][j])/2+0.5);
+               fCellGain[i][j]   = (wf2[i][j] - wf1[i][j])/65536.0*1000 / 650.0;
+            } else {
+               /* only have offset */
+               fCellOffset[i][j] = wf1[i][j];
+               fCellGain[i][j]   = 1;
+            }
+         } else {
+            /* calculate offset and gain for data channel */
+            fCellOffset[i][j] = wf1[i][j];
+            if (fCellOffset[i][j] < 100) {
+               // mark stuck pixel
+               n_stuck ++;
+               fCellOffset[i][j] = 0;
+               fCellGain[i][j] = 1;
+            } else
+               fCellGain[i][j] = (wf2[i][j] - fCellOffset[i][j])/65536.0*1000 / ((0.4+fRange)*1000);
+         }
+
+         /* check gain */
+         if (fCellGain[i][j] < 0.5 || fCellGain[i][j] > 1.1) {
+            printf("Gain of %6.3lf for channel %2d, cell %4d out of range 0.5 ... 1.1\n",
+               fCellGain[i][j], i, j);
+            fCellGain[i][j] = 1;
+         }
+      }
+   }
+
+   /*
+   FILE *fh = fopen("calib.txt", "wt");
+   for (i=0 ; i<nChan ; i++) {
+      fprintf(fh, "CH%02d:", i);
+      for (j=0 ; j<20 ; j++)
+         fprintf(fh, " %5d", fCellOffset[i][j]-32768);
+      fprintf(fh, "\n");
+   }
+   fclose(fh);
+   */
+
+   /* perform secondary calibration */
+   if (fBoardType == 5) {
+      nChan = 9;
+      timingChan = 8;
+
+      /* measure offset */
+      EnableAcal(0, 0); // no inputs signal is allowed during calibration!
+      EnableTcal(0, 0);
+      Sleep(100);
+      AverageWaveforms(pcb, 1, 9, 66, 100, wf1[0], 500, false);
+   } else if (fBoardType == 6 && fTransport == TR_VME) {
+      nChan = 36;
+      timingChan = 8;
+
+      /* measure offset */
+      EnableAcal(0, 0); // no inputs signal is allowed during calibration!
+      EnableTcal(0, 0);
+      Sleep(100);
+      AverageWaveforms(pcb, 4, 9, 50, 75, wf1[0], 500, false);
+   }
+
+   /* convert offset to 16-bit values */
+   memset(fCellOffset2, 0, sizeof(fCellOffset2));
+   for (i=0 ; i<nChan ; i++)
+      for (j=0 ; j<kNumberOfBins; j++)
+         if (i % 9 != timingChan)
+            fCellOffset2[i][j] = wf1[i][j];
+
+   /*
+   FILE *fh = fopen("calib.txt", "wt");
+   for (i=0 ; i<nChan ; i++) {
+      for (j=0 ; j<kNumberOfBins; j++)
+         fprintf(fh, "%5d: %5d %5d\n", j, fCellOffset2[0][j]-32768, fCellOffset2[1][j]-32768);
+      fprintf(fh, "\n");
+   }
+   fclose(fh);
+   */
+
+   if (fBoardType == 5) {
+      /* write calibration CH0-CH7 to EEPROM page 1 */
+      for (i=0 ; i<8 ; i++)
+         for (j=0 ; j<1024; j++) {
+            buf[(i*1024+j)*2]   = fCellOffset[i][j];
+            buf[(i*1024+j)*2+1] = (unsigned short) ((fCellGain[i][j] - 0.7) / 0.4 * 65535);
+         }
+      WriteEEPROM(1, buf, 1024*32);
+
+      /* write calibration CH8 and secondary calibration to EEPROM page 2 */
+      ReadEEPROM(2, buf, 1024*5*4);
+      for (j=0 ; j<1024; j++) {
+         buf[j*2]   = fCellOffset[8][j];
+         buf[j*2+1] = (unsigned short) ((fCellGain[8][j] - 0.7) / 0.4 * 65535);
+      }
+      for (i=0 ; i<4 ; i++)
+         for (j=0 ; j<1024; j++) {
+            buf[2*1024+((i*2)*1024+j)*2]   = fCellOffset2[i*2][j];
+            buf[2*1024+((i*2)*1024+j)*2+1] = fCellOffset2[i*2+1][j];
+         }
+      WriteEEPROM(2, buf, 1024*5*4);
+
+      /* write calibration method and range */
+      ReadEEPROM(0, buf, 2048); // 0-0x0FFF
+      buf[2] = VCALIB_METHOD | ((signed char)(fRange * 100)) << 8;
+      WriteEEPROM(0, buf, 2048);
+      fCellCalibratedRange = fRange;
+
+   } else if (fBoardType == 6) {
+      for (chip=0 ; chip<4 ; chip++) {
+         /* write calibration of A0 to A7 to EEPROM page 1 
+                                 B0 to B7 to EEPROM page 2 and so on */
+         for (i=0 ; i<8 ; i++)
+            for (j=0 ; j<1024; j++) {
+               buf[(i*1024+j)*2]   = fCellOffset[i+chip*9][j];
+               buf[(i*1024+j)*2+1] = (unsigned short) ((fCellGain[i+chip*9][j] - 0.7) / 0.4 * 65535);
+            }
+         WriteEEPROM(1+chip, buf, 1024*32);
+         if (pcb != NULL)
+            pcb->Progress(75+chip*4);
+       }
+
+      /* write calibration A/B/C/D/CLK to EEPROM page 5 */
+      ReadEEPROM(5, buf, 1024*4*4);
+      for (chip=0 ; chip<4 ; chip++) {
+         for (j=0 ; j<1024; j++) {
+            buf[j*2+chip*0x0800]   = fCellOffset[8+chip*9][j];
+            buf[j*2+1+chip*0x0800] = (unsigned short) ((fCellGain[8+chip*9][j] - 0.7) / 0.4 * 65535);
+         }
+      }
+      WriteEEPROM(5, buf, 1024*4*4);
+      if (pcb != NULL)
+         pcb->Progress(90);
+
+      /* write secondary calibration to EEPROM page 7 and 8 */
+      for (i=0 ; i<8 ; i++) {
+         for (j=0 ; j<1024; j++) {
+            buf[i*0x800 + j*2]   = fCellOffset2[i][j];
+            buf[i*0x800 + j*2+1] = fCellOffset2[i+9][j];
+         }
+      }
+      WriteEEPROM(7, buf, 1024*32);
+      if (pcb != NULL)
+         pcb->Progress(94);
+
+      for (i=0 ; i<8 ; i++) {
+         for (j=0 ; j<1024; j++) {
+            buf[i*0x800 + j*2]   = fCellOffset2[i+18][j];
+            buf[i*0x800 + j*2+1] = fCellOffset2[i+27][j];
+         }
+      }
+      WriteEEPROM(8, buf, 1024*32);
+      if (pcb != NULL)
+         pcb->Progress(98);
+
+      /* write calibration method and range */
+      ReadEEPROM(0, buf, 2048); // 0-0x0FFF
+      buf[2] = VCALIB_METHOD | ((signed char)(fRange * 100)) << 8;
+      WriteEEPROM(0, buf, 2048);
+      fCellCalibratedRange = fRange;
+      if (pcb != NULL)
+         pcb->Progress(100);
+   }
+
+   if (n_stuck)
+      printf("\nFound %d stuck pixels on this board\n", n_stuck);
+
+   fCellCalibrationValid = true;
+
+   /* remove calibration voltage */
+   EnableAcal(0, 0);
+   EnableTcal(0, 0);
+   EnableTrigger(trg1, trg2);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int DRSBoard::AnalyzeWF(int nIter, float wf[kNumberOfBins], int tCell, double cellT[kNumberOfBins])
+{
+int    i, i1, i2, j, k, nzx, zeroXing[1000], edge, n_correct;
+double damping, zeroLevel, ta, tb, dt, corr, inv_corr;
+
+   /* calculate zero level */
+   for (i=0,zeroLevel=0 ; i<1024 ; i++)
+      zeroLevel += wf[i];
+   zeroLevel /= 1024;
+
+   /* correct for zero common mode */
+   for (i=0 ; i<1024 ; i++)
+      wf[i] -= (float)zeroLevel;
+
+   /* estimate damping factor */
+   damping = fFrequency / nIter * 40;
+
+   for (edge = 0 ; edge < 2 ; edge ++) {
+
+      /* find edge zero crossing with wrap-around */
+      for (i=tCell+3,nzx=0 ; i<tCell+1023 && nzx < (int)(sizeof(zeroXing)/sizeof(int)) ; i++) {
+         if (edge == 0) {
+            if (wf[(i+1) % 1024] < 0 && wf[i % 1024] > 0) // falling edge
+               zeroXing[nzx++] = i;
+         } else {
+            if (wf[(i+1) % 1024] > 0 && wf[i % 1024] < 0) // rising edge
+               zeroXing[nzx++] = i;
+         }
+      }
+
+      if (nzx < 20)
+         return 0;
+
+      for (i=n_correct=0 ; i<nzx-1 ; i++) {
+         i1 = zeroXing[i] % 1024;
+         if (i1 == 1023)
+            continue;
+         ta = cellT[i1] + (cellT[i1+1] - cellT[i1])*(1/(1-wf[(i1+1) % 1024]/wf[i1]));
+         i2 = zeroXing[i+1] % 1024;
+         if (i2 == 1023)
+            continue;
+         tb = cellT[i2] + (cellT[i2+1] - cellT[i2])*(1/(1-wf[(i2+1) % 1024]/wf[i2]));
+
+//      for (i=n_correct=0 ; i<nzx-1 ; i++) {
+ //        i1 = zeroXing[i] % 1024;
+ //        ta = cellT[i1] + (cellT[i1+1] - cellT[i1])*(1/(1-wf[(i1+1)%1024]/wf[i1]));
+//         i2 = zeroXing[i+1] % 1024;
+//         tb = cellT[i2] + (cellT[i2+1] - cellT[i2])*(1/(1-wf[(i2+1)%1024]/wf[i2]));
+
+         /* wrap-around ? */
+         if (tb - ta < 0)
+            tb += 1/fFrequency*1024;
+
+         /* calculate correction to nominal period in ns */
+         corr = 1/fTCALFrequency*1000 - (tb - ta);
+
+         /* skip very large corrections (noise?) */
+         if (fabs(corr/(1/fTCALFrequency*1000)) > 0.5)
+            continue;
+
+         /* remeber number of valid corrections */
+         n_correct++;
+
+         /* apply damping factor */
+         corr *= damping;
+
+         /* calculate inverse correction */
+         inv_corr = -corr;
+
+         /* apply from (i1+1)+1 to i2 inclusive */
+         i1 = zeroXing[i]+2;
+         i2 = zeroXing[i+1];
+
+         /* distribute correciton equally into bins inside the region ... */
+         corr = corr / (i2-i1+1);
+
+         /* ... and inverse correction into the outside bins */
+         inv_corr = inv_corr / (1024 - (i2-i1+1));
+
+         i1 = i1 % 1024;
+         i2 = i2 % 1024;
+
+         double oldT[kNumberOfBins];
+         memcpy(oldT, cellT, sizeof(double)*1024);
+         for (j=0,ta=0 ; j<1024 ; j++) {
+            if (j < 1023)
+               dt = cellT[j+1] - cellT[j];
+            else
+               dt = 1/fFrequency*1024 - cellT[j];
+            if ((i2 > i1 && (j >= i1 && j<= i2)) ||
+                (i2 < i1 && (j >= i1 || j<= i2)) )
+               dt += corr;
+            else
+               dt += inv_corr;
+
+            cellT[j] = ta;		  
+            ta += dt;
+         }
+
+         /* check and correct for too narrow bin widths */
+         for (j=0 ; j<1023 ; j++) {
+            dt = cellT[j+1] - cellT[j];
+            if (dt < 1/fFrequency*0.1) {
+               /* if width is smaller than 10% of nominal width, "undo" 5x that correction,
+                  otherwise next iteration would cause this problem again */
+               corr = 5*(1/fFrequency*0.1-dt);
+               inv_corr = -corr;
+
+               /* distribute inverse correction equally into the outside bins */
+               inv_corr = inv_corr / 1022;
+
+               for (k=0,ta=0 ; k<1024 ; k++) {
+                  if (k < 1023)
+                     dt = cellT[k+1] - cellT[k];
+                  else
+                     dt = 1/fFrequency*1024 - cellT[k];
+                  if (k == j)
+                     dt += corr;
+                  else
+                     dt += inv_corr;
+
+                  cellT[k] = ta;
+                  ta += dt;
+               }
+            }
+         }
+      }
+
+      if (n_correct < nzx/3)
+         return 0;
+   }
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+
+int DRSBoard::CalibrateTiming(DRSCallback *pcb)
+{
+int    index, status, tCell, i, c, chip, mode, nIter, clkon, phase, refclk, trg1, trg2;
+double f, range, t1[4], t2[4];
+unsigned short buf[1024*4];
+float  wf[1024];
+   
+   nIter   = 1000;
+   f       = fFrequency;
+   range   = fRange;
+   clkon   = (GetCtrlReg() & BIT_TCAL_EN) > 0;
+   refclk  = (GetCtrlReg() & BIT_REFCLK_SOURCE) > 0;
+   trg1    = fTriggerEnable1;
+   trg2    = fTriggerEnable2;
+
+   Init();
+   fFrequency = f;
+   SetRefclk(refclk);
+   SetFrequency(fFrequency, true);
+   if (fBoardType == 5)
+      fTCALFrequency = 240; // 240 MHz, for MEZZ this is set by ConfigureLMK
+   SetDominoMode(1);
+   SetDominoActive(1);
+   SetReadoutMode(1);
+   EnableTrigger(0, 0);
+   EnableTcal(1, 0, 0);
+   if (fBoardType == 5)
+      SelectClockSource(1); // 2nd quartz
+   StartDomino();
+
+   /* initialize time array */
+   for (i=0 ; i<1024 ; i++)
+      for (chip=0 ; chip<4 ; chip++)
+         fCellT[chip][i] = (float)1/fFrequency*i; // [ns]
+
+   for (index = 0 ; index < nIter ; index++) {
+      if (index % 10 == 0)
+         pcb->Progress(100*index/nIter);
+
+      if (fTransport == TR_VME) {
+         SoftTrigger();
+         while (IsBusy());
+
+         /* select random phase */
+         phase = (rand() % 30) - 15;
+         if (phase == 0)
+            phase = 15;
+         EnableTcal(1, 0, phase);
+
+         StartDomino();
+         TransferWaves();
+
+         for (chip=0 ; chip<4 ; chip++) {
+            tCell = GetStopCell(chip);
+            GetWave(chip, 8, wf, true, tCell, true);
+            status = AnalyzeWF(nIter, wf, tCell, fCellT[chip]);
+
+            if (!status)
+               return 0;
+         }
+      } else {
+         if (fBoardType == 5) { // DRS4 Evaluation board: 1 Chip
+            SoftTrigger();
+            while (IsBusy());
+
+            StartDomino();
+            TransferWaves();
+
+            tCell = GetStopCell(0);
+            GetWave(0, 8, wf, true, tCell, true);
+            status = AnalyzeWF(nIter, wf, tCell, fCellT[0]);
+
+            if (!status)
+               return 0;
+
+         } else {               // DRS4 Mezzanine board: 4 Chips
+            for (mode=0 ; mode<2 ; mode++) {
+               SetChannelConfig(mode*2, 8, 8);
+               SoftTrigger();
+               while (IsBusy());
+
+               /* select random phase */
+               phase = (rand() % 30) - 15;
+               if (phase == 0)
+                  phase = 15;
+               EnableTcal(1, 0, phase);
+
+               StartDomino();
+               TransferWaves();
+
+               for (chip=0 ; chip<4 ; chip+=2) {
+                  tCell = GetStopCell(chip+mode);
+                  GetWave(chip+mode, 8, wf, true, tCell, true);
+                  status = AnalyzeWF(nIter, wf, tCell, fCellT[chip+mode]);
+
+                  if (!status)
+                     return 0;
+               }
+            }
+         }
+      }
+   }
+
+   pcb->Progress(100);
+
+   // use following lines to save calibration into an ASCII file
+#if 0
+   FILE *fh;
+
+   fh = fopen("cellt.csv", "wt");
+   if (!fh)
+      printf("Cannot open file \"cellt.csv\"\n");
+   else {
+      fprintf(fh, "index;d_ch1;d_ch2;d_ch3;d_ch4\n");
+      for (i=0 ; i<1024 ; i++)
+         fprintf(fh, "%4d;%5.3lf;%5.3lf;%5.3lf;%5.3lf\n", i, 
+                 fCellT[0][i]-i/fFrequency, 
+                 fCellT[1][i]-i/fFrequency, 
+                 fCellT[2][i]-i/fFrequency, 
+                 fCellT[3][i]-i/fFrequency);
+      fclose(fh);
+   }
+#endif
+
+   if (fBoardType == 5) {
+      /* write timing calibration to EEPROM page 0 */
+      ReadEEPROM(0, buf, sizeof(buf));
+      for (i=0,t1[0]=0 ; i<1024; i++) {
+         t2[0] = fCellT[0][i] - t1[0];
+         t2[0] = (unsigned short) (t2[0] * 10000 + 0.5);
+         t1[0] += t2[0] / 10000.0;
+         buf[i*2+1] = (unsigned short) t2[0];
+      }
+
+      /* write calibration method and frequency */
+      buf[4] = TCALIB_METHOD;
+      buf[6] = (unsigned short) (fFrequency / 6.0 * 65535.0);
+      fTimingCalibratedFrequency = buf[6] / 65535.0 * 6.0;
+      WriteEEPROM(0, buf, sizeof(buf));
+   } else {
+      /* write timing calibration to EEPROM page 6 */
+      ReadEEPROM(6, buf, sizeof(buf));
+      for (c=0 ; c<4 ; c++)
+         t1[c] = 0;
+      for (i=0 ; i<1024; i++) {
+         for (c=0 ; c<4 ; c++) {
+            t2[c] = fCellT[c][i] - t1[c];
+            t2[c] = (unsigned short) (t2[c] * 10000 + 0.5);
+            t1[c] += t2[c] / 10000.0;
+         }
+         buf[i*2]         = (unsigned short) t2[0];
+         buf[i*2+1]       = (unsigned short) t2[1];
+         buf[i*2+0x800]   = (unsigned short) t2[2];
+         buf[i*2+0x800+1] = (unsigned short) t2[3];
+      }
+      WriteEEPROM(6, buf, sizeof(buf));
+
+      /* write calibration method and frequency */
+      ReadEEPROM(0, buf, 16);
+      buf[4] = TCALIB_METHOD;
+      buf[6] = (unsigned short) (fFrequency / 6.0 * 65535.0);
+      fTimingCalibratedFrequency = buf[6] / 65535.0 * 6.0;
+      WriteEEPROM(0, buf, 16);
+   }
+
+   fTimingCalibrationValid = true;
+
+   /* remove calibration voltage */
+   EnableAcal(0, 0);
+   EnableTcal(clkon, 0);
+   SetInputRange(range);
+   EnableTrigger(trg1, trg2);
+
+   return 1;
+}
+
+
+/*------------------------------------------------------------------*/
+
+
+void DRSBoard::RemoveSymmetricSpikes(short **wf, int nwf,
+                                     short diffThreshold, int spikeWidth,
+                                     short maxPeakToPeak, short spikeVoltage,
+                                     int nTimeRegionThreshold)
+{
+   // Remove a specific kind of spike on DRS4.
+   // This spike has some features,
+   //  - Common on all the channels on a chip
+   //  - Constant heigh and width
+   //  - Two spikes per channel
+   //  - Symmetric to cell #0.
+   //
+   // This is not general purpose spike-removing function.
+   // 
+   // wf                   : Waveform data. cell#0 must be at bin0,
+   //                        and number of bins must be kNumberOfBins.
+   // nwf                  : Number of channels which "wf" holds.
+   // diffThreshold        : Amplitude threshold to find peak
+   // spikeWidth           : Width of spike
+   // maxPeakToPeak        : When peak-to-peak is larger than this, the channel
+   //                        is not used to find spikes.
+   // spikeVoltage         : Amplitude of spikes. When it is 0, it is calculated in this function
+   //                        from voltage difference from neighboring bins.
+   // nTimeRegionThreshold : Requirement of number of time regions having spike at common position.
+   //                        Total number of time regions is 2*"nwf".
+
+   if (!wf || !nwf || !diffThreshold || !spikeWidth) {
+      return;
+   }
+
+   int          ibin, jbin, kbin;
+   double       v;
+   int          nbin;
+   int          iwf;
+   short        maximum, minimum;
+   int          spikeCount[kNumberOfBins / 2];
+   int          spikeCountSum[kNumberOfBins / 2] = {0};
+   bool         largePulse[kNumberOfChannelsMax * 2] = {0};
+   const short  diffThreshold2 = diffThreshold + diffThreshold;
+
+   const short  maxShort = 0xFFFF>>1;
+   const short  minShort = -maxShort - 1;
+
+   // search spike
+   for (iwf = 0; iwf < nwf; iwf++) {
+      // first half
+      memset(spikeCount, 0, sizeof(spikeCount));
+      maximum = minShort;
+      minimum = maxShort;
+      for (ibin = 0; ibin < kNumberOfBins / 2; ibin++) {
+         jbin = ibin;
+         maximum = max(maximum, wf[iwf][jbin]);
+         minimum = min(minimum, wf[iwf][jbin]);
+         if (jbin - 1 >= 0 && jbin + spikeWidth < kNumberOfBins) {
+            v = 0;
+            nbin = 0;
+            for (kbin = 0; kbin < spikeWidth; kbin++) {
+               v += wf[iwf][jbin + kbin];
+               nbin++;
+            }
+            if ((nbin == 2 && v - (wf[iwf][jbin - 1] + wf[iwf][jbin + spikeWidth]) > diffThreshold2) ||
+                (nbin != 2 && nbin && v / nbin - (wf[iwf][jbin - 1] + wf[iwf][jbin + spikeWidth]) / 2 > diffThreshold)) {
+               spikeCount[ibin]++;
+            }
+         }
+      }
+      if (maximum != minShort && minimum != maxShort &&
+          (!maxPeakToPeak || maximum - minimum < maxPeakToPeak)) {
+         for (ibin = 0; ibin < kNumberOfBins / 2; ibin++) {
+            spikeCountSum[ibin] += spikeCount[ibin];
+         }
+         largePulse[iwf] = false;
+#if 0 /* this part can be enabled to skip checking other channels */
+         if (maximum != minShort && minimum != maxShort &&
+             maximum - minimum < diffThreshold) {
+            return;
+         }
+#endif
+      } else {
+         largePulse[iwf] = true;
+      }
+
+      // second half
+      memset(spikeCount, 0, sizeof(spikeCount));
+      maximum = minShort;
+      minimum = maxShort;
+      for (ibin = 0; ibin < kNumberOfBins / 2; ibin++) {
+         jbin = kNumberOfBins - 1 - ibin;
+         maximum = max(maximum, wf[iwf][jbin]);
+         minimum = min(minimum, wf[iwf][jbin]);
+         if (jbin + 1 < kNumberOfBins && jbin - spikeWidth >= 0) {
+            v = 0;
+            nbin = 0;
+            for (kbin = 0; kbin < spikeWidth; kbin++) {
+               v += wf[iwf][jbin - kbin];
+               nbin++;
+            }
+            if ((nbin == 2 && v - (wf[iwf][jbin + 1] + wf[iwf][jbin - spikeWidth]) > diffThreshold2) ||
+                (nbin != 2 && nbin && v / nbin - (wf[iwf][jbin + 1] + wf[iwf][jbin - spikeWidth]) / 2 > diffThreshold)) {
+               spikeCount[ibin]++;
+            }
+         }
+      }
+      if (maximum != minShort && minimum != maxShort &&
+          maximum - minimum < maxPeakToPeak) {
+         for (ibin = 0; ibin < kNumberOfBins / 2; ibin++) {
+            spikeCountSum[ibin] += spikeCount[ibin];
+         }
+         largePulse[iwf + nwf] = false;
+#if 0 /* this part can be enabled to skip checking other channels */
+         if (maximum != minShort && minimum != maxShort &&
+             maximum - minimum < diffThreshold) {
+            return;
+         }
+#endif
+      } else {
+         largePulse[iwf + nwf] = true;
+      }
+   }
+
+   // Find common spike
+   int commonSpikeBin = -1;
+   int commonSpikeMax = -1;
+   for (ibin = 0; ibin < kNumberOfBins / 2; ibin++) {
+      if (commonSpikeMax < spikeCountSum[ibin]) {
+         commonSpikeMax = spikeCountSum[ibin];
+         commonSpikeBin = ibin;
+      }
+   } 
+
+   if (spikeCountSum[commonSpikeBin] >= nTimeRegionThreshold) {
+      if (spikeVoltage == 0) {
+         // Estimate spike amplitude
+         double  baseline      = 0;
+         int    nBaseline      = 0;
+         double  peakAmplitude = 0;
+         int    nPeakAmplitude = 0;
+         for (iwf = 0; iwf < nwf; iwf++) {
+            // first half
+            if (!largePulse[iwf]) {
+               // baseline
+               if ((jbin = commonSpikeBin - 1) >= 0 && jbin < kNumberOfBins) {
+                  baseline += wf[iwf][jbin];
+                  nBaseline++;
+               }
+               if ((jbin = commonSpikeBin + spikeWidth + 1) >= 0 && jbin < kNumberOfBins) {
+                  baseline += wf[iwf][jbin];
+                  nBaseline++;
+               }
+               // spike
+               for (ibin = 0; ibin < spikeWidth; ibin++) {
+                  if ((jbin = commonSpikeBin + ibin) >= 0 && jbin < kNumberOfBins) {
+                     peakAmplitude += wf[iwf][jbin];
+                     nPeakAmplitude++;
+                  }
+               }
+            }
+
+            // second half
+            if (!largePulse[iwf + nwf]) {
+               // baseline
+               if ((jbin = kNumberOfBins - 1 - commonSpikeBin + 1) >= 0 && jbin < kNumberOfBins) {
+                  baseline += wf[iwf][jbin];
+                  nBaseline++;
+               }
+               if ((jbin = kNumberOfBins - 1 - commonSpikeBin - spikeWidth - 1) >= 0 && jbin < kNumberOfBins) {
+                  baseline += wf[iwf][jbin];
+                  nBaseline++;
+               }
+               // spike
+               for (ibin = 0; ibin < spikeWidth; ibin++) {
+                  if ((jbin = kNumberOfBins - 1 - commonSpikeBin - ibin) >= 0 && jbin < kNumberOfBins) {
+                     peakAmplitude += wf[iwf][jbin];
+                     nPeakAmplitude++;
+                  }
+               }
+            }
+         }
+         if (nBaseline && nPeakAmplitude) {
+            baseline /= nBaseline;
+            peakAmplitude /= nPeakAmplitude;
+            spikeVoltage = static_cast<short>(peakAmplitude - baseline);
+         } else {
+            spikeVoltage = 0;
+         }
+      }
+
+      // Remove spike
+      if (spikeVoltage > 0) {
+         for (iwf = 0; iwf < nwf; iwf++) {
+            for (ibin = 0; ibin < spikeWidth; ibin++) {
+               if ((jbin = commonSpikeBin + ibin) >= 0 && jbin < kNumberOfBins) {
+                  wf[iwf][jbin] -= spikeVoltage; 
+               }
+               if ((jbin = kNumberOfBins - 1 - commonSpikeBin - ibin) >= 0 && jbin < kNumberOfBins) {
+                  wf[iwf][jbin] -= spikeVoltage; 
+               }
+            }
+         }
+      }
+   }
+}
+
+/*------------------------------------------------------------------*/
+
+void ResponseCalibration::SetCalibrationParameters(int numberOfPointsLowVolt, int numberOfPoints,
+                                                   int numberOfMode2Bins, int numberOfSamples,
+                                                   int numberOfGridPoints, int numberOfXConstPoints,
+                                                   int numberOfXConstGridPoints, double triggerFrequency,
+                                                   int showStatistics)
+{
+   DeleteFields();
+   InitFields(numberOfPointsLowVolt, numberOfPoints, numberOfMode2Bins, numberOfSamples, numberOfGridPoints,
+              numberOfXConstPoints, numberOfXConstGridPoints, triggerFrequency, showStatistics);
+}
+
+/*------------------------------------------------------------------*/
+
+void ResponseCalibration::ResetCalibration()
+{
+   int i;
+   for (i = 0; i < kNumberOfChipsMax; i++)
+      fCalibrationData[i]->fRead = false;
+   fCurrentPoint = 0;
+   fCurrentLowVoltPoint = 0;
+   fCurrentSample = 0;
+   fCurrentFitChannel = 0;
+   fCurrentFitBin = 0;
+   fRecorded = false;
+   fFitted = false;
+   fOffset = false;
+};
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::WriteCalibration(unsigned int chipIndex)
+{
+   if (!fOffset)
+      return false;
+   if (fBoard->GetDRSType() == 3)
+      return WriteCalibrationV4(chipIndex);
+   else
+      return WriteCalibrationV3(chipIndex);
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::WriteCalibrationV3(unsigned int chipIndex)
+{
+   if (!fOffset)
+      return false;
+
+   int ii, j, k;
+   char str[1000];
+   char strt[1000];
+   short tempShort;
+   CalibrationData *data = fCalibrationData[chipIndex];
+   CalibrationData::CalibrationDataChannel * chn;
+
+   // Open File
+   fBoard->GetCalibrationDirectory(strt);
+   sprintf(str, "%s/board%d", strt, fBoard->GetBoardSerialNumber());
+   if (MakeDir(str) == -1) {
+      printf("Error: Cannot create directory \"%s\"\n", str);
+      return false;
+   }
+   sprintf(str, "%s/board%d/ResponseCalib_board%d_chip%d_%dMHz.bin", strt, fBoard->GetBoardSerialNumber(),
+           fBoard->GetBoardSerialNumber(), chipIndex, static_cast < int >(fBoard->GetFrequency() * 1000));
+   fCalibFile = fopen(str, "wb");
+   if (fCalibFile == NULL) {
+      printf("Error: Cannot write to file \"%s\"\n", str);
+      return false;
+   }
+   // Write File
+   fwrite(&data->fNumberOfGridPoints, 1, 1, fCalibFile);
+   tempShort = static_cast < short >(data->fStartTemperature) * 10;
+   fwrite(&tempShort, 2, 1, fCalibFile);
+   tempShort = static_cast < short >(data->fEndTemperature) * 10;
+   fwrite(&tempShort, 2, 1, fCalibFile);
+   fwrite(&data->fMin, 4, 1, fCalibFile);
+   fwrite(&data->fMax, 4, 1, fCalibFile);
+   fwrite(&data->fNumberOfLimitGroups, 1, 1, fCalibFile);
+
+   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
+      chn = data->fChannel[ii];
+      for (j = 0; j < kNumberOfBins; j++) {
+         fwrite(&chn->fLimitGroup[j], 1, 1, fCalibFile);
+         fwrite(&chn->fLookUpOffset[j], 2, 1, fCalibFile);
+         fwrite(&chn->fNumberOfLookUpPoints[j], 1, 1, fCalibFile);
+         for (k = 0; k < chn->fNumberOfLookUpPoints[j]; k++) {
+            fwrite(&chn->fLookUp[j][k], 1, 1, fCalibFile);
+         }
+         for (k = 0; k < data->fNumberOfGridPoints; k++) {
+            fwrite(&chn->fData[j][k], 2, 1, fCalibFile);
+         }
+         fwrite(&chn->fOffsetADC[j], 2, 1, fCalibFile);
+         fwrite(&chn->fOffset[j], 2, 1, fCalibFile);
+      }
+   }
+   fclose(fCalibFile);
+
+   printf("Calibration successfully written to\n\"%s\"\n", str);
+   return true;
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::WriteCalibrationV4(unsigned int chipIndex)
+{
+   if (!fOffset)
+      return false;
+
+   int ii, j;
+   char str[1000];
+   char strt[1000];
+   CalibrationData *data = fCalibrationData[chipIndex];
+   CalibrationData::CalibrationDataChannel * chn;
+
+   // Open File
+   fBoard->GetCalibrationDirectory(strt);
+   sprintf(str, "%s/board%d", strt, fBoard->GetBoardSerialNumber());
+   if (MakeDir(str) == -1) {
+      printf("Error: Cannot create directory \"%s\"\n", str);
+      return false;
+   }
+   sprintf(str, "%s/board%d/ResponseCalib_board%d_chip%d_%dMHz.bin", strt, fBoard->GetBoardSerialNumber(),
+           fBoard->GetBoardSerialNumber(), chipIndex, static_cast < int >(fBoard->GetFrequency() * 1000));
+   fCalibFile = fopen(str, "wb");
+   if (fCalibFile == NULL) {
+      printf("Error: Cannot write to file \"%s\"\n", str);
+      return false;
+   }
+   // Write File
+   for (ii = 0; ii < kNumberOfCalibChannelsV4; ii++) {
+      chn = data->fChannel[ii];
+      for (j = 0; j < kNumberOfBins; j++) {
+         fwrite(&chn->fOffset[j], 2, 1, fCalibFile);
+         fwrite(&chn->fGain[j], 2, 1, fCalibFile);
+      }
+   }
+   fclose(fCalibFile);
+
+   printf("Calibration successfully written to\n\"%s\"\n", str);
+   return true;
+}
+
+/*------------------------------------------------------------------*/
+
+void ResponseCalibration::CalibrationTrigger(int mode, double voltage)
+{
+   fBoard->Reinit();
+   fBoard->EnableAcal(mode, voltage);
+   fBoard->StartDomino();
+   fBoard->SoftTrigger();
+   while (fBoard->IsBusy()) {
+   }
+}
+
+/*------------------------------------------------------------------*/
+
+void ResponseCalibration::CalibrationStart(double voltage)
+{
+   fBoard->SetDominoMode(1);
+   fBoard->EnableAcal(0, voltage);
+   fBoard->StartDomino();
+   fBoard->IsBusy();
+   fBoard->IsBusy();
+   fBoard->IsBusy();
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::RecordCalibrationPoints(int chipNumber)
+{
+   if (!fInitialized)
+      return true;
+   if (fBoard->GetDRSType() == 3)
+      return RecordCalibrationPointsV4(chipNumber);
+   else
+      return RecordCalibrationPointsV3(chipNumber);
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::RecordCalibrationPointsV3(int chipNumber)
+{
+   int j, k, ii;
+   int notdone, nsample;
+   double voltage;
+   float mean;
+   const double minVolt = 0.006;
+   const double xpos[50] =
+       { 0.010, 0.027, 0.052, 0.074, 0.096, 0.117, 0.136, 0.155, 0.173, 0.191, 0.208, 0.226, 0.243, 0.260,
+      0.277, 0.294, 0.310,
+      0.325, 0.342, 0.358, 0.374, 0.390, 0.406, 0.422, 0.439, 0.457, 0.477, 0.497, 0.520, 0.546, 0.577, 0.611,
+      0.656, 0.710,
+      0.772, 0.842, 0.916,
+      0.995, 1.075, 1.157, 1.240, 1.323, 1.407, 1.490, 1.575, 1.659, 1.744, 1.829, 1.914, 2.000
+   };
+
+   // Initialisations
+   if (fCurrentLowVoltPoint == 0) {
+      fBoard->SetDAC(fBoard->fDAC_CLKOFS, 0);
+      // Record Temperature
+      fCalibrationData[chipNumber]->fStartTemperature = static_cast < float >(fBoard->GetTemperature());
+   }
+   // Record current Voltage
+   if (fCurrentLowVoltPoint < fNumberOfPointsLowVolt)
+      voltage =
+          (xpos[0] - minVolt) * fCurrentLowVoltPoint / static_cast <
+          double >(fNumberOfPointsLowVolt) + minVolt;
+   else
+   voltage = xpos[fCurrentPoint];
+   fBoard->SetCalibVoltage(voltage);
+   fResponseY[fCurrentPoint + fCurrentLowVoltPoint] = static_cast < float >(voltage) * 1000;
+
+   // Loop Over Number Of Samples For Statistics
+   for (j = 0; j < fNumberOfSamples; j++) {
+      // Read Out Second Part of the Waveform
+      CalibrationTrigger(3, voltage);
+      fBoard->TransferWaves();
+      for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
+         fBoard->GetRawWave(chipNumber, ii, fWaveFormMode3[ii][j]);
+      }
+      // Read Out First Part of the Waveform
+      CalibrationStart(voltage);
+      CalibrationTrigger(2, voltage);
+      fBoard->TransferWaves();
+      for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
+         fBoard->GetRawWave(chipNumber, ii, fWaveFormMode2[ii][j]);
+      }
+      CalibrationStart(voltage);
+   }
+   // Average Sample Points
+   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
+      for (k = 0; k < kNumberOfBins; k++) {
+         fResponseX[ii][k][fCurrentPoint + fCurrentLowVoltPoint] = 0;
+         for (j = 0; j < fNumberOfSamples; j++) {
+            fSampleUsed[j] = 1;
+            if (k < fNumberOfMode2Bins)
+               fSamples[j] = fWaveFormMode2[ii][j][k];
+            else
+               fSamples[j] = fWaveFormMode3[ii][j][k];
+            fResponseX[ii][k][fCurrentPoint + fCurrentLowVoltPoint] += fSamples[j];
+         }
+         mean = fResponseX[ii][k][fCurrentPoint + fCurrentLowVoltPoint] / fNumberOfSamples;
+         notdone = 1;
+         nsample = fNumberOfSamples;
+         while (notdone) {
+            notdone = 0;
+            for (j = 0; j < fNumberOfSamples; j++) {
+               if (fSampleUsed[j] && abs(static_cast < int >(fSamples[j] - mean)) > 3) {
+                  notdone = 1;
+                  fSampleUsed[j] = 0;
+                  nsample--;
+                  fResponseX[ii][k][fCurrentPoint + fCurrentLowVoltPoint] -= fSamples[j];
+                  mean = fResponseX[ii][k][fCurrentPoint + fCurrentLowVoltPoint] / nsample;
+               }
+            }
+         }
+         fResponseX[ii][k][fCurrentPoint + fCurrentLowVoltPoint] = mean;
+      }
+   }
+   if (fCurrentLowVoltPoint < fNumberOfPointsLowVolt)
+      fCurrentLowVoltPoint++;
+   else
+      fCurrentPoint++;
+
+   if (fCurrentPoint == fNumberOfPoints) {
+      fCalibrationData[chipNumber]->fEndTemperature = static_cast < float >(fBoard->GetTemperature());
+      fRecorded = true;
+      fFitted = false;
+      fOffset = false;
+      fCalibrationData[chipNumber]->fRead = false;
+      fCalibrationData[chipNumber]->fHasOffsetCalibration = false;
+      fBoard->SetCalibVoltage(0.0);
+      fBoard->EnableAcal(1, 0.0);
+      fBoard->SetDAC(fBoard->fDAC_CLKOFS, 0.0);
+      return true;
+   }
+
+   return false;
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::RecordCalibrationPointsV4(int chipNumber)
+{
+   int i, j, k, n;
+   double voltage, s, s2, average, sigma;
+
+   if (fCurrentPoint == 0) {
+      fBoard->SetDominoMode(1);
+      fBoard->EnableAcal(1, 0);
+      fBoard->SoftTrigger();
+      while (fBoard->IsBusy());
+      fBoard->StartDomino();
+      fCalibrationData[chipNumber]->fStartTemperature = static_cast < float >(fBoard->GetTemperature());
+   }
+   voltage = 1.0 * fCurrentPoint / (static_cast < double >(fNumberOfPoints) - 1) +0.1;
+   fBoard->SetCalibVoltage(voltage);
+   Sleep(10);
+   fBoard->SetCalibVoltage(voltage);
+   Sleep(10);
+
+   // One dummy cycle for unknown reasons
+   fBoard->SoftTrigger();
+   while (fBoard->IsBusy());
+   fBoard->StartDomino();
+   Sleep(50);
+   fBoard->TransferWaves();
+
+   // Loop over number of samples for statistics
+   for (i = 0; i < fNumberOfSamples; i++) {
+      if (fBoard->Debug()) {
+         printf("%02d:%02d\r", fNumberOfPoints - fCurrentPoint, fNumberOfSamples - i);
+         fflush(stdout);
+      }
+
+
+      fBoard->SoftTrigger();
+      while (fBoard->IsBusy());
+      fBoard->StartDomino();
+      Sleep(50);
+      fBoard->TransferWaves();
+      for (j = 0; j < kNumberOfCalibChannelsV4; j++) {
+         fBoard->GetRawWave(chipNumber, j, fWaveFormMode3[j][i]);
+      }
+   }
+
+   // Calculate averages
+   for (i = 0; i < kNumberOfCalibChannelsV4; i++) {
+      for (k = 0; k < kNumberOfBins; k++) {
+         s = s2 = 0;
+
+         for (j = 0; j < fNumberOfSamples; j++) {
+            s += fWaveFormMode3[i][j][k];
+            s2 += fWaveFormMode3[i][j][k] * fWaveFormMode3[i][j][k];
+         }
+         n = fNumberOfSamples;
+         average = s / n;
+         sigma = sqrt((n * s2 - s * s) / (n * (n - 1)));
+
+         fResponseX[i][k][fCurrentPoint] = static_cast < float >(average);
+      }
+   }
+
+#ifdef DEBUG_CALIB
+   for (j = 0; j < fNumberOfSamples; j++)
+      printf("%d ", fWaveFormMode3[1][j][10]);
+
+   s = s2 = 0;
+   for (j = 0; j < fNumberOfSamples; j++) {
+      s += fWaveFormMode3[i][j][k];
+      s2 += fWaveFormMode3[i][j][k] * fWaveFormMode3[i][j][k];
+   }
+   n = fNumberOfSamples;
+   average = s / n;
+   sigma = sqrt((n * s2 - s * s) / (n * (n - 1)));
+
+   printf("\n");
+   printf("%1.2lf V: %6.1lf (%1.4lf)\n", voltage,
+          fResponseX[1][10][fCurrentPoint], fResponseX[1][10][fCurrentPoint] / 4096.0);
+#endif
+
+   fCurrentPoint++;
+   if (fCurrentPoint == fNumberOfPoints) {
+      fCalibrationData[chipNumber]->fEndTemperature = static_cast < float >(fBoard->GetTemperature());
+      fRecorded = true;
+      return true;
+   }
+
+   return false;
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::FitCalibrationPoints(int chipNumber)
+{
+   if (!fRecorded || fFitted)
+      return true;
+   if (fBoard->GetDRSType() == 3)
+      return FitCalibrationPointsV4(chipNumber);
+   else
+      return FitCalibrationPointsV3(chipNumber);
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::FitCalibrationPointsV3(int chipNumber)
+{
+   int i, j, k;
+   float x1, x2, y1, y2;
+   float uu;
+   float yc, yr;
+   float xminExt, xrangeExt;
+   float xmin, xrange;
+   float average, averageError, averageExt, averageErrorExt;
+   unsigned short i0, i1;
+
+   CalibrationData *data = fCalibrationData[chipNumber];
+   CalibrationData::CalibrationDataChannel * chn = data->fChannel[fCurrentFitChannel];
+
+   data->DeletePreCalculatedBSpline();
+
+   if (fCurrentFitBin == 0 && fCurrentFitChannel == 0) {
+      data->fNumberOfLimitGroups = 0;
+      data->fMin = 100000;
+      data->fMax = -100000;
+      for (i = 0; i < kNumberOfCalibChannelsV3; i++) {
+         for (j = 0; j < kNumberOfBins; j++) {
+            if (data->fMin > fResponseX[i][j][fNumberOfPointsLowVolt + fNumberOfPoints - 1])
+               data->fMin = fResponseX[i][j][fNumberOfPointsLowVolt + fNumberOfPoints - 1];
+            if (data->fMax < fResponseX[i][j][fNumberOfPointsLowVolt])
+               data->fMax = fResponseX[i][j][fNumberOfPointsLowVolt];
+         }
+      }
+   }
+   // Low Volt
+   i0 = static_cast < unsigned short >(fResponseX[fCurrentFitChannel][fCurrentFitBin][0]);
+   i1 = static_cast <
+       unsigned short >(fResponseX[fCurrentFitChannel][fCurrentFitBin][fNumberOfPointsLowVolt]) + 1;
+   chn->fLookUpOffset[fCurrentFitBin] = i0;
+   delete chn->fLookUp[fCurrentFitBin];
+   if (i0 - i1 + 1 < 2) {
+      chn->fNumberOfLookUpPoints[fCurrentFitBin] = 2;
+      chn->fLookUp[fCurrentFitBin] = new unsigned char[2];
+      chn->fLookUp[fCurrentFitBin][0] = 0;
+      chn->fLookUp[fCurrentFitBin][1] = 0;
+   } else {
+      chn->fNumberOfLookUpPoints[fCurrentFitBin] = i0 - i1 + 1;
+      chn->fLookUp[fCurrentFitBin] = new unsigned char[i0 - i1 + 1];
+      for (i = 0; i < i0 - i1 + 1; i++) {
+         for (j = 0; j < fNumberOfPointsLowVolt; j++) {
+            if (i0 - i >= fResponseX[fCurrentFitChannel][fCurrentFitBin][j + 1]) {
+               x1 = fResponseX[fCurrentFitChannel][fCurrentFitBin][j];
+               x2 = fResponseX[fCurrentFitChannel][fCurrentFitBin][j + 1];
+               y1 = fResponseY[j];
+               y2 = fResponseY[j + 1];
+               chn->fLookUp[fCurrentFitBin][i] =
+                   static_cast < unsigned char >(((y2 - y1) * (i0 - i - x1) / (x2 - x1) + y1) / fPrecision);
+               break;
+            }
+         }
+      }
+   }
+
+   // Copy Points
+   for (i = 0; i < fNumberOfPoints; i++) {
+      fPntX[0][i] = fResponseX[fCurrentFitChannel][fCurrentFitBin][fNumberOfPointsLowVolt + i];
+      fPntY[0][i] = fResponseY[fNumberOfPointsLowVolt + i];
+   }
+   // Fit BSpline
+   for (i = 0; i < fNumberOfPoints; i++) {
+      fUValues[0][i] = static_cast < float >(1 - i / (fNumberOfPoints - 1.));
+   }
+   if (!Approx(fPntX[0], fUValues[0], fNumberOfPoints, fNumberOfGridPoints, fResX[fCurrentFitBin]))
+      return true;
+   if (!Approx(fPntY[0], fUValues[0], fNumberOfPoints, fNumberOfGridPoints, fRes[fCurrentFitBin]))
+      return true;
+
+   // X constant fit
+   for (k = 0; k < fNumberOfXConstPoints - 2; k++) {
+      fPntX[1][k + 1] =
+          GetValue(fResX[fCurrentFitBin],
+                   static_cast < float >(1 - k / static_cast < float >(fNumberOfXConstPoints - 3)),
+                   fNumberOfGridPoints);
+      fPntY[1][k + 1] =
+          GetValue(fRes[fCurrentFitBin],
+                   static_cast < float >(1 - k / static_cast < float >(fNumberOfXConstPoints - 3)),
+                   fNumberOfGridPoints);
+   }
+   xmin = fPntX[1][fNumberOfXConstPoints - 2];
+   xrange = fPntX[1][1] - xmin;
+
+   for (i = 0; i < fNumberOfXConstPoints - 2; i++) {
+      fUValues[1][i + 1] = (fPntX[1][i + 1] - xmin) / xrange;
+   }
+
+   if (!Approx
+       (&fPntY[1][1], &fUValues[1][1], fNumberOfXConstPoints - 2, fNumberOfXConstGridPoints, chn->fTempData))
+      return true;
+
+   // error statistics
+   if (fShowStatistics) {
+      for (i = 0; i < fNumberOfPoints; i++) {
+         uu = (fPntX[0][i] - xmin) / xrange;
+         yc = GetValue(chn->fTempData, uu, fNumberOfXConstGridPoints);
+         yr = fPntY[0][i];
+         fStatisticsApprox[i][fCurrentFitBin + fCurrentFitChannel * kNumberOfBins] = yc - yr;
+      }
+   }
+   // Add min and max point
+   chn->fLimitGroup[fCurrentFitBin] = 0;
+   while (xmin - kBSplineXMinOffset > data->fMin + kBSplineXMinOffset * chn->fLimitGroup[fCurrentFitBin]) {
+      chn->fLimitGroup[fCurrentFitBin]++;
+   }
+   if (data->fNumberOfLimitGroups <= chn->fLimitGroup[fCurrentFitBin])
+      data->fNumberOfLimitGroups = chn->fLimitGroup[fCurrentFitBin] + 1;
+   xminExt = data->fMin + kBSplineXMinOffset * chn->fLimitGroup[fCurrentFitBin];
+   xrangeExt = data->fMax - xminExt;
+
+   fPntX[1][0] = data->fMax;
+   uu = (fPntX[1][0] - xmin) / xrange;
+   fPntY[1][0] = GetValue(chn->fTempData, uu, fNumberOfXConstGridPoints);
+
+   fPntX[1][fNumberOfXConstPoints - 1] = xminExt;
+   uu = (fPntX[1][fNumberOfXConstPoints - 1] - xmin) / xrange;
+   fPntY[1][fNumberOfXConstPoints - 1] = GetValue(chn->fTempData, uu, fNumberOfXConstGridPoints);
+
+   for (i = 0; i < fNumberOfXConstPoints; i++) {
+      fUValues[1][i] = (fPntX[1][i] - xminExt) / xrangeExt;
+   }
+
+   if (!Approx(fPntY[1], fUValues[1], fNumberOfXConstPoints, fNumberOfXConstGridPoints, chn->fTempData))
+      return true;
+
+   // error statistics
+   if (fShowStatistics) {
+      for (i = 0; i < fNumberOfPoints; i++) {
+         uu = (fPntX[0][i] - xminExt) / xrangeExt;
+         yc = GetValue(chn->fTempData, uu, fNumberOfXConstGridPoints);
+         yr = fPntY[0][i];
+         fStatisticsApproxExt[i][fCurrentFitBin + fCurrentFitChannel * kNumberOfBins] = yc - yr;
+      }
+   }
+   for (i = 0; i < fNumberOfXConstGridPoints; i++) {
+      chn->fData[fCurrentFitBin][i] = static_cast < short >(chn->fTempData[i] / fPrecision);
+   }
+
+   // write end of file
+   fCurrentFitBin++;
+   if (fCurrentFitBin == kNumberOfBins) {
+      fCurrentFitChannel++;
+      fCurrentFitBin = 0;
+   }
+   if (fCurrentFitChannel == kNumberOfCalibChannelsV3) {
+      if (fShowStatistics) {
+         for (i = 0; i < fNumberOfPoints; i++) {
+            average = 0;
+            averageError = 0;
+            averageExt = 0;
+            averageErrorExt = 0;
+            for (j = 0; j < kNumberOfCalibChannelsV3 * kNumberOfBins; j++) {
+               average += fStatisticsApprox[i][j];
+               averageError += fStatisticsApprox[i][j] * fStatisticsApprox[i][j];
+               averageExt += fStatisticsApproxExt[i][j];
+               averageErrorExt += fStatisticsApproxExt[i][j] * fStatisticsApproxExt[i][j];
+            }
+            average /= kNumberOfCalibChannelsV3 * kNumberOfBins;
+            averageError =
+                sqrt((averageError -
+                      average * average / kNumberOfCalibChannelsV3 * kNumberOfBins) /
+                     (kNumberOfCalibChannelsV3 * kNumberOfBins - 1));
+            averageExt /= kNumberOfCalibChannelsV3 * kNumberOfBins;
+            averageErrorExt =
+                sqrt((averageErrorExt -
+                      averageExt * averageExt / kNumberOfCalibChannelsV3 * kNumberOfBins) /
+                     (kNumberOfCalibChannelsV3 * kNumberOfBins - 1));
+            printf("Error at %3.1f V : % 2.3f +- % 2.3f ; % 2.3f +- % 2.3f\n", fPntY[0][i], average,
+                   averageError, averageExt, averageErrorExt);
+         }
+      }
+      fFitted = true;
+      fOffset = false;
+      fCalibrationData[chipNumber]->fRead = true;
+      fCalibrationData[chipNumber]->fHasOffsetCalibration = false;
+      data->PreCalculateBSpline();
+      return true;
+   }
+   return false;
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::FitCalibrationPointsV4(int chipNumber)
+{
+   if (!fRecorded || fFitted)
+      return true;
+   int i;
+   double par[2];
+   static int error;
+
+   CalibrationData *data = fCalibrationData[chipNumber];
+   CalibrationData::CalibrationDataChannel * chn = data->fChannel[fCurrentFitChannel];
+
+   if (fCurrentFitBin == 0 && fCurrentFitChannel == 0) {
+      error = 0;
+      for (i = 0; i < fNumberOfPoints; i++)
+         fWWFit[i] = 1;
+   }
+
+   for (i = 0; i < fNumberOfPoints; i++) {
+      fXXFit[i] = 1.0 * i / (static_cast < double >(fNumberOfPoints) - 1) +0.1;
+      fYYFit[i] = fResponseX[fCurrentFitChannel][fCurrentFitBin][i];
+      if (fCurrentFitBin == 10 && fCurrentFitChannel == 1) {
+         fXXSave[i] = fXXFit[i];
+         fYYSave[i] = fYYFit[i];
+      }
+   }
+
+   // DRSBoard::LinearRegression(fXXFit, fYYFit, fNumberOfPoints, &par[1], &par[0]);
+   // exclude first two points (sometimes are on limit of FADC)
+   DRSBoard::LinearRegression(fXXFit + 2, fYYFit + 2, fNumberOfPoints - 2, &par[1], &par[0]);
+
+   chn->fOffset[fCurrentFitBin] = static_cast < unsigned short >(par[0] + 0.5);
+   chn->fGain[fCurrentFitBin] = static_cast < unsigned short >(par[1] + 0.5);
+
+   // Remember min/max of gain
+   if (fCurrentFitBin == 0 && fCurrentFitChannel == 0)
+      fGainMin = fGainMax = chn->fGain[0];
+   if (chn->fGain[fCurrentFitBin] < fGainMin)
+      fGainMin = chn->fGain[fCurrentFitBin];
+   if (chn->fGain[fCurrentFitBin] > fGainMax)
+      fGainMax = chn->fGain[fCurrentFitBin];
+
+   // abort if outside normal region
+   if (chn->fGain[fCurrentFitBin] / 4096.0 < 0.8 || chn->fGain[fCurrentFitBin] / 4096.0 > 1) {
+      error++;
+
+      if (error < 20)
+         printf("Gain=%1.3lf for bin %d on channel %d on chip %d outside valid region\n",
+                chn->fGain[fCurrentFitBin] / 4096.0, fCurrentFitBin, fCurrentFitChannel, chipNumber);
+   }
+
+   if (fCurrentFitChannel == 1 && fCurrentFitBin == 10) {
+      for (i = 0; i < fNumberOfPoints; i++) {
+         fXXSave[i] = fXXFit[i];
+         fYYSave[i] = (fYYFit[i] - chn->fOffset[10]) / chn->fGain[10] - fXXFit[i];
+      }
+   }
+
+   fCurrentFitBin++;
+   if (fCurrentFitBin == kNumberOfBins) {
+      fCurrentFitChannel++;
+      fCurrentFitBin = 0;
+   }
+   if (fCurrentFitChannel == kNumberOfCalibChannelsV4) {
+
+      if (fBoard->Debug()) {
+         printf("Gain min=%1.3lf max=%1.3lf\n", fGainMin / 4096.0, fGainMax / 4096.0);
+         fflush(stdout);
+      }
+      // allow up to three bad bins
+      if (error > 3) {
+         printf("Aborting calibration!\n");
+         return true;
+      }
+
+      fFitted = true;
+      fOffset = false;
+      fCalibrationData[chipNumber]->fRead = true;
+      fCalibrationData[chipNumber]->fHasOffsetCalibration = false;
+      return true;
+   }
+
+   return false;
+}
+
+unsigned int millitime()
+{
+#ifdef _MSC_VER
+
+   return (int) GetTickCount();
+
+#else
+   struct timeval tv;
+
+   gettimeofday(&tv, NULL);
+
+   return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+#endif
+   return 0;
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::OffsetCalibration(int chipNumber)
+{
+   if (!fFitted || fOffset)
+      return true;
+   if (fBoard->GetDRSType() == 3)
+      return OffsetCalibrationV4(chipNumber);
+   else
+      return OffsetCalibrationV3(chipNumber);
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::OffsetCalibrationV3(int chipNumber)
+{
+   int k, ii, j;
+   int t1, t2;
+   float mean, error;
+   CalibrationData *data = fCalibrationData[chipNumber];
+   CalibrationData::CalibrationDataChannel * chn;
+
+   if (fCurrentSample == 0) {
+      data->fHasOffsetCalibration = false;
+      fBoard->SetCalibVoltage(0.0);
+      fBoard->EnableAcal(0, 0.0);
+   }
+   // Loop Over Number Of Samples For Statistics
+   t1 = millitime();
+   fBoard->SoftTrigger();
+   while (fBoard->IsBusy()) {
+   }
+   fBoard->TransferWaves();
+   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
+      fBoard->GetRawWave(chipNumber, ii, fWaveFormOffsetADC[ii][fCurrentSample]);
+      fBoard->CalibrateWaveform(chipNumber, ii, fWaveFormOffsetADC[ii][fCurrentSample],
+                                fWaveFormOffset[ii][fCurrentSample], true, false, false, 0, true);
+   }
+   fBoard->StartDomino();
+   fBoard->IsBusy();
+   fBoard->IsBusy();
+   fBoard->IsBusy();
+   t2 = millitime();
+   while (t2 - t1 < (1000 / fTriggerFrequency)) {
+      t2 = millitime();
+   }
+   fCurrentSample++;
+
+   if (fCurrentSample == fNumberOfSamples) {
+      // Average Sample Points
+      float *sample = new float[fNumberOfSamples];
+      for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
+         chn = data->fChannel[ii];
+         for (k = 0; k < kNumberOfBins; k++) {
+            for (j = 0; j < fNumberOfSamples; j++)
+               sample[j] = static_cast < float >(fWaveFormOffset[ii][j][k]);
+            Average(1, sample, fNumberOfSamples, mean, error, 2);
+            chn->fOffset[k] = static_cast < short >(mean);
+            for (j = 0; j < fNumberOfSamples; j++)
+               sample[j] = fWaveFormOffsetADC[ii][j][k];
+            Average(1, sample, fNumberOfSamples, mean, error, 2);
+            chn->fOffsetADC[k] = static_cast < unsigned short >(mean);
+         }
+      }
+      fOffset = true;
+      fCalibrationData[chipNumber]->fHasOffsetCalibration = true;
+      delete sample;
+      return true;
+   }
+
+   return false;
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::OffsetCalibrationV4(int chipNumber)
+{
+   int k, ii, j;
+   float mean, error;
+   CalibrationData *data = fCalibrationData[chipNumber];
+   CalibrationData::CalibrationDataChannel * chn;
+
+   /* switch DRS to input, hope that no real signal occurs */
+   if (fCurrentSample == 0) {
+      data->fHasOffsetCalibration = false;
+      fBoard->SetCalibVoltage(0.0);
+      fBoard->EnableAcal(0, 0.0);
+      /* one dummy trigger for unknown reasons */
+      fBoard->SoftTrigger();
+      while (fBoard->IsBusy());
+      fBoard->StartDomino();
+      Sleep(50);
+   }
+   // Loop Over Number Of Samples For Statistics
+   fBoard->SoftTrigger();
+   while (fBoard->IsBusy());
+   fBoard->TransferWaves();
+   for (ii = 0; ii < kNumberOfCalibChannelsV4; ii++)
+      fBoard->GetRawWave(chipNumber, ii, fWaveFormOffsetADC[ii][fCurrentSample]);
+
+   fBoard->StartDomino();
+   Sleep(50);
+   fCurrentSample++;
+
+   if (fBoard->Debug()) {
+      printf("%02d\r", fNumberOfSamples - fCurrentSample);
+      fflush(stdout);
+   }
+
+   if (fCurrentSample == fNumberOfSamples) {
+      // Average Sample Points
+      float *sample = new float[fNumberOfSamples];
+      for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
+         chn = data->fChannel[ii];
+         for (k = 0; k < kNumberOfBins; k++) {
+            for (j = 0; j < fNumberOfSamples; j++)
+               sample[j] = static_cast < float >(fWaveFormOffsetADC[ii][j][k]);
+            Average(1, sample, fNumberOfSamples, mean, error, 2);
+            chn->fOffset[k] = static_cast < unsigned short >(mean);
+         }
+      }
+      fOffset = true;
+      fCalibrationData[chipNumber]->fHasOffsetCalibration = true;
+      delete sample;
+      return true;
+   }
+
+   return false;
+}
+
+/*------------------------------------------------------------------*/
+
+void ResponseCalibration::InitFields(int numberOfPointsLowVolt, int numberOfPoints, int numberOfMode2Bins,
+                                     int numberOfSamples, int numberOfGridPoints, int numberOfXConstPoints,
+                                     int numberOfXConstGridPoints, double triggerFrequency,
+                                     int showStatistics)
+{
+   int ii, j, i;
+   fInitialized = true;
+   fNumberOfPointsLowVolt = numberOfPointsLowVolt;
+   fNumberOfPoints = numberOfPoints;
+   fNumberOfMode2Bins = numberOfMode2Bins;
+   fNumberOfSamples = numberOfSamples;
+   fNumberOfGridPoints = numberOfGridPoints;
+   fNumberOfXConstPoints = numberOfXConstPoints;
+   fNumberOfXConstGridPoints = numberOfXConstGridPoints;
+   fTriggerFrequency = triggerFrequency;
+   fShowStatistics = showStatistics;
+   fCurrentPoint = 0;
+   fCurrentSample = 0;
+   fCurrentFitChannel = 0;
+   fCurrentFitBin = 0;
+   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
+      for (j = 0; j < kNumberOfBins; j++) {
+         fResponseX[ii][j] = new float[fNumberOfPoints + fNumberOfPointsLowVolt];
+      }
+   }
+   fResponseY = new float[fNumberOfPoints + fNumberOfPointsLowVolt];
+   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
+      fWaveFormMode3[ii] = new unsigned short *[fNumberOfSamples];
+      fWaveFormMode2[ii] = new unsigned short *[fNumberOfSamples];
+      fWaveFormOffset[ii] = new short *[fNumberOfSamples];
+      fWaveFormOffsetADC[ii] = new unsigned short *[fNumberOfSamples];
+      for (i = 0; i < fNumberOfSamples; i++) {
+         fWaveFormMode3[ii][i] = new unsigned short[kNumberOfBins];
+         fWaveFormMode2[ii][i] = new unsigned short[kNumberOfBins];
+         fWaveFormOffset[ii][i] = new short[kNumberOfBins];
+         fWaveFormOffsetADC[ii][i] = new unsigned short[kNumberOfBins];
+      }
+   }
+   fSamples = new unsigned short[fNumberOfSamples];
+   fSampleUsed = new int[fNumberOfSamples];
+
+   for (j = 0; j < kNumberOfBins; j++) {
+      fRes[j] = new float[fNumberOfGridPoints];
+      fResX[j] = new float[fNumberOfGridPoints];
+   }
+   for (i = 0; i < 2; i++) {
+      fPntX[i] = new float[fNumberOfPoints * (1 - i) + fNumberOfXConstPoints * i];
+      fPntY[i] = new float[fNumberOfPoints * (1 - i) + fNumberOfXConstPoints * i];
+      fUValues[i] = new float[fNumberOfPoints * (1 - i) + fNumberOfXConstPoints * i];
+   }
+   fXXFit = new double[fNumberOfPoints];
+   fYYFit = new double[fNumberOfPoints];
+   fWWFit = new double[fNumberOfPoints];
+   fYYFitRes = new double[fNumberOfPoints];
+   fYYSave = new double[fNumberOfPoints];
+   fXXSave = new double[fNumberOfPoints];
+
+   fStatisticsApprox = new float *[fNumberOfPoints];
+   fStatisticsApproxExt = new float *[fNumberOfPoints];
+   for (i = 0; i < fNumberOfPoints; i++) {
+      fStatisticsApprox[i] = new float[kNumberOfCalibChannelsV3 * kNumberOfBins];
+      fStatisticsApproxExt[i] = new float[kNumberOfCalibChannelsV3 * kNumberOfBins];
+   }
+   for (i = 0; i < kNumberOfChipsMax; i++) {
+      fCalibrationData[i] = new CalibrationData(numberOfXConstGridPoints);
+   }
+}
+
+/*------------------------------------------------------------------*/
+
+void ResponseCalibration::DeleteFields()
+{
+   if (!fInitialized)
+      return;
+   fInitialized = false;
+   int ii, j, i;
+   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
+      for (j = 0; j < kNumberOfBins; j++) {
+         delete fResponseX[ii][j];
+      }
+   }
+   delete fResponseY;
+   for (ii = 0; ii < kNumberOfCalibChannelsV3; ii++) {
+      for (i = 0; i < fNumberOfSamples; i++) {
+         if (fWaveFormMode3[ii] != NULL)
+            delete fWaveFormMode3[ii][i];
+         if (fWaveFormMode2[ii] != NULL)
+            delete fWaveFormMode2[ii][i];
+         if (fWaveFormOffset[ii] != NULL)
+            delete fWaveFormOffset[ii][i];
+         if (fWaveFormOffsetADC[ii] != NULL)
+            delete fWaveFormOffsetADC[ii][i];
+      }
+      delete fWaveFormMode3[ii];
+      delete fWaveFormMode2[ii];
+      delete fWaveFormOffset[ii];
+      delete fWaveFormOffsetADC[ii];
+   }
+   delete fSamples;
+   delete fSampleUsed;
+
+   for (j = 0; j < kNumberOfBins; j++) {
+      delete fRes[j];
+      delete fResX[j];
+   }
+   for (i = 0; i < 2; i++) {
+      delete fPntX[i];
+      delete fPntY[i];
+      delete fUValues[i];
+   }
+   delete fXXFit;
+   delete fYYFit;
+   delete fWWFit;
+   delete fYYFitRes;
+   delete fYYSave;
+   delete fXXSave;
+
+   for (i = 0; i < fNumberOfPoints; i++) {
+      delete fStatisticsApprox[i];
+      delete fStatisticsApproxExt[i];
+   }
+   delete fStatisticsApprox;
+   delete fStatisticsApproxExt;
+   for (i = 0; i < kNumberOfChipsMax; i++)
+      delete fCalibrationData[i];
+}
+
+/*------------------------------------------------------------------*/
+
+double ResponseCalibration::GetTemperature(unsigned int chipIndex)
+{
+   if (fCalibrationData[chipIndex] == NULL)
+      return 0;
+   if (!fCalibrationData[chipIndex]->fRead)
+      return 0;
+   return (fCalibrationData[chipIndex]->fStartTemperature + fCalibrationData[chipIndex]->fEndTemperature) / 2;
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::Calibrate(unsigned int chipIndex, unsigned int channel, unsigned short *adcWaveform,
+                                    short *uWaveform, int triggerCell, float threshold, bool offsetCalib)
+{
+   int i;
+   unsigned int NumberOfCalibChannels;
+   int hasOffset;
+   bool aboveThreshold;
+   float wave, v;
+   int j, irot;
+
+   CalibrationData *data = fCalibrationData[chipIndex];
+   CalibrationData::CalibrationDataChannel * chn;
+
+   if (fBoard->GetDRSType() == 3)
+      NumberOfCalibChannels = kNumberOfCalibChannelsV4;
+   else
+      NumberOfCalibChannels = kNumberOfCalibChannelsV3;
+
+   if (channel >= NumberOfCalibChannels || data == NULL) {
+      for (i = 0; i < kNumberOfBins; i++) {
+         irot = i;
+         if (triggerCell > -1)
+            irot = (triggerCell + i) % kNumberOfBins;
+
+         uWaveform[i] = adcWaveform[irot];
+      }
+      return true;
+   }
+   if (!data->fRead) {
+      for (i = 0; i < kNumberOfBins; i++) {
+         uWaveform[i] = adcWaveform[i];
+      }
+      return true;
+   }
+
+   chn = data->fChannel[channel];
+
+   hasOffset = data->fHasOffsetCalibration;
+   aboveThreshold = (threshold == 0);   // if threshold equal zero, always return true
+
+   short offset;
+
+   // Calibrate
+   for (i = 0; i < kNumberOfBins; i++) {
+      if (fBoard->GetDRSType() != 3) {
+         irot = i;
+         if (triggerCell > -1)
+            irot = (triggerCell + i) % kNumberOfBins;
+         offset = offsetCalib ? chn->fOffset[irot] : 0;
+         if (adcWaveform[irot] > chn->fLookUpOffset[irot]) {
+            uWaveform[i] =
+                ((chn->fLookUp[irot][0] - chn->fLookUp[irot][1]) * (adcWaveform[irot] -
+                                                                    chn->fLookUpOffset[irot]) +
+                 chn->fLookUp[irot][0]);
+         } else if (adcWaveform[irot] <= chn->fLookUpOffset[irot]
+                    && adcWaveform[irot] > chn->fLookUpOffset[irot] - chn->fNumberOfLookUpPoints[irot]) {
+            uWaveform[i] = chn->fLookUp[irot][chn->fLookUpOffset[irot] - adcWaveform[irot]];
+         } else {
+            wave = 0;
+            for (j = 0; j < kBSplineOrder; j++) {
+               wave +=
+                   chn->fData[irot][data->fBSplineOffsetLookUp[adcWaveform[irot]][chn->fLimitGroup[irot]] + j]
+                   * data->fBSplineLookUp[adcWaveform[irot]][chn->fLimitGroup[irot]][j];
+            }
+            uWaveform[i] = static_cast < short >(wave);
+         }
+         // Offset Calibration
+         if (hasOffset)
+            uWaveform[i] -= offset;
+      } else {
+         irot = i;
+         if (triggerCell > -1)
+            irot = (triggerCell + i) % kNumberOfBins;
+#if 0                           /* not enabled yet for DRS3 */
+         offset = offsetCalib ? chn->fOffset[irot] : 0;
+#else
+         offset = chn->fOffset[irot];
+#endif
+         v = static_cast < float >(adcWaveform[irot] - offset) / chn->fGain[irot];
+         uWaveform[i] = static_cast < short >(v * 1000 / GetPrecision() + 0.5);
+      }
+
+      // Check for Threshold
+      if (!aboveThreshold) {
+         if (uWaveform[i] >= threshold)
+            aboveThreshold = true;
+      }
+   }
+   return aboveThreshold;
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::SubtractADCOffset(unsigned int chipIndex, unsigned int channel,
+                                            unsigned short *adcWaveform,
+                                            unsigned short *adcCalibratedWaveform,
+                                            unsigned short newBaseLevel)
+{
+   int i;
+   unsigned int NumberOfCalibChannels;
+   CalibrationData *data = fCalibrationData[chipIndex];
+   CalibrationData::CalibrationDataChannel * chn;
+
+   if (fBoard->GetDRSType() == 3)
+      NumberOfCalibChannels = kNumberOfCalibChannelsV4;
+   else
+      NumberOfCalibChannels = kNumberOfCalibChannelsV3;
+
+   if (channel >= NumberOfCalibChannels || data == NULL)
+      return false;
+   if (!data->fRead || !data->fHasOffsetCalibration)
+      return false;
+
+   chn = data->fChannel[channel];
+   for (i = 0; i < kNumberOfBins; i++)
+      adcCalibratedWaveform[i] = adcWaveform[i] - chn->fOffsetADC[i] + newBaseLevel;
+   return true;
+}
+
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::ReadCalibration(unsigned int chipIndex)
+{
+  if (fBoard->GetDRSType() == 3) return ReadCalibrationV4(chipIndex);
+  else return ReadCalibrationV3(chipIndex);
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::ReadCalibrationV3(unsigned int chipIndex)
+{
+   int k, l, m, num;
+   unsigned char ng;
+   short tempShort;
+   char fileName[2000];
+   FILE *fileHandle;
+   char calibDir[1000];
+
+   // Read Response Calibration
+   delete fCalibrationData[chipIndex];
+   fCalibrationData[chipIndex] = NULL;
+
+   fBoard->GetCalibrationDirectory(calibDir);
+   sprintf(fileName, "%s/board%d/ResponseCalib_board%d_chip%d_%dMHz.bin", calibDir,
+           fBoard->GetBoardSerialNumber(), fBoard->GetBoardSerialNumber(), chipIndex,
+           static_cast < int >(fBoard->GetFrequency() * 1000));
+
+   fileHandle = fopen(fileName, "rb");
+   if (fileHandle == NULL) {
+      printf("Board %d --> Could not find response calibration file:\n", fBoard->GetBoardSerialNumber());
+      printf("%s\n", fileName);
+      return false;
+   }
+   // Number Of Grid Points
+   num = fread(&ng, 1, 1, fileHandle);
+   if (num != 1) {
+      printf("Error while reading response calibration file '%s'\n", fileName);
+      printf("   at 'NumberOfGridPoints'.\n");
+      return false;
+   }
+
+   fCalibrationData[chipIndex] = new CalibrationData(ng);
+   CalibrationData *data = fCalibrationData[chipIndex];
+   CalibrationData::CalibrationDataChannel * chn;
+   data->fRead = true;
+   data->fHasOffsetCalibration = 1;
+   data->DeletePreCalculatedBSpline();
+   fCalibrationValid[chipIndex] = true;
+
+   // Start Temperature
+   num = fread(&tempShort, 2, 1, fileHandle);
+   if (num != 1) {
+      printf("Error while reading response calibration file '%s'\n", fileName);
+      printf("   at 'StartTemperature'.\n");
+      return false;
+   }
+   data->fStartTemperature = static_cast < float >(tempShort) / 10;
+   // End Temperature
+   num = fread(&tempShort, 2, 1, fileHandle);
+   if (num != 1) {
+      printf("Error while reading response calibration file '%s'\n", fileName);
+      printf("   at 'EndTemperature'.\n");
+      return false;
+   }
+   data->fEndTemperature = static_cast < float >(tempShort) / 10;
+   if (fBoard->GetDRSType() != 3) {
+      // Min
+      num = fread(&data->fMin, 4, 1, fileHandle);
+      if (num != 1) {
+         printf("Error while reading response calibration file '%s'\n", fileName);
+         printf("   at 'Min'.\n");
+         return false;
+      }
+      // Max
+      num = fread(&data->fMax, 4, 1, fileHandle);
+      if (num != 1) {
+         printf("Error while reading response calibration file '%s'\n", fileName);
+         printf("   at 'Max'.\n");
+         return false;
+      }
+      // Number Of Limit Groups
+      num = fread(&data->fNumberOfLimitGroups, 1, 1, fileHandle);
+      if (num != 1) {
+         printf("Error while reading response calibration file '%s'\n", fileName);
+         printf("   at 'NumberOfLimitGroups'.\n");
+         return false;
+      }
+   }
+   // read channel
+   for (k = 0; k < kNumberOfCalibChannelsV3; k++) {
+      chn = data->fChannel[k];
+      for (l = 0; l < kNumberOfBins; l++) {
+         if (fBoard->GetDRSType() != 3) {
+            // Range Group
+            num = fread(&chn->fLimitGroup[l], 1, 1, fileHandle);
+            if (num != 1) {
+               printf("Error while reading response calibration file '%s'\n", fileName);
+               printf("   at 'RangeGroup' of channel %d bin %d.\n", k, l);
+               return false;
+            }
+            // Look Up Offset
+            num = fread(&chn->fLookUpOffset[l], 2, 1, fileHandle);
+            if (num != 1) {
+               printf("Error while reading response calibration file '%s'\n", fileName);
+               printf("   at 'LookUpOffset' of channel %d bin %d.\n", k, l);
+               return false;
+            }
+            // Number Of Look Up Points
+            num = fread(&chn->fNumberOfLookUpPoints[l], 1, 1, fileHandle);
+            if (num != 1) {
+               printf("Error while reading response calibration file '%s'\n", fileName);
+               printf("   at 'NumberOfLookUpPoints' of channel %d bin %d.\n", k, l);
+               return false;
+            }
+            // Look Up Points
+            delete chn->fLookUp[l];
+            chn->fLookUp[l] = new unsigned char[chn->fNumberOfLookUpPoints[l]];
+            for (m = 0; m < chn->fNumberOfLookUpPoints[l]; m++) {
+               num = fread(&chn->fLookUp[l][m], 1, 1, fileHandle);
+               if (num != 1) {
+                  printf("Error while reading response calibration file '%s'\n", fileName);
+                  printf("   at 'LookUp %d' of channel %d bin %d.\n", m, k, l);
+                  return false;
+               }
+            }
+            // Points
+            for (m = 0; m < data->fNumberOfGridPoints; m++) {
+               num = fread(&chn->fData[l][m], 2, 1, fileHandle);
+               if (num != 1) {
+                  printf("Error while reading response calibration file '%s'\n", fileName);
+                  printf("   at 'Point %d' of channel %d bin %d.\n", m, k, l);
+                  return false;
+               }
+            }
+            // ADC Offset
+            num = fread(&chn->fOffsetADC[l], 2, 1, fileHandle);
+            if (num != 1) {
+               printf("Error while reading response calibration file '%s'\n", fileName);
+               printf("   at 'ADC Offset' of channel %d bin %d.\n", k, l);
+               return false;
+            }
+         }
+         // Offset
+         num = fread(&chn->fOffset[l], 2, 1, fileHandle);
+         if (num != 1) {
+            printf("Error while reading response calibration file '%s'\n", fileName);
+            printf("   at 'Offset' of channel %d bin %d.\n", k, l);
+            return false;
+         }
+         if (fBoard->GetDRSType() == 3) {
+            // Gain
+            num = fread(&chn->fGain[l], 2, 1, fileHandle);
+            if (num != 1) {
+               printf("Error while reading response calibration file '%s'\n", fileName);
+               printf("   at 'Gain' of channel %d bin %d.\n", k, l);
+               return false;
+            }
+         }
+      }
+   }
+   fclose(fileHandle);
+
+   if (fBoard->GetDRSType() != 3) {
+      data->PreCalculateBSpline();
+   }
+
+   return true;
+}
+
+/*------------------------------------------------------------------*/
+
+bool ResponseCalibration::ReadCalibrationV4(unsigned int chipIndex)
+{
+   int k, l, num;
+   char fileName[2000];
+   FILE *fileHandle;
+   char calibDir[1000];
+
+   // Read Response Calibration
+
+   fBoard->GetCalibrationDirectory(calibDir);
+   sprintf(fileName, "%s/board%d/ResponseCalib_board%d_chip%d_%dMHz.bin", calibDir,
+           fBoard->GetBoardSerialNumber(), fBoard->GetBoardSerialNumber(), chipIndex,
+           static_cast < int >(fBoard->GetFrequency() * 1000));
+
+   fileHandle = fopen(fileName, "rb");
+   if (fileHandle == NULL) {
+      printf("Board %d --> Could not find response calibration file:\n", fBoard->GetBoardSerialNumber());
+      printf("%s\n", fileName);
+      return false;
+   }
+
+   if (fInitialized)
+      delete fCalibrationData[chipIndex];
+   fCalibrationData[chipIndex] = new CalibrationData(1);
+   CalibrationData *data = fCalibrationData[chipIndex];
+   CalibrationData::CalibrationDataChannel * chn;
+   data->fRead = true;
+   data->fHasOffsetCalibration = 1;
+   fCalibrationValid[chipIndex] = true;
+   data->fStartTemperature = 0;
+   data->fEndTemperature = 0;
+
+   // read channel
+   for (k = 0; k < kNumberOfCalibChannelsV4; k++) {
+      chn = data->fChannel[k];
+      for (l = 0; l < kNumberOfBins; l++) {
+         // Offset
+         num = fread(&chn->fOffset[l], 2, 1, fileHandle);
+         if (num != 1) {
+            printf("Error while reading response calibration file '%s'\n", fileName);
+            printf("   at 'Offset' of channel %d bin %d.\n", k, l);
+            return false;
+         }
+         if (fBoard->GetDRSType() == 3) {
+            // Gain
+            num = fread(&chn->fGain[l], 2, 1, fileHandle);
+            if (num != 1) {
+               printf("Error while reading response calibration file '%s'\n", fileName);
+               printf("   at 'Gain' of channel %d bin %d.\n", k, l);
+               return false;
+            }
+         }
+      }
+   }
+
+   fclose(fileHandle);
+   return true;
+}
+
+/*------------------------------------------------------------------*/
+
+float ResponseCalibration::GetValue(float *coefficients, float u, int n)
+{
+   int j, ii;
+   float bsplines[4];
+   ii = CalibrationData::CalculateBSpline(n, u, bsplines);
+
+   float s = 0;
+   for (j = 0; j < kBSplineOrder; j++) {
+      s += coefficients[ii + j] * bsplines[j];
+   }
+   return s;
+}
+
+/*------------------------------------------------------------------*/
+
+int ResponseCalibration::Approx(float *p, float *uu, int np, int nu, float *coef)
+{
+   int i, iu, j;
+
+   const int mbloc = 50;
+   int ip = 0;
+   int ir = 0;
+   int mt = 0;
+   int ileft, irow;
+   float bu[kBSplineOrder];
+   float *matrix[kBSplineOrder + 2];
+   for (i = 0; i < kBSplineOrder + 2; i++)
+      matrix[i] = new float[mbloc + nu + 1];
+   for (iu = kBSplineOrder - 1; iu < nu; iu++) {
+      for (i = 0; i < np; i++) {
+         if (1 <= uu[i])
+            ileft = nu - 1;
+         else if (uu[i] < 0)
+            ileft = kBSplineOrder - 2;
+         else
+            ileft = kBSplineOrder - 1 + static_cast < int >(uu[i] * (nu - kBSplineOrder + 1));
+         if (ileft != iu)
+            continue;
+         irow = ir + mt;
+         mt++;
+         CalibrationData::CalculateBSpline(nu, uu[i], bu);
+         for (j = 0; j < kBSplineOrder; j++) {
+            matrix[j][irow] = bu[j];
+         }
+         matrix[kBSplineOrder][irow] = p[i];
+         if (mt < mbloc)
+            continue;
+         LeastSquaresAccumulation(matrix, kBSplineOrder, &ip, &ir, mt, iu - kBSplineOrder + 1);
+         mt = 0;
+      }
+      if (mt == 0)
+         continue;
+      LeastSquaresAccumulation(matrix, kBSplineOrder, &ip, &ir, mt, iu - kBSplineOrder + 1);
+      mt = 0;
+   }
+   if (!LeastSquaresSolving(matrix, kBSplineOrder, ip, ir, coef, nu)) {
+      for (i = 0; i < kBSplineOrder + 2; i++)
+         delete matrix[i];
+      return 0;
+   }
+
+   for (i = 0; i < kBSplineOrder + 2; i++)
+      delete matrix[i];
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+void ResponseCalibration::LeastSquaresAccumulation(float **matrix, int nb, int *ip, int *ir, int mt, int jt)
+{
+   int i, j, l, mu, k, kh;
+   float rho;
+
+   if (mt <= 0)
+      return;
+   if (jt != *ip) {
+      if (jt > (*ir)) {
+         for (i = 0; i < mt; i++) {
+            for (j = 0; j < nb + 1; j++) {
+               matrix[j][jt + mt - i] = matrix[j][(*ir) + mt - i];
+            }
+         }
+         for (i = 0; i < jt - (*ir); i++) {
+            for (j = 0; j < nb + 1; j++) {
+               matrix[j][(*ir) + i] = 0;
+            }
+         }
+         *ir = jt;
+      }
+      mu = min(nb - 1, (*ir) - (*ip) - 1);
+      if (mu != 0) {
+         for (l = 0; l < mu; l++) {
+            k = min(l + 1, jt - (*ip));
+            for (i = l + 1; i < nb; i++) {
+               matrix[i - k][(*ip) + l + 1] = matrix[i][(*ip) + l + 1];
+            }
+            for (i = 0; i < k; i++) {
+               matrix[nb - i - 1][(*ip) + l + 1] = 0;
+            }
+         }
+      }
+      *ip = jt;
+   }
+   kh = min(nb + 1, (*ir) + mt - (*ip));
+
+   for (i = 0; i < kh; i++) {
+      Housholder(i, max(i + 1, (*ir) - (*ip)), (*ir) + mt - (*ip), matrix, i, (*ip), &rho, matrix, i + 1,
+                 (*ip), 1, nb - i);
+   }
+
+   *ir = (*ip) + kh;
+   if (kh < nb + 1)
+      return;
+   for (i = 0; i < nb; i++) {
+      matrix[i][(*ir) - 1] = 0;
+   }
+}
+
+/*------------------------------------------------------------------*/
+
+int ResponseCalibration::LeastSquaresSolving(float **matrix, int nb, int ip, int ir, float *x, int n)
+{
+   int i, j, l, ii;
+   float s, rsq;
+   for (j = 0; j < n; j++) {
+      x[j] = matrix[nb][j];
+   }
+   rsq = 0;
+   if (n <= ir - 1) {
+      for (j = n; j < ir; j++) {
+         rsq += pow(matrix[nb][j], 2);
+      }
+   }
+
+   for (ii = 0; ii < n; ii++) {
+      i = n - ii - 1;
+      s = 0;
+      l = max(0, i - ip);
+      if (i != n - 1) {
+         for (j = 1; j < min(n - i, nb); j++) {
+            s += matrix[j + l][i] * x[i + j];
+         }
+      }
+      if (matrix[l][i] == 0) {
+         printf("Error in LeastSquaresSolving.\n");
+         return 0;
+      }
+      x[i] = (x[i] - s) / matrix[l][i];
+   }
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+void ResponseCalibration::Housholder(int lpivot, int l1, int m, float **u, int iU1, int iU2, float *up,
+                                     float **c, int iC1, int iC2, int ice, int ncv)
+{
+   int i, j, incr;
+   float tol = static_cast < float >(1e-20);
+   float tolb = static_cast < float >(1e-24);
+   float cl, clinv, sm, b;
+
+   if (lpivot < 0 || lpivot >= l1 || l1 > m - 1)
+      return;
+   cl = fabs(u[iU1][iU2 + lpivot]);
+
+   // Construct the transformation
+   for (j = l1 - 1; j < m; j++)
+      cl = max(fabsf(u[iU1][iU2 + j]), cl);
+   if (cl < tol)
+      return;
+   clinv = 1 / cl;
+   sm = pow(u[iU1][iU2 + lpivot] * clinv, 2);
+   for (j = l1; j < m; j++) {
+      sm = sm + pow(u[iU1][iU2 + j] * clinv, 2);
+   }
+   cl *= sqrt(sm);
+   if (u[iU1][iU2 + lpivot] > 0)
+      cl = -cl;
+   *up = u[iU1][iU2 + lpivot] - cl;
+   u[iU1][iU2 + lpivot] = cl;
+
+   if (ncv <= 0)
+      return;
+   b = (*up) * u[iU1][iU2 + lpivot];
+   if (fabs(b) < tolb)
+      return;
+   if (b >= 0)
+      return;
+   b = 1 / b;
+   incr = ice * (l1 - lpivot);
+   for (j = 0; j < ncv; j++) {
+      sm = c[iC1 + j][iC2 + lpivot] * (*up);
+      for (i = l1; i < m; i++) {
+         sm = sm + c[iC1 + j][iC2 + lpivot + incr + (i - l1) * ice] * u[iU1][iU2 + i];
+      }
+      if (sm == 0)
+         continue;
+      sm *= b;
+      c[iC1 + j][iC2 + lpivot] = c[iC1 + j][iC2 + lpivot] + sm * (*up);
+      for (i = l1; i < m; i++) {
+         c[iC1 + j][iC2 + lpivot + incr + (i - l1) * ice] =
+             c[iC1 + j][iC2 + lpivot + incr + (i - l1) * ice] + sm * u[iU1][iU2 + i];
+      }
+   }
+}
+
+/*------------------------------------------------------------------*/
+
+int ResponseCalibration::MakeDir(const char *path)
+{
+   struct stat buf;
+   if (stat(path, &buf)) {
+#ifdef _MSC_VER
+      return mkdir(path);
+#else
+      return mkdir(path, 0711);
+#endif                          // R__UNIX
+   }
+   return 0;
+}
+
+/*------------------------------------------------------------------*/
+
+ResponseCalibration::ResponseCalibration(DRSBoard *board)
+:  fBoard(board)
+    , fPrecision(0.1)           // mV
+    , fInitialized(false)
+    , fRecorded(false)
+    , fFitted(false)
+    , fOffset(false)
+    , fNumberOfPointsLowVolt(0)
+    , fNumberOfPoints(0)
+    , fNumberOfMode2Bins(0)
+    , fNumberOfSamples(0)
+    , fNumberOfGridPoints(0)
+    , fNumberOfXConstPoints(0)
+    , fNumberOfXConstGridPoints(0)
+    , fTriggerFrequency(0)
+    , fShowStatistics(0)
+    , fCalibFile(0)
+    , fCurrentLowVoltPoint(0)
+    , fCurrentPoint(0)
+    , fCurrentSample(0)
+    , fCurrentFitChannel(0)
+    , fCurrentFitBin(0)
+    , fResponseY(0)
+    , fSamples(0)
+    , fSampleUsed(0)
+    , fXXFit(0)
+    , fYYFit(0)
+    , fWWFit(0)
+    , fYYFitRes(0)
+    , fYYSave(0)
+    , fXXSave(0)
+    , fStatisticsApprox(0)
+    , fStatisticsApproxExt(0)
+{
+   int i;
+   // Initializing the Calibration Class
+   CalibrationData::fIntRevers[0] = 0;
+   for (i = 1; i < 2 * kBSplineOrder - 2; i++) {
+      CalibrationData::fIntRevers[i] = static_cast < float >(1.) / i;
+   }
+   for (i = 0; i < kNumberOfChipsMax; i++) {
+      fCalibrationData[i] = NULL;
+   }
+   // Initializing the Calibration Creation
+   fCalibrationValid[0] = false;
+   fCalibrationValid[1] = false;
+   
+   fBoard = board;
+}
+
+/*------------------------------------------------------------------*/
+
+ResponseCalibration::~ResponseCalibration()
+{
+   // Delete the Calibration
+   for (int i=0 ; i<kNumberOfChipsMax ; i++)
+      delete fCalibrationData[i];
+
+   // Deleting the Calibration Creation
+   DeleteFields();
+}
+
+/*------------------------------------------------------------------*/
+
+float ResponseCalibration::CalibrationData::fIntRevers[2 * kBSplineOrder - 2];
+ResponseCalibration::CalibrationData::CalibrationData(int numberOfGridPoints)
+:fRead(false)
+, fNumberOfGridPoints(numberOfGridPoints)
+, fHasOffsetCalibration(0)
+, fStartTemperature(0)
+, fEndTemperature(0)
+, fMin(0)
+, fMax(0)
+, fNumberOfLimitGroups(0)
+{
+   int i;
+   for (i = 0; i < kNumberOfCalibChannelsV3; i++) {
+      fChannel[i] = new CalibrationDataChannel(numberOfGridPoints);
+   }
+   for (i = 0; i < kNumberOfADCBins; i++) {
+      fBSplineOffsetLookUp[i] = NULL;
+      fBSplineLookUp[i] = NULL;
+   }
+};
+
+/*------------------------------------------------------------------*/
+
+void ResponseCalibration::CalibrationData::PreCalculateBSpline()
+{
+   int i, j;
+   float uu;
+   float xmin, xrange;
+   int nk = fNumberOfGridPoints - kBSplineOrder + 1;
+   for (i = 0; i < kNumberOfADCBins; i++) {
+      fBSplineLookUp[i] = new float *[fNumberOfLimitGroups];
+      fBSplineOffsetLookUp[i] = new int[fNumberOfLimitGroups];
+      for (j = 0; j < fNumberOfLimitGroups; j++) {
+         fBSplineLookUp[i][j] = new float[kBSplineOrder];
+         xmin = fMin + j * kBSplineXMinOffset;
+         xrange = fMax - xmin;
+         uu = (i - xmin) / xrange;
+         if (i < xmin) {
+            uu = 0;
+         }
+         if (i - xmin > xrange) {
+            uu = 1;
+         }
+         fBSplineOffsetLookUp[i][j] = static_cast < int >(uu * nk);
+         CalculateBSpline(fNumberOfGridPoints, uu, fBSplineLookUp[i][j]);
+      }
+   }
+}
+
+/*------------------------------------------------------------------*/
+
+void ResponseCalibration::CalibrationData::DeletePreCalculatedBSpline()
+{
+   int i, j;
+   for (i = 0; i < kNumberOfADCBins; i++) {
+      if (fBSplineLookUp[i] != NULL) {
+         for (j = 0; j < fNumberOfLimitGroups; j++)
+            delete fBSplineLookUp[i][j];
+      }
+      delete fBSplineLookUp[i];
+      delete fBSplineOffsetLookUp[i];
+   }
+}
+
+/*------------------------------------------------------------------*/
+
+ResponseCalibration::CalibrationData::~CalibrationData()
+{
+   int i, j;
+   for (i = 0; i < kNumberOfCalibChannelsV3; i++) {
+      delete fChannel[i];
+   }
+   for (i = 0; i < kNumberOfADCBins; i++) {
+      if (fBSplineLookUp[i] != NULL) {
+         for (j = 0; j < fNumberOfLimitGroups; j++) {
+            delete fBSplineLookUp[i][j];
+         }
+      }
+      delete fBSplineLookUp[i];
+      delete fBSplineOffsetLookUp[i];
+   }
+};
+
+/*------------------------------------------------------------------*/
+
+int ResponseCalibration::CalibrationData::CalculateBSpline(int nGrid, float value, float *bsplines)
+{
+   int minimum;
+   int maximum;
+   float xl;
+
+   int nk = nGrid - kBSplineOrder + 1;
+   float vl = value * nk;
+   int ivl = static_cast < int >(vl);
+
+   if (1 <= value) {
+      xl = vl - nk + 1;
+      minimum = 1 - nk;
+   } else if (value < 0) {
+      xl = vl;
+      minimum = 0;
+   } else {
+      xl = vl - ivl;
+      minimum = -ivl;
+   }
+   maximum = nk + minimum;
+
+//   printf("xl = %f\n",xl);
+   float vm, vmprev;
+   int jl, ju;
+   int nb = 0;
+
+   bsplines[0] = 1;
+   for (int i = 0; i < kBSplineOrder - 1; i++) {
+      vmprev = 0;
+      for (int j = 0; j < nb + 1; j++) {
+         jl = max(minimum, j - nb);
+         ju = min(maximum, j + 1);
+         vm = bsplines[j] * fIntRevers[ju - jl];
+         bsplines[j] = vm * (ju - xl) + vmprev;
+         vmprev = vm * (xl - jl);
+      }
+      nb++;
+      bsplines[nb] = vmprev;
+   }
+   return -minimum;
+}
+
+/*------------------------------------------------------------------*/
+
+void ResponseCalibration::Average(int method, float *points, int numberOfPoints, float &mean, float &error,
+                                  float sigmaBoundary)
+{
+   // Methods :
+   // 0 : Average
+   // 1 : Average inside sigmaBoundary*sigma
+   int i;
+   float sum = 0;
+   float sumSquare = 0;
+
+   if (method == 0 || method == 1) {
+      for (i = 0; i < numberOfPoints; i++) {
+         sum += points[i];
+         sumSquare += points[i] * points[i];
+      }
+
+      mean = sum / numberOfPoints;
+      error = sqrt((sumSquare - sum * sum / numberOfPoints) / (numberOfPoints - 1));
+   }
+   if (method == 1) {
+      int numberOfGoodPoints = numberOfPoints;
+      bool found = true;
+      bool *goodSample = new bool[numberOfGoodPoints];
+      for (i = 0; i < numberOfGoodPoints; i++)
+         goodSample[i] = true;
+
+      while (found) {
+         found = false;
+         for (i = 0; i < numberOfPoints; i++) {
+            if (goodSample[i] && fabs(points[i] - mean) > sigmaBoundary * error) {
+               found = true;
+               goodSample[i] = false;
+               numberOfGoodPoints--;
+               sum -= points[i];
+               sumSquare -= points[i] * points[i];
+               mean = sum / numberOfGoodPoints;
+               error = sqrt((sumSquare - sum * sum / numberOfGoodPoints) / (numberOfGoodPoints - 1));
+            }
+         }
+      }
+      delete goodSample;
+   }
+}
+
+
+//**************************************************************************************************
+//**
+//** All functions special to the Concurrent Technologies single-board computer are collected here
+//**
+//************************************************************************************************** 
+
+#ifdef CT_VME 
+
+// Open VME connection
+int DRS::OpenVME() {
+
+  if ((ErrorCode = VME_Open()) != VME_SUCCESS) {
+    VME_ErrorString(ErrorCode,ErrorString); 
+    printf("VME_Open()  %s\n",ErrorString);
+  }  
+  return ErrorCode;
+}
+
+// Do master mapping  
+int DRS::MasterMapVME(int *MMap) {
+
+  if (ErrorCode = VME_MasterMap(&MasterMap, MMap)) {
+    VME_ErrorString(ErrorCode,ErrorString); 
+    printf("VME_MasterMap()  %s\n",ErrorString);
+  }
+  return(ErrorCode);
+}
+
+// Delete master mapping
+int DRS::MasterUnMapVME(int MMap) {
+
+  if (ErrorCode = VME_MasterUnmap(MMap)) {
+    VME_ErrorString(ErrorCode,ErrorString); 
+    printf("VME_MasterUnmap()  %s\n",ErrorString);
+  }  
+  return(ErrorCode);  
+}
+
+// Close VME connection
+int DRS::CloseVME() {
+
+  if ((ErrorCode = VME_Close()) != VME_SUCCESS) {    
+    VME_ErrorString(ErrorCode,ErrorString);
+    printf("VME_Close()  %s\n",ErrorString);
+  }
+  return ErrorCode;
+}
+
+// Open CMEM package
+int DRS::OpenCMEM() {
+  
+  if ((ErrorCode = CMEM_Open()) != CMEM_RCC_SUCCESS) {
+    VME_ErrorString(ErrorCode,ErrorString); 
+    printf("CMEM_Open()  %s\n",ErrorString);
+  }
+  return ErrorCode;
+}
+
+// Close CMEM package
+int DRS::CloseCMEM() {
+
+  if ((ErrorCode = CMEM_Close()) != CMEM_RCC_SUCCESS) {  
+    VME_ErrorString(ErrorCode,ErrorString);
+    printf("CMEM_Close  %s\n",ErrorString);
+  }
+  return ErrorCode;
+}
+
+int DRSBoard::AllocateSegmentCMEM(unsigned int SegSize, int *CMEM_SegIdentifier) {
+  
+  if ((ErrorCode = CMEM_SegmentAllocate(SegSize, "DMA_BUFFER", CMEM_SegIdentifier)) != CMEM_RCC_SUCCESS) {
+    VME_ErrorString(ErrorCode,ErrorString); 
+    printf("CMEM_SegmentAllocate()  %s\n",ErrorString);
+  }
+  return ErrorCode;
+}
+
+
+int DRSBoard::AssignPhysicalSegAddressCMEM(int CMEM_SegIdentifier, unsigned long* PCIAddress) {
+  
+  if ((ErrorCode = CMEM_SegmentPhysicalAddress(CMEM_SegIdentifier, PCIAddress)) != CMEM_RCC_SUCCESS) {
+    VME_ErrorString(ErrorCode,ErrorString);
+    printf("CMEM_SegmentPhysicalAddress()  %s\n",ErrorString);
+  }
+  return ErrorCode;
+}
+
+
+int DRSBoard::AssignVirtualSegAddressCMEM(int CMEM_SegIdentifier, unsigned long* VirtualAddress) {
+
+  if ((ErrorCode = CMEM_SegmentVirtualAddress(CMEM_SegIdentifier, VirtualAddress)) != CMEM_RCC_SUCCESS) {
+    VME_ErrorString(ErrorCode,ErrorString);
+    printf("CMEM_SegmentVirtualAddress()  %s\n",ErrorString);
+  }
+  return ErrorCode;
+}
+
+// Free memory segment
+int DRSBoard::FreeSegmentCMEM(int CMEM_SegIdentifier) {
+
+  if ((ErrorCode = CMEM_SegmentFree(CMEM_SegIdentifier)) != CMEM_RCC_SUCCESS) {
+    VME_ErrorString(ErrorCode,ErrorString); 
+    printf("CMEM_SegmentFree()  %s\n",ErrorString);
+  }  
+  return ErrorCode;
+ }
+
+
+#endif
Index: /fact/drsdaq/DRS/DRS.h
===================================================================
--- /fact/drsdaq/DRS/DRS.h	(revision 9833)
+++ /fact/drsdaq/DRS/DRS.h	(revision 9833)
@@ -0,0 +1,781 @@
+/********************************************************************
+  DRS.h, S.Ritt, M. Schneebeli - PSI
+
+  $Id: DRS.h 14428 2009-10-19 12:59:46Z ritt $
+
+********************************************************************/
+#ifndef DRS_H
+#define DRS_H
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_LIBUSB
+#   ifndef HAVE_USB
+#      define HAVE_USB
+#   endif
+#endif
+
+#ifdef HAVE_USB
+#   include <musbstd.h>
+#endif                          // HAVE_USB
+
+#ifdef HAVE_VME
+#   include <mvmestd.h>
+#endif                          // HAVE_VME
+
+// Concurrent Technologies VME single board computer
+#ifdef CT_VME 
+  #include "rcc_error/rcc_error.h"   // Error reporting
+  #include "vme_rcc/vme_rcc.h"       // VME access
+  #include "cmem_rcc/cmem_rcc.h"     // Allocation of contiguous memory
+#endif
+
+/* disable "deprecated" warning */
+#ifdef _MSC_VER
+#pragma warning(disable: 4996)
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* transport mode */
+#define TR_VME   1
+#define TR_USB   2
+#define TR_USB2  3
+
+/* address types */
+#ifndef T_CTRL
+#define T_CTRL   1
+#define T_STATUS 2
+#define T_RAM    3
+#define T_FIFO   4
+#endif
+
+/*---- Register addresses ------------------------------------------*/
+
+#define REG_CTRL                     0x00000    /* 32 bit control reg */
+#define REG_DAC_OFS                  0x00004
+#define REG_DAC0                     0x00004
+#define REG_DAC1                     0x00006
+#define REG_DAC2                     0x00008
+#define REG_DAC3                     0x0000A
+#define REG_DAC4                     0x0000C
+#define REG_DAC5                     0x0000E
+#define REG_DAC6                     0x00010
+#define REG_DAC7                     0x00012
+#define REG_CHANNEL_CONFIG           0x00014    // low byte
+#define REG_CONFIG                   0x00014    // high byte
+#define REG_CHANNEL_MODE             0x00016
+#define REG_ADCCLK_PHASE             0x00016
+#define REG_FREQ_SET_HI              0x00018    // DRS2
+#define REG_FREQ_SET_LO              0x0001A    // DRS2
+#define REG_TRG_DELAY                0x00018    // DRS4
+#define REG_FREQ_SET                 0x0001A    // DRS4
+#define REG_TRIG_DELAY               0x0001C
+#define REG_LMK_MSB                  0x0001C    // DRS4 Mezz
+#define REG_CALIB_TIMING             0x0001E    // DRS2
+#define REG_EEPROM_PAGE_EVAL         0x0001E    // DRS4 Eval
+#define REG_EEPROM_PAGE_MEZZ         0x0001A    // DRS4 Mezz
+#define REG_LMK_LSB                  0x0001E    // DRS4 Mezz
+#define REG_WARMUP                   0x00020    // DRS4 Mezz
+#define REG_COOLDOWN                 0x00022    // DRS4 Mezz
+
+#define REG_MAGIC                    0x00000
+#define REG_BOARD_TYPE               0x00002
+#define REG_STATUS                   0x00004
+#define REG_RDAC_OFS                 0x0000E
+#define REG_RDAC0                    0x00008
+#define REG_STOP_CELL0               0x00008
+#define REG_RDAC1                    0x0000A
+#define REG_STOP_CELL1               0x0000A
+#define REG_RDAC2                    0x0000C
+#define REG_STOP_CELL2               0x0000C
+#define REG_RDAC3                    0x0000E
+#define REG_STOP_CELL3               0x0000E
+#define REG_RDAC4                    0x00000
+#define REG_RDAC5                    0x00002
+#define REG_RDAC6                    0x00014
+#define REG_RDAC7                    0x00016
+#define REG_EVENTS_IN_FIFO           0x00018
+#define REG_EVENT_COUNT              0x0001A
+#define REG_FREQ1                    0x0001C
+#define REG_FREQ2                    0x0001E
+#define REG_TEMPERATURE              0x00020
+#define REG_TRIGGER_BUS              0x00022
+#define REG_SERIAL_BOARD             0x00024
+#define REG_VERSION_FW               0x00026
+
+/*---- Control register bit definitions ----------------------------*/
+
+#define BIT_START_TRIG        (1<<0)    // write a "1" to start domino wave
+#define BIT_REINIT_TRIG       (1<<1)    // write a "1" to stop & reset DRS
+#define BIT_SOFT_TRIG         (1<<2)    // write a "1" to stop and read data to RAM
+#define BIT_EEPROM_WRITE_TRIG (1<<3)    // write a "1" to write into serial EEPROM
+#define BIT_EEPROM_READ_TRIG  (1<<4)    // write a "1" to read from serial EEPROM
+#define BIT_AUTOSTART        (1<<16)
+#define BIT_DMODE            (1<<17)    // (*DRS2*) 0: single shot, 1: circular
+#define BIT_LED              (1<<18)    // 1=on, 0=blink during readout
+#define BIT_TCAL_EN          (1<<19)    // switch on (1) / off (0) for 33 MHz calib signal
+#define BIT_TCAL_SOURCE      (1<<20)
+#define BIT_REFCLK_SOURCE    (1<<20)
+#define BIT_FREQ_AUTO_ADJ    (1<<21)    // DRS2/3
+#define BIT_TRANSP_MODE      (1<<21)    // DRS4
+#define BIT_ENABLE_TRIGGER1  (1<<22)    // External LEMO/FP/TRBUS trigger
+#define BIT_LONG_START_PULSE (1<<23)    // (*DRS2*) 0:short start pulse (>0.8GHz), 1:long start pulse (<0.8GHz)
+#define BIT_READOUT_MODE     (1<<23)    // (*DRS3*,*DRS4*) 0:start from first bin, 1:start from domino stop
+#define BIT_DELAYED_START    (1<<24)    // DRS2: start domino wave 400ns after soft trigger, used for waveform
+                                        // generator startup
+#define BIT_NEG_TRIGGER      (1<<24)    // DRS4: use high-to-low trigger if set
+#define BIT_ACAL_EN          (1<<25)    // connect DRS to inputs (0) or to DAC6 (1)
+#define BIT_TRIGGER_DELAYED  (1<<26)    // select delayed trigger from trigger bus
+#define BIT_ADCCLK_INVERT    (1<<26)    // invert ADC clock
+#define BIT_DACTIVE          (1<<27)    // keep domino wave running during readout
+#define BIT_STANDBY_MODE     (1<<28)    // put chip in standby mode
+#define BIT_TR_SOURCE1       (1<<29)    // trigger source selection bits
+#define BIT_TR_SOURCE2       (1<<30)    // trigger source selection bits
+#define BIT_ENABLE_TRIGGER2  (1<<31)    // analog threshold (internal) trigger
+
+/* DRS4 configuration register bit definitions */
+#define BIT_CONFIG_DMODE      (1<<8)    // 0: single shot, 1: circular
+#define BIT_CONFIG_PLLEN      (1<<9)    // write a "1" to enable the internal PLL
+#define BIT_CONFIG_WSRLOOP   (1<<10)    // write a "1" to connect WSROUT to WSRIN internally
+
+/*---- Status register bit definitions -----------------------------*/
+
+#define BIT_RUNNING           (1<<0)    // one if domino wave running or readout in progress
+#define BIT_NEW_FREQ1         (1<<1)    // one if new frequency measurement available
+#define BIT_NEW_FREQ2         (1<<2)
+#define BIT_PLL_LOCKED0       (1<<1)    // 1 if PLL has locked (DRS4 evaluation board only)
+#define BIT_PLL_LOCKED1       (1<<2)    // 1 if PLL DRS4 B has locked (DRS4 mezzanine board only)
+#define BIT_PLL_LOCKED2       (1<<3)    // 1 if PLL DRS4 C has locked (DRS4 mezzanine board only)
+#define BIT_PLL_LOCKED3       (1<<4)    // 1 if PLL DRS4 D has locked (DRS4 mezzanine board only)
+#define BIT_SERIAL_BUSY       (1<<5)    // 1 if EEPROM operation in progress
+#define BIT_LMK_LOCKED        (1<<6)    // 1 if PLL of LMK chip has locked (DRS4 mezzanine board only)
+
+enum DRSBoardConstants {
+   kNumberOfChannelsMax         =   10,
+   kNumberOfCalibChannelsV3     =   10,
+   kNumberOfCalibChannelsV4     =    8,
+   kNumberOfBins                = 1024,
+   kNumberOfChipsMax            =    4,
+   kFrequencyCacheSize          =   10,
+   kBSplineOrder                =    4,
+   kPreCaliculatedBSplines      = 1000,
+   kPreCaliculatedBSplineGroups =    5,
+   kNumberOfADCBins             = 4096,
+   kBSplineXMinOffset           =   20,
+   kMaxNumberOfClockCycles      =  100,
+};
+
+enum DRSErrorCodes {
+   kSuccess                     =  0,
+   kInvalidTriggerSignal        = -1,
+   kWrongChannelOrChip          = -2,
+   kInvalidTransport            = -3,
+   kZeroSuppression             = -4,
+   kWaveNotAvailable            = -5
+};
+
+/*---- callback class ----*/
+
+class DRSCallback
+{
+public:
+   virtual void Progress(int value) = 0;
+   virtual ~DRSCallback() {};
+};
+
+/*------------------------*/
+
+class DRSBoard;
+
+class ResponseCalibration {
+protected:
+
+   class CalibrationData {
+   public:
+      class CalibrationDataChannel {
+      public:
+         unsigned char   fLimitGroup[kNumberOfBins];           //!
+         float           fMin[kNumberOfBins];                  //!
+         float           fRange[kNumberOfBins];                //!
+         short           fOffset[kNumberOfBins];               //!
+         short           fGain[kNumberOfBins];                 //!
+         unsigned short  fOffsetADC[kNumberOfBins];            //!
+         short          *fData[kNumberOfBins];                 //!
+         unsigned char  *fLookUp[kNumberOfBins];               //!
+         unsigned short  fLookUpOffset[kNumberOfBins];         //!
+         unsigned char   fNumberOfLookUpPoints[kNumberOfBins]; //!
+         float          *fTempData;                            //!
+
+      private:
+         CalibrationDataChannel(const CalibrationDataChannel &c);              // not implemented
+         CalibrationDataChannel &operator=(const CalibrationDataChannel &rhs); // not implemented
+
+      public:
+         CalibrationDataChannel(int numberOfGridPoints)
+         :fTempData(new float[numberOfGridPoints]) {
+            int i;
+            for (i = 0; i < kNumberOfBins; i++) {
+               fData[i] = new short[numberOfGridPoints];
+            }
+            memset(fLimitGroup,           0, sizeof(fLimitGroup));
+            memset(fMin,                  0, sizeof(fMin));
+            memset(fRange,                0, sizeof(fRange));
+            memset(fOffset,               0, sizeof(fOffset));
+            memset(fGain,                 0, sizeof(fGain));
+            memset(fOffsetADC,            0, sizeof(fOffsetADC));
+            memset(fLookUp,               0, sizeof(fLookUp));
+            memset(fLookUpOffset,         0, sizeof(fLookUpOffset));
+            memset(fNumberOfLookUpPoints, 0, sizeof(fNumberOfLookUpPoints));
+         }
+         ~CalibrationDataChannel() {
+            int i;
+            delete fTempData;
+            for (i = 0; i < kNumberOfBins; i++) {
+               delete fData[i];
+               delete fLookUp[i];
+            }
+         }
+      };
+
+      bool                    fRead;                                  //!
+      CalibrationDataChannel *fChannel[10];                           //!
+      unsigned char           fNumberOfGridPoints;                    //!
+      int                     fHasOffsetCalibration;                  //!
+      float                   fStartTemperature;                      //!
+      float                   fEndTemperature;                        //!
+      int                    *fBSplineOffsetLookUp[kNumberOfADCBins]; //!
+      float                 **fBSplineLookUp[kNumberOfADCBins];       //!
+      float                   fMin;                                   //!
+      float                   fMax;                                   //!
+      unsigned char           fNumberOfLimitGroups;                   //!
+      static float            fIntRevers[2 * kBSplineOrder - 2];
+
+   private:
+      CalibrationData(const CalibrationData &c);              // not implemented
+      CalibrationData &operator=(const CalibrationData &rhs); // not implemented
+
+   public:
+      CalibrationData(int numberOfGridPoints);
+      ~CalibrationData();
+      static int CalculateBSpline(int nGrid, float value, float *bsplines);
+      void       PreCalculateBSpline();
+      void       DeletePreCalculatedBSpline();
+   };
+
+   // General Fields
+   DRSBoard        *fBoard;
+
+   double           fPrecision;
+
+   // Fields for creating the Calibration
+   bool             fInitialized;
+   bool             fRecorded;
+   bool             fFitted;
+   bool             fOffset;
+   bool             fCalibrationValid[2];
+
+   int              fNumberOfPointsLowVolt;
+   int              fNumberOfPoints;
+   int              fNumberOfMode2Bins;
+   int              fNumberOfSamples;
+   int              fNumberOfGridPoints;
+   int              fNumberOfXConstPoints;
+   int              fNumberOfXConstGridPoints;
+   double           fTriggerFrequency;
+   int              fShowStatistics;
+   FILE            *fCalibFile;
+
+   int              fCurrentLowVoltPoint;
+   int              fCurrentPoint;
+   int              fCurrentSample;
+   int              fCurrentFitChannel;
+   int              fCurrentFitBin;
+
+   float           *fResponseX[10][kNumberOfBins];
+   float           *fResponseY;
+   unsigned short **fWaveFormMode3[10];
+   unsigned short **fWaveFormMode2[10];
+   short          **fWaveFormOffset[10];
+   unsigned short **fWaveFormOffsetADC[10];
+   unsigned short  *fSamples;
+   int             *fSampleUsed;
+
+   float           *fPntX[2];
+   float           *fPntY[2];
+   float           *fUValues[2];
+   float           *fRes[kNumberOfBins];
+   float           *fResX[kNumberOfBins];
+
+   double          *fXXFit;
+   double          *fYYFit;
+   double          *fWWFit;
+   double          *fYYFitRes;
+   double          *fYYSave;
+   double          *fXXSave;
+   double          fGainMin;
+   double          fGainMax;
+
+   float          **fStatisticsApprox;
+   float          **fStatisticsApproxExt;
+
+   // Fields for applying the Calibration
+   CalibrationData *fCalibrationData[kNumberOfChipsMax];
+
+private:
+         ResponseCalibration(const ResponseCalibration &c);              // not implemented
+         ResponseCalibration &operator=(const ResponseCalibration &rhs); // not implemented
+
+public:
+   ResponseCalibration(DRSBoard* board);
+   ~ResponseCalibration();
+
+   void   SetCalibrationParameters(int numberOfPointsLowVolt, int numberOfPoints, int numberOfMode2Bins,
+                                   int numberOfSamples, int numberOfGridPoints, int numberOfXConstPoints,
+                                   int numberOfXConstGridPoints, double triggerFrequency, int showStatistics = 0);
+   void   ResetCalibration();
+   bool   RecordCalibrationPoints(int chipNumber);
+   bool   RecordCalibrationPointsV3(int chipNumber);
+   bool   RecordCalibrationPointsV4(int chipNumber);
+   bool   FitCalibrationPoints(int chipNumber);
+   bool   FitCalibrationPointsV3(int chipNumber);
+   bool   FitCalibrationPointsV4(int chipNumber);
+   bool   OffsetCalibration(int chipNumber);
+   bool   OffsetCalibrationV3(int chipNumber);
+   bool   OffsetCalibrationV4(int chipNumber);
+   double GetTemperature(unsigned int chipIndex);
+
+   bool   WriteCalibration(unsigned int chipIndex);
+   bool   WriteCalibrationV3(unsigned int chipIndex);
+   bool   WriteCalibrationV4(unsigned int chipIndex);
+   bool   ReadCalibration(unsigned int chipIndex);
+   bool   ReadCalibrationV3(unsigned int chipIndex);
+   bool   ReadCalibrationV4(unsigned int chipIndex);
+   bool   Calibrate(unsigned int chipIndex, unsigned int channel, float *adcWaveform,
+                    float *uWaveform, float threshold, bool offsetCalib);
+   bool   Calibrate(unsigned int chipIndex, unsigned int channel, unsigned short *adcWaveform, short *uWaveform,
+                    int triggerCell, float threshold, bool offsetCalib);
+   bool   SubtractADCOffset(unsigned int chipIndex, unsigned int channel, unsigned short *adcWaveform,
+                            unsigned short *adcCalibratedWaveform, unsigned short newBaseLevel);
+   bool   IsRead(int chipIndex) const { return fCalibrationValid[chipIndex]; }
+   double GetPrecision() const { return fPrecision; };
+
+   double GetOffsetAt(int chip,int chn,int bin) const { return fCalibrationData[chip]->fChannel[chn]->fOffset[bin]; };
+   double GetGainAt(int chip,int chn,int bin) const { return fCalibrationData[chip]->fChannel[chn]->fGain[bin]; };
+   double GetMeasPointXAt(int ip) const { return fXXSave[ip]; };
+   double GetMeasPointYAt(int ip) const { return fYYSave[ip]; };
+
+protected:
+   void   InitFields(int numberOfPointsLowVolt, int numberOfPoints, int numberOfMode2Bins, int numberOfSamples,
+                     int numberOfGridPoints, int numberOfXConstPoints, int numberOfXConstGridPoints,
+                     double triggerFrequency, int showStatistics);
+   void   DeleteFields();
+   void   CalibrationTrigger(int mode, double voltage);
+   void   CalibrationStart(double voltage);
+
+   static float  GetValue(float *coefficients, float u, int n);
+   static int    Approx(float *p, float *uu, int np, int nu, float *coef);
+   static void   LeastSquaresAccumulation(float **matrix, int nb, int *ip, int *ir, int mt, int jt);
+   static int    LeastSquaresSolving(float **matrix, int nb, int ip, int ir, float *x, int n);
+   static void   Housholder(int lpivot, int l1, int m, float **u, int iU1, int iU2, float *up, float **c, int iC1,
+                            int iC2, int ice, int ncv);
+
+   static int    MakeDir(const char *path);
+   static void   Average(int method,float *samples,int numberOfSamples,float &mean,float &error,float sigmaBoundary);
+};
+
+
+class DRSBoard {
+protected:
+   class TimeData {
+   public:
+      class FrequencyData {
+      public:
+         int    fFrequency;
+         double fBin[kNumberOfBins];
+      };
+
+      enum {
+         kMaxNumberOfFrequencies = 4000
+      };
+      int            fChip;
+      int            fNumberOfFrequencies;
+      FrequencyData *fFrequency[kMaxNumberOfFrequencies];
+
+   private:
+      TimeData(const TimeData &c);              // not implemented
+      TimeData &operator=(const TimeData &rhs); // not implemented
+
+   public:
+      TimeData()
+      :fChip(0)
+      ,fNumberOfFrequencies(0) {
+      }
+      ~TimeData() {
+         int i;
+         for (i = 0; i < fNumberOfFrequencies; i++) {
+            delete fFrequency[i];
+         }
+      }
+   };
+
+public:
+   // DAC channels (CMC Version 1 : DAC_COFSA,DAC_COFSB,DAC_DRA,DAC_DSA,DAC_TLEVEL,DAC_ACALIB,DAC_DSB,DAC_DRB)
+   unsigned int         fDAC_COFSA;
+   unsigned int         fDAC_COFSB;
+   unsigned int         fDAC_DRA;
+   unsigned int         fDAC_DSA;
+   unsigned int         fDAC_TLEVEL;
+   unsigned int         fDAC_ACALIB;
+   unsigned int         fDAC_DSB;
+   unsigned int         fDAC_DRB;
+   // DAC channels (CMC Version 2+3 : DAC_COFS,DAC_DSA,DAC_DSB,DAC_TLEVEL,DAC_ADCOFS,DAC_CLKOFS,DAC_ACALIB)
+   unsigned int         fDAC_COFS;
+   unsigned int         fDAC_ADCOFS;
+   unsigned int         fDAC_CLKOFS;
+   // DAC channels (CMC Version 4 : DAC_ROFS_1,DAC_DSA,DAC_DSB,DAC_ROFS_2,DAC_ADCOFS,DAC_ACALIB,DAC_INOFS,DAC_BIAS)
+   unsigned int         fDAC_ROFS_1;
+   unsigned int         fDAC_ROFS_2;
+   unsigned int         fDAC_INOFS;
+   unsigned int         fDAC_BIAS;
+   // DAC channels (USB EVAL1 (Version 5) : DAC_ROFS_1,DAC_CMOFS,DAC_CALN,DAC_CALP,DAC_BIAS,DAC_TLEVEL,DAC_ONOFS)
+   unsigned int         fDAC_CMOFS;
+   unsigned int         fDAC_CALN;
+   unsigned int         fDAC_CALP;
+   unsigned int         fDAC_ONOFS;
+   // DAC channels (DRS4 MEZZ1 (Version 6) : DAC_ONOFS,DAC_CMOFSP,DAC_CALN,DAC_CALP,DAC_BIAS,DAC_CMOFSN,DAC_ROFS_1)
+   unsigned int         fDAC_CMOFSP;
+   unsigned int         fDAC_CMOFSN;
+
+protected:
+   // Fields for DRS
+   int                  fDRSType;
+   int                  fBoardType;
+   int                  fNumberOfChips;
+   int                  fNumberOfChannels;
+   int                  fRequiredFirmwareVersion;
+   int                  fFirmwareVersion;
+   int                  fBoardSerialNumber;
+   unsigned int         fTransport;
+   unsigned int         fCtrlBits;
+   int                  fNumberOfReadoutChannels;
+   int                  fReadoutChannelConfig;
+   int                  fADCClkPhase;
+   bool                 fADCClkInvert;
+   double               fExternalClockFrequency;
+#ifdef HAVE_USB
+   MUSB_INTERFACE      *fUsbInterface;
+#endif
+#ifdef HAVE_VME
+   MVME_INTERFACE      *fVmeInterface;
+   mvme_addr_t          fBaseAddress;
+#endif
+#ifdef CT_VME 
+  VME_ErrorCode_t          ErrorCode;
+  VME_BlockTransferList_t  BLT_List;
+  char                 ErrorString[VME_MAXSTRING];
+  int                  CMEM_SegIdentifier; 
+  unsigned long        PCIAddress;        // Physical address of contiguous buffer
+  unsigned long        VirtualAddress;    // Virtual address of contiguous buffer  
+  unsigned int         fBaseAddress;
+  unsigned int         fBoardAddress;
+  int                  fMasterMapping;
+
+  unsigned int GetBaseAddress() const {return fBaseAddress; }
+  unsigned int GetBoardAddress() const {return fBoardAddress; }
+
+  int          AllocateSegmentCMEM(unsigned int SegSize, int *CMEM_SegIdentifier);
+  int          AssignPhysicalSegAddressCMEM(int CMEM_SegIdentifier, unsigned long* PCIAddress);
+  int          AssignVirtualSegAddressCMEM(int CMEM_SegIdentifier, unsigned long* VirtualAddress);
+  int          FreeSegmentCMEM(int CMEM_SegIdentifier);
+#endif
+
+   int                  fSlotNumber;
+   double               fFrequency;
+   double               fTCALFrequency;
+   double               fRefClock;
+   int                  fDominoMode;
+   int                  fDominoActive;
+   int                  fChannelConfig;
+   int                  fWSRLoop;
+   int                  fReadoutMode;
+   int                  fTriggerEnable1;
+   int                  fTriggerEnable2;
+   int                  fTriggerSource;
+   int                  fTriggerDelay;
+   int                  fDelayedStart;
+   int                  fTranspMode;
+   unsigned short       fStopCell[4];
+   double               fROFS;
+   double               fRange;
+   double               fCommonMode;
+   int                  fAcalMode;
+   int                  fbkAcalMode;
+   double               fAcalVolt;
+   double               fbkAcalVolt;
+   int                  fTcalFreq;
+   int                  fbkTcalFreq;
+   int                  fTcalLevel;
+   int                  fbkTcalLevel;
+   int                  fTcalPhase;
+   int                  fTcalSource;
+
+   unsigned char        fWaveforms[kNumberOfChipsMax * kNumberOfChannelsMax * 2 * kNumberOfBins];
+
+   // Fields for Calibration
+   int                  fMaxChips;
+   char                 fCalibDirectory[1000];
+
+   // Fields for Response Calibration old method
+   ResponseCalibration *fResponseCalibration;
+
+   // Fields for Calibration new method
+   bool                 fCellCalibrationValid;
+   double               fCellCalibratedRange;
+   unsigned short       fCellOffset[kNumberOfChipsMax * kNumberOfChannelsMax][kNumberOfBins];
+   unsigned short       fCellOffset2[kNumberOfChipsMax * kNumberOfChannelsMax][kNumberOfBins];
+   double               fCellGain[kNumberOfChipsMax * kNumberOfChannelsMax][kNumberOfBins];
+
+   bool                 fTimingCalibrationValid;
+   double               fTimingCalibratedFrequency;
+   double               fCellT[kNumberOfChipsMax][kNumberOfBins];
+   signed short         fCellDT[kNumberOfChipsMax * kNumberOfChannelsMax][kNumberOfBins];
+
+   // Fields for Time Calibration
+   TimeData           **fTimeData;
+   int                  fNumberOfTimeData;
+
+   // General debugging flag
+   int fDebug;
+
+   // Fields for wave transfer
+   bool                 fWaveTransferred[kNumberOfChipsMax * kNumberOfChannelsMax];
+
+   // Waveform Rotation
+   int                  fTriggerStartBin; // Start Bin of the trigger
+
+private:
+   DRSBoard(const DRSBoard &c);              // not implemented
+   DRSBoard &operator=(const DRSBoard &rhs); // not implemented
+
+public:
+   // Public Methods
+#ifdef HAVE_USB
+   DRSBoard(MUSB_INTERFACE * musb_interface, int usb_slot);
+#endif
+#ifdef HAVE_VME
+   DRSBoard(MVME_INTERFACE * mvme_interface, mvme_addr_t base_address, int slot_number);
+
+   MVME_INTERFACE *GetVMEInterface() const { return fVmeInterface; };
+#endif
+#ifdef CT_VME 
+  DRSBoard(int MasterMapping, unsigned int BaseAddress, unsigned int BoardAddress, int SlotNumber);
+#endif
+
+   ~DRSBoard();
+
+   int          SetBoardSerialNumber(unsigned short serialNumber);
+   int          GetBoardSerialNumber() const { return fBoardSerialNumber; }
+   int          GetFirmwareVersion() const { return fFirmwareVersion; }
+   int          GetRequiredFirmwareVersion() const { return fRequiredFirmwareVersion; }
+   int          GetDRSType() const { return fDRSType; }
+   int          GetBoardType() const { return fBoardType; }
+   int          GetNumberOfChips() const { return fNumberOfChips; }
+   int          GetNumberOfChannels() const { return fNumberOfChannels; }
+   int          GetSlotNumber() const { return fSlotNumber; }
+   int          InitFPGA(void);
+   int          Write(int type, unsigned int addr, void *data, int size);
+   int          Read(int type, void *data, unsigned int addr, int size);
+   int          GetTransport() const { return fTransport; }
+   void         RegisterTest(void);
+   int          RAMTest(int flag);
+   int          ChipTest();
+   unsigned int GetCtrlReg(void);
+   unsigned short GetConfigReg(void);
+   unsigned int GetStatusReg(void);
+   void         SetLED(int state);
+   void         SetChannelConfig(int firstChannel, int lastChannel, int nConfigChannels);
+   void         SetADCClkPhase(int phase, bool invert);
+   void         SetWarmup(unsigned int ticks);
+   void         SetCooldown(unsigned int ticks);
+   int          GetReadoutChannelConfig() { return fReadoutChannelConfig; }
+   void         SetNumberOfChannels(int nChannels);
+   int          EnableTrigger(int flag1, int flag2);
+   int          GetTriggerEnable(int i) { return i?fTriggerEnable2:fTriggerEnable1; }
+   int          SetDelayedTrigger(int flag);
+   int          SetTriggerDelay(int delay);
+   int          GetTriggerDelay() { return fTriggerDelay; }
+   int          SetTriggerLevel(double value, bool negative);
+   int          SetTriggerSource(int source);
+   int          GetTriggerSource() { return fTriggerSource; }
+   int          SetDelayedStart(int flag);
+   int          SetTranspMode(int flag);
+   int          SetStandbyMode(int flag);
+   int          IsBusy(void);
+   int          IsPLLLocked(void);
+   int          IsLMKLocked(void);
+   int          IsNewFreq(unsigned char chipIndex);
+   int          SetDAC(unsigned char channel, double value);
+   int          ReadDAC(unsigned char channel, double *value);
+   int          GetRegulationDAC(double *value);
+   int          StartDomino();
+   int          StartClearCycle();
+   int          FinishClearCycle();
+   int          Reinit();
+   int          Init();
+   void         SetDebug(int debug) { fDebug = debug; }
+   int          Debug() { return fDebug; }
+   int          SetDominoMode(unsigned char mode);
+   int          SetDominoActive(unsigned char mode);
+   int          SetReadoutMode(unsigned char mode);
+   int          SoftTrigger(void);
+   int          ReadFrequency(unsigned char chipIndex, double *f);
+   int          SetFrequency(double freq, bool wait);
+   double       VoltToFreq(double volt);
+   double       FreqToVolt(double freq);
+   double       GetFrequency() const { return fFrequency; }
+   int          RegulateFrequency(double freq);
+   int          SetExternalClockFrequency(double frequencyMHz);
+   double       GetExternalClockFrequency();
+   void         SetVoltageOffset(double offset1, double offset2);
+   int          SetInputRange(double center);
+   double       GetInputRange(void) { return fRange; }
+   double       GetCalibratedInputRange(void) { return fCellCalibratedRange; }
+   double       GetCalibratedFrequency(void) { return fTimingCalibratedFrequency; }
+   int          TransferWaves(int numberOfChannels = kNumberOfChipsMax * kNumberOfChannelsMax);
+   int          TransferWaves(unsigned char *p, int numberOfChannels = kNumberOfChipsMax * kNumberOfChannelsMax);
+   int          TransferWaves(int firstChannel, int lastChannel);
+   int          TransferWaves(unsigned char *p, int firstChannel, int lastChannel);
+   int          DecodeWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel,
+                           unsigned short *waveform);
+   int          DecodeWave(unsigned int chipIndex, unsigned char channel, unsigned short *waveform);
+   int          GetWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel, short *waveform,
+                        bool responseCalib = false, int triggerCell = -1, bool adjustToClock = false,
+                        float threshold = 0, bool offsetCalib = true);
+   int          GetWave(unsigned char *waveforms, unsigned int chipIndex, unsigned char channel, float *waveform,
+                        bool responseCalib = false, int triggerCell = -1, bool adjustToClock = false,
+                        float threshold = 0, bool offsetCalib = true);
+   int          GetWave(unsigned int chipIndex, unsigned char channel, short *waveform, bool responseCalib = false,
+                        int triggerCell = -1, bool adjustToClock = false, float threshold = 0, bool offsetCalib = true);
+   int          GetWave(unsigned int chipIndex, unsigned char channel, float *waveform, bool responseCalib,
+                        int triggerCell = -1, bool adjustToClock = false, float threshold = 0, bool offsetCalib = true);
+   int          GetWave(unsigned int chipIndex, unsigned char channel, float *waveform);
+   int          GetRawWave(unsigned int chipIndex, unsigned char channel, unsigned short *waveform, bool adjustToClock = false);
+   int          GetRawWave(unsigned char *waveforms,unsigned int chipIndex, unsigned char channel,
+                           unsigned short *waveform, bool adjustToClock = false);
+   int          GetTime(unsigned int chipIndex, double freq, float *time, bool tcalibrated=true, bool rotated=true);
+   int          GetTime(unsigned int chipIndex, float *time, bool tcalibrated=true, bool rotated=true);
+   int          GetTriggerCell(unsigned int chipIndex);
+   int          GetStopCell(unsigned int chipIndex);
+   int          GetTriggerCell(unsigned char *waveforms,unsigned int chipIndex);
+   void         TestDAC(int channel);
+   void         MeasureSpeed();
+   void         InteractSpeed();
+   void         MonitorFrequency();
+   int          TestShift(int n);
+   int          EnableAcal(int mode, double voltage);
+   int          GetAcalMode() { return fAcalMode; }
+   double       GetAcalVolt() { return fAcalVolt; }
+   int          EnableTcal(int freq, int level=0, int phase=0);
+   int          SelectClockSource(int source);
+   int          SetRefclk(int source);
+   int          GetTcalFreq() { return fTcalFreq; }
+   int          GetTcalLevel() { return fTcalLevel; }
+   int          GetTcalPhase() { return fTcalPhase; }
+   int          GetTcalSource() { return fTcalSource; }
+   int          SetCalibVoltage(double value);
+   int          SetCalibTiming(int t1, int t2);
+   double       GetTemperature();
+   int          GetTriggerBus();
+   int          ReadEEPROM(unsigned short page, void *buffer, int size);
+   int          WriteEEPROM(unsigned short page, void *buffer, int size);
+   bool         HasCorrectFirmware();
+   int          ConfigureLMK(double sampFreq, bool freqChange, int calFreq, int calPhase);
+
+   bool         InitTimeCalibration(unsigned int chipIndex);
+   void         SetCalibrationDirectory(const char *calibrationDirectoryPath);
+   void         GetCalibrationDirectory(char *calibrationDirectoryPath);
+
+   ResponseCalibration *GetResponseCalibration() const { return fResponseCalibration; }
+
+   double       GetPrecision() const { return fResponseCalibration ? fResponseCalibration->GetPrecision() : 0.1; }
+   int          CalibrateWaveform(unsigned int chipIndex, unsigned char channel, unsigned short *adcWaveform,
+                                  short *waveform, bool responseCalib, int triggerCell, bool adjustToClock,
+                                  float threshold, bool offsetCalib);
+
+   static void  LinearRegression(double *x, double *y, int n, double *a, double *b);
+   
+   void         ReadSingleWaveform(int nChips, int nChan, 
+                                  unsigned short wfu[kNumberOfChipsMax][kNumberOfChannelsMax][kNumberOfBins], bool rotated);
+   int          AverageWaveforms(DRSCallback *pcb, int chipIndex, int nChan, int prog1, int prog2, unsigned short *awf, int n, bool rotated);
+   int          RobustAverageWaveforms(DRSCallback *pcb, int chipIndex, int nChan, int prog1, int prog2, unsigned short *awf, int n, bool rotated);
+   int          CalibrateVolt(DRSCallback *pcb);
+   int          AnalyzeWF(int nIter, float wf[kNumberOfBins], int tCell, double cellT[kNumberOfBins]);
+   int          CalibrateTiming(DRSCallback *pcb);
+   bool         IsCalibrationValid() { return fCellCalibrationValid; }
+   bool         IsTimingCalibrationValid() { return fTimingCalibrationValid; }
+   static void  RemoveSymmetricSpikes(short **wf, int nwf,
+                                      short diffThreshold, int spikeWidth,
+                                      short maxPeakToPeak, short spikeVoltage,
+                                      int nTimeRegionThreshold);
+protected:
+   // Protected Methods
+   void         ConstructBoard();
+   void         ReadSerialNumber();
+   void         ReadCalibration(void);
+
+   TimeData    *GetTimeCalibration(unsigned int chipIndex, bool reinit = false);
+
+   int          GetStretchedTime(float *time, float *measurement, int numberOfMeasurements, float period);
+};
+
+class DRS {
+protected:
+   // constants
+   enum {
+      kMaxNumberOfBoards = 40
+   };
+
+protected:
+   DRSBoard       *fBoard[kMaxNumberOfBoards];
+   int             fNumberOfBoards;
+   char            fError[256];
+#ifdef HAVE_VME
+   MVME_INTERFACE *fVmeInterface;
+#endif
+
+private:
+   DRS(const DRS &c);              // not implemented
+   DRS &operator=(const DRS &rhs); // not implemented
+
+#ifdef CT_VME 
+  VME_MasterMap_t  MasterMap;
+  VME_ErrorCode_t  ErrorCode;
+  char             ErrorString[VME_MAXSTRING];
+  int              MasterMapping[kMaxNumberOfBoards];
+
+  int OpenVME();
+  int MasterMapVME(int* MMap);
+  int MasterUnMapVME(int MMap);
+  int CloseVME();
+  int OpenCMEM();
+  int CloseCMEM();
+#endif
+
+public:
+   // Public Methods
+   DRS();
+   ~DRS();
+
+   DRSBoard        *GetBoard(int i) { return fBoard[i]; }
+   DRSBoard       **GetBoards() { return fBoard; }
+   int              GetNumberOfBoards() const { return fNumberOfBoards; }
+   bool             GetError(char *str, int size);
+#ifdef HAVE_VME
+   MVME_INTERFACE *GetVMEInterface() const { return fVmeInterface; };
+#endif
+};
+
+#endif                          // DRS_H
Index: /fact/drsdaq/DRS/mxml.c
===================================================================
--- /fact/drsdaq/DRS/mxml.c	(revision 9833)
+++ /fact/drsdaq/DRS/mxml.c	(revision 9833)
@@ -0,0 +1,2301 @@
+/********************************************************************\
+
+   Name:         mxml.c
+   Created by:   Stefan Ritt
+
+   Contents:     Midas XML Library
+
+   This is a simple implementation of XML functions for writing and
+   reading XML files. For writing an XML file from scratch, following
+   functions can be used:
+
+   writer = mxml_open_file(file_name);
+     mxml_start_element(writer, name);
+     mxml_write_attribute(writer, name, value);
+     mxml_write_value(writer, value);
+     mxml_end_element(writer); 
+     ...
+   mxml_close_file(writer);
+
+   To read an XML file, the function
+
+   tree = mxml_parse_file(file_name, error, sizeof(error));
+
+   is used. It parses the complete XML file and stores it in a
+   hierarchical tree in memory. Nodes in that tree can be searched
+   for with
+
+   mxml_find_node(tree, xml_path);
+
+   or
+
+   mxml_find_nodes(tree, xml_path, &nodelist);
+
+   which support a subset of the XPath specification. Another set of
+   functions is availabe to retrieve attributes and values from nodes
+   in the tree and for manipulating nodes, like replacing, adding and
+   deleting nodes.
+
+   $Id: mxml.c 61 2007-10-22 13:39:10Z ritt@PSI.CH $
+
+\********************************************************************/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+
+#ifdef _MSC_VER
+
+#include <windows.h>
+#include <io.h>
+#include <time.h>
+
+#else
+
+#define TRUE 1
+#define FALSE 0
+
+#ifndef O_TEXT
+#define O_TEXT 0
+#define O_BINARY 0
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <errno.h>
+#ifndef OS_VXWORKS
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+#endif
+
+#include "mxml.h"
+#include "strlcpy.h"
+
+#define XML_INDENT "  "
+
+#if defined(__GNUC__) && !defined(__MAKECINT__)
+#   define MXML_GNUC_PRINTF( format_idx, arg_idx )          \
+   __attribute__((format (printf, format_idx, arg_idx)))
+#   define MXML_GNUC_SCANF( format_idx, arg_idx )           \
+   __attribute__((format (scanf, format_idx, arg_idx)))
+#   define MXML_GNUC_FORMAT( arg_idx )                      \
+   __attribute__((format_arg (arg_idx)))
+#else
+#   define MXML_GNUC_PRINTF( format_idx, arg_idx )
+#   define MXML_GNUC_SCANF( format_idx, arg_idx )
+#   define MXML_GNUC_FORMAT( arg_idx )
+#endif
+
+static int mxml_suppress_date_flag = 0; /* suppress writing date at the top of file. */
+
+/* local prototypes */
+static PMXML_NODE read_error(PMXML_NODE root, const char *file_name, int line_number, char *error, int error_size,
+                             const char *format, ...) MXML_GNUC_PRINTF(6, 7);
+static void mxml_encode(char *src, int size, int translate);
+static void mxml_decode(char *str);
+static int mxml_write_subtree(MXML_WRITER *writer, PMXML_NODE tree, int indent);
+static int mxml_write_line(MXML_WRITER *writer, const char *line);
+static int mxml_start_element1(MXML_WRITER *writer, const char *name, int indent);
+static int mxml_add_resultnode(PMXML_NODE node, const char *xml_path, PMXML_NODE **nodelist, int *found);
+static int mxml_find_nodes1(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist, int *found);
+
+/*------------------------------------------------------------------*/
+
+int mxml_write_line(MXML_WRITER *writer, const char *line)
+{
+   int len;
+   
+   len = strlen(line);
+
+   if (writer->buffer) {
+      if (writer->buffer_len + len >= writer->buffer_size) {
+         writer->buffer_size += 10000;
+         writer->buffer = (char *)realloc(writer->buffer, writer->buffer_size);
+      }
+      strcpy(writer->buffer + writer->buffer_len, line);
+      writer->buffer_len += len;
+      return len;
+   } else {
+      return write(writer->fh, line, len);
+   }
+
+   return 0;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * open a memory buffer and write XML header
+ */
+MXML_WRITER *mxml_open_buffer(void)
+{
+   char str[256], line[1000];
+   time_t now;
+   MXML_WRITER *writer;
+
+   writer = (MXML_WRITER *)malloc(sizeof(MXML_WRITER));
+   memset(writer, 0, sizeof(MXML_WRITER));
+   writer->translate = 1;
+
+   writer->buffer_size = 10000;
+   writer->buffer = (char *)malloc(10000);
+   writer->buffer[0] = 0;
+   writer->buffer_len = 0;
+
+   /* write XML header */
+   strcpy(line, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
+   mxml_write_line(writer, line);
+   time(&now);
+   strcpy(str, ctime(&now));
+   str[24] = 0;
+   sprintf(line, "<!-- created by MXML on %s -->\n", str);
+   if (mxml_suppress_date_flag == 0)
+      mxml_write_line(writer, line);
+
+   /* initialize stack */
+   writer->level = 0;
+   writer->element_is_open = 0;
+
+   return writer;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * suppress writing date at the top of file.
+ */
+void mxml_suppress_date(int suppress)
+{
+   mxml_suppress_date_flag = suppress;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * open a file and write XML header
+ */
+MXML_WRITER *mxml_open_file(const char *file_name) 
+{
+   char str[256], line[1000];
+   time_t now;
+   MXML_WRITER *writer;
+
+   writer = (MXML_WRITER *)malloc(sizeof(MXML_WRITER));
+   memset(writer, 0, sizeof(MXML_WRITER));
+   writer->translate = 1;
+
+   writer->fh = open(file_name, O_RDWR | O_CREAT | O_TRUNC | O_TEXT, 0644);
+
+   if (writer->fh == -1) {
+      sprintf(line, "Unable to open file \"%s\": ", file_name);
+      perror(line);
+      free(writer);
+      return NULL;
+   }
+
+   /* write XML header */
+   strcpy(line, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
+   mxml_write_line(writer, line);
+   time(&now);
+   strcpy(str, ctime(&now));
+   str[24] = 0;
+   sprintf(line, "<!-- created by MXML on %s -->\n", str);
+   if (mxml_suppress_date_flag == 0)
+      mxml_write_line(writer, line);
+
+   /* initialize stack */
+   writer->level = 0;
+   writer->element_is_open = 0;
+
+   return writer;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * convert '<' '>' '&' '"' ''' into &xx;
+ */
+void mxml_encode(char *src, int size, int translate)
+{
+   char *ps, *pd;
+   static char *buffer = NULL;
+   static int buffer_size = 1000;
+
+   assert(size);
+
+   if (buffer == NULL)
+      buffer = (char *) malloc(buffer_size);
+
+   if (size > buffer_size) {
+      buffer = (char *) realloc(buffer, size*2);
+      buffer_size = size;
+   }
+
+   ps = src;
+   pd = buffer;
+   for (ps = src ; *ps && (size_t)pd - (size_t)buffer < (size_t)(size-10) ; ps++) {
+
+     if (translate) { /* tranlate "<", ">", "&", """, "'" */
+         switch (*ps) {
+         case '<':
+            strcpy(pd, "&lt;");
+            pd += 4;
+            break;
+         case '>':
+            strcpy(pd, "&gt;");
+            pd += 4;
+            break;
+         case '&':
+            strcpy(pd, "&amp;");
+            pd += 5;
+            break;
+         case '\"':
+            strcpy(pd, "&quot;");
+            pd += 6;
+            break;
+         case '\'':
+            strcpy(pd, "&apos;");
+            pd += 6;
+            break;
+         default:
+            *pd++ = *ps;
+         }
+      } else {
+       switch (*ps) { /* translate only illegal XML characters "<" and "&" */
+         case '<':
+            strcpy(pd, "&lt;");
+            pd += 4;
+            break;
+         case '&':
+            strcpy(pd, "&amp;");
+            pd += 5;
+            break;
+         default:
+            *pd++ = *ps;
+         }
+      }
+   }
+   *pd = 0;
+
+   strlcpy(src, buffer, size);
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * reverse of mxml_encode, strip leading or trailing '"'
+ */
+void mxml_decode(char *str)
+{
+   char *p;
+
+   p = str;
+   while ((p = strchr(p, '&')) != NULL) {
+      if (strncmp(p, "&lt;", 4) == 0) {
+         *(p++) = '<';
+         memmove(p, p+3, strlen(p+3) + 1);
+      }
+      else if (strncmp(p, "&gt;", 4) == 0) {
+         *(p++) = '>';
+         memmove(p, p+3, strlen(p+3) + 1);
+      }
+      else if (strncmp(p, "&amp;", 5) == 0) {
+         *(p++) = '&';
+         memmove(p, p+4, strlen(p+4) + 1);
+      }
+      else if (strncmp(p, "&quot;", 6) == 0) {
+         *(p++) = '\"';
+         memmove(p, p+5, strlen(p+5) + 1);
+      }
+      else if (strncmp(p, "&apos;", 6) == 0) {
+         *(p++) = '\'';
+         memmove(p, p+5, strlen(p+5) + 1);
+      }
+      else {
+         p++; // skip unknown entity
+      }
+   }
+/*   if (str[0] == '\"' && str[strlen(str)-1] == '\"') {
+      memmove(str, str+1, strlen(str+1) + 1);
+      str[strlen(str)-1] = 0;
+   }*/
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * set translation of <,>,",',&, on/off in writer
+ */
+int mxml_set_translate(MXML_WRITER *writer, int flag)
+{
+   int old_flag;
+
+   old_flag = writer->translate;
+   writer->translate = flag;
+   return old_flag;
+}
+/*------------------------------------------------------------------*/
+
+/**
+ * start a new XML element, must be followed by mxml_end_elemnt
+ */
+int mxml_start_element1(MXML_WRITER *writer, const char *name, int indent)
+{
+   int i;
+   char line[1000], name_enc[1000];
+
+   if (writer->element_is_open) {
+      mxml_write_line(writer, ">\n");
+      writer->element_is_open = FALSE;
+   }
+
+   line[0] = 0;
+   if (indent)
+      for (i=0 ; i<writer->level ; i++)
+         strlcat(line, XML_INDENT, sizeof(line));
+   strlcat(line, "<", sizeof(line));
+   strlcpy(name_enc, name, sizeof(name_enc));
+   mxml_encode(name_enc, sizeof(name_enc), writer->translate);
+   strlcat(line, name_enc, sizeof(line));
+
+   /* put element on stack */
+   if (writer->level == 0)
+      writer->stack = (char **)malloc(sizeof(char *));
+   else
+      writer->stack = (char **)realloc(writer->stack, sizeof(char *)*(writer->level+1));
+   
+   writer->stack[writer->level] = (char *) malloc(strlen(name_enc)+1);
+   strcpy(writer->stack[writer->level], name_enc);
+   writer->level++;
+   writer->element_is_open = TRUE;
+   writer->data_was_written = FALSE;
+
+   return mxml_write_line(writer, line) == (int)strlen(line);
+}
+
+/*------------------------------------------------------------------*/
+
+int mxml_start_element(MXML_WRITER *writer, const char *name)
+{
+   return mxml_start_element1(writer, name, TRUE);
+}
+
+/*------------------------------------------------------------------*/
+
+int mxml_start_element_noindent(MXML_WRITER *writer, const char *name)
+{
+   return mxml_start_element1(writer, name, FALSE);
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * close an open XML element
+ */
+int mxml_end_element(MXML_WRITER *writer)
+{
+   int i;
+   char line[1000];
+
+   if (writer->level == 0)
+      return 0;
+   
+   writer->level--;
+
+   if (writer->element_is_open) {
+      writer->element_is_open = FALSE;
+      free(writer->stack[writer->level]);
+      if (writer->level == 0)
+         free(writer->stack);
+      strcpy(line, "/>\n");
+      return mxml_write_line(writer, line) == (int)strlen(line);
+   }
+
+   line[0] = 0;
+   if (!writer->data_was_written) {
+      for (i=0 ; i<writer->level ; i++)
+         strlcat(line, XML_INDENT, sizeof(line));
+   }
+
+   strlcat(line, "</", sizeof(line));
+   strlcat(line, writer->stack[writer->level], sizeof(line));
+   free(writer->stack[writer->level]);
+   if (writer->level == 0)
+      free(writer->stack);
+   strlcat(line, ">\n", sizeof(line));
+   writer->data_was_written = FALSE;
+
+   return mxml_write_line(writer, line) == (int)strlen(line);
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * write an attribute to the currently open XML element
+ */
+int mxml_write_attribute(MXML_WRITER *writer, const char *name, const char *value)
+{
+   char name_enc[4096], val_enc[4096], line[8192];
+
+   if (!writer->element_is_open)
+      return FALSE;
+
+   strcpy(name_enc, name);
+   mxml_encode(name_enc, sizeof(name_enc), writer->translate);
+   strcpy(val_enc, value);
+   mxml_encode(val_enc, sizeof(val_enc), writer->translate);
+
+   sprintf(line, " %s=\"%s\"", name_enc, val_enc);
+
+   return mxml_write_line(writer, line) == (int)strlen(line);
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * write value of an XML element, like <[name]>[value]</[name]>
+ */
+int mxml_write_value(MXML_WRITER *writer, const char *data)
+{
+   static char *data_enc;
+   static int data_size = 0;
+
+   if (!writer->element_is_open)
+      return FALSE;
+
+   if (mxml_write_line(writer, ">") != 1)
+      return FALSE;
+   writer->element_is_open = FALSE;
+   writer->data_was_written = TRUE;
+
+   if (data_size == 0) {
+      data_enc = (char *)malloc(1000);
+      data_size = 1000;
+   } else if ((int)strlen(data)*2+1000 > data_size) {
+      data_size = 1000+strlen(data)*2;
+      data_enc = (char *)realloc(data_enc, data_size);
+   }
+
+   strcpy(data_enc, data);
+   mxml_encode(data_enc, data_size, writer->translate);
+   return mxml_write_line(writer, data_enc) == (int)strlen(data_enc);
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * write empty line
+ */
+int mxml_write_empty_line(MXML_WRITER *writer)
+{
+   if (writer->element_is_open) {
+      mxml_write_line(writer, ">\n");
+      writer->element_is_open = FALSE;
+   }
+
+   if (mxml_write_line(writer, "\n") != 1)
+      return FALSE;
+
+   return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * write a comment to an XML file, enclosed in "<!--" and "-->"
+ */
+int mxml_write_comment(MXML_WRITER *writer, const char *string)
+{
+   int  i;
+   char line[1000];
+
+   if (writer->element_is_open) {
+      mxml_write_line(writer, ">\n");
+      writer->element_is_open = FALSE;
+   }
+
+   line[0] = 0;
+   for (i=0 ; i<writer->level ; i++)
+      strlcat(line, XML_INDENT, sizeof(line));
+
+   strlcat(line, "<!-- ", sizeof(line));
+   strlcat(line, string, sizeof(line));
+   strlcat(line, " -->\n", sizeof(line));
+   if (mxml_write_line(writer, line) != (int)strlen(line))
+      return FALSE;
+
+   return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * shortcut to write an element with a value but without attribute
+ */
+int mxml_write_element(MXML_WRITER *writer, const char *name, const char *value)
+{
+   int i;
+
+   i = mxml_start_element(writer, name);
+   i += mxml_write_value(writer, value);
+   i += mxml_end_element(writer);
+   return i;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * close a file opened with mxml_open_writer
+ */
+char *mxml_close_buffer(MXML_WRITER *writer)
+{
+   int i;
+   char *p;
+
+   if (writer->element_is_open) {
+      writer->element_is_open = FALSE;
+      if (mxml_write_line(writer, ">\n") != 2)
+         return NULL;
+   }
+
+   /* close remaining open levels */
+   for (i = 0 ; i<writer->level ; i++)
+      mxml_end_element(writer);
+
+   p = writer->buffer;
+   free(writer);
+   return p;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * close a file opened with mxml_open_writer
+ */
+int mxml_close_file(MXML_WRITER *writer)
+{
+   int i;
+
+   if (writer->element_is_open) {
+      writer->element_is_open = FALSE;
+      if (mxml_write_line(writer, ">\n") != 2)
+         return 0;
+   }
+
+   /* close remaining open levels */
+   for (i = 0 ; i<writer->level ; i++)
+      mxml_end_element(writer);
+
+   close(writer->fh);
+   free(writer);
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * create root node of an XML tree
+ */
+PMXML_NODE mxml_create_root_node(void)
+{
+   PMXML_NODE root;
+
+   root = (PMXML_NODE)calloc(sizeof(MXML_NODE), 1);
+   strcpy(root->name, "root");
+   root->node_type = DOCUMENT_NODE;
+
+   return root;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * add a subnode (child) to an existing parent node as a specific position
+ */
+PMXML_NODE mxml_add_special_node_at(PMXML_NODE parent, int node_type, const char *node_name, const char *value, int idx)
+{
+   PMXML_NODE pnode, pchild;
+   int i, j;
+
+   assert(parent);
+   if (parent->n_children == 0)
+      parent->child = (PMXML_NODE)malloc(sizeof(MXML_NODE));
+   else {
+      pchild = parent->child;
+      parent->child = (PMXML_NODE)realloc(parent->child, sizeof(MXML_NODE)*(parent->n_children+1));
+
+   }
+   assert(parent->child);
+
+   /* move following nodes one down */
+   if (idx < parent->n_children) 
+      for (i=parent->n_children ; i > idx ; i--)
+         memcpy(&parent->child[i], &parent->child[i-1], sizeof(MXML_NODE));
+
+   /* correct parent pointer for children */
+   for (i=0 ; i<parent->n_children ; i++) {
+      pchild = parent->child+i;
+      for (j=0 ; j<pchild->n_children ; j++)
+         pchild->child[j].parent = pchild;
+   }
+
+   /* initialize new node */
+   pnode = &parent->child[idx];
+   memset(pnode, 0, sizeof(MXML_NODE));
+   strlcpy(pnode->name, node_name, sizeof(pnode->name));
+   pnode->node_type = node_type;
+   pnode->parent = parent;
+   
+   parent->n_children++;
+
+   if (value && *value) {
+      pnode->value = (char *)malloc(strlen(value)+1);
+      assert(pnode->value);
+      strcpy(pnode->value, value);
+   }
+
+   return pnode;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * add a subnode (child) to an existing parent node at the end
+ */
+PMXML_NODE mxml_add_special_node(PMXML_NODE parent, int node_type, const char *node_name, const char *value)
+{
+   return mxml_add_special_node_at(parent, node_type, node_name, value, parent->n_children);
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * write value of an XML element, like <[name]>[value]</[name]>
+ */
+PMXML_NODE mxml_add_node(PMXML_NODE parent, const char *node_name, const char *value)
+{
+   return mxml_add_special_node_at(parent, ELEMENT_NODE, node_name, value, parent->n_children);
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * add a subnode (child) to an existing parent node at the end
+ */
+PMXML_NODE mxml_add_node_at(PMXML_NODE parent, const char *node_name, const char *value, int idx)
+{
+   return mxml_add_special_node_at(parent, ELEMENT_NODE, node_name, value, idx);
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * add a whole node tree to an existing parent node at a specific position
+ */
+int mxml_add_tree_at(PMXML_NODE parent, PMXML_NODE tree, int idx)
+{
+   PMXML_NODE pchild;
+   int i, j, k;
+
+   assert(parent);
+   assert(tree);
+   if (parent->n_children == 0)
+      parent->child = (PMXML_NODE)malloc(sizeof(MXML_NODE));
+   else {
+      pchild = parent->child;
+      parent->child = (PMXML_NODE)realloc(parent->child, sizeof(MXML_NODE)*(parent->n_children+1));
+
+      if (parent->child != pchild) {
+         /* correct parent pointer for children */
+         for (i=0 ; i<parent->n_children ; i++) {
+            pchild = parent->child+i;
+            for (j=0 ; j<pchild->n_children ; j++)
+               pchild->child[j].parent = pchild;
+         }
+      }
+   }
+   assert(parent->child);
+
+   if (idx < parent->n_children) 
+      for (i=parent->n_children ; i > idx ; i--) {
+         /* move following nodes one down */
+         memcpy(&parent->child[i], &parent->child[i-1], sizeof(MXML_NODE));
+
+         /* correct parent pointer for children */
+         for (j=0 ; j<parent->n_children ; j++) {
+            pchild = parent->child+j;
+            for (k=0 ; k<pchild->n_children ; k++)
+               pchild->child[k].parent = pchild;
+         }
+      }
+
+   /* initialize new node */
+   memcpy(parent->child+idx, tree, sizeof(MXML_NODE));
+   parent->n_children++;
+   parent->child[idx].parent = parent;
+
+   /* correct parent pointer for children */
+   for (i=0 ; i<parent->n_children ; i++) {
+      pchild = parent->child+i;
+      for (j=0 ; j<pchild->n_children ; j++)
+         pchild->child[j].parent = pchild;
+   }
+
+   return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * add a whole node tree to an existing parent node at the end
+ */
+int mxml_add_tree(PMXML_NODE parent, PMXML_NODE tree)
+{
+   return mxml_add_tree_at(parent, tree, parent->n_children);
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * add an attribute to an existing node
+ */
+int mxml_add_attribute(PMXML_NODE pnode, const char *attrib_name, const char *attrib_value)
+{
+   if (pnode->n_attributes == 0) {
+      pnode->attribute_name  = (char*)malloc(MXML_NAME_LENGTH);
+      pnode->attribute_value = (char**)malloc(sizeof(char *));
+   } else {
+      pnode->attribute_name  = (char*)realloc(pnode->attribute_name,  MXML_NAME_LENGTH*(pnode->n_attributes+1));
+      pnode->attribute_value = (char**)realloc(pnode->attribute_value, sizeof(char *)*(pnode->n_attributes+1));
+   }
+
+   strlcpy(pnode->attribute_name+pnode->n_attributes*MXML_NAME_LENGTH, attrib_name, MXML_NAME_LENGTH);
+   pnode->attribute_value[pnode->n_attributes] = (char *)malloc(strlen(attrib_value)+1);
+   strcpy(pnode->attribute_value[pnode->n_attributes], attrib_value);
+   pnode->n_attributes++;
+
+   return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * return number of subnodes (children) of a node
+ */
+int mxml_get_number_of_children(PMXML_NODE pnode)
+{
+   assert(pnode);
+   return pnode->n_children;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * return number of subnodes (children) of a node
+ */
+PMXML_NODE mxml_subnode(PMXML_NODE pnode, int idx)
+{
+   assert(pnode);
+   if (idx < pnode->n_children)
+      return &pnode->child[idx];
+   return NULL;
+}
+
+/*------------------------------------------------------------------*/
+
+
+int mxml_find_nodes1(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist, int *found);
+
+int mxml_add_resultnode(PMXML_NODE node, const char *xml_path, PMXML_NODE **nodelist, int *found)
+{
+   /* if at end of path, add this node */
+   if (*xml_path == 0) {
+      if (*found == 0)
+         *nodelist = (PMXML_NODE *)malloc(sizeof(PMXML_NODE));
+      else
+         *nodelist = (PMXML_NODE *)realloc(*nodelist, sizeof(PMXML_NODE)*(*found + 1));
+
+      (*nodelist)[*found] = node;
+      (*found)++;
+   } else {
+      /* if not at end of path, branch into subtree */
+      return mxml_find_nodes1(node, xml_path+1, nodelist, found);
+   }
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+   Return list of XML nodes with a subset of XPATH specifications.
+   Following elemets are possible
+
+   /<node>/<node>/..../<node>          Find a node in the tree hierarchy
+   /<node>[idx]                        Find child #[idx] of node (index starts from 1)
+   /<node>[idx]/<node>                 Find subnode of the above
+   /<node>[<subnode>=<value>]          Find a node which has a specific subnode
+   /<node>[<subnode>=<value>]/<node>   Find subnode of the above
+   /<node>[@<attrib>=<value>]/<node>   Find a node which has a specific attribute
+*/
+int mxml_find_nodes1(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist, int *found)
+{
+   PMXML_NODE pnode;
+   const char *p1,*p2;
+   char *p3, node_name[256], condition[256];
+   char cond_name[MXML_MAX_CONDITION][256], cond_value[MXML_MAX_CONDITION][256];
+   int  cond_type[MXML_MAX_CONDITION];
+   int i, j, k, idx, num_cond;
+   int cond_satisfied,cond_index;
+   size_t len;
+
+   p1 = xml_path;
+   pnode = tree;
+
+   /* skip leading '/' */
+   if (*p1 && *p1 == '/')
+      p1++;
+
+   do {
+      p2 = p1;
+      while (*p2 && *p2 != '/' && *p2 != '[')
+         p2++;
+      len = (size_t)p2 - (size_t)p1;
+      if (len >= sizeof(node_name))
+         return 0;
+
+      memcpy(node_name, p1, len);
+      node_name[len] = 0;
+      idx = 0;
+      num_cond = 0;
+      while (*p2 == '[') {
+         cond_name[num_cond][0] = cond_value[num_cond][0] = cond_type[num_cond] = 0;
+         p2++;
+         if (isdigit(*p2)) {
+            /* evaluate [idx] */
+            idx = atoi(p2);
+            p2 = strchr(p2, ']');
+            if (p2 == NULL)
+               return 0;
+            p2++;
+         } else {
+            /* evaluate [<@attrib>/<subnode>=<value>] */
+            while (*p2 && isspace((unsigned char)*p2))
+               p2++;
+            strlcpy(condition, p2, sizeof(condition));
+            if (strchr(condition, ']'))
+               *strchr(condition, ']') = 0;
+            else
+               return 0;
+            p2 = strchr(p2, ']')+1;
+            if ((p3 = strchr(condition, '=')) != NULL) {
+               if (condition[0] == '@') {
+                  cond_type[num_cond] = 1;
+                  strlcpy(cond_name[num_cond], &condition[1], sizeof(cond_name[num_cond]));
+               } else {
+                  strlcpy(cond_name[num_cond], condition, sizeof(cond_name[num_cond]));
+               }
+
+               *strchr(cond_name[num_cond], '=') = 0;
+               while (cond_name[num_cond][0] && isspace(cond_name[num_cond][strlen(cond_name[num_cond])-1]))
+                  cond_name[num_cond][strlen(cond_name[num_cond])-1] = 0;
+
+               p3++;
+               while (*p3 && isspace(*p3))
+                  p3++;
+               if (*p3 == '\"') {
+                  strlcpy(cond_value[num_cond], p3+1, sizeof(cond_value[num_cond]));
+                  while (cond_value[num_cond][0] && isspace(cond_value[num_cond][strlen(cond_value[num_cond])-1]))
+                     cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
+                  if (cond_value[num_cond][0] && cond_value[num_cond][strlen(cond_value[num_cond])-1] == '\"')
+                     cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
+               } else if (*p3 == '\'') {
+                  strlcpy(cond_value[num_cond], p3+1, sizeof(cond_value[num_cond]));
+                  while (cond_value[num_cond][0] && isspace(cond_value[num_cond][strlen(cond_value[num_cond])-1]))
+                     cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
+                  if (cond_value[num_cond][0] && cond_value[num_cond][strlen(cond_value[num_cond])-1] == '\'')
+                     cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
+               } else {
+                  strlcpy(cond_value[num_cond], p3, sizeof(cond_value[num_cond]));
+                  while (cond_value[num_cond][0] && isspace(cond_value[num_cond][strlen(cond_value[num_cond])-1]))
+                     cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
+               }
+               num_cond++;
+            }
+         }
+      }
+
+      cond_index = 0;
+      for (i=j=0 ; i<pnode->n_children ; i++) {
+         if (num_cond) {
+            cond_satisfied = 0;
+            for (k=0;k<num_cond;k++) {
+               if (cond_type[k]) {
+                  /* search node with attribute */
+                  if (strcmp(pnode->child[i].name, node_name) == 0)
+                     if (mxml_get_attribute(pnode->child+i, cond_name[k]) &&
+                        strcmp(mxml_get_attribute(pnode->child+i, cond_name[k]), cond_value[k]) == 0)
+                        cond_satisfied++;
+               }
+               else {
+                  /* search subnode */
+                  for (j=0 ; j<pnode->child[i].n_children ; j++)
+                     if (strcmp(pnode->child[i].child[j].name, cond_name[k]) == 0)
+                        if (strcmp(pnode->child[i].child[j].value, cond_value[k]) == 0)
+                           cond_satisfied++;
+               }
+            }
+            if (cond_satisfied==num_cond) {
+               cond_index++;
+               if (idx == 0 || cond_index == idx) {
+                  if (!mxml_add_resultnode(pnode->child+i, p2, nodelist, found))
+                     return 0;
+               }
+            }
+         } else {
+            if (strcmp(pnode->child[i].name, node_name) == 0)
+               if (idx == 0 || ++j == idx)
+                  if (!mxml_add_resultnode(pnode->child+i, p2, nodelist, found))
+                     return 0;
+         }
+      }
+
+      if (i == pnode->n_children)
+         return 1;
+
+      pnode = &pnode->child[i];
+      p1 = p2;
+      if (*p1 == '/')
+         p1++;
+
+   } while (*p2);
+
+   return 1;
+}
+
+/*------------------------------------------------------------------*/
+
+int mxml_find_nodes(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist)
+{
+   int status, found = 0;
+   
+   status = mxml_find_nodes1(tree, xml_path, nodelist, &found);
+
+   if (status == 0)
+      return -1;
+
+   return found;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ *  Search for a specific XML node with a subset of XPATH specifications.
+ *  Return first found node. For syntax see mxml_find_nodes()
+ */
+PMXML_NODE mxml_find_node(PMXML_NODE tree, const char *xml_path)
+{
+   PMXML_NODE *node, pnode;
+   int n;
+
+   n = mxml_find_nodes(tree, xml_path, &node);
+   if (n > 0) {
+      pnode = node[0];
+      free(node);
+   } else 
+      pnode = NULL;
+
+   return pnode;
+}
+
+/*------------------------------------------------------------------*/
+
+char *mxml_get_name(PMXML_NODE pnode)
+{
+   assert(pnode);
+   return pnode->name;
+}
+
+/*------------------------------------------------------------------*/
+
+char *mxml_get_value(PMXML_NODE pnode)
+{
+   assert(pnode);
+   return pnode->value;
+}
+
+/*------------------------------------------------------------------*/
+
+char *mxml_get_attribute(PMXML_NODE pnode, const char *name)
+{
+   int i;
+
+   assert(pnode);
+   for (i=0 ; i<pnode->n_attributes ; i++) 
+      if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, name) == 0)
+         return pnode->attribute_value[i];
+
+   return NULL;
+}
+
+/*------------------------------------------------------------------*/
+
+int mxml_replace_node_name(PMXML_NODE pnode, const char *name)
+{
+   strlcpy(pnode->name, name, sizeof(pnode->name));
+   return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+
+int mxml_replace_node_value(PMXML_NODE pnode, const char *value)
+{
+   if (pnode->value)
+      pnode->value = (char *)realloc(pnode->value, strlen(value)+1);
+   else if (value)
+      pnode->value = (char *)malloc(strlen(value)+1);
+   else
+      pnode->value = NULL;
+   
+   if (value)
+      strcpy(pnode->value, value);
+
+   return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+   replace value os a subnode, like
+
+   <parent>
+     <child>value</child>
+   </parent>
+
+   if pnode=parent, and "name"="child", then "value" gets replaced
+*/
+int mxml_replace_subvalue(PMXML_NODE pnode, const char *name, const char *value)
+{
+   int i;
+
+   for (i=0 ; i<pnode->n_children ; i++) 
+      if (strcmp(pnode->child[i].name, name) == 0)
+         break;
+
+   if (i == pnode->n_children)
+      return FALSE;
+
+   return mxml_replace_node_value(&pnode->child[i], value);
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * change the name of an attribute, keep its value
+ */
+int mxml_replace_attribute_name(PMXML_NODE pnode, const char *old_name, const char *new_name)
+{
+   int i;
+
+   for (i=0 ; i<pnode->n_attributes ; i++) 
+      if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, old_name) == 0)
+         break;
+
+   if (i == pnode->n_attributes)
+      return FALSE;
+
+   strlcpy(pnode->attribute_name+i*MXML_NAME_LENGTH, new_name, MXML_NAME_LENGTH);
+   return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * change the value of an attribute
+ */
+int mxml_replace_attribute_value(PMXML_NODE pnode, const char *attrib_name, const char *attrib_value)
+{
+   int i;
+
+   for (i=0 ; i<pnode->n_attributes ; i++) 
+      if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, attrib_name) == 0)
+         break;
+
+   if (i == pnode->n_attributes)
+      return FALSE;
+
+   pnode->attribute_value[i] = (char *)realloc(pnode->attribute_value[i], strlen(attrib_value)+1);
+   strcpy(pnode->attribute_value[i], attrib_value);
+   return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * free memory of a node and remove it from the parent's child list
+ */
+int mxml_delete_node(PMXML_NODE pnode)
+{
+   PMXML_NODE parent;
+   int i, j;
+
+   /* remove node from parent's list */
+   parent = pnode->parent;
+
+   if (parent) {
+      for (i=0 ; i<parent->n_children ; i++)
+         if (&parent->child[i] == pnode)
+            break;
+
+      /* free allocated node memory recursively */
+      mxml_free_tree(pnode);
+
+      if (i < parent->n_children) {
+         for (j=i ; j<parent->n_children-1 ; j++)
+            memcpy(&parent->child[j], &parent->child[j+1], sizeof(MXML_NODE));
+         parent->n_children--;
+         if (parent->n_children)
+            parent->child = (PMXML_NODE)realloc(parent->child, sizeof(MXML_NODE)*(parent->n_children));
+         else
+            free(parent->child);
+      }
+   } else 
+      mxml_free_tree(pnode);
+
+   return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+
+int mxml_delete_attribute(PMXML_NODE pnode, const char *attrib_name)
+{
+   int i, j;
+
+   for (i=0 ; i<pnode->n_attributes ; i++) 
+      if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, attrib_name) == 0)
+         break;
+
+   if (i == pnode->n_attributes)
+      return FALSE;
+
+   free(pnode->attribute_value[i]);
+   for (j=i ; j<pnode->n_attributes-1 ; j++) {
+      strcpy(pnode->attribute_name+j*MXML_NAME_LENGTH, pnode->attribute_name+(j+1)*MXML_NAME_LENGTH);
+      pnode->attribute_value[j] = pnode->attribute_value[j+1];
+   }
+
+   if (pnode->n_attributes > 0) {
+      pnode->attribute_name  = (char *)realloc(pnode->attribute_name,  MXML_NAME_LENGTH*(pnode->n_attributes-1));
+      pnode->attribute_value = (char **)realloc(pnode->attribute_value, sizeof(char *)*(pnode->n_attributes-1));
+   } else {
+      free(pnode->attribute_name);
+      free(pnode->attribute_value);
+   }
+
+   return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+
+#define HERE root, file_name, line_number, error, error_size
+
+/**
+ * used inside mxml_parse_file for reporting errors
+ */
+PMXML_NODE read_error(PMXML_NODE root, const char *file_name, int line_number, char *error, int error_size, const char *format, ...)
+{
+   char *msg, str[1000];
+   va_list argptr;
+
+   if (file_name && file_name[0])
+      sprintf(str, "XML read error in file \"%s\", line %d: ", file_name, line_number);
+   else
+      sprintf(str, "XML read error, line %d: ", line_number);
+   msg = (char *)malloc(error_size);
+   strlcpy(error, str, error_size);
+
+   va_start(argptr, format);
+   vsprintf(str, (char *) format, argptr);
+   va_end(argptr);
+
+   strlcat(error, str, error_size);
+   free(msg);
+   mxml_free_tree(root);
+
+   return NULL;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * Parse a XML buffer and convert it into a tree of MXML_NODE's.
+ * Return NULL in case of an error, return error description.
+ * Optional file_name is used for error reporting if called from mxml_parse_file()
+ */
+PMXML_NODE mxml_parse_buffer(const char *buf, char *error, int error_size)
+{
+   char node_name[256], attrib_name[256], attrib_value[1000], quote;
+   const char *p, *pv;
+   int i,j, line_number;
+   PMXML_NODE root, ptree, pnew;
+   int end_element;
+   size_t len;
+   char *file_name = NULL; /* dummy for 'HERE' */
+
+   p = buf;
+   line_number = 1;
+
+   root = mxml_create_root_node();
+   ptree = root;
+
+   /* parse file contents */
+   do {
+      if (*p == '<') {
+
+         end_element = FALSE;
+
+         /* found new element */
+         p++;
+         while (*p && isspace(*p)) {
+            if (*p == '\n')
+               line_number++;
+            p++;
+         }
+         if (!*p)
+            return read_error(HERE, "Unexpected end of file");
+
+         if (strncmp(p, "!--", 3) == 0) {
+            
+            /* found comment */
+
+            pnew = mxml_add_special_node(ptree, COMMENT_NODE, "Comment", NULL);
+            pv = p+3;
+            while (*pv == ' ')
+               pv++;
+
+            p += 3;
+            if (strstr(p, "-->") == NULL)
+               return read_error(HERE, "Unterminated comment");
+            
+            while (strncmp(p, "-->", 3) != 0) {
+               if (*p == '\n')
+                  line_number++;
+               p++;
+            }
+
+            len = (size_t)p - (size_t)pv;
+            pnew->value = (char *)malloc(len+1);
+            memcpy(pnew->value, pv, len);
+            pnew->value[len] = 0;
+            mxml_decode(pnew->value);
+
+            p += 3;
+
+         } else if (*p == '?') {
+
+            /* found ?...? element */
+            pnew = mxml_add_special_node(ptree, PROCESSING_INSTRUCTION_NODE, "PI", NULL);
+            pv = p+1;
+
+            p++;
+            if (strstr(p, "?>") == NULL)
+               return read_error(HERE, "Unterminated ?...? element");
+            
+            while (strncmp(p, "?>", 2) != 0) {
+               if (*p == '\n')
+                  line_number++;
+               p++;
+            }
+
+            len = (size_t)p - (size_t)pv;
+            pnew->value = (char *)malloc(len+1);
+            memcpy(pnew->value, pv, len);
+            pnew->value[len] = 0;
+            mxml_decode(pnew->value);
+
+            p += 2;
+
+         } else if (strncmp(p, "!DOCTYPE", 8) == 0 ) {
+
+            /* found !DOCTYPE element , skip it */
+            p += 8;
+            if (strstr(p, ">") == NULL)
+               return read_error(HERE, "Unterminated !DOCTYPE element");
+
+            j = 0;
+            while (*p && (*p != '>' || j > 0)) {
+               if (*p == '\n')
+                  line_number++;
+               else if (*p == '<')
+                  j++;
+               else if (*p == '>')
+                  j--;
+               p++;
+            }
+            if (!*p)
+               return read_error(HERE, "Unexpected end of file");
+
+            p++;
+
+         } else {
+            
+            /* found normal element */
+            if (*p == '/') {
+               end_element = TRUE;
+               p++;
+               while (*p && isspace((unsigned char)*p)) {
+                  if (*p == '\n')
+                     line_number++;
+                  p++;
+               }
+               if (!*p)
+                  return read_error(HERE, "Unexpected end of file");
+            }
+
+            /* extract node name */
+            i = 0;
+            node_name[i] = 0;
+            while (*p && !isspace((unsigned char)*p) && *p != '/' && *p != '>' && *p != '<')
+               node_name[i++] = *p++;
+            node_name[i] = 0;
+            if (!*p)
+               return read_error(HERE, "Unexpected end of file");
+            if (*p == '<')
+               return read_error(HERE, "Unexpected \'<\' inside element \"%s\"", node_name);
+
+            mxml_decode(node_name);
+
+            if (end_element) {
+
+               if (!ptree)
+                  return read_error(HERE, "Found unexpected </%s>", node_name);
+
+               /* close previously opened element */
+               if (strcmp(ptree->name, node_name) != 0)
+                  return read_error(HERE, "Found </%s>, expected </%s>", node_name, ptree->name);
+            
+               /* go up one level on the tree */
+               ptree = ptree->parent;
+
+            } else {
+            
+               if (ptree == NULL)
+                  return read_error(HERE, "Unexpected second top level node");
+
+               /* allocate new element structure in parent tree */
+               pnew = mxml_add_node(ptree, node_name, NULL);
+
+               while (*p && isspace((unsigned char)*p)) {
+                  if (*p == '\n')
+                     line_number++;
+                  p++;
+               }
+               if (!*p)
+                  return read_error(HERE, "Unexpected end of file");
+
+               while (*p != '>' && *p != '/') {
+
+                  /* found attribute */
+                  pv = p;
+                  while (*pv && !isspace((unsigned char)*pv) && *pv != '=' && *pv != '<' && *pv != '>')
+                     pv++;
+                  if (!*pv)
+                     return read_error(HERE, "Unexpected end of file");
+                  if (*pv == '<' || *pv == '>')
+                     return read_error(HERE, "Unexpected \'%c\' inside element \"%s\"", *pv, node_name);
+
+                  /* extract attribute name */
+                  len = (size_t)pv - (size_t)p;
+                  if (len > sizeof(attrib_name)-1)
+                     len = sizeof(attrib_name)-1;
+                  memcpy(attrib_name, p, len);
+                  attrib_name[len] = 0;
+                  mxml_decode(attrib_name);
+
+                  p = pv;
+                  while (*p && isspace((unsigned char)*p)) {
+                     if (*p == '\n')
+                        line_number++;
+                     p++;
+                  }
+                  if (!*p)
+                     return read_error(HERE, "Unexpected end of file");
+                  if (*p != '=')
+                     return read_error(HERE, "Expect \"=\" here");
+
+                  p++;
+                  while (*p && isspace((unsigned char)*p)) {
+                     if (*p == '\n')
+                        line_number++;
+                     p++;
+                  }
+                  if (!*p)
+                     return read_error(HERE, "Unexpected end of file");
+                  if (*p != '\"' && *p != '\'')
+                     return read_error(HERE, "Expect \" or \' here");
+                  quote = *p;
+                  p++;
+
+                  /* extract attribute value */
+                  pv = p;
+                  while (*pv && *pv != quote)
+                     pv++;
+                  if (!*pv)
+                     return read_error(HERE, "Unexpected end of file");
+
+                  len = (size_t)pv - (size_t)p;
+                  if (len > sizeof(attrib_value)-1)
+                     len = sizeof(attrib_value)-1;
+                  memcpy(attrib_value, p, len);
+                  attrib_value[len] = 0;
+                  mxml_decode(attrib_value);
+
+                  /* add attribute to current node */
+                  mxml_add_attribute(pnew, attrib_name, attrib_value);
+
+                  p = pv+1;
+                  while (*p && isspace((unsigned char)*p)) {
+                     if (*p == '\n')
+                        line_number++;
+                     p++;
+                  }
+                  if (!*p)
+                     return read_error(HERE, "Unexpected end of file");
+               }
+
+               if (*p == '/') {
+
+                  /* found empty node, like <node/>, just skip closing bracket */
+                  p++;
+
+                  while (*p && isspace((unsigned char)*p)) {
+                     if (*p == '\n')
+                        line_number++;
+                     p++;
+                  }
+                  if (!*p)
+                     return read_error(HERE, "Unexpected end of file");
+                  if (*p != '>')
+                     return read_error(HERE, "Expected \">\" after \"/\"");
+                  p++;
+               }
+
+               if (*p == '>') {
+
+                  p++;
+
+                  /* check if we have sub-element or value */
+                  pv = p;
+                  while (*pv && isspace((unsigned char)*pv)) {
+                     if (*pv == '\n')
+                        line_number++;
+                     pv++;
+                  }
+                  if (!*pv)
+                     return read_error(HERE, "Unexpected end of file");
+
+                  if (*pv == '<' && *(pv+1) != '/') {
+
+                     /* start new subtree */
+                     ptree = pnew;
+                     p = pv;
+
+                  } else {
+
+                     /* extract value */
+                     while (*pv && *pv != '<') {
+                        if (*pv == '\n')
+                           line_number++;
+                        pv++;
+                     }
+                     if (!*pv)
+                        return read_error(HERE, "Unexpected end of file");
+
+                     len = (size_t)pv - (size_t)p;
+                     pnew->value = (char *)malloc(len+1);
+                     memcpy(pnew->value, p, len);
+                     pnew->value[len] = 0;
+                     mxml_decode(pnew->value);
+                     p = pv;
+
+                     ptree = pnew;
+                  }
+               }
+            }
+         }
+      }
+
+      /* go to next element */
+      while (*p && *p != '<') {
+         if (*p == '\n')
+            line_number++;
+         p++;
+      }
+   } while (*p);
+
+   return root;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * parse !ENTYTY entries of XML files and replace with references.
+ * Return 0 in case of no errors, return error description.
+ * Optional file_name is used for error reporting if called from mxml_parse_file()
+ */
+int mxml_parse_entity(char **buf, const char *file_name, char *error, int error_size)
+{
+   char *p;
+   char *pv;
+   char delimiter;
+   int i, j, k, line_number;
+   char *replacement;
+   char entity_name[MXML_MAX_ENTITY][256];
+   char entity_reference_name[MXML_MAX_ENTITY][256];
+   char *entity_value[MXML_MAX_ENTITY];
+   int entity_type[MXML_MAX_ENTITY];    /* internal or external */
+   int nentity;
+   int fh, length, len;
+   char *buffer;
+   PMXML_NODE root = mxml_create_root_node();   /* dummy for 'HERE' */
+   int ip;                      /* counter for entity value */
+   char directoryname[FILENAME_MAX];
+   char filename[FILENAME_MAX];
+   int entity_value_length[MXML_MAX_ENTITY];
+   int entity_name_length[MXML_MAX_ENTITY];
+
+   for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+      entity_value[ip] = NULL;
+
+   line_number = 1;
+   nentity = -1;
+
+   if (!buf || !(*buf) || !strlen(*buf))
+      return 0;
+
+   strcpy(directoryname, file_name);
+   mxml_dirname(directoryname);
+
+   /* copy string to temporary space */
+   buffer = (char *) malloc(strlen(*buf) + 1);
+   if (buffer == NULL) {
+      read_error(HERE, "Cannot allocate memory.");
+      free(buffer);
+      for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+         free(entity_value[ip]);
+      return 1;
+   }
+   strcpy(buffer, *buf);
+
+   p = strstr(buffer, "!DOCTYPE");
+   if (p == NULL) {             /* no entities */
+      mxml_free_tree(root);
+      free(buffer);
+      for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+         free(entity_value[ip]);
+      return 0;
+   }
+
+   pv = strstr(p, "[");
+   if (pv == NULL) {            /* no entities */
+      mxml_free_tree(root);
+      free(buffer);
+      for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+         free(entity_value[ip]);
+      return 0;
+   }
+
+   p = pv + 1;
+
+   /* search !ENTITY */
+   do {
+      if (*p == ']')
+         break;
+
+      if (*p == '<') {
+
+         /* found new entity */
+         p++;
+         while (*p && isspace((unsigned char)*p)) {
+            if (*p == '\n')
+               line_number++;
+            p++;
+         }
+         if (!*p) {
+            read_error(HERE, "Unexpected end of file");
+            free(buffer);
+            for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+               free(entity_value[ip]);
+            return 1;
+         }
+
+         if (strncmp(p, "!--", 3) == 0) {
+            /* found comment */
+            p += 3;
+            if (strstr(p, "-->") == NULL) {
+               read_error(HERE, "Unterminated comment");
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+
+            while (strncmp(p, "-->", 3) != 0) {
+               if (*p == '\n')
+                  line_number++;
+               p++;
+            }
+            p += 3;
+         }
+
+         else if (strncmp(p, "!ENTITY", 7) == 0) {
+            /* found entity */
+            nentity++;
+            if (nentity >= MXML_MAX_ENTITY) {
+               read_error(HERE, "Too much entities");
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+
+            pv = p + 7;
+            while (*pv == ' ')
+               pv++;
+
+            /* extract entity name */
+            p = pv;
+
+            while (*p && isspace((unsigned char)*p) && *p != '<' && *p != '>') {
+               if (*p == '\n')
+                  line_number++;
+               p++;
+            }
+            if (!*p) {
+               read_error(HERE, "Unexpected end of file");
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+            if (*p == '<' || *p == '>') {
+               read_error(HERE, "Unexpected \'%c\' inside !ENTITY", *p);
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+
+            pv = p;
+            while (*pv && !isspace((unsigned char)*pv) && *pv != '<' && *pv != '>')
+               pv++;
+
+            if (!*pv) {
+               read_error(HERE, "Unexpected end of file");
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+            if (*pv == '<' || *pv == '>') {
+               read_error(HERE, "Unexpected \'%c\' inside entity \"%s\"", *pv, &entity_name[nentity][1]);
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+
+            len = (size_t) pv - (size_t) p;
+
+            entity_name[nentity][0] = '&';
+            i = 1;
+            entity_name[nentity][i] = 0;
+            while (*p && !isspace((unsigned char)*p) && *p != '/' && *p != '>' && *p != '<' && i < 253)
+               entity_name[nentity][i++] = *p++;
+            entity_name[nentity][i++] = ';';
+            entity_name[nentity][i] = 0;
+
+            if (!*p) {
+               read_error(HERE, "Unexpected end of file");
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+            if (*p == '<') {
+               read_error(HERE, "Unexpected \'<\' inside entity \"%s\"", &entity_name[nentity][1]);
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+
+            /* extract replacement or SYSTEM */
+            while (*p && isspace((unsigned char)*p)) {
+               if (*p == '\n')
+                  line_number++;
+               p++;
+            }
+            if (!*p) {
+               read_error(HERE, "Unexpected end of file");
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+            if (*p == '>') {
+               read_error(HERE, "Unexpected \'>\' inside entity \"%s\"", &entity_name[nentity][1]);
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+
+            /* check if SYSTEM */
+            if (strncmp(p, "SYSTEM", 6) == 0) {
+               entity_type[nentity] = EXTERNAL_ENTITY;
+               p += 6;
+            } else {
+               entity_type[nentity] = INTERNAL_ENTITY;
+            }
+
+            /* extract replacement */
+            while (*p && isspace((unsigned char)*p)) {
+               if (*p == '\n')
+                  line_number++;
+               p++;
+            }
+            if (!*p) {
+               read_error(HERE, "Unexpected end of file");
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+            if (*p == '>') {
+               read_error(HERE, "Unexpected \'>\' inside entity \"%s\"", &entity_name[nentity][1]);
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+
+            if (*p != '\"' && *p != '\'') {
+               read_error(HERE, "Replacement was not found for entity \"%s\"", &entity_name[nentity][1]);
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+            delimiter = *p;
+            p++;
+            if (!*p) {
+               read_error(HERE, "Unexpected end of file");
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+            pv = p;
+            while (*pv && *pv != delimiter)
+               pv++;
+
+            if (!*pv) {
+               read_error(HERE, "Unexpected end of file");
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+            if (*pv == '<') {
+               read_error(HERE, "Unexpected \'%c\' inside entity \"%s\"", *pv, &entity_name[nentity][1]);
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+
+            len = (size_t) pv - (size_t) p;
+            replacement = (char *) malloc(len + 1);
+            if (replacement == NULL) {
+               read_error(HERE, "Cannot allocate memory.");
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+
+            memcpy(replacement, p, len);
+            replacement[len] = 0;
+            mxml_decode(replacement);
+
+            if (entity_type[nentity] == EXTERNAL_ENTITY) {
+               strcpy(entity_reference_name[nentity], replacement);
+            } else {
+               entity_value[nentity] = (char *) malloc(strlen(replacement));
+               if (entity_value[nentity] == NULL) {
+                  read_error(HERE, "Cannot allocate memory.");
+                  free(buffer);
+                  for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                     free(entity_value[ip]);
+                  return 1;
+               }
+               strcpy(entity_value[nentity], replacement);
+            }
+            free(replacement);
+
+            p = pv;
+            while (*p && isspace((unsigned char)*p)) {
+               if (*p == '\n')
+                  line_number++;
+               p++;
+            }
+            if (!*p) {
+               read_error(HERE, "Unexpected end of file");
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+         }
+      }
+
+      /* go to next element */
+      while (*p && *p != '<') {
+         if (*p == '\n')
+            line_number++;
+         p++;
+      }
+   } while (*p);
+   nentity++;
+
+   /* read external file */
+   for (i = 0; i < nentity; i++) {
+      if (entity_type[i] == EXTERNAL_ENTITY) {
+         if ( entity_reference_name[i][0] == DIR_SEPARATOR ) /* absolute path */
+            strcpy(filename, entity_reference_name[i]);
+         else /* relative path */
+            sprintf(filename, "%s%c%s", directoryname, DIR_SEPARATOR, entity_reference_name[i]);
+         fh = open(filename, O_RDONLY | O_TEXT, 0644);
+
+         if (fh == -1) {
+            entity_value[i] =
+                (char *) malloc(strlen(entity_reference_name[i]) + strlen("<!--  is missing -->") + 1);
+            if (entity_value[i] == NULL) {
+               read_error(HERE, "Cannot allocate memory.");
+               free(buffer);
+               for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                  free(entity_value[ip]);
+               return 1;
+            }
+            sprintf(entity_value[i], "<!-- %s is missing -->", entity_reference_name[i]);
+         } else {
+            length = lseek(fh, 0, SEEK_END);
+            lseek(fh, 0, SEEK_SET);
+            if (length == 0) {
+               entity_value[i] = (char *) malloc(1);
+               if (entity_value[i] == NULL) {
+                  read_error(HERE, "Cannot allocate memory.");
+                  free(buffer);
+                  for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                     free(entity_value[ip]);
+                  return 1;
+               }
+               entity_value[i][0] = 0;
+            } else {
+               entity_value[i] = (char *) malloc(length);
+               if (entity_value[i] == NULL) {
+                  read_error(HERE, "Cannot allocate memory.");
+                  free(buffer);
+                  for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                     free(entity_value[ip]);
+                  return 1;
+               }
+
+               /* read complete file at once */
+               length = read(fh, entity_value[i], length);
+               entity_value[i][length - 1] = 0;
+               close(fh);
+
+               /* recursive parse */
+               if (mxml_parse_entity(&entity_value[i], filename, error, error_size) != 0) {
+                  mxml_free_tree(root);
+                  free(buffer);
+                  for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+                     free(entity_value[ip]);
+                  return 1;
+               }
+            }
+         }
+      }
+   }
+
+   /* count length of output string */
+   length = strlen(buffer);
+   for (i = 0; i < nentity; i++) {
+      p = buffer;
+      entity_value_length[i] = strlen(entity_value[i]);
+      entity_name_length[i] = strlen(entity_name[i]);
+      while (1) {
+         pv = strstr(p, entity_name[i]);
+         if (pv) {
+            length += entity_value_length[i] - entity_name_length[i];
+            p = pv + 1;
+         } else {
+            break;
+         }
+      }
+   }
+
+   /* re-allocate memory */
+   free(*buf);
+   *buf = (char *) malloc(length + 1);
+   if (*buf == NULL) {
+      read_error(HERE, "Cannot allocate memory.");
+      free(buffer);
+      for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+         free(entity_value[ip]);
+      return 1;
+   }
+
+   /* replace entities */
+   p = buffer;
+   pv = *buf;
+   do {
+      if (*p == '&') {
+         /* found entity */
+         for (j = 0; j < nentity; j++) {
+            if (strncmp(p, entity_name[j], entity_name_length[j]) == 0) {
+               for (k = 0; k < (int) entity_value_length[j]; k++)
+                  *pv++ = entity_value[j][k];
+               p += entity_name_length[j];
+               break;
+            }
+         }
+      }
+      *pv++ = *p++;
+   } while (*p);
+   *pv = 0;
+
+   free(buffer);
+   for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
+      free(entity_value[ip]);
+
+   mxml_free_tree(root);
+   return 0;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * parse a XML file and convert it into a tree of MXML_NODE's.
+ * Return NULL in case of an error, return error description
+ */
+PMXML_NODE mxml_parse_file(const char *file_name, char *error, int error_size)
+{
+   char *buf, line[1000];
+   int fh, length;
+   PMXML_NODE root;
+
+   if (error)
+      error[0] = 0;
+
+   fh = open(file_name, O_RDONLY | O_TEXT, 0644);
+
+   if (fh == -1) {
+      sprintf(line, "Unable to open file \"%s\": ", file_name);
+      strlcat(line, strerror(errno), sizeof(line));
+      strlcpy(error, line, error_size);
+      return NULL;
+   }
+
+   length = lseek(fh, 0, SEEK_END);
+   lseek(fh, 0, SEEK_SET);
+   buf = (char *)malloc(length+1);
+   if (buf == NULL) {
+      close(fh);
+      sprintf(line, "Cannot allocate buffer: ");
+      strlcat(line, strerror(errno), sizeof(line));
+      strlcpy(error, line, error_size);
+      return NULL;
+   }
+
+   /* read complete file at once */
+   length = read(fh, buf, length);
+   buf[length] = 0;
+   close(fh);
+
+   if (mxml_parse_entity(&buf, file_name, error, error_size) != 0) {
+      free(buf);
+      return NULL;
+   }
+
+   root = mxml_parse_buffer(buf, error, error_size);
+
+   free(buf);
+
+   return root;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * write complete subtree recursively into file opened with mxml_open_document()
+ */
+int mxml_write_subtree(MXML_WRITER *writer, PMXML_NODE tree, int indent)
+{
+   int i;
+
+   mxml_start_element1(writer, tree->name, indent);
+   for (i=0 ; i<tree->n_attributes ; i++)
+      if (!mxml_write_attribute(writer, tree->attribute_name+i*MXML_NAME_LENGTH, tree->attribute_value[i]))
+         return FALSE;
+   
+   if (tree->value)
+      if (!mxml_write_value(writer, tree->value))
+         return FALSE;
+
+   for (i=0 ; i<tree->n_children ; i++)
+      if (!mxml_write_subtree(writer, &tree->child[i], (tree->value == NULL) || i > 0))
+         return FALSE;
+
+   return mxml_end_element(writer);
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * write a complete XML tree to a file
+ */
+int mxml_write_tree(const char *file_name, PMXML_NODE tree)
+{
+   MXML_WRITER *writer;
+   int i;
+
+   assert(tree);
+   writer = mxml_open_file(file_name);
+   if (!writer)
+      return FALSE;
+
+   for (i=0 ; i<tree->n_children ; i++)
+     if (tree->child[i].node_type == ELEMENT_NODE) /* skip PI and comments */
+         if (!mxml_write_subtree(writer, &tree->child[i], TRUE))
+            return FALSE;
+
+   if (!mxml_close_file(writer))
+      return FALSE;
+
+   return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+
+PMXML_NODE mxml_clone_tree(PMXML_NODE tree)
+{
+   PMXML_NODE clone;
+   int i;
+
+   clone = (PMXML_NODE)calloc(sizeof(MXML_NODE), 1);
+
+   /* copy name, node_type, n_attributes and n_children */
+   memcpy(clone, tree, sizeof(MXML_NODE));
+
+   clone->value = NULL;
+   mxml_replace_node_value(clone, tree->value);
+
+   clone->attribute_name = NULL;
+   clone->attribute_value = NULL;
+   for (i=0 ; i<tree->n_attributes ; i++)
+      mxml_add_attribute(clone, tree->attribute_name+i*MXML_NAME_LENGTH, tree->attribute_value[i]);
+
+   clone->child = NULL;
+   clone->n_children = 0;
+   for (i=0 ; i<tree->n_children ; i++)
+      mxml_add_tree(clone, mxml_clone_tree(mxml_subnode(tree, i)));
+
+   return clone;
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * print XML tree for debugging
+ */
+void mxml_debug_tree(PMXML_NODE tree, int level)
+{
+   int i, j;
+
+   for (i=0 ; i<level ; i++)
+      printf("  ");
+   printf("Name: %s\n", tree->name);
+   for (i=0 ; i<level ; i++)
+      printf("  ");
+   printf("Valu: %s\n", tree->value);
+   for (i=0 ; i<level ; i++)
+      printf("  ");
+   printf("Type: %d\n", tree->node_type);
+
+   for (j=0 ; j<tree->n_attributes ; j++) {
+      for (i=0 ; i<level ; i++)
+         printf("  ");
+      printf("%s: %s\n", tree->attribute_name+j*MXML_NAME_LENGTH, 
+         tree->attribute_value[j]);
+   }
+
+   for (i=0 ; i<level ; i++)
+      printf("  ");
+   printf("Addr: %08zX\n", (size_t)tree);
+   for (i=0 ; i<level ; i++)
+      printf("  ");
+   printf("Prnt: %08zX\n", (size_t)tree->parent);
+   for (i=0 ; i<level ; i++)
+      printf("  ");
+   printf("NCld: %d\n", tree->n_children);
+
+   for (i=0 ; i<tree->n_children ; i++)
+      mxml_debug_tree(tree->child+i, level+1);
+
+   if (level == 0)
+      printf("\n");
+}
+
+/*------------------------------------------------------------------*/
+
+/**
+ * free memory of XML tree, must be called after any 
+ * mxml_create_root_node() or mxml_parse_file()
+ */
+void mxml_free_tree(PMXML_NODE tree)
+{
+   int i;
+
+   /* first free children recursively */
+   for (i=0 ; i<tree->n_children ; i++)
+      mxml_free_tree(&tree->child[i]);
+   if (tree->n_children)
+      free(tree->child);
+
+   /* now free dynamic data */
+   for (i=0 ; i<tree->n_attributes ; i++)
+      free(tree->attribute_value[i]);
+
+   if (tree->n_attributes) {
+      free(tree->attribute_name);
+      free(tree->attribute_value);
+   }
+   
+   if (tree->value)
+      free(tree->value);
+
+   /* if we are the root node, free it */
+   if (tree->parent == NULL)
+      free(tree);
+}
+
+/*------------------------------------------------------------------*/
+
+/*
+void mxml_test()
+{
+   char err[256];
+   PMXML_NODE tree, tree2, node;
+
+   tree = mxml_parse_file("c:\\tmp\\test.xml", err, sizeof(err));
+   tree2 = mxml_clone_tree(tree);
+
+   printf("Orig:\n");
+   mxml_debug_tree(tree, 0);
+
+   printf("\nClone:\n");
+   mxml_debug_tree(tree2, 0);
+
+   printf("\nCombined:\n");
+   node = mxml_find_node(tree2, "cddb"); 
+   mxml_add_tree(tree, node);
+   mxml_debug_tree(tree, 0);
+
+   mxml_free_tree(tree);
+}
+*/
+
+/*------------------------------------------------------------------*/
+ /**
+   mxml_basename deletes any prefix ending with the last slash '/' character
+   present in path. mxml_dirname deletes the filename portion, beginning with
+   the last slash '/' character to the end of path. Followings are examples
+   from these functions
+
+    path               dirname   basename
+    "/"                "/"       ""
+    "."                "."       "."
+    ""                 ""        ""
+    "/test.txt"        "/"       "test.txt"
+    "path/to/test.txt" "path/to" "test.txt"
+    "test.txt          "."       "test.txt"
+
+   Under Windows, '\\' and ':' are recognized ad separator too.
+ */
+
+void mxml_basename(char *path)
+{
+   char str[FILENAME_MAX];
+   char *p;
+   char *name;
+
+   if (path) {
+      strcpy(str, path);
+      p = str;
+      name = str;
+      while (1) {
+         if (*p == 0)
+            break;
+         if (*p == '/'
+#ifdef _MSC_VER
+             || *p == ':' || *p == '\\'
+#endif
+             )
+            name = p + 1;
+         p++;
+      }
+      strcpy(path, name);
+   }
+
+   return;
+}
+
+void mxml_dirname(char *path)
+{
+   char *p;
+#ifdef _MSC_VER
+   char *pv;
+#endif
+
+   if (!path || strlen(path) == 0)
+      return;
+
+   p = strrchr(path, '/');
+#ifdef _MSC_VER
+   pv = strrchr(path, ':');
+   if (pv > p)
+      p = pv;
+   pv = strrchr(path, '\\');
+   if (pv > p)
+      p = pv;
+#endif
+
+   if (p == 0)                  /* current directory */
+      strcpy(path, ".");
+   else if (p == path)          /* root directory */
+      sprintf(path, "%c", *p);
+   else
+      *p = 0;
+
+   return;
+}
+
+/*------------------------------------------------------------------*/
Index: /fact/drsdaq/DRS/mxml.h
===================================================================
--- /fact/drsdaq/DRS/mxml.h	(revision 9833)
+++ /fact/drsdaq/DRS/mxml.h	(revision 9833)
@@ -0,0 +1,137 @@
+/********************************************************************\
+
+   Name:         mxml.h
+   Created by:   Stefan Ritt
+
+   Contents:     Header file for mxml.c
+
+   $Id: mxml.h 62 2007-10-23 17:53:45Z sawada $
+
+\********************************************************************/
+
+/*------------------------------------------------------------------*/
+
+#ifndef _MXML_H_
+#define _MXML_H_
+
+#define MXML_NAME_LENGTH 64
+
+#define ELEMENT_NODE                  1
+#define TEXT_NODE                     2
+#define PROCESSING_INSTRUCTION_NODE   3
+#define COMMENT_NODE                  4
+#define DOCUMENT_NODE                 5
+
+#define INTERNAL_ENTITY               0
+#define EXTERNAL_ENTITY               1
+#define MXML_MAX_ENTITY             500
+
+#define MXML_MAX_CONDITION           10
+
+#ifdef _MSC_VER
+#define DIR_SEPARATOR              '\\'
+#else
+#define DIR_SEPARATOR               '/'
+#endif
+
+typedef struct {
+   int  fh;
+   char *buffer;
+   int  buffer_size;
+   int  buffer_len;
+   int  level;
+   int  element_is_open;
+   int  data_was_written;
+   char **stack;
+   int  translate;
+} MXML_WRITER;
+
+typedef struct mxml_struct *PMXML_NODE;
+
+typedef struct mxml_struct {
+   char       name[MXML_NAME_LENGTH];  // name of element    <[name]>[value]</[name]>
+   int        node_type;               // type of node XXX_NODE
+   char       *value;                  // value of element
+   int        n_attributes;            // list of attributes
+   char       *attribute_name;
+   char       **attribute_value;
+   int        line_number;             // line number for source file
+   PMXML_NODE parent;                  // pointer to parent element
+   int        n_children;              // list of children
+   PMXML_NODE child;
+} MXML_NODE;
+
+/*------------------------------------------------------------------*/
+
+/* make functions callable from a C++ program */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef EXPRT
+#if defined(EXPORT_DLL)
+#define EXPRT __declspec(dllexport)
+#else
+#define EXPRT
+#endif
+#endif
+
+void mxml_suppress_date(int suppress);
+MXML_WRITER *mxml_open_file(const char *file_name);
+MXML_WRITER *mxml_open_buffer(void); 
+int mxml_set_translate(MXML_WRITER *writer, int flag);
+int mxml_start_element(MXML_WRITER *writer, const char *name);
+int mxml_start_element_noindent(MXML_WRITER *writer, const char *name);
+int mxml_end_element(MXML_WRITER *writer); 
+int mxml_write_comment(MXML_WRITER *writer, const char *string);
+int mxml_write_element(MXML_WRITER *writer, const char *name, const char *value);
+int mxml_write_attribute(MXML_WRITER *writer, const char *name, const char *value);
+int mxml_write_value(MXML_WRITER *writer, const char *value);
+int mxml_write_empty_line(MXML_WRITER *writer);
+char *mxml_close_buffer(MXML_WRITER *writer);
+int mxml_close_file(MXML_WRITER *writer);
+
+int mxml_get_number_of_children(PMXML_NODE pnode);
+PMXML_NODE mxml_subnode(PMXML_NODE pnode, int idx);
+PMXML_NODE mxml_find_node(PMXML_NODE tree, const char *xml_path);
+int mxml_find_nodes(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist);
+char *mxml_get_name(PMXML_NODE pnode);
+char *mxml_get_value(PMXML_NODE pnode);
+char *mxml_get_attribute(PMXML_NODE pnode, const char *name);
+
+int mxml_add_attribute(PMXML_NODE pnode, const char *attrib_name, const char *attrib_value);
+PMXML_NODE mxml_add_special_node(PMXML_NODE parent, int node_type, const char *node_name, const char *value);
+PMXML_NODE mxml_add_special_node_at(PMXML_NODE parent, int node_type, const char *node_name, const char *value, int idx);
+PMXML_NODE mxml_add_node(PMXML_NODE parent, const char *node_name, const char *value);
+PMXML_NODE mxml_add_node_at(PMXML_NODE parent, const char *node_name, const char *value, int idx);
+
+PMXML_NODE mxml_clone_tree(PMXML_NODE tree);
+int mxml_add_tree(PMXML_NODE parent, PMXML_NODE tree);
+int mxml_add_tree_at(PMXML_NODE parent, PMXML_NODE tree, int idx);
+
+int mxml_replace_node_name(PMXML_NODE pnode, const char *new_name);
+int mxml_replace_node_value(PMXML_NODE pnode, const char *value);
+int mxml_replace_subvalue(PMXML_NODE pnode, const char *name, const char *value);
+int mxml_replace_attribute_name(PMXML_NODE pnode, const char *old_name, const char *new_name);
+int mxml_replace_attribute_value(PMXML_NODE pnode, const char *attrib_name, const char *attrib_value);
+
+int mxml_delete_node(PMXML_NODE pnode);
+int mxml_delete_attribute(PMXML_NODE, const char *attrib_name);
+
+PMXML_NODE mxml_create_root_node(void);
+PMXML_NODE mxml_parse_file(const char *file_name, char *error, int error_size);
+PMXML_NODE mxml_parse_buffer(const char *buffer, char *error, int error_size);
+int mxml_parse_entity(char **buf, const char* file_name, char *error, int error_size);
+int mxml_write_tree(const char *file_name, PMXML_NODE tree);
+void mxml_debug_tree(PMXML_NODE tree, int level);
+void mxml_free_tree(PMXML_NODE tree);
+
+void mxml_dirname(char* path);
+void mxml_basename(char *path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MXML_H_ */
+/*------------------------------------------------------------------*/
Index: /fact/drsdaq/DRS/strlcpy.c
===================================================================
--- /fact/drsdaq/DRS/strlcpy.c	(revision 9833)
+++ /fact/drsdaq/DRS/strlcpy.c	(revision 9833)
@@ -0,0 +1,82 @@
+/********************************************************************\
+
+   Name:         strlcpy.c
+   Created by:   Stefan Ritt
+
+   Contents:     Contains strlcpy and strlcat which are versions of
+                 strcpy and strcat, but which avoid buffer overflows
+
+   $Id: strlcpy.c 16 2005-10-07 13:05:38Z ritt $
+
+\********************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include "strlcpy.h"
+
+/*
+* Copy src to string dst of size siz.  At most siz-1 characters
+* will be copied.  Always NUL terminates (unless size == 0).
+* Returns strlen(src); if retval >= siz, truncation occurred.
+*/
+size_t strlcpy(char *dst, const char *src, size_t size)
+{
+   char *d = dst;
+   const char *s = src;
+   size_t n = size;
+
+   /* Copy as many bytes as will fit */
+   if (n != 0 && --n != 0) {
+      do {
+         if ((*d++ = *s++) == 0)
+            break;
+      } while (--n != 0);
+   }
+
+   /* Not enough room in dst, add NUL and traverse rest of src */
+   if (n == 0) {
+      if (size != 0)
+         *d = '\0';             /* NUL-terminate dst */
+      while (*s++);
+   }
+
+   return (s - src - 1);        /* count does not include NUL */
+}
+
+/*-------------------------------------------------------------------*/
+
+/*
+* Appends src to string dst of size siz (unlike strncat, siz is the
+* full size of dst, not space left).  At most siz-1 characters
+* will be copied.  Always NUL terminates (unless size <= strlen(dst)).
+* Returns strlen(src) + MIN(size, strlen(initial dst)).
+* If retval >= size, truncation occurred.
+*/
+size_t strlcat(char *dst, const char *src, size_t size)
+{
+   char *d = dst;
+   const char *s = src;
+   size_t n = size;
+   size_t dlen;
+
+   /* Find the end of dst and adjust bytes left but don't go past end */
+   while (n-- != 0 && *d != '\0')
+      d++;
+   dlen = d - dst;
+   n = size - dlen;
+
+   if (n == 0)
+      return (dlen + strlen(s));
+   while (*s != '\0') {
+      if (n != 1) {
+         *d++ = *s;
+         n--;
+      }
+      s++;
+   }
+   *d = '\0';
+
+   return (dlen + (s - src));   /* count does not include NUL */
+}
+
+/*-------------------------------------------------------------------*/
Index: /fact/drsdaq/DRS/strlcpy.h
===================================================================
--- /fact/drsdaq/DRS/strlcpy.h	(revision 9833)
+++ /fact/drsdaq/DRS/strlcpy.h	(revision 9833)
@@ -0,0 +1,34 @@
+/********************************************************************\
+
+   Name:         strlcpy.h
+   Created by:   Stefan Ritt
+
+   Contents:     Header file for strlcpy.c
+
+   $Id: strlcpy.h 62 2007-10-23 17:53:45Z sawada $
+
+\********************************************************************/
+
+#ifndef _STRLCPY_H_
+#define _STRLCPY_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef EXPRT
+#if defined(EXPORT_DLL)
+#define EXPRT __declspec(dllexport)
+#else
+#define EXPRT
+#endif
+#endif
+
+size_t EXPRT strlcpy(char *dst, const char *src, size_t size);
+size_t EXPRT strlcat(char *dst, const char *src, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_STRLCPY_H_ */
Index: /fact/drsdaq/History.txt
===================================================================
--- /fact/drsdaq/History.txt	(revision 9833)
+++ /fact/drsdaq/History.txt	(revision 9833)
@@ -0,0 +1,76 @@
+Modification history of drsdaq program
+--------------------------------------
+
+23/3/2009   Changed 'Events' entry in run header. It now gives the number of
+    	    events in the current file.
+24/3/2009   Copied into FACT subversion directory. Updated location of
+    	    configuration file and PixelMap.txt (in config/ in repository).
+	        Improved PrintMessage() and error outputs.
+26/3/2009   Added PixelMap translation to communication with HV control.
+30/3/2009   Added feedback gain. Checked into repository.
+1/4/2009    Changed many 'char *' to 'const char *' to honour compiler warning
+            on ihp-pc26 (newer version of g++ than on eth-vme02)
+2/4/2009    Introduced check for magic number in RawDataCTX.cc
+3/4/2009    Added frequency and scale factor transmission to socket protocol
+6/4/2009    Allowed reading of data via read command also while DAQ is active.
+22/4/2009   Included sizes of RunHeader, EventHeader and BoardStructure in
+    	    RunHeader and changed some data widths in RunHeader to U32 to
+	        increase compatibility with Magic raw data format. Unique run
+	        numbers are now generated using the file LastRunNumber.
+28/4/2009   Raw file writing uses now writev() and is changed entirely to
+    	    Linux system calls instead of C++ library functions. Introduced
+	        new magic number that indicates an error while writing. More
+	        extensive error checking for I/O functions. Requesting zero events
+	        will let run go until stopped manually. Feedback writes slow data.
+29/4/2009   DAQ writes run summary to slow data file.
+14/5/2009   Streamlined raw data format.
+18/5/2009   Included some version and size checking in RawDataCTX.cc.
+19/5/2009   Subversion revision number included in run header. Added human readable
+    	    date and time to slow data.
+28/5/2009   Replaced NCMCBoards by NBoards for clarity. Added line editing
+    	    capability with the readline library.
+29/5/2009   Number of lines in log file limited to configurable number.
+4/6/2009    Config file can contain array of numbers. Reading of config file now
+    	    safe for buffer overflow. RawDataCTX class take the possibility of
+	        varying event data size into account.
+12/6/2009   Trigger cells (determined using DRS class GetTriggerCell()) are
+    	    stored as array of integers in event data. RawDataCTX class pointer
+	        'Data' changed from short* to char*.
+16/6/2009   Data is not rotated by copying in memory, but by saving to disk in
+    	    correct order using writev() (10% gain in rate)
+18/6/2009   Run date is now calculated with a change to the next date on 13:00 UTC.
+9/7/2009    SlowData class is now independet of PrintMessage() method, thus more
+	    	universal
+14/7/2009   Fixed assignment of trigger type in event header (now 0  is hardware
+    	    trigger, 1 software trigger). Fixed pointer-arithmetic for trigger cell
+	    	writing if first board written to disk is not physically first board.	    
+28/7/2009   Added configuration parameters SlowDataPath and DefaultFrequency. All
+    	    configuration parameters are now mandatory: if one is not found, the
+	    	program terminates.
+5/8/2009    Streamlined DRS response calibration (removing unnecessary
+    	    initialisation steps)
+10/8/2009   Socket interface to hvcontrol only checks, if first reponse starts with
+    	    'OK', otherwise assumes command to hvcontrol failed.
+22/9/2009   The 'start' command will now start the DAQ without disk writing, but
+    	    with invocation of the feedback.
+23/9/2009   Fixed bug in RawDataCTX.cc: sequential event number in a file is only
+    	    the same as the event number in the run for the first file part
+16/12/2009  Removed automatic gain adaption in feedback (caused spikes in the correction)
+9/3/2010	Feedback now depended on DIM for communication with bias server.
+			Started migration to DRS4 (last tested revision as daqct3 for DRS2 is 161).
+			DRS class will not run anymore with DRS2 FPGA firmware.
+11/3/2010	Removed SlowData class.			
+12/3/2010	Removed local configuration and logging.
+30/4/2010	Command 'start' now starts a normal run as 'take', but data is written to
+			/dev/null, the run number is -1.
+4/5/2010	Removed socket server, added DIM command handler (thus cannot communicate
+			with ddd over socket anymore). Added DIM service containing full event.
+18/5/2010	Rate of event service adjustable with command 'update'.
+21/5/2010	Fix so that 'exit' command also works as DimCommand.
+20/7/2010	Replaced mutex with Lock()/Unlock() from Evidence class, introduced DIM
+			feedback state service, streamlined PrintMessage().
+27/7/2010	Removed feedback from drsdaq. Feedback is now a separate DIM server and
+			invoked through a DIM command. Last revision with feedback integrated into
+			drsdaq is 264.
+30/7/2010	Published some run-related information as DIM service.
+			
Index: /fact/drsdaq/Makefile
===================================================================
--- /fact/drsdaq/Makefile	(revision 9833)
+++ /fact/drsdaq/Makefile	(revision 9833)
@@ -0,0 +1,51 @@
+#
+#  Makefile for the DRS DAQ 
+#
+# Compiled and streamlined from original makefiles - O. Grimm, Nov 2008
+# Use the VMECTRL flag to switch between different VME controllers: -DCT_VME
+# for Concurrent Technologies, -DSTRUCK_VME for Struck
+
+VMECTRL = -DCT_VME
+
+CC  	= g++   	# Compiler to use
+
+SOURCES = DAQReadout.cc RawDataCTX.cc ../pixelmap/Pixel.cc ../pixelmap/PixelMap.cc DRS/DRS.cc DRS/mxml.c DRS/strlcpy.c drsdaq.cpp ../Evidence/Evidence.cc 
+OBJECTS = $(addsuffix .o, $(basename $(SOURCES)))
+INCDIRS   = -I. -IDRS -I../pixelmap -I../Evidence -I$(DIMDIR)/dim
+
+REVISION = $(shell svnversion -n)
+
+ifeq ($(VMECTRL),-DCT_VME)
+ VMELIB = -L./VME/atlas/lib -lvme_rcc -lcmem_rcc -lrcc_time_stamp
+ INCDIRS := $(INCDIRS) -I./VME/atlas/include/
+else
+ VMELIB = -L./VME/struck
+ INCDIRS := $(INCDIRS) -I./VME/struck/
+ OBJECTS := $(OBJECTS) ./VME/struck/sis3100.o ./VME/struck/sis3100_vme_calls.o
+endif
+
+CPPFLAGS = -DREVISION='"$(REVISION)"' -O3 -Wall $(VMECTRL)
+LIBS = -lstdc++ -lz -lpthread -lutil -lfl -lreadline -ltermcap $(VMELIB) $(DIMDIR)/linux/libdim.a
+
+drsdaq: $(OBJECTS)
+	$(CC) $(CPPFLAGS) -Wl,-rpath,VME/atlas/lib -o $@ $(OBJECTS) $(LIBS)
+
+clean:
+	@rm -f $(OBJECTS)
+	@rm -f *.d
+	@rm -f *~
+
+-include Dep.d
+	
+# Implicit rules
+
+%.o : %.c
+	$(CC) $(CPPFLAGS) $(INCDIRS) -c -o $@ $<
+%.o : %.cc
+	$(CC) $(CPPFLAGS) $(INCDIRS) -c -o $@ $<
+%.o : %.cpp
+	$(CC) $(CPPFLAGS) $(INCDIRS) -c -o $@ $< 
+%.d : 
+	@echo "Generating dependencies" $@
+	@$(CC) -MM $(SOURCES) $(INCDIRS) \
+	| sed 's/^\(.*\).o:/$@ \1.o:/' > $@
Index: /fact/drsdaq/RawDataCTX.cc
===================================================================
--- /fact/drsdaq/RawDataCTX.cc	(revision 9833)
+++ /fact/drsdaq/RawDataCTX.cc	(revision 9833)
@@ -0,0 +1,199 @@
+/********************************************************************\
+
+  RawDataCTX.cc
+
+  Class for raw data handling of the DRS 2-based DAQ
+
+  Oliver Grimm
+  
+\********************************************************************/
+
+#include "RawDataCTX.h"
+
+// *** Constructor
+RawDataCTX::RawDataCTX(bool BeSilent) {
+  RHeader = new RunHeader; 
+  EHeader = new EventHeader;
+  FileOpen = false;
+  Silent = BeSilent;
+}
+
+// *** Destructor
+RawDataCTX::~RawDataCTX() {
+  if(FileOpen) CloseDataFile();
+
+  delete RHeader;
+  delete EHeader;
+}
+
+// *** Open raw data file
+CTX_ErrCode RawDataCTX::OpenDataFile(char *Filename, FILE *fptr) {
+
+  // Close file if currently open    
+  if(FileOpen) CloseDataFile();
+
+  // Open file
+  if ((Rawfile = fopen(Filename, "r")) == NULL) {
+    if(!Silent) printf("Error: Could not open file: %s\n", Filename);
+    return CTX_FOPEN;
+  }
+
+  // Read run header
+  if (fread(RHeader, sizeof(RunHeader), 1, Rawfile) != 1) {
+    if(!Silent) printf("Error: Could not read run header\n");
+    fclose(Rawfile);
+    return CTX_RHEADER;  
+  }
+
+  // Check magic number of run header
+  if (RHeader->MagicNum!=MAGICNUM_OPEN && RHeader->MagicNum!=MAGICNUM_CLOSED && RHeader->MagicNum!=MAGICNUM_ERROR) {
+    if(!Silent) printf("Error: Magic number of run header incorrect\n");
+    fclose(Rawfile);
+    return CTX_RHEADER;  
+  }
+
+  // Check if version of this software is not older than raw data format
+  if (RHeader->DataFormat > DATA_FORMAT) {
+    if(!Silent) printf("Error: Data format too new, incompatible with this read-out routine\n");
+    fclose(Rawfile);
+    return CTX_VERSION;  
+  }  
+
+  // Check if allocated headers are long enough
+  if (RHeader->RunHeaderSize>sizeof(RunHeader) || RHeader->BoardStructureSize>sizeof(BoardStructure) || RHeader->EventHeaderSize>sizeof(EventHeader)) {
+    if(!Silent) printf("Error: Header size(s) too long (there must be a problem with the data version!)\n");
+    fclose(Rawfile);
+    return CTX_VERSION;  
+  }
+  
+  // Read board structures
+  BStruct = new BoardStructure [RHeader->NBoards];
+  for(unsigned int i=0; i<RHeader->NBoards; i++) {
+    if(fread(&BStruct[i], RHeader->BoardStructureSize, 1, Rawfile) != 1) {
+      if(!Silent) printf("Error: Could not read board structure of board number %d\n",i+1);
+      fclose(Rawfile);
+      delete[] BStruct;
+      return CTX_BSTRUCT;
+    }
+  }
+
+  // Initialize variables
+  Data = NULL;
+  FileOpen = true;
+  
+  // If requested, print run header (including board structures) to file 
+  if(fptr != NULL) {
+    fprintf(fptr, "Magic number          %x (%s)\n", RHeader->MagicNum, RHeader->MagicNum==MAGICNUM_CLOSED?"OK":(RHeader->MagicNum==MAGICNUM_OPEN?"File not closed":"Error"));
+    fprintf(fptr, "Data format:          %u\n", RHeader->DataFormat);
+    fprintf(fptr, "Software revision:    %d\n", RHeader->SoftwareRevision);         
+
+    fprintf(fptr, "Run header size:      %d\n", RHeader->RunHeaderSize);
+    fprintf(fptr, "Event header size:    %d\n", RHeader->EventHeaderSize);
+    fprintf(fptr, "Board structure size: %d\n", RHeader->BoardStructureSize);
+
+    fprintf(fptr, "Description:      %s\n", RHeader->Description);
+    fprintf(fptr, "Identification:   %u\n", RHeader->Identification);
+    fprintf(fptr, "Run type:         %u\n", RHeader->Type);
+    fprintf(fptr, "Run number:       %u\n", RHeader->RunNumber);
+    fprintf(fptr, "File number:      %u\n", RHeader->FileNumber);
+    
+    fprintf(fptr, "Events:           %u\n", RHeader->Events);
+    fprintf(fptr, "Boards:  	     %u\n", RHeader->NBoards);
+    fprintf(fptr, "Chips/board:      %u\n", RHeader->NChips);
+    fprintf(fptr, "Channels/chip:    %u\n", RHeader->NChannels);
+    fprintf(fptr, "Samples:          %u\n", RHeader->Samples);
+    fprintf(fptr, "Offset:           %u\n", RHeader->Offset);
+    fprintf(fptr, "Bytes/sample:     %u\n", RHeader->NBytes);
+
+    fprintf(fptr, "Start second:     %u - UTC %s", RHeader->StartSecond, asctime(gmtime((time_t *) &RHeader->StartSecond)));
+    fprintf(fptr, "  microsecond:    %u\n", RHeader->StartMicrosecond);
+    fprintf(fptr, "End second:       %u - UTC %s", RHeader->EndSecond, asctime(gmtime((time_t *) &RHeader->EndSecond)));
+    fprintf(fptr, "  microsecond:    %u\n", RHeader->EndMicrosecond);
+
+    for (unsigned int i=0; i<RHeader->NBoards; i++) {
+      fprintf(fptr, "*** Board %d ***\n", i);
+      fprintf(fptr, "Serial number:            %d\n", BStruct[i].SerialNo);
+      fprintf(fptr, "Sampling frequency:       %.3f GHz\n", BStruct[i].NomFreq);
+      fprintf(fptr, "Temperature:              %.3f deg C\n", BStruct[i].BoardTemp);  
+      fprintf(fptr, "Scale factor:             %.3f\n", BStruct[i].ScaleFactor);
+    }     
+  }
+
+  return CTX_OK;
+}
+
+// *** Close raw data file
+CTX_ErrCode RawDataCTX::CloseDataFile() {
+
+  if(!FileOpen) return CTX_NOTOPEN;
+
+  if (fclose(Rawfile) == EOF) {
+    if(!Silent) perror("Could not close file");
+    return CTX_FCLOSE;
+  }
+  
+  delete[] BStruct; 	delete[] Data;
+ 
+  FileOpen = false;
+  return CTX_OK;
+}
+
+// *** Read next event from file
+CTX_ErrCode RawDataCTX::ReadEvent(unsigned int EventNo, FILE *fptr) {
+    
+  if (!FileOpen) {
+    if(!Silent) printf("Error: No data file open.\n");
+    return CTX_NOTOPEN;
+  }
+  
+  // Move file pointer to desired event header (if zero read next event)
+  bool SEEK_OK = true;
+  unsigned int Count = 0;
+  
+  if (EventNo!=0 && fseek(Rawfile, RHeader->RunHeaderSize+RHeader->BoardStructureSize*RHeader->NBoards, SEEK_SET) != 0) SEEK_OK=false;
+  while(SEEK_OK) {
+    if (fread(EHeader, RHeader->EventHeaderSize, 1, Rawfile) != 1) {
+      if (feof(Rawfile)==0) {
+	if (!Silent) printf("Error: Could not read event header\n");
+	return CTX_EHEADER;
+      }
+      else return CTX_EOF;  
+    }
+    else {
+      if ((++Count)==EventNo || EventNo==0) break;
+      if (fseek(Rawfile, EHeader->EventSize, SEEK_CUR) != 0) SEEK_OK = false;
+    }
+  }
+  if(!SEEK_OK) {
+    if(!Silent) printf("Error: Could not move to requested event\n");
+    return CTX_SEEK;
+  }
+
+  // Allocate memory for event data (enlarge if necessary)
+  if(Data==NULL || (sizeof(Data) < EHeader->EventSize)) {
+    delete[] Data;
+    Data = new char[EHeader->EventSize];
+  }
+
+  // Read event data
+  if(fread(Data, 1, EHeader->EventSize, Rawfile) != EHeader->EventSize) {
+    if(!Silent) printf("Error: Could not read (all) event data\n");
+    return CTX_DATA;    
+  }
+
+  // If requested, print event header to file 
+  if(fptr != NULL) {
+    fprintf(fptr, "Event number:    %u\n",        EHeader->EventNumber);
+    fprintf(fptr, "Time [sec]:      %u - UTC %s", EHeader->Second, asctime(gmtime((time_t *) &EHeader->Second)));
+    fprintf(fptr, "Time [usec]:     %u\n",        EHeader->Microsecond);
+    fprintf(fptr, "Trigger type:    0x%0X\n",     EHeader->TriggerType);
+    fprintf(fptr, "Size [byte]:     %u\n",        EHeader->EventSize);
+  }
+  
+  return CTX_OK;
+}
+
+// *** Check if file currently open
+bool RawDataCTX::IsFileOpen() {
+  return FileOpen;
+}
Index: /fact/drsdaq/RawDataCTX.h
===================================================================
--- /fact/drsdaq/RawDataCTX.h	(revision 9833)
+++ /fact/drsdaq/RawDataCTX.h	(revision 9833)
@@ -0,0 +1,115 @@
+/* Data organisation on disk:
+
+                                  Board 1      Board 2      ...     Board 1      Board 2      ...
+   RH  BS1 BS2 ... EH TC1 TC2 ... C1 C2 C3 ... C1 C2 C3 ... ...  EH C1 C2 C3 ... C1 C2 C3 ... ...
+                   --------------------------------  --------------------------------
+                   Event 1                           Event 2
+
+  RH Run header   BSx Board structures   EV Event header   TCx Trigger cell of chip x (int)
+  Cx Channel (0-19 for 2 chips), Channel data are written as shorts, length of event data is in event header
+
+  Structures are defined using #pragma pack (1) to not include any padding. Note that
+  using the gcc attribute __attribute__((__packed__)) is incompatible with root.
+
+  The convention for the header structure is that exisitng structure entries
+  should never be deleted. New items may only be added at the end.
+*/
+
+#ifndef RAWDATACTX_H_SEEN
+#define RAWDATACTX_H_SEEN
+
+#include <stdio.h>
+#include <time.h>
+
+#define DATA_FORMAT 1
+#define IDENTIFICATION 1    // Raw data file identification
+
+typedef char I8;
+typedef int I32;
+typedef unsigned int U32;
+typedef float F32;
+
+#define MAGICNUM_OPEN 0xe0e1    // Magic number for run header while file open
+#define MAGICNUM_CLOSED 0xe0e0  //    ... and when file is closed
+#define MAGICNUM_ERROR 0xe0e2   //    ... and when an error occurred
+
+// Error codes
+enum CTX_ErrCode {CTX_OK, CTX_FOPEN, CTX_FCLOSE, CTX_NOTOPEN, CTX_RHEADER,
+    	    	  CTX_BSTRUCT, CTX_EHEADER, CTX_DATA, CTX_SEEK, CTX_EOF, CTX_VERSION};
+
+
+#pragma pack (1)  // Switch padding off
+
+// Run header
+typedef struct {
+  U32 MagicNum;
+  U32 DataFormat;       // Increasing whenever format changes
+
+  U32 RunHeaderSize;
+  U32 EventHeaderSize;
+  U32 BoardStructureSize;
+
+  I32 SoftwareRevision;	// Subversion revision number (negative for modified working copy)
+
+  U32 Identification;
+  U32 Type;          	// Run type: 0=data, 1=pedestal, 3=test
+  U32 RunNumber;
+  U32 FileNumber;
+  I8  Description[100];
+       
+  U32 NBoards;	    	// Number of used mezzanine boards
+  U32 NChips;		// Number of DRS chips per board
+  U32 NChannels;	// Number of channels per chip
+
+  U32 Samples;          // Number of samples
+  U32 Offset;           // Offset from trigger
+
+  U32 Events;           // Number of events in the file
+  U32 NBytes;	    	// Bytes per sample
+  
+  U32 StartSecond;  	// Opening and closing time of the file
+  U32 StartMicrosecond;
+  U32 EndSecond;
+  U32 EndMicrosecond;
+} RunHeader;
+
+// Board structure
+typedef struct {
+  I32 SerialNo;     // Board serial number
+  F32 NomFreq;	    // Nominal sampling frequency [GHz]
+  F32 BoardTemp;    // Board temperature [deg C]
+  F32 ScaleFactor;  // Factor for conversion to mV
+} BoardStructure;
+
+// Event header
+typedef struct {
+  U32 EventNumber;
+  U32 Second;          // Event time stamp (result of gettimeofday())
+  U32 Microsecond;
+  U32 TriggerType;
+  U32 EventSize; 	// Size of following data in bytes
+} EventHeader;
+
+#pragma pack ()     // Set default padding
+
+// Class definition
+class RawDataCTX {
+    FILE *Rawfile;
+    bool FileOpen;
+    bool Silent;    	// No textual output if true
+    
+  public:
+    RunHeader   *RHeader; 
+    EventHeader *EHeader;
+    BoardStructure *BStruct;
+    char *Data; 
+    
+    RawDataCTX(bool = false);
+    ~RawDataCTX();
+    
+    CTX_ErrCode OpenDataFile(char*, FILE* = NULL);
+    CTX_ErrCode CloseDataFile();
+    CTX_ErrCode ReadEvent(unsigned int = 0, FILE* = NULL);
+    bool IsFileOpen();
+};
+#endif
Index: /fact/drsdaq/VME/README.txt
===================================================================
--- /fact/drsdaq/VME/README.txt	(revision 9833)
+++ /fact/drsdaq/VME/README.txt	(revision 9833)
@@ -0,0 +1,6 @@
+To recover Struck VME libraries, unpack file struck.tar.gz into this direcory.
+
+Packing the Struck libraries was necessary to commit it to the 
+subversion repository.
+
+Oliver Grimm, 30/3/2009
Index: /fact/drsdaq/VME/atlas/drivers_tdaq
===================================================================
--- /fact/drsdaq/VME/atlas/drivers_tdaq	(revision 9833)
+++ /fact/drsdaq/VME/atlas/drivers_tdaq	(revision 9833)
@@ -0,0 +1,278 @@
+#!/bin/sh
+#
+# drivers_tdaq:       Starts TDAQ related drivers
+#
+# Version:      @(#) /etc/rc.d/init.d/drivers_tdaq 1.1
+#
+# chkconfig: 5 95 5
+# description: Starts and stops tdaq drivers at boot time and shutdown.
+#
+# hide: true
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+export DRIVER_PATH=/usr/atlas/driver
+export LD_LIBRARY_PATH=/usr/atlas/lib
+export BINARY_PATH=/usr/atlas/bin
+export VMETAB_PATH=/usr/atlas/driver
+
+# See how we were called.
+case "$1" in
+  start)
+        echo "Starting CMEM_RCC driver "
+        # load the module
+        /sbin/insmod -f $DRIVER_PATH/cmem_rcc-`uname -r`.ko
+        # remove old device node
+        rm -f /dev/cmem_rcc
+        # get major number
+        major=`awk "\\$2==\"cmem_rcc\" {print \\$1}" /proc/devices`
+        echo major number is $major
+        # make device node
+        mknod /dev/cmem_rcc c $major 0
+        #give permissions
+        chmod 666 /dev/cmem_rcc
+        echo
+	
+        echo "Starting IO_RCC driver "
+        # load the module
+        /sbin/insmod -f $DRIVER_PATH/io_rcc-`uname -r`.ko
+        # remove old device node
+        rm -f /dev/io_rcc
+        # get major number
+        major=`awk "\\$2==\"io_rcc\" {print \\$1}" /proc/devices`
+        echo major number is $major
+        # make device node
+        mknod /dev/io_rcc c $major 0
+        # give permissions
+        chmod 666 /dev/io_rcc
+ 	echo
+
+	export NROBINS=`/sbin/lspci -n | grep -c "10dc:0144"`
+	echo $NROBINS Robin cards found
+	if [ $NROBINS -gt 0 ]; then
+          echo "Starting ROBIN driver "
+          # load the module
+          /sbin/insmod -f $DRIVER_PATH/robin-`uname -r`.ko
+          # remove old device node
+          rm -f /dev/robin
+          # get major number
+          major=`awk "\\$2==\"robin\" {print \\$1}" /proc/devices`
+          echo major number is $major
+          # make device node
+          mknod /dev/robin c $major 0
+          # give permissions
+          chmod 666 /dev/robin
+   	  if [ $NROBINS -gt 0 ]; then
+            $BINARY_PATH/robinconfig 0 5
+	  fi  
+   	  if [ $NROBINS -gt 1 ]; then
+            $BINARY_PATH/robinconfig 1 5
+	  fi
+	  if [ $NROBINS -gt 2 ]; then
+            $BINARY_PATH/robinconfig 2 5
+	  fi
+	  if [ $NROBINS -gt 3 ]; then
+            $BINARY_PATH/robinconfig 3 5
+	  fi 
+	  if [ $NROBINS -gt 4 ]; then
+	    echo configuring robin 4 
+            $BINARY_PATH/robinconfig 4 5
+	  fi 
+	  echo
+        fi 
+
+        /sbin/lspci -n | grep "10e3:0000"
+	if [ $? = 0 ]; then
+          echo "Starting VME_RCC driver "
+          # load the module
+          /sbin/insmod -f $DRIVER_PATH/vme_rcc-`uname -r`.ko
+          # remove old device node
+          rm -f /dev/vme_rcc
+          # get major number
+          major=`awk "\\$2==\"vme_rcc\" {print \\$1}" /proc/devices`
+          echo major number is $major
+          # make device node
+          mknod /dev/vme_rcc c $major 0
+          # give permissions
+          chmod 666 /dev/vme_rcc
+          # Initialize the Universe chip
+          $BINARY_PATH/vmeconfig -a $VMETAB_PATH/vmetab
+          echo
+	fi
+ 
+        /sbin/lspci -n | grep "10dc:001b"
+	if [ $? = 0 ]; then
+          echo "Starting QUEST driver "
+          # load the module
+          /sbin/insmod -f $DRIVER_PATH/quest-`uname -r`.ko
+          # remove old device node
+          rm -f /dev/quest
+          # get major number
+          major=`awk "\\$2==\"quest\" {print \\$1}" /proc/devices`
+          echo major number is $major
+          # make device node
+          mknod /dev/quest c $major 0
+          # give permissions
+          chmod 666 /dev/quest
+          echo
+	fi
+	
+        /sbin/lspci -n | grep "10dc:0017"
+	if [ $? = 0 ]; then
+	  echo "Starting SOLAR driver "
+          # load the module
+          /sbin/insmod -f $DRIVER_PATH/solar-`uname -r`.ko
+          # remove old device node
+          rm -f /dev/solar
+          # get major number
+          major=`awk "\\$2==\"solar\" {print \\$1}" /proc/devices`
+          echo major number is $major
+          # make device node
+          mknod /dev/solar c $major 0
+          # give permissions
+          chmod 666 /dev/solar
+          echo
+	fi
+
+        /sbin/lspci -n | grep "10dc:0014"
+	if [ $? = 0 ]; then
+          echo "Starting FILAR driver "
+          # load the module
+          /sbin/insmod -f $DRIVER_PATH/filar-`uname -r`.ko
+          # remove old device node
+          rm -f /dev/filar
+          # get major number
+          major=`awk "\\$2==\"filar\" {print \\$1}" /proc/devices`
+          echo major number is $major
+          # make device node
+          mknod /dev/filar c $major 0
+          # give permissions
+          chmod 666 /dev/filar
+          echo
+	fi
+        ;;
+
+  stop)       
+        echo "Shutting down CMEM_RCC driver "
+        /sbin/rmmod cmem_rcc
+
+        echo "Shutting down IO_RCC driver "
+        /sbin/rmmod io_rcc
+
+        /sbin/lsmod | grep robin
+	if [ $? = 0 ]; then
+          echo "Shutting down ROBIN driver "
+          /sbin/rmmod robin
+	fi
+
+        /sbin/lsmod | grep vme_rcc
+	if [ $? = 0 ]; then
+          echo "Shutting down VME_RCC driver "
+          /sbin/rmmod vme_rcc
+	fi
+
+        /sbin/lsmod | grep filar
+	if [ $? = 0 ]; then
+          echo "Shutting down FILAR driver "
+          /sbin/rmmod filar
+	fi
+	
+	/sbin/lsmod | grep quest
+	if [ $? = 0 ]; then
+          echo "Shutting down QUEST driver "
+          /sbin/rmmod quest
+	fi
+	
+	/sbin/lsmod | grep solar
+	if [ $? = 0 ]; then
+          echo "Shutting down SOLAR driver "
+          /sbin/rmmod solar
+	fi
+	
+	/sbin/lsmod | grep robintty
+	if [ $? = 0 ]; then
+          echo "Shutting down robin TTY driver "
+          /sbin/rmmod robintty
+	fi	
+        ;;
+
+  start_robin_tty)
+        /sbin/lsmod | grep robin-`uname -r`
+	if [ $? = 0 ]; then
+          echo "Starting robin TTY driver "
+          # load the module
+          /sbin/insmod -f $DRIVER_PATH/robintty-`uname -r`.ko 
+          # remove old device node
+          rm -f /dev/robintty_*
+          # get major number
+          major=`awk "\\$2==\"tty_robin\" {print \\$1}" /proc/devices`
+          echo major number is $major
+          # make device node
+          mknod /dev/robin_tty0 c $major 0
+          mknod /dev/robin_tty1 c $major 1
+          mknod /dev/robin_tty2 c $major 2
+          mknod /dev/robin_tty3 c $major 3
+          mknod /dev/robin_tty4 c $major 4
+          mknod /dev/robin_tty5 c $major 5
+          # give permissions
+          chmod 666 /dev/robin_tty0
+          chmod 666 /dev/robin_tty1
+          chmod 666 /dev/robin_tty2
+          chmod 666 /dev/robin_tty3
+          chmod 666 /dev/robin_tty4
+          chmod 666 /dev/robin_tty5
+          echo
+	fi  
+	;;
+
+  stop_robin_tty)
+        echo "Shutting down robin TTY driver "
+        /sbin/rmmod robintty-`uname -r`
+        echo
+	;;
+
+  status)       
+	echo "Status of the cmem_rcc driver"
+	more /proc/cmem_rcc
+
+	echo "Status of the io_rcc driver"
+	more /proc/io_rcc
+
+        /sbin/lsmod | grep robin-`uname -r`
+	if [ $? = 0 ]; then
+          echo "Status of the ROBIN driver "
+          more /proc/robin
+	fi
+
+        /sbin/lsmod | grep vme_rcc-`uname -r`
+	if [ $? = 0 ]; then
+          echo "Status of the VME_RCC driver "
+          more /proc/vme_rcc
+	fi
+
+        /sbin/lsmod | grep filar-`uname -r`
+	if [ $? = 0 ]; then
+          echo "Status of the FILAR driver "
+          more /proc/filar
+	fi
+	
+	/sbin/lsmod | grep quest-`uname -r`
+	if [ $? = 0 ]; then
+          echo "Status of the QUEST driver "
+          more /proc/quest
+	fi
+	
+	/sbin/lsmod | grep solar-`uname -r`
+	if [ $? = 0 ]; then
+          echo "Status of the SOLAR driver "
+          /more /proc/solar
+	fi	
+        ;; 
+  *)
+        echo "*** Usage: drivers_tdaq {start|stop|status|start_robin_tty|stop_robin_tty}"
+        exit 1
+
+esac
+
+exit 0
Index: /fact/drsdaq/VME/atlas/include/DFDebug/DFDebug.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/DFDebug/DFDebug.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/DFDebug/DFDebug.h	(revision 9833)
@@ -0,0 +1,97 @@
+/********************************************************/
+/* file: DFDebug.h (originally ROSDebug.h)		*/
+/* description: Nice macros for debugging purposesq	*/
+/* maintainer: Markus Joos, CERN/PH-ESS			*/
+/********************************************************/
+
+#include <iostream>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pthread.h>
+#include "DFDebug/GlobalDebugSettings.h"
+
+#ifndef DFDEBUG_H
+#define DFDEBUG_H
+
+  #if (DEBUG_LEVEL>0)
+  #define DEBUG_TEXT(my_package, level, text)\
+    {\
+      int oldState;\
+      pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);\
+      DF::GlobalDebugSettings::lock();\
+      pthread_t my_tid;\
+      my_tid = pthread_self();\
+      if ((my_package == DF::GlobalDebugSettings::packageId()) || (DF::GlobalDebugSettings::packageId() == 0))\
+        if (DF::GlobalDebugSettings::traceLevel() >= level)\
+          std::cout << std::dec << "Debug(" << my_package << "," << my_tid << "): " << text << std::endl;\
+      DF::GlobalDebugSettings::unlock();\
+      pthread_setcancelstate(oldState, 0);\
+    }
+  #else
+    #define DEBUG_TEXT(my_package, level, text)
+  #endif
+
+  #define OUT_TEXT(my_package, text)\
+    {\
+      int oldState;\
+      pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);\
+      std::cout << "Printout(" << my_package << "): " << text << std::endl;\
+      pthread_setcancelstate(oldState, 0);\
+    }
+
+  #define ERR_TEXT(my_package, text)\
+    {\
+      int oldState;\
+      pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);\
+      std::cerr << "Error(" << my_package << "): " << text << std::endl;\
+      pthread_setcancelstate(oldState, 0);\
+    }
+
+  #define HEX(n) std::hex << n << std::dec 
+
+  // Definition of some package identifiers
+  // Naming convention:
+  // DFDB_<A>_<B> with:
+  // A = (abbreviated) package name without underscores
+  // B = Suidentifier within package
+  //
+  // NOTE:
+  // IF YOU ARE ADDING A NEW PACKAGE HERE DO NOT FORGET TO ALSO ADD
+  // IT TO THE DFDEBUG_MENU TEST PROGRAM IN ../SRC/TEST
+  //
+  
+  enum
+  {
+    DFDB_ROSFM = 1,          //ROSFragmentManagement
+    DFDB_ROSEF,              //ROSEventFragment
+    DFDB_ROSSWROBIN,         //ROSSWRobin
+    DFDB_ROSFILAR,           //ROSfilar
+    DFDB_ROSMEMPOOL, 	     //ROSMemoryPool
+    DFDB_ROSEIM,             //ROSEventInputManager
+    DFDB_ROSIO,              //ROSIO
+    DFDB_ROSTG,              //Trigger generator in ROSIO
+    DFDB_SLINK,              //ROSslink
+    DFDB_ROSSOLAR,           //ROSsolar
+    DFDB_ROSQUEST,           //ROSsolar (QUEST)
+    DFDB_QUEUE,              //Queue debugging (ROSSWRobin, ROSIO, ROSCore)
+    DFDB_ROSCORE,            //ROSCore
+    DFDB_ROSROBIN,           //ROSRobin
+    DFDB_DFDB,               //DFDebug
+    DFDB_CMEMRCC = 100,      //cmem_rcc
+    DFDB_IORCC,              //io_rcc
+    DFDB_VMERCC,             //vme_rcc
+    DFDB_RCCTS,              //rcc_time_stamp
+    DFDB_RCDEXAMPLE = 300,   //RCDExample
+    DFDB_RCDBITSTRING,       //RCDBitString
+    DFDB_RCDMENU,            //RCDMenu
+    DFDB_RCDMODULE,          //RCDModule
+    DFDB_RCDTTC,             //RCDTtc
+    DFDB_RCDVME,             //RCDVme
+    DFDB_RCDLTP,             //RCDLTPModule and RCDLTPConfiguration
+    DFDB_RCDRODBUSY,         //rcc_rodbusy and RODBusyModule
+    DFDB_RCDTTCVIMODULE,     //RCDTtcviModule
+    DFDB_RF2TTC,             //RF2TTC and RFRX modules
+    DFDB_TTCVI = 400         //Ttcvi
+  };
+
+#endif //DFDEBUG_H
Index: /fact/drsdaq/VME/atlas/include/DFDebug/GlobalDebugSettings.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/DFDebug/GlobalDebugSettings.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/DFDebug/GlobalDebugSettings.h	(revision 9833)
@@ -0,0 +1,66 @@
+// -*- c++ -*-
+/************************************************/
+/* file: GlobalDebugSettings.h			*/
+/* description: Class GlobalDebugSettings	*/
+/* maintainer: Markus Joos, CERN/PH-ESS		*/
+/************************************************/
+
+#ifndef DFGLOBALDEBUGSETTINGS_H
+#define DFGLOBALDEBUGSETTINGS_H
+
+#include <pthread.h>
+
+namespace DF 
+{
+  class GlobalDebugSettings 
+  {
+    public:
+      static unsigned int traceLevel();
+      static unsigned int packageId();
+      static void lock();
+      static void unlock();
+      static void setup(unsigned int traceLevel,unsigned int packageId = 0); 
+    private:
+      static unsigned int s_traceLevel;
+      static unsigned int s_packageId;
+      static int s_mutexLocked;
+      static pthread_mutex_t debug_mutex;
+      static pthread_t s_mutexOwner;
+  };
+  
+  inline unsigned int GlobalDebugSettings::traceLevel() 
+  {
+    return(s_traceLevel);
+  }
+
+  inline unsigned int GlobalDebugSettings::packageId() 
+  {
+    return(s_packageId);
+  }
+
+  inline void GlobalDebugSettings::lock() 
+  {
+    if ((s_mutexLocked == 0) || (pthread_equal(s_mutexOwner, pthread_self()) == 0)) 
+    {
+       pthread_mutex_lock(&debug_mutex);
+       s_mutexOwner = pthread_self();
+    }
+    s_mutexLocked++;
+  }      
+  
+  inline void GlobalDebugSettings::unlock() 
+  {
+    s_mutexLocked--;
+    if (s_mutexLocked == 0) 
+      pthread_mutex_unlock(&debug_mutex);
+  }
+  
+  inline void GlobalDebugSettings::setup(unsigned int traceLevel, unsigned int packageId) 
+  {
+    s_traceLevel = traceLevel;
+    s_packageId = packageId;
+  }
+
+} // end of namespace DF
+#endif //DFGLOBALDEBUGSETTINGS_H
+
Index: /fact/drsdaq/VME/atlas/include/ROSGetInput/get_input.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/ROSGetInput/get_input.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/ROSGetInput/get_input.h	(revision 9833)
@@ -0,0 +1,34 @@
+/********************************************************/
+/*  file : get_input.h					*/
+/*							*/
+/*  prototypes for get_input functions			*/
+/*							*/
+/*  Maintained by: Markus Joos, CERN PH/ESS		*/
+/********************************************************/
+
+#ifndef _GET_INPUT_H
+#define _GET_INPUT_H
+
+#ifdef __cplusplus
+extern "C" 
+{
+#endif
+
+  long getdec(void);
+  long getdecd(long defa);
+  unsigned long gethex(void);
+  unsigned long gethexd(unsigned long defa);
+  char getfstchar(void);
+  void getstrd(char* a, char* defa);
+  float getfloatd(float defa);
+
+  int ReadCard_int (char* filename, char* tag, int nr, int* i);
+  int ReadCard_uint(char* filename, char* tag, int nr, unsigned int* u);
+  int ReadCard_str (char* filename, char* tag, int nr, char* c);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
Index: /fact/drsdaq/VME/atlas/include/cmem_rcc/cmem_rcc.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/cmem_rcc/cmem_rcc.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/cmem_rcc/cmem_rcc.h	(revision 9833)
@@ -0,0 +1,61 @@
+/************************************************************************/
+/*									*/
+/*  This is the common header file for the CMEM_RCC 			*/
+/*  library & applications						*/
+/*									*/
+/*  12. Dec. 01  MAJO  created						*/
+/*									*/
+/*******C 2001 - The software with that certain something****************/
+
+#ifndef _CMEM_RCC_H
+#define _CMEM_RCC_H
+
+#include <sys/types.h>
+#include "cmem_rcc_common.h"
+
+//Error strings
+#define CMEM_RCC_SUCCESS_STR      "No error"
+#define CMEM_RCC_ERROR_FAIL_STR   "Failed to install the error library"
+#define CMEM_RCC_FILE_STR         "Failed to open /dev/cmem_rcc"
+#define CMEM_RCC_NOTOPEN_STR      "Library has not yet been opened"
+#define CMEM_RCC_IOCTL_STR        "Error from call to ioctl function"
+#define CMEM_RCC_MMAP_STR         "Error from call to mmap function"
+#define CMEM_RCC_MUNMAP_STR       "Error from call to munmap function"
+#define CMEM_RCC_NO_CODE_STR      "Unknown error"
+#define CMEM_RCC_OVERFLOW_STR     "All descriptors are in use"
+#define CMEM_RCC_TOOBIG_STR       "Size is too big"
+#define CMEM_RCC_ILLHAND_STR      "Invalid handle"
+#define CMEM_RCC_GETP_STR         "Error from call to CMEM_SegmentGet"
+#define CMEM_RCC_NOSIZE_STR       "The <size> paremeter is zero"
+#define CMEM_RCC_CFU_STR          "Error from the driver in call to copy_from_user"
+#define CMEM_RCC_GFP_STR          "Error from the driver in call to __get_free_pages"
+#define CMEM_RCC_BPA_STR          "Error from the driver in call to BPA function"
+#define CMEM_RCC_CTU_STR          "Error from the driver in call to copy_To_user"
+#define CMEM_RCC_KMALLOC_STR      "Error from the driver in call to kmalloc"
+/************/
+/*Prototypes*/
+/************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+CMEM_Error_code_t CMEM_Open(void);
+CMEM_Error_code_t CMEM_Close(void);
+CMEM_Error_code_t CMEM_SegmentAllocate(u_int size, char *name, int *segment_identifier);
+CMEM_Error_code_t CMEM_SegmentFree(int segment_identifier);
+CMEM_Error_code_t CMEM_SegmentSize(int segment_identifier, u_int *actual_size);
+CMEM_Error_code_t CMEM_SegmentLock(int segment_identifier);
+CMEM_Error_code_t CMEM_SegmentUnlock(int segment_identifier);
+CMEM_Error_code_t CMEM_SegmentPhysicalAddress(int segment_identifier, u_long *physical_address);
+CMEM_Error_code_t CMEM_SegmentVirtualAddress(int segment_identifier, u_long *virtual_address);
+CMEM_Error_code_t CMEM_Dump(void);
+CMEM_Error_code_t CMEM_err_get(err_pack err, err_str pid, err_str code);
+CMEM_Error_code_t CMEM_BPASegmentAllocate(u_int size, char *name, int *segment_identifier);
+CMEM_Error_code_t CMEM_BPASegmentFree(int segment_identifier);
+CMEM_Error_code_t CMEM_SegmentGet(int segment_identifier, cmem_rcc_t *params);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /fact/drsdaq/VME/atlas/include/cmem_rcc/cmem_rcc_common.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/cmem_rcc/cmem_rcc_common.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/cmem_rcc/cmem_rcc_common.h	(revision 9833)
@@ -0,0 +1,63 @@
+/************************************************************************/
+/*									*/
+/*  This is the common header file for the CMEM_RCC 			*/
+/*  driver, library & applications					*/
+/*									*/
+/*  12. Dec. 01  MAJO  created						*/
+/*									*/
+/*******C 2005 - The software with that certain something****************/
+
+#ifndef _CMEM_RCC_COMMON_H
+#define _CMEM_RCC_COMMON_H
+
+#define CMEM_MAX_NAME    40
+#define TYPE_GFP         1
+#define TYPE_BPA         2
+
+#ifdef __KERNEL__
+  #include <linux/types.h>
+  #define P_ID_CMEM_RCC 7   // Needs to be re-defined here since we do not want to include rcc_error.h at this level
+#else
+  #include "rcc_error/rcc_error.h"
+  #include <sys/types.h>
+#endif
+
+// Error codes
+enum
+{
+  CMEM_RCC_SUCCESS = 0,
+  CMEM_RCC_ERROR_FAIL = (P_ID_CMEM_RCC << 8) + 1,
+  CMEM_RCC_FILE,
+  CMEM_RCC_NOTOPEN,
+  CMEM_RCC_IOCTL,
+  CMEM_RCC_MMAP,
+  CMEM_RCC_MUNMAP,
+  CMEM_RCC_OVERFLOW,
+  CMEM_RCC_TOOBIG,
+  CMEM_RCC_ILLHAND,
+  CMEM_RCC_NOSIZE,
+  CMEM_RCC_GETP,
+  CMEM_RCC_CFU,
+  CMEM_RCC_GFP,
+  CMEM_RCC_BPA,
+  CMEM_RCC_CTU,
+  CMEM_RCC_KMALLOC,
+  CMEM_RCC_NO_CODE
+};
+
+typedef struct
+{
+  u_long paddr;
+  u_long uaddr;
+  u_long kaddr;
+  u_int size;
+  u_int order;
+  u_int locked;
+  u_int type;
+  u_int handle;
+  char name[CMEM_MAX_NAME];
+} cmem_rcc_t;
+
+typedef u_int CMEM_Error_code_t;
+
+#endif
Index: /fact/drsdaq/VME/atlas/include/cmem_rcc/cmem_rcc_drv.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/cmem_rcc/cmem_rcc_drv.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/cmem_rcc/cmem_rcc_drv.h	(revision 9833)
@@ -0,0 +1,87 @@
+/************************************************************************/
+/*									*/
+/*  This is the common header file for the CMEM_RCC driver & library	*/
+/*									*/
+/*  12. Dec. 01  MAJO  created						*/
+/*									*/
+/*******C 2005 - The software with that certain something****************/
+
+#ifndef _CMEM_RCC_IOCTL_H
+#define _CMEM_RCC_IOCTL_H
+
+#include "cmem_rcc_common.h"
+
+// Constants
+#define MAX_BUFFS          1000    // Max. number of buffers for all processes
+#define TEXT_SIZE          3000    // For ioctl(CMEM_RCC_DUMP)
+#define MAX_PROC_TEXT_SIZE 0x10000 //The output of "more /proc/cmem_rcc" must not generate more characters than that
+
+/********/
+/*Macros*/
+/********/
+#ifdef DRIVER_DEBUG
+  #define kdebug(x) {if (debug) printk x;}
+#else
+  #define kdebug(x)
+#endif
+
+#ifdef DRIVER_ERROR
+  #define kerror(x) {if (errorlog) printk x;}
+#else
+  #define kerror(x)
+#endif
+
+
+// Types
+typedef struct
+{
+  u_long paddr;
+  u_long kaddr;
+  u_long uaddr;
+  u_int size;
+  u_int locked;
+  u_int order;
+  u_int type;
+  u_int used;
+  int pid;
+  char name[40];
+} buffer_t;
+
+typedef struct
+{
+  u_int buffer[MAX_BUFFS];
+} private_stuff;  
+
+struct cmem_proc_data_t
+{
+  char name[10];
+  char value[100];
+};
+
+  
+/*************/
+/*ioctl codes*/
+/*************/
+enum
+{
+  CMEM_RCC_GET = 1,
+  CMEM_RCC_FREE,
+  CMEM_RCC_LOCK,
+  CMEM_RCC_UNLOCK,
+  CMEM_RCC_GETPARAMS,
+  CMEM_RCC_SETUADDR,
+  CMEM_RCC_DUMP
+};
+
+/******************************/
+/*Standard function prototypes*/
+/******************************/
+static int cmem_rcc_open(struct inode *inode, struct file *file);
+static int cmem_rcc_release(struct inode *inode, struct file *file);
+static int cmem_rcc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg);
+static int cmem_rcc_mmap(struct file *file, struct vm_area_struct *vma);
+static int cmem_rcc_proc_write(struct file *file, const char *buffer, u_long count, void *data);
+static int cmem_rcc_proc_read(char *buf, char **start, off_t offset, int count, int *eof, void *data);
+
+#endif
+
Index: /fact/drsdaq/VME/atlas/include/io_rcc/io_rcc.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/io_rcc/io_rcc.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/io_rcc/io_rcc.h	(revision 9833)
@@ -0,0 +1,74 @@
+/************************************************************************/
+/*									*/
+/*  This is the application  header file for the IO_RCC 		*/
+/*  library & applications						*/
+/*									*/
+/*   6. Jun. 02  MAJO  created						*/
+/*									*/
+/*******C 2002 - The software with that certain something****************/
+
+#ifndef _IO_RCC_H
+#define _IO_RCC_H
+
+#include "io_rcc_common.h"
+
+//Definitions used in IO_GetHostInfo
+//Board type definitions are in io_rcc_common.h
+
+//Board manufacturers
+#define CCT 1
+
+//Operating system type
+#define LINUX 1
+
+//Operating system version
+#define K249 1
+
+//Error strings
+#define IO_RCC_SUCCESS_STR       "No error"
+#define IO_RCC_ERROR_FAIL_STR    "Failed to install the error library"
+#define IO_RCC_FILE_STR          "Failed to open /dev/io_rcc"
+#define IO_RCC_NOTOPEN_STR       "Library has not yet been opened"
+#define IO_RCC_MMAP_STR          "Error from call to mmap function"
+#define IO_RCC_MUNMAP_STR        "Error from call to munmap function"
+#define IO_RCC_ILLMANUF_STR      "Unable to determine board manufacturer"
+#define IO_RCC_IOFAIL_STR        "Error from IO_IOPeek or IO_IOPoke"
+#define IO_RCC_NO_CODE_STR       "Unknown error"
+#define IO_PCI_TABLEFULL_STR     "Internal device table is full"
+#define IO_PCI_NOT_FOUND_STR     "PCI Device not found"
+#define IO_PCI_ILL_HANDLE_STR    "Illegal handle"
+#define IO_PCI_CONFIGRW_STR      "Error from pci_(read/write)_config_dword system call"
+#define IO_PCI_UNKNOWN_BOARD_STR "Board type can not be determined"
+#define IO_RCC_ILL_OFFSET_STR    "Illegal offset (alignment)"
+#define IO_PCI_REMAP_STR         "Error from remap_page_range system call"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+IO_ErrorCode_t IO_Open(void);
+IO_ErrorCode_t IO_Close(void);
+IO_ErrorCode_t IO_PCIMemMap(u_int pci_addr, u_int size, u_long *virt_addr);
+IO_ErrorCode_t IO_PCIMemUnmap(u_long virt_addr, u_int size);
+IO_ErrorCode_t IO_IOPeekUInt(u_int address, u_int *data);
+IO_ErrorCode_t IO_IOPokeUInt(u_int address, u_int data);
+IO_ErrorCode_t IO_IOPeekUShort(u_int address, u_short *data);
+IO_ErrorCode_t IO_IOPokeUShort(u_int address, u_short data);
+IO_ErrorCode_t IO_IOPeekUChar(u_int address, u_char *data);
+IO_ErrorCode_t IO_IOPokeUChar(u_int address, u_char data);
+IO_ErrorCode_t IO_PCIDeviceLink(u_int vendor_id, u_int device_id, u_int occurrence, u_int *handle);
+IO_ErrorCode_t IO_PCIDeviceUnlink(u_int handle);
+IO_ErrorCode_t IO_PCIDeviceInfo(u_int handle, pci_info_t *info);
+IO_ErrorCode_t IO_PCIConfigReadUInt(u_int handle, u_int offset, u_int *data);
+IO_ErrorCode_t IO_PCIConfigWriteUInt(u_int handle, u_int offset, u_int data);
+IO_ErrorCode_t IO_PCIConfigReadUShort(u_int handle, u_int offset, u_short *data);
+IO_ErrorCode_t IO_PCIConfigWriteUShort(u_int handle, u_int offset, u_short data);
+IO_ErrorCode_t IO_PCIConfigReadUChar(u_int handle, u_int offset, u_char *data);
+IO_ErrorCode_t IO_PCIConfigWriteUChar(u_int handle, u_int offset, u_char data);
+IO_ErrorCode_t IO_GetHostInfo(HostInfo_t *host_info);
+IO_ErrorCode_t IO_Mark(u_int bus);
+unsigned int IO_RCC_err_get(err_pack err, err_str pid, err_str code);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /fact/drsdaq/VME/atlas/include/io_rcc/io_rcc_common.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/io_rcc/io_rcc_common.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/io_rcc/io_rcc_common.h	(revision 9833)
@@ -0,0 +1,100 @@
+/************************************************************************/
+/*									*/
+/*  This is the common header file for the IO_RCC 			*/
+/*  library & applications						*/
+/*									*/
+/*   6. Jun. 02  MAJO  created						*/
+/*									*/
+/*******C 2002 - The software with that certain something****************/
+
+#ifndef _IO_RCC_COMMON_H
+#define _IO_RCC_COMMON_H
+
+#ifdef __KERNEL__
+  #include <linux/types.h>
+  #define P_ID_IO_RCC 8   // Needs to be re-defined here since we do not want to include rcc_error.h at this level
+#else
+  #include <sys/types.h>
+#endif
+
+typedef u_int IO_ErrorCode_t;
+
+//Board types
+#define VP_UNKNOWN 0
+#define VP_PSE     1
+#define VP_PMC     2
+#define VP_100     3
+#define VP_CP1     4
+
+//Stolen from /usr/src/linuxcern/includelinux/ioport.h
+#define IORESOURCE_IO           0x00000100
+#define IORESOURCE_MEM          0x00000200
+
+typedef struct
+{
+  u_int offset;
+  u_int data;
+  u_int size;
+} IO_RCC_IO_t;
+
+typedef struct
+{
+  u_int board_manufacturer;
+  u_int board_type;
+  u_int operating_system_type;
+  u_int operating_system_version;
+} HostInfo_t;
+
+typedef struct
+{
+  u_int vid;
+  u_int did;
+  u_int occ;
+  u_int handle;
+} IO_PCI_FIND_t;
+
+typedef struct
+{
+  u_int handle;
+  u_int offs;
+  u_int func;
+  u_int data;
+  u_int size;
+} IO_PCI_CONF_t;
+
+typedef struct
+{
+  u_int base;
+  u_int size;
+  u_int flags;
+} pci_bar;
+
+typedef struct
+{
+  u_int handle;
+  pci_bar bar[6];
+} pci_info_t;
+
+
+// Error codes
+enum
+{
+  IO_RCC_SUCCESS = 0,
+  IO_RCC_ERROR_FAIL = (P_ID_IO_RCC <<8)+1,
+  IO_RCC_FILE,
+  IO_RCC_NOTOPEN,
+  IO_RCC_MMAP,
+  IO_RCC_MUNMAP,
+  IO_RCC_ILLMANUF,
+  IO_RCC_IOFAIL,
+  IO_PCI_TABLEFULL,
+  IO_PCI_NOT_FOUND,
+  IO_PCI_ILL_HANDLE,
+  IO_PCI_UNKNOWN_BOARD,
+  IO_PCI_CONFIGRW,
+  IO_PCI_REMAP,
+  IO_RCC_ILL_OFFSET,
+  IO_RCC_NO_CODE
+};
+
+#endif
Index: /fact/drsdaq/VME/atlas/include/io_rcc/io_rcc_driver.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/io_rcc/io_rcc_driver.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/io_rcc/io_rcc_driver.h	(revision 9833)
@@ -0,0 +1,92 @@
+/************************************************************************/
+/*									*/
+/*  This is the driver header file for the IO_RCC package		*/
+/*									*/
+/*   6. Jun. 02  MAJO  created						*/
+/*									*/
+/*******C 2002 - The software with that certain something****************/
+
+#ifndef _IO_RCC_DRIVER_H
+#define _IO_RCC_DRIVER_H
+
+#include "io_rcc/io_rcc_common.h"   
+
+#define IO_MAX_PCI         100     //Max. number of PCI devices linked at any time
+#define MAX_PROC_TEXT_SIZE 0x10000 //The output of "more /proc/io_rcc" must not generate more characters than that
+
+#define CMOSA              0x70
+#define CMOSD              0x71
+#define BID1               0x35
+#define BID2               0x36
+
+
+/********/
+/*Macros*/
+/********/
+#ifdef DRIVER_DEBUG
+  #define kdebug(x) {if (debug) printk x;}
+#else
+  #define kdebug(x)
+#endif
+
+#ifdef DRIVER_ERROR
+  #define kerror(x) {if (errorlog) printk x;}
+#else
+  #define kerror(x)
+#endif
+
+
+/*********/
+/* Types */
+/*********/
+typedef struct
+{
+  struct pci_dev *dev_ptr;
+  u_int vid;
+  u_int did;
+  u_int occ;
+  u_int pid;
+} pci_devices_t;
+
+typedef struct
+{
+  u_int linked[IO_MAX_PCI];
+} private_stuff; 
+
+struct io_proc_data_t
+{
+  char name[10];
+  char value[100];
+};
+
+
+/************/
+/*Prototypes*/
+/************/
+static void io_rcc_vmaClose(struct vm_area_struct *vma);
+static void io_rcc_vmaOpen(struct vm_area_struct *vma);
+static int io_rcc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg);
+static int io_rcc_open(struct inode *ino, struct file *filep);
+static int io_rcc_mmap(struct file *file, struct vm_area_struct *vma);
+static int io_rcc_release(struct inode *ino, struct file *filep);
+static int io_rcc_write_procmem(struct file *file, const char *buffer, u_long count, void *data);
+static int io_rcc_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data);
+
+
+/*************/
+/*ioctl codes*/
+/*************/
+enum
+{
+  IOPEEK=1,
+  IOPOKE,
+  IOGETID,
+  IOPCILINK,
+  IOPCIUNLINK,
+  IOPCICONFR,
+  IOPCICONFW,
+  IOPCIINFO
+};
+
+
+#endif
Index: /fact/drsdaq/VME/atlas/include/rcc_error/rcc_error.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/rcc_error/rcc_error.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/rcc_error/rcc_error.h	(revision 9833)
@@ -0,0 +1,139 @@
+/*
+ * rcc_error.h (originally iom_error)
+ *
+ * ref : ATLAS Technical Note 51
+ *       "Error reporting in the I/O module libraries"
+ *
+ * HP Beck  26-01-1998
+ * HP Beck  21-04-1998  revised
+ * HP Beck  04-05-1998  Package IDs and Package ID strings added
+ * JOP      27-05-1998  LS package added 
+ * MAJO     06-11-2001  Adapted to RCC environment
+ */
+
+#ifndef _RCC_ERROR_H_
+#define _RCC_ERROR_H_
+
+#include <stdio.h>
+
+/* Package Identifers */
+
+typedef enum 
+{
+  P_ID_RCC_ERROR =  1,
+  P_ID_VMERCC    =  2,  //Do not change
+  P_ID_UIO,
+  P_ID_SMEM,
+  P_ID_TS,
+  P_ID_CORBO,
+  P_ID_CMEM_RCC,
+  P_ID_IO_RCC,          // = 8; Do not change
+  P_ID_FILAR,           // = 9; Do not change
+  P_ID_SOLAR,           // = 10; Do not change
+  P_ID_QUEST,           // = 11; Do not change
+  P_ID_EM_RCC,
+  P_ID_LL_RCC,
+  P_ID_SLINK,
+  P_ID_S5933,
+  P_ID_RODBUSY,
+  P_ID_ROBIN,           // = 17; Do not change
+  P_ID_RF2TTC
+} err_pid;              // Package ID Type
+
+/* Definitions for error types */
+
+typedef unsigned int  err_type ;     /* full error information             */
+typedef unsigned int  err_pack ;     /* only major or only minor error     */
+typedef unsigned int  err_field;     /* error number                       */
+typedef char          err_str[256] ; /* textual representation of errors   */
+
+/* Textual representations for Package Identifiers */
+
+#define P_ID_RCC_ERROR_STR       "RCC Error"
+#define P_ID_UIO_STR             "UIO library"
+#define P_ID_SMEM_STR            "Smem library in iom_utils"
+#define P_ID_TS_STR              "Time stamping library"
+#define P_ID_LL_RCC_STR          "Linked List Library"
+#define P_ID_EM_RCC_STR          "FILAR EM library"
+#define P_ID_CORBO_STR           "Corbo library"
+#define P_ID_RODBUSY_STR         "ROD-BUSY library"
+#define P_ID_VMERCC_STR          "VMEbus driver/library for the RCC"
+#define P_ID_CMEM_RCC_STR        "CMEM RCC library"
+#define P_ID_IO_RCC_STR          "IO RCC library"
+#define P_ID_FILAR_STR           "FILAR library"
+#define P_ID_SOLAR_STR           "SOLAR library"
+#define P_ID_SLINK_STR           "AMCC based S-Link library"
+#define P_ID_S5933_STR           "S5933 library"
+#define P_ID_ROBIN_STR           "ROBIN library"
+#define P_ID_RF2TTC_STR          "Library for RF2TTC and RFRX"
+
+/* Errors from the RCC_error package */
+
+enum {ERCC_OK      = 0 ,
+      ERCC_NOTOPEN = (P_ID_RCC_ERROR<<8) + 1 ,
+      ERCC_NOINIT ,
+      ERCC_STREAM ,
+      ERCC_NOCODE 
+} ;
+
+/* ... and their textual representation */
+
+#define ERCC_OK_STR       "all OK"
+#define ERCC_NOTOPEN_STR  "no open performed"
+#define ERCC_NOINIT_STR   "packX_err_get is NULL pointer"
+#define ERCC_STREAM_STR   "stream not writeable"
+#define ERCC_NOCODE_STR   "no such error code"
+
+
+/* MACRO definitions */
+
+#define RCC_ERROR_RETURN(maj, min) \
+              (    !(min) ? 0 : \
+                ( ( (maj) & 0xffff0000 ) ? \
+                  ( ((maj) & 0xffff0000) + ((min) & 0xffff) ) : \
+                  ( ((maj)<<16) + ((min) & 0xffff) ) ) )
+
+
+#define RCC_ERROR_MAJOR(error_code) \
+                ( ((error_code) & 0xffff0000) ? \
+                  ((error_code) & 0xffff0000)>>16 : \
+                  ((error_code) & 0x0000ffff) )
+
+#define RCC_ERROR_MINOR(error_code) \
+                 ( (error_code) & 0x0000ffff )
+
+#define RCC_ERROR_MINOR_PID(error_code) \
+                ( ((error_code) & 0x0000ff00)>>8 )
+
+#define RCC_ERROR_MINOR_ERRNO(error_code) \
+               (  (error_code) & 0x000000ff )
+
+#define RCC_ERROR_MAJOR_PID(error_code) \
+                ( ((error_code) & 0xffff0000) ? \
+                  ((error_code) & 0xff000000)>>24 : \
+                  ((error_code) & 0x0000ff00)>>8 )
+
+#define RCC_ERROR_MAJOR_ERRNO(error_code) \
+                ( ((error_code) & 0xffff0000) ? \
+                  ((error_code) & 0x00ff0000)>>16 : \
+                  ((error_code) & 0x000000ff) )
+
+/* The rcc_error API prototyping */ 
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+err_type rcc_error_open( void ) ;
+err_type rcc_error_close( void ) ;
+err_type rcc_error_init(err_pid, err_type (*)(err_pack, err_str, err_str) );
+err_type iom_error_init(err_pid, err_type (*)(err_pack, err_str, err_str) );
+err_type rcc_error_print(FILE* stream, err_type) ;
+err_type rcc_error_get(err_type, err_str, err_str, err_str, err_str);
+err_type rcc_error_string(char *text, err_type err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /fact/drsdaq/VME/atlas/include/rcc_time_stamp/my_ts_events.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/rcc_time_stamp/my_ts_events.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/rcc_time_stamp/my_ts_events.h	(revision 9833)
@@ -0,0 +1,18 @@
+
+enum
+  {
+  F1_START = 1,
+  F1_PRINT,
+  F1_OVERHEAD, 
+  F1_END
+  };
+
+enum 
+  {
+  F2_START = 100,
+  F2_PRINT,
+  F2_END,
+  F2_PAUSE,
+  F2_RESUME
+  };
+
Index: /fact/drsdaq/VME/atlas/include/rcc_time_stamp/tstamp.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/rcc_time_stamp/tstamp.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/rcc_time_stamp/tstamp.h	(revision 9833)
@@ -0,0 +1,175 @@
+/************************************************************************/
+/*                                                                      */
+/*   file: tstamplib.h                                                  */
+/*   author: Markus Joos, CERN-EP/ESS                                   */
+/*                                                                      */
+/*   This is the header of the timestamping library.                    */
+/*                                                                      */
+/*   History:                                                           */
+/*   22.Sep.98  MAJO  created                                           */
+/*    3.Mar.99  MAJO  major rewrite; macros added                       */
+/*    5.Mar.99  MAJO  default handles and include statement for		*/
+/*                    event symbols added                               */
+/*    27.Jul.99 JOP   move my_ts_events.h out                           */
+/*    16.Jul.02 MAJO  Ported to RCC environment				*/
+/*                                                                      */
+/*******Copyright Ecosoft 2007 - Made from 80% recycled bytes************/
+ 
+#include <sys/types.h>
+#include "rcc_error/rcc_error.h"
+
+#ifndef _TSTAMPLIB_H
+  #define _TSTAMPLIB_H
+
+  typedef struct tstamp
+  { 
+    u_int high;
+    u_int low;
+    u_int data;
+  } tstamp;
+  
+  typedef u_int TS_ErrorCode_t;
+  
+  
+  #define TS_DUMMY       0
+  #define TS_MODE_NORING 0
+  #define TS_MODE_RING   1
+ 
+  #ifdef TSTAMP
+    // Constants
+    #define FREQUENCY_LOW     16500000.0
+    #define FREQUENCY_HIGH    (1.0/256.0)
+    #define TS_FULL           1
+    #define TS_STOP_TS        2
+    #define TS_START_TS       ~TS_STOP_TS
+    #define MAX_OPENS         10
+  
+    enum
+      {
+      TS_H1 = 1,
+      TS_H2,
+      TS_H3,
+      TS_H4,
+      TS_H5,
+      TS_H6,
+      TS_H7,
+      TS_H8,
+      TS_H9,
+      TS_H10
+      };
+
+    // Macros
+    static int tstamp_ret;
+
+      #define TS_OPEN(size, ts_handle)\
+      {\
+      tstamp_ret = ts_open(size, ts_handle);\
+      if (tstamp_ret)\
+        rcc_error_print(stdout, tstamp_ret);\
+      }
+
+      #define TS_SAVE(handle, name)\
+      {\
+      tstamp_ret=ts_save(handle, name);\
+      if (tstamp_ret)\
+        rcc_error_print(stdout, tstamp_ret);\
+      }
+      
+      #define TS_CLOSE(handle)\
+      {\
+      tstamp_ret = ts_close(handle);\
+      if (tstamp_ret)\
+        rcc_error_print(stdout, tstamp_ret);\
+      }
+      
+      #define TS_RECORD(handle, udata)      ts_record(handle, udata)
+      #define TS_START(handle)              ts_start(handle)
+      #define TS_SETT0                      ts_sett0()
+      #define TS_PAUSE(handle)              ts_pause(handle) 
+      #define TS_RESUME(handle)             ts_resume(handle)
+      #define TS_DURATION( t1, t2)          ts_duration(t1, t2)
+      #define TS_CLOCK(time)                ts_clock(time)
+      #define TS_DELAY(time)                ts_delay(time)
+      #define TS_MODE(handle, mode)         ts_mode(handle, mode)
+      #define TS_COMPARE(t1, t2)            ts_compare(t1, t2)
+      #define TS_OFFSET(ts, usecs)          ts_offset(ts, usecs)
+      #define TS_WAIT_UNTIL(target, nyield) ts_wait_until(target, nyield)
+  #else
+      #define TS_OPEN(size, ts_handle)
+      #define TS_SAVE(handle, name)
+      #define TS_CLOSE(handle)
+      #define TS_RECORD(handle,udata)
+      #define TS_START(handle)
+      #define TS_PAUSE(handle)
+      #define TS_RESUME(handle)
+      #define TS_DURATION(t1, t2)
+      #define TS_CLOCK(time) 
+      #define TS_DELAY(time) 
+      #define TS_MODE(handle, mode)
+      #define TS_COMPARE(t1, t2)
+      #define TS_OFFSET(ts, usecs)
+      #define TS_WAIT_UNTIL(target, nyield)
+      #define TS_SETT0
+  #endif
+
+  // Error codes
+  enum
+  {
+    TSE_OK = 0,
+    TSE_IS_CLOSED = (P_ID_TS << 8) + 1,
+    TSE_WHAT,
+    TSE_SMEM,
+    TSE_NO_FREQ,
+    TSE_FILE,
+    TSE_ILL_HANDLE,
+    TSE_ILL_SIZE,
+    TSE_PFILE,
+    TSE_NO_REF,
+    TSE_ERROR_FAIL,
+    TSE_ILL_MODE,
+    TSE_NOCODE
+  };
+  
+  #define TSE_OK_STR           "No error"  
+  #define TSE_NO_FREQ_STR      "Failed to set frequency"
+  #define TSE_FILE_STR         "Failed to open /proc/cpuinfo"
+  #define TSE_ILL_HANDLE_STR   "Parameter >handle< is out of range"
+  #define TSE_IS_CLOSED_STR    "The library has not been opened"
+  #define TSE_ILL_SIZE_STR     "Parameter >size< is out of range"
+  #define TSE_PFILE_STR        "Failed to open/close data file"
+  #define TSE_NO_REF_STR       "Reference time missing (ts_sett0)"
+  #define TSE_ERROR_FAIL_STR   "Failed to initialise the error system"
+  #define TSE_ILL_MODE_STR     "Parameter >mode< is out of range"
+  #define TSE_NOCODE_STR       "Unknown error code"
+   
+  #ifdef __cplusplus
+    extern "C" {
+  #endif
+
+  /*prototypes*/
+  float ts_duration(tstamp t1, tstamp t2); 
+  int ts_compare(tstamp t1, tstamp t2);
+  TS_ErrorCode_t ts_offset(tstamp *ts, u_int usecs);
+  TS_ErrorCode_t ts_open(int size, int handle);
+  TS_ErrorCode_t ts_close(int handle);
+  TS_ErrorCode_t ts_save(int handle, char *name);
+  TS_ErrorCode_t ts_elapsed (tstamp t1, float *time);
+  TS_ErrorCode_t ts_get_freq(void);
+  TS_ErrorCode_t ts_record(int handle, int udata);
+  TS_ErrorCode_t ts_sett0(void);
+  TS_ErrorCode_t ts_start(int handle);
+  TS_ErrorCode_t ts_pause(int handle); 
+  TS_ErrorCode_t ts_resume(int handle);
+  TS_ErrorCode_t ts_clock(tstamp *time);
+  TS_ErrorCode_t ts_delay(u_int usecs);
+  TS_ErrorCode_t ts_mode(int handle, u_int mode);
+  TS_ErrorCode_t ts_wait(u_int usecs, u_int *nyield);
+  TS_ErrorCode_t ts_wait_until(tstamp target, u_int *nyield);
+  TS_ErrorCode_t packTS_err_get (err_pack err, err_str pid_str, err_str en_str);
+
+  #ifdef __cplusplus
+    }
+  #endif
+#endif
+
+
Index: /fact/drsdaq/VME/atlas/include/vme_rcc/cct_berr.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/vme_rcc/cct_berr.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/vme_rcc/cct_berr.h	(revision 9833)
@@ -0,0 +1,16 @@
+// $Id: cct_berr.h,v 1.2 2003/07/09 06:47:18 joos Exp $
+/************************************************************************/
+/*                                                                      */
+/* File: cct_berr.h                                                     */
+/*                                                                      */
+/* VME bus error register for VP PMC/C1x and VP CPI/Pxx boards          */
+/* "borrowed" from the CCT driver                                       */
+/*                                                                      */
+/*  29. Nov. 01  MAJO  created                                          */
+/*                                                                      */
+/************ C 2001 - The software with that certain something *********/
+
+#define BERR_INT_PORT           0x212
+#define BERR_INT_ENABLE         0x10
+#define BERR_INT_MASK           0x20
+#define BERR_VP100              0x213
Index: /fact/drsdaq/VME/atlas/include/vme_rcc/universe.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/vme_rcc/universe.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/vme_rcc/universe.h	(revision 9833)
@@ -0,0 +1,333 @@
+// $Id: universe.h,v 1.3 2004/06/15 09:03:26 joos Exp $
+#ifndef __universe_h
+#define __universe_h
+
+/* Full register space of Tundra Universe ASIC */
+
+/* UCSR Registers */
+typedef struct {
+volatile unsigned int pci_id;				/* 000 */
+volatile unsigned int pci_csr;				/* 004 */
+volatile unsigned int pci_class;			/* 008 */
+volatile unsigned int pci_misc0;			/* 00C */
+volatile unsigned int pci_bs;				/* 010 */
+volatile unsigned int pci_bs1;				/* 014 */
+volatile unsigned int reserved1 [9];			/* 018 - 038 */
+volatile unsigned int pci_misc1;			/* 03C */
+volatile unsigned int reserved2[48];			/* 040 */
+volatile unsigned int lsi0_ctl;		   	        /* 100 */
+volatile unsigned int lsi0_bs;	        		/* 104 */
+volatile unsigned int lsi0_bd;	        		/* 108 */
+volatile unsigned int lsi0_to;	        		/* 10C */
+volatile unsigned int reserved3;        		/* 110 */
+volatile unsigned int lsi1_ctl;	        	        /* 114 */
+volatile unsigned int lsi1_bs;		        	/* 118 */
+volatile unsigned int lsi1_bd;		        	/* 11C */
+volatile unsigned int lsi1_to;		        	/* 120 */
+volatile unsigned int reserved4;	        	/* 124 */
+volatile unsigned int lsi2_ctl;	        		/* 128 */
+volatile unsigned int lsi2_bs;		        	/* 12C */
+volatile unsigned int lsi2_bd;		        	/* 130 */
+volatile unsigned int lsi2_to;		        	/* 134 */
+volatile unsigned int reserved5;	        	/* 138 */
+volatile unsigned int lsi3_ctl;	        		/* 13C */
+volatile unsigned int lsi3_bs;		        	/* 140 */
+volatile unsigned int lsi3_bd;		        	/* 144 */
+volatile unsigned int lsi3_to;		        	/* 148 */
+volatile unsigned int reserved6[9];	        	/* 14C-16C */
+volatile unsigned int scyc_ctl;				/* 170 */
+volatile unsigned int scyc_addr;			/* 174 */
+volatile unsigned int scyc_en;				/* 178 */
+volatile unsigned int scyc_cmp;				/* 17C */
+volatile unsigned int scyc_swp;				/* 180 */
+volatile unsigned int lmisc;				/* 184 */
+volatile unsigned int slsi;            			/* 188 */
+volatile unsigned int l_cmderr;				/* 18C */
+volatile unsigned int laerr;				/* 190 */
+volatile unsigned int reserved7[3]; 			/* 194-19C */
+volatile unsigned int lsi4_ctl;	        		/* 1A0 */
+volatile unsigned int lsi4_bs;		        	/* 1A4 */
+volatile unsigned int lsi4_bd;		        	/* 1A8 */
+volatile unsigned int lsi4_to;		        	/* 1AC */
+volatile unsigned int reserved8;	        	/* 1B0 */
+volatile unsigned int lsi5_ctl;	        		/* 1B4 */
+volatile unsigned int lsi5_bs;		        	/* 1B8 */
+volatile unsigned int lsi5_bd;		        	/* 1BC */
+volatile unsigned int lsi5_to;		        	/* 1C0 */
+volatile unsigned int reserved9;	        	/* 1C4 */
+volatile unsigned int lsi6_ctl;	        		/* 1C8 */
+volatile unsigned int lsi6_bs;		        	/* 1CC */
+volatile unsigned int lsi6_bd;		        	/* 1D0 */
+volatile unsigned int lsi6_to;		        	/* 1D4 */
+volatile unsigned int reserved10;	        	/* 1D8 */
+volatile unsigned int lsi7_ctl;	        		/* 1DC */
+volatile unsigned int lsi7_bs;		        	/* 1E0 */
+volatile unsigned int lsi7_bd;		        	/* 1E4 */
+volatile unsigned int lsi7_to;		        	/* 1E8 */
+volatile unsigned int reserved11[5];	        	/* 1EC - 1FC*/
+volatile unsigned int dctl;				/* 200 */
+volatile unsigned int dtbc;				/* 204 */
+volatile unsigned int dla;				/* 208 */
+volatile unsigned int reserved12;			/* 20c */
+volatile unsigned int dva;				/* 210 */
+volatile unsigned int reserved13;			/* 214 */
+volatile unsigned int dcpp;				/* 218 */
+volatile unsigned int reserved14;			/* 21C */
+volatile unsigned int dgcs;				/* 220 */
+volatile unsigned int d_llue;				/* 224 */
+volatile unsigned int reserved15[(0x300-0x228)/4];	/* 228 - 2FC */
+volatile unsigned int lint_en;				/* 300 */
+volatile unsigned int lint_stat;			/* 304 */
+volatile unsigned int lint_map0;			/* 308 */
+volatile unsigned int lint_map1;			/* 30C */
+volatile unsigned int vint_en;				/* 310 */
+volatile unsigned int vint_stat;			/* 314 */
+volatile unsigned int vint_map0;			/* 318 */
+volatile unsigned int vint_map1;			/* 31C */
+volatile unsigned int statid;				/* 320 */
+volatile unsigned int vx_statid[7];			/* 324 - 33C */
+volatile unsigned int lint_map2;			/* 340 */
+volatile unsigned int vint_map2;			/* 344 */
+volatile unsigned int mbox0;				/* 348 */
+volatile unsigned int mbox1;				/* 34C */
+volatile unsigned int mbox2;				/* 350 */
+volatile unsigned int mbox3;				/* 354 */
+volatile unsigned int sema0;				/* 358 */
+volatile unsigned int sema1;			 	/* 35C */
+volatile unsigned int reserved16[(0x400-0x360)/4];      /* 360 - 3FC */
+volatile unsigned int mast_ctl;				/* 400 */
+volatile unsigned int misc_ctl;				/* 404 */
+volatile unsigned int misc_stat;			/* 408 */
+volatile unsigned int user_am;				/* 40C */
+volatile unsigned int reserved17[(0x4fC-0x410)/4];      /* 410 - 4F8 */
+volatile unsigned int u2spec;				/* 4FC */
+volatile unsigned int reserved17b[(0xf00-0x500)/4];     /* 500 - EFC */
+volatile unsigned int vsi0_ctl;				/* F00 */
+volatile unsigned int vsi0_bs;				/* F04 */
+volatile unsigned int vsi0_bd;				/* F08 */
+volatile unsigned int vsi0_to;				/* F0C */
+volatile unsigned int reserved18;			/* F10 */
+volatile unsigned int vsi1_ctl;				/* F14 */
+volatile unsigned int vsi1_bs;				/* F18 */
+volatile unsigned int vsi1_bd;				/* F1C */
+volatile unsigned int vsi1_to;				/* F20 */
+volatile unsigned int reserved19;			/* F24 */
+volatile unsigned int vsi2_ctl;				/* F28 */
+volatile unsigned int vsi2_bs;				/* F2C */
+volatile unsigned int vsi2_bd;				/* F30 */
+volatile unsigned int vsi2_to;				/* F34 */
+volatile unsigned int reserved20;			/* F38 */
+volatile unsigned int vsi3_ctl;				/* F3C */
+volatile unsigned int vsi3_bs;				/* F40 */
+volatile unsigned int vsi3_bd;				/* F44 */
+volatile unsigned int vsi3_to;				/* F48 */
+volatile unsigned int reserved21[(0xf64-0xf4c)/4];      /* FC4 - F60 */
+volatile unsigned int lm_ctl;				/* F64 */
+volatile unsigned int tl_bs;				/* F68 */
+volatile unsigned int reserved22;		        /* F6C */
+volatile unsigned int vrai_ctl;				/* F70 */
+volatile unsigned int vrai_bs;				/* F74 */
+volatile unsigned int reserved23[2];                    /* F78 - F7C */
+volatile unsigned int vcsr_ctl;                   	/* F80 */
+volatile unsigned int vcsr_to;                   	/* F84 */
+volatile unsigned int v_amerr;				/* F88 */
+volatile unsigned int vaerr;				/* F8C */
+volatile unsigned int vsi4_ctl;				/* F90 */
+volatile unsigned int vsi4_bs;				/* F94 */
+volatile unsigned int vsi4_bd;				/* F98 */
+volatile unsigned int vsi4_to;				/* F9C */
+volatile unsigned int reserved24;			/* FA0 */
+volatile unsigned int vsi5_ctl;				/* FA4 */
+volatile unsigned int vsi5_bs;				/* FA8 */
+volatile unsigned int vsi5_bd;				/* FAC */
+volatile unsigned int vsi5_to;				/* FB0 */
+volatile unsigned int reserved25;			/* FB4 */
+volatile unsigned int vsi6_ctl;				/* FB8 */
+volatile unsigned int vsi6_bs;				/* FBC */
+volatile unsigned int vsi6_bd;				/* FC0 */
+volatile unsigned int vsi6_to;				/* FC4 */
+volatile unsigned int reserved26;			/* FC8 */
+volatile unsigned int vsi7_ctl;				/* FCC */
+volatile unsigned int vsi7_bs;				/* FD0 */
+volatile unsigned int vsi7_bd;				/* FD4 */
+volatile unsigned int vsi7_to;				/* FD8 */
+volatile unsigned int reserved27[6];     		/* FDC - FF0 */
+volatile unsigned int vcsr_clr;                   	/* FF4 */
+volatile unsigned int vcsr_set;                   	/* FF8 */
+volatile unsigned int vcsr_bs;                   	/* FFC */
+}universe_regs_t;
+
+/* Universe II Register Offsets */
+
+#define PCI_ID		0x0000
+#define PCI_CSR		0x0004
+#define PCI_CLASS	0x0008
+#define PCI_MISC0	0x000C
+#define PCI_BS0		0x0010
+#define PCI_BS1		0x0014
+#define PCI_MISC1	0x003C
+
+#define LSI0_CTL	0x0100
+#define LSI0_BS		0x0104
+#define LSI0_BD		0x0108
+#define LSI0_TO		0x010C
+
+#define LSI1_CTL	0x0114
+#define LSI1_BS		0x0118
+#define LSI1_BD		0x011C
+#define LSI1_TO		0x0120
+#define LSI2_CTL	0x0128
+#define LSI2_BS		0x012C
+#define LSI2_BD		0x0130
+#define LSI2_TO		0x0134
+
+#define LSI3_CTL	0x013C
+#define LSI3_BS		0x0140
+#define LSI3_BD		0x0144
+#define LSI3_T		O0x0148
+
+#define SCYC_CTL	0x0170
+#define SCYC_ADDR	0x0174
+#define SCYC_EN		0x0178
+#define SCYC_CMP	0x017C
+#define SCYC_SWP	0x0180
+#define LMISC		0x0184
+#define SLSI		0x0188
+#define L_CMDERR	0x018C
+#define LAERR		0x0190
+
+#define LSI4_CTL	0x01A0
+#define LSI4_BS		0x01A4
+#define LSI4_BD		0x01A8
+#define LSI4_TO		0x01AC
+
+#define LSI5_CTL	0x01B4
+#define LSI5_BS		0x01B8
+#define LSI5_BD		0x01BC
+#define LSI5_TO		0x01C0
+
+#define LSI6_CTL	0x01C8
+#define LSI6_BS		0x01CC
+#define LSI6_BD		0x01D0
+#define LSI6_TO		0x01D4
+
+#define LSI7_CTL	0x01DC
+#define LSI7_BS		0x01E0
+#define LSI7_BD		0x01E4
+#define LSI7_TO		0x01E8
+
+#define DCTL		0x0200
+#define DTBC		0x0204
+#define DLA		0x0208
+#define DVA		0x0210
+#define DCPP		0x0218
+#define DGCS		0x0220
+#define D_LLUE		0x0224
+
+#define LINT_EN		0x0300
+#define LINT_STAT       0x0304
+#define LINT_MAP0       0x0308
+#define LINT_MAP1       0x030C
+#define VINT_EN		0x0310
+#define VINT_STAT       0x0314
+#define VINT_MAP0       0x0318
+#define VINT_MAP1       0x031C
+#define STATID		0x0320
+#define V1_STATID       0x0324
+#define V2_STATID       0x0328
+#define V3_STATID       0x032C
+#define V4_STATID       0x0330
+#define V5_STATID       0x0334
+#define V6_STATID       0x0338
+#define V7_STATID       0x033C
+
+#define LINT_MAP2	0x340
+#define VINT_MAP2	0x344
+#define MBOX0		0x348
+#define MBOX1		0x34C
+#define MBOX2		0x350
+#define MBOX3		0x354
+#define SEMA0		0x358
+#define SEMA1		0x35C
+
+#define MAST_CTL	0x0400
+#define MISC_CTL	0x0404
+#define MISC_STAT	0x0408
+#define USER_AM		0x040C
+
+#define VSI0_CTL	0x0F00
+#define VSI0_BS		0x0F04
+#define VSI0_BD		0x0F08
+#define VSI0_TO		0x0F0C
+
+#define VSI1_CTL	0x0F14
+#define VSI1_BS		0x0F18
+#define VSI1_BD		0x0F1C
+#define VSI1_TO		0x0F20
+
+#define VSI2_CT		L0x0F28
+#define VSI2_BS		0x0F2C
+#define VSI2_BD		0x0F30
+#define VSI2_TO		0x0F34
+
+#define VSI3_CTL	0x0F3C
+#define VSI3_BS		0x0F40
+#define VSI3_BD		0x0F44
+#define VSI3_TO		0x0F48
+
+#define LM_CTL		0xF64
+#define LM_BS		0xF68
+
+#define VRAI_CTL	0x0F70
+#define VRAI_BS		0x0F74
+#define VCSR_CTL	0x0F80
+#define VCSR_TO		0x0F84
+#define V_AMERR		0x0F88
+#define VAERR		0x0F8C
+
+#define VSI4_CTL	0x0F90
+#define VSI4_BS		0x0F94
+#define VSI4_BD		0x0F98
+#define VSI4_TO		0x0F9C
+
+#define VSI5_CTL	0x0FA4
+#define VSI5_BS		0x0FA8
+#define VSI5_BD		0x0FAC
+#define VSI5_TO		0x0FB0
+
+#define VSI6_CTL	0x0FB8
+#define VSI6_BS		0x0FBC
+#define VSI6_BD		0x0FC0
+#define VSI6_TO		0x0FC4
+
+#define VSI7_CTL	0x0FCC
+#define VSI7_BS		0x0FD0
+#define VSI7_BD		0x0FD4
+#define VSI7_TO		0x0FD8
+
+#define VCSR_CLR	0x0FF4
+#define VCSR_SET	0x0FF8
+#define VCSR_BS		0x0FFC
+
+/* Interrupt Control Registers - "borrowed" from the Hannapel driver */
+#define BM_LINT_ACFAIL             0x00008000
+#define BM_LINT_SYSFAIL            0x00004000
+#define BM_LINT_SW_INT             0x00002000
+#define BM_LINT_SW_IACK            0x00001000
+#define BM_LINT_VERR               0x00000400
+#define BM_LINT_LERR               0x00000200
+#define BM_LINT_DMA                0x00000100
+#define BM_LINT_VIRQ               0x000000FE
+#define BM_LINT_VIRQ7              0x00000080
+#define BM_LINT_VIRQ6              0x00000040
+#define BM_LINT_VIRQ5              0x00000020
+#define BM_LINT_VIRQ4              0x00000010
+#define BM_LINT_VIRQ3              0x00000008
+#define BM_LINT_VIRQ2              0x00000004
+#define BM_LINT_VIRQ1              0x00000002
+#define BM_LINT_VOWN               0x00000001
+#define BM_VX_STATID_STATID        0x000000FF
+#define OF_VX_STATID_STATID        0
+#define BM_VX_STATID_ERR           0x00000100
+
+#endif /* __universe_h */
Index: /fact/drsdaq/VME/atlas/include/vme_rcc/vme_rcc.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/vme_rcc/vme_rcc.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/vme_rcc/vme_rcc.h	(revision 9833)
@@ -0,0 +1,205 @@
+// $Id: vme_rcc.h,v 1.11 2007/11/08 07:56:44 joos Exp $
+/************************************************************************/
+/*									*/
+/* File: vme_rcc.h							*/
+/*									*/
+/* This is the public header file for RCC VMEbus library		*/
+/*									*/
+/* 12. Oct. 01  MAJO  created						*/
+/*									*/
+/* 011101 JOP add error codes						*/
+/* 020418 JOP ROAK / RORA						*/
+/* 020527 JOP update interrupt API					*/
+/*									*/
+/************ C 2003 - The software with that certain something *********/
+
+#ifndef _VME_RCC_H 
+#define _VME_RCC_H
+
+#include <linux/types.h>
+#include "vme_rcc_common.h"
+
+/***********************************/
+/*VMEbus transfer type  definitions*/
+/***********************************/
+#define VME_RP             0x01
+#define VME_WP             0x02
+#define VME_A16            0x0
+#define VME_A24            0x1
+#define VME_A32            0x2
+#define VME_CRCSR          0x5
+#define VME_USER1          0x6
+#define VME_USER2          0x7
+#define VME_AM09           (VME_A32)
+#define VME_AM39           (VME_A24)
+#define VME_AM29           (VME_A16)
+#define VME_AM2f           (VME_CRCSR)
+#define VME_AM_PROGRAM     0x00004000
+#define VME_AM_DATA        0x00000000
+#define VME_AM_USER        0x00000000
+#define VME_AM_SUPERVISOR  0x00001000
+#define VME_DMA_READ       0x00000000
+#define VME_DMA_WRITE      0x80000000
+#define VME_DMA_D32        0x00800000
+#define VME_DMA_D64        0x00c00000
+#define VME_DMA_DEF        0x00020100 //A32, user, data, BLT
+#define VME_DMA_D32W       (VME_DMA_DEF | VME_DMA_D32 | VME_DMA_WRITE)
+#define VME_DMA_D32R       (VME_DMA_DEF | VME_DMA_D32 | VME_DMA_READ)
+#define VME_DMA_D64W       (VME_DMA_DEF | VME_DMA_D64 | VME_DMA_WRITE)
+#define VME_DMA_D64R       (VME_DMA_DEF | VME_DMA_D64 | VME_DMA_READ)
+#define VME_DMA_DEF_A24    0x00010100 //A24, user, data, BLT
+#define VME_DMA_A24D32W    (VME_DMA_DEF_A24 | VME_DMA_D32 | VME_DMA_WRITE)
+#define VME_DMA_A24D32R    (VME_DMA_DEF_A24 | VME_DMA_D32 | VME_DMA_READ)
+#define VME_FIFO_DMA_D32W  0x80820200  //Write in A32/D32 single cycle mode to a constant address
+#define VME_FIFO_DMA_D32R  0x00820200  //Read in A32/D32 single cycle mode from a constant address
+
+/************************************************/
+/*Identifier definitions for CR/CSR space access*/
+/*                                              */
+/* Bits 31..28  Number of bytes to read         */
+/* Bits 27..24  Data width		        */
+/*              0 = D08				*/
+/*              1 = D16				*/
+/*              2 = D32				*/
+/* Bits 19..00  Offset of first byte            */
+/************************************************/
+#define ONE_BYTE           0x10000000
+#define TWO_BYTE           0x20000000
+#define THREE_BYTE         0x30000000
+#define FOUR_BYTE          0x40000000
+
+#define CRCSR_D08          0x00000000
+#define CRCSR_D16          0x01000000
+#define CRCSR_D32          0x02000000
+
+#define CR_CHECKSUM        (CRCSR_D08 | ONE_BYTE   | 0x00003)
+#define CR_LENGTH          (CRCSR_D08 | THREE_BYTE | 0x00007)
+#define CR_CRACCESSWIDTH   (CRCSR_D08 | ONE_BYTE   | 0x00013)
+#define CR_CSRACCESSWIDTH  (CRCSR_D08 | ONE_BYTE   | 0x00017)
+#define CR_CRCSRSPEC       (CRCSR_D08 | ONE_BYTE   | 0x0001b)
+#define CR_ASCII1          (CRCSR_D08 | ONE_BYTE   | 0x0001f)
+#define CR_ASCII2          (CRCSR_D08 | ONE_BYTE   | 0x00023)
+#define CR_MANUFID         (CRCSR_D08 | THREE_BYTE | 0x00027)
+#define CR_BOARDID         (CRCSR_D08 | FOUR_BYTE  | 0x00033)
+#define CR_REVID           (CRCSR_D08 | FOUR_BYTE  | 0x00043)
+       
+#define CSR_BAR            (CRCSR_D08 | ONE_BYTE   | 0x7ffff)
+#define CSR_BSR            (CRCSR_D08 | ONE_BYTE   | 0x7fffb)
+#define CSR_BCR            (CRCSR_D08 | ONE_BYTE   | 0x7fff7)
+#define CSR_ADER7          (CRCSR_D08 | FOUR_BYTE  | 0x7ffd3)
+#define CSR_ADER6          (CRCSR_D08 | FOUR_BYTE  | 0x7ffc3)
+#define CSR_ADER5          (CRCSR_D08 | FOUR_BYTE  | 0x7ffb3)
+#define CSR_ADER4          (CRCSR_D08 | FOUR_BYTE  | 0x7ffa3)
+#define CSR_ADER3          (CRCSR_D08 | FOUR_BYTE  | 0x7ff93)
+#define CSR_ADER2          (CRCSR_D08 | FOUR_BYTE  | 0x7ff83)
+#define CSR_ADER1          (CRCSR_D08 | FOUR_BYTE  | 0x7ff73)
+#define CSR_ADER0          (CRCSR_D08 | FOUR_BYTE  | 0x7ff63)
+
+/***************************************/
+/*various upper limits for arrays, etc.*/
+/***************************************/
+#define VME_MAXSTRING   256 
+
+
+/******************/
+/*type definitions*/
+/******************/
+typedef struct
+{
+  int                     number_of_items;
+  VME_BlockTransferItem_t list_of_items[VME_MAXCHAINEL];
+} VME_BlockTransferList_t;
+
+typedef struct
+{
+  int                 number_of_items;
+  VME_InterruptItem_t list_of_items[VME_MAXINTERRUPT];
+} VME_InterruptList_t;
+
+
+/************/
+/*Prototypes*/
+/************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*******************************/ 
+/*Official functions of the API*/
+/*******************************/ 
+int VME_ErrorPrint(VME_ErrorCode_t error_code);
+int VME_ErrorString(VME_ErrorCode_t error_code, char *error_string);
+int VME_ErrorNumber(VME_ErrorCode_t error_code, int *error_number);
+VME_ErrorCode_t VME_Open(void);
+VME_ErrorCode_t VME_Close(void);
+VME_ErrorCode_t VME_ReadCRCSR(int slot_number, u_int crcsr_identifier, u_int *value);
+VME_ErrorCode_t VME_WriteCRCSR(int slot_number, u_int crcsr_identifier, u_int value);
+VME_ErrorCode_t VME_MasterMap(VME_MasterMap_t *master_map, int *master_mapping);
+VME_ErrorCode_t VME_MasterMapVirtualAddress(int master_mapping, u_int *virtual_address);
+VME_ErrorCode_t VME_ReadSafeUInt(int master_mapping, u_int address_offset, u_int *value);
+VME_ErrorCode_t VME_ReadSafeUShort(int master_mapping, u_int address_offset, u_short *value);
+VME_ErrorCode_t VME_ReadSafeUChar(int master_mapping, u_int address_offset, u_char *value);
+VME_ErrorCode_t VME_WriteSafeUInt(int master_mapping, u_int address_offset, u_int value);
+VME_ErrorCode_t VME_WriteSafeUShort(int master_mapping, u_int address_offset, u_short value);
+VME_ErrorCode_t VME_WriteSafeUChar(int master_mapping, u_int address_offset, u_char value);
+VME_ErrorCode_t VME_MasterUnmap(int master_mapping);
+VME_ErrorCode_t VME_MasterMapDump(void);
+VME_ErrorCode_t VME_BusErrorRegisterSignal(int signal_number);
+VME_ErrorCode_t VME_BusErrorInfoGet(VME_BusErrorInfo_t *bus_error_info);
+VME_ErrorCode_t VME_SlaveMap(VME_SlaveMap_t *slave_map, int *slave_mapping);
+VME_ErrorCode_t VME_SlaveMapVmebusAddress(int slave_mapping, u_int *vmebus_address);
+VME_ErrorCode_t VME_SlaveUnmap(int slave_mapping);
+VME_ErrorCode_t VME_SlaveMapDump(void);
+VME_ErrorCode_t VME_BlockTransferInit(VME_BlockTransferList_t *block_transfer_list, int *block_transfer);
+VME_ErrorCode_t VME_BlockTransferStart(int block_transfer);
+VME_ErrorCode_t VME_BlockTransferWait(int block_transfer, int time_out, VME_BlockTransferList_t *block_transfer_list);
+VME_ErrorCode_t VME_BlockTransferEnd(int block_transfer);
+VME_ErrorCode_t VME_BlockTransfer(VME_BlockTransferList_t *block_transfer_list, int time_out);
+VME_ErrorCode_t VME_BlockTransferDump(void);
+VME_ErrorCode_t VME_BlockTransferStatus(VME_BlockTransferList_t *block_transfer_list, int position_of_block, VME_ErrorCode_t *status);
+VME_ErrorCode_t VME_BlockTransferRemaining(VME_BlockTransferList_t *block_transfer_list, int position_of_block, int *remaining);
+VME_ErrorCode_t VME_InterruptLink(VME_InterruptList_t* vmebus_interrupt_list, int *interrupt);
+VME_ErrorCode_t VME_InterruptReenable(int interrupt);
+VME_ErrorCode_t VME_InterruptWait(int interrupt, int time_out, VME_InterruptInfo_t* ir_info);
+VME_ErrorCode_t VME_InterruptRegisterSignal(int interrupt, int signal_number);
+VME_ErrorCode_t VME_InterruptInfoGet(int interrupt, VME_InterruptInfo_t* ir_info);
+VME_ErrorCode_t VME_InterruptUnlink(int interrupt);
+VME_ErrorCode_t VME_InterruptGenerate(int level, u_char vector);
+VME_ErrorCode_t VME_InterruptDump(void);
+VME_ErrorCode_t VME_SysfailInterruptLink(void);
+VME_ErrorCode_t VME_SysfailInterruptRegisterSignal(int signal_number);
+VME_ErrorCode_t VME_SysfailInterruptWait(int time_out);
+VME_ErrorCode_t VME_SysfailInterruptUnlink(void);
+VME_ErrorCode_t VME_SysfailInterruptReenable(void);
+VME_ErrorCode_t VME_SysfailSet(void);
+VME_ErrorCode_t VME_SysfailReset(void);
+VME_ErrorCode_t VME_SysfailPoll(int *flag);
+VME_ErrorCode_t VME_UniverseMap(u_int *virtual_address);
+VME_ErrorCode_t VME_UniverseUnmap(u_int virtual_address);
+VME_ErrorCode_t VME_CCTSetSwap(u_char data);
+VME_ErrorCode_t VME_Update(u_int *data);
+VME_ErrorCode_t VME_SendSysreset(void);
+void VME_ReadFastUInt(int master_mapping, u_int address_offset, u_int *value);
+void VME_ReadFastUShort(int master_mapping, u_int address_offset, u_short *value);
+void VME_ReadFastUChar(int master_mapping, u_int address_offset, u_char *value);
+void VME_WriteFastUInt(int master_mapping, u_int address_offset, u_int value);
+void VME_WriteFastUShort(int master_mapping, u_int address_offset, u_short value);
+void VME_WriteFastUChar(int master_mapping, u_int address_offset, u_char value);
+
+/******************************/
+/* Internal service functions */
+/******************************/
+VME_ErrorCode_t vmercc_err_get(err_pack err, err_str pid, err_str code);
+
+/**********************************/ 
+/*Additional (temporary) functions*/
+/**********************************/
+VME_ErrorCode_t VME_test(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /fact/drsdaq/VME/atlas/include/vme_rcc/vme_rcc_common.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/vme_rcc/vme_rcc_common.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/vme_rcc/vme_rcc_common.h	(revision 9833)
@@ -0,0 +1,331 @@
+// $Id: vme_rcc_common.h,v 1.13 2007/11/08 07:56:44 joos Exp $
+/************************************************************************/
+/*									*/
+/* File: vme_rcc_common.h						*/
+/*									*/
+/* This is the common public header file for RCC VMEbus library		*/
+/* and driver								*/
+/*									*/
+/*  2. Nov. 01  MAJO  created						*/
+/*  2. Nov. 01  JOP   interrupts					*/
+/*  26 Nov. 01  JOP   bus errors					*/
+/*  19 Apr. 02  JOP   ROAK / RORA					*/
+/*  27 May. 02  JOP   modify interrupt API				*/
+/*									*/
+/************ C 2005 - The software with that certain something *********/
+
+#ifndef _VME_RCC_COMMON_H 
+#define _VME_RCC_COMMON_H
+
+#ifdef __KERNEL__
+  #include <linux/types.h>
+  #define P_ID_VMERCC 2   // Needs to be re-defined here since we do not want to include rcc_error.h at this level
+#else
+  #include <sys/types.h>
+#endif
+
+/***************/
+/*CCT SBC types*/
+/***************/
+#define VP_UNKNOWN 0
+#define VP_PSE     1
+#define VP_PMC     2
+#define VP_100     3
+#define VP_CP1     4
+#define VP_110     5
+#define VP_315     6
+#define VP_317     7
+#define VP_325     8
+
+/*******************/
+/* Other constants */
+/*******************/
+#define TEXT_SIZE1              3000
+#define TEXT_SIZE2              20000
+#define VME_MAXINTERRUPT        256	// Max # vectors per handle
+#define VME_MAX_BERR_PROCS      10	// Max # processes with BERR handling
+#define VME_MAX_MASTERMAP       200  // Max # of concurrent master mappings in all processes
+#define VME_MAXCHAINEL          32       //Max number of elements in DMA chain 
+#define VME_MAXCHAIN            16       //Max number of internal DMA chains
+#define DMA_DESC_SIZE           (VME_MAXCHAINEL * VME_MAXCHAIN * sizeof(VME_DmaChain_t)) 
+
+enum 
+{
+  VME_LEVELISDISABLED = 0,
+  VME_INT_ROAK,
+  VME_INT_RORA
+};
+
+/*************/
+/*error codes*/
+/*************/
+
+//error codes
+enum
+{
+  VME_SUCCESS = 0,
+  VME_NOTKNOWN = (P_ID_VMERCC << 8) + 1,
+  VME_UNKNOWN,
+  VME_NOTOPEN,
+  VME_RANGE,
+  VME_BUSERROR,
+  VME_ALIGN,
+  VME_NOCHAINMEM,
+  VME_NOBUSERROR,
+  VME_TOOLONG,
+  VME_DMABUSY,          // 10
+  VME_TIMEOUT,
+  VME_FILE,
+  VME_NOMAP,
+  VME_NOSTATMAP,
+  VME_IRGBUSY,
+  VME_EIO,
+  VME_EFAULT,
+  VME_VIRT,
+  VME_REMAP,
+  VME_ENOSYS,		// 20
+  VME_NOSIZE,
+  VME_CMEM_FAIL,
+  VME_ERESTARTSYS,
+  VME_DMAERR,
+  VME_PCI_ERR,
+  VME_VME_ERR,
+  VME_PROTOCOL_ERR,
+  VME_NOT_EXECUTED,     
+  VME_MUNMAP,
+  VME_ILLREV,		// 30
+  VME_IOREMAP,
+  VME_REQIRQ,
+  VME_TOOMANYINT,
+  VME_TOOMANYHDL,
+  VME_INTUSED,
+  VME_ILLINTLEVEL,
+  VME_ILLINTTYPE,
+  VME_INTCONF,
+  VME_LVLDISABLED,
+  VME_LVLISNOTRORA,	// 40
+  VME_ILLINTHANDLE,
+  VME_INTBYSIGNAL,
+  VME_NOINTERRUPT,
+  VME_ENOMEM,           
+  VME_KMALLOC,
+  VME_BERRTBLFULL,
+  VME_BERRNOTFOUND,
+  VME_ERROR_FAIL,
+  VME_ILL_TO,
+  VME_NODOMEMEM,	// 50
+  VME_NO_CODE,
+  VME_UNKNOWN_BOARD,
+  VME_IO_FAIL,
+  VME_SYSFAILTBLFULL,
+  VME_SYSFAILTBLNOTLINKED,
+  VME_SYSFAILNOTLINKED,
+  VME_NOSTATMAP2,
+  VME_IOUNMAP,
+  VME_INTDISABLED,
+  VME_NOCRCSRMAP
+};
+
+
+/*************/
+/*ioctl codes*/
+/*************/
+enum 
+{
+  VMEMASTERMAP = 1,
+  VMEMASTERUNMAP,
+  VMEMASTERMAPDUMP,
+  VMEBERRREGISTERSIGNAL,
+  VMEBERRINFO,
+  VMESLAVEMAP,
+  VMESLAVEMAPDUMP,
+  VMESCSAFE,
+  VMEDMASTART,
+  VMEDMAPOLL,               //10
+  VMEDMADUMP,
+  VMELINK,
+  VMEINTENABLE,
+  VMEINTDISABLE,
+  VMEWAIT,
+  VMEREGISTERSIGNAL,
+  VMEINTERRUPTINFOGET,
+  VMEUNLINK,
+  VMETSTART,
+  VMETSTOP,                 //20
+  VMEUPDATE,
+  VMESYSFAILUNLINK,
+  VMESYSFAILWAIT,
+  VMESYSFAILREGISTERSIGNAL,
+  VMESYSFAILLINK,
+  VMESYSFAILREENABLE,
+  VMESYSFAILPOLL,
+  VMESYSFAILSET,
+  VMESYSFAILRESET,
+  VMESYSRST,                //30
+  VMETEST
+};
+
+
+/******************/
+/*type definitions*/
+/******************/
+typedef struct 
+{
+  u_int vmebus_address;
+  u_int window_size;
+  u_int address_modifier;
+  u_int options; 
+} VME_MasterMap_t;
+
+typedef struct 
+{
+  VME_MasterMap_t  in;
+  u_int            pci_address;
+  u_int            virt_address;  //for the user
+  u_int            kvirt_address; //for the kernel
+  u_int            used;
+}VME_MasterMapInt_t;
+
+typedef struct 
+{
+  u_int system_iobus_address;
+  u_int window_size;
+  u_int address_width;
+  u_int options;
+} VME_SlaveMap_t;
+
+typedef struct 
+{
+  VME_SlaveMap_t  in;
+  u_int           vme_address;
+  u_int           used;
+}VME_SlaveMapInt_t;
+
+typedef struct 
+{
+  u_int kvirt_address;
+  u_int offset;
+  u_int data;
+  u_int nbytes;
+  u_int rw;
+}VME_SingleCycle_t;
+
+typedef struct 
+{
+  u_int dctl;         //Universe chain descriptor
+  u_int dtbc;         //Universe chain descriptor
+  u_int dla;          //Universe chain descriptor
+  u_int reserved1;    //Universe chain descriptor
+  u_int dva;          //Universe chain descriptor
+  u_int reserved2;    //Universe chain descriptor
+  u_int dcpp;         //Universe chain descriptor
+  u_int reserved3;    //Universe chain descriptor
+  u_int ref;          //used by the library
+  u_int reserved4[7]; //the size of the whole structure has to be a multiple of 32 bytes
+} VME_DmaChain_t;
+
+typedef struct
+{
+  u_int handle;
+  u_int pid;  
+  u_int ctrl;
+  u_int counter;
+  u_int timeout;
+  u_int index;
+  int timeoutval;
+} VME_DMAhandle_t;
+
+typedef struct
+{
+  u_int nvectors;
+  u_int vector[VME_MAXINTERRUPT];    // use int internally
+  u_int level;			     // SAME for all vectors
+  u_int type;			     // SAME for all vectors
+} VME_IntHandle_t;
+
+typedef struct
+{
+  u_int level;
+  u_int type;
+} VME_IntEnable_t;
+
+typedef struct
+{
+  VME_IntHandle_t int_handle;
+  int timeout;
+  u_int level;
+  u_int type;
+  u_int vector;	// use int internally
+  u_int multiple;
+} VME_WaitInt_t;
+
+typedef struct
+{
+  VME_IntHandle_t int_handle;
+  int signum;
+} VME_RegSig_t;
+
+typedef struct
+{
+  u_int paddr;
+  u_int handle;
+} VME_DMAstart_t;
+
+typedef struct
+{
+  u_int irq_mode[9];
+} VME_Update_t;
+
+typedef u_int   VME_ErrorCode_t;
+
+typedef struct 
+{
+  u_int vmebus_address;
+  u_int system_iobus_address;
+  u_int size_requested;
+  u_int control_word;
+  u_int size_remaining;
+  u_int status_word;
+} VME_BlockTransferItem_t;
+
+typedef struct
+{
+  u_char vector;
+  u_int level;
+  u_int type;
+} VME_InterruptItem_t;
+
+typedef struct 
+{
+  u_char vector;
+  u_int level;
+  u_int type;
+  u_int multiple;
+} VME_InterruptInfo_t;
+
+typedef struct
+{
+  u_int vmebus_address;
+  u_int address_modifier;
+  u_int lword;
+  u_int iack;
+  u_int ds0;
+  u_int ds1;
+  u_int wr;
+  int  multiple;
+} VME_BusErrorInfo_t;
+
+/********/
+/*Macros*/
+/********/
+#ifdef __powerpc__
+  #define BSWAP(x) bswap(x)
+  #define SYNC __asm__("eieio")
+#endif
+
+#ifdef __i386__
+  #define BSWAP(x) (x)
+  #define SYNC
+#endif
+
+#endif
Index: /fact/drsdaq/VME/atlas/include/vme_rcc/vme_rcc_driver.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/vme_rcc/vme_rcc_driver.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/vme_rcc/vme_rcc_driver.h	(revision 9833)
@@ -0,0 +1,200 @@
+// $Id: vme_rcc_driver.h,v 1.13 2007/09/28 15:18:25 joos Exp $
+/************************************************************************/
+/*									*/
+/* File: vme_rcc_driver.h						*/
+/*									*/
+/* This is the private header file for RCC VMEbus driver		*/
+/*									*/
+/* 26. Oct. 01  MAJO  created						*/
+/* 02. Nov. 01  JOP   interrupts					*/
+/* 26. Nov. 01  JOP   bus errors					*/
+/* 17. Apr. 02  JOP   RORA						*/
+/*									*/
+/************ C 2005 - The software with that certain something *********/
+
+#ifndef _VME_RCC_DRIVER_H 
+#define _VME_RCC_DRIVER_H
+
+#include <linux/types.h>
+#include "vme_rcc_common.h"
+
+#define VME_VCTSIZE        256
+#define VME_DMADONESIZE    64      //max. number of completed DMA transactions
+#define MAX_PROC_TEXT_SIZE 0x10000 //The output of "more /proc/vme_rcc" must not generate more characters than that
+#define BAR0               0
+
+// I/O registers
+#define CMOSA              0x70
+#define CMOSD              0x71
+#define BID1               0x35
+#define BID2               0x36
+
+
+/********/
+/*Macros*/
+/********/
+#ifdef DRIVER_DEBUG
+  #define kdebug(x) {if (debug) printk x;}
+#else
+  #define kdebug(x)
+#endif
+
+#ifdef DRIVER_ERROR
+  #define kerror(x) {if (errorlog) printk x;}
+#else
+  #define kerror(x)
+#endif
+
+
+/******************/
+/*Type definitions*/
+/******************/
+typedef struct 
+{
+  u_int vbase;
+  u_int vtop;
+  u_int pbase;
+  u_int ptop;
+  u_int am;   
+  u_int enab; 
+  u_int wp;   
+  u_int rp;   
+  u_int space;
+  u_int options;
+} mstslvmap_t;
+
+typedef struct 
+{
+  mstslvmap_t master[8];
+  mstslvmap_t slave[8]; 
+} all_mstslvmap_t;  
+
+//  a link between an interrupt and a program is described by a LINK DESCRIPTOR
+typedef struct
+{
+  int                 pid;            // pid of linked process, 0 if not linked 
+  u_int               vct;            // vector #
+  u_int               lvl;            // level
+  u_int               type;           // type
+  u_int               pending;        // interrupt pending 
+  u_int               total_count;    // total # interrupts with this vector (after LINK)
+  VME_IntHandle_t*    group_ptr;      // pointer to the structure of vectors with same handle 
+  struct semaphore    sem;            // its semaphore
+  int                 sig;            // its signal
+} link_dsc_t;
+
+typedef struct
+{
+  link_dsc_t          link_dsc[VME_VCTSIZE];
+  struct semaphore    link_dsc_sem;
+} link_dsc_table_t;
+
+//  a link between BERR and a program is described by a BERR LINK DESCRIPTOR
+typedef struct
+{
+  int                 pid;            // pid of linked process, 0 if not linked 
+  int                 sig;            // its signal
+} berr_link_dsc_t;
+
+typedef struct
+{
+  berr_link_dsc_t     berr_link_dsc[VME_MAX_BERR_PROCS];
+  struct semaphore    proc_sem;
+} berr_proc_table_t;
+
+// holds info about the bus errors - filled by the BERR ISR
+typedef struct
+{
+  u_int    vmeadd;            // last latched VMEbus address 
+  u_int    am;                // and its AM code
+  u_int    iack;              // indicates BERR for IACK cycle
+  u_int    lword;             // Only for VP-100 & VP-110
+  u_int    ds0;               // Only for VP-100 & VP-110
+  u_int    ds1;               // Only for VP-100 & VP-110
+  u_int    wr;                // Only for VP-100 & VP-110
+  int	   multiple;
+  int      flag;
+} berr_dsc_t;
+  
+typedef struct
+{
+  int                pid;         // pid of linked process, 0 if not linked 
+  int                sig;         // the signal (0 if semaphore used)
+  u_int              num;         // number of interrupts received
+  struct semaphore   sem;         // external semaphore
+  struct semaphore   proc_sem;    // internal semaphore
+} sysfail_link_dsc_t; 
+
+typedef struct
+{
+  int                pid;         // pid of process owning the master mapping
+  u_int              vaddr;       // address returned by ioremap
+} master_map_dsc_t;
+
+typedef struct
+{
+  master_map_dsc_t map[VME_MAX_MASTERMAP];
+  struct semaphore sem;
+} master_map_t;
+
+typedef struct
+{       
+  u_int              berr_dsc_flag[VME_MAX_BERR_PROCS];
+  u_int              link_dsc_flag[VME_VCTSIZE];
+  u_int              sysfail_dsc_flag;
+  u_int              dma[VME_DMADONESIZE];
+  u_int              mastermap[VME_MAX_MASTERMAP];
+  VME_BusErrorInfo_t VME_BerrInfo;
+  VME_IntHandle_t    VME_int_handle2;
+  VME_IntEnable_t    VME_IntEnable;
+} private_data_t;
+
+typedef struct InterruptCounters_t 
+{
+  u_int acfail;
+  u_int sysfail;
+  u_int sw_int;
+  u_int sw_iack;
+  u_int verr;
+  u_int lerr;
+  u_int dma;
+  u_int virq[7];
+  u_int vown;
+} InterruptCounters_t;
+
+struct vme_proc_data_t
+{
+  char name[10];
+  char value[100];
+};
+ 
+
+/******************************/
+/*Standard function prototypes*/
+/******************************/
+static int vme_rcc_open(struct inode *ino, struct file *filep);
+static int vme_rcc_release(struct inode *ino, struct file *filep);
+static int vme_rcc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg);
+static int vme_rcc_mmap(struct file *file, struct vm_area_struct *vma);
+static int vme_rcc_write_procmem(struct file *file, const char *buffer, u_long count, void *data);
+static int vme_rcc_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data);
+static void vme_rcc_vmaClose(struct vm_area_struct *vma);
+
+/*****************************/
+/*Special function prototypes*/
+/*****************************/
+static int berr_check(u_int *addr, u_int *multi, u_int *am);
+static int cct_berrInt(void);
+static void fill_mstmap(u_int d1, u_int d2, u_int d3, u_int d4, mstslvmap_t *mstslvmap);
+static void fill_slvmap(u_int d1, u_int d2, u_int d3, u_int d4, mstslvmap_t *mstslvmap);
+static void vme_dmaTimeout(u_long arg);
+static void vme_intTimeout(u_long arg);
+static void vme_intSysfailTimeout(u_long arg);
+static void vme_dma_handler(void);
+static void vme_irq_handler(int level);
+static void init_cct_berr(void);
+static void mask_cct_berr(void);
+static void send_berr_signals(void);
+static void read_berr_capture(void);
+
+#endif
Index: /fact/drsdaq/VME/atlas/include/vme_rcc/vme_rcc_lib.h
===================================================================
--- /fact/drsdaq/VME/atlas/include/vme_rcc/vme_rcc_lib.h	(revision 9833)
+++ /fact/drsdaq/VME/atlas/include/vme_rcc/vme_rcc_lib.h	(revision 9833)
@@ -0,0 +1,117 @@
+// $Id: vme_rcc_lib.h,v 1.9 2007/11/08 07:56:44 joos Exp $
+/************************************************************************/
+/*									*/
+/* File: vme_rcc_lib.h							*/
+/*									*/
+/* This is the private header file for RCC VMEbus library		*/
+/*									*/
+/*  2. Nov. 01  MAJO  created						*/
+/*  2. Nov. 01  JOP   interrupts					*/
+/*									*/
+/************ C 2001 - The software with that certain something *********/
+
+#ifndef _VME_RCC_LIB_H 
+#define _VME_RCC_LIB_H
+
+#include <linux/types.h>
+#include "vme_rcc_common.h"
+
+/***************/
+/*error strings*/
+/***************/
+#define VME_SUCCESS_STR             "Function successfully executed"
+#define VME_NOTKNOWN_STR            "Input parameter has illegal value"
+#define VME_UNKNOWN_STR             "Unknown type of error"
+#define VME_NOTOPEN_STR             "The library has not yet been opened"
+#define VME_RANGE_STR               "Input parameter is out ofrange"
+#define VME_BUSERROR_STR            "VMEbus bus error received"
+#define VME_ALIGN_STR               "Misaligned address/size"
+#define VME_NOCHAINMEM_STR          "Not enough memory available for DMA chain"
+#define VME_NOBUSERROR_STR          "There was no bus error information"
+#define VME_TOOLONG_STR             "Overflow of internal chain"
+#define VME_DMABUSY_STR             "The DMA controller is busy"
+#define VME_TIMEOUT_STR             "The time-out has expired"
+#define VME_FILE_STR                "Error from file operation (open/close)"
+#define VME_NOMAP_STR               "All master mappings are in use"
+#define VME_NOSTATMAP_STR           "Static mapping does not fit parameters"
+#define VME_IRGBUSY_STR             "The interrupg generator is busy"
+#define VME_EIO_STR                 "Unix: I/O error"
+#define VME_EFAULT_STR              "Unix: Bad address"
+#define VME_VIRT_STR                "Failed to get user virtual address"
+#define VME_REMAP_STR               "Error from remap_page_range() kernel function"
+#define VME_ENOSYS_STR              "Unix: Function not implemented"
+#define VME_NOSIZE_STR              "A <size> parameter is zero"
+#define VME_CMEM_FAIL_STR           "Error from CMEM_RCC library"
+#define VME_ERESTARTSYS_STR         "Unix: driver got interrupted in down_interruptible"
+#define VME_DMAERR_STR              "Error in DMA transfer, see status words in transfer list"
+#define VME_PCI_ERR_STR             "Error on PCI"
+#define VME_VME_ERR_STR             "VMEbus BERR"
+#define VME_PROTOCOL_ERR_STR        "Protocol error"
+#define VME_NOT_EXECUTED_STR        "This transfer has not been executed"
+#define VME_MUNMAP_STR              "Error from call to munmap()"
+#define VME_ILLREV_STR              "Universe has wrong revision (too old)"
+#define VME_IOREMAP_STR             "Error from call to ioremap()"
+#define VME_REQIRQ_STR              "Error from call to request_irq()"
+#define VME_TOOMANYINT_STR          "Too many interrupt items"
+#define VME_TOOMANYHDL_STR          "Too many Interrupt handles"
+#define VME_INTUSED_STR             "Vector already in use"
+#define VME_ILLINTLEVEL_STR         "Illegal Interrupt Level"
+#define VME_ILLINTTYPE_STR          "Illegal Interrupt Type"
+#define VME_INTCONF_STR             "Illegal level, type combination"
+#define VME_LVLDISABLED_STR         "Level is disabled"
+#define VME_LVLISNOTRORA_STR        "Level is not RORA"
+#define VME_ILLINTHANDLE_STR        "Illegal interrupt handle"
+#define VME_INTBYSIGNAL_STR         "Interrupted by signal"
+#define VME_NOINTERRUPT_STR         "No pending interrupt found"
+#define VME_ENOMEM_STR              "No memory"
+#define VME_KMALLOC_STR             "Error in driver from call to kmalloc"
+#define VME_BERRTBLFULL_STR         "BERR process table full"
+#define VME_BERRNOTFOUND_STR        "Entry in BERR process table not found"
+#define VME_ILL_TO_STR              "Value for time_out is out of range"
+#define VME_NODOMEMEM_STR           "No morememory left in DMA done list"
+#define VME_NO_CODE_STR             "No error code available"
+#define VME_UNKNOWN_BOARD_STR       "Failed to determine board type"
+#define VME_IO_FAIL_STR             "Error from IO_RCC library"
+#define VME_NOCRCSRMAP_STR          "There is no mapping for CR/CSR space access"
+#define VME_SYSFAILTBLFULL_STR      "An other process has already linked the SYSFAIL interrupt"
+#define VME_SYSFAILTBLNOTLINKED_STR "The SYSFAIL interrupt is not linked by your process"
+#define VME_SYSFAILNOTLINKED_STR    "You have to link the SYSFAIL interrupt before you can register a signal"
+#define VME_NOSTATMAP2_STR          "All master maps are in use"
+#define VME_IOUNMAP_STR             "The kernel virtual address for the given master mapping could not be found"
+#define VME_INTDISABLED_STR         "You are trying to link to a disabled interrupt level. Check the status with vmeconfig"
+
+/***************************************/
+/*Various upper limits for arrays, etc.*/
+/***************************************/
+#define VME_MAX_SLAVEMAP         4        //Max number of slave mappings
+#define VME_DMA_MAX_BLOCK_SIZE   0x800000 //Max size of single DMA
+#define VME_MAX_INTHANDLE	 10	  // Max # interrupt handles
+
+/***************************************/
+/*Definitions to be used in the library*/
+/*do not modify                        */
+/***************************************/
+#define DEVICE                  "/dev/vme_rcc"
+#define DMA_CHAIN_END           1
+#define CRCSR_BASE              0
+#define CRCSR_SIZE              0x01000000
+#define CRCSR_OPT               0
+#define CRCSR_OFF               0x80000
+
+/********/
+/*Macros*/
+/********/
+#define ISOPEN {if(!is_open) return(VME_NOTOPEN);}
+#define PE(x) {printf("Error from vme_rcc library: %s\n",x);     break;}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/******************************/
+/* Internal service functions */
+/******************************/
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /fact/drsdaq/VME/atlas/lib/libcmem_rcc.a
===================================================================
--- /fact/drsdaq/VME/atlas/lib/libcmem_rcc.a	(revision 9833)
+++ /fact/drsdaq/VME/atlas/lib/libcmem_rcc.a	(revision 9833)
@@ -0,0 +1,1 @@
+!<arch>
Index: /fact/drsdaq/VME/atlas/lib/libvme_rcc.a
===================================================================
--- /fact/drsdaq/VME/atlas/lib/libvme_rcc.a	(revision 9833)
+++ /fact/drsdaq/VME/atlas/lib/libvme_rcc.a	(revision 9833)
@@ -0,0 +1,1 @@
+!<arch>
Index: /fact/drsdaq/drsdaq.cpp
===================================================================
--- /fact/drsdaq/drsdaq.cpp	(revision 9833)
+++ /fact/drsdaq/drsdaq.cpp	(revision 9833)
@@ -0,0 +1,111 @@
+/**************************************************************\
+
+  drsdaq
+
+  Main program for data acquisition, starts threads for console input.
+  
+  Original program by S. Commichau.
+  
+  Oliver Grimm, May 2010
+ 
+\**************************************************************/
+
+#define LOCKFILE "/tmp/CT3_DAQ_LOCK"
+
+#include <stdio.h>
+#include <signal.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "DAQReadout.h"
+
+// Function prototypes
+void CrashHandler(int);
+void ExitFunction();
+
+// ================
+//   Main program
+// ================
+//
+// Several unlikely system call failures are handled via throwing an exception.
+
+int main() {
+
+  char str[MAX_COM_SIZE], *Command;
+  int LockDescriptor;
+
+  // Assure only one instance of program runs (lock creator written to log file)
+  // Lock file deleted by destructor
+  if((LockDescriptor = open(LOCKFILE,O_WRONLY|O_CREAT|O_EXCL, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)) == -1) {
+    if(errno==EEXIST) {
+      printf("Error: Lock file already existing\n");
+      snprintf(str, sizeof(str), "paste %s -s -d ' '",LOCKFILE);
+      system(str);
+    }
+    else printf("Could not create lock file %s (%s)\n", LOCKFILE, strerror(errno));
+    exit(EXIT_FAILURE);
+  }
+  close(LockDescriptor);
+  sprintf(str,"echo Created >%s; date >>%s; echo by $USER@$HOSTNAME >>%s",LOCKFILE,LOCKFILE,LOCKFILE);
+  system(str);
+
+  // Readline library uses getc() (allows interruption by signal)
+  rl_getc_function = getc;
+
+  // writev() in DAQ thread needs to be able to write at least 3 chunks
+  if(IOV_MAX < 2) {
+	printf("Fatal error: IOV_MAX is less than 2, cannot use writev() to write event data.\n");
+	exit(EXIT_FAILURE);
+  }
+     
+  system("clear");
+  printf("\n*** DRS readout (built %s, %s, revision %s) *** \n\n",__DATE__, __TIME__, REVISION);
+
+  // Set exit function
+  atexit(&ExitFunction);
+
+  // Construct main instance (static ensures destructor is called with exit())
+  static DAQReadout M;
+
+  // Set signal handlers
+  signal(SIGILL, &CrashHandler);
+  signal(SIGABRT, &CrashHandler);
+  signal(SIGFPE, &CrashHandler);
+  signal(SIGSEGV, &CrashHandler);
+  signal(SIGBUS, &CrashHandler);
+
+  // Command loop
+  while (!M.ExitRequest) {
+    // Assemble prompt
+    if (M.NumBoards == 0) snprintf(M.Prompt,sizeof(M.Prompt),"\rDAQ> "); 
+    else if (M.FirstBoard == M.LastBoard) snprintf(M.Prompt,sizeof(M.Prompt),"\rDAQ|B%d> ",M.FirstBoard); 
+    else snprintf(M.Prompt,sizeof(M.Prompt),"\rDAQ|B%d-%d> ",M.FirstBoard,M.LastBoard); 
+
+    // Read Command
+    Command = readline(M.Prompt);
+    if (Command == NULL) continue;
+    if(strlen(Command)>0) add_history(Command);
+
+    // Process command
+	DimClient::sendCommand(SERVER_NAME"/Command", Command);
+    free(Command);
+  }
+}
+
+
+// Remove lock file before running default signal code
+void CrashHandler(int Signal) {
+
+  remove(LOCKFILE);
+  printf("Caught signal number %d. Removed lockfile and performing standard signal action. Good luck.\n",Signal);
+  signal(Signal, SIG_DFL);
+  raise(Signal);
+}
+
+// This function will be implicitly called by exit()
+void ExitFunction() {
+
+  if (remove(LOCKFILE) == -1) {
+    printf("Could not remove lock file %s (%s)\n", LOCKFILE, strerror(errno));
+  }
+}
