V4 Design: driverInterfaces
EPICS V4: driverInterfaces - Communication beween record support and drivers
July 14 2005
Overview
This is the first attempt at defining how EPICS V4 record support will interface to driver support.
What follows is taken from asynDriver. Since this is being prepared for a talk to be given in about two hours, the names still use asyn and everything is C rather than C++. Thus many syntax changes are required but the basic ideas are what is proposed for EPICS V4.
For V4 the V3 concept of record support calling device suppport calling driver support will no longer exist. Instead drivers will implement one or more interfaces. Record support communicates with drivers via these interfaces.
The interfaces are cdivided into two basic classes:
- message based - Communication is via ascii messages.
- register based - Communication is via primitive data types or arrays of primitive types.
Standard Message Interfaces
These are interfaces for communicating with message based devices, where message based means that the device communicates via octet strings, i.e. arrays of 8 bit bytes. Three interfaces are provided: asynOctet, asynOctetBase, and asynOctetSyncIO. asynOctet is generic message based interface. asynOctetBase is an interface used by port drivers that implement asynOctet. It's primary putpose is to help with interrupt support. asynOctetSyncIO provides a synchronous inteface to asynOctet and can be used by code that is willing to block.
asynOctet
asynOctet describes the methods implemented by drivers that use octet strings for sending commands and receiving responses from a device. This interface is for records like stringin, stringout, and waveform.
NOTE: The name octet is used instead of ASCII because it implies that communication is done via 8-bit bytes.
#define ASYN_EOM_CNT 0x0001 /*Request count reached*/ #define ASYN_EOM_EOS 0x0002 /*End of String detected*/ #define ASYN_EOM_END 0x0004 /*End indicator detected*/ typedef void (*interruptCallbackOctet)(void *userPvt, asynUser *pasynUser, char *data,size_t numchars, int eomReason); typedef struct asynOctetInterrupt { asynUser *pasynUser; int addr; interruptCallbackOctet callback; void *userPvt; }asynOctetInterrupt; #define asynOctetType "asynOctet" typedef struct asynOctet{ asynStatus (*write)(void *drvPvt,asynUser *pasynUser, const char *data,size_t numchars,size_t *nbytesTransfered); asynStatus (*writeRaw)(void *drvPvt,asynUser *pasynUser, const char *data,size_t numchars,size_t *nbytesTransfered); asynStatus (*read)(void *drvPvt,asynUser *pasynUser, char *data,size_t maxchars,size_t *nbytesTransfered, int *eomReason); asynStatus (*readRaw)(void *drvPvt,asynUser *pasynUser, char *data,size_t maxchars,size_t *nbytesTransfered, int *eomReason); asynStatus (*flush)(void *drvPvt,asynUser *pasynUser); asynStatus (*registerInterruptUser)(void *drvPvt,asynUser *pasynUser, interruptCallbackOctet callback, void *userPvt, void **registrarPvt); asynStatus (*cancelInterruptUser)(void *registrarPvt, asynUser *pasynUser); asynStatus (*setInputEos)(void *drvPvt,asynUser *pasynUser, const char *eos,int eoslen); asynStatus (*getInputEos)(void *drvPvt,asynUser *pasynUser, char *eos, int eossize, int *eoslen); asynStatus (*setOutputEos)(void *drvPvt,asynUser *pasynUser, const char *eos,int eoslen); asynStatus (*getOutputEos)(void *drvPvt,asynUser *pasynUser, char *eos, int eossize, int *eoslen); }asynOctet; /* asynOctetBase does the following: calls registerInterface for asynOctet. Implements registerInterruptUser and cancelInterruptUser Provides default implementations of all methods. registerInterruptUser and cancelInterruptUser can be called directly rather than via queueRequest. */ #define asynOctetBaseType "asynOctetBase" typedef struct asynOctetBase { asynStatus (*initialize)(const char *portName, asynDriverasynInterface *pint32Interface, int processEosIn,int processEosOut,int interruptProcess); void (*callInterruptUsers)(asynUser *pasynUser,void *pasynPvt, char *data,size_t *nbytesTransfered,int *eomReason); } asynOctetBase; epicsShareExtern asynOctetBase *pasynOctetBase;
asynOctet has the following methods:
- write
- Send a message to the device. *nbytesTransfered is the number of 8-bit bytes sent to the device. Interpose or driver code may add end of string terminators to the message but the extra characters are not included in *nbytesTransfered.
- writeRaw
- Send a message to the device. *nbytesTransfered is the number of 8-bit bytes sent to the device. Interpose or driver code must not add end of string terminators to the message.
- read
- Read a message from the device. *nbytesTransfered is the number of 8-bit bytes read from the device. If read returns asynSuccess than eomReason tells why the read completed. Interpose or driver code may strip end of string terminators from the message. If it does the first eos character will be replaced by null and the eos characters will not be included in nbytesTransfered.
- readRaw
- Read a message from the device. *nbytesTransfered is the number of 8-bit bytes read from the device. If read returns asynSuccess than eomReason tells why the read completed. Interpose or driver code must not strip end of string terminators from the message.
- flush
- Flush the input buffer.
- registerInterruptUser
- Register a user that will be called whenever a new message is received. NOTE: The callback must not block and must not call registerInterruptUser or cancelInterruptUser.
- cancelInterruptUser
- Cancel a registered user.
- setInputEos
- Set End Of String for input. For example "\n". Note that gpib drivers usually accept at most a one character string.
- getInputEos
- Get the current End of String.
- setOutputEos
- Set End Of String for output.
- getOutputEos
- Get the current End of String.
asynOctetBase is an interface and implementation for drivers that implement interface asynOctet. asynOctetBase implements registerInterruptUser and cancelInterruptUser.
For single device support, it can optionally implement interrupt support. A driver that implements interrupts must call registerInterruptSource. If it asks asunOctetBase to handle interrupts it calls asynOctetBase:callInterruptUsers when it has new data.
For single device support asynOctetBase can optionally call asynInterposeEosConfig to handle end of string processing for input and/or output.
Any null method in the interface passed to initialize are replaced by a method supplied by asynOctetBase.
asynOctetBase has the following methods:
- initialize
- After a driver calls registerPort is can call: "pasynOctetBase->initialize(...". Any null methods in the asynInterface are replaced by default implementations. asynOctetBase always implements registerInterruptUser and cancelInterruptUser. If the port is not multi-device and either processEosIn or processEosOut is specified, asynInterposeEosConfig is called. If the port is not multi-device and interruptProcess is specified, then whenever read or readRaw is called, asynBase calls all the registered interrupt users. asynOctetBase can not implement processEosIn, processEosOut, and interruptProcess if the port is a multi-device port. Since this method is called only during initialization it can be called directly rather than via queueRequest.
- callInterruptUsers
- Calls the callbacks registered via registerInterruptUser.
Standard Register Interfaces
Introduction
This section descibes interfaces for register based devices. Support is provided for:
- Int32 - registers appear as 32 integers
- UInt32Digital - registers appear a 32 bit unsigned integers and masks can be used to address specific bits.
- Float64 - registers appear as double precision floats.
- Int32Array - Arrays of 32 bit integers.
- Float64Array - Arrays of double precision floats.
Lets use Int32 as an example. The interfaces for Int32 are intended for ADC or DAC register based devices. This the ai, ao, etc records would use this interface. Since communication is done via interfaces, other specialized records can also use this interface.
For Int32 the following are provided.
- asynInt32 - An interface with methods: read, write, getBounds, registerInterruptUser, and cancelInterruptUser.
- asynInt32Base - An interface used by drivers that implement asynInt32. It also has an implementation that:
- registers the asynInt32 interface
- has default methods for read, write, and getBounds. A null method in the interface passed to initialize is replaced by a method implemented by asynInt32Base.
- implements registerInterruptUser and cancelInterruptUser. The caller should leave these methods null because asynInt32Base always replaces them by it's implementation.
- Drivers that implement asynInt32 normally call asynInt32Base:initialize. It implements registerInterruptUser and cancelInterruptUser. If the driver provides interrupt support it must:
- Call "initialize"
- Call "pasynManager->registerInterruptSource"
- Interact with asynManager to call the users that have registered with asynInt32Base:registerInterruptUser
addr - What does it mean for register based interfaces?
Low level register based drivers are normally multi-device. The meaning of addr is:
- Int32 - The driver supports an array of Int32 values. addr selects an array element. For example a 16 channel ADC would support addr 0 through 15.
- Int32Array - Each addr is an array of Int32 values.
- Float64 - The driver supports an array of Float64 values. addr selects an array element.
- Float64Array - Each addr is an array of Float64 values.
- UInt32Digital - The driver supports an array of UInt32 values. addr selects an array element. For example a 128 bit digital I/O module appears as an array of four UInt32 registers.
asynInt32
asynInt32 describes the methods implemented by drivers that use integers for communicating with a device.
typedef void (*interruptCallbackInt32)(void *userPvt, asynUser *pasynUser, epicsInt32 data); typedef struct asynInt32Interrupt { int addr; asynUser *pasynUser; interruptCallbackInt32 callback; void *userPvt; } asynInt32Interrupt; #define asynInt32Type "asynInt32" typedef struct asynInt32 { asynStatus (*write)(void *drvPvt, asynUser *pasynUser, epicsInt32 value); asynStatus (*read)(void *drvPvt, asynUser *pasynUser, epicsInt32 *value); asynStatus (*getBounds)(void *drvPvt, asynUser *pasynUser, epicsInt32 *low, epicsInt32 *high); asynStatus (*registerInterruptUser)(void *drvPvt,asynUser *pasynUser, interruptCallbackInt32 callback, void *userPvt, void **registrarPvt); asynStatus (*cancelInterruptUser)(void *registrarPvt, asynUser *pasynUser); } asynInt32; /* asynInt32Base does the following: calls registerInterface for asynInt32. Implements registerInterruptUser and cancelInterruptUser Provides default implementations of all methods. registerInterruptUser and cancelInterruptUser can be called directly rather than via queueRequest. */ #define asynInt32BaseType "asynInt32Base" typedef struct asynInt32Base { asynStatus (*initialize)(const char *portName, asynInterface *pint32Interface); } asynInt32Base;
epicsShareExtern asynInt32Base *pasynInt32Base;
asynInt32 has the following methods:
- write
- Write an integer value to the device.
- read
- Read an integer value from the device.
- getBounds
- Get the bounds. For example a 16 bit ADC might set low=-32768 and high = 32767.
- registerInterruptUser
- Registers a callback that will be called whenever new data is available. Since it can be called directly rather than via a queueRequest this method must not block.
- cancelInterruptUser
- Cancels the callback. Since it can be called directly rather than via a queueRequest this method must not block.