PAUL SCHERRER INSTITUT
EPICS at PSI
PSIEPICSSLSSwissFELProscan

EPICS

EPICS at PSI
Software
Training

web epics.web.psi.ch

Author: Dirk Zimoch
Phone: +41 56 310 5182
Updated: 24.08.2021


Printer friendly version
 

[Validate HTML][Validate CSS]

RegDev EPICS Device Support

RegDev EPICS Device Support Documentation

Contents

  1. Device Support
    1. Connection Status (bi)
    2. Analog Input (ai)
    3. Analog Output (ao)
    4. Calculation Output (calcout)
    5. Binary Input (bi)
    6. Binary Outpu (bo)t
    7. Multi Bit Binary Input (mbbi)
    8. Multi Bit Binary Output (mbbo)
    9. Multi Bit Binary Input Direct (mbbiDirect)
    10. Multi Bit Binary Output Direct (mbboDirect)
    11. Long Input (longin)
    12. Long Output (longout)
    13. Integer 64 bit Input (int64in)
    14. Integer 64 bit Output (int64out)
    15. String Input (stringin)
    16. String Output (stringout)
    17. Waveform Input (waveform)
    18. Array Analog Input (aai)
    19. Array Analog Output (aao)
  2. Driver Functions
  3. Debugging

Intro

In this document, I assume that the reader is familiar with EPICS, the record concept and meanings of the fields of the standard records. Recommended reading: EPICS Record Reference Manual.

This generic device support is intended to connect arbitrary synchronous register based devices to EPICS in a simple and flexible way. It can be used for rapid prototyping before a proper driver is available, to access registers that are unsupported by the proper driver or as the ultimate driver for any device which is "simple enough".

It is assumed that the device allocates a continuous block of registers. The registers should be independent, that means one device action is performed by reading or writing only one register. Because each record connects to one register, it is not possible to lock the device for several register accesses with this device support, except if that task is performed in the low level driver and hidden from regDev. The user has to know the register offsets and data types when configuring the records.

The actual hardware access is neither defined nor implemented in this device support. Instead, regDev defines an interface for low level drivers that implement the hardware access. Examples are mmap (a driver for memory mapped registers, VME or mmap()) or s7plc (a driver for network communication to a Siemens S7 PLC). What is defined in regDev is the interface to the standard records. Thus it is not necessary to deal with record when writing a new low-level driver. Consequently, all regDev compatible devices look the same on the record level. The general idea is to have a simple and flexible device support interface which is independent of the underlying hardware structure.

To be usable with regDev, a driver must implement some API functions and register a device to regDev by name. Each piece of regDev hardware must have a name that is unique on the IOC. Typically, the low level driver registers a device in a configuration function called from the startup script.

In principle, a piece of hardware can have many blocks of registers (e.g. on differenct VME address spaces). The low level driver may handle this by mapping ranges of register offsets to different blocks. Or the low level driver registers each block with an individual name. But then, regDev will handle them as different independent devices.

A demo driver is included.

Dirk Zimoch <dirk.zimoch@psi.ch>, 2009.

Device Support

RegDev supports the standard record types ai, ao, bi, bo, mbbi, mbbo, mbbiDirect, mbboDirect, longin, longout, stringin, stringout, and waveform. From EPICS R3.14.5 on calcout is supported. From EPICS R3.14.12 on aai and aao records are supported. (These records used to be commented out in earlier EPICS versions.) From EPICS R3.16 on int64in and int64out are supported.

The DTYP is "regDev" for all record types, independent of the low level driver.

If a record processes when the device is not connected (off, down, unreachable), the record raises an alarm with SEVR="INVALID" and STAT="READ" or "WRITE"

There is also a connection status support for bi. The DTYP is "regDev stat". This record does not raise an alarm when the device is disconnected. It just changes to 0 when the device disconencts and to 1 when it connects.

It is possible to use SCAN="I/O Intr" if the low level driver supports it. Input records in "I/O Intr" process whenever new data has been received from the device. If supported by the driver, output records are processed in "I/O Intr" mode whenever the driver is ready to accept new output. This may be useful for drivers with externally triggered or periodic output cycles.

The general form of the INP or OUT link is:
"@devicename:offset:initoffset T=type L=low H=high B=bit I=invertmask P=packing U=update_ms"

  • devicename is the name that a low level driver has used to register the device.
  • offset is the address of the register relative to the beginning of the register block for this device. It depends on the low level driver if offset is measured in bytes or anything else. The offset must be a positive integer expression within the limits of the device register block size. It may be calculated using the operators +-*().
    It is possible to calculate offset dynamically from another record. In this case the name of the other record must be the the first operand in the calculation. If the record name contains any of the characters :+-*(), it must be single quoted.
    Example: 'otherrecord'*8+0x100;
    If dynamic offsets exceed the limits of the register block, the record will rais an INVALID alarm.
  • initoffset is optionally used by output records to initialize from a hardware register. If initoffset is not specified but the second : is there, the record initializes from the normal offset address. If :initoffset is skipped (including the :), the record is not initialized by regDev and may be initialized for example by auto save and restore. Records read from initoffset during the init_record phase in unpredictable order.
  • T=type defines the data type of the register. See table below. The default is T=int16 for most record types.
  • L=low and H=high are used for linear conversion in ai and ao records if LINR=LINEAR and in waveform, aai and aao if FTVL=FLOAT or DOUBLE and T is an integer type. They define the raw values which correspond to EGUL and EGUF (or LOPR and HOPR for arrays) respectively. Output records will never write intger values lower than L or higher than H. Instead the output value saturates at the nearest limit. The default values for L and H depend on the data type.
  • B=bit is only used for bi and bo records to define the bit number within the data byte, word, or doubleword (depending on T). Bit number 0 is the least significant bit. Note that in big endian byte order (also known as motorola format) bit 0 is in the last byte, while in little endian byte order (intel format) bit 0 is in the first byte. If in doubt and allowed by the hardware, use T=byte to avoid all byte order problems when handling single bits.
  • I=invertmask is used to flip bits of integer values before writing them to or after reading them from a register. This allows to invert the logic of bits. The default value is 0 (i.e. do not flip bits).
  • P=packing is used for accessing arrays through FIFO registers. It defines how many array elements are packed in one register access. For example T=int16 P=1 defines a FIFO of 16 bit values while T=int16 P=2 is a FIFO with 32 bits width that contains two 16 bit values in each access.
  • U=update_ms is used to update output records periodically. Every update_ms milliseconds the output record reads back the value from the register at initoffset or offset if initoffset is not specified.

Not all parameters T, L, H, B, I, and P are always required. Unused parameters or parameters equal to the default value may be omitted. The default values depend on the data type. All parameters are case insensitive.

T Register Data Type Default L Default H
int8 8 bit (1 byte) signed integer number -0x7f
-127
0x7f
127
uint8
unsign8
unsigned8
byte
char
8 bit (1 byte) unsigned integer number 0x00
0
0xff
255
int16
short
16 bit (2 bytes) signed integer number -0x7fff
-32767
0x7fff
32767
uint16
unsign16
unsigned16
word
16 bit (2 bytes) unsigned integer number 0x0000
0
0xffff
65535
int32
long
32 bit (4 bytes) signed integer number -0x7fffffff
-2147483647
0x7fffffff
2147483647
uint32
unsign32
unsigned32
dword
32 bit (4 bytes) unsigned integer number 0x00000000
0
0xffffffff
4294967295
int64
longlong
64 bit (8 bytes) signed integer number -0x7fffffffffffffff
-9223372036854775807
0x7fffffffffffffff
9223372036854775807
uint64
unsign64
unsigned64
dword
64 bit (8 bytes) unsigned integer number 0x0000000000000000
0
0xffffffffffffffff
18446744073709551615
bcd8
bcd
8 bit (1 byte, 2 digits) unsigned binary coded decimal 00 99
bcd16 16 bit (2 bytes, 4 digits) unsigned binary coded decimal 0000 9999
bcd32 32 bit (4 bytes, 8 digits) unsigned binary coded decimal 00000000 99999999
bcd64 64 bit (8 bytes, 16 digits) unsigned binary coded decimal 0000000000000000 9999999999999999
real32
float32
float
single
32 bit (4 bytes) floating point number n/an/a
real64
float64
double
64 bit (8 bytes) floating point number n/an/a
string character array 40n/a

If T=string, L means length, not low. The length includes the terminating null byte. The default value is the length of the VAL field. In the case of the stringin and stringout records, this is 40. In case of array records (aai, aao, waveform) with CHAR data this is the size of the array (usually NELM).

Note that for signed integer types, L is one off the smallest possible value. This makes linear conversion symmatric on the positive and negative side (i.e. makes sure 0 is in the middle). As a side effect, the most negative value will never be written to the register but will be saturated to L. If that is a problem, set L explicitly (and maybe adjust LOPR).

Example Records

In the following examples it is assumed that the low level driver supports "I/O Intr" for input registers. If that is not the case or not applicable for all registers use any other scanning method instead. All output records in the examples use PINI=YES to make sure an initial value is written to the register. Depending on the low level driver, this may not be necessary if the record gets initialized from an initoffset.

Connection Status (bi)

 record (bi, "$(RECORDNAME)") {
  field (DTYP, "regDev stat")
  field (INP,  "@$(DEVICE)")
  field (SCAN, "I/O Intr")
 }

The record value is 1 if a connection to the device is established and 0 if not. Disconnect does not raise an alarm.

Analog Input (ai)

Integer registers with linear conversion

 record (ai, "$(RECORDNAME)") {
  field (DTYP, "regDev")
  field (INP,  "@$(DEVICE):$(OFFSET) T=$(T) L=$(L) H=$(H)")
  field (SCAN, "I/O Intr")
  field (LINR, "LINEAR")
  field (EGUL, "$(MINVAL)")
  field (EGUF, "$(MAXVAL)")
 }

If T is an integer type, the register is read into RVAL. If LINR=LINEAR, then the record converts RVAL to VAL so that L maps to MINVAL and H maps to MAXVAL.

Default type is T=int16. Defaults for L and H depend on T (see table above).

Floating point registers

 record (ai, "$(RECORDNAME)") {
  field (DTYP, "regDev")
  field (INP,  "@$(DEVICE):$(OFFSET) T=$(T)")
  field (SCAN, "I/O Intr")
 }

If T=float or T=double, the register is read directly into VAL and L, H, EGUL and EGUF are ignored. The device support emulates adjustment scaling and smoothing according to ASLO, AOFF and SMOO which is done by the record itself only when using conversion from integer registers.

VAL = (register*ASLO+AOFF)*(1-SMOO) + VALold*SMOO

T=string is not valid for ai records.

Analog Output (ao)

Integer registers with linear conversion

 record (ao, "$(RECORDNAME)") {
  field (DTYP, "regDev")
  field (OUT,  "@$(DEVICE):$(OFFSET) T=$(T) L=$(L) H=$(H)")
  field (LINR, "LINEAR")
  field (PINI, "YES")
  field (EGUL, "$(MINVAL)")
  field (EGUF, "$(MAXVAL)")
 }

If T is an integer type, RVAL is written to the register. If LINR=LINEAR, then the record support first scales the record value (to be exact OVAL) so that MINVAL maps to L and MAXVAL maps to H. The record may then use adjustment scaling to modify RVAL. However, the device support will never write any value lower than L or higher than H. If necessary the value will be saturated to the nearest limit.

Default type is T=int16. Defaults for L and H depend on T (see table above).

Floating point registers

 record (ao, "$(RECORDNAME)") {
  field (DTYP, "regDev")
  field (OUT,  "@$(DEVICE):$(OFFSET) T=$(T)")
  field (PINI, "YES")
 }

If T=float or T=double, OVAL is written to the register. L, H, EGUL and EGUF are ignored. The device support emulates adjustment scaling according to AOFF and ASLO which is done by the record itself only when converting to integer registers.

register=(OVAL-AOFF)/ASLO

T=string is not valid for ao records.

Calculation Output (calcout)

 record(calcout, "$(NAME)") {
  field (DTYP, "regDev")
  field (OUT,  "@$(DEVICE):$(OFFSET) T=$(T) L=$(L) H=$(H)")
  field (PINI, "YES")
 }

Default type is T=int16. Defaults for L and H depend on T (see table above).

OVAL (the result of CALC or OCAL, depending on DOPT) is written to the register. If T is an integer type, the value is truncated to an integer and compared to L and H. If OVAL is lower than L or higher than H, it is saturated to the nearest limit.

If T=float or T=double, OVAL is written to the register directly without any conversion.

T=string is not valid for calcout records.

To use this device support with calcout records, you need at least EPICS R3.14.5.

Binary Input (bi)

 record(bi, "$(NAME)") {
  field (DTYP, "regDev")
  field (INP,  "@$(DEVICE):$(OFFSET) T=$(T) B=$(B) I=$(I)")
  field (SCAN, "I/O Intr")
 }

Default type is T=int16. Default bit is B=0. Default invert mask is I=0.

Depending on T, B can vary from 0 to 7, 15, or 31. Bit 0 is the least significant bit. In little endian byte order, bit 0 is in the first byte, in big endian byte order it is in the last byte of the register. If in doubt and allowed by the hardware, use T=byte to avoid all byte order problems when handling single bits.

The register is read to RVAL and masked with 2B. If I!=0 the bit is inverted. The record sets VAL to 1 if RVAL is not 0.

RVAL=(I==0?register:~register)&(1<<B); VAL=(RVAL!=0)?1:0

T=string, T=float or T=double are not valid for bo records. Signed and unsigned types are equivalent.

Binary Output (bo)

 record(bo, "$(NAME)") {
  field (DTYP, "regDev")
  field (OUT,  "@$(DEVICE):$(OFFSET) T=$(T) B=$(B)")
  field (PINI, "YES")
 }

Default type is T=int16. Default bit is B=0.

Depending on T, B can vary from 0 to 7, 15, or 31. Bit 0 is the least significant bit. In little endian byte order, bit 0 is in the first byte, in big endian byte order it is in the last byte of the register. If in doubt and allowed by the hardware, use T=byte to avoid all byte order problems when handling single bits.

If VAL is not 0, then RVAL is set to 2B, else RVAL is set to 0. Only the referenced bit of the register is modified while all other bits remain unchanged. Thus, other output records can write to different bits of the same register.

RVAL=(VAL!=0)?(1<<B):0; register=(registerold&~(1<<B)) | RVAL

T=string, T=float or T=double are not valid for bo records. Signed and unsigned types are equivalent.

Multi Bit Binary Input (mbbi)

 record(mbbi, "$(NAME)") {
  field (DTYP, "regDev")
  field (INP,  "@$(DEVICE):$(OFFSET) T=$(T)")
  field (SCAN, "I/O Intr")
  field (NOBT, "$(NUMBER_OF_BITS)")
  field (SHFT, "$(RIGHT_SHIFT)")
 }

Default type is T=int16.

The register is read to RVAL, shifted right by SHFT bits and masked with NOBT bits. Valid values for NOBT and SHFT depend on T: NOBT+SHFT must not exceed the number of bits of the type.

Bit 0 is the least significant bit. In little endian byte order, bit 0 is in the first byte, in big endian byte order it is in the last byte of the register.

Example: Use bits 4 to 9 out of 16. T=int16, NOBT=6, SHFT=4

register 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
RVAL                     9 8 7 6 5 4

T=string, T=float or T=double are not valid for mbbi records. Signed and unsigned types are equivalent.

Multi Bit Binary Output (mbbo)

 record(mbbo, "$(NAME)") {
  field (DTYP, "regDev")
  field (OUT,  "@$(DEVICE):$(OFFSET) T=$(T)")
  field (PINI, "YES")
  field (NOBT, "$(NUMBER_OF_BITS)")
  field (SHFT, "$(LEFT_SHIFT)")
 }

Default type is T=int16.

RVAL is masked with NOBT bits, shifted left by SHFT bits and written to the register. Valid values for NOBT and SHFT depend on T: NOBT+SHFT must not exceed the number of bits of the type.

Bit 0 is the least significant bit. In little endian byte order, bit 0 is in the first byte, in big endian byte order it is in the last byte of the register.

Only the referenced NOBT bits of the register are modified. All other bits remain unchanged. Thus, other output records can write to different bits of the same register.

Example: Use bits 5 to 8 out of 16. T=int16, NOBT=4, SHFT=5

RVAL                         8 7 6 5
register 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

T=string, T=float or T=double are not valid for mbbo records. Signed and unsigned types are equivalent.

Multi Bit Binary Input Direct (mbbiDirect)

 record(mbbiDirect, "$(NAME)") {
  field (DTYP, "regDev")
  field (INP,  "@$(DEVICE):$(OFFSET) T=$(T)")
  field (SCAN, "I/O Intr")
  field (NOBT, "$(NUMBER_OF_BITS)")
  field (SHFT, "$(RIGHT_SHIFT)")
 }

Default type is T=int16.

The register is read to VAL, shifted right by SHFT bits and masked with NOBT bits (see mbbi). Valid values for NOBT and SHFT depend on T: NOBT+SHFT must not exceed the number of bits of the type.

Bit 0 is the least significant bit. In little endian byte order, bit 0 is in the first byte, in big endian byte order it is in the last byte of the register.

T=string, T=float or T=double are not valid for mbbiDirect records. Signed and unsigned types are equivalent.

Multi Bit Binary Output Direct (mbboDirect)

 record(mbboDirect, "$(NAME)") {
  field (DTYP, "regDev")
  field (OUT,  "@$(DEVICE):$(OFFSET) T=$(T)")
  field (PINI, "YES")
  field (NOBT, "$(NUMBER_OF_BITS)")
  field (SHFT, "$(LEFT_SHIFT)")
 }

Default type is T=int16.

VAL is masked with NOBT bits, shifted left by SHFT bits and written to the register (see mbbo). Valid values for NOBT and SHFT depend on T: NOBT+SHFT must not exceed the number of bits of the type.

Bit 0 is the least significant bit. In little endian byte order, bit 0 is in the first byte, in big endian byte order it is in the last byte of the register.

Only the referenced NOBT bits of the register are modified. All other bits remain unchanged. Thus, other output records can write to different bits of the same register.

T=string, T=float or T=double are not valid for mbboDirect records. Signed and unsigned types are equivalent.

Long Input (longin)

 record(longin, "$(NAME)") {
  field (DTYP, "regDev")
  field (INP,  "@$(DEVICE):$(OFFSET) T=$(T)")
  field (SCAN, "I/O Intr")
 }

Default type is T=int16.

The register is read to VAL. If the type has less than 32 bits, the value is zero extended or sign extended depending on the signedness of the type.

T=string, T=float or T=double are not valid for longin records.

Long Output (longout)

 record(longout, "$(NAME)") {
  field (DTYP, "regDev")
  field (OUT,  "@$(DEVICE):$(OFFSET) T=$(T)")
  field (PINI, "YES")
 }

Default type is T=int16.

Depending on T, the least significant 8, 16, or 32 bits of VAL are written to the register.

T=string, T=float or T=double are not valid for longout records.

Integer 64 Input (int64in)

 record(int64in, "$(NAME)") {
  field (DTYP, "regDev")
  field (INP,  "@$(DEVICE):$(OFFSET) T=$(T)")
  field (SCAN, "I/O Intr")
 }

Default type is T=int64.

The register is read to VAL. If the type has less than 64 bits, the value is zero extended or sign extended depending on the signedness of the type.

T=string, T=float or T=double are not valid for int64in records.

To use int64in records, you need at least EPICS R3.16.

Integer 64 Output (int64out)

 record(int64out, "$(NAME)") {
  field (DTYP, "regDev")
  field (OUT,  "@$(DEVICE):$(OFFSET) T=$(T)")
  field (PINI, "YES")
 }

Default type is T=int64.

Depending on T, the least significant 8, 16, 32, or 64 bits of VAL are written to the register.

T=string, T=float or T=double are not valid for int64out records.

To use int64out records, you need at least EPICS R3.16.

String Input (stringin)

 record(stringin, "$(NAME)") {
  field (DTYP, "regDev")
  field (INP,  "@$(DEVICE):$(OFFSET) L=$(LENGTH)")
  field (SCAN, "I/O Intr")
 }

Default and only valid type is T=string. Default length is L=40.

L bytes are read from the register to VAL and null terminated. Thus, the effective string length is maximal L-1 bytes.

String Output (stringout)

 record(stringout, "$(NAME)") {
  field (DTYP, "regDev")
  field (OUT,  "@$(DEVICE):$(OFFSET) L=$(LENGTH)")
  field (PINI, "YES")
 }

Default and only valid type is T=string. Default length is L=40.

L bytes are written from VAL to the register. If the actual string length of VAL is shorter than L, the remaining space is filled with null bytes. If it is longer than L, the string is truncated and not null terminated.

Waveform Input (waveform)

 record(waveform, "$(NAME)") {
  field (DTYP, "regDev")
  field (INP,  "@$(DEVICE):$(OFFSET)")
  field (SCAN, "I/O Intr")
  field (NELM, "$(NUMBER_OF_ELEMENTS)")
  field (FTVL, "$(DATATYPE)")
 }

NELM elements are read from registers to VAL.

The default type T depends on FTVL. For example FTVL=LONG results in T=INT32.

If T is an integer type, e.g. T=INT32 but FTVL is either FLOAT or DOUBLE, then values are scaled so that L=low and H=high map to the fields LOPR and HOPR respectively.

If T=string then FTVL must be CHAR or UCHAR or STRING. For CHAR and UCHAR, L=length can be specified but defaults to NELM and should not exceed it. If L is less than NELM, the remaining elements are left unchanged. For STRING, NELM strings, each of length L (default 40), are read.

Array Analog Input (aai)

 record(aai, "$(NAME)") {
  field (DTYP, "regDev")
  field (INP,  "@$(DEVICE):$(OFFSET)")
  field (SCAN, "I/O Intr")
  field (NELM, "$(NUMBER_OF_ELEMENTS)")
  field (FTVL, "$(DATATYPE)")
 }

NELM elements are read from registers to VAL.

The default type T depends on FTVL. For example FTVL=LONG results in T=INT32.

If T is an integer type, e.g. T=INT32 but FTVL is either FLOAT or DOUBLE, then values are scaled so that L=low and H=high map to the fields LOPR and HOPR respectively.

If T=string then FTVL must be CHAR or UCHAR or STRING. For CHAR and UCHAR, L=length can be specified but defaults to NELM and should not exceed it. If L is less than NELM, the remaining elements are left unchanged. For STRING, NELM strings, each of length L (default 40), are read.

The aai record is similar to the waveform record, but may be more efficient, because the low level driver may use DMA to fill the record. Aai records must be enabled in EPICS base, which is not the case by default before EPICS version 3.14.12.

Array Analog Output (aao)

 record(aai, "$(NAME)") {
  field (DTYP, "regDev")
  field (OUT,  "@$(DEVICE):$(OFFSET)")
  field (NELM, "$(NUMBER_OF_ELEMENTS)")
  field (FTVL, "$(DATATYPE)")
  field (PINI, "YES")
 }

NELM elements of VAL are written to registers.

The default type T depends on FTVL. For example FTVL=LONG results in T=INT32.

If T is an integer type, e.g. T=INT32 but FTVL is either FLOAT or DOUBLE, then values are scaled so that fields LOPR and HOPR map to L=low and H=high respectively. However, the device support will never write any value lower than L or higher than H. If necessary the value will be saturated to the nearest limit.

If T=string then FTVL must be CHAR or UCHAR or STRING. For CHAR and UCHAR, L=length can be specified but defaults to NELM and should not exceed it. If L is less than NELM, the remaining elements are ignored. For STRING, NELM strings, each of length L (default 40), are written.

The aai record is similar to the waveform record, but may be more efficient, because the low level driver may use DMA to fill the record. Aai records must be enabled in EPICS base, which is not the case by default before EPICS version 3.14.12.

Driver Functions

Registration

A driver must implement the API functions it wants to support and fill the function pointers into a regDevSupport structure. It contains the functions report, getInScanPvt, getOutScanPvt, read, and write. Write NULL for any API function that is not implemented by the driver. The support structure can be global and static like this:

static regDevSupport support = {
    report,
    getInScanPvt,
    getOutScanPvt,
    read,
    write
};

Each configured device must be registered with the following function:

int regDevRegisterDevice (const char* devicename, const regDevSupport* support, regDevice* device);

The devicename must be unique on the IOC and is used in the record links to reference the device. The support parameter is a pointer to the regDevSupport structure of this driver. The last parameter driver is a pointer to a driver private regDevice structure. It will be passed as a device handle to any API function. The driver can freely define struct regDevice and put in any information it needs to operate one device.

API functions

The driver can assume that all API functions are called in a thread save context. That means it will never happen that two API functions are called for the same device at the same time. However, API functions for different devices may be called at the same time.

void report (regDevice* device, int level);

IOSCANPVT getInScanPvt (regDevice* device, size_t offset, unsigned int vec, const char *user);

IOSCANPVT getOutScanPvt (regDevice* device, size_t offset, unsigned int vec, const char *user);

int read (regDevice* device, size_t offset, unsigned int datalength, size_t nelem, void* pdata, int priority, regDevTransferComplete callback, const char *user);

int  write (regDevice* device, size_t offset, unsigned int datalength, size_t nelem, void* pdata, void* pmask, int priority, regDevTransferComplete callback, const char *user);

  • offset is the address offset of the register relative to the beginning to the register block.
  • datalength is the length of the register in bytes (one element in case of arrays).
  • nelem is the number of elements in an array. It is 1 for scalar values but may be larger for arrays or strings. It may also be 0. In that case the driver shall just report the connection state by returning SUCCESS or an error code.
  • pdata is a pointer to a buffer of nelem*dlen bytes. registers values are copied to or from this buffer. If nelem==0, pdata may be NULL.
  • mask is a pointer to a bit mask of datalength bytes or NULL. Only those bits shall be modified where the mask contains 1 bits. All other bits shall remain unchanged.
  • priority is a number 0...2 and may be used as a hint for the driver to shedule requests.
  • callback is a function to be called upon request completion if the driver decides to handle the request asynchronously and returns ASYNC_COMPLETION. The driver may ignore it completely and handle all requests synchronously. The function may be NULL. In that case the driver must handle the request synchroously and not return ASYNC_COMPLETION. in wh
  • user is a string which can be used for messages. It is the record name (actually a pointer to the record itself). If the driver decides to use the callback, this pointer must be passed to the callback function.

Strings are handled as arrays of characters: datalength=1 and nelem is the buffer size including space for the terminating null byte.

1 Debugging

The variable regDevDebug can be set in the startup script or at any time on the command line to change the amount or debug output. The following levels are supported:

-1:  fatal errors only
 0:  errors only
 1:  startup messages
 2:+ output record processing
 3:+ input record processing
 4:+ driver calls
 5:+ io printout

Be careful using level>1 because many messages can introduce considerable delays which may result in connection losses. Default level is 0.

On vxWorks, regDevDebug can be set with regDevDebug=level

In the iocsh use var regDevDebug level


Dirk Zimoch, 2009
Author: Dirk Zimoch   Phone: +41 56 310 5182   Email: dirk.zimoch@psi.ch   Updated: 24.08.2021   Source: /afs/psi.ch/project/epics/webhosting/software/regDev/index.php