diff --git a/Makefile b/Makefile
index 60e6686..afe0ac4 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@ DIRS := $(DIRS) $(filter-out $(DIRS), configure)
DIRS := $(DIRS) $(filter-out $(DIRS), common)
DIRS := $(DIRS) $(filter-out $(DIRS), pciApp)
DIRS := $(DIRS) $(filter-out $(DIRS), vmeApp)
+DIRS := $(DIRS) $(filter-out $(DIRS), exploreApp)
DIRS := $(DIRS) $(filter-out $(DIRS), testApp)
# 3.14.10 style directory dependencies
@@ -20,6 +21,7 @@ iocBoot_DEPEND_DIRS += $(filter %App,$(DIRS))
pciApp_DEPEND_DIRS += common
vmeApp_DEPEND_DIRS += common
testApp_DEPEND_DIRS += pciApp
+exploreApp_DEPEND_DIRS += pciApp
include $(TOP)/configure/RULES_TOP
diff --git a/README.md b/README.md
index efadd72..a85a38d 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,6 @@ VCS sources
git clone https://github.com/epics-modules/devlib2.git
```
-This is file is generated from git revision aaf8baff70421f0549ee86f070c26834668f2144
+This is file is generated from git revision bc6c46921f5ae3cf367dac5ab22b15c8c05ce1a2
CI Build Status
diff --git a/documentation/Doxyfile b/documentation/Doxyfile
index fd1a276..90c19e5 100644
--- a/documentation/Doxyfile
+++ b/documentation/Doxyfile
@@ -31,7 +31,7 @@ PROJECT_NAME = devLib2
# This could be handy for archiving the generated documentation or
# if some version control system is used.
-PROJECT_NUMBER = 2.8
+PROJECT_NUMBER = 2.9
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
diff --git a/documentation/mainpage.h b/documentation/mainpage.h
index 8e4c5a7..a57d8d5 100644
--- a/documentation/mainpage.h
+++ b/documentation/mainpage.h
@@ -50,6 +50,17 @@ even those which lack an implementation.
@li @subpage pciusage
+@li @ref exploreapp
+
+@li @ref iocshpci
+
+@subsubsection explorepci Generic PCI driver
+
+The exploreApp @ref exploreapp is a generic EPICS driver intended to support
+development of custom PCI/PCIe devices.
+It provides a set of records which read/write to individual registers.
+Basic support for interrupts is also available (Linux only).
+
@subsection vmesec VME CSR
The VME64x library provides several functions for accessing the CSR/CR
@@ -63,6 +74,8 @@ definitions of standard registers, and functions to access them.
@li @ref vmecsrregs "CSR/CR Register Definitions"
+@li @ref iocshvme
+
@subsection mmiosec MMIO
The MMIO library provides an OS and CPU architecture independent way to
@@ -73,6 +86,16 @@ the width and order of accesses.
@section changelog Changelog
+@subsection ver29 2.9 (July 2017)
+
+@li pci: change devPCIFindSpec() to parse B:D.F as hex.
+ This is an incompatible change!
+@li pci: Fixups for vxWorks 5 (Dirk Zimoch)
+@li pci: Add missing offset bounds check to PCI iocsh functions
+@li vme: Various fixes for VME iocsh functions
+@li explore: Add exploreApp toolkit for PCI driver/hardware development (@ref exploreapp)
+@li linux: Add pci_generic_msi.c UIO driver to support devices with single vector MSI
+
@subsection ver28 2.8 (Sept. 2016)
@li Fixes an bug with epicsMMIO.h for some targets where a single read
@@ -515,4 +538,122 @@ The kernel source tree contains several example drivers.
*/
+/** @page exploreapp PCI driver/hardware development tool
+
+@section exploreintro Purpose
+
+The exploreApp support is intended as a tool to explore new PCI devices.
+This may be useful when preparing to write a dedicated driver and/or
+during development of PCI/PCIe device firmware.
+exploreApp allows a PCI register to be read and/or written an EPICS record.
+For example:
+
+@code
+record(longout, "pcitestout") {
+ field(DTYP, "Explore Write32 LSB")
+ field(OUT , "@8:0.0 bar=0 offset=0xc")
+}
+@endcode
+
+The record @b pcitestout will be connected to the PCI device 8:0.0 (bus 8, device 0, function 0) with the first BAR.
+A single write of 4 bytes is made when the record is processed.
+
+The following can be added to some @b xyzApp/src/Makefile to include exploreApp.
+
+@code
+PROD_IOC += myioc
+DBD += myioc.dbd
+myioc_DBD += exploreSupport.dbd
+myioc_LIBS += explorepci epicspci
+@endcode
+
+@section exploreopts Options
+
+@b INP/OUT link strings may contain the following components
+
+@li "bar=#" (default: 0)
+@li "offset=#" in bytes (default: 0)
+@li "mask=#" bit mask (default: 0 aka. no mask)
+@li "shift=#" in bits (default: 0)
+@li "step=#" in bytes (default: read size. eg Read32 defaults to step=4)
+@li "initread=1|0" bool (default: 1 for .OUT recordtypes, 0 otherwise)
+
+
+For record types: @b longout, @b bo, @b mbbo, @b mbboDirect, @b ao
+allowed @b DTYP are:
+
+@li Explore Write8
+@li Explore Write16 NAT
+@li Explore Write16 LSB
+@li Explore Write16 MSB
+@li Explore Write32 NAT
+@li Explore Write32 LSB
+@li Explore Write32 MSB
+
+For record types: @b longin, @b longout, @b bi, @b bo, @b mbbi, @b mbbo, @b mbbiDirect, @b mbboDirect, @b ai, @b ao
+
+@li Explore Read8
+@li Explore Read16 NAT
+@li Explore Read16 LSB
+@li Explore Read16 MSB
+@li Explore Read32 NAT
+@li Explore Read32 LSB
+@li Explore Read32 MSB
+
+The @b ao record type also accepts
+
+@li Explore WriteF32 LSB
+@li Explore WriteF32 MSB
+
+The @b ai record type also accepts
+
+@li Explore ReadF32 LSB
+@li Explore ReadF32 MSB
+
+The @b waveform record type accepts both integer Read and Write @b DTYP.
+The @b step= link option may be applied to change how the address counter is incremented.
+The default step size is the read size (eg. 4 for Read32).
+A step size of 0 will read the base address @b NELM times.
+
+@section exploreirq PCI Interrupt
+
+Limited support of PCI interrupts is available on Linux only.
+A @b longin record with DTYP="Explore IRQ Count" and SCAN="I/O Intr"
+will be processed each time an interrupt occurs.
+
+This requires that a UIO kernel module be installed.
+
+@section explorefrib FRIB Specific
+
+The DTYP="Explore FRIB Flash" support implements a FRIB specific protocol
+for accessing a SPI flash chip over PCI.
+The @b frib-flash.db file demonstrates use.
+
+*/
+
+/** @page iocsh IOC shell functions
+
+Several IOC shell functions are provided to access PCI and VME devices
+
+@section iocshpci IOCsh functions for PCI devices
+
+- devPCIShow() List PCI devices present
+- devLibPCIUse() Select PCI system access implementation
+- pcidiagset() Select device for read/write functions
+- pciwrite()
+- pciread()
+- pciconfread()
+
+To use, begin by calling pcidiagset() to select the device and BAR that subsequent
+read/write calls will operate on.
+
+@section iocshvme IOCsh functions for VME devices
+
+- vmeread()
+- vmewrite()
+- vmeirqattach()
+- vmeirq()
+
+*/
+
#endif // MAINPAGE_H
diff --git a/exploreApp/Db/Makefile b/exploreApp/Db/Makefile
new file mode 100644
index 0000000..903bf28
--- /dev/null
+++ b/exploreApp/Db/Makefile
@@ -0,0 +1,22 @@
+TOP=../..
+include $(TOP)/configure/CONFIG
+#----------------------------------------
+# ADD MACRO DEFINITIONS AFTER THIS LINE
+
+#----------------------------------------------------
+# Optimization of db files using dbst (DEFAULT: NO)
+#DB_OPT = YES
+
+#----------------------------------------------------
+# Create and install (or just install) into /db
+# databases, templates, substitutions like this
+DB += frib-flash.db
+
+#----------------------------------------------------
+# If .db template is not named *.template add
+# _template =
+
+include $(TOP)/configure/RULES
+#----------------------------------------
+# ADD RULES AFTER THIS LINE
+
diff --git a/exploreApp/Db/frib-flash.db b/exploreApp/Db/frib-flash.db
new file mode 100644
index 0000000..9351db8
--- /dev/null
+++ b/exploreApp/Db/frib-flash.db
@@ -0,0 +1,35 @@
+record(waveform, "$(P)bitfile") {
+ field(DTYP, "Explore FRIB Flash")
+ field(INP , "@$(DEV) pci_offset=$(offset=0x2004) flash_offset=$(location=0) flash_size=$(size=16777216)")
+ field(FTVL, "UCHAR")
+ field(NELM, "$(NELM=4194304)")
+}
+
+record(mbbi, "$(P)sts") {
+ field(DTYP, "Explore FRIB Flash")
+ field(INP , "@$(DEV) pci_offset=$(offset=0x2004) flash_offset=$(location=0)")
+ field(SCAN, "I/O Intr")
+ field(PINI, "YES")
+ field(ZRVL, "0")
+ field(ONVL, "1")
+ field(TWVL, "2")
+ field(THVL, "3")
+ field(FRVL, "4")
+ field(FVVL, "5")
+ field(ZRST, "Idle")
+ field(ONST, "Erase")
+ field(TWST, "Program")
+ field(THST, "Verify")
+ field(FRST, "Success")
+ field(FVST, "Failure")
+ field(FVSV, "MAJOR")
+}
+
+# Write 1 to start sequence
+# Write 0 to abort sequence
+record(longout, "$(P)ctrl") {
+ field(DTYP, "Explore FRIB Flash")
+ field(OUT , "@$(DEV) pci_offset=$(offset=0x2004) flash_offset=$(location=0)")
+ # set TPRO to 2 or 3 to enable debugging prints
+ # field(TPRO, "2")
+}
diff --git a/exploreApp/Makefile b/exploreApp/Makefile
new file mode 100644
index 0000000..10e0126
--- /dev/null
+++ b/exploreApp/Makefile
@@ -0,0 +1,8 @@
+TOP = ..
+include $(TOP)/configure/CONFIG
+DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*))
+DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*))
+DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*))
+DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*))
+include $(TOP)/configure/RULES_DIRS
+
diff --git a/exploreApp/README.md b/exploreApp/README.md
new file mode 100644
index 0000000..85cdd0e
--- /dev/null
+++ b/exploreApp/README.md
@@ -0,0 +1,129 @@
+Record recipies for PCI "explorer" IOC
+======================================
+
+INP/OUT field values
+--------------------
+
+All records with DTYP="Explore ..." should begin the INP or OUT string
+in one of the following formats.
+
+* "@BB:DD.FF"
+* "@slot=#"
+
+Where "BB:DD.FF" is a PCI geographic address (bus, device, function),
+and "slot=SN" gives a PCI slot number (when OS support is present).
+
+A space seperated list of the following may follow.
+
+* "bar=#" (default: 0)
+* "offset=#" in bytes (default: 0)
+* "mask=#" bit mask (default: 0 aka. no mask)
+* "shift=#" in bits (default: 0)
+* "step=#" in bytes (default: read size. eg Read32 defaults to step=4)
+* "initread=1|0" bool (default: 1 for .OUT recordtypes, 0 otherwise)
+
+```
+ field(INP , "@8:0.0 bar=1 offset=0x14")
+... or ...
+ field(OUT , "@8:0.0 bar=1 offset=0x14")
+```
+
+Selects device "8:0.0" BAR 1 starting with byte offset 0x14.
+
+Numbers prefixed with '0x' are hexidecimal. Numbers w/o a prefix are decimal.
+
+Optional for all input/read records
+-----------------------------------
+
+```
+ field(PINI, "YES")
+```
+
+Read once on IOC startup
+
+```
+ field(SCAN, "1 second")
+```
+
+Read periodically, ever 1 second. Other options are ".1 second", ".2 second",
+".5 second", "2 second", "5 second", and "10 second".
+
+
+Read a scalar value
+-------------------
+
+Reading a 32-bit register as an scalar integer in little endian byte order.
+Reads periodically at 1Hz.
+
+```
+record(longin, "pcitest0") {
+ field(DTYP, "Explore Read32 LSB")
+ field(INP , "@8:0.0 bar=0 offset=0")
+ field(SCAN, "1 second")
+}
+```
+
+Read an array of values
+-----------------------
+
+```
+record(waveform, "pcitest0_10") {
+ field(DTYP, "Explore Read32 LSB")
+ field(INP , "@8:0.0 bar=0 offset=8 step=4")
+ field(FTVL, "ULONG")
+ field(NELM, "16")
+ field(SCAN, "1 second")
+}
+```
+
+Makes 16 sequential reads of 4 bytes each.
+The first read is of address 8.
+
+The number of elements to read is determined by the NELM field.
+
+Write a scalar value
+--------------------
+
+```
+record(longout, "pcitestout") {
+ field(DTYP, "Explore Write32 LSB")
+ field(OUT , "@8:0.0 bar=0 offset=0xc")
+}
+```
+
+Make a single 4 byte write to address 0xc
+
+Bit-field access
+----------------
+
+A read-modify-write operation to address 0xc.
+
+```
+record(longout, "pcitestout") {
+ field(DTYP, "Explore Write32 LSB")
+ field(OUT , "@8:0.0 0 offset=0xc mask=0xff00 shift=8")
+}
+```
+
+Equivlant pseudo-code.
+
+```
+u32 newval = read()&~mask
+newval |= (VAL<>shift
+```
diff --git a/exploreApp/src/Makefile b/exploreApp/src/Makefile
new file mode 100644
index 0000000..1eb9fb6
--- /dev/null
+++ b/exploreApp/src/Makefile
@@ -0,0 +1,80 @@
+TOP=../..
+
+include $(TOP)/configure/CONFIG
+#----------------------------------------
+# ADD MACRO DEFINITIONS AFTER THIS LINE
+#=============================
+
+# explore app doesn't build with older vxworks (eg. tornado22)
+# don't build at all until someone asks
+LIBRARY_IOC_DEFAULT = explorepci
+LIBRARY_IOC_vxWorks = -nil-
+
+explorepci_SRCS += devexplore.cpp
+explorepci_SRCS += devexplore_irq.cpp
+explorepci_SRCS += devexplore_frib.cpp
+explorepci_SRCS += devexplore_util.cpp
+
+explorepci_LIBS += epicspci
+explorepci_LIBS += $(EPICS_BASE_IOC_LIBS)
+
+#=============================
+# Build the IOC application
+
+PROD_IOC_DEFAULT = explore
+PROD_IOC_vxWorks = -nil-
+# explore.dbd will be created and installed
+DBD += explore.dbd
+DBD += exploreSupport.dbd
+
+# explore.dbd will be made up from these files:
+explore_DBD += base.dbd
+explore_DBD += system.dbd
+explore_DBD += epicspci.dbd
+explore_DBD += exploreSupport.dbd
+
+# explore_registerRecordDeviceDriver.cpp derives from explore.dbd
+explore_SRCS += explore_registerRecordDeviceDriver.cpp
+
+explore_SRCS += exploreMain.cpp
+
+explore_LIBS += explorepci epicspci
+
+# Finally link to the EPICS Base libraries
+explore_LIBS += $(EPICS_BASE_IOC_LIBS)
+
+TESTPROD_HOST += testutil
+testutil_SRCS += testutil.cpp
+
+testutil_LIBS += explorepci epicspci
+testutil_LIBS += $(EPICS_BASE_IOC_LIBS)
+TESTS += testutil
+
+# if VER >= 3.15.3
+# do real IOC testing
+ifneq ($(EPICS_VERSION)$(EPICS_REVISION),314)
+ifeq ($(findstring $(EPICS_REVISION)$(EPICS_MODIFICATION),151 152),)
+TESTPROD_HOST += testexplore
+DBD += testexplore.dbd
+TESTS += testexplore
+
+testexplore_DBD += base.dbd
+testexplore_DBD += epicspci.dbd
+testexplore_DBD += exploreSupport.dbd
+
+testexplore_SRCS += testexplore.cpp
+testexplore_SRCS += testexplore_registerRecordDeviceDriver.cpp
+
+testexplore_LIBS += explorepci epicspci
+testexplore_LIBS += $(EPICS_BASE_IOC_LIBS)
+
+endif
+endif
+
+TESTSCRIPTS_HOST += $(TESTS:%=%.t)
+
+#===========================
+
+include $(TOP)/configure/RULES
+#----------------------------------------
+# ADD RULES AFTER THIS LINE
diff --git a/exploreApp/src/devexplore.cpp b/exploreApp/src/devexplore.cpp
new file mode 100644
index 0000000..26d96ea
--- /dev/null
+++ b/exploreApp/src/devexplore.cpp
@@ -0,0 +1,639 @@
+/*
+ * This software is Copyright by the Board of Trustees of Michigan
+ * State University (c) Copyright 2016.
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "devLibPCI.h"
+
+#define epicsExportSharedSymbols
+#include "devexplore.h"
+
+extern
+volatile void * const exploreTestBase;
+extern
+const epicsUInt32 exploreTestSize;
+
+
+static
+volatile epicsUInt32 exploreTestRegion[1024];
+
+volatile void * const exploreTestBase = (volatile void*)exploreTestRegion;
+const epicsUInt32 exploreTestSize = sizeof(exploreTestRegion);
+
+namespace {
+
+union punny32 {
+ epicsUInt32 ival;
+ epicsFloat32 fval;
+};
+
+// value conversion
+// cast different integer sizes using C rules
+// type pun int to/from float
+template
+struct castval { static TO op(FROM v) { return v; } };
+template
+struct castval { static epicsFloat32 op(FROM v) {punny32 P; P.ival = v; return P.fval;} };
+template
+struct castval { static TO op(epicsFloat32 v) {punny32 P; P.fval = v; return P.ival;} };
+
+struct priv {
+
+ epicsMutex lock;
+
+ std::string pciname;
+ unsigned bar;
+ // offset of first element within BAR
+ epicsUInt32 offset;
+ // step between elements (waveform only)
+ epicsInt32 step;
+
+ // size of a single value
+ unsigned valsize;
+ // endianness of value
+ enum ORD {
+ NAT,
+ BE,
+ LE
+ } ord;
+
+ unsigned vshift;
+ epicsUInt32 vmask;
+
+ volatile void *base;
+ epicsUInt32 barsize;
+
+ bool initread;
+
+ priv() :bar(0u), offset(0u), step(0), valsize(1), ord(NAT), vshift(0u), vmask(0u), base(0), initread(false) {}
+
+ template
+ VAL readraw(epicsUInt32 off=0) const
+ {
+ volatile char *addr = (volatile char*)base+offset+off;
+ epicsUInt32 OV = -1;
+ switch(valsize) {
+ case 1: OV = ioread8(addr); break;
+ case 2: switch(ord) {
+ case NAT: OV = nat_ioread16(addr); break;
+ case BE: OV = be_ioread16(addr); break;
+ case LE: OV = le_ioread16(addr); break;
+ }
+ break;
+ case 4: switch(ord) {
+ case NAT: OV = nat_ioread32(addr); break;
+ case BE: OV = be_ioread32(addr); break;
+ case LE: OV = le_ioread32(addr); break;
+ }
+ break;
+ }
+ return OV;
+ }
+
+ template
+ VAL read(epicsUInt32 off=0) const
+ {
+ epicsUInt32 OV(readraw(off));
+ if(vmask) OV &= vmask;
+ OV >>= vshift;
+ return OV;
+ }
+
+ template
+ unsigned readArray(VAL *val, unsigned count) const
+ {
+ epicsUInt32 addr = 0,
+ end = barsize-offset;
+ unsigned i;
+ for(i=0; i=0; i++, addr+=step)
+ {
+ epicsUInt32 OV = read(addr);
+ *val++ = castval::op(OV);
+ }
+ return i;
+ }
+
+ template
+ void write(VAL val, epicsUInt32 off=0)
+ {
+ volatile char *addr = (volatile char*)base+offset+off;
+ epicsUInt32 V = castval::op(val)<(off)&(~vmask);
+ }
+
+ switch(valsize) {
+ case 1: iowrite8(addr, V); break;
+ case 2: switch(ord) {
+ case NAT: nat_iowrite16(addr, V); break;
+ case BE: be_iowrite16(addr, V); break;
+ case LE: le_iowrite16(addr, V); break;
+ }
+ break;
+ case 4: switch(ord) {
+ case NAT: nat_iowrite32(addr, V); break;
+ case BE: be_iowrite32(addr, V); break;
+ case LE: le_iowrite32(addr, V); break;
+ }
+ break;
+ }
+ }
+
+ template
+ unsigned writeArray(const VAL *val, unsigned count)
+ {
+ epicsUInt32 addr = 0,
+ end = barsize-offset;
+
+ unsigned i;
+ for(i=0; i=0; i++, addr+=step)
+ {
+ write(*val++, addr);
+ }
+ return i;
+ }
+};
+
+static const epicsPCIID anypci[] = {
+ DEVPCI_DEVICE_VENDOR(DEVPCI_ANY_DEVICE, DEVPCI_ANY_VENDOR),
+ DEVPCI_END
+};
+
+priv *parseLink(dbCommon *prec, const DBEntry& ent, unsigned vsize, priv::ORD ord)
+{
+ std::auto_ptr pvt(new priv);
+ pvt->valsize = vsize;
+ pvt->step = vsize;
+ pvt->ord = ord;
+
+ DBLINK *link = ent.getDevLink();
+ if(link->type!=INST_IO)
+ throw std::logic_error("No INST_IO");
+
+ // auto read on initialization for output records
+ pvt->initread = strcmp(ent.pentry()->pflddes->name, "OUT")==0;
+
+ std::string linkstr(link->value.instio.string);
+
+ size_t sep = linkstr.find_first_of(" \t");
+ pvt->pciname = linkstr.substr(0, sep);
+
+ const epicsPCIDevice *pdev = NULL;
+ if(pvt->pciname!="test") {
+ if(devPCIFindSpec(anypci, pvt->pciname.c_str(), &pdev, 0))
+ throw std::runtime_error(SB()<name<<" Invalid PCI device "<pciname);
+ }
+
+ sep = linkstr.find_first_not_of(" \t", sep);
+
+ if(prec->tpro>1) {
+ std::cerr<name<<" linkstr='"<=send)
+ throw std::runtime_error(SB()<<"Expected '=' in '"<tpro>1) {
+ std::cerr<name<<" opt '"<bar = parseU32(optval);
+ } else if(optname=="offset") {
+ pvt->offset = parseU32(optval);
+ } else if(optname=="step") {
+ pvt->step = parseU32(optval);
+ } else if(optname=="mask") {
+ pvt->vmask = parseU32(optval);
+ } else if(optname=="shift") {
+ pvt->vshift = parseU32(optval);
+ } else if(optname=="initread") {
+ pvt->initread = parseU32(optval)!=0;
+ } else {
+ throw std::runtime_error(SB()<<"Unknown option '"<tpro>1) {
+ std::cerr<name<<" : bar="<bar
+ <<" offset="<offset
+ <<" step="<step
+ <<" mask="<vmask
+ <<" shift="<vshift
+ <<" size="<valsize
+ <<" ord="<<(int)pvt->ord
+ <<"\n";
+ }
+
+ if(pdev) {
+ if(devPCIToLocalAddr(pdev, pvt->bar, &pvt->base, 0))
+ throw std::runtime_error(SB()<name<<" Failed to map bar "<bar);
+ if(devPCIBarLen(pdev, pvt->bar, &pvt->barsize))
+ throw std::runtime_error(SB()<name<<" Failed to find size of bar "<bar);
+ } else {
+ // testing mode
+ pvt->base = exploreTestBase;
+ pvt->barsize = exploreTestSize;
+ }
+
+ if(pvt->offset>=pvt->barsize || pvt->offset+pvt->valsize>pvt->barsize)
+ throw std::runtime_error(SB()<name<<" offset "<offset<<" out of range");
+
+ return pvt.release();
+}
+
+template
+long explore_init_record(dbCommon *prec)
+{
+ try {
+ DBEntry ent(prec);
+ std::auto_ptr pvt(parseLink(prec, ent, SIZE, ord));
+
+ prec->dpvt = pvt.release();
+ return 0;
+ } catch(std::exception& e) {
+ std::cerr<name<<" Error in init_record "<dpvt) return 0; priv *pvt = static_cast(prec->dpvt); (void)pvt; try
+#define CATCH() catch(std::exception& e) { std::cerr<name<<" Error : "<
+long explore_read_int_val(REC *prec)
+{
+ TRY {
+ Guard G(pvt->lock);
+ prec->val = pvt->read();
+ if(prec->tpro>1) {
+ errlogPrintf("%s: read %08x -> VAL=%08x\n", prec->name, (unsigned)pvt->offset, (unsigned)prec->val);
+ }
+ return 0;
+ } CATCH()
+}
+
+template
+long explore_write_int_val(REC *prec)
+{
+ TRY {
+ Guard G(pvt->lock);
+ if(prec->tpro>1) {
+ errlogPrintf("%s: write %08x <- VAL=%08x\n", prec->name, (unsigned)pvt->offset, (unsigned)prec->val);
+ }
+ pvt->write(prec->val);
+ return 0;
+ } CATCH()
+}
+
+template
+long explore_init_record_int_val(REC *prec)
+{
+ long ret = explore_init_record((dbCommon*)prec);
+ priv *pvt = static_cast(prec->dpvt);
+ if(ret==0 && pvt->initread)
+ ret = explore_read_int_val(prec);
+ return ret;
+}
+
+// integer to/from RVAL
+
+template
+long explore_read_int_rval(REC *prec)
+{
+ TRY {
+ Guard G(pvt->lock);
+ prec->rval = pvt->read();
+ if(prec->tpro>1) {
+ errlogPrintf("%s: read %08x -> RVAL=%08x\n", prec->name, (unsigned)pvt->offset, (unsigned)prec->rval);
+ }
+ return 0;
+ } CATCH()
+}
+
+template
+long explore_write_int_rval(REC *prec)
+{
+ TRY {
+ Guard G(pvt->lock);
+ if(prec->tpro>1) {
+ errlogPrintf("%s: write %08x <- VAL=%08x\n", prec->name, (unsigned)pvt->offset, (unsigned)prec->rval);
+ }
+ pvt->write(prec->rval);
+ return 0;
+ } CATCH()
+}
+
+template
+long explore_init_record_int_rval(REC *prec)
+{
+ long ret = explore_init_record((dbCommon*)prec);
+ priv *pvt = static_cast(prec->dpvt);
+ if(ret==0 && pvt->initread)
+ ret = explore_read_int_rval(prec);
+ return ret;
+}
+
+// float to/from VAL
+
+template
+long explore_read_real_val(REC *prec)
+{
+
+ TRY {
+ punny32 pun;
+
+ epicsUInt32 ival;
+ {
+ Guard G(pvt->lock);
+ ival = pun.ival = pvt->read();
+ }
+
+ pun.fval += prec->roff;
+ if(prec->aslo) pun.fval *= prec->aslo;
+ pun.fval += prec->aoff;
+ if(prec->eslo) pun.fval *= prec->eslo;
+ pun.fval += prec->eoff;
+
+ prec->val = pun.fval;
+
+ if(prec->tpro>1) {
+ errlogPrintf("%s: read %08x -> %08x -> VAL=%g\n", prec->name, (unsigned)pvt->offset, (unsigned)ival, prec->val);
+ }
+
+ return 2;
+ } CATCH()
+}
+
+template
+long explore_write_real_val(REC *prec)
+{
+ TRY {
+ punny32 pun;
+ pun.fval = prec->val;
+
+ pun.fval -= prec->eoff;
+ if(prec->eslo) pun.fval /= prec->eslo;
+ pun.fval -= prec->aoff;
+ if(prec->aslo) pun.fval /= prec->aslo;
+ pun.fval -= prec->roff;
+
+ if(prec->tpro>1) {
+ errlogPrintf("%s: write %08x <- %08x <- VAL=%g\n", prec->name, (unsigned)pvt->offset, (unsigned)pun.ival, prec->val);
+ }
+
+ Guard G(pvt->lock);
+ pvt->write(pun.ival);
+
+ return 0;
+ } CATCH()
+}
+
+template
+long explore_init_record_real_val(REC *prec)
+{
+ long ret = explore_init_record((dbCommon*)prec);
+ priv *pvt = static_cast(prec->dpvt);
+ if(ret==0 && pvt->initread)
+ ret = explore_read_real_val(prec);
+ return ret;
+}
+
+// Read into Waveform
+
+long explore_read_wf(waveformRecord *prec)
+{
+ TRY {
+ Guard G(pvt->lock);
+ switch(prec->ftvl) {
+ case menuFtypeCHAR :
+ case menuFtypeUCHAR : prec->nord = pvt->readArray((epicsUInt8*) prec->bptr, prec->nelm); break;
+ case menuFtypeSHORT :
+ case menuFtypeUSHORT: prec->nord = pvt->readArray((epicsUInt16*) prec->bptr, prec->nelm); break;
+ case menuFtypeLONG :
+ case menuFtypeULONG : prec->nord = pvt->readArray((epicsUInt32*) prec->bptr, prec->nelm); break;
+ case menuFtypeFLOAT : prec->nord = pvt->readArray((epicsFloat32*)prec->bptr, prec->nelm); break;
+ default:
+ (void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM);
+ }
+
+ return 0;
+ } CATCH()
+}
+
+long explore_write_wf(waveformRecord *prec)
+{
+ TRY {
+ Guard G(pvt->lock);
+ unsigned nwritten = -1;
+ switch(prec->ftvl) {
+ case menuFtypeCHAR :
+ case menuFtypeUCHAR : nwritten = pvt->writeArray((epicsUInt8*) prec->bptr, prec->nord); break;
+ case menuFtypeSHORT :
+ case menuFtypeUSHORT: nwritten = pvt->writeArray((epicsUInt16*) prec->bptr, prec->nord); break;
+ case menuFtypeLONG :
+ case menuFtypeULONG : nwritten = pvt->writeArray((epicsUInt32*) prec->bptr, prec->nord); break;
+ case menuFtypeFLOAT : nwritten = pvt->writeArray((epicsFloat32*)prec->bptr, prec->nord); break;
+ default:
+ (void)recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
+ }
+ if(nwritten!=prec->nord)
+ (void)recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
+ return 0;
+ } CATCH()
+}
+
+template
+long explore_init_record_wf(waveformRecord *prec)
+{
+ long ret = explore_init_record((dbCommon*)prec);
+ priv *pvt = static_cast(prec->dpvt);
+ if(ret==0 && pvt->initread)
+ ret = explore_read_wf(prec);
+ return ret;
+}
+
+
+template
+struct dset6 {
+ long nsup;
+ long (*report)(int);
+ long (*init)(int);
+ long (*init_record)(REC*);
+ long (*get_ioint_info)(int dir, REC*, IOSCANPVT*);
+ long (*readwrite)(REC*);
+ long (*unused)();
+};
+
+} // namespace
+
+#define SUP(NAME, REC, OP, DIR, SIZE, END) static dset6 NAME = \
+ {6, NULL, NULL, &explore_init_record_##OP, NULL, &explore_##DIR##_##OP, NULL}; \
+ epicsExportAddress(dset, NAME)
+
+SUP(devExploreLiReadU8, longin, int_val, read, 1, priv::NAT);
+SUP(devExploreLiReadU16NAT, longin, int_val, read, 2, priv::NAT);
+SUP(devExploreLiReadU16LSB, longin, int_val, read, 2, priv::LE);
+SUP(devExploreLiReadU16MSB, longin, int_val, read, 2, priv::BE);
+SUP(devExploreLiReadU32NAT, longin, int_val, read, 4, priv::NAT);
+SUP(devExploreLiReadU32LSB, longin, int_val, read, 4, priv::LE);
+SUP(devExploreLiReadU32MSB, longin, int_val, read, 4, priv::BE);
+
+SUP(devExploreLoWriteU8, longout, int_val, write, 1, priv::NAT);
+SUP(devExploreLoWriteU16NAT, longout, int_val, write, 2, priv::NAT);
+SUP(devExploreLoWriteU16LSB, longout, int_val, write, 2, priv::LE);
+SUP(devExploreLoWriteU16MSB, longout, int_val, write, 2, priv::BE);
+SUP(devExploreLoWriteU32NAT, longout, int_val, write, 4, priv::NAT);
+SUP(devExploreLoWriteU32LSB, longout, int_val, write, 4, priv::LE);
+SUP(devExploreLoWriteU32MSB, longout, int_val, write, 4, priv::BE);
+
+SUP(devExploreBiReadU8, bi, int_rval, read, 1, priv::NAT);
+SUP(devExploreBiReadU16NAT, bi, int_rval, read, 2, priv::NAT);
+SUP(devExploreBiReadU16LSB, bi, int_rval, read, 2, priv::LE);
+SUP(devExploreBiReadU16MSB, bi, int_rval, read, 2, priv::BE);
+SUP(devExploreBiReadU32NAT, bi, int_rval, read, 4, priv::NAT);
+SUP(devExploreBiReadU32LSB, bi, int_rval, read, 4, priv::LE);
+SUP(devExploreBiReadU32MSB, bi, int_rval, read, 4, priv::BE);
+
+SUP(devExploreBoWriteU8, bo, int_rval, write, 1, priv::NAT);
+SUP(devExploreBoWriteU16NAT, bo, int_rval, write, 2, priv::NAT);
+SUP(devExploreBoWriteU16LSB, bo, int_rval, write, 2, priv::LE);
+SUP(devExploreBoWriteU16MSB, bo, int_rval, write, 2, priv::BE);
+SUP(devExploreBoWriteU32NAT, bo, int_rval, write, 4, priv::NAT);
+SUP(devExploreBoWriteU32LSB, bo, int_rval, write, 4, priv::LE);
+SUP(devExploreBoWriteU32MSB, bo, int_rval, write, 4, priv::BE);
+
+SUP(devExploreMbbiReadU8, mbbi, int_rval, read, 1, priv::NAT);
+SUP(devExploreMbbiReadU16NAT, mbbi, int_rval, read, 2, priv::NAT);
+SUP(devExploreMbbiReadU16LSB, mbbi, int_rval, read, 2, priv::LE);
+SUP(devExploreMbbiReadU16MSB, mbbi, int_rval, read, 2, priv::BE);
+SUP(devExploreMbbiReadU32NAT, mbbi, int_rval, read, 4, priv::NAT);
+SUP(devExploreMbbiReadU32LSB, mbbi, int_rval, read, 4, priv::LE);
+SUP(devExploreMbbiReadU32MSB, mbbi, int_rval, read, 4, priv::BE);
+
+SUP(devExploreMbboWriteU8, mbbo, int_rval, write, 1, priv::NAT);
+SUP(devExploreMbboWriteU16NAT, mbbo, int_rval, write, 2, priv::NAT);
+SUP(devExploreMbboWriteU16LSB, mbbo, int_rval, write, 2, priv::LE);
+SUP(devExploreMbboWriteU16MSB, mbbo, int_rval, write, 2, priv::BE);
+SUP(devExploreMbboWriteU32NAT, mbbo, int_rval, write, 4, priv::NAT);
+SUP(devExploreMbboWriteU32LSB, mbbo, int_rval, write, 4, priv::LE);
+SUP(devExploreMbboWriteU32MSB, mbbo, int_rval, write, 4, priv::BE);
+
+SUP(devExploreMbbiDirectReadU8, mbbiDirect, int_rval, read, 1, priv::NAT);
+SUP(devExploreMbbiDirectReadU16NAT, mbbiDirect, int_rval, read, 2, priv::NAT);
+SUP(devExploreMbbiDirectReadU16LSB, mbbiDirect, int_rval, read, 2, priv::LE);
+SUP(devExploreMbbiDirectReadU16MSB, mbbiDirect, int_rval, read, 2, priv::BE);
+SUP(devExploreMbbiDirectReadU32NAT, mbbiDirect, int_rval, read, 4, priv::NAT);
+SUP(devExploreMbbiDirectReadU32LSB, mbbiDirect, int_rval, read, 4, priv::LE);
+SUP(devExploreMbbiDirectReadU32MSB, mbbiDirect, int_rval, read, 4, priv::BE);
+
+SUP(devExploreMbboDirectWriteU8, mbboDirect, int_rval, write, 1, priv::NAT);
+SUP(devExploreMbboDirectWriteU16NAT, mbboDirect, int_rval, write, 2, priv::NAT);
+SUP(devExploreMbboDirectWriteU16LSB, mbboDirect, int_rval, write, 2, priv::LE);
+SUP(devExploreMbboDirectWriteU16MSB, mbboDirect, int_rval, write, 2, priv::BE);
+SUP(devExploreMbboDirectWriteU32NAT, mbboDirect, int_rval, write, 4, priv::NAT);
+SUP(devExploreMbboDirectWriteU32LSB, mbboDirect, int_rval, write, 4, priv::LE);
+SUP(devExploreMbboDirectWriteU32MSB, mbboDirect, int_rval, write, 4, priv::BE);
+
+SUP(devExploreAiReadU8, ai, int_rval, read, 1, priv::NAT);
+SUP(devExploreAiReadU16NAT, ai, int_rval, read, 2, priv::NAT);
+SUP(devExploreAiReadU16LSB, ai, int_rval, read, 2, priv::LE);
+SUP(devExploreAiReadU16MSB, ai, int_rval, read, 2, priv::BE);
+SUP(devExploreAiReadU32NAT, ai, int_rval, read, 4, priv::NAT);
+SUP(devExploreAiReadU32LSB, ai, int_rval, read, 4, priv::LE);
+SUP(devExploreAiReadU32MSB, ai, int_rval, read, 4, priv::BE);
+
+SUP(devExploreAoWriteU8, ao, int_rval, write, 1, priv::NAT);
+SUP(devExploreAoWriteU16NAT, ao, int_rval, write, 2, priv::NAT);
+SUP(devExploreAoWriteU16LSB, ao, int_rval, write, 2, priv::LE);
+SUP(devExploreAoWriteU16MSB, ao, int_rval, write, 2, priv::BE);
+SUP(devExploreAoWriteU32NAT, ao, int_rval, write, 4, priv::NAT);
+SUP(devExploreAoWriteU32LSB, ao, int_rval, write, 4, priv::LE);
+SUP(devExploreAoWriteU32MSB, ao, int_rval, write, 4, priv::BE);
+
+SUP(devExploreAiReadF32LSB, ai, real_val, read, 4, priv::LE);
+SUP(devExploreAiReadF32MSB, ai, real_val, read, 4, priv::BE);
+
+SUP(devExploreAoWriteF32LSB, ao, real_val, write, 4, priv::LE);
+SUP(devExploreAoWriteF32MSB, ao, real_val, write, 4, priv::BE);
+
+#undef SUP
+#define SUP(NAME, DIR, SIZE, END) static dset6 NAME = \
+ {6, NULL, NULL, &explore_init_record_wf, NULL, &explore_##DIR##_wf, NULL}; \
+ epicsExportAddress(dset, NAME)
+
+SUP(devExploreWfReadU8, read, 1, priv::NAT);
+SUP(devExploreWfReadU16NAT, read, 2, priv::NAT);
+SUP(devExploreWfReadU16LSB, read, 2, priv::LE);
+SUP(devExploreWfReadU16MSB, read, 2, priv::BE);
+SUP(devExploreWfReadU32NAT, read, 4, priv::NAT);
+SUP(devExploreWfReadU32LSB, read, 4, priv::LE);
+SUP(devExploreWfReadU32MSB, read, 4, priv::BE);
+
+SUP(devExploreWfWriteU8, write, 1, priv::NAT);
+SUP(devExploreWfWriteU16NAT, write, 2, priv::NAT);
+SUP(devExploreWfWriteU16LSB, write, 2, priv::LE);
+SUP(devExploreWfWriteU16MSB, write, 2, priv::BE);
+SUP(devExploreWfWriteU32NAT, write, 4, priv::NAT);
+SUP(devExploreWfWriteU32LSB, write, 4, priv::LE);
+SUP(devExploreWfWriteU32MSB, write, 4, priv::BE);
diff --git a/exploreApp/src/devexplore.h b/exploreApp/src/devexplore.h
new file mode 100644
index 0000000..1f33006
--- /dev/null
+++ b/exploreApp/src/devexplore.h
@@ -0,0 +1,75 @@
+#ifndef DEVEXPLORE_H
+#define DEVEXPLORE_H
+
+#ifdef __cplusplus
+
+#include