EPICS: asynDriver devGpib
Instrument Device Support

Marty Kraimer and Eric Norum

April 1, 2004


Contents

Introduction
Acknowledgments
Install and Build
Using Instrument Support
Creating Instrument device support
devCommonGpib
devSupportGpib
Changes to previous EPICS GPIB support
General GPIB Problems
License Agreement

License Agreement

This product is available via the open source license described at the end of this document


Introduction

devGpib is the successor to the GPIB support that came with EPICS base 3.13. The 3.13 code was unbundled by Benjamin Franksen, and ultimately became gpibCore which is the 3.14 version of Benjamin's support. devGpib is the successor to the device support portion of gpibCore. The driver part of gpibCore is replaced by asynDriver and asynGpib.

This manual assumes that the reader is familiar with EPICS IOC record and device support and also with asynDriver.

devGpib is one method for implementing EPICS IOC device support for instruments. Other methods are available. If support for an instrument does not exist consider using STREAMS instead of devGpib.

The 3.13 GPIB support only worked with real GPIB instruments. Since devGpib uses low level asyn drivers, it can also work with other instruments, e.g. serial devices. Thus this manual uses the terminology instrument rather than GPIB device.

devGpib requires that a device support module be created for each supported instrument. Facilities are provided to make it relatively easy to write new support.

The following features are provided


Acknowledgments

John Winans
John provided the original EPICS GPIB support. Most databases using John's support can be used without modification. With some modification, device support modules written for John's support can be used.
Benjamin Franksen
John's support only worked on vxWorks. In addition the driver support was implement as a single source file. Benjamin defined an interface between drvCommon and low level controllers. The low level drivers became separate source modules that implemented the interface. He also created the original support for VXI11.
Eric Norum
Eric started with Benjamin's code and converted it to use the Operating System Independent features of EPICS 3.14.
Marty Kraimer
Marty started with Eric's version and made changes to support secondary addressing and to replace ioctl with code to support general bus management, universal commands, and addressed commands. He also made the conversion to using asyn drivers.

Install and Build

devGpib is bundled with the asynDriver. The code is in library asyn. Thus once asyn is included as part of an application devGpib will also be available. It is only necessary to include devGpib.dbd in your <app>Include.dbd file. In addition device support for your specific instruments must be installed.


Using Instrument Support

devGpib does not provide support for specific GPIB devices but for implementing such support. Within the EPICS community, support exists for many GPIB devices. The EPICS supported hardware list shows some of the support. If your device is not listed and a message to tech-talk does not provide any help then you will have to write your own device support. This section assume that an instrument support is available.

An EPICS record is connected to GPIB by the fields DTYP and INP or OUT.

The DTYP field has the format:

    field(DTYP,"<device support name>")

where

<device support name> - The name from a device database definition, i.e. a definition of the form:

    device(<record type>,GPIB_IO,<dsetName>,"<device support name>")

The INP or OUT field has the format:

    field(INP,"#L<link> A<addr> @<number>")
    or
    field(OUT,"#L<link> A<addr> @<number>")
where:
<link>
Link number. Low level drivers use portName to provide access to a specific communication interface.. In order to keep compatibility with link numbers, the portName MUST be Lxxx where xxx is the value that appears in the #Lxxx portion of the INP or OUT fields of record instances.
<addr>
GPIB address of your device, which can be a primary address or an extended address. A primary address has a value <=30. An extended address is of the form: PSS,where P represents the primary address and SS is the secondary address. It is not possible to express an extended address if the primary address is 0. Some examples are :
A9    primary address 9
A900  extended address: primary address is 9, secondary address is 0
A906  extended address: primary address is 9, secondary address is 6. 
<number>
An integer that identifies a gpibCmd definition in a GPIB device support module. If the implementer is nice documentation like the following is provided:
   recordType  @<number>   Description
If such documentation is not available, look at the device support itself for statements like:
  /* Param 12 */
  {&DSET_LI, GPIBREAD, IB_Q_LOW, "*ESR?",  "%ld", 0, 20, 0, 0, 0, 0, 0, 0},
The above states that @12 is a GPIB read command via a longin record. Thus the record definition would be:
    record(longin,"<name>") {
        field(DTYP,"<device support name>")
        ...
        field(OUT,"#L<link> A<addr> @12")
        ...
    }

Reports and Timeouts

In order to have these commands available, devGpib.dbd must be included as part of the applications xxxInclude.dbd file.

A report of all devGpib devices can be generated via the command:

    dbior("devGpib",level)

Three timeouts are defined:


Creating Instrument device support

This section describes how to write device support for an instrument. It is assumed that the reader is already familiar with the dialogue required to operate the instrument and EPICS record and device support.

Purpose

An instrument support module provides access to the operating parameters of the instrument.

Overview

Instruments typically have many operating parameters, each of which may be thought of in terms of an EPICS database record type. It is the job of the instrument support designer to map operating parameters to record types. Once this mapping is complete, an instrument support module can be written. For each operating parameter a gpibCmd must be created.

device DBD definition

For each instrument support module device definitions must be defined:
device(<record type>,<link type>,<DSET name>,"<DTYP name>")

where:

<record type>
The record type, e.g. ai,ao,...
<link type>
Link type. Must be GPIB_IO.
<DSET name>
Device Support Entry Table name. This is the external name defined in the device support code.
<DTYP name>
Device Type name. This is what appears in the DTYP field of record instances.

For example the definitions for the test supplied with devGpib are:

device(ai,GPIB_IO,devAiTestGpib,"GPIB Test")
device(ao,GPIB_IO,devAoTestGpib,"GPIB Test")
device(bi,GPIB_IO,devBiTestGpib,"GPIB Test")
device(bo,GPIB_IO,devBoTestGpib,"GPIB Test")
device(longin,GPIB_IO,devLiTestGpib,"GPIB Test")
device(longout,GPIB_IO,devLoTestGpib,"GPIB Test")
device(mbbi,GPIB_IO,devMbbiTestGpib,"GPIB Test")
device(mbbo,GPIB_IO,devMbboTestGpib,"GPIB Test")
device(stringin,GPIB_IO,devSiTestGpib,"GPIB Test")
device(stringout,GPIB_IO,devSoTestGpib,"GPIB Test")

For more information about device support, and also how to define INP or OUT links of records, see the EPICS Application Developers Guide.

Instrument Device Support Module

If you are writing a new instrument support module just:

An instrument device support module consists of DSET entries, an array of gpibCmds, efast tables (optional), name tables (optional), a devGpibParmBlock, a debugging flag, an init_ai routine, and custom conversion functions (optional.).

A simplified version of the skeleton file is:

/* devSkeletonGpib.c */
#include <devCommonGpib.h>
/* define all desired DSETs */
#define DSET_AI    devAiSkeletonGpib
#define DSET_BI    devBiSkeletonGpib
#define DSET_MBBI   devMbbiSkeletonGpib
#include <devGpib.h>    /* must be included after DSET defines */
#define TIMEOUT     1.0
#define TIMEWINDOW  2.0
/* example choices for BI */
/* example EFAST table */
static char *userOffOn[] = {"USER OFF;", "USER ON;", 0};
/* example devGpibNames */
static  char    *offOnList[] = { "Off", "On" };
static  struct  devGpibNames   offOn = { 2, offOnList, 0, 1 };
/* Array of structures that define all GPIB messages */
static struct gpibCmd gpibCmds[] =
{
  /* Param 0 */
  {&DSET_BO, GPIBEFASTO, IB_Q_HIGH,0,0, 0, 32,0, 0, 0, userOffOn, &offOn, 0},
  /* definitions for other parameters follow*/
};
/* The following is the number of elements in the command array above.  */
#define NUMPARAMS sizeof(gpibCmds)/sizeof(struct gpibCmd)
/* User MUST define init_ai */
static long init_ai(int parm)
{
    if(parm==0)  {
        devSupParms.name = "devSkeletonGpib";
        devSupParms.gpibCmds = gpibCmds;
        devSupParms.numparams = NUMPARAMS;
        devSupParms.timeout = TIMEOUT;
        devSupParms.timeWindow = TIMEWINDOW;
        devSupParms.respond2Writes = -1;
    }
}

The meaning of each portion of the code should become clear as you read the following sections:

DSET - Device Support Entry Tables

The following statements create the Device Support Entry Tables

#define DSET_AI    devAiSkeletonGpib
#define DSET_BI    devBiSkeletonGpib
#define DSET_MBBI   devMbbiSkeletonGpib
...
#include <devGpib.h>    /* must be included after DSET defines */

The actual DSETs are created by devGpib.h based on which DSET_xx definitions are defined. A #define must appear for each record type required. DSET_AI must be defined because an init_ai routine must be implemented as described below.

devCommonGpib provides device support for many standard record types. It is also possible to create custom support but this is more difficult.

gpibCmd definitions.

This is where the translation to and from the language of the instrument is defined. The actual table contains one element for each parameter that is made available to the user via the @<number> portion of an INP or OUT field.

In the example above the definitions for the gpibCmds are:

static struct gpibCmd gpibCmds[] =
{
  /* Param 0 */
  {&DSET_BO, GPIBEFASTO, IB_Q_HIGH, 0, 0, 0, 32,0, 0, 0, userOffOn, &offOn, 0},
  /* definitions for other parameters follow*/
};

This example defines a single command. A database record using this definition must define field OUT as

field(OUT,,"#L<link> A<addr> @0")

gpibCmd is

typedef struct gpibCmd {
    gDset *dset; /* used to indicate record type supported */
    int type;    /* enum - GPIBREAD...GPIBSRQHANDLER */
    short pri;   /* request priority IB_Q_LOW, IB_G_MEDIUM, or IB_Q_HIGH */
    char *cmd;   /* CONSTANT STRING to send to instrument */
    char *format;/* string used to generate or interpret msg */
    int rspLen;  /* room for response error message */
    int msgLen;  /* room for return data message length */
    /*convert is optional custom routine for conversions */
    int (*convert) (gpibDpvt *pgpibDpvt,int P1, int P2, char **P3);
    int P1;      /* P1 plays a dual role: */
                 /*      For EFAST it is set internally to the
                 /*      number of entries in the EFAST table */
                 /*      For convert it is passed to convert() */
    int P2;      /* user defined parameter passed to convert() */
    char **P3;   /* P3 plays a dual role: */
                 /*      For EFAST it holds the address of the EFAST table */
                 /*      For convert it is passed to convert() */
    devGpibNames *pdevGpibNames; /* pointer to name strings */
    char * eos; /* input end-of-string */
} gpibCmd;

where

dset
Address of the Device Support Entry Table (DSET) that describes the record type supported by the table entry..
type
Type of GPIB I/O operation that is to be performed. The type field must be set to one of the enumerated values declared in devSupportGpib.h, i.e. GPIBREAD,...,GPIBSRQHANDLER. See next section for the definitions.
pri
Processing priority of the I/O operation. Must be IB_Q_HIGH, IB_Q_MEDIUM, or IB_Q_LOW.
cmd
Constant string that is used differently depending on the value of type. See the discussion of type below. Set this field to 0 if not used.
format
Printf/scanf format string that is used differently depending on the value of type. See the discussion of type. Set this field to 0 if not used.
rspLen
Size needed to read back from a device when performing a respond2Writes read operation. If a gpibCmd is a read operation or a write operation that does not respond then set this field to zero. When devGpib initializes it finds the maximum rspLen of all commands associated with a port instance and allocates storage of that size.
msgLen
Size needed to read or write from a device. If not needed set it to zero. When devGpib initializes it finds the maximum msgLen of all commands associated with a port instance and allocates storage of that size.
convert
A conversion function that has the prototype:
 int (*convert) (gpibDpvt *pgpibDpvt,int P1, int P2, char **P3);
The use depends on the pgpibCmd->type. See below for details. Set to 0 when no conversion function is present.

If a convert routine finds an error it should do the following:

P1
This plays a dual role.

For EFAST operations it is set equal to the number of entries in the efast table by devSupportGpib:init_record.

For other operations, it is an integer passed to the conversion function specified in convert.

P2
Integer passed to the conversion function specified in convert.
P3
This field plays a dual role.

When type is one of the EFAST operations, this field points to the EFAST table. See the EFAST operation descriptions under the type field definitions and the section "Efast Tables" for more on the use of this field. Set this field to 0 when it is not used.

For other operations, it is passed to the conversion function specified in convert. It has a char** value.

pdevGpibNames
Pointer to a Name Table. Name tables are described in the section "Name Tables". Set this to 0 when no Name Table is used.
eos
Input message termination string. It can be:
0
A null pointer means that the read operation does not look for end of message termination characters.
""
An empty string signifies that a single null character ('\0') is the terminator.
non-empty string (e.g. "\n")
The termination characters (not including the null character terminating the string). For example, "\n" sets the message terminator to a single ASCII newline ('\n') character. Some drivers, e.g. the vxi11, allow only a single termination character.

Definitions for pgpibCmd->type

The following describes the semantics for the device support provided by the devGpib support provided with asynDriver. If an application extends this support it must to document its changes.

GPIBREAD

Supports record types: ai, bi, event, longin, mbbi, mbbiDirect, stringin, and waveform. For all of these types the following is done.

If convert is defined then:

If convert is not defined then what is done depends on the record type.

GPIBWRITE

Supports record types: ao, bo, longout, mbbo, mbboDirect, stringout, and waveform.

GPIBCVTIO

Supports record types: ai,ao,bi,bo,event,longin.longout, mbbi,mbbo,mbbiDirect,mmboDirect,stringin,stringout,waveform.

All I/O is done by the convert routine, which must be defined. convert is called by a callback routine and thus can make an arbitrary number of calls to low level drivers. It is passed the address of gpibDpvt which contains the information needed to call the low level drivers: asynCommon, asynOctet, and asynGpib. Note that asynGpib may not be present, i.e. pasynGpib is null. gpibDpvt also contains a field pupvt which can be used by the convert routine. Is is initialized to null. The macro gpibCmdGet can be used to get the address of gpibCmd which contains other usefull information.

If the end of string terminator needs to be changed, it must be changed by calling pdevSupportGpib->setEos rather than calling pgpibDpvt->pasynOctet->setEos

Efast (Enumerated Fast I/O) Tables

A device's command set often has things like "OFF" or "ON" in the command string. It is convenient to issue such commands via binary and multibit binary records. Efast tables specify such strings. Simply specifies the string value for each of the possible states of the VAL field of the record.

The format of an efast table is:

    static char  *tableName[] = {
        "OFF",    /* when VAL = 0 */
        "ON",    /* when VAL = 1 */
        0        /* list terminator */
}; And is referenced in an output parameter table entry like this:
    {&DSET_BO, GPIBEFASTO, IB_Q_HIGH, 0, 0, 0, 0,0, 0, 0, tableName, 0, 0},
For an input entry, it would look like this:
    {&DSET_BI, GPIBEFASTI, IB_Q_HIGH, "<command>", 0, 0, 50, 0, 0, 0, tableName, 0, 0},
The efast table MUST be 0 terminated.

For outputs the VAL field is used to index into the efast table and select a string to send to the instrument. If cmd is 0 the string is sent to the instrument exactly as as it appears in the efast table If cmd is defined then that string is prepended to the string obtained from the efast table.

For input operations, the cmd string is sent to the instrument, and msg is read from the instrument. msg is compared against each of the entries in the efast table starting at the zeroth entry. The slot number of the first table entry that matches the response string is used as the setting for the RVAL field of the record. When strings are compared, they are compared from left to right until the number of characters in the efast table are checked. When ALL of the characters up to but NOT including the 0 of the string in the efast table match the corresponding characters of the response string, it is considered a valid match. This allows the user to check response strings fairly fast. For example, it a device returns something like "ON;XOFF;9600" or "OFF;XOFF;9600" in response to a status check, and you wish to know if the first field is either "OFF" or "ON", your efast table could look like this:

    static char *statCheck[] = {
        "OFF",      /* set RVAL to 0 */
        "ON",      /* set RVAL to 1 */
        0};     /* list terminator */
The 0 field is extremely important. If it is omitted and the instrument gets confused and responds with something that does not start with an "OFF" or "ON", the GPIB support library code will end up running off the end of the table.

In the case when none of the choices in an efast table match for an input operation the record is placed into a INVALID alarm state.

devGpibNames - Name Table

For binary and multibit binary records the choice fields of a record can be assigned values at record initialization. If pdevGpibNames has a value, then when record is initialized, the instrument support uses the associated name table to assign values to choice fields that have not been assigned values. These name tables have nothing to do with I/O operations.

devGpibNames is:

struct devGpibNames {
    int count;            /* CURRENTLY only used for MBBI and MBBO */
    char **item;
    unsigned long *value; /* CURRENTLY only used for MBBI and MBBO */
    short nobt;           /* CURRENTLY only used for MBBI and MBBO */
};

To use a name table, the address of the table must be put into pdevGpibNames of the parameter table. The table format for a multibit record type looks like this:

    static char *tABCDList[] = {
        "T",                 /* zrst*/
        "A",                 /* onst */
        "B",                 /* twst */
        "C",                 /* thst */
        "D"};                /* frst */

    static unsigned long tABCDVal[] = {
        1,                  /* zrvl */
        2,                  /* onvl */
        3,                  /* twvl */
        5,                  /* thvl */
        6 };                /* frvl */

    static devGpibNames tABCD = {
        5,                  /* number of elements in string table */
        tABCDList,          /* pointer to string table */
        tABCDVal,           /* pointer to value table */
        3 };                /* value for the nobt field */
The table format for a binary record type looks like this:
    static char *disableEnableList[] = { 
        "Disable",          /* znam */
        "Enable" };         /* onam */

    static devGpibNames disableEnable = {
        2,                  /* number of elements */
        disableEnableList,  /* pointer to strings */
        0,               /* pointer to value list */
        1};                 /* number of valid bits */
devGpibNames is defined in devSupportGpib.h. For binary records, the strings are placed into the name fields in order from lowest to highest as shown above. For multibit binary records, up to sixteen strings can be defined. A devGpibNames structure referencing these strings is then defined.

value and nobt are not used for binary record types, but should be specified anyway as if the binary record was a multibit binary record with only 2 values.

For multibit record types, the name strings, values, and NOBT fields are filled in from the devGpibNames information. For binary record types, only the znam and onam fields are filled in.

Name strings (and their associated values in the multibit cases) are not filled in if the database designer has assigned them values..

Defining the devGpibParmBlock.

Each DSET of the instrument support contains the address of a devGpibParmBlock. init_aie MUST initialize this structure.
static long init_ai(int parm)
{
    if(parm==0) {
        devSupParms.name = "devSkeletonGpib";
        devSupParms.gpibCmds = gpibCmds;
        devSupParms.numparams = NUMPARAMS;
        devSupParms.timeout = TIMEOUT;
        devSupParms.timeWindow = TIMEWINDOW;
        devSupParms.respond2Writes = -1;
    }
    return(0);
}

devGpibParmBlock is:

struct devGpibParmBlock {
    char *name;         /* Name of this device support*/
    gpibCmd *gpibCmds;  /* pointer to gpib command list */
    int numparams;  /* number of elements in the command list */
    double timeout; /* seconds to wait for I/O */
    double timeWindow;  /* seconds to stop I/O after a timeout*/
    int respond2Writes; /* set to true if a device responds to writes */
    /*The following are set by devSupportGpib*/
    int  msgLenMax;     /*max msgLen all commands*/
    int  rspLenMax;     /*rsp rspLen all commands*/
};
gpibCmds
name
The device support module type name. Used only to generate diagnostic messages.
Address of an array of gpibCmd definitions..
numparams
The number of gpibCmds defined. A macro should be used to set it's value.
timeout
timeout in seconds for an individual I/O operation.
timeWindow
The number of seconds after a timeout before a new I/O operation will issued to the device. During this time window, any I/O operations directed to the timed out device will result in an error and the appropriate alarm status will be raised for the record (either READ_ALARM or WRITE_ALARM depending on the record type and VALID_ALARM in all cases.)
respond2Writes
This field is for devices that echo writes. If respond2Writes is >=0 then after an output command the following is done:
msgLenMax
This is set by devGpibSupport. The value is that of the maximum size input message, i.e. the largest msgLen defined in gpibCmds.
rspLenMax
This is set by devGpibSupport. The value is that of the maximum size response message, i.e. the largest rspLen defined in gpibCmds.

SRQ Processing

If the low level device driver implements interface asynGpib then devSupportGpib registers itself to handle SRQs. It defines two types of SRQs: solicited and unsollicited. Solicited SRQs are for GPIBREADW and GPIBEFASTIW commands. If an SRQ is raised for a gpibAddr that does not have an outstanding GPIBREADW or GPIBEFASTIW command, the SRQ is considered unsolicited.

When the devSupportGpib receives an unsolicited SRQ and it has a registered handler for the gpibAddr, it calls the handler, otherwise it issues a message that it received an unsolicited SRQ.

The device support for the longinRecord supports gpibCmd type GPIBSRQHANDLER. It calls the devSupportGpib registerSrqHandler method. When it's srqHandler gets called it puts the SRQ status byte into the VAL field and then makes a request to process the record. By forward linking this record to other records the user can handle SRQs from specific devices.


devCommonGpib

The facilities described above should satisfy most gpib requirements. This section and the next explain how to provide additional support. For example support could be written for additional record types. This section explains devCommonGpib and the next section devSupportGpib.

devCommonGpib provides support for specific record types by making calls to devSupportGpib. As explained above an instrument support module must define some combination of DSET_AI,...,DSET_WF and then include devGpib.h. devGpib.h defines DSETs (Device Support Entry Tables) that refer to methods implemented in devCommonGpib.c

If you want to write additional support code that can be used for writing instrument specific device support, the easiest way is to start with code in devCommonGpib and modify it

devGpib provides three header files:


devSupportGpib

devSupportGpib.h describes all the public structures used by devGpib. The structures gpibCmd, devGpibNames, and devGpibParmBlock were described above. They are needed to create an instrument device support module. The remaining structures are used by devCommonGpib, devSupportGpib, or other support. These structures are: gDset, gpibDpvt, devGpibPvt, and devSupportGpib.

gDset

A gDset is an "overloaded" definition of a DSET. A gDset looks to iocCore like a regular DSET but it has an additional field that is the address of a devGpibParmBlock.

struct gDset
{
    long number;
    DEVSUPFUN funPtr[6];
    devGpibParmBlock *pdevGpibParmBlock;
};

where

number
This is required for a DSET. It should always be initialized to 6.
funPtr
Pointers to device support functions. Allowing for 6 is sufficient for all the standard types in EPICS base.
pdevGpibParmBlock
The address of the devGpibParmBlock for this instrument.

gpibDpvt

The dpvt field of a record with devGpib device support contains the address of a gpibDpvt

struct gpibDpvt
{
    devGpibParmBlock *pdevGpibParmBlock;
    CALLBACK callback;
    dbCommon *precord;
    asynUser *pasynUser;
    asynCommon *pasynCommon;
    void *pasynCommonPvt;
    asynOctet *pasynOctet;
    void *pasynOctetPvt;
    asynGpib *pasynGpib;
    void *pasynGpibPvt;
    int parm;                 /* parameter index into gpib commands */
    char *rsp;                /* for read/write message error responses */
    char *msg;                /* for read/write messages */
    int  msgInputLen;         /* number of characters in last READ*/
    int efastVal;             /* For GPIBEFASTxxx */
    void     *pupvt;          /*private pointer for custom code*/
    devGpibPvt *pdevGpibPvt;  /*private for devGpibCommon*/
};

where

pdevGpibParmBlock
Address of the devGpibParmBlock. It is the same value as pgDset->pdevGpibPvt
callback
For use by requestProcessCallback.
precord
Address of the record.
pasynUser
Address of an asynUser which is needed to call asynDriver support.
pasynCommon,pasynCommonPvt
Used to call asynCommon methods. If initialization is successful these will have a value.
pasynOctet,pasynOctetPvt
Used to call asynOctet methods. If initialization is successful these will have a value.
pasynGpib,pasynGpibPvt
Used to call asynGpib methods. These may be 0 even if initialization is successful. If a record that requires asynGpib is processed and pasynGpib is 0 then an error message is generated and the record put into alarm.
parm
The value of the parm field of INP or OUT. It locates the gpibCmd for its record.
rsp
If respond2Writes is >=0 and rspLen is >0, this is the buffer that holds the read after each write..
msg
A buffer of length msgLen. It is used by several GPIBXXX request types.
msgInputLen
The length of the last input message.
efastVal
Used for GPIBEFASTO, GPIBEFASTI, GPIBEFASTIW. For GPIBEFASTO the device support sets efastVal from RVAL or VAL before queuing an I/O request. For GPIBEFASTI and GPIBEFASTIW the device support set RVAL or VAL from efastVal after an I/O request completes.
pupvt
This is a private pointer for use by specialized instrument support.
pdevGpibPvt
This is used by devSupportGpib. Note that the structure is described in devSupportGpib.c, i.e. it is private.

devSupportGpib

describes methods implemented by devSupportGpib.c.

/* If a method returns int then (0,-1) => (OK, failure) */
typedef void (*gpibWork)(gpibDpvt *pgpibDpvt,int failure);
typedef int (*gpibStart)(gpibDpvt *pgpibDpvt,int failure);
typedef void (*gpibFinish)(gpibDpvt *pgpibDpvt,int failure);

typedef int (*gpibWork)(gpibDpvt *pgpibDpvt,int failure);
struct devSupportGpib {
    long (*initRecord)(dbCommon *precord, struct link * plink);
    void (*processGPIBSOFT)(gpibDpvt *pgpibDpvt);
    void (*queueReadRequest)(gpibDpvt *pgpibDpvt,gpibStart start,gpibFinish finish);
    void (*queueWriteRequest)(gpibDpvt *pgpibDpvt,gpibStart start, gpibFinish finish);
    /* queueRequest returns (0,1) for (failure,success) */
    int (*queueRequest)(gpibDpvt *pgpibDpvt, gpibWork work);
    void (*registerSrqHandler)(
        gpibDpvt *pgpibDpvt,srqHandler handler,void *userRivate);
    int (*writeMsgLong)(gpibDpvt *pgpibDpvt,long val);
    int (*writeMsgULong)(gpibDpvt *pgpibDpvt,unsigned long val);
    int (*writeMsgDouble)(gpibDpvt *pgpibDpvt,double val);
    int (*writeMsgString)(gpibDpvt *pgpibDpvt,const char *str);
    int (*readArbitraryBlockProgramData)(gpibDpvt *pgpibDpvt);
};
epicsShareExtern devSupportGpib *pdevSupportGpib;
gpibWork
Prototype of a function that is called when a request is dequeued by asynManager. This can be either a function passed to queueRequest or an internal function that devGpibSupport uses to implement queueReadRequest and queueWriteRequest
gpibStart
If queueReadRequest or queueWriteRequest is called the internal gpibWork function calls the start routine before it processes the request.
gpibFinish
If queueReadRequest or queueWriteRequest is called the internal gpibWork function calls the finish routine when it is done processing.
initRecord
This initializes all the devGpib structures attached to the record starting with dbCommon.dpvt.
processGPIBSOFT
If pgpibCmd->type is GPIBSOFT just call this method.
queueReadRequest
This handles GPIBREADW, GPIBEFASTIW, GPIBREAD, GPIBEFASTI, and GPIBRAWREAD. The only thing device support needs to provide is work functions to do record specific processing of the received message. The start function is called before devGpibSupport issues write/reads and finish is called after the read. start is optional and not usually required for read operations. The msg buffer MUST not be accessed except via the start or finish work functions.
queueWriteRequest
The handles GPIBWRITE, GPIBCMD, GPIBACMD, and GPIBEFASTO. Device support provides a start and finish work function. The start function is called before devGpibSupport issues a write and finish is called after the write. The msg buffer MUST not be accessed except via the start or finish work functions. start must set msg or efastVal depending on the pgpibCmd->type.
queueRequest
The device support provides a work function that is called when the request is dequeued. queueRequest returns (0,1) if the request (was not, was) queued.
report
This generates a report or all devGpib devices.
registerSrqHandler
Registers an SRQ handler for gpibAddr.
writeMsgLong,writeMsgULong,writeMsgDouble,writeMsgString
pgpibDpvt->msg is written from pgpibCmd->format and the value passed by the caller.
readArbitraryBlockProgramData
Reads zero or more preamble (non-#) characters followed by IEEE-488.2 definite-length arbitrary block program data followed by the end-of-string specified by the active gpibCmd entry and stores the entire response in pgpibDpvt->msg. This routine is intended for use by custom input functions to read a block of arbitrary data from a serial-line device.
pdevSupportGpib
An external variable which points to devSupportGpib

Changes to previous EPICS GPIB support

gpibCmd

GPIBEOS is no longer present and the last field of gpibCmd is now called eos. Previously the last field was an int that was supposed to be initialized to -1 or to a single termination character for reads. Eos is a char *. If no terminator is desired then eos should be 0, i.e. a NULL pointer. If old support initilizes this to -1 compiler warnings will be generated and a core dump is likely when the application is run.

devGpibParmBlock

EXTREMELY IMPORTANT: Previously timeouts were in units of 1/60th of a second or milliseconds. Now timeouts are in seconds. For example 3.13 device support had definitions like:

#define TIME_WINDOW     120     /* 2 seconds  */
#define DMA_TIME        60      /* 1 second   */

Now the definitions would be:

#define TIMEWINDOW  2.0  /* wait 2 seconds after device timeout */
#define TIMEOUT     1.0  /* I/O must complete within 1 second */

The fields in devGpibParmBlock have been reordered and some have gone away. In addition the timeout fields are now of type double and have units of seconds. The following fields have disappeared:

debugFlag
This is an obsolete field since devGpib uses the asynTrace facility.
magicSrq
This is an obsolete field.
srqHandler
SRQs are handled differently than before. It is no longer possible for device support code to provide it's own SRQ handler for ALL gpib devices attached to the gpib interface. It is possible, as described above to register a handler for a specific gpibAddr.
wrConversion
The secondary conversion routine is no longer supported. It was used if respond2Writes is >=0.

Older device support initialized devGpibParmBlock with a C initializer statement:

static struct devGpibParmBlock devSupParms = {
&xxxDebug,
... 
};

Newer support initialized it by a function:

static long
init_dev_sup(parm)
int     parm;
{
  if(parm==0)  {
    devSupParms.debugFlag = &xxxDebug;
    ...

Now this is done via the function:

static long init_ai(int parm)
{
  if(parm==0)  {
    devSupParms.name = "devAB300";
    devSupParms.gpibCmds = gpibCmds;
    devSupParms.numparams = NUMPARAMS;
    devSupParms.timeout = TIMEOUT;
    devSupParms.timeWindow = TIMEWINDOW;
    devSupParms.respond2Writes = 0;
  }
  return 0;
}

The older support must be modified to use drvGpib.h and to implement init_ai.

devGpib.h

Instead of using devGpib.h, older device support created it's own DSET_xxx definitions. Remove all of these and use the technique of defining DSET_AI as shown above

Other changes

In a device support module used GPIBIOCTL it will have to be changed to use one on the new GPIBxxx command types.

The old drvGpib support is replaced by asynDriver and asynGpib.

If old support makes calls to the old devGpibLib_xxx routines then they must be replaced by calls to devSupportGpib routines.


General GPIB Problems

NOTE: The following comments are from John Winan's original GPIB documentation.

Every type of communication system has its problems. Some instrument vendors don't properly test the GPIB interfaces on their products. Some devices miss messages or commands they are too close together in time. There are handshaking lines that are supposed to throttle the speed, but are apparently improperly implemented by device vendors, or make the (wrong) assumption that the controller in charge is slow in its ability to burst bytes down the bus. The only way that this problem can be worked around is to add delays in the GPIB device device support modules. The current device support library does not provide any means to do this.

Very often, a device will slow down over 800% when a user presses a button on the front panel of the device. This can cause the GPIB message transfer to time out, alarms to be set and so on. When devices of this type have to be used, operators will have to be instructed to "look, but don't touch."

Some devices like to go out to lunch once every hour, or day or so and not respond to a command for up to 5 seconds or so (the DG 535 has done this on more than one occasion.) This can be more frustrating that anything else. All that can be said about these types of things is BEWARE of machines that don't work as advertised. There is probably something wrong with it that won't surface until it is in use and controlling something very important.

Test, test, and test your devices after writing a new device support module. Many devices can run fine if doing only three or five transactions per second, but crank it up to 50 or more, and watch it go down in flames. Even if all the records in an EPICS database are scanned slowly, they can still get processed in bursts. EPICS can actually process over 20,000 records in one second if they are all ready to go at the same time. And if there are enough records tied to the same device there is no telling how fast the device will be pushed.


License Agreement

Copyright (c) 2002 University of Chicago, The Regents of the
University of California, and Berliner Elektronenspeicherring
Gesellschaft fuer Synchrotronstrahlung m.b.H. (BESSY) All rights
reserved.

asynDriver is distributed subject to the following license conditions:

 SOFTWARE LICENSE AGREEMENT
 Software: asynDriver

 1. The "Software", below, refers to asynDriver (in either source code, or
    binary form and accompanying documentation). Each licensee is
    addressed as "you" or "Licensee."

 2. The copyright holders shown above and their third-party licensors
    hereby grant Licensee a royalty-free nonexclusive license, subject to
    the limitations stated herein and U.S. Government license rights.

 3. You may modify and make a copy or copies of the Software for use
    within your organization, if you meet the following conditions:
      a. Copies in source code must include the copyright notice and this
         Software License Agreement.
      b. Copies in binary form must include the copyright notice and this
         Software License Agreement in the documentation and/or other
         materials provided with the copy.

 4. You may modify a copy or copies of the Software or any portion of it,
    thus forming a work based on the Software, and distribute copies of
    such work outside your organization, if you meet all of the following
    conditions:
      a. Copies in source code must include the copyright notice and this
         Software License Agreement;
      b. Copies in binary form must include the copyright notice and this
         Software License Agreement in the documentation and/or other
         materials provided with the copy;
      c. Modified copies and works based on the Software must carry
         prominent notices stating that you changed specified portions of
         the Software.

 5. Portions of the Software resulted from work developed under a U.S.
    Government contract and are subject to the following license: the
    Government is granted for itself and others acting on its behalf a
    paid-up, nonexclusive, irrevocable worldwide license in this computer
    software to reproduce, prepare derivative works, and perform publicly
    and display publicly.

 6. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT WARRANTY
    OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY LICENSORS, THE
    UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND THEIR
    EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
    BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
    FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME
    ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS,
    OR USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE
    SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT
    THAT THE SOFTWARE WILL FUNCTION UNINTERRUPTED, THAT IT IS ERROR-FREE
    OR THAT ANY ERRORS WILL BE CORRECTED.

 7. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR
    THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT
    OF ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
    CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE,
    INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY
    REASON WHATSOEVER, WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF
    CONTRACT, TORT (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR
    OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
    POSSIBILITY OF SUCH LOSS OR DAMAGES.