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 +#include +#include +#include + +#include +#include +#include +#include + +typedef epicsGuard Guard; +typedef epicsGuardRelease UnGuard; + +struct SB { + std::ostringstream strm; + operator std::string() { return strm.str(); } + template + SB& operator<<(const T& v) { + strm< strmap_t; + +void parseToMap(const std::string& inp, strmap_t& ret); + +epicsUInt32 parseU32(const std::string& s); + +class DBEntry { + DBENTRY entry; +public: + DBENTRY *pentry() const { return const_cast(&entry); } + DBEntry(dbCommon *prec) { + dbInitEntry(pdbbase, &entry); + if(dbFindRecord(&entry, prec->name)) + throw std::logic_error(SB()<<"getLink can't find record "<name); + } + DBEntry(const DBEntry& ent) { + dbCopyEntryContents(const_cast(&ent.entry), &entry); + } + DBEntry& operator=(const DBEntry& ent) { + dbFinishEntry(&entry); + dbCopyEntryContents(const_cast(&ent.entry), &entry); + return *this; + } + ~DBEntry() { + dbFinishEntry(&entry); + } + DBLINK *getDevLink() const { + if(dbFindField(pentry(), "INP") && dbFindField(pentry(), "OUT")) + throw std::logic_error(SB()<recordname<<" has no INP/OUT?!?!"); + if(entry.pflddes->field_type!=DBF_INLINK && + entry.pflddes->field_type!=DBF_OUTLINK) + throw std::logic_error(SB()<recordname<<" not devlink or IN/OUT?!?!"); + return (DBLINK*)entry.pfield; + } + const char *info(const char *name, const char *def) const + { + if(dbFindInfo(pentry(), name)) + return def; + else + return entry.pinfonode->string; + } +}; + + +#endif /* __cplusplus */ + +#endif // DEVEXPLORE_H diff --git a/exploreApp/src/devexplore_frib.cpp b/exploreApp/src/devexplore_frib.cpp new file mode 100644 index 0000000..78385c4 --- /dev/null +++ b/exploreApp/src/devexplore_frib.cpp @@ -0,0 +1,445 @@ +/* + * This software is Copyright by the Board of Trustees of Michigan + * State University (c) Copyright 2017. + */ +// FRIB specific operations + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devLibPCI.h" +#include "epicsMMIO.h" + +#define epicsExportSharedSymbols +#include "devexplore.h" + +namespace { + +static const epicsPCIID anypci[] = { + DEVPCI_DEVICE_VENDOR(DEVPCI_ANY_DEVICE, DEVPCI_ANY_VENDOR), + DEVPCI_END +}; + +// Read a static ID code 0xF1A54001 +// Write 0xC001D00D to enable SPI engine write mode, any other value disables +#define REG_LOCKOUT (0) +// Read ready for new command (mask 0x00000001) +// Write command (mask 0xff000000) and address (mask 0x00ffffff) +#define REG_CMDADDR (4) +// Input for FIFO of 4x 32-bit words used with the write command +#define REG_WDATA (8) +// Results of a read command +#define REG_RDATA (12) +#define REGMAX (16) + +struct flashProg : public epicsThreadRunable { + epicsMutex lock; + epicsEvent evt; + + const std::string pciname; + const unsigned bar; + const epicsPCIDevice *pdev; + + epicsUInt32 pci_offset, // PCI address of REG_LOCKOUT + flash_offset, // first address to write in flash chip + flash_size, // capacity of flash chip + flash_last; + + volatile char* pci_base; + + IOSCANPVT scan; + + // we cheat by read and write abort flag w/o locking + volatile unsigned abort; + + // set before worker starts, read w/o locking in worker + unsigned debug; + + enum state_t { + Idle, + Erase, + Program, + Verify, + Success, + Fail + } state; + + std::vector bitfile; + + std::auto_ptr worker; + + flashProg(const std::string& pname, unsigned bar, epicsUInt32 poffset, epicsUInt32 foffset) + :pciname(pname), bar(bar), pdev(NULL), pci_offset(poffset) + ,flash_offset(foffset), flash_size(0), flash_last(0) + ,abort(0) + ,state(Idle) + { + if(devPCIFindSpec(anypci, pciname.c_str(), &pdev, 0)) + throw std::runtime_error(SB()<<" Invalid PCI device "<2) + printf("Write %x <- %08x\n", pci_offset+offset, (unsigned)val); + le_iowrite32(pci_base+offset, val); + } + + epicsUInt32 read32(unsigned offset) { + epicsUInt32 ret = le_ioread32(pci_base+offset); + if(debug>2) + printf("Read %x -> %08x\n", pci_offset+offset, (unsigned)ret); + return ret; + } + + void wait_for_ready() { + bool ready; + do{ + ready = read32(REG_CMDADDR)&1; + } while(!ready && !abort); + } + + void wait_for_ready(double sleep) { + bool ready; + do{ + evt.wait(sleep); + ready = read32(REG_CMDADDR)&1; + } while(!ready && !abort); + } + + virtual void run() + { + epicsUInt32 lastaddr = 0; + Guard G(lock); + try { + if(bitfile.empty()) + throw std::runtime_error("No image"); + if(bitfile.size()+flash_offset>flash_size) + throw std::runtime_error("image size+offset exceeds capacity"); + if(flash_offset&0xffff) + throw std::runtime_error("offset not aligned to 64k"); + + if(debug>1) errlogPrintf("flash offset=%x size=%x\n", (unsigned)flash_offset, (unsigned)flash_size); + if(flash_offset>=0x1000000 || flash_size>0x1000000) + throw std::runtime_error("Flash addresses must be 24-bit"); + + std::vector file; + file.swap(bitfile); // consume image + + // zero pad to 16 byte boundary (we write only in 16 byte blocks) + file.resize( ((file.size()-1u)|0xf)+1u, 0 ); + assert((file.size()%16u==0)); + + const epicsUInt32 fstart = flash_offset, + fend = flash_offset + std::min(file.size(), (size_t)flash_size); + + epicsUInt32 id = read32(REG_LOCKOUT); + if(id!=0xF1A54001) + throw std::runtime_error(SB()<<"wrong id 0x"< %x\n", (unsigned)fstart, (unsigned)fend); + + // unlock write logic + write32(REG_LOCKOUT, 0xC001D00D); + + // erase in 64k blocks + { + state = Erase; + UnGuard U(G); + scanIoRequest(scan); + + for(lastaddr = fstart; lastaddr progs_t; +progs_t progs; + +long init_record_common(dbCommon *prec) +{ + try { + DBEntry ent(prec); + + DBLINK *plink = ent.getDevLink(); + assert(plink && plink->type==INST_IO); + + std::string lstr(plink->value.instio.string); + + size_t sep(lstr.find_first_of(" \t")); + if(sep>=lstr.size()) + throw std::runtime_error(SB()<<"Missing expected space in INP/OUT \""<second); + } else { + throw std::runtime_error(SB()<<"Missing required 'pci_offset' in \""<second); + } else { + throw std::runtime_error(SB()<<"Missing required 'flash_offset' in \""<second); + } + + if(prec->tpro) + fprintf(stderr, "%s: pcidev=%s offset=%x\n", prec->name, pciname.c_str(), (unsigned)pci_offset); + + flashProg *priv = NULL; + + for(progs_t::const_iterator it = progs.begin(), end = progs.end(); + it != end; ++it) + { + flashProg *F = *it; + if(F->pciname==pciname && F->bar==bar && F->pci_offset==pci_offset && F->flash_offset==flash_offset) { + priv = F; + break; + } + } + if(!priv) { + priv = new flashProg(pciname, bar, pci_offset, flash_offset); + progs.push_back(priv); + } + prec->dpvt = priv; + + if((it=args.find("flash_size"))!=args.end()) { + priv->flash_size = parseU32(it->second); + } + + } catch(std::exception& e) { + fprintf(stderr, "%s: init_record error: %s\n", prec->name, e.what()); + } + return 0; +} + +long load_bitfile_wf(waveformRecord *prec) +{ + flashProg *priv = static_cast(prec->dpvt); + if(!priv) { + (void)recGblSetSevr(prec, COMM_ALARM, INVALID_ALARM); + return S_dev_noDevice; + } + try { + std::vector buf(prec->nord); + const char *ibuf = (const char*)prec->bptr; + + std::copy(ibuf, ibuf+buf.size(), buf.begin()); + + Guard G(priv->lock); + + priv->bitfile.swap(buf); + + return 0; + } catch(std::exception& e) { + fprintf(stderr, "%s: load_bitfile_wf error: %s\n", prec->name, e.what()); + (void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); + return S_dev_noDevice; + } +} + +long startstop_lo(longoutRecord *prec) +{ + flashProg *priv = static_cast(prec->dpvt); + if(!priv) { + (void)recGblSetSevr(prec, COMM_ALARM, INVALID_ALARM); + return S_dev_noDevice; + } + try { + + Guard G(priv->lock); + + if(prec->val && !priv->worker.get()) { + if(prec->tpro>1) + errlogPrintf("%s: start programming\n", prec->name); + priv->debug = prec->tpro; + priv->worker.reset(new epicsThread(*priv, "flasher", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityScanLow+1)); + priv->worker->start(); + + } else if(!prec->val && priv->worker.get()) { + if(prec->tpro>1) + errlogPrintf("%s: abort programming\n", prec->name); + priv->abort = 1; + priv->evt.signal(); + } + + return 0; + } catch(std::exception& e) { + fprintf(stderr, "%s: load_bitfile_wf error: %s\n", prec->name, e.what()); + (void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); + return S_dev_noDevice; + } +} + +long status_mbbi(mbbiRecord *prec) +{ + flashProg *priv = static_cast(prec->dpvt); + if(!priv) { + (void)recGblSetSevr(prec, COMM_ALARM, INVALID_ALARM); + return S_dev_noDevice; + } + try { + + Guard G(priv->lock); + + prec->rval = (int)priv->state; + + return 0; + } catch(std::exception& e) { + fprintf(stderr, "%s: load_bitfile_wf error: %s\n", prec->name, e.what()); + (void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); + return S_dev_noDevice; + } +} + +long status_get_iointr_info(int dir, dbCommon* prec, IOSCANPVT* ppscan) +{ + flashProg *priv = static_cast(prec->dpvt); + if(priv) { + *ppscan = priv->scan; + } + return 0; +} + +struct dset6 { + dset base; + DEVSUPFUN read; + DEVSUPFUN junk; +}; +#define DSET(NAME, INITREC, IOINTR, RW) static dset6 NAME = {{6, NULL, NULL, (DEVSUPFUN)INITREC, (DEVSUPFUN)IOINTR}, (DEVSUPFUN)RW}; \ + epicsExportAddress(dset, NAME) + +} // namespace + +DSET(devExploreFRIBFlashWf, &init_record_common, NULL, &load_bitfile_wf); +DSET(devExploreFRIBFlashLo, &init_record_common, NULL, &startstop_lo); +DSET(devExploreFRIBFlashMbbi, &init_record_common, &status_get_iointr_info, &status_mbbi); diff --git a/exploreApp/src/devexplore_irq.cpp b/exploreApp/src/devexplore_irq.cpp new file mode 100644 index 0000000..791faf5 --- /dev/null +++ b/exploreApp/src/devexplore_irq.cpp @@ -0,0 +1,264 @@ +/* + * 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 + +#define epicsExportSharedSymbols +#include "devLibPCI.h" + +#ifdef EPICS_VERSION_INT +#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1) +# define USE_COMPLETE +#endif +#endif + +#ifndef __rtems__ +#define printk errlogPrintf +#endif + +namespace { + +typedef epicsGuard Guard; + +std::set irq_used; + +struct priv { + const epicsPCIDevice *dev; + epicsMutex lock; + IOSCANPVT scan; + unsigned wait_for; + bool queued, scan_queued; + unsigned errd; + epicsUInt32 irqs, lost; + +#ifndef USE_COMPLETE + CALLBACK waiters[NUM_CALLBACK_PRIORITIES]; +#endif + priv() :dev(NULL), wait_for(0), + queued(false), scan_queued(true), errd(0), + irqs(0), lost(0) { + scanIoInit(&scan); + } + + void doscan() { +#ifdef USE_COMPLETE + wait_for = scanIoRequest(scan); +#else + scanIoRequest(scan); + wait_for = (1<(raw); + try { + Guard G(pvt->lock); + if(pvt->wait_for) { + pvt->lost++; + pvt->queued = 1; + } else { + pvt->irqs++; + pvt->doscan(); + } + if(pvt->errd) + printk("Error in ISRFN %x:%x.%x Clears\n", + pvt->dev->bus, + pvt->dev->device, + pvt->dev->function); + pvt->errd = 0; + } catch(std::exception& e) { + if(!pvt->errd) + printk("Error in ISRFN %x:%x.%x: %s\n", + pvt->dev->bus, + pvt->dev->device, + pvt->dev->function, + e.what()); + pvt->errd = 1; + } +} + +static +#ifdef USE_COMPLETE +void irq_scan_complete(void *usr, IOSCANPVT scan, int prio) +{ + priv *pvt = static_cast(usr); +#else +void irq_scan_complete(CALLBACK* pcb) +{ + priv *pvt = (priv*)pcb->user; + int prio = pcb->priority; +#endif + try { + Guard G(pvt->lock); + if(pvt->wait_for==0) + errlogPrintf("Extra callback for %x:%x.%x\n", + pvt->dev->bus, + pvt->dev->device, + pvt->dev->function); + pvt->wait_for &= ~(1<scan_queued && pvt->wait_for==0 && pvt->queued) { + pvt->queued = 0; + pvt->doscan(); + } + } catch(std::exception& e) { + pvt->errd = 1; + errlogPrintf("Error in irq_scan_complete %x:%x.%x: %s\n", + pvt->dev->bus, + pvt->dev->device, + pvt->dev->function, + e.what()); + } +} + +static +void isr_stop(void *raw) +{ + priv *pvt = static_cast(raw); + devPCIDisconnectInterrupt(pvt->dev, &isrfn, raw); +} + +static +long init_record_li_irq(longinRecord *prec) +{ + try { + std::auto_ptr pvt(new priv); + + if(devPCIFindSpec(anypci, + prec->inp.value.instio.string, + &pvt->dev, + 0)) + throw std::runtime_error("Failed to match PCI device"); + + epicsUInt32 bdf = pvt->dev->bus<<16 + | pvt->dev->device<<8 + | pvt->dev->function; + + if(prec->tpro>1) + printf("%s: matched %x:%x.%x %s\n", + prec->name, + pvt->dev->bus, + pvt->dev->device, + pvt->dev->function, + pvt->dev->slot); + + if(irq_used.find(bdf)!=irq_used.end()) + throw std::runtime_error("IRQ already used by another record"); + + irq_used.insert(bdf); + + if(devPCIConnectInterrupt(pvt->dev, &isrfn, pvt.get(), 0)) + throw std::runtime_error("Failed to Connect IRQ"); + + if(devPCIEnableInterrupt(pvt->dev)) + throw std::runtime_error("Failed to Enable IRQ"); + +#ifdef USE_COMPLETE + scanIoSetComplete(pvt->scan, irq_scan_complete, pvt.get()); +#else + for(unsigned i=0; iwaiters[i]); + callbackSetCallback(irq_scan_complete, &pvt->waiters[i]); + callbackSetUser(pvt.get(), &pvt->waiters[i]); + } +#endif + + prec->dpvt = pvt.release(); + + epicsAtExit(isr_stop, prec->dpvt); + return 0; + + } catch(std::exception &e) { + printf("%s: init error: %s\n", prec->name, e.what()); + return EINVAL; + } +} + +static +long get_io_intr_irq(int dir, dbCommon* prec, IOSCANPVT* ppscan) +{ + priv *pvt = static_cast(prec->dpvt); + if (pvt) + *ppscan = pvt->scan; + return 0; +} + +static +long read_irq(longinRecord *prec) +{ + priv *pvt = static_cast(prec->dpvt); + if (pvt) { + Guard G(pvt->lock); + prec->val = pvt->irqs; + if (prec->tpro > 1 && pvt->lost) { + errlogPrintf("%s: lost %u IRQs\n", prec->name, (unsigned)pvt->lost); + pvt->lost = 0; + } + return 0; + } else { + (void)recGblSetSevr(prec, COMM_ALARM, INVALID_ALARM); + return EINVAL; + } +} + +} //namespace + +static struct dset5 { + dset base; + DEVSUPFUN read; +} devExploreLiIRQ = { + {5, NULL, NULL, + (DEVSUPFUN)&init_record_li_irq, + (DEVSUPFUN)&get_io_intr_irq, + }, + (DEVSUPFUN)&read_irq, +}; + +extern "C" { +epicsExportAddress(dset, devExploreLiIRQ); +} diff --git a/exploreApp/src/devexplore_util.cpp b/exploreApp/src/devexplore_util.cpp new file mode 100644 index 0000000..2453e44 --- /dev/null +++ b/exploreApp/src/devexplore_util.cpp @@ -0,0 +1,106 @@ + +#include + +#include + +#include +#include + +#define epicsExportSharedSymbols +#include "devexplore.h" + +void parseToMap(const std::string& inp, strmap_t& ret) +{ + ret.clear(); + + size_t sep = inp.find_first_not_of(" \t"); + + while(sep=send) + throw std::runtime_error(SB()<<"Expected '=' in '"< 0xffffffffULL) + if (value > 0xffffffffUL && value <= ~0xffffffffUL) + return S_stdlib_overflow; +#endif + + *to = (epicsUInt32) value; + return 0; +} +#endif /* EPICS_REVISION<=14 */ + + + +epicsUInt32 parseU32(const std::string& s) +{ + epicsUInt32 ret; + int err = epicsParseUInt32(s.c_str(), &ret, 0, NULL); + if(err) { + char msg[80]; + errSymLookup(err, msg, sizeof(msg)); + throw std::runtime_error(SB()<<"Error parsing '"< +#include +#include +#include +#include + +#include "epicsExit.h" +#include "epicsThread.h" +#include "iocsh.h" + +int main(int argc,char *argv[]) +{ + if(argc>=2) { + iocsh(argv[1]); + epicsThreadSleep(.2); + } + iocsh(NULL); + epicsExit(0); + return(0); +} diff --git a/exploreApp/src/exploreSupport.dbd b/exploreApp/src/exploreSupport.dbd new file mode 100644 index 0000000..9993abe --- /dev/null +++ b/exploreApp/src/exploreSupport.dbd @@ -0,0 +1,114 @@ +include "epicspci.dbd" + +# devexplore.cpp +device(longin, INST_IO, devExploreLiReadU8, "Explore Read8") +device(longin, INST_IO, devExploreLiReadU16NAT, "Explore Read16 NAT") +device(longin, INST_IO, devExploreLiReadU16LSB, "Explore Read16 LSB") +device(longin, INST_IO, devExploreLiReadU16MSB, "Explore Read16 MSB") +device(longin, INST_IO, devExploreLiReadU32NAT, "Explore Read32 NAT") +device(longin, INST_IO, devExploreLiReadU32LSB, "Explore Read32 LSB") +device(longin, INST_IO, devExploreLiReadU32MSB, "Explore Read32 MSB") + +device(longout,INST_IO, devExploreLoWriteU8, "Explore Write8") +device(longout,INST_IO, devExploreLoWriteU16NAT, "Explore Write16 NAT") +device(longout,INST_IO, devExploreLoWriteU16LSB, "Explore Write16 LSB") +device(longout,INST_IO, devExploreLoWriteU16MSB, "Explore Write16 MSB") +device(longout,INST_IO, devExploreLoWriteU32NAT, "Explore Write32 NAT") +device(longout,INST_IO, devExploreLoWriteU32LSB, "Explore Write32 LSB") +device(longout,INST_IO, devExploreLoWriteU32MSB, "Explore Write32 MSB") + +device(bi, INST_IO, devExploreBiReadU8, "Explore Read8") +device(bi, INST_IO, devExploreBiReadU16NAT, "Explore Read16 NAT") +device(bi, INST_IO, devExploreBiReadU16LSB, "Explore Read16 LSB") +device(bi, INST_IO, devExploreBiReadU16MSB, "Explore Read16 MSB") +device(bi, INST_IO, devExploreBiReadU32NAT, "Explore Read32 NAT") +device(bi, INST_IO, devExploreBiReadU32LSB, "Explore Read32 LSB") +device(bi, INST_IO, devExploreBiReadU32MSB, "Explore Read32 MSB") + +device(bo,INST_IO, devExploreBoWriteU8, "Explore Write8") +device(bo,INST_IO, devExploreBoWriteU16NAT, "Explore Write16 NAT") +device(bo,INST_IO, devExploreBoWriteU16LSB, "Explore Write16 LSB") +device(bo,INST_IO, devExploreBoWriteU16MSB, "Explore Write16 MSB") +device(bo,INST_IO, devExploreBoWriteU32NAT, "Explore Write32 NAT") +device(bo,INST_IO, devExploreBoWriteU32LSB, "Explore Write32 LSB") +device(bo,INST_IO, devExploreBoWriteU32MSB, "Explore Write32 MSB") + +device(mbbi, INST_IO, devExploreMbbiReadU8, "Explore Read8") +device(mbbi, INST_IO, devExploreMbbiReadU16NAT, "Explore Read16 NAT") +device(mbbi, INST_IO, devExploreMbbiReadU16LSB, "Explore Read16 LSB") +device(mbbi, INST_IO, devExploreMbbiReadU16MSB, "Explore Read16 MSB") +device(mbbi, INST_IO, devExploreMbbiReadU32NAT, "Explore Read32 NAT") +device(mbbi, INST_IO, devExploreMbbiReadU32LSB, "Explore Read32 LSB") +device(mbbi, INST_IO, devExploreMbbiReadU32MSB, "Explore Read32 MSB") + +device(mbbo,INST_IO, devExploreMbboWriteU8, "Explore Write8") +device(mbbo,INST_IO, devExploreMbboWriteU16NAT, "Explore Write16 NAT") +device(mbbo,INST_IO, devExploreMbboWriteU16LSB, "Explore Write16 LSB") +device(mbbo,INST_IO, devExploreMbboWriteU16MSB, "Explore Write16 MSB") +device(mbbo,INST_IO, devExploreMbboWriteU32NAT, "Explore Write32 NAT") +device(mbbo,INST_IO, devExploreMbboWriteU32LSB, "Explore Write32 LSB") +device(mbbo,INST_IO, devExploreMbboWriteU32MSB, "Explore Write32 MSB") + +device(mbbiDirect, INST_IO, devExploreMbbiDirectReadU8, "Explore Read8") +device(mbbiDirect, INST_IO, devExploreMbbiDirectReadU16NAT, "Explore Read16 NAT") +device(mbbiDirect, INST_IO, devExploreMbbiDirectReadU16LSB, "Explore Read16 LSB") +device(mbbiDirect, INST_IO, devExploreMbbiDirectReadU16MSB, "Explore Read16 MSB") +device(mbbiDirect, INST_IO, devExploreMbbiDirectReadU32NAT, "Explore Read32 NAT") +device(mbbiDirect, INST_IO, devExploreMbbiDirectReadU32LSB, "Explore Read32 LSB") +device(mbbiDirect, INST_IO, devExploreMbbiDirectReadU32MSB, "Explore Read32 MSB") + +device(mbboDirect,INST_IO, devExploreMbboDirectWriteU8, "Explore Write8") +device(mbboDirect,INST_IO, devExploreMbboDirectWriteU16NAT, "Explore Write16 NAT") +device(mbboDirect,INST_IO, devExploreMbboDirectWriteU16LSB, "Explore Write16 LSB") +device(mbboDirect,INST_IO, devExploreMbboDirectWriteU16MSB, "Explore Write16 MSB") +device(mbboDirect,INST_IO, devExploreMbboDirectWriteU32NAT, "Explore Write32 NAT") +device(mbboDirect,INST_IO, devExploreMbboDirectWriteU32LSB, "Explore Write32 LSB") +device(mbboDirect,INST_IO, devExploreMbboDirectWriteU32MSB, "Explore Write32 MSB") + +device(ai, INST_IO, devExploreAiReadU8, "Explore Read8") +device(ai, INST_IO, devExploreAiReadU16NAT, "Explore Read16 NAT") +device(ai, INST_IO, devExploreAiReadU16LSB, "Explore Read16 LSB") +device(ai, INST_IO, devExploreAiReadU16MSB, "Explore Read16 MSB") +device(ai, INST_IO, devExploreAiReadU32NAT, "Explore Read32 NAT") +device(ai, INST_IO, devExploreAiReadU32LSB, "Explore Read32 LSB") +device(ai, INST_IO, devExploreAiReadU32MSB, "Explore Read32 MSB") + +device(ao,INST_IO, devExploreAoWriteU8, "Explore Write8") +device(ao,INST_IO, devExploreAoWriteU16NAT, "Explore Write16 NAT") +device(ao,INST_IO, devExploreAoWriteU16LSB, "Explore Write16 LSB") +device(ao,INST_IO, devExploreAoWriteU16MSB, "Explore Write16 MSB") +device(ao,INST_IO, devExploreAoWriteU32NAT, "Explore Write32 NAT") +device(ao,INST_IO, devExploreAoWriteU32LSB, "Explore Write32 LSB") +device(ao,INST_IO, devExploreAoWriteU32MSB, "Explore Write32 MSB") + +device(ai, INST_IO, devExploreAiReadF32LSB, "Explore ReadF32 LSB") +device(ai, INST_IO, devExploreAiReadF32MSB, "Explore ReadF32 MSB") + +device(ao, INST_IO, devExploreAoWriteF32LSB, "Explore WriteF32 LSB") +device(ao, INST_IO, devExploreAoWriteF32MSB, "Explore WriteF32 MSB") + + +device(waveform, INST_IO, devExploreWfReadU8, "Explore Read8") +device(waveform, INST_IO, devExploreWfReadU16NAT, "Explore Read16 NAT") +device(waveform, INST_IO, devExploreWfReadU16LSB, "Explore Read16 LSB") +device(waveform, INST_IO, devExploreWfReadU16MSB, "Explore Read16 MSB") +device(waveform, INST_IO, devExploreWfReadU32NAT, "Explore Read32 NAT") +device(waveform, INST_IO, devExploreWfReadU32LSB, "Explore Read32 LSB") +device(waveform, INST_IO, devExploreWfReadU32MSB, "Explore Read32 MSB") + +device(waveform,INST_IO, devExploreWfWriteU8, "Explore Write8") +device(waveform,INST_IO, devExploreWfWriteU16NAT, "Explore Write16 NAT") +device(waveform,INST_IO, devExploreWfWriteU16LSB, "Explore Write16 LSB") +device(waveform,INST_IO, devExploreWfWriteU16MSB, "Explore Write16 MSB") +device(waveform,INST_IO, devExploreWfWriteU32NAT, "Explore Write32 NAT") +device(waveform,INST_IO, devExploreWfWriteU32LSB, "Explore Write32 LSB") +device(waveform,INST_IO, devExploreWfWriteU32MSB, "Explore Write32 MSB") + +# from devexplore_irq.cpp +device(longin, INST_IO, devExploreLiIRQ, "Explore IRQ Count") + + +# from devexplore_frib.cpp +device(waveform, INST_IO, devExploreFRIBFlashWf, "Explore FRIB Flash") +device(longout, INST_IO, devExploreFRIBFlashLo, "Explore FRIB Flash") +device(mbbi, INST_IO, devExploreFRIBFlashMbbi, "Explore FRIB Flash") diff --git a/exploreApp/src/testexplore.cpp b/exploreApp/src/testexplore.cpp new file mode 100644 index 0000000..e515f33 --- /dev/null +++ b/exploreApp/src/testexplore.cpp @@ -0,0 +1,329 @@ +/* + * 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 + +extern +volatile void * const exploreTestBase; +extern +const epicsUInt32 exploreTestSize; + +extern "C" +int testexplore_registerRecordDeviceDriver(dbBase *pbase); + +namespace { + +struct Channel { + dbChannel *chan; + explicit Channel(const char *name) + :chan(dbChannelCreate(name)) + { + if(!chan) + testAbort("No channel %s", name); + if(dbChannelOpen(chan)) + testAbort("Can't open channel %s", name); + } + ~Channel() { + dbChannelDelete(chan); + } + operator dbChannel*() const { return chan; } + void get_int32(std::vector& val) { + val.resize(dbChannelFinalElements(chan)); + long nReq = (long)val.size(); + if(dbChannelGetField(chan, DBF_ULONG, &val[0], NULL, &nReq, NULL)) + testAbort("get %s fails", dbChannelName(chan)); + val.resize(nReq); + } + void put_int32(const std::vector& val) { + if(dbChannelPutField(chan, DBF_ULONG, &val[0], val.size())) + testAbort("get %s fails", dbChannelName(chan)); + } +}; + +#define testEqual(A,B,msg) testOk((A)==(B), #A " (0x%x) == " #B " (0x%x) %s", (unsigned)(A), (unsigned)(B), msg) + +template +void testRead(const char *recname, unsigned offset, unsigned size, epicsUInt32 store, short dbf, V expect) +{ + if(offset>=exploreTestSize || offset+size>exploreTestSize) + testAbort("Invalid offset %x", offset); + { + volatile char * A= offset+(volatile char*)exploreTestBase; + epicsUInt32 T=store; + switch(size) { + case 1: + iowrite8(A, T); + break; + case 2: + be_iowrite16(A, T); + break; + case 4: + be_iowrite32(A, T); + break; + default: + testAbort("testRead size=%u", size); + } + } + + std::string proc(recname); + proc+=".PROC"; + testdbPutFieldOk(proc.c_str(), DBF_ULONG, 1); + testdbGetFieldEqual(recname, dbf, expect); +} + +void writeVal(unsigned offset, epicsUInt32 val) +{ + volatile char * A= offset+(volatile char*)exploreTestBase; + be_iowrite32(A, val); +} + +void testVal(unsigned offset, epicsUInt32 expect, const char *msg="") +{ + volatile char * A= offset+(volatile char*)exploreTestBase; + epicsUInt32 actual = be_ioread32(A); + testEqual(actual, expect, msg); +} + +template +void testWrite(const char *recname, unsigned offset, unsigned size, epicsUInt32 expect, short dbf, V store) +{ + if(offset>=exploreTestSize || offset+size>exploreTestSize) + testAbort("Invalid offset %x", offset); + + testdbPutFieldOk(recname, dbf, store); + epicsUInt32 actual = 0; + volatile char * A= offset+(volatile char*)exploreTestBase; + + switch(size) { + case 1: + actual = ioread8(A); + break; + case 2: + actual = be_ioread16(A); + break; + case 4: + actual = be_ioread32(A); + break; + default: + testAbort("testWrite size=%u", size); + } + testOk(actual==expect, "testWrite %s %08x == %08x", recname, (unsigned)actual, (unsigned)expect); +} + +void testInitial() +{ + testDiag("Read initial values"); + /* initial "register" values [0xf0, 0xf1, 0xf2, 0xf3, ...] */ + testdbGetFieldEqual("longin8", DBF_ULONG, 0xf1); + testdbGetFieldEqual("longin8_1", DBF_ULONG, 0xf1&0x8f); + testdbGetFieldEqual("longin8_2", DBF_ULONG, (0xf2&0xf0)>>4); + testdbGetFieldEqual("longin8_x", DBF_ULONG, 0); // initread=0 + + testdbGetFieldEqual("longin16", DBF_ULONG, 0xf2f3); + testdbGetFieldEqual("longin16_1",DBF_ULONG, 0xf3f2); + + testdbGetFieldEqual("longin32", DBF_ULONG, 0xf4f5f6f7); + testdbGetFieldEqual("longin32_1",DBF_ULONG, 0xf7f6f5f4); + + testdbGetFieldEqual("longout8", DBF_ULONG, 0xf0); + testdbGetFieldEqual("longout8_1",DBF_ULONG, 0xf); + + testdbGetFieldEqual("longout16", DBF_ULONG, 0xf2f3); + testdbGetFieldEqual("longout16_1",DBF_ULONG, 0xf3f2); + testdbGetFieldEqual("longout16_2",DBF_ULONG, 0x2f); + + testdbGetFieldEqual("longout32", DBF_ULONG, 0xf4f5f6f7); + testdbGetFieldEqual("longout32_1",DBF_ULONG, 0xf7f6f5f4); + + union { + epicsUInt32 ival; + epicsFloat32 fval; + } pun; + pun.ival = 0xf4f5f6f7; + testDiag("expected float val 0x%08x -> %g", (unsigned)pun.ival, pun.fval); + testdbGetFieldEqual("floatin32", DBF_FLOAT, pun.fval); + testdbGetFieldEqual("floatout32", DBF_FLOAT, pun.fval); + + { + std::vector val; + + Channel chan("wfin32"); + testDiag("initial value of %s", dbChannelName(chan.chan)); + + chan.get_int32(val); + testOk1(val.size()==2); + val.resize(2); + testEqual(val[0], 0xf4f5f6f7, ""); + testEqual(val[1], 0xf8f9fafb, ""); + } + { + std::vector val; + + Channel chan("wfin32_1"); + testDiag("initial value of %s", dbChannelName(chan.chan)); + + chan.get_int32(val); + testOk1(val.size()==2); + val.resize(2); + testEqual(val[0], 0xf4f5f6f7, ""); + testEqual(val[1], 0xf4f5f6f7, ""); + } + { + std::vector val; + + Channel chan("wfout32"); + testDiag("initial value of %s", dbChannelName(chan.chan)); + + chan.get_int32(val); + testOk1(val.size()==2); + val.resize(2); + testEqual(val[0], 0xf4f5f6f7, ""); + testEqual(val[1], 0xf8f9fafb, ""); + } + { + std::vector val; + + Channel chan("wfout32_1"); + testDiag("initial value of %s", dbChannelName(chan.chan)); + + chan.get_int32(val); + testOk1(val.size()==2); + val.resize(2); + testEqual(val[0], 0xf4f5f6f7, ""); + testEqual(val[1], 0xf4f5f6f7, ""); + } +} + +void testScalarRead() +{ + testDiag("Read scalar values"); + + testRead("longin8", 1, 1, 0x7c, DBF_ULONG, 0x7c); + testRead("longin8_1", 1, 1, 0x7c, DBF_ULONG, 0x7c&0x8f); + testRead("longin8_2", 2, 1, 0x7c, DBF_ULONG, 0x7); + + testRead("longin16", 2, 2, 0xabcd, DBF_ULONG, 0xabcd); + testRead("longin16_1", 2, 2, 0xabcd, DBF_ULONG, 0xcdab); + + testRead("longin32", 4, 4, 0x01020304, DBF_ULONG, 0x01020304); + testRead("longin32_1", 4, 4, 0x01020304, DBF_ULONG, 0x04030201); +} + +void testScalarWrite() +{ + testDiag("Write scalar values"); + + testWrite("longout8", 0, 1, 0xab, DBF_ULONG, 0xab); + // RMW assumes previous success + testWrite("longout8_1", 0, 1, 0x3b, DBF_ULONG, 3); + + testWrite("longout16", 2, 2, 0xabcd, DBF_ULONG, 0xabcd); + testWrite("longout16_1",2, 2, 0xabcd, DBF_ULONG, 0xcdab); + // RMW assumes previous success + testWrite("longout16_2",2, 2, 0xa12d, DBF_ULONG, 0x12); + + testWrite("longout32", 4, 4, 0x01020304, DBF_ULONG, 0x01020304); + testWrite("longout32_1",4, 4, 0x01020304, DBF_ULONG, 0x04030201); +} + +void testFloatRW() +{ + testDiag("Test read/write of float32 values"); + + union { + epicsUInt32 ival; + epicsFloat32 fval; + } pun; + pun.ival = 0x01040203; + + testRead("floatin32" , 4, 4, pun.ival, DBF_FLOAT, pun.fval); + + pun.ival = 0xdeadbeef; + testWrite("floatout32", 4, 4, pun.ival, DBF_FLOAT, pun.fval); +} + +void testWF() +{ + testDiag("read/write uint32 array"); + + std::vector val; + Channel wfin32("wfin32"), + wfin32_1("wfin32_1"), + wfout32("wfout32"), + wfout32_1("wfout32_1"); + + writeVal(4, 0x12345678); + writeVal(8, 0x9abcdef0); + + testDiag("read array step=4"); + testdbPutFieldOk("wfin32.PROC", DBF_LONG, 1); + wfin32.get_int32(val); + testEqual(val.size(), 2, ""); + val.resize(2); + testEqual(val[0], 0x12345678, ""); + testEqual(val[1], 0x9abcdef0, ""); + + testDiag("read array step=0"); + testdbPutFieldOk("wfin32_1.PROC", DBF_LONG, 1); + wfin32_1.get_int32(val); + testEqual(val.size(), 2, ""); + val.resize(2); + testEqual(val[0], 0x12345678, ""); + testEqual(val[1], 0x12345678, ""); + + testDiag("write array step=4"); + val.resize(2); + val[0] = 0xdeadbeef; + val[1] = 0x1badface; + wfout32.put_int32(val); + testVal(4, 0xdeadbeef); + testVal(8, 0x1badface); +} + +} // namespace + +MAIN(testexplore) +{ + testPlan(71); + + { + volatile char *base = (volatile char*)exploreTestBase; + for(epicsUInt32 i=0; i + +#include +#include + +#include "devexplore.h" + +namespace { + +void testParseLink() +{ + strmap_t ret; + + testDiag("Parse empty string"); + parseToMap("", ret); + testOk1(ret.empty()); + + testDiag("Parse whitespace string"); + parseToMap(" \t ", ret); + testOk1(ret.empty()); + + testDiag("Parse single key value"); + parseToMap(" K=V ", ret); + testOk1(ret.size()==1); + if(ret.find("K")!=ret.end()) + testOk(ret["K"]=="V", "ret[\"K\"] = %s", ret["K"].c_str()); + else + testFail("Missing key K"); + + testDiag("Parse two key value"); + parseToMap(" K=V XYZ=ABC ", ret); + testOk1(ret.size()==2); + if(ret.find("K")!=ret.end()) + testOk(ret["K"]=="V", "ret[\"K\"] = %s", ret["K"].c_str()); + else + testFail("Missing key K"); + if(ret.find("XYZ")!=ret.end()) + testOk(ret["XYZ"]=="ABC", "ret[\"XYZ\"] = %s", ret["XYZ"].c_str()); + else + testFail("Missing key XYZ"); +} + +} // namespace + +MAIN(testutil) +{ + testPlan(0); + try { + testParseLink(); + }catch(std::exception& e) { + testAbort("Unexpected c++ exception: %s", e.what()); + } + return testDone(); +} diff --git a/iocBoot/Makefile b/iocBoot/Makefile new file mode 100644 index 0000000..91e47d0 --- /dev/null +++ b/iocBoot/Makefile @@ -0,0 +1,6 @@ +TOP = .. +include $(TOP)/configure/CONFIG +DIRS += $(wildcard *ioc*) +DIRS += $(wildcard as*) +include $(CONFIG)/RULES_DIRS + diff --git a/iocBoot/iocdummy/Makefile b/iocBoot/iocdummy/Makefile new file mode 100644 index 0000000..79c4ce6 --- /dev/null +++ b/iocBoot/iocdummy/Makefile @@ -0,0 +1,5 @@ +TOP = ../.. +include $(TOP)/configure/CONFIG +ARCH = linux-x86_64 +TARGETS = envPaths +include $(TOP)/configure/RULES.ioc diff --git a/iocBoot/iocdummy/st.cmd b/iocBoot/iocdummy/st.cmd new file mode 100755 index 0000000..6557132 --- /dev/null +++ b/iocBoot/iocdummy/st.cmd @@ -0,0 +1 @@ +#!../../bin/linux-x86_64/explore diff --git a/iocBoot/iochvpanda/99-hv-panda.rules b/iocBoot/iochvpanda/99-hv-panda.rules new file mode 100644 index 0000000..ce6319c --- /dev/null +++ b/iocBoot/iochvpanda/99-hv-panda.rules @@ -0,0 +1,8 @@ +# Copy as /etc/udev/rules.d/99-hv-panda.rules +# Change USERNAME as approprate + +ACTION=="add", SUBSYSTEM=="pci", \ +ATTR{vendor}=="0x1172", ATTR{device}=="0x1234", \ +ATTR{subsystem_device}=="0x0000", ATTR{subsystem_vendor}=="0x0000", \ +RUN+="/bin/chown USERNAME %S%p/resource0", \ +RUN+="/bin/chmod 0600 %S%p/resource0" diff --git a/iocBoot/iochvpanda/Makefile b/iocBoot/iochvpanda/Makefile new file mode 100644 index 0000000..cec9156 --- /dev/null +++ b/iocBoot/iochvpanda/Makefile @@ -0,0 +1,5 @@ +TOP = ../.. +include $(TOP)/configure/CONFIG +ARCH = linux-x86 +TARGETS = envPaths +include $(TOP)/configure/RULES.ioc diff --git a/iocBoot/iochvpanda/hv-panda-chan.db b/iocBoot/iochvpanda/hv-panda-chan.db new file mode 100644 index 0000000..ce303e2 --- /dev/null +++ b/iocBoot/iochvpanda/hv-panda-chan.db @@ -0,0 +1,84 @@ + +record(bo, "$(P)$(CH)ENA_CMD") { + field(ZNAM, "Disabled") + field(ONAM, "Enabled") + field(OUT, "$(P)$(CH)ENA_ PP") + field(PINI, "YES") +} + +record(longout, "$(P)$(CH)ENA_") { + field(DTYP, "Explore PCI Write32") + field(OUT , "@$(DEV) 0 100 $(OFFSET)") +} + +record(bi, "$(P)$(CH)ENA_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ NPP MSI") + field(DTYP, "Explore PCI Read32") + field(INP , "@$(DEV) 0 110 $(OFFSET)") + field(MASK, "1") + field(ZNAM, "Disabled") + field(ONAM, "Enabled") + field(SCAN, ".1 second") +} + +record(ai, "$(P)$(CH)I_RD") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ NPP MSI") + field(DTYP, "Explore PCI Read32") + field(INP , "@$(DEV) 0 10c $(OFFSET)") + field(EGU , "A") + field(SCAN, ".1 second") +} + +record(ai, "$(P)$(CH)V_RD") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ NPP MSI") + field(DTYP, "Explore PCI Read32") + field(INP , "@$(DEV) 0 108 $(OFFSET)") + field(ASLO, "0.1") + field(EGU , "V") + field(SCAN, ".1 second") +} + +record(ao, "$(P)$(CH)V_CSET") { + field(DTYP, "Explore PCI Write32") + field(OUT , "@$(DEV) 0 104 $(OFFSET)") + field(ASLO, "0.1") + field(EGU , "V") + field(PINI, "YES") + field(HOPR, "4000") + field(DRVH, "4001") + field(DRVL, "0") + field(HIGH, "4000") + field(HSV, "INVALID") + field(IVOA, "Don't drive outputs") +} + +record(ao, "$(P)$(CH)RUP_CSET") { + field(DTYP, "Explore PCI Write32") + field(OUT , "@$(DEV) 0 114 $(OFFSET)") + field(EGU , "V/s") + field(RVAL , "500") + field(PINI, "YES") + field(HOPR, "700") + field(DRVH, "701") + field(DRVL, "0") + field(HIGH, "700") + field(HSV, "INVALID") + field(IVOA, "Don't drive outputs") +} + +record(ao, "$(P)$(CH)RDN_CSET") { + field(DTYP, "Explore PCI Write32") + field(OUT , "@$(DEV) 0 118 $(OFFSET)") + field(EGU , "V/s") + field(RVAL , "500") + field(PINI, "YES") + field(HOPR, "700") + field(DRVH, "701") + field(DRVL, "0") + field(HIGH, "700") + field(HSV, "INVALID") + field(IVOA, "Don't drive outputs") +} diff --git a/iocBoot/iochvpanda/hv-panda-spi.db b/iocBoot/iochvpanda/hv-panda-spi.db new file mode 100644 index 0000000..740dd76 --- /dev/null +++ b/iocBoot/iochvpanda/hv-panda-spi.db @@ -0,0 +1,205 @@ +# force "reset" by clearing SPI BUSY +record(bo, "$(P)SPI:RST_CMD") { + field(ZNAM, "Reset") + field(ONAM, "Reset") + field(FLNK, "$(P)SPI:STSCLR_") +} + +# Periodic scanning begins here +# 1 bit counter to select which +# SPI register to read in this + # cycle. +record(calcout, "$(P)SPI:SCAN:SEQ_") { + field(SCAN, "1 second") + field(CALC, "B:=(A+1)&1;B") + field(INPA, "$(P)SPI:SCAN:SEQ_ NPP") + field(DOPT, "Use OCAL") + field(OCAL, "B+1") + field(OUT, "$(P)SPI:SCAN:SEL_.SELN PP") +} + +record(fanout, "$(P)SPI:SCAN:SEL_") { + field(SELM, "Specified") + field(LNK1, "$(P)SPI:ILK:SCAN_") + field(LNK2, "$(P)SPI:OI:SCAN_") +} + +# Send command to read interlock status +record(longout, "$(P)SPI:ILK:SCAN_") { + field(VAL, "0x80000019") + field(OUT, "$(P)SPI:CMD_ PP") +} + +# Send command to read over current status +record(longout, "$(P)SPI:OI:SCAN_") { + field(VAL, "0x80000018") + field(OUT, "$(P)SPI:CMD_ PP") +} + +# SPI command register +# writes begin an operation +record(longout, "$(P)SPI:CMD_") { + field(DTYP, "Explore PCI Write32") + field(OUT, "@$(DEV) 0 2900") + # immediately poll the busy status + field(FLNK, "$(P)SPI:BUSY_RSTS") +} + +# when busy, periodically re-poll status when operation in progress +record(bi, "$(P)SPI:BUSY:POLL_") { + field(DISV, "0") # do nothing when BUSY==0 + field(SDIS, "$(P)SPI:BUSY_RSTS") + field(SCAN, ".1 second") + field(VAL , "0") + field(FLNK, "$(P)SPI:BUSY_RSTS") +} + +# Read busy bit from SPI status register +record(bi, "$(P)SPI:BUSY_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ NPP MSI") +# field(SCAN, ".1 second") + field(DTYP, "Explore PCI Read32") + field(INP, "@$(DEV) 0 2904") + field(MASK, "0x100") + field(ZNAM, "IDLE/BUSY") + field(ONAM, "DONE") + field(ZSV , "MINOR") + field(FLNK, "$(P)SPI:SCAN:CLR_") +} + +# Conditional (BUSY==DISV) +# When BUSY==0 do nothing +# When BUSY!=0 trigger clear of busy +# bit and read of data registers. +record(longout, "$(P)SPI:SCAN:CLR_") { + field(DISV, "0") + field(SDIS, "$(P)SPI:BUSY_RSTS") + field(VAL , "0") + field(OUT , "$(P)SPI:STSCLR_.PROC PP") + field(FLNK, "$(P)SPI:DONE:FOut_") +} + +# SPI status bit clear register +record(longout, "$(P)SPI:STSCLR_") { + field(DTYP, "Explore PCI Write32") + field(OMSL, "closed_loop") + field(DOL, "0x100") + field(OUT, "@$(DEV) 0 2908") + field(PINI, "YES") # force clear once on startup +} + +# trigger readout of SPI data register. +# we read all even though only one has updated +# since we don't track which command was last issued +record(fanout, "$(P)SPI:DONE:FOut_") { + field(LNK1, "$(P)CH0:ILK_RSTS") + field(LNK2, "$(P)CH0:OI_RSTS") +} + +record(bi, "$(P)CH0:ILK_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ CP MSI") + field(DTYP, "Explore PCI Read32") + field(INP, "@$(DEV) 0 3024") + field(MASK, "0x80") + field(ZNAM, "Operate") + field(ONAM, "Trip") + field(OSV , "MAJOR") + field(FLNK, "$(P)CH1:ILK_RSTS") +} + +record(bi, "$(P)CH1:ILK_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ CP MSI") + field(DTYP, "Explore PCI Read32") + field(INP, "@$(DEV) 0 3024") + field(MASK, "0x40") + field(ZNAM, "Operate") + field(ONAM, "Trip") + field(OSV , "MAJOR") + field(FLNK, "$(P)CH2:ILK_RSTS") +} + +record(bi, "$(P)CH2:ILK_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ CP MSI") + field(DTYP, "Explore PCI Read32") + field(INP, "@$(DEV) 0 3024") + field(MASK, "0x20") + field(ZNAM, "Operate") + field(ONAM, "Trip") + field(OSV , "MAJOR") + field(FLNK, "$(P)CH3:ILK_RSTS") +} + +record(bi, "$(P)CH3:ILK_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ CP MSI") + field(DTYP, "Explore PCI Read32") + field(INP, "@$(DEV) 0 3024") + field(MASK, "0x10") + field(ZNAM, "Operate") + field(ONAM, "Trip") + field(OSV , "MAJOR") + field(FLNK, "$(P)ILK_RSTS") +} + +record(bi, "$(P)ILK_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ CP MSI") + field(DTYP, "Explore PCI Read32") + field(INP, "@$(DEV) 0 3024") +# field(MASK, "0x01") + field(MASK, "0xff") + field(ZNAM, "Operate") + field(ONAM, "Trip") + field(OSV , "MAJOR") +} + +record(bi, "$(P)CH0:OI_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ CP MSI") + field(DTYP, "Explore PCI Read32") + field(INP, "@$(DEV) 0 3020") + field(MASK, "0x80") + field(ZNAM, "Operate") + field(ONAM, "Trip") + field(OSV , "MAJOR") + field(FLNK, "$(P)CH1:OI_RSTS") +} + +record(bi, "$(P)CH1:OI_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ CP MSI") + field(DTYP, "Explore PCI Read32") + field(INP, "@$(DEV) 0 3020") + field(MASK, "0x40") + field(ZNAM, "Operate") + field(ONAM, "Trip") + field(OSV , "MAJOR") + field(FLNK, "$(P)CH2:OI_RSTS") +} + +record(bi, "$(P)CH2:OI_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ CP MSI") + field(DTYP, "Explore PCI Read32") + field(INP, "@$(DEV) 0 3020") + field(MASK, "0x20") + field(ZNAM, "Operate") + field(ONAM, "Trip") + field(OSV , "MAJOR") + field(FLNK, "$(P)CH3:OI_RSTS") +} + +record(bi, "$(P)CH3:OI_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ CP MSI") + field(DTYP, "Explore PCI Read32") + field(INP, "@$(DEV) 0 3020") + field(MASK, "0x10") + field(ZNAM, "Operate") + field(ONAM, "Trip") + field(OSV , "MAJOR") +} diff --git a/iocBoot/iochvpanda/hv-panda.db b/iocBoot/iochvpanda/hv-panda.db new file mode 100644 index 0000000..aae9029 --- /dev/null +++ b/iocBoot/iochvpanda/hv-panda.db @@ -0,0 +1,86 @@ + +# ai +# VAL = (RVAL+ROFF)*ASLO+AOFF + +record(longin, "$(P)FWVER_RD") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ NPP MSI") + field(DTYP, "Explore PCI Read32") + field(INP , "@$(DEV) 0 0") + field(SCAN, "10 second") +} + +# watchdog counter +record(longin, "$(P)WD_RD") { + field(DTYP, "Explore PCI Read32") + field(INP , "@$(DEV) 0 4") + field(SCAN, ".1 second") +} + +record(calc, "$(P)WD:RATE_RD") { + field(INPA, "$(P)WD_RD NPP") + field(INPD, "4294967295") # assume rollover at 32-bits + field(CALC, "C:=(A-B)&D;B:=A;C") + field(SCAN, "1 second") + field(EGU , "cnt/s") + # observed rate ~= 1k + field(HIGH, "1500") + field(LOW , "500") + field(HSV , "MAJOR") + field(LSV , "MAJOR") + field(FLNK, "$(P)FAIL_") +} + +# This is used internally to fan out an INVALID +# alarm when the watchdog counter stops (maybe card removed) +record(bi, "$(P)FAIL_") { + field(DTYP, "Raw Soft Channel") + field(INP , "$(P)WD:RATE_RD.SEVR NPP") + field(ZNAM, "OK") + field(ONAM, "FAIL") + field(OSV , "INVALID") +} + +record(bi, "$(P)CH0:POL_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ NPP MSI") + field(DTYP, "Explore PCI Read32") + field(INP , "@$(DEV) 0 20") + field(SCAN, "10 second") + field(MASK, "0x8") + field(ZNAM, "Positive") + field(ONAM, "Negative") +} + +record(bi, "$(P)CH1:POL_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ NPP MSI") + field(DTYP, "Explore PCI Read32") + field(INP , "@$(DEV) 0 20") + field(SCAN, "10 second") + field(MASK, "0x4") + field(ZNAM, "Positive") + field(ONAM, "Negative") +} + +record(bi, "$(P)CH2:POL_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ NPP MSI") + field(DTYP, "Explore PCI Read32") + field(INP , "@$(DEV) 0 20") + field(SCAN, "10 second") + field(MASK, "0x2") + field(ZNAM, "Positive") + field(ONAM, "Negative") +} + +record(bi, "$(P)CH3:POL_RSTS") { + field(DISV, "-1") + field(SDIS, "$(P)FAIL_ NPP MSI") + field(DTYP, "Explore PCI Read32") + field(INP , "@$(DEV) 0 20") + field(SCAN, "10 second") + field(MASK, "0x1") + field(ZNAM, "Positive") + field(ONAM, "Negative") +} diff --git a/iocBoot/iochvpanda/panda.map b/iocBoot/iochvpanda/panda.map new file mode 100644 index 0000000..f700d09 --- /dev/null +++ b/iocBoot/iochvpanda/panda.map @@ -0,0 +1,82 @@ +# name number of elements address size bar width fracbits signed +VERSION 0x00000001 0x00000000 0x00000004 0x00000000 32 0 0 +# reads 0x00010004 +WATCHDOG 0x00000001 0x00000004 0x00000004 0x00000000 32 0 0 +# free running counter +STATUS 0x00000001 0x00000008 0x00000004 0x00000000 32 0 0 +CONTROL 0x00000001 0x00000010 0x00000004 0x00000000 32 0 0 + +CH_POLARITY 0x00000001 0x00000020 0x00000004 0x00000000 32 0 0 +# reads 0x3 +# one bit per channel, if set V and I values are negative + +CH1_CTRL 0x00000001 0x00000100 0x00000004 0x00000000 32 0 0 +# 0x1 enable control 0 - off, 1 - on +CH1_V_SET 0x00000001 0x00000104 0x00000004 0x00000000 32 0 1 +# units are 1/10. V +CH1_V_MON 0x00000001 0x00000108 0x00000004 0x00000000 32 0 1 +# units are 1/10. V +CH1_I_MON 0x00000001 0x0000010C 0x00000004 0x00000000 32 0 1 +# units are A +CH1_STATUS 0x00000001 0x00000110 0x00000004 0x00000000 32 0 0 +# 0x001 - enable status 0 - off, 1 - on +# 0x400 - ?? +CH1_RAMP_UP 0x00000001 0x00000114 0x00000004 0x00000000 32 0 0 +# units are V/s (default 500 V/s) +CH1_RAMP_DOWN 0x00000001 0x00000118 0x00000004 0x00000000 32 0 0 +# units are V/s (default 500 V/s) + +CH2_CTRL 0x00000001 0x00000120 0x00000004 0x00000000 32 0 0 +CH2_V_SET 0x00000001 0x00000124 0x00000004 0x00000000 32 0 1 +CH2_V_MON 0x00000001 0x00000128 0x00000004 0x00000000 32 0 1 +CH2_I_MON 0x00000001 0x0000012C 0x00000004 0x00000000 32 0 1 +CH2_STATUS 0x00000001 0x00000130 0x00000004 0x00000000 32 0 0 +CH2_RAMP_UP 0x00000001 0x00000134 0x00000004 0x00000000 32 0 0 +CH2_RAMP_DOWN 0x00000001 0x00000138 0x00000004 0x00000000 32 0 0 + +CH3_CTRL 0x00000001 0x00000140 0x00000004 0x00000000 32 0 0 +CH3_V_SET 0x00000001 0x00000144 0x00000004 0x00000000 32 0 1 +CH3_V_MON 0x00000001 0x00000148 0x00000004 0x00000000 32 0 1 +CH3_I_MON 0x00000001 0x0000014C 0x00000004 0x00000000 32 0 1 +CH3_STATUS 0x00000001 0x00000150 0x00000004 0x00000000 32 0 0 +CH3_RAMP_UP 0x00000001 0x00000154 0x00000004 0x00000000 32 0 0 +CH3_RAMP_DOWN 0x00000001 0x00000158 0x00000004 0x00000000 32 0 0 + +CH4_CTRL 0x00000001 0x00000160 0x00000004 0x00000000 32 0 0 +CH4_V_SET 0x00000001 0x00000164 0x00000004 0x00000000 32 0 1 +CH4_V_MON 0x00000001 0x00000168 0x00000004 0x00000000 32 0 1 +CH4_I_MON 0x00000001 0x0000016C 0x00000004 0x00000000 32 0 1 +CH4_STATUS 0x00000001 0x00000170 0x00000004 0x00000000 32 0 0 +CH4_RAMP_UP 0x00000001 0x00000174 0x00000004 0x00000000 32 0 0 +CH4_RAMP_DOWN 0x00000001 0x00000178 0x00000004 0x00000000 32 0 0 + + +SPI_CONTROL 0x00000001 0x00002900 0x00000004 0x00000000 32 0 0 +# write +# 0x80000018 - query overcurrent status +# 0x80000019 - query interlock status +SPI_STATUS 0x00000001 0x00002904 0x00000004 0x00000000 32 0 0 +# 0x100 - done +SPI_STATUS_CLEAR 0x00000001 0x00002908 0x00000004 0x00000000 32 0 0 +# 0x100 - done clean done +SPI_DATA_0 0x00000001 0x00003000 0x00000004 0x00000000 32 0 0 +SPI_DATA_1 0x00000001 0x00003004 0x00000004 0x00000000 32 0 0 +SPI_DATA_2 0x00000001 0x00003008 0x00000004 0x00000000 32 0 0 +SPI_DATA_3 0x00000001 0x0000300C 0x00000004 0x00000000 32 0 0 +SPI_DATA_4 0x00000001 0x00003010 0x00000004 0x00000000 32 0 0 +SPI_DATA_5 0x00000001 0x00003014 0x00000004 0x00000000 32 0 0 +SPI_DATA_6 0x00000001 0x00003018 0x00000004 0x00000000 32 0 0 +SPI_DATA_7 0x00000001 0x0000301C 0x00000004 0x00000000 32 0 0 +SPI_DATA_8 0x00000001 0x00003020 0x00000004 0x00000000 32 0 0 +# results of 0x80000018 +# 0x80 - ch 0 +# 0x40 - ch 1 +# 0x20 - ch 2 +# 0x10 - ch 3 +SPI_DATA_9 0x00000001 0x00003024 0x00000004 0x00000000 32 0 0 +# results of 0x80000019 +# 0x80 - ch 0 +# 0x40 - ch 1 +# 0x20 - ch 2 +# 0x10 - ch 3 +# 0x01 - global diff --git a/iocBoot/iochvpanda/st.cmd b/iocBoot/iochvpanda/st.cmd new file mode 100755 index 0000000..622e838 --- /dev/null +++ b/iocBoot/iochvpanda/st.cmd @@ -0,0 +1,16 @@ +#!../../bin/linux-x86_64/explore + + +dbLoadDatabase("../../dbd/explore.dbd",0,0) +explore_registerRecordDeviceDriver(pdbbase) + +epicsEnvSet("BASE","a:0.0") +epicsEnvSet("P","TST:PANDA:") +dbLoadRecords("hv-panda.db","DEV=$(BASE),P=$(P)") +dbLoadRecords("hv-panda-spi.db","DEV=$(BASE),P=$(P)") +dbLoadRecords("hv-panda-chan.db","DEV=$(BASE),OFFSET=0,P=$(P),CH=CH0:") +dbLoadRecords("hv-panda-chan.db","DEV=$(BASE),OFFSET=20,P=$(P),CH=CH1:") +dbLoadRecords("hv-panda-chan.db","DEV=$(BASE),OFFSET=40,P=$(P),CH=CH2:") +dbLoadRecords("hv-panda-chan.db","DEV=$(BASE),OFFSET=60,P=$(P),CH=CH3:") + +iocInit() diff --git a/iocBoot/iocpci/Makefile b/iocBoot/iocpci/Makefile new file mode 100644 index 0000000..79c4ce6 --- /dev/null +++ b/iocBoot/iocpci/Makefile @@ -0,0 +1,5 @@ +TOP = ../.. +include $(TOP)/configure/CONFIG +ARCH = linux-x86_64 +TARGETS = envPaths +include $(TOP)/configure/RULES.ioc diff --git a/iocBoot/iocpci/st.cmd b/iocBoot/iocpci/st.cmd new file mode 100755 index 0000000..da32c75 --- /dev/null +++ b/iocBoot/iocpci/st.cmd @@ -0,0 +1,15 @@ +#!../../bin/linux-x86_64/explore + +## You may have to change explore to something else +## everywhere it appears in this file + +#< envPaths + +## Register all support components +dbLoadDatabase("../../dbd/explore.dbd",0,0) +explore_registerRecordDeviceDriver(pdbbase) + +## Load record instances +##dbLoadRecords("../../db/explore.db","") + +iocInit() diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 0000000..a20f312 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1,9 @@ +*.o +*.ko +*.cmd +.tmp_versions +*.symvers +modules.order +*.mod.c + +devkern.* diff --git a/linux/Makefile b/linux/Makefile new file mode 100644 index 0000000..29de487 --- /dev/null +++ b/linux/Makefile @@ -0,0 +1,20 @@ + +ifneq ($(KERNELRELEASE),) + + obj-m := uio_pci_generic_msi.o + + uio_pci_generic_msi-objs := pci_generic_msi.o + +else + + KERNELDIR ?= /lib/modules/$(shell uname -r)/build + PWD := $(shell pwd) + +all: modules + +modules modules_install clean: + $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ + +.PHONY: all modules modules_install clean + +endif diff --git a/linux/pci_generic_msi.c b/linux/pci_generic_msi.c new file mode 100644 index 0000000..cc5ec7e --- /dev/null +++ b/linux/pci_generic_msi.c @@ -0,0 +1,208 @@ +/* + * This software is Copyright by the Board of Trustees of Michigan + * State University (c) Copyright 2016. + */ +/* Similar to uio_pci_generic.c in stock kernel, + * But uses PCI MSI instead of INT disable. + * This driver has no ID list and must be explicitly bound. + * + * # echo "8086 10f5" > /sys/bus/pci/drivers/pci_generic_msi/new_id + * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind + * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/pci_generic_msi/bind + * # ls -l /sys/bus/pci/devices/0000:00:19.0/driver + * .../0000:00:19.0/driver -> ../../../bus/pci/drivers/pci_generic_msi + */ + +#include +#include +#include +#include +#include +#include + +#ifndef VM_RESERVED +#define VM_RESERVED 0 +#endif + +#ifndef module_pci_driver +/* added in 3.4 */ +#define module_pci_driver(__pci_driver) \ + module_driver(__pci_driver, pci_register_driver, \ + pci_unregister_driver) +#endif + +#define DRV_NAME "pci_generic_msi" +#define DRV_VERSION "0.0" + +struct pci_generic_msi { + struct uio_info uio; + struct pci_dev *pdev; + unsigned maskable:1; +}; + +/** Linux 3.12 adds a size test when mapping UIO_MEM_PHYS ranges + * to fix an clear security issue. 7314e613d5ff9f0934f7a0f74ed7973b903315d1 + * + * Unfortunately this makes it impossible to map ranges less than a page, + * such as the control registers for the PLX bridges (128 bytes). + * A further change in b65502879556d041b45104c6a35abbbba28c8f2d + * prevents mapping of ranges which don't start on a page boundary, + * which is also true of the PLX chips (offset 0xc00 on my test system). + * + * This remains the case though the present (4.1 in May 2015). + * + * These patches have been applied to the Debian 3.2.0 kernel. + * + * The following is based uio_mmap_physical() from 3.2.0. + */ +static +int mmap_generic_msi(struct uio_info *info, struct vm_area_struct *vma) +{ + struct pci_dev *dev = info->priv; + int mi = vma->vm_pgoff; /* bounds check already done in uio_mmap() */ + + if (vma->vm_end - vma->vm_start > PAGE_ALIGN(info->mem[mi].size)) { + dev_err(&dev->dev, "mmap alignment/size test fails %lx %lx %u\n", + vma->vm_start, vma->vm_end, (unsigned)PAGE_ALIGN(info->mem[mi].size)); + return -EINVAL; + } + + vma->vm_flags |= VM_IO | VM_RESERVED; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + return remap_pfn_range(vma, + vma->vm_start, + info->mem[mi].addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} + +static +irqreturn_t +handle_generic_msi(int irq, struct uio_info *info) +{ + /*struct pci_generic_msi *priv = container_of(info, struct pci_generic_msi, uio);*/ + /*TODO: automatically mask this IRQ when it occurs? + if (priv->maskable) { + mask_msi_irq(); + } + */ + return IRQ_HANDLED; +} + +static +int control_generic_msi(struct uio_info *info, s32 onoff) +{ + /*struct pci_generic_msi *priv = container_of(info, struct pci_generic_msi, uio);*/ + /* (un)mask MSI */ + return 0; +} + +#define ERR(COND, LABEL, MSG) if(COND) { dev_err(&pdev->dev, DRV_NAME ": %s", MSG); goto LABEL; } + +static +const char *mem_names[IORESOURCE_MEM] = { + "BAR0", + "BAR1", + "BAR2", + "BAR3", + "BAR4", + "BAR5", +}; + +static int probe_generic_msi(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct pci_generic_msi *priv; + int err; + unsigned i; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + err = -ENOMEM; + } + priv->pdev = pdev; + + priv->uio.name = DRV_NAME; + priv->uio.version = DRV_VERSION; + priv->uio.priv = pdev; + priv->uio.mmap = mmap_generic_msi; + + pci_set_drvdata(pdev, priv); + + err = pci_enable_device(pdev); + ERR(err, err_free, "Can't enable device\n"); + + pci_set_master(pdev); + + err = pci_enable_msi(pdev); + ERR(err, err_disable_dev, "Device does not support MSI\n"); + + priv->uio.irq = pdev->irq; + priv->uio.handler = handle_generic_msi; + priv->uio.irqcontrol = control_generic_msi; + priv->uio.irq_flags = 0; + + for (i=0; iuio.mem[i].name = mem_names[i]; + + if ((flags & IORESOURCE_MEM) && size>0) + { + priv->uio.mem[i].addr = pci_resource_start(pdev, i); + priv->uio.mem[i].size = size; + priv->uio.mem[i].memtype = UIO_MEM_PHYS; + } else { + priv->uio.mem[i].size = 1; /* Otherwise UIO will stop searching... */ + priv->uio.mem[i].memtype = UIO_MEM_NONE; /* prevent mapping */ + } + } + + { + struct msi_desc *desc = irq_get_msi_desc(pdev->irq); + priv->maskable = desc ? desc->msi_attrib.maskbit : 0; + dev_info(&pdev->dev, "MSI is %smaskable\n", priv->maskable ? "" : "not "); + } + + err = uio_register_device(&pdev->dev, &priv->uio); + ERR(err, err_disable_msi, "Can't add UIO dev\n"); + + return 0; + +err_disable_msi: + pci_disable_msi(pdev); +err_disable_dev: + pci_disable_device(pdev); +err_free: + pci_set_drvdata(pdev, NULL); + kfree(priv); + return err; +} + +static void remove_generic_msi(struct pci_dev *pdev) +{ + struct uio_info *info = pci_get_drvdata(pdev); + struct pci_generic_msi *priv = container_of(info, struct pci_generic_msi, uio); + + uio_unregister_device(info); + pci_disable_msi(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + kfree(priv); +} + +static struct pci_driver pci_generic_msi_driver = { + .name = "pci_generic_msi", + .id_table = NULL, + .probe = probe_generic_msi, + .remove = remove_generic_msi, +}; + +module_pci_driver(pci_generic_msi_driver); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Davidsaver "); +MODULE_DESCRIPTION("UIO Generic driver for PCI devices supporting MSI"); diff --git a/pciApp/devLibPCI.c b/pciApp/devLibPCI.c index f87f0ac..1325ff8 100644 --- a/pciApp/devLibPCI.c +++ b/pciApp/devLibPCI.c @@ -18,9 +18,8 @@ #include #include -#include "devLibPCIImpl.h" - #define epicsExportSharedSymbols +#include "devLibPCIImpl.h" #include "devLibPCI.h" #ifndef CONTAINER @@ -35,6 +34,11 @@ # endif #endif +#if defined(vxWorks) && !defined(_WRS_VXWORKS_MAJOR) +/* vxWorks 5 has no strdup */ +#define strdup(x) ({char*s=malloc(strlen(x)+1);s?strcpy(s,x):s;}) +#endif + int devPCIDebug = 0; static ELLLIST pciDrivers; @@ -52,6 +56,7 @@ static int devPCIInit_result = 42; static void regInit(void* junk) { + (void)junk; pciDriversLock = epicsMutexMustCreate(); } @@ -144,6 +149,7 @@ const char* devLibPCIDriverName() static void devInit(void* junk) { + (void)junk; epicsThreadOnce(&devPCIReg_once, ®Init, NULL); epicsMutexMustLock(pciDriversLock); if(!pdevLibPCI && devLibPCIUse(NULL)) { @@ -230,13 +236,26 @@ int devPCIFindSpec( unsigned int opt ) { - int err; + int err = 0; struct bdfmatch find; memset(&find, 0, sizeof(find)); if(!found || !spec) return S_dev_badArgument; + /* When originally introduced in 2.8, devPCIFindSpec() parsed as decimal, + * which is confusing as BDF are usually shown in hex. + * Changed in 2.9 to parse as hex. + * TODO: remove this notice after 2.9 + */ + if(devPCIDebug>=0) { + static int shown=0; + if(!shown) { + fprintf(stderr, "Notice: devPCIFindSpec() expect B:D.F in hex\n"); + shown = 1; + } + } + /* parse the spec. string */ { char *save, *alloc, *tok; @@ -250,14 +269,14 @@ int devPCIFindSpec( { unsigned dom, bus, dev, func=0; - if(sscanf(tok, "%u:%u:%u.%u", &dom, &bus, &dev, &func)>=3) { + if(sscanf(tok, "%x:%x:%x.%x", &dom, &bus, &dev, &func)>=3) { find.matchaddr = 1; find.domain = dom; find.b = bus; find.d = dev; find.f = func; - } else if(sscanf(tok, "%u:%u.%u", &bus, &dev, &func)>=2) { + } else if(sscanf(tok, "%x:%x.%x", &bus, &dev, &func)>=2) { find.matchaddr = 1; find.domain = 0; find.b = bus; @@ -275,17 +294,24 @@ int devPCIFindSpec( } else if(sscanf(tok, "inst=%u", &dom)==1) { find.stopat = dom==0 ? 0 : dom-1; - } else { + } else if(strchr(tok, '=')!=NULL) { fprintf(stderr, "Ignoring unknown spec '%s'\n", tok); + + } else { + fprintf(stderr, "Error: invalid spec '%s'\n", tok); + err = S_dev_badArgument; } } free(alloc); } + if(err) + return err; + if(devPCIDebug>4) { if(find.matchaddr) - fprintf(stderr, " Match BDF %u:%u:%u.%u\n", + fprintf(stderr, " Match BDF %x:%x:%x.%x\n", find.domain, find.b, find.d, find.f); if(find.matchslot) fprintf(stderr, " Match slot %s\n", find.slot); @@ -484,7 +510,7 @@ devPCIShowMatch(int lvl, const char *spec, int vendor, int device) void devPCIShowDevice(int lvl, const epicsPCIDevice *dev) { - int i; + unsigned int i; printf("PCI %04x:%02x:%02x.%x IRQ %u\n" " vendor:device %04x:%04x rev %02x\n", diff --git a/pciApp/devLibPCI.h b/pciApp/devLibPCI.h index 5efa5c1..c6dcf90 100644 --- a/pciApp/devLibPCI.h +++ b/pciApp/devLibPCI.h @@ -114,7 +114,7 @@ typedef struct { unsigned int bus; unsigned int device; unsigned int function; - //!< Chassis slot "number" identifier (may not be a simple number) or DEVPCI_NO_SLOT if not supported + /*!< Chassis slot "number" identifier (may not be a simple number) or DEVPCI_NO_SLOT if not supported*/ const char* slot; struct PCIBar bar[6]; epicsUInt8 irq; diff --git a/pciApp/devLibPCIImpl.h b/pciApp/devLibPCIImpl.h index 0f30312..5a8092c 100644 --- a/pciApp/devLibPCIImpl.h +++ b/pciApp/devLibPCIImpl.h @@ -71,8 +71,8 @@ devLibPCIRegisterDriver2(devLibPCI*, size_t); #define devLibPCIRegisterDriver(TPTR) devLibPCIRegisterDriver2(TPTR, sizeof(*(TPTR))) -//! Register default implementation -//! Not needed by IOCs +/*! Register default implementation */ +/*! Not needed by IOCs */ void devLibPCIRegisterBaseDefault(void); #ifdef __cplusplus diff --git a/pciApp/devLibPCIStrings.c b/pciApp/devLibPCIStrings.c index e2ba84c..fdd5a61 100644 --- a/pciApp/devLibPCIStrings.c +++ b/pciApp/devLibPCIStrings.c @@ -173,7 +173,7 @@ const char* devPCIDeviceClassToString(int classId) while (m>n) { i = (n+m)>>1; - assert(i>=0 && i classId) m=i; @@ -181,7 +181,7 @@ const char* devPCIDeviceClassToString(int classId) if(i=0 && idir; ++curloc) { + int ret=-1; + char *devdir=NULL; devdir=allocPrintf(curloc->dir, osd->dev.domain, osd->dev.bus, osd->dev.device, osd->dev.function); if (!devdir) break; @@ -371,12 +371,12 @@ open_uio(struct osdPCIDevice* osd) } static int -open_res(struct osdPCIDevice *osd, int bar) +open_res(struct osdPCIDevice *osd, unsigned int bar) { int ret = 1; char *fname=NULL; - if ( bar < 0 || bar >= PCIBARCOUNT ) + if ( bar >= PCIBARCOUNT ) return ret; if ( osd->rfd[bar] >= 0 ) @@ -399,7 +399,7 @@ static void close_uio(struct osdPCIDevice* osd) { - int i; + unsigned int i; for(i=0; ibase[i]) continue; @@ -427,7 +427,6 @@ int linuxDevPCIInit(void) DIR* sysfsPci_dir=NULL; struct dirent* dir; - int i; osdPCIDevice *osd=NULL; pciLock = epicsMutexMustCreate(); int host_is_first = 0; @@ -451,6 +450,7 @@ int linuxDevPCIInit(void) int match; unsigned long long int start,stop,flags; char dname[80]; + unsigned int i; if (!dir->d_name || dir->d_name[0]=='.') continue; /* Skip invalid entries */ @@ -596,14 +596,14 @@ int linuxDevPCIInit(void) if(fscanf(fp, "%x:%x:%x", &dom, &B, &D)==3) { ELLNODE *cur; if(devPCIDebug>2) - fprintf(stderr, "found slot %s with %04u:%02u:%02u.*\n", dir->d_name, dom, B, D); + fprintf(stderr, "found slot %s with %04x:%02x:%02x.*\n", dir->d_name, dom, B, D); for(cur=ellFirst(&devices); cur; cur=ellNext(cur)) { osdPCIDevice *osd = CONTAINER(cur, osdPCIDevice, node); if(osd->dev.domain!=dom || osd->dev.bus!=B || osd->dev.device!=D) continue; if(osd->dev.slot==DEVPCI_NO_SLOT) { - osd->dev.slot = strdup(dir->d_name); // return NULL would mean slot remains unlabeled + osd->dev.slot = strdup(dir->d_name); /* return NULL would mean slot remains unlabeled */ } else { fprintf(stderr, "Duplicate slot address for %s\n", dir->d_name); } @@ -675,7 +675,7 @@ linuxDevPCIFindCB( const epicsPCIID *idlist, devPCISearchFn searchfn, void *arg, - unsigned int opt /* always 0 */ + unsigned int opt ) { int err=0, ret=0; @@ -683,6 +683,7 @@ linuxDevPCIFindCB( osdPCIDevice *curdev=NULL; const epicsPCIID *search; + (void) opt; if(!searchfn || !idlist) return S_dev_badArgument; @@ -695,7 +696,7 @@ linuxDevPCIFindCB( epicsMutexMustLock(curdev->devLock); if(devPCIDebug>1) - printf("Consider %d:%d.%d\n", curdev->dev.bus, curdev->dev.device, curdev->dev.function); + printf("Consider %x:%x.%x\n", curdev->dev.bus, curdev->dev.device, curdev->dev.function); for(search=idlist, i=0; search->device!=DEVPCI_LAST_DEVICE; search++, i++){ @@ -707,31 +708,36 @@ linuxDevPCIFindCB( } else if(search->vendor!=DEVPCI_ANY_VENDOR && search->vendor!=curdev->dev.id.vendor) { - if(devPCIDebug>1) printf(" %u mismatch vendor\n", i); + if(devPCIDebug>1) printf(" %u mismatch vendor %x %x\n", i, + (unsigned)search->vendor, (unsigned)curdev->dev.id.vendor); continue; } else if( search->sub_device!=DEVPCI_ANY_SUBDEVICE && search->sub_device!=curdev->dev.id.sub_device ) { - if(devPCIDebug>1) printf(" %u mismatch subdevice\n", i); + if(devPCIDebug>1) printf(" %u mismatch subdevice %x %x\n", i, + (unsigned)search->sub_device, (unsigned)curdev->dev.id.sub_device); continue; } else if( search->sub_vendor!=DEVPCI_ANY_SUBVENDOR && search->sub_vendor!=curdev->dev.id.sub_vendor ) { - if(devPCIDebug>1) printf(" %u mismatch subvendor\n", i); + if(devPCIDebug>1) printf(" %u mismatch subvendor %x %x\n", i, + (unsigned)search->sub_vendor, (unsigned)curdev->dev.id.sub_vendor); continue; } else if( search->pci_class!=DEVPCI_ANY_CLASS && search->pci_class!=curdev->dev.id.pci_class ) { - if(devPCIDebug>1) printf(" %u mismatch class\n", i); + if(devPCIDebug>1) printf(" %u mismatch class %x %x\n", i, + (unsigned)search->pci_class, (unsigned)curdev->dev.id.pci_class); continue; } else if( search->revision!=DEVPCI_ANY_REVISION && search->revision!=curdev->dev.id.revision ) { - if(devPCIDebug>1) printf(" %u mismatch revision\n", i); + if(devPCIDebug>1) printf(" %u mismatch revision %x %x\n", i, + search->revision, curdev->dev.id.revision); continue; } if(devPCIDebug>1) @@ -771,21 +777,20 @@ linuxDevPCIToLocalAddr( unsigned int opt ) { - int mapno,i; - int mapfd; - osdPCIDevice *osd=CONTAINER((epicsPCIDevice*)dev,osdPCIDevice,dev); epicsMutexMustLock(osd->devLock); if (open_res(osd, bar) && open_uio(osd)) { - fprintf(stderr, "Can neither open resource file nor uio file of PCI device %04x:%02x:%02x.%x BAR %i\n", + fprintf(stderr, "Can neither open resource file nor uio file of PCI device %04x:%02x:%02x.%x BAR %u\n", osd->dev.domain, osd->dev.bus, osd->dev.device, osd->dev.function, bar); epicsMutexUnlock(osd->devLock); return S_dev_addrMapFail; } if (!osd->base[bar]) { + int mapno; + int mapfd; if ( osd->dev.bar[bar].ioport ) { fprintf(stderr, "Failed to MMAP BAR %u of PCI device %04x:%02x:%02x.%x -- mapping of IOPORTS is not possible\n", bar, @@ -805,6 +810,7 @@ linuxDevPCIToLocalAddr( * valid mappings are only PCI memory regions. * Let's count them here */ + unsigned int i; for ( i=0; i<=bar; i++ ) { if ( osd->dev.bar[i].ioport ) { mapno--; @@ -867,6 +873,7 @@ int linuxDevPCIConnectInterrupt( osdISR *other, *isr=calloc(1,sizeof(osdISR)); int ret = S_dev_vecInstlFail; + (void) opt; if (!isr) return S_dev_noMemory; isr->fptr=pFunction; @@ -926,7 +933,7 @@ void isrThread(void* arg) { osdISR *isr=arg; osdPCIDevice *osd=isr->osd; - int interrupted=0, ret; + int interrupted=0; epicsInt32 event, next=0; const char* name; int isrflag; @@ -944,6 +951,7 @@ void isrThread(void* arg) isr->waiter_status = osdISRRunning; while (isr->waiter_status==osdISRRunning) { + int ret; epicsMutexUnlock(osd->devLock); /* The interrupted flag lets us check @@ -1090,7 +1098,7 @@ linuxDevPCIConfigAccess(const epicsPCIDevice *dev, unsigned offset, void *pArg, st = pread( osd->cfd, pArg, CFG_ACC_WIDTH(mode), offset ); } - if ( CFG_ACC_WIDTH(mode) != st ) { + if ( (ssize_t)CFG_ACC_WIDTH(mode) != st ) { if ( st < 0 ) fprintf(stderr, "devLibPCIOSD: Unable to %s %u bytes %s configuration space: %s\n", CFG_ACC_WRITE(mode) ? "write" : "read", @@ -1129,15 +1137,16 @@ linuxDevPCISwitchInterrupt(const epicsPCIDevice *dev, int level) } devLibPCI plinuxPCI = { - "native", - linuxDevPCIInit, linuxDevFinal, - linuxDevPCIFindCB, - linuxDevPCIToLocalAddr, - linuxDevPCIBarLen, - linuxDevPCIConnectInterrupt, - linuxDevPCIDisconnectInterrupt, - linuxDevPCIConfigAccess, - linuxDevPCISwitchInterrupt + .name = "native", + .pDevInit = linuxDevPCIInit, + .pDevFinal = linuxDevFinal, + .pDevPCIFind = linuxDevPCIFindCB, + .pDevPCIToLocalAddr = linuxDevPCIToLocalAddr, + .pDevPCIBarLen = linuxDevPCIBarLen, + .pDevPCIConnectInterrupt = linuxDevPCIConnectInterrupt, + .pDevPCIDisconnectInterrupt = linuxDevPCIDisconnectInterrupt, + .pDevPCIConfigAccess = linuxDevPCIConfigAccess, + .pDevPCISwitchInterrupt = linuxDevPCISwitchInterrupt, }; #include diff --git a/pciApp/os/vxWorks/devLibPCIOSD.c b/pciApp/os/vxWorks/devLibPCIOSD.c index 999b479..62be435 100644 --- a/pciApp/os/vxWorks/devLibPCIOSD.c +++ b/pciApp/os/vxWorks/devLibPCIOSD.c @@ -65,6 +65,9 @@ int dummyPciIntConnect ( VOIDFUNCPTR routine, int parameter) { + (void)vector; + (void)routine; + (void)parameter; return S_dev_vecInstlFail; } @@ -76,6 +79,8 @@ int dummyPciIntDisconnect ( VOIDFUNCPTR* vector, VOIDFUNCPTR routine) { + (void)vector; + (void)routine; return S_dev_intDisconnect; } @@ -88,6 +93,9 @@ int dummySysBusToLocalAdrs ( char* busAdrs, char** pLocalAdrs) { + (void)adrsSpace; + (void)busAdrs; + (void)pLocalAdrs; return -1; } @@ -158,6 +166,9 @@ int vxworksDevPCIConnectInterrupt( * is too small, just use the irq as the interrupt number. */ unsigned char irq = osd->dev.irq; + + (void)opt; + if (inumTableSize && (irq < inumTableSize)) irq = inumTable[(int)irq]; @@ -252,15 +263,16 @@ vxworksDevPCISwitchInterrupt(const epicsPCIDevice *dev, int level) } devLibPCI pvxworksPCI = { - "native", - vxworksDevPCIInit, NULL, - sharedDevPCIFindCB, - vxworksPCIToLocalAddr, - sharedDevPCIBarLen, - vxworksDevPCIConnectInterrupt, - vxworksDevPCIDisconnectInterrupt, - sharedDevPCIConfigAccess, - vxworksDevPCISwitchInterrupt + .name = "native", + .pDevInit = vxworksDevPCIInit, + .pDevFinal = NULL, + .pDevPCIFind = sharedDevPCIFindCB, + .pDevPCIToLocalAddr = vxworksPCIToLocalAddr, + .pDevPCIBarLen = sharedDevPCIBarLen, + .pDevPCIConnectInterrupt = vxworksDevPCIConnectInterrupt, + .pDevPCIDisconnectInterrupt = vxworksDevPCIDisconnectInterrupt, + .pDevPCIConfigAccess = sharedDevPCIConfigAccess, + .pDevPCISwitchInterrupt = vxworksDevPCISwitchInterrupt, }; #include diff --git a/pciApp/os/vxWorks/devLibPCIOSD.h b/pciApp/os/vxWorks/devLibPCIOSD.h index 318087e..b348337 100644 --- a/pciApp/os/vxWorks/devLibPCIOSD.h +++ b/pciApp/os/vxWorks/devLibPCIOSD.h @@ -16,7 +16,7 @@ */ #ifndef PCIUINT32 - typedef uint32_t PCIUINT32; + typedef UINT32 PCIUINT32; #endif #define pci_find_device pciFindDevice diff --git a/pciApp/osdPciShared.c b/pciApp/osdPciShared.c index 82884a6..a4854fb 100644 --- a/pciApp/osdPciShared.c +++ b/pciApp/osdPciShared.c @@ -35,7 +35,7 @@ static ELLLIST devices; int sharedDevPCIInit(void) { - int b, d, f, bar; + unsigned int b, d, f, bar; osdPCIDevice *next; uint8_t val8, header; PCIUINT32 val32; @@ -155,6 +155,7 @@ sharedDevPCIFindCB( osdPCIDevice *curdev=NULL; const epicsPCIID *search; + (void)opt; if (devPCIDebug>=1) errlogPrintf("sharedDevPCIFindCB\n"); @@ -219,6 +220,7 @@ sharedDevPCIToLocalAddr( ) { struct osdPCIDevice *osd=pcidev2osd(dev); + (void)opt; /* No locking since the base address is not changed * after the osdPCIDevice is created diff --git a/pciApp/pcish.c b/pciApp/pcish.c index be294d6..9101317 100644 --- a/pciApp/pcish.c +++ b/pciApp/pcish.c @@ -24,7 +24,7 @@ static volatile void *diagbase; static epicsUInt32 diaglen; struct bdf { - int b,d,f; + unsigned int b,d,f; const epicsPCIDevice *dev; }; @@ -52,7 +52,7 @@ void pcidiagset(int b, int d, int f, int bar, int vendor, int device, int exact) diagdev = NULL; diaglen = 0; - printf("Looking for %u:%u.%u\n", b, d, f); + printf("Looking for %x:%x.%x\n", b, d, f); if(vendor==0 && !exact) ids[0].vendor=DEVPCI_ANY_VENDOR; @@ -74,7 +74,7 @@ void pcidiagset(int b, int d, int f, int bar, int vendor, int device, int exact) return; } - printf("Mapping %u:%u.%u\n", loc.dev->bus, loc.dev->device, loc.dev->function); + printf("Mapping %x:%x.%x\n", loc.dev->bus, loc.dev->device, loc.dev->function); #if defined(linux) if(devPCIBarLen(loc.dev, bar, &len)) { @@ -90,15 +90,15 @@ void pcidiagset(int b, int d, int f, int bar, int vendor, int device, int exact) diagdev = loc.dev; diaglen=len; -#if defined(linux) - printf("BAR %u from 0x%08lx for %u bytes\n",bar, (unsigned long)diagbase, diaglen); +#if defined(__linux__) + printf("BAR %d from %p for %u bytes\n",bar, (void*)diagbase, (unsigned)diaglen); #else - printf("BAR %u from 0x%08lx\n",bar, (unsigned long)diagbase); + printf("BAR %d from %p\n",bar, (void*)diagbase); #endif } -static int check_args(int dmod, int offset, int count) +static int check_args(int dmod, unsigned int offset, unsigned int count) { switch(dmod){ case 8: @@ -109,13 +109,18 @@ static int check_args(int dmod, int offset, int count) printf("Invalid data width %d\n",dmod); return 1; } + + if(offset>=diaglen || offset+count>diaglen) { + printf("Invalid offset and/or count\n"); + return 1; + } return 0; } void pciwrite(int dmod, int offset, int value) { epicsUInt32 tval = value; - volatile char* dptr = diagbase + offset; + volatile char* dptr = offset + (volatile char*)diagbase; if(!diagbase) { printf("Run pcidiagset first\n"); @@ -152,7 +157,7 @@ void pciread(int dmod, int offset, int count) count/=dbytes; if(count==0) count=1; - for(i=0, dptr=diagbase+offset; iid.vendor==val); testOk1(devPCIConfigRead16(dev, 2, &val)==0); testOk1(dev->id.device==val); + + dev2 = NULL; + testOk1(devPCIFindSpec(hostbridge, "", &dev2, 0)==0); + testOk(dev==dev2, "%x:%x.%x == %x:%x.%x", + dev->bus, dev->device, dev->function, + dev2->bus, dev2->device, dev2->function); + + dev2 = NULL; + testOk1(devPCIFindSpec(hostbridge, "instance=1", &dev2, 0)==0); + testOk(dev==dev2, "%x:%x.%x == %x:%x.%x", + dev->bus, dev->device, dev->function, + dev2->bus, dev2->device, dev2->function); + + dev2 = NULL; + testOk1(devPCIFindSpec(hostbridge, "0:0.0", &dev2, 0)==0); + testOk(dev==dev2, "%x:%x.%x == %x:%x.%x", + dev->bus, dev->device, dev->function, + dev2->bus, dev2->device, dev2->function); + + testDiag("Unknown qualifiers are ignored"); + dev2 = NULL; + testOk1(devPCIFindSpec(hostbridge, "foo=bar", &dev2, 0)==0); + testOk(dev==dev2, "%x:%x.%x == %x:%x.%x", + dev->bus, dev->device, dev->function, + dev2->bus, dev2->device, dev2->function); + + testOk1(devPCIFindSpec(hostbridge, "instance=2", &dev2, 0)!=0); + + testOk1(devPCIFindSpec(hostbridge, "slot=42", &dev2, 0)!=0); + + testOk1(devPCIFindSpec(hostbridge, "foo", &dev2, 0)!=0); } MAIN(pcitest) { - testPlan(0); + testPlan(24); devLibPCIRegisterBaseDefault(); devLibPCIUse(NULL); testDiag("Using driver: %s", devLibPCIDriverName()); diff --git a/vmeApp/devcsr.c b/vmeApp/devcsr.c index 8ba36d4..91fef07 100644 --- a/vmeApp/devcsr.c +++ b/vmeApp/devcsr.c @@ -18,39 +18,39 @@ epicsShareFunc volatile unsigned char* devCSRProbeSlot(int slot) { - volatile unsigned char* addr; - char cr[3]; + volatile unsigned char* addr; + char cr[3]; - if(slot<0 || slot>VMECSRSLOTMAX){ - errlogPrintf("VME slot number out of range\n"); - return NULL; - } + if(slot<0 || slot>VMECSRSLOTMAX){ + errlogPrintf("VME slot number out of range\n"); + return NULL; + } - if( devBusToLocalAddr( - atVMECSR, - CSRSlotBase(slot), - (volatile void**)(void*)&addr) ) - { + if( devBusToLocalAddr( + atVMECSR, + CSRSlotBase(slot), + (volatile void**)(void*)&addr) ) + { - errlogPrintf("Failed to map slot %d to CR/CSR address 0x%08lx\n",slot, - (unsigned long)CSRSlotBase(slot)); - return NULL; - } + errlogPrintf("Failed to map slot %d to CR/CSR address 0x%08lx\n",slot, + (unsigned long)CSRSlotBase(slot)); + return NULL; + } - if( devReadProbe(1, addr+CR_ASCII_C, &cr[0]) ){ - errlogPrintf("No card in slot %d\n",slot); - return NULL; - } + if( devReadProbe(1, addr+CR_ASCII_C, &cr[0]) ){ + errlogPrintf("No card in slot %d\n",slot); + return NULL; + } - cr[1]=*(addr+CR_ASCII_R); - cr[2]='\0'; + cr[1]=*(addr+CR_ASCII_R); + cr[2]='\0'; - if( cr[0]!='C' || cr[1]!='R' ){ - errlogPrintf("Card in slot %d has non-standard CR layout. Ignoring...\n",slot); - return NULL; - } + if( cr[0]!='C' || cr[1]!='R' ){ + errlogPrintf("Card in slot %d has non-standard CR layout. Ignoring...\n",slot); + return NULL; + } - return addr; + return addr; } /* @@ -60,55 +60,55 @@ volatile unsigned char* devCSRProbeSlot(int slot) static int csrMatch(const struct VMECSRID* A, const struct VMECSRID* B) { - if( A->vendor!=B->vendor && - A->vendor!=VMECSRANY && - B->vendor!=VMECSRANY - ) - return 0; - - if( A->board!=B->board && - A->board!=VMECSRANY && - B->board!=VMECSRANY - ) - return 0; - - if( A->revision!=B->revision && - A->revision!=VMECSRANY && - B->revision!=VMECSRANY - ) - return 0; - - return 1; + if( A->vendor!=B->vendor && + A->vendor!=VMECSRANY && + B->vendor!=VMECSRANY + ) + return 0; + + if( A->board!=B->board && + A->board!=VMECSRANY && + B->board!=VMECSRANY + ) + return 0; + + if( A->revision!=B->revision && + A->revision!=VMECSRANY && + B->revision!=VMECSRANY + ) + return 0; + + return 1; } epicsShareFunc volatile unsigned char* devCSRTestSlot( - const struct VMECSRID* devs, - int slot, - struct VMECSRID* info -) + const struct VMECSRID* devs, + int slot, + struct VMECSRID* info + ) { - struct VMECSRID test; - volatile unsigned char* addr=devCSRProbeSlot(slot); - - if(!addr) return addr; - - test.vendor=CSRRead24(addr + CR_IEEE_OUI); - test.board=CSRRead32(addr + CR_BOARD_ID); - test.revision=CSRRead32(addr + CR_REVISION_ID); - - for(; devs && devs->vendor; devs++){ - if(csrMatch(devs,&test)){ - if(!!info){ - info->vendor=test.vendor; - info->board=test.board; - info->revision=test.revision; - } - return addr; + struct VMECSRID test; + volatile unsigned char* addr=devCSRProbeSlot(slot); + + if(!addr) return addr; + + test.vendor=CSRRead24(addr + CR_IEEE_OUI); + test.board=CSRRead32(addr + CR_BOARD_ID); + test.revision=CSRRead32(addr + CR_REVISION_ID); + + for(; devs && devs->vendor; devs++){ + if(csrMatch(devs,&test)){ + if(!!info){ + info->vendor=test.vendor; + info->board=test.board; + info->revision=test.revision; + } + return addr; + } } - } - return NULL; + return NULL; } /* Decode contents of CSR/CR and print to screen. @@ -122,12 +122,11 @@ void vmecsrprint(int N,int v) { volatile unsigned char* addr; char ctrlsts=0; - int i,j,space; - size_t ader; + int space; if( N<0 || N>=32 ){ - errlogPrintf("Slot number of of range (1-31)\n"); - return; + errlogPrintf("Slot number of of range (1-31)\n"); + return; } errlogPrintf("====== Slot %d\n",N); @@ -136,93 +135,97 @@ void vmecsrprint(int N,int v) if(!addr) return; if(v>=2){ - for(i=0;i<512;i++){ - if(i%16==0) { - printf("%04x: ",i); - } - - printf("%02x", ((int)*(addr+i))&0xff); - - if(i%16==15) - printf("\n"); - else if(i%4==3) - printf(" "); - } + unsigned i; + for(i=0;i<512;i++){ + if(i%16==0) { + printf("%04x: ",i); + } + + printf("%02x", ((int)*(addr+i))&0xff); + + if(i%16==15) + printf("\n"); + else if(i%4==3) + printf(" "); + } } if(v>=1){ - errlogPrintf("ROM Checksum : 0x%02x\n",CSRRead8(addr + CR_ROM_CHECKSUM)); - errlogPrintf("ROM Length : 0x%06x\n",CSRRead24(addr + CR_ROM_LENGTH)); - errlogPrintf("CR data width: 0x%02x\n",CSRRead8(addr + CR_DATA_ACCESS_WIDTH)); - errlogPrintf("CSR data width:0x%02x\n",CSRRead8(addr + CSR_DATA_ACCESS_WIDTH)); + errlogPrintf("ROM Checksum : 0x%02x\n",CSRRead8(addr + CR_ROM_CHECKSUM)); + errlogPrintf("ROM Length : 0x%06x\n",CSRRead24(addr + CR_ROM_LENGTH)); + errlogPrintf("CR data width: 0x%02x\n",CSRRead8(addr + CR_DATA_ACCESS_WIDTH)); + errlogPrintf("CSR data width:0x%02x\n",CSRRead8(addr + CSR_DATA_ACCESS_WIDTH)); } space=CSRRead8(addr + CR_SPACE_ID); errlogPrintf("CR space id: "); if(space==1) - errlogPrintf("VME64\n"); + errlogPrintf("VME64\n"); else if(space==2) - errlogPrintf("VME64x\n"); + errlogPrintf("VME64x\n"); else - errlogPrintf("Unknown (0x%02x)\n",space); + errlogPrintf("Unknown (0x%02x)\n",space); errlogFlush(); if(space>=1){ - errlogPrintf("Vendor ID : 0x%06x\n",CSRRead24(addr + CR_IEEE_OUI)); - errlogPrintf("Board ID : 0x%08x\n",CSRRead32(addr + CR_BOARD_ID)); - errlogPrintf("Revision ID : 0x%08x\n",CSRRead32(addr + CR_REVISION_ID)); - errlogPrintf("Program ID : 0x%02x\n",CSRRead8(addr + CR_PROGRAM_ID)); - - errlogPrintf("CSR Bar : 0x%02x\n",CSRRead8(addr + CSR_BAR)); - ctrlsts=CSRRead8(addr + CSR_BIT_SET); - errlogPrintf("CSR CS : 0x%02x\n",ctrlsts); - errlogPrintf("CSR Reset : %s\n",ctrlsts&CSR_BITSET_RESET_MODE?"Yes":"No"); - errlogPrintf("CSR Sysfail : %s\n",ctrlsts&CSR_BITSET_SYSFAIL_ENA?"Yes":"No"); - errlogPrintf("CSR Fail : %s\n",ctrlsts&CSR_BITSET_MODULE_FAIL?"Yes":"No"); - errlogPrintf("CSR Enabled : %s\n",ctrlsts&CSR_BITSET_MODULE_ENA?"Yes":"No"); - errlogPrintf("CSR Bus Err : %s\n",ctrlsts&CSR_BITSET_BERR?"Yes":"No"); + errlogPrintf("Vendor ID : 0x%06x\n",CSRRead24(addr + CR_IEEE_OUI)); + errlogPrintf("Board ID : 0x%08x\n",CSRRead32(addr + CR_BOARD_ID)); + errlogPrintf("Revision ID : 0x%08x\n",CSRRead32(addr + CR_REVISION_ID)); + errlogPrintf("Program ID : 0x%02x\n",CSRRead8(addr + CR_PROGRAM_ID)); + + errlogPrintf("CSR Bar : 0x%02x\n",CSRRead8(addr + CSR_BAR)); + ctrlsts=CSRRead8(addr + CSR_BIT_SET); + errlogPrintf("CSR CS : 0x%02x\n",ctrlsts); + errlogPrintf("CSR Reset : %s\n",ctrlsts&CSR_BITSET_RESET_MODE?"Yes":"No"); + errlogPrintf("CSR Sysfail : %s\n",ctrlsts&CSR_BITSET_SYSFAIL_ENA?"Yes":"No"); + errlogPrintf("CSR Fail : %s\n",ctrlsts&CSR_BITSET_MODULE_FAIL?"Yes":"No"); + errlogPrintf("CSR Enabled : %s\n",ctrlsts&CSR_BITSET_MODULE_ENA?"Yes":"No"); + errlogPrintf("CSR Bus Err : %s\n",ctrlsts&CSR_BITSET_BERR?"Yes":"No"); } if(space>=2){ - errlogPrintf("User CR : %08x -> %08x\n", - CSRRead24(addr + CR_BEG_UCR),CSRRead24(addr + CR_END_UCR)); - errlogPrintf("User CSR : %08x -> %08x\n", - CSRRead24(addr + CR_BEG_UCSR),CSRRead24(addr + CR_END_UCSR)); - errlogPrintf("CSR Owned : %s\n",ctrlsts&CSR_BITSET_CRAM_OWNED?"Yes":"No"); - errlogPrintf("Owner : 0x%02x\n",CSRRead8(addr + CSR_CRAM_OWNER)); - errlogPrintf("User bits : 0x%02x\n",CSRRead8(addr + CSR_UD_BIT_SET)); - errlogPrintf("Serial Number: 0x"); - for(i=CR_BEG_SN; i<=CR_END_SN; i+=4) - errlogPrintf("%02x",CSRRead8(addr + i)); - errlogPrintf("\n"); - if(v>=1){ - errlogFlush(); - errlogPrintf("Master Cap. : 0x%02x\n",CSRRead16(addr + CR_MASTER_CHAR)); - errlogPrintf("Slave Cap. : 0x%02x\n",CSRRead16(addr + CR_SLAVE_CHAR)); - errlogPrintf("IRQ Sink Cap.: 0x%02x\n",CSRRead8(addr + CR_IRQ_HANDLER_CAP)); - errlogPrintf("IRQ Src Cap. : 0x%02x\n",CSRRead8(addr + CR_IRQ_CAP)); - errlogPrintf("CRAM data width:0x%02x\n",CSRRead8(addr + CR_CRAM_WIDTH)); - for(i=0;i<8;i++){ - errlogPrintf("Function %d\n",i); - errlogPrintf(" Data width: %02x\n",CSRRead8(addr + CR_FN_DAWPR(i))); - errlogPrintf(" Data AM : "); - for(j=0;j<0x20;j+=4) - errlogPrintf("%02x",CSRRead8(addr + CR_FN_AMCAP(i) + j)); - errlogPrintf("\n"); - errlogPrintf(" Data XAM : "); - for(j=0;j<0x80;j+=4) - errlogPrintf("%02x",CSRRead8(addr + CR_FN_XAMCAP(i) + j)); - errlogPrintf("\n"); - errlogPrintf(" Data ADEM : "); - for(j=0;j<0x10;j+=4) - errlogPrintf("%02x",CSRRead8(addr + CR_FN_ADEM(i) + j)); - errlogPrintf("\n"); - ader=CSRRead32(addr + CSR_FN_ADER(i)); - errlogPrintf(" Data ADER : Base %08x Mod %02x\n", - (unsigned int)ader&0xFfffFf00,(int)(ader&0xff)>>2); + unsigned i; + errlogPrintf("User CR : %08x -> %08x\n", + CSRRead24(addr + CR_BEG_UCR),CSRRead24(addr + CR_END_UCR)); + errlogPrintf("User CSR : %08x -> %08x\n", + CSRRead24(addr + CR_BEG_UCSR),CSRRead24(addr + CR_END_UCSR)); + errlogPrintf("CSR Owned : %s\n",ctrlsts&CSR_BITSET_CRAM_OWNED?"Yes":"No"); + errlogPrintf("Owner : 0x%02x\n",CSRRead8(addr + CSR_CRAM_OWNER)); + errlogPrintf("User bits : 0x%02x\n",CSRRead8(addr + CSR_UD_BIT_SET)); + errlogPrintf("Serial Number: 0x"); + for(i=CR_BEG_SN; i<=CR_END_SN; i+=4) + errlogPrintf("%02x",CSRRead8(addr + i)); + errlogPrintf("\n"); + if(v>=1){ + errlogFlush(); + errlogPrintf("Master Cap. : 0x%02x\n",CSRRead16(addr + CR_MASTER_CHAR)); + errlogPrintf("Slave Cap. : 0x%02x\n",CSRRead16(addr + CR_SLAVE_CHAR)); + errlogPrintf("IRQ Sink Cap.: 0x%02x\n",CSRRead8(addr + CR_IRQ_HANDLER_CAP)); + errlogPrintf("IRQ Src Cap. : 0x%02x\n",CSRRead8(addr + CR_IRQ_CAP)); + errlogPrintf("CRAM data width:0x%02x\n",CSRRead8(addr + CR_CRAM_WIDTH)); + for(i=0;i<8;i++){ + unsigned j; + size_t ader; + errlogPrintf("Function %d\n",i); + errlogPrintf(" Data width: %02x\n",CSRRead8(addr + CR_FN_DAWPR(i))); + errlogPrintf(" Data AM : "); + for(j=0;j<0x20;j+=4) + errlogPrintf("%02x",CSRRead8(addr + CR_FN_AMCAP(i) + j)); + errlogPrintf("\n"); + errlogPrintf(" Data XAM : "); + for(j=0;j<0x80;j+=4) + errlogPrintf("%02x",CSRRead8(addr + CR_FN_XAMCAP(i) + j)); + errlogPrintf("\n"); + errlogPrintf(" Data ADEM : "); + for(j=0;j<0x10;j+=4) + errlogPrintf("%02x",CSRRead8(addr + CR_FN_ADEM(i) + j)); + errlogPrintf("\n"); + ader=CSRRead32(addr + CSR_FN_ADER(i)); + errlogPrintf(" Data ADER : Base %08x Mod %02x\n", + (unsigned int)ader&0xFfffFf00,(int)(ader&0xff)>>2); + } } - } } return; @@ -231,16 +234,16 @@ void vmecsrprint(int N,int v) void vmecsrdump(int v) { - int i; - errlogFlush(); + int i; + errlogFlush(); - errlogPrintf(">>> CSR/CR Dump\n"); + errlogPrintf(">>> CSR/CR Dump\n"); - for(i=0;i<22;i++) { - vmecsrprint(i,v); - errlogFlush(); - } + for(i=0;i<22;i++) { + vmecsrprint(i,v); + errlogFlush(); + } - errlogPrintf(">>> CSR/CR Dump End\n"); - return; + errlogPrintf(">>> CSR/CR Dump End\n"); + return; } diff --git a/vmeApp/os/vxWorks/devLibVMEOSD.c b/vmeApp/os/vxWorks/devLibVMEOSD.c index af6831e..182f138 100644 --- a/vmeApp/os/vxWorks/devLibVMEOSD.c +++ b/vmeApp/os/vxWorks/devLibVMEOSD.c @@ -253,8 +253,6 @@ static long vxDevDisableInterruptLevelVME (unsigned level) static long vxDevMapAddr (epicsAddressType addrType, unsigned options, size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress) { - long status; - if (ppPhysicalAddress==NULL) { return S_dev_badArgument; } @@ -265,6 +263,7 @@ static long vxDevMapAddr (epicsAddressType addrType, unsigned options, } else { + long status; status = sysBusToLocalAdrs (EPICStovxWorksAddrType[addrType], (char *) logicalAddress, (char **)ppPhysicalAddress); if (status) { @@ -315,7 +314,6 @@ static myISR *isrFetch(unsigned vectorNumber) myISR *psub; myISR *pCISR; void *pParam; - int s; /* * fetch the handler or C stub attached at this vector @@ -330,6 +328,7 @@ static myISR *isrFetch(unsigned vectorNumber) * and if so finds the function pointer and * the parameter passed */ + int s; s = cISRTest(psub, &pCISR, &pParam); if(!s){ psub = pCISR; diff --git a/vmeApp/vmesh.c b/vmeApp/vmesh.c index 3ee8da8..ccfa34a 100644 --- a/vmeApp/vmesh.c +++ b/vmeApp/vmesh.c @@ -24,7 +24,6 @@ static int validate_widths(epicsUInt32 addr, int amod, int dmod, int count, volatile void** mptr) { - epicsUInt32 tval; epicsAddressType atype; short dbytes; @@ -55,8 +54,8 @@ int validate_widths(epicsUInt32 addr, int amod, int dmod, int count, volatile vo return 1; } - if( (addr > ((1<= ((1< ((1U<= ((1U<>24)&0xff);break; + case 16: printf("%04x",(tval>>16)&0xffff);break; + case 32: printf("%08x",tval&0xffffffff);break; } } printf("\n"); + if(berr) + printf("*** Bus errors occurred ***\n"); } static const iocshArg vmereadArg0 = { "address",iocshArgInt}; @@ -147,17 +141,23 @@ void vmewrite(int rawaddr, int amod, int dmod, int rawvalue) { epicsUInt32 addr = rawaddr, value = rawvalue; volatile void* mptr; + long err; - epicsPrintf("Writing to 0x%08x A%d D%d value 0x%08x\n",addr,amod,dmod, (unsigned)value); + printf("Writing to 0x%08x A%d D%d value 0x%08x\n",addr,amod,dmod, (unsigned)value); if(validate_widths(addr, amod, dmod, 1, &mptr)) return; - switch(dmod){ - case 8: iowrite8(mptr, value); break; - case 16: nat_iowrite16(mptr, value); break; - case 32: nat_iowrite32(mptr, value); break; + switch (dmod) { + case 8: value<<=24; break; + case 16: value<<=16; break; + case 32: break; } + + err = devWriteProbe(dmod/8, mptr, &value); + + if(err) + printf("*** Bus Error detected ***\n"); } static const iocshArg vmewriteArg0 = { "address",iocshArgInt};