Title: EPICS record/device/driver Support
1EPICSrecord/device/driverSupport
- kasemir_at_lanl.gov,many slides copied from
johill_at_lanl.gov
2Interfacing Hardware
General Idea
EPICS
Software
IOC Core Db, CA,
Record Support
Driver
Device Support
Hardware
Driver Support
Hardware
3Where to extend
- Common Case New Hardware (I/O Board,..)
- Driver Any low-level code to talk to the
hardware.Might not have any knowledge of EPICS - Device EPICS-specific glue code between driver
and (subset of) records - Sometimes Specialized Record
- Copy of existing record w/ slight change
- Seldom New Record Type
- Can task be handled by combination of existing
records, maybe w/ help of SNL?
4Driver/Device/Record Support
- Read the IOC Application Developer's Guide
before even thinking about doing this! - Common Idea
- Describe the new driver/device/record via DBD
(Database Description File, ASCII) - Implement the functionality, providing a function
table specific to driver/device/record support
(init(), report(), do_something(), ) - Link/load the binaries which export a function
table - During startup, iocCore will parse the DBD,
locate the function table and invoke the
appropriate functions - Well-defined interfaces for adding new support,
minimal recompilation.
5Driver Support
- Drivers can be complicated
- Bus-level access, critical timing, interrupts,
semaphores, threads, deadlocks, fail-safe,
OS-specific (vxWorks, Linux, Win32), - Typical collection of routinesCheck, report,
init, read, write, setup_trigger(callback), - EPICS part Optional Trivial!
6Driver Support Entry Table
- / EPICS Base include file ltdrvSup.hgt /
- typedef long (DRVSUPFUN) ()
- struct drvet
-
- long number /number of support
routines/DRVSUPFUN report /print
report/DRVSUPFUN init /init the driver / -
- Any routine pointer can be NULL
7DSET Example
- / xy.c /
- includeltdrvSup.hgt
- static long xy_report()
-
- printf(XY Driver Info\n)
-
-
- static long xy_init()
-
- if (xy_check())
-
-
- struct drvet drvXy
- 2, xy_report,
- xy_init
-
8Registering Driver w/ EPICS
- EPICS DBD File Entry
- driver(drvXy)
- Results
- EPICS iocInit will locate drvXy in symbol
table, call the registered init routine (unless
NULL) - EPICS dbior will invoke report routine (unless
NULL)
9Good Practice
- Wrong Assume e.g. two XY board, one at base
address 0x1234 and one at 0x4567, all hard-coded
in driver. - Best Provide configure routine, callable from
e.g. vxWorks startup file before invoking
iocInit - EPICS records that refer to XY 0 will
- access board at base addr. 0xfe12
- in mode 15
- xy_config(0, 0xfe12, 15)
10Device Support
- Glue between record and driver is highly specific
to the resp. record - AI record, DTYPXY, INPC0 S5Device
support has to call driver for XY card 0 and
get signal 5 into the records RVAL field - Dev.sup routines common to all records
- Report Show info
- Init Called once
- Init_Record Called for each record
- Get I/O Interrupt Info Used w/ SCANI/O Intr
- Most are optional, but check specific record type.
11Device Support Entry Table
- / Defined in devSup.h /
- struct dset
-
- long number / number of support routines /
- DEVSUPFUN report / print report/
- DEVSUPFUN init / init support/
- DEVSUPFUN init_record / init particular record
/ - DEVSUPFUN get_ioint_info / get I/O Intr. Info
/ - / Rest specific to record, e.g. BI
- DEVSUPFUN read_bi
- Result (0,2,error) 0 -gt raw value stored
in RVAL, convert to VAL 2 -gt value already
stored in VAL, dont convert - /
-
12Registering Device Support
- EPICS DBD File
- device(ai,INST_IO,devAiXX,My XX")
- Result iocCore
- now allows DTYPMy XX for ai records
- will locate DSET devAiXX in symbol table,call
init(), then init_record() for each record,
record will call read() whenever record gets
processed,
13Before Implementing Dev.Sup
- Understand how to use the driver
- Read
- Application Developer Guide
- Source for specific record type XX, understand
how record calls its device support - Examples in EPICS base sources
base/src/dev/softDev/devXXSoft.c
14Device Support for AI Record
- Common
- report
- initialization
- initialize instance
- attach to device interrupt
- AI-Specific
- read ai device value
- linear conversion(RVAL-gtVAL)
15AI Device Support Type Initialization
- long aiDevInit (unsigned pass)
- common to all record types
- device specific initialization
- pass 0, prior to initializing each record
during "iocInit() - Check hardware, but note that records are not
ready to handle data - pass 1, after initializing each record during
"iocInit() - Activate triggers,
16AI Device Report
- long aiDevReport (struct aiRecord pai, int
level) - common to all records, but gets passed pointer to
specific record type - called once for every record instance when the
user types "dbior ltlevelgt" - device status to "stdout" from this routine
- Idea detail increases with increasing "level"
17AI Device Initialization for Record
- long aiDevInitInstance(struct aiRecord pai)
- Called from within iocInit() once for each
record attached to device - Typical
- Parse check device address (pai-gtinp)
- Store device-data, e.g. the parsed signal ,
driver handles, in DPVT - pvt (X ) calloc(1, sizeof(X)
- pvt-gtsignal signal_this_record_wants
- pvt-gtdrv magic_handle_we_got_from_driver
- pai-gtdpvt (void ) pvt
18Read Signal Value
- long aiDevRead_(struct aiRecord pai)
- long rval
- if (device OK) rvalpDevMemoryMap-gt
- aiRegisterpai-gtdpvt-gtsignal
- pai-gtrval rval
- else
- recGblSetSevr(pai,
- READ_ALARM, INVALID_ALARM)
19AI Linear Conversion
- long aiDevLinearConv (
- struct aiRecord pai, int after)
- Setup the slope and offset for the conversion to
engineering units - if (!after)
- return S_XXXX_OK
- / A 12 bit DAC is assumed here /
- pai-gteslo (pai-gteguf - pai-gtegul)/0x0FFF
- pai-gtroff 0/ roff could be different for
device w/ e.g. -5V, half scale 0V /
20From convert() in aiRecord.c
- double val
- val pai-gtrval pai-gtroff
- /
- adjust with slope/offset
- if linear convert is used
- /
- if ( pai-gtaslo ! 0.0 ) val pai-gtaslo
- if( pai-gtaoff ! 0.0 ) val pai-gtaoff
- if(pai-gtlinr menuConvertLINEAR) val (val
pai-gteslo) pai-gteoff
21Advanced Interrupts
- Device supports interrupts, want to use SCANI/O
Intr - higher scan rate
- scan synchronized with device
- Difficulty
- Interrupts interrupt level, most OS routines
prohibited - Record processing task level
- IOSCANPVT
- EPICS Core Helper for processing records in
response to interrupt
22IOSCANPVT
- Initialize keep one IOSCANPVT per distinct
interrupt that the hardware can generate - / Records DPVT points to struct X which
contains IOCSCANPVT ioscanpvt /X (X )
rec-gtdpvtscanIoInit(X-gtioscanpvt) - Each Interrupt Occurrence (ISR)
- scanIoRequest(X-gtioscanpvt)
- safe to call from ISR, but dont call
scanIoRequest() until after database init
(iocInit()) completes(extern volatile int
interruptAccept)
23Provide IO Interrupt Info
- long aiDevGetIoIntInfo (
- int cmd,
- struct aiRecord pai,
- IOSCANPVT ppvt)
- associates interrupt source with record
- ppvt X-gtioscanpvt
- cmd0 - insert into IO interrupt scan
- cmd1 - remove from IO Interrupt scan
24Asynchronous Devices
- read/write routine sets PACT true and returns
zero for success. - asynchronous IO completion callback completes
record processing - dont process a record from within an ISR
25Example Asynchronous Read
- long devXxxRead (struct aiRecord pai)
- if (pai-gtpact)
- return S_devXxx_OK / zero /
- pai-gtpact TRUE
- devXxxBeginAsyncIO(pai-gtdpvt)
- return S_devXxx_OK
26Example Asynchronous Read Completion
- void devXxxAsyncIOCompletion(struct aiRecord
pai, long ioStatus) -
- struct rset prset (struct rset ) pai-gtrset
- dbScanLock(pai)
- if (ioStatus ! S_devXxx_OK)
- recGblSetSevr(pai, READ_ALARM, INVALID_ALARM)
-
- (prset-gtprocess)(pai)
- dbScanUnlock(pai)
27Record Support
- Similar to device driver support
- Certain routines need to be implemented
- Binary exports a Record support entry table
which contains those routines - DBD File to describe record and each field (name,
data type, maybe menu of enumerated values, )
28Record DBD File
- Need to read IOC Application Developer's Guide
to understand full DBD syntax! - Use xxxRecord created by makeBaseApp as example
- recordtype(xxx) Each record needs to start
w/ the common fields! include "dbCommon.dbd"
field(VAL,DBF_DOUBLE) prompt("Current EGU
Value") asl(ASL0) pp(TRUE)
29Record support entry table
- InitializationGeneral and per-record instance
- Process routineImplements functionality of
record. Often - calls device support specific to this record type
- checks for alarms
- posts monitors
- processes forward links
- Utility routines that allow iocCore to properly
read, write display fields of this record
30Record support entry table
- Record Implementation must export struct rset
ltxxxRecordgtRSET - struct rset / record support entry table /
- long number / number of support routine /
- RECSUPFUN report / print report /
- RECSUPFUN init / init support /
- RECSUPFUN init_record / init record /
- RECSUPFUN process / process record /
- RECSUPFUN special / special processing /
- RECSUPFUN get_value / OBSOLETE Just leave
NULL / - RECSUPFUN cvt_dbaddr / cvt dbAddr /
- RECSUPFUN get_array_info
- RECSUPFUN put_array_info
- RECSUPFUN get_units
- RECSUPFUN get_precision
- RECSUPFUN get_enum_str / get string from enum
/ - RECSUPFUN get_enum_strs / get all enum
strings / - RECSUPFUN put_enum_str / put enum from string
/ - RECSUPFUN get_graphic_double
31Initialization
- init()
- Called once during IOC startup
- init_record(void precord, int pass)
- Called twice per record instance. Second pass can
affect other records.
32Processing
- Usually follows this example
- static long process(void precord)
-
- xxxRecordpxxx (xxxRecord )precord
- xxxdset pdset (xxxdset )pxxx-gtdset
- long status
- unsigned char pactpxxx-gtpact
- if( (pdsetNULL) (pdset-gtread_xxxNULL) )
-
- / leave pact true so that dbProcess doesnt
call again/ - pxxx-gtpactTRUE
- recGblRecordError(S_dev_missingSup, pxxx,
read_xxx) - return (S_dev_missingSup)
-
- / pact must not be set true until read_xxx
completes/ - status(pdset-gtread_xxx)(pxxx) / read the new
value / - / return if beginning of asynch processing/
- if(!pact pxxx-gtpact) return(0)
33Utility Routines
- Allow to define
- how the record responds when fields are read or
written - the units, precision, limitsnot only of the
VAL field!
34Utility Routines
- special(DBADDR addr, int after)
- Allows us to react before/after somebody else
accesses field refed by addr. - get_units(DBADDR addr, char units),get_precisio
n(DBADDR addr, long prec),get_array_info() - Can simply copy contents of EGU or PREC
fields,can also provide information specific to
field refed by addr