RegDev EPICS Device Support
RegDev EPICS Device Support Documentation
Contents
- Device Support
- Connection Status (bi)
- Analog Input (ai)
- Analog Output (ao)
- Calculation Output (calcout)
- Binary Input (bi)
- Binary Outpu (bo)t
- Multi Bit Binary Input (mbbi)
- Multi Bit Binary Output (mbbo)
- Multi Bit Binary Input Direct (mbbiDirect)
- Multi Bit Binary Output Direct (mbboDirect)
- Long Input (longin)
- Long Output (longout)
- Integer 64 bit Input (int64in)
- Integer 64 bit Output (int64out)
- String Input (stringin)
- String Output (stringout)
- Waveform Input (waveform)
- Array Analog Input (aai)
- Array Analog Output (aao)
- Driver Functions
- 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/a | n/a |
real64 float64 double |
64 bit (8 bytes) floating point number |
n/a | n/a |
string |
character array |
40 | n/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
|