/******************************************************************************
 *
 * devCommonGpib.c,v 
 *
 ******************************************************************************/
/***********************************************************************
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory, and the Regents of the University of
* California, as Operator of Los Alamos National Laboratory, and
* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY).
* gpibCore is distributed subject to a Software License Agreement
* found in file LICENSE that is included with this distribution.
***********************************************************************/
/*
 *		Common library for device specific support modules
 *
 * Current Author: Benjamin Franksen
 * Original Author: John Winans
 *
 ******************************************************************************
 *
 * Notes:
 *
 * This module should be still compatible to the original (JW's) version,
 * although this gets increasingly cumbersome.
 *
 */

#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>

#include <dbAccess.h>
#include <recGbl.h>
#include <alarm.h>
#include <cvtTable.h>
#include <dbDefs.h>
#include <dbAccess.h>
#include <dbScan.h>
#include <devSup.h>
#include <recSup.h>
#include <callback.h>
#include <drvSup.h>
#include <link.h>
#include <errlog.h>
#include <epicsThread.h>

#include "devCommonGpib.h"
static int readDeviceResponse(gpibDpvt * pgpibDpvt, int cmdType);

/* macros for accessing some commonly used fields*/
#define devGpibParmBlockGet(precord) \
    ((devGpibParmBlock *) \
     (((gDset *)((precord)->dset))->funPtr[(precord)->dset->number]))
#define cmdTypeGet(dpvt,pdevGpibParmBlock) \
    ((pdevGpibParmBlock)->gpibCmds[  \
        ((gpibDpvt *)(dpvt))->parm].type % GPIBEOS)
#define devGpibNamesGet(dpvt,pdevGpibParmBlock) \
    ((pdevGpibParmBlock)->gpibCmds[ \
     ((gpibDpvt *)(dpvt))->parm].pdevGpibNames)

void devGpibLib_dumpDpvt(gpibDpvt * pgpibDpvt)
{
    printf("%s.dpvt =\n", pgpibDpvt->precord->name);
    printf("  head.workStart = %p\n", (void *) pgpibDpvt->head.workStart);
    printf("  head.link = %d\n", pgpibDpvt->head.link);
    printf("  head.device = %d\n", pgpibDpvt->head.device);
    printf("  head.secondary = %d\n", pgpibDpvt->head.secondary);
    printf("  head.pibLink = %p\n", (void *) pgpibDpvt->head.pibLink);
    printf("  timeout = %d\n", pgpibDpvt->timeout);
    printf("  parm = %d\n", pgpibDpvt->parm);
    if (pgpibDpvt->rsp)
	printf("  rsp = \"%s\"\n", pgpibDpvt->rsp);
    if (pgpibDpvt->msg)
	printf("  msg = \"%s\"\n", pgpibDpvt->msg);
    printf("  precord = %p\n", (void *) pgpibDpvt->precord);
    printf("  phwpvt = %p\n", (void *) pgpibDpvt->phwpvt);
    printf("  pupvt = %p\n", pgpibDpvt->pupvt);
}

/*
 * See if operation follows too closely behind a timeout
 */
static int timeoutPause(gpibDpvt * pgpibDpvt, devGpibParmBlock * pdevGpibParmBlock)
{
    epicsTimeStamp now;
    if (pgpibDpvt->phwpvt->tmoFlag) {
	epicsTimeGetCurrent(&now);
	if ((epicsTimeDiffInSeconds(&now, &pgpibDpvt->phwpvt->tmoVal) * 1000.0)
            < pdevGpibParmBlock->timeWindow) return 1;
	pgpibDpvt->phwpvt->tmoFlag = 0;
    }
    return 0;
}

/******************************************************************************
 *
 * This is a little help function, used to parse enumerated response strings.
 *
 * Only as many bytes as are found in the enums array elements are compared
 * when searching for a match.  The \0 at the end of the enums array elements
 * is not considered in the comparison.
 *
 ******************************************************************************/
static int checkEnums(char *msg, char **enums)
{
    int i;
    int j;
    i = 0;
    while (enums[i] != NULL) {	/* check each enum until match found *//* will
				 * stop on first match */
	j = 0;	/* only check as much as found in enums[i] */
	while (enums[i][j] && enums[i][j] == msg[j])
	    j++;
	if (enums[i][j] == '\0')
	    return (i);	/* found, return index */
	i++;
    }
    return NONE;
}

/******************************************************************************
 *
 * Print a report of operating statistics for all devices supported by this
 * module.
 *
 ******************************************************************************/
long devGpibLib_report(gDset * dset)
{
    hwpvt *phwpvt;
    devGpibParmBlock *pdevGpibParmBlock;
    pdevGpibParmBlock = (devGpibParmBlock *) (dset->funPtr[dset->number]);
    for (phwpvt = pdevGpibParmBlock->hwpvtHead; phwpvt; phwpvt = phwpvt->next) {
	if (phwpvt->link < 256) {
	    printf("  link %d,", phwpvt->link);
	} else {
	    printf("  link %d, bbaddr %d,",
		   (phwpvt->link >> 8) - 1, (phwpvt->link & 0x00ff));
	}
	printf(" device %d %d, timeouts %ld\n",
	       phwpvt->device, phwpvt->secondary, phwpvt->tmoCount);
    }
    return (0);
}

/******************************************************************************
 *
 * Initialization for device support
 * This is called one time before any records are initialized with a pass
 * value of 0.  And then again AFTER all record-level init is complete
 * with a pass value of 1.
 *
 ******************************************************************************/
long devGpibLib_initDevSup(int pass, gDset * dset)
{
    devGpibParmBlock *pdevGpibParmBlock;

    pdevGpibParmBlock = (devGpibParmBlock *) (dset->funPtr[dset->number]);
    if ((pass == 0) && *pdevGpibParmBlock->debugFlag > 5)
	errlogPrintf("%s() Gpib Device Support Initializing ...\n", pdevGpibParmBlock->name);
    return (0);
}

/******************************************************************************
 * Initialization routines.
 ******************************************************************************/
/******************************************************************************
 * ai record init
 ******************************************************************************/
long devGpibLib_initAi(struct aiRecord * pai)
{
    long result;
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;

    /* do common initialization */
    if ((result = devGpibLib_initXx((struct dbCommon *) pai, &pai->inp)))
	return (result);
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(pai);
    cmdType = cmdTypeGet(pai->dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBREAD &&
	cmdType != GPIBREADW &&
	cmdType != GPIBRAWREAD &&
	cmdType != GPIBSOFT) {
        errlogPrintf("%s devGpibLib_initAi: "
	    "invalid command type for AI record in param %d\n",
	    pai->name, ((gpibDpvt *) pai->dpvt)->parm);
	pai->pact = TRUE;
	return (S_db_badField);
    }
    if (((gDset *) pai->dset)->funPtr[5])/* do we have a special_linconv? */
	((gDset *) pai->dset)->funPtr[5] (pai, TRUE);
    return (0);
}

/******************************************************************************
 * ao record init
 ******************************************************************************/
long devGpibLib_initAo(struct aoRecord * pao)
{
    long result;
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;

    /* do common initialization */
    if ((result = devGpibLib_initXx((struct dbCommon *) pao, &pao->out)))
	return (result);
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(pao);
    cmdType = cmdTypeGet(pao->dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBWRITE &&
	cmdType != GPIBCMD &&
	cmdType != GPIBACMD &&
	cmdType != GPIBSOFT) {
        errlogPrintf("%s devGpibLib_initAo: "
	    "invalid command type for AO record in param %d\n",
	    pao->name, ((gpibDpvt *) pao->dpvt)->parm);
	pao->pact = TRUE;
	return (S_db_badField);
    }
    if (((gDset *) pao->dset)->funPtr[5])/* do we have a special_linconv? */
	((gDset *) pao->dset)->funPtr[5] (pao, TRUE);
    return (2);	/* don't let record processing do an RVAL to VAL conversion */
}

/******************************************************************************
 * li record init
 ******************************************************************************/
long devGpibLib_initLi(struct longinRecord * pli)
{
    long result;
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;
    gpibDpvt *dpvt;

    /* do common initialization */
    if ((result = devGpibLib_initXx((struct dbCommon *) pli, &pli->inp)))
	return (result);
    dpvt = (gpibDpvt *)pli->dpvt;
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(pli);
    cmdType = cmdTypeGet(dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBREAD &&
	cmdType != GPIBSOFT &&
	cmdType != GPIBRAWREAD &&
	cmdType != GPIBREADW &&
	cmdType != GPIBSRQHANDLER) {
        errlogPrintf("%s devGpibLib_initLi: "
	    "invalid command type for LI record in param %d\n",
	    pli->name, dpvt->parm);
	pli->pact = TRUE;
	return (S_db_badField);
    }
    return (0);
}

/******************************************************************************
 * lo record init
 ******************************************************************************/
long devGpibLib_initLo(struct longoutRecord * plo)
{
    long result;
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;
    /* do common initialization */
    if ((result = devGpibLib_initXx((struct dbCommon *) plo, &plo->out)))
	return (result);
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(plo);
    cmdType = cmdTypeGet(plo->dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBWRITE &&
	cmdType != GPIBCMD &&
	cmdType != GPIBACMD &&
	cmdType != GPIBSOFT) {
        errlogPrintf("%s devGpibLib_initLo: "
	    "invalid command type for LO record in param %d\n",
	    plo->name, ((gpibDpvt *) plo->dpvt)->parm);
	plo->pact = TRUE;
	return (S_db_badField);
    }
    return (0);
}

/******************************************************************************
 * bi record init
 ******************************************************************************/
long devGpibLib_initBi(struct biRecord * pbi)
{
    long result;
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;
    devGpibNames *pdevGpibNames;

    /* do common initialization */
    if ((result = devGpibLib_initXx((struct dbCommon *) pbi, &pbi->inp)))
	return (result);
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(pbi);
    cmdType = cmdTypeGet(pbi->dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBREAD &&
	cmdType != GPIBSOFT &&
	cmdType != GPIBEFASTI &&
	cmdType != GPIBEFASTIW &&
	cmdType != GPIBRAWREAD &&
	cmdType != GPIBREADW) {
        errlogPrintf("%s devGpibLib_initBi: "
	    "invalid command type for BI record in param %d\n",
	    pbi->name, ((gpibDpvt *) pbi->dpvt)->parm);
	pbi->pact = TRUE;
	return (S_db_badField);
    }
    /* see if there are names asociated with the record that should */
    /* be filled in */
    pdevGpibNames = devGpibNamesGet(pbi->dpvt,pdevGpibParmBlock);
    if(pdevGpibNames != NULL) {
	if (pbi->znam[0] == '\0') {
	    strcpy(pbi->znam, pdevGpibNames->item[0]);
	}
	if (pbi->onam[0] == '\0') {
	    strcpy(pbi->onam, pdevGpibNames->item[1]);
	}
    }
    return (0);
}

/******************************************************************************
 * bo record init
 ******************************************************************************/
static char *ifcName[] = {"noop", "IFC", 0};
static char *renName[] = {"drop REN", "assert REN", 0};
static char *dclName[] = {"noop", "DCL", "0"};
static char *lloName[] = {"noop", "LLO", "0"};
static char *sdcName[] = {"noop", "SDC", "0"};
static char *gtlName[] = {"noop", "GTL", "0"};
static char *resetName[] = {"noop", "resetLink", "0"};
long devGpibLib_initBo(struct boRecord * pbo)
{
    long result;
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;
    devGpibNames *pdevGpibNames;

    /* do common initialization */
    if ((result = devGpibLib_initXx((struct dbCommon *) pbo, &pbo->out)))
	return (result);
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(pbo);
    cmdType = cmdTypeGet(pbo->dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBWRITE &&
	cmdType != GPIBCMD &&
	cmdType != GPIBACMD &&
	cmdType != GPIBSOFT &&
	cmdType != GPIBEFASTO &&
	cmdType != GPIBIFC &&
	cmdType != GPIBREN &&
	cmdType != GPIBDCL &&
	cmdType != GPIBLLO &&
	cmdType != GPIBSDC &&
	cmdType != GPIBGTL &&
	cmdType != GPIBRESETLNK) {
        errlogPrintf("%s devGpibLib_initBo: "
	    "invalid command type for BO record in param %d\n",
	    pbo->name, ((gpibDpvt *) pbo->dpvt)->parm);
	pbo->pact = TRUE;
	return (S_db_badField);
    }
    /* see if there are names asociated with the record that should */
    /* be filled in */
    pdevGpibNames = devGpibNamesGet(pbo->dpvt,pdevGpibParmBlock);
    if (pdevGpibNames != NULL) {
	if (pbo->znam[0] == '\0') {
	    strcpy(pbo->znam, pdevGpibNames->item[0]);
	}
	if (pbo->onam[0] == '\0') {
	    strcpy(pbo->onam, pdevGpibNames->item[1]);
	}
    } else {	/* supply defaults for menus */
	char **papname = 0;
	switch (cmdType) {
	case GPIBIFC: papname = ifcName; break;
	case GPIBREN: papname = renName; break;
	case GPIBDCL: papname = dclName; break;
	case GPIBLLO: papname = lloName; break;
	case GPIBSDC: papname = sdcName; break;
	case GPIBGTL: papname = gtlName; break;
	case GPIBRESETLNK: papname = resetName; break;
	default:
	    break;
	}
	if (papname) {
	    if (pbo->znam[0] == '\0') {
		strcpy(pbo->znam, papname[0]);
	    }
	    if (pbo->onam[0] == '\0') {
		strcpy(pbo->onam, papname[1]);
	    }
	}
    }
    return (2);	/* don't let record processing do an RVAL to VAL conversion */
}

/******************************************************************************
 * mbbi record init
 ******************************************************************************/
long devGpibLib_initMbbi(struct mbbiRecord * pmbbi)
{
    long result;
    int name_ct;	/* for filling in the name strings */
    char *name_ptr;	/* index into name list array */
    unsigned long *val_ptr;	/* indev into the value list array */
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;
    gpibDpvt *dpvt = (gpibDpvt *) pmbbi->dpvt;
    devGpibNames *pdevGpibNames;

    /* do common initialization */
    if ((result = devGpibLib_initXx((struct dbCommon *) pmbbi, &pmbbi->inp)))
	return (result);
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(pmbbi);
    cmdType = cmdTypeGet(pmbbi->dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBREAD &&
	cmdType != GPIBSOFT &&
	cmdType != GPIBEFASTI &&
	cmdType != GPIBEFASTIW &&
	cmdType != GPIBRAWREAD &&
	cmdType != GPIBREADW) {
        errlogPrintf("%s devGpibLib_initMbbi: "
	    "invalid command type for MBBI record in param %d\n",
            pmbbi->name, ((gpibDpvt *) pmbbi->dpvt)->parm);
	pmbbi->pact = TRUE;
	return (S_db_badField);
    }
    /* do initialization of other fields in the record that are unique to this
     * record type */
    /* see if there are names asociated with the record that should */
    /* be filled in */
    pdevGpibNames = devGpibNamesGet(pmbbi->dpvt,pdevGpibParmBlock);
    if (pdevGpibNames != NULL) {
	if (pdevGpibNames->value == NULL) {
            errlogPrintf("%s: init_rec_mbbi: MBBI value list wrong for param #%d\n",
                pmbbi->name, dpvt->parm);
	    pmbbi->pact = TRUE;
	    return (S_db_badField);
	}
	pmbbi->nobt = pdevGpibNames->nobt;
	name_ct = 0;	/* current name string element */
	name_ptr = pmbbi->zrst;	/* first name string element */
	val_ptr = &(pmbbi->zrvl);	/* first value element */
	while (name_ct < pdevGpibNames->count) {
	    if (name_ptr[0] == '\0') {
		strcpy(name_ptr, pdevGpibNames->item[name_ct]);
		*val_ptr = pdevGpibNames->value[name_ct];
	    }
	    name_ct++;
	    name_ptr += sizeof(pmbbi->zrst);
	    val_ptr++;
	}
    }
    return (0);
}

/******************************************************************************
 * mbbo record init
 ******************************************************************************/
long devGpibLib_initMbbo(struct mbboRecord * pmbbo)
{
    long result;
    gpibDpvt *dpvt;	/* pointer to gpibDpvt, not yet assigned */
    int name_ct;	/* for filling in the name strings */
    char *name_ptr;	/* index into name list array */
    unsigned long *val_ptr;	/* index into the value list array */
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;
    devGpibNames *pdevGpibNames;

    /* do common initialization */
    if ((result = devGpibLib_initXx((struct dbCommon *) pmbbo, &pmbbo->out)))
	return (result);
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(pmbbo);
    cmdType = cmdTypeGet(pmbbo->dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBWRITE &&
	cmdType != GPIBCMD &&
	cmdType != GPIBACMD &&
	cmdType != GPIBSOFT &&
	cmdType != GPIBEFASTO) {
        errlogPrintf("%s devGpibLib_initMbbo: "
	    "invalid command type for MBBO record in param %d\n",
            pmbbo->name, ((gpibDpvt *) pmbbo->dpvt)->parm);
	pmbbo->pact = TRUE;
	return (S_db_badField);
    }
    dpvt = (gpibDpvt *) pmbbo->dpvt;	/* init pointer to gpibDpvt */
    /* do initialization of other fields in the record that are unique to this
     * record type */
    /* see if there are names asociated with the record that should */
    /* be filled in */
    pdevGpibNames = devGpibNamesGet(pmbbo->dpvt,pdevGpibParmBlock);
    if (pdevGpibNames != NULL) {
	if (pdevGpibNames->value == NULL) {
	    errlogPrintf("%s: init_rec_mbbo: MBBO value list wrong for param #%d\n",
                pmbbo->name, dpvt->parm);
	    pmbbo->pact = TRUE;
	    return (S_db_badField);
	}
	pmbbo->nobt = pdevGpibNames->nobt;
	name_ct = 0;	/* current name string element */
	name_ptr = pmbbo->zrst;	/* first name string element */
	val_ptr = &(pmbbo->zrvl);	/* first value element */
	while (name_ct < pdevGpibNames->count) {
	    if (name_ptr[0] == '\0') {
		strcpy(name_ptr, pdevGpibNames->item[name_ct]);
		*val_ptr = pdevGpibNames->value[name_ct];
	    }
	    name_ct++;
	    name_ptr += sizeof(pmbbo->zrst);
	    val_ptr++;
	}
    }
    return (2);	/* don't let record processing do an RVAL to VAL conversion */
}

/******************************************************************************
 * mbbiDirect record init
 ******************************************************************************/
long devGpibLib_initMbbiDirect(struct mbbiDirectRecord * pmbbiDirect)
{
    long result;
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;
    /* do common initialization */
    result = devGpibLib_initXx((struct dbCommon *)pmbbiDirect,&pmbbiDirect->inp);
     if(result) return (result);
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(pmbbiDirect);
    cmdType = cmdTypeGet(pmbbiDirect->dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBREAD &&
	cmdType != GPIBSOFT &&
	cmdType != GPIBRAWREAD &&
	cmdType != GPIBREADW) {
        errlogPrintf("%s devGpibLib_initMbbiDirect: "
	    "invalid command type for MBBI_DIRECT record in param %d\n",
            pmbbiDirect->name, ((gpibDpvt *) pmbbiDirect->dpvt)->parm);
	pmbbiDirect->pact = TRUE;
	return (S_db_badField);
    }
    return (0);
}

/******************************************************************************
 * mbboDirect record init
 ******************************************************************************/
long devGpibLib_initMbboDirect(struct mbboDirectRecord * pmbboDirect)
{
    long result;
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;
    /* do common initialization */
    result = devGpibLib_initXx((struct dbCommon *) pmbboDirect, &pmbboDirect->out);
    if(result) return (result);
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(pmbboDirect);
    cmdType = cmdTypeGet(pmbboDirect->dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBWRITE &&
	cmdType != GPIBCMD &&
	cmdType != GPIBACMD &&
	cmdType != GPIBSOFT &&
	cmdType != GPIBEFASTO) {
        errlogPrintf("%s devGpibLib_initMbboDirect: "
	    "invalid command type for MBBO_DIRECT record in param %d\n",
            pmbboDirect->name, ((gpibDpvt *) pmbboDirect->dpvt)->parm);
	pmbboDirect->pact = TRUE;
	return (S_db_badField);
    }
    return (2);	/* don't let record processing do an RVAL to VAL conversion */
}

/******************************************************************************
 * stringin record init
 ******************************************************************************/
long devGpibLib_initSi(struct stringinRecord * psi)
{
    long result;
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;
    /* do common initialization */
    if ((result = devGpibLib_initXx((struct dbCommon *) psi, &psi->inp)))
	return (result);
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(psi);
    cmdType = cmdTypeGet(psi->dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBREAD &&
	cmdType != GPIBSOFT &&
	cmdType != GPIBRAWREAD &&
	cmdType != GPIBREADW) {
        errlogPrintf("%s devGpibLib_initSi: "
	    "invalid command type for SI record in param %d\n",
	    psi->name, ((gpibDpvt *) psi->dpvt)->parm);
	psi->pact = TRUE;
	return (S_db_badField);
    }
    return (0);
}

/******************************************************************************
 * stringout record init
 ******************************************************************************/
long devGpibLib_initSo(struct stringoutRecord * pso)
{
    long result;
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;
    /* do common initialization */
    if ((result = devGpibLib_initXx((struct dbCommon *) pso, &pso->out))) {
	return (result);
    }
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(pso);
    cmdType = cmdTypeGet(pso->dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBWRITE &&
	cmdType != GPIBCMD &&
	cmdType != GPIBACMD &&
	cmdType != GPIBSOFT) {
        errlogPrintf("%s devGpibLib_initSo: "
	    "invalid command typefor a SO record in param %d\n",
	    pso->name,  ((gpibDpvt *) pso->dpvt)->parm);
	pso->pact = TRUE;
	return (S_db_badField);
    }
    return (0);
}

/******************************************************************************
 * event record init
 ******************************************************************************/
long devGpibLib_initEv(struct eventRecord * pev)
{
    long result;
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;
    /* do common initialization */
    if ((result = devGpibLib_initXx((struct dbCommon *) pev, &pev->inp)))
	return (result);
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(pev);
    cmdType = cmdTypeGet(pev->dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBREAD &&
	cmdType != GPIBSOFT &&
	cmdType != GPIBRAWREAD &&
	cmdType != GPIBREADW) {
        errlogPrintf("%s devGpibLib_initEv: "
	    "invalid command type for EV record in param %d\n",
	    pev->name, ((gpibDpvt *) pev->dpvt)->parm);
	pev->pact = TRUE;
	return (S_db_badField);
    }
    return (0);
}

/******************************************************************************
 * wf record init
 ******************************************************************************/
long devGpibLib_initWf(struct waveformRecord * pwf)
{
    long result;
    devGpibParmBlock *pdevGpibParmBlock;
    int cmdType;
    /* do common initialization */
    if ((result = devGpibLib_initXx((struct dbCommon *) pwf, &pwf->inp)))
	return (result);
    /* make sure the command type makes sense for the record type */
    pdevGpibParmBlock = devGpibParmBlockGet(pwf);
    cmdType = cmdTypeGet(pwf->dpvt,pdevGpibParmBlock);
    if (cmdType != GPIBREAD &&
	cmdType != GPIBWRITE &&
	cmdType != GPIBCMD &&
	cmdType != GPIBACMD &&
	cmdType != GPIBSOFT &&
	cmdType != GPIBREADW &&
	cmdType != GPIBRAWREAD) {
        errlogPrintf("%s devGpibLib_initWf: "
	    "invalid command type for WF record in param %d\n",
	    pwf->name, ((gpibDpvt *) pwf->dpvt)->parm);
	pwf->pact = TRUE;
	return (S_db_badField);
    }
    return (0);
}

/******************************************************************************
 * This init routine is common to all record types
 ******************************************************************************/
long devGpibLib_initXx(struct dbCommon * prec, struct link * plink)
{
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(prec);
    gpibDpvt *pgpibDpvt;
    gpibCmd *pgpibCmd;
    char hwpvtFound = FALSE;

    if (*pdevGpibParmBlock->debugFlag)
	errlogPrintf("initXx dealing with record >%s< device >%s<\n",
		     prec->name, pdevGpibParmBlock->name);
    /* allocate space for the private structure */
    pgpibDpvt = (gpibDpvt *) calloc(1, sizeof(gpibDpvt));
    prec->dpvt = pgpibDpvt;
    pgpibDpvt->timeout = pdevGpibParmBlock->timeout;
    pgpibDpvt->precord = prec;
    pgpibDpvt->parm = -1;	/* in case the sscanf fails */
    switch (plink->type) {
    case GPIB_IO:	/* general gpib link */
	pgpibDpvt->head.link = plink->value.gpibio.link;
	pgpibDpvt->head.device = plink->value.gpibio.addr;
	if (pgpibDpvt->head.device >= 100) {
	    pgpibDpvt->head.secondary = pgpibDpvt->head.device % 100;
	    pgpibDpvt->head.device /= 100;
	} else {
	    pgpibDpvt->head.secondary = IBNOSECONDARY;
	}
	sscanf(plink->value.gpibio.parm, "%hd", &pgpibDpvt->parm);
	if (*pdevGpibParmBlock->debugFlag > 5) {
	    errlogPrintf("initXx GPIB_IO link info for record '%s':\n",
		 prec->name);
	    errlogPrintf("  link:  '%d'\n", plink->value.gpibio.link);
	    errlogPrintf("  addr:  '%d'\n", plink->value.gpibio.addr);
	    errlogPrintf("  parm:  '%s'\n", plink->value.gpibio.parm);
	}
	break;
    case BBGPIB_IO:
	pgpibDpvt->head.link =
	    ((plink->value.bbgpibio.link + 1) << 8) + plink->value.bbgpibio.bbaddr;
	pgpibDpvt->head.device = plink->value.bbgpibio.gpibaddr;
	if (pgpibDpvt->head.device >= 100) {
	    pgpibDpvt->head.secondary = pgpibDpvt->head.device % 100;
	    pgpibDpvt->head.device /= 100;
	} else {
	    pgpibDpvt->head.secondary = IBNOSECONDARY;
	}
	sscanf(plink->value.bbgpibio.parm, "%hd", &(pgpibDpvt->parm));
	if (*pdevGpibParmBlock->debugFlag > 5) {
	    errlogPrintf("initXx BBGPIB_IO link info for record '%s':\n",
			 prec->name);
	    errlogPrintf("  card:     '%d'\n", plink->value.bbgpibio.link);
	    errlogPrintf("  bbaddr:   '%d'\n", plink->value.bbgpibio.bbaddr);
	    errlogPrintf("  gpibaddr: '%d'\n", plink->value.bbgpibio.gpibaddr);
	    errlogPrintf("  parm:     '%s'\n", plink->value.bbgpibio.parm);
	}
	break;
    default:
	errlogPrintf("%s: init_record : GPIB link type %d is invalid",
            prec->name,plink->type);
	prec->pact = TRUE;	/* keep record from being processed */
	return (S_db_badField);
    }

    /* try to find the hardware private structure */
    if (*pdevGpibParmBlock->debugFlag > 5) {
	errlogPrintf("%s() looking for hwpvt for link %d device %d %d\n",
	     pdevGpibParmBlock->name, pgpibDpvt->head.link,
	     pgpibDpvt->head.device, pgpibDpvt->head.secondary);
    }
    for (pgpibDpvt->phwpvt = pdevGpibParmBlock->hwpvtHead;
    pgpibDpvt->phwpvt;
    pgpibDpvt->phwpvt = pgpibDpvt->phwpvt->next) {
	if (pgpibDpvt->phwpvt->link == pgpibDpvt->head.link
	    && pgpibDpvt->phwpvt->device == pgpibDpvt->head.device
	    && pgpibDpvt->phwpvt->secondary == pgpibDpvt->head.secondary) {
	    hwpvtFound = TRUE;
	    break;
	}
    }
    if (!hwpvtFound) {	/* Couldn't find it. Allocate a new one */
	if (*pdevGpibParmBlock->debugFlag)
	    errlogPrintf("%s() allocating a hwpvt structure for link %d "
		 " device %d secondary %d\n",
		 pdevGpibParmBlock->name, pgpibDpvt->head.link,
		 pgpibDpvt->head.device, pgpibDpvt->head.secondary);
	pgpibDpvt->phwpvt = (hwpvt *) calloc(1, sizeof(hwpvt));
	pgpibDpvt->phwpvt->next = pdevGpibParmBlock->hwpvtHead;	/* put in the list */
	pdevGpibParmBlock->hwpvtHead = pgpibDpvt->phwpvt;
	pgpibDpvt->phwpvt->link = pgpibDpvt->head.link;
	pgpibDpvt->phwpvt->device = pgpibDpvt->head.device;
	pgpibDpvt->phwpvt->secondary = pgpibDpvt->head.secondary;
	scanIoInit(&pgpibDpvt->phwpvt->ioscanpvt);
    }
    pgpibDpvt->head.pibLink = drvGpib.getLink(
        pgpibDpvt->head.link,pgpibDpvt->head.device, pgpibDpvt->head.secondary);
    if (!pgpibDpvt->head.pibLink) {
        int addr;
        if(pgpibDpvt->head.secondary==IBNOSECONDARY) {
            addr = pgpibDpvt->head.device;
        } else {
            addr = pgpibDpvt->head.device*100 + pgpibDpvt->head.secondary;
        }
	if (plink->type == BBGPIB_IO) {
	    errlogPrintf("%s: no device L%d B%d G%d init_record(%s)",
                prec->name,
                (pgpibDpvt->head.link >> 8) - 1,
                (pgpibDpvt->head.link & 0x00ff),addr);
	} else {
	    errlogPrintf("%s: no device L%d A%d init_record(%s)",
                prec->name,
                pgpibDpvt->head.link, addr);
	}
	prec->pact = TRUE;	/* prevent processing */
	return (S_dev_noDeviceFound);
    }
    if (*pdevGpibParmBlock->debugFlag > 5)
	errlogPrintf("initXx checking GPIB address for record >%s< device >%s<\n",
		     prec->name, pdevGpibParmBlock->name);
    /* check for valid GPIB address */
    if (!checkGpibAddr(pgpibDpvt->head.device)
    || !checkSecondary(pgpibDpvt->head.secondary)) {
        errlogPrintf("%s : init_record : GPIB address out of range\n",
            prec->name);
	prec->pact = TRUE;	/* keep record from being processed */
	return (S_db_badField);
    }
    if (*pdevGpibParmBlock->debugFlag > 5)
	errlogPrintf("initXx checking param entry for record >%s< "
            "device >%s< parsed parm number %d\n",
	     prec->name, pdevGpibParmBlock->name, pgpibDpvt->parm);
    /* check for valid param entry */
    if((pgpibDpvt->parm < 0) || (pgpibDpvt->parm > pdevGpibParmBlock->numparams)) {
        errlogPrintf("%s : init_record : GPIB parameter # out of range\n",
            prec->name);
	prec->pact = TRUE;	/* keep record from being processed */
	return (S_db_badField);
    }
    if (*pdevGpibParmBlock->debugFlag > 5)
	errlogPrintf("initXx checking record coherency for record >%s< "
            "device >%s<\n",
	    prec->name, pdevGpibParmBlock->name);
    /* make sure that the record type matches the GPIB port type (jrw) */
    if (pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm].rec_typ != (gDset *) prec->dset) {
        errlogPrintf("%s : init_record : record type invalid for spec'd "
            "GPIB param#%d\n",
            prec->name,pgpibDpvt->parm);
	prec->pact = TRUE;	/* keep record from being processed */
	return (S_db_badField);
    }
    pgpibCmd = &pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm];
    if (pgpibCmd->msgLen > 0)
	pgpibDpvt->msg = (char *) (calloc(pgpibCmd->msgLen, sizeof(char)));
    if (pgpibCmd->rspLen > 0)
	pgpibDpvt->rsp = (char *) (calloc(pgpibCmd->rspLen, sizeof(char)));
    /* if there is a srqHandler and we have a new hwpvt register it */
    if(!hwpvtFound && pdevGpibParmBlock->srqHandler) {
        drvGpib.registerSrqHandler(pgpibDpvt->head.pibLink,
            pgpibDpvt->head.device, pgpibDpvt->head.secondary,
            pdevGpibParmBlock->srqHandler, pgpibDpvt->phwpvt);
    }
    /* fill in the work routine pointer */
    pgpibDpvt->head.workStart = (int (*) ())
	(((gDset *) prec->dset)->funPtr[(((gDset *) prec->dset)->number) + 1]);
    if (*pdevGpibParmBlock->debugFlag > 5)
	devGpibLib_dumpDpvt(pgpibDpvt);
    return (0);
}

/* Generic SRQ handler */
long devGpibLib_srqHandler(void *parm, int statusByte)
{
    hwpvt *phwpvt = (hwpvt *)parm;
    if (ibSrqDebug)
        errlogPrintf("devGpibLib_srqHandler(%p, 0x%.2X) called\n",
             phwpvt, statusByte);
    if (phwpvt->srqCallback)    /* solicited SRQ */
        return phwpvt->srqCallback(phwpvt->parm, statusByte);

    /* unsolicited SRQ */
    if (ibSrqDebug) errlogPrintf("devGpibLib_srqHandler() Unsolicited SRQ\n");
    phwpvt->srqPollStatus = (long)statusByte;
    scanIoRequest(phwpvt->ioscanpvt);
    return IDLE;
}

/******************************************************************************
 * A generic get_ioint_info. This can be made part of the dset.
 ******************************************************************************/
long devGpibLib_get_ioint_info(int cmd, dbCommon * prec, IOSCANPVT * ppvt)
{
    gpibDpvt *pgpibDpvt = (gpibDpvt *) (prec->dpvt);
    *ppvt = pgpibDpvt->phwpvt->ioscanpvt;
    return (0);
}


/******************************************************************************
 * devGpibLib_xxGpibSrq()
 ******************************************************************************/
int devGpibLib_xxGpibSrq(gpibDpvt * pgpibDpvt, int statusByte,
    void (*finish)(gpibDpvt * pgpibDpvt))
{
    hwpvt *phwpvt = pgpibDpvt->phwpvt;
    struct dbCommon *prec = (struct dbCommon *) (pgpibDpvt->precord);
    devGpibParmBlock *pdevGpibParmBlock;
    gpibCmd *pgpibCmd;
    int status;

    pdevGpibParmBlock = devGpibParmBlockGet(prec);
    pgpibCmd = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);
    if (*pdevGpibParmBlock->debugFlag || ibSrqDebug)
	errlogPrintf("%s calling %s(): processing srq, statusByte = 0x%.2X\n",
	    pdevGpibParmBlock->name, "devGpibLib_xxGpibSrq", statusByte);
    phwpvt->srqCallback = NULL;	/* unmark the handler */
    phwpvt->parm = NULL;	/* unmark the handler */
    /* read the response back */
    status = devGpibLib_xxGpibWork(pgpibDpvt,
        (GPIBRAWREAD | ((pgpibCmd->type & GPIBEOS) ? GPIBEOS : 0)), -1);
    if(status == -1) {
	recGblSetSevr(prec, READ_ALARM, INVALID_ALARM);
	callbackRequestProcessCallback(&pgpibDpvt->callback, priorityLow, prec);
    } else
	finish(pgpibDpvt);	/* finish the processing */
    return (IDLE);	/* indicate device now idle */
}

/******************************************************************************
 * functions that communicate with the GPIB device.
 * They are called by record-processing to perform an I/O operation.	In the
 * GPIB case, all I/O is done via anynchronous processing, so all these
 * functions do is queue requests for the driver to take care of when it is
 * ready.
 ******************************************************************************/
/******************************************************************************
 * readXx() - This init routine is common to all input record types
 ******************************************************************************/
long devGpibLib_readXx(struct dbCommon * prec, long success_ret)
{
    gpibDpvt *pgpibDpvt = (gpibDpvt *) (prec->dpvt);
    gpibCmd *pgpibCmd;
    devGpibParmBlock *pdevGpibParmBlock;
    pdevGpibParmBlock = devGpibParmBlockGet(prec);
    pgpibCmd = &pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm];

    if (prec->pact) {
	return (success_ret);	/* work is all done, finish processing */
    } else if (pgpibCmd->type == GPIBSOFT) {
	return (pgpibCmd->convert(pgpibDpvt, pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3));
    } else {	/* put pointer to dvpt field on transaction fifo */
	if (drvGpib.qGpibReq(&pgpibDpvt->head, pgpibCmd->pri) == -1) {
	    recGblSetSevr(prec, READ_ALARM, INVALID_ALARM);
	    return (0);
	}
	prec->pact = TRUE;
	return (0);
    }
}

/******************************************************************************
 * writeXx() - This init routine is common to all output record types
 ******************************************************************************/
long devGpibLib_writeXx(struct dbCommon * prec)
{
    gpibDpvt *pgpibDpvt = (gpibDpvt *) (prec->dpvt);
    gpibCmd *pgpibCmd;
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(prec);
    pgpibCmd = &pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm];

    if (prec->pact) {
	return (0);	/* work is all done, finish processing */
    } else if (pgpibCmd->type == GPIBSOFT) {
	return (pgpibCmd->convert(pgpibDpvt, pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3));
    } else {	/* put pointer to dvpt field on transaction fifo */
	if (drvGpib.qGpibReq(&pgpibDpvt->head, pgpibCmd->pri) == -1) {
	    recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
	    return (0);
	}
	prec->pact = TRUE;
	return (0);
    }
}

long devGpibLib_readAi(struct aiRecord * pai)
{
    if (((gDset *) pai->dset)->funPtr[5])/* do we have a special_linconv? */
	return devGpibLib_readXx((struct dbCommon *) pai, 0);
    else
	return devGpibLib_readXx((struct dbCommon *) pai, 2);
}

long devGpibLib_writeAo(struct aoRecord * pao)
{
    return devGpibLib_writeXx((struct dbCommon *) pao);
}

long devGpibLib_readLi(struct longinRecord * pli)
{
    gpibDpvt *pgpibDpvt = (gpibDpvt *) (pli->dpvt);
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pli);
    gpibCmd *pgpibCmd = &pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm];
    int type = pgpibCmd->type;

    if (type == GPIBSRQHANDLER) {
        pli->val = pgpibDpvt->phwpvt->srqPollStatus;
	    return (0);	/* all done */
    }
    return devGpibLib_readXx((struct dbCommon *) pli, 0);
}

long devGpibLib_writeLo(struct longoutRecord * plo)
{
    return devGpibLib_writeXx((struct dbCommon *) plo);
}

long devGpibLib_readBi(struct biRecord * pbi)
{
    return devGpibLib_readXx((struct dbCommon *) pbi, 0);
}

long devGpibLib_writeBo(struct boRecord * pbo)
{
    gpibDpvt *pgpibDpvt = (gpibDpvt *) (pbo->dpvt);
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pbo);
    gpibCmd *pgpibCmd = &pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm];
    int type = pgpibCmd->type;

    if (type == GPIBIFC || type == GPIBDCL || type == GPIBLLO
    || type == GPIBSDC || type == GPIBGTL || type == GPIBRESETLNK) {
	if (pbo->rval == 0)
	    return (0);	/* dont do anything */
    }
    return devGpibLib_writeXx((struct dbCommon *) pbo);
}

long devGpibLib_readMbbi(struct mbbiRecord * pmbbi)
{
    return devGpibLib_readXx((struct dbCommon *) pmbbi, 0);
}

long devGpibLib_writeMbbo(struct mbboRecord * pmbbo)
{
    return devGpibLib_writeXx((struct dbCommon *) pmbbo);
}

long devGpibLib_readMbbiDirect(struct mbbiDirectRecord * pmbbiDirect)
{
    return devGpibLib_readXx((struct dbCommon *) pmbbiDirect, 0);
}

long devGpibLib_writeMbboDirect(struct mbboDirectRecord * pmbboDirect)
{
    return devGpibLib_writeXx((struct dbCommon *) pmbboDirect);
}

long devGpibLib_readSi(struct stringinRecord * psi)
{
    return devGpibLib_readXx((struct dbCommon *) psi, 0);
}

long devGpibLib_writeSo(struct stringoutRecord * pso)
{
    return devGpibLib_writeXx((struct dbCommon *) pso);
}

long devGpibLib_readEv(struct eventRecord * pev)
{
    return devGpibLib_readXx((struct dbCommon *) pev, 0);
}

long devGpibLib_readWf(struct waveformRecord * pwf)
{
    return devGpibLib_readXx((struct dbCommon *) pwf, 0);
}

/******************************************************************************
 * Routines that do the actual GPIB work.  They are called by the linkTask in
 * response to work requests passed in from the readXx and writeXx functions
 * above.
 ******************************************************************************/
/******************************************************************************
 * devGpibLib_xxinGpibWork()
 ******************************************************************************/
int devGpibLib_xxinGpibWork(gpibDpvt * pgpibDpvt, void (*finish) (gpibDpvt * pgpibDpvt))
{
    hwpvt *phwpvt = pgpibDpvt->phwpvt;
    struct dbCommon *prec = (struct dbCommon *) (pgpibDpvt->precord);
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(prec);
    gpibCmd *pgpibCmd = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    /* go send predefined cmd msg and read response into msg[] */
    if (*pdevGpibParmBlock->debugFlag)
	errlogPrintf("%s calling %s(): starting with record %s, cmd type = %d\n",
	  pdevGpibParmBlock->name, "devGpibLib_xxinGpibWork", prec->name, pgpibCmd->type);
    if (devGpibLib_xxGpibWork(pgpibDpvt, pgpibCmd->type, -1) == -1) {
	recGblSetSevr(prec, READ_ALARM, INVALID_ALARM);
	callbackRequestProcessCallback(&pgpibDpvt->callback,
            priorityLow, pgpibDpvt->precord);
    } else {
	if (pgpibCmd->type % GPIBEOS != GPIBREADW
        && pgpibCmd->type % GPIBEOS != GPIBEFASTIW) {
	    finish(pgpibDpvt);	/* if not waiting on SRQ, finish */
	} else {
	    if (*pdevGpibParmBlock->debugFlag || ibSrqDebug)
		errlogPrintf("%s calling %s(): marking srqHandler\n",
                    pdevGpibParmBlock->name, "devGpibLib_xxinGpibWork");
	    phwpvt->srqCallback =
                (((gDset *) prec->dset)->funPtr[prec->dset->number + 2]);
	    phwpvt->parm = pgpibDpvt;	/* mark the handler */
	    return (BUSY);	/* indicate device still in use */
	}
    }
    return (IDLE);	/* indicate device is now idle */
}

/******************************************************************************
 * devGpibLib_aiGpibWork()
 ******************************************************************************/
int devGpibLib_aiGpibWork(gpibDpvt * pgpibDpvt)
{
    return devGpibLib_xxinGpibWork(pgpibDpvt, devGpibLib_aiGpibFinish);
}

/******************************************************************************
 * devGpibLib_aiGpibSrq()
 ******************************************************************************/
int devGpibLib_aiGpibSrq(gpibDpvt * pgpibDpvt, int statusByte)
{
    return devGpibLib_xxGpibSrq(pgpibDpvt, statusByte, devGpibLib_aiGpibFinish);
}

/******************************************************************************
 * devGpibLib_aiGpibFinish()
 ******************************************************************************/
void devGpibLib_aiGpibFinish(gpibDpvt * pgpibDpvt)
{
    int result = 0;
    double value;
    long rawvalue;
    struct aiRecord *pai = ((struct aiRecord *) (pgpibDpvt->precord));
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pai);
    gpibCmd *pgpibCmd = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    if (pgpibCmd->convert != NULL) {
	if (*pdevGpibParmBlock->debugFlag)
	    errlogPrintf("%s() calling convert ...\n", "devGpibLib_aiGpibFinish");
	pgpibCmd->convert(pgpibDpvt, pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
    } else {/* interpret msg with predefined format and write into val/rval */
	if (!pgpibDpvt->msg) {
	    recGblSetSevr(pai, READ_ALARM, INVALID_ALARM);
	    if (*pdevGpibParmBlock->debugFlag)
		errlogPrintf("%s(%s) no msg buffer\n",
                    "devGpibLib_aiGpibFinish", pai->name);
	} else if (((gDset *) pai->dset)->funPtr[5]) {
            /* do we have a special_linconv? */
	    if (pgpibDpvt->msg
            && (result = sscanf(pgpibDpvt->msg, pgpibCmd->format, &rawvalue)) == 1) {
		pai->rval = rawvalue;
		pai->udf = FALSE;
	    }
	} else {
	    if (pgpibDpvt->msg
            && (result = sscanf(pgpibDpvt->msg, pgpibCmd->format, &value)) == 1) {
		pai->val = value;
		pai->udf = FALSE;
	    }
	}
	if (result != 1) {/* sscanf did not find or assign the parameter */
	    recGblSetSevr(pai, READ_ALARM, INVALID_ALARM);
	    if (*pdevGpibParmBlock->debugFlag)
		errlogPrintf("%s(%s): can't convert msg >%s<\n",
                    "devGpibLib_aiGpibFinish", pai->name, pgpibDpvt->msg);
	}
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback, priorityLow, pgpibDpvt->precord);
}

/******************************************************************************
 *
 * devGpibLib_aoGpibWork()
 *
 ******************************************************************************/
int devGpibLib_aoGpibWork(gpibDpvt * pgpibDpvt)
{
    int cnvrtStat = 0;
    struct aoRecord *pao = ((struct aoRecord *) (pgpibDpvt->precord));
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pao);
    gpibCmd *pgpibCmd = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    /* generate command string to be sent */
    /* call convert routine, if defined */
    if (pgpibCmd->convert != NULL) {
	cnvrtStat = pgpibCmd->convert(pgpibDpvt, pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
    } else {	/* generate msg using predefined format and current val */
	/* tstraumann:	better test if there was space allocated */
	if (pgpibCmd->type % GPIBEOS == GPIBWRITE) {	/* only if needs
							 * formatting */
	    if (pgpibDpvt->msg) {	/* do we have a special_linconv? */
		if (((gDset *) pao->dset)->funPtr[5])
		    sprintf(pgpibDpvt->msg, pgpibCmd->format, pao->rval);
		else
		    sprintf(pgpibDpvt->msg, pgpibCmd->format, pao->oval);
	    } else {	/* alarm if there´s no buffer space */
		recGblSetSevr(pao, WRITE_ALARM, INVALID_ALARM);
		errlogPrintf("aoGpibWork: no buffer space\n");
	    }
	}
    }
    /* go access board with this message, unless convert was unsuccessful */
    if ((cnvrtStat == -1)
    || (devGpibLib_xxGpibWork(pgpibDpvt, pgpibCmd->type, -1) == -1)) {
	recGblSetSevr(pao, WRITE_ALARM, INVALID_ALARM);
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback, priorityLow, pgpibDpvt->precord);
    return (IDLE);
}

/******************************************************************************
 * devGpibLib_liGpibWork()
 ******************************************************************************/
int devGpibLib_liGpibWork(gpibDpvt * pgpibDpvt)
{
    return devGpibLib_xxinGpibWork(pgpibDpvt, devGpibLib_liGpibFinish);
}

/******************************************************************************
 * devGpibLib_liGpibSrq()
 ******************************************************************************/
int devGpibLib_liGpibSrq(gpibDpvt * pgpibDpvt, int statusByte)
{
    return devGpibLib_xxGpibSrq(pgpibDpvt, statusByte, devGpibLib_liGpibFinish);
}

/******************************************************************************
 * devGpibLib_liGpibFinish()
 ******************************************************************************/
void devGpibLib_liGpibFinish(gpibDpvt * pgpibDpvt)
{
    long value;
    struct longinRecord *pli = ((struct longinRecord *) (pgpibDpvt->precord));
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pli);
    gpibCmd *pgpibCmd = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    if (pgpibCmd->convert != NULL) {
	if (*pdevGpibParmBlock->debugFlag)
	    errlogPrintf("%s() calling convert ...\n", "devGpibLib_liGpibFinish");
	pgpibCmd->convert(pgpibDpvt, pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
    } else {	/* interpret msg with predefined format and write into .val */
	/* scan response string, return value will be 1 if successful */
	/* tstrauman:	test for valid msg buffer */
	if (pgpibDpvt->msg && sscanf(pgpibDpvt->msg, pgpibCmd->format, &value) == 1) {
	    pli->val = value;
	    pli->udf = FALSE;
	} else {	/* sscanf did not find or assign the parameter */
	    recGblSetSevr(pli, READ_ALARM, INVALID_ALARM);
	    if (*pdevGpibParmBlock->debugFlag) {
		if (!pgpibDpvt->msg) {
		    errlogPrintf("%s(%s) no msg buffer\n",
                        "devGpibLib_liGpibFinish", pli->name);
		} else {
		    errlogPrintf("%s(%s): can't convert msg >%s<\n",
			    "devGpibLib_liGpibFinish", pli->name, pgpibDpvt->msg);
                }
	    }
	}
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback, priorityLow, pgpibDpvt->precord);
}

/******************************************************************************
 * devGpibLib_loGpibWork()
 ******************************************************************************/
int devGpibLib_loGpibWork(gpibDpvt * pgpibDpvt)
{
    int cnvrtStat = 0;
    struct longoutRecord *plo = ((struct longoutRecord *) (pgpibDpvt->precord));
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(plo);
    gpibCmd *pgpibCmd = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    /* generate command string to be sent */
    /* call convert routine, if defined */
    if (pgpibCmd->convert != NULL) {
	cnvrtStat = pgpibCmd->convert(pgpibDpvt, pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
    } else {	/* generate msg using predefined format and current val */
	/* tstraumann:	better test if there was space allocated */
	if (pgpibCmd->type % GPIBEOS == GPIBWRITE) {	/* only if needs
							 * formatting */
	    if (pgpibDpvt->msg) {
		sprintf(pgpibDpvt->msg, pgpibCmd->format, plo->val);
	    } else {	/* alarm if there´s no buffer space */
		recGblSetSevr(plo, WRITE_ALARM, INVALID_ALARM);
		errlogPrintf("loGpibWork: no buffer space\n");
	    }
	}
    }
    /* go access board with this message, unless convert was unsuccessful */
    if ((cnvrtStat == -1)
    || (devGpibLib_xxGpibWork(pgpibDpvt, pgpibCmd->type, -1) == -1)) {
	recGblSetSevr(plo, WRITE_ALARM, INVALID_ALARM);
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback, priorityLow, pgpibDpvt->precord);
    return (IDLE);
}

/******************************************************************************
 * devGpibLib_biGpibWork()
 ******************************************************************************/
int devGpibLib_biGpibWork(gpibDpvt * pgpibDpvt)
{
    return devGpibLib_xxinGpibWork(pgpibDpvt, devGpibLib_biGpibFinish);
}

/******************************************************************************
 * devGpibLib_biGpibSrq()
 ******************************************************************************/
int devGpibLib_biGpibSrq(gpibDpvt * pgpibDpvt, int statusByte)
{
    return devGpibLib_xxGpibSrq(pgpibDpvt, statusByte, devGpibLib_biGpibFinish);
}

/******************************************************************************
 *
 * devGpibLib_biGpibFinish()
 *
 ******************************************************************************/
void devGpibLib_biGpibFinish(gpibDpvt * pgpibDpvt)
{
    unsigned long value;
    int status;
    struct biRecord *pbi = ((struct biRecord *) (pgpibDpvt->precord));
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pbi);
    gpibCmd *pgpibCmd = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    if (pgpibCmd->convert != NULL) {
	pgpibCmd->convert(pgpibDpvt, pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
    } else {	/* interpret msg with predefined format and write into .rval */
	if (pgpibCmd->type % GPIBEOS == GPIBEFASTI
        || pgpibCmd->type % GPIBEOS == GPIBEFASTIW) {
            /* check the response against enumerations */
	    /* tstraumann:	test for valid msg buffer */
	    if (pgpibDpvt->msg
            && (status = checkEnums(pgpibDpvt->msg, pgpibCmd->P3)) >= 0) {
		pbi->rval = status;
	    } else {
		recGblSetSevr(pbi, READ_ALARM, INVALID_ALARM);
		if (!pgpibDpvt->msg)
		    errlogPrintf("%s() no msg buffer\n",
                        "devGpibLib_biGpibFinish");
	    }
	} else {
            /* scan response string, return value will be 1 if successful*/
	    /* tstraumann:	 test for valid msg buffer */
	    if (pgpibDpvt->msg
            && sscanf(pgpibDpvt->msg, pgpibCmd->format, &value) == 1) {
		pbi->rval = value;
	    } else {
                /* no msg buffer, sscanf did not find or assign the parameter*/
		recGblSetSevr(pbi, READ_ALARM, INVALID_ALARM);
		if (*pdevGpibParmBlock->debugFlag) {
		    if (!pgpibDpvt->msg) {
			errlogPrintf("%s(%s) no msg buffer\n",
                           "devGpibLib_biGpibFinish", pbi->name);
		    } else {
			errlogPrintf("%s(%s): can't convert msg >%s<\n",
			    "devGpibLib_biGpibFinish", pbi->name, pgpibDpvt->msg);
                    }
		}
	    }
	}
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback,
        priorityLow, pgpibDpvt->precord);
}

/******************************************************************************
 * devGpibLib_boGpibWork()
 ******************************************************************************/
int devGpibLib_boGpibWork(gpibDpvt * pgpibDpvt)
{
    int cnvrtStat = 0;
    struct boRecord *pbo = ((struct boRecord *) (pgpibDpvt->precord));
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pbo);
    gpibCmd *pgpibCmd = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);
    int type = pgpibCmd->type % GPIBEOS;
    unsigned short val;

    /* generate command string to be sent */
    /* call convert routine, if defined */
    if (pgpibCmd->convert != NULL) {
	cnvrtStat = pgpibCmd->convert(pgpibDpvt,
            pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
    } else {	/* generate msg using predefined format and current val */
	/* tstraumann:	better test if there was space allocated */
	if (type % GPIBEOS == GPIBWRITE) {
            /* only if needs formatting */
	    if (pgpibDpvt->msg) {
		sprintf(pgpibDpvt->msg, pgpibCmd->format, (unsigned int) pbo->val);
	    } else {	/* alarm if there´s no buffer space */
		recGblSetSevr(pbo, WRITE_ALARM, INVALID_ALARM);
		errlogPrintf("boGpibWork: no buffer space\n");
	    }
	}
    }
    if (type == GPIBIFC || type == GPIBDCL || type == GPIBLLO
    || type == GPIBSDC || type == GPIBGTL || type == GPIBRESETLNK) {
	val = pbo->val;
	if (!val)
	    return (IDLE);
    } else {
	val = pbo->val ? 1 : 0;
    }
    /* go access board with this message, unless convert was unsuccessful */
    if ((cnvrtStat == -1)
    || (devGpibLib_xxGpibWork(pgpibDpvt, pgpibCmd->type, val) == -1)) {
	recGblSetSevr(pbo, WRITE_ALARM, INVALID_ALARM);
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback, priorityLow, pgpibDpvt->precord);
    return (IDLE);
}

/******************************************************************************
 * devGpibLib_mbbiGpibWork()
 ******************************************************************************/
int devGpibLib_mbbiGpibWork(gpibDpvt * pgpibDpvt)
{
    return devGpibLib_xxinGpibWork(pgpibDpvt, devGpibLib_mbbiGpibFinish);
}

/******************************************************************************
 * devGpibLib_mbbiGpibSrq()
 ******************************************************************************/
int devGpibLib_mbbiGpibSrq(gpibDpvt * pgpibDpvt, int statusByte)
{
    return devGpibLib_xxGpibSrq(pgpibDpvt, statusByte, devGpibLib_mbbiGpibFinish);
}

/******************************************************************************
 * devGpibLib_mbbiGpibFinish()
 ******************************************************************************/
void devGpibLib_mbbiGpibFinish(gpibDpvt * pgpibDpvt)
{
    unsigned long value;
    int status;
    struct mbbiRecord *pmbbi = ((struct mbbiRecord *) (pgpibDpvt->precord));
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pmbbi);
    gpibCmd *pgpibCmd = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    if (pgpibCmd->convert != NULL) {
	pgpibCmd->convert(pgpibDpvt, pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
    } else { /* interpret msg with predefined format and write into .rval */
	/* tstraumann:	test for valid msg buffer */
	if (!pgpibDpvt->msg) {
	    errlogPrintf("%s() no msg buffer\n", "devGpibLib_mbbiGpibFinish");
	} else if (pgpibCmd->type % GPIBEOS == GPIBEFASTI
        || pgpibCmd->type % GPIBEOS == GPIBEFASTIW) {
            /* check the response against enumerations */
	    if ((status = checkEnums(pgpibDpvt->msg, pgpibCmd->P3)) >= 0)
		pmbbi->rval = status;
	    else
		recGblSetSevr(pmbbi, READ_ALARM, INVALID_ALARM);
	} else {/* scan response string, return value will be 1 if successful */
	    if (sscanf(pgpibDpvt->msg, pgpibCmd->format, &value) == 1) {
		pmbbi->rval = value;
	    } else {	/* sscanf did not find or assign the parameter */
		recGblSetSevr(pmbbi, READ_ALARM, INVALID_ALARM);
		if (*pdevGpibParmBlock->debugFlag) {
		    if (!pgpibDpvt->msg) {
			errlogPrintf("%s(%s) no msg buffer\n",
                            "devGpibLib_mbbiGpibFinish", pmbbi->name);
		    } else {
			errlogPrintf("%s(%s): can't convert msg >%s<\n",
			"devGpibLib_mbbiGpibFinish", pmbbi->name, pgpibDpvt->msg);
                    }
		}
	    }
	}
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback, priorityLow, pgpibDpvt->precord);
}

/******************************************************************************
 * devGpibLib_mbboGpibWork()
 ******************************************************************************/
int devGpibLib_mbboGpibWork(gpibDpvt * pgpibDpvt)
{
    int cnvrtStat = 0;
    struct mbboRecord *pmbbo = ((struct mbboRecord *) (pgpibDpvt->precord));
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pmbbo);
    gpibCmd *pgpibCmd = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    /* generate command string to be sent */
    /* call convert routine, if defined */
    if (pgpibCmd->convert != NULL) {
	cnvrtStat = pgpibCmd->convert(pgpibDpvt,
            pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
    } else {/* generate msg using predefined format and current val */
	/* tstraumann:	better test if there was space allocated */
	if (pgpibCmd->type % GPIBEOS == GPIBWRITE) {/* only if needs formatting */
	    if (pgpibDpvt->msg) {
		sprintf(pgpibDpvt->msg, pgpibCmd->format, (unsigned int) pmbbo->rval);
	    } else {
		cnvrtStat = -1;
		errlogPrintf("mbboGpibWork: no buffer space\n");
	    }
	}
    }
    /* go access board with this message, unless convert was unsuccessful */
    /* NOTE the use of val instead of rval for the EFASTO operation index! */
    if ((cnvrtStat == -1)
    || (devGpibLib_xxGpibWork(pgpibDpvt, pgpibCmd->type, pmbbo->val) == -1)) {
	recGblSetSevr(pmbbo, WRITE_ALARM, INVALID_ALARM);
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback, priorityLow, pgpibDpvt->precord);
    return (IDLE);
}

/******************************************************************************
 * devGpibLib_mbbiDirectGpibWork()
 ******************************************************************************/
int devGpibLib_mbbiDirectGpibWork(gpibDpvt * pgpibDpvt)
{
    return devGpibLib_xxinGpibWork(pgpibDpvt, devGpibLib_mbbiDirectGpibFinish);
}

/******************************************************************************
 * devGpibLib_mbbiDirectGpibSrq()
 ******************************************************************************/
int devGpibLib_mbbiDirectGpibSrq(gpibDpvt * pgpibDpvt, int statusByte)
{
    return devGpibLib_xxGpibSrq(pgpibDpvt, statusByte, devGpibLib_mbbiDirectGpibFinish);
}

/******************************************************************************
 * devGpibLib_mbbiDirectGpibFinish()
 ******************************************************************************/
void devGpibLib_mbbiDirectGpibFinish(gpibDpvt * pgpibDpvt)
{
    unsigned long value;
    struct mbbiDirectRecord *pmbbiDirect =
        ((struct mbbiDirectRecord *) (pgpibDpvt->precord));
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pmbbiDirect);
    gpibCmd *pgpibCmd  = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    if (pgpibCmd->convert != NULL) {
	pgpibCmd->convert(pgpibDpvt, pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
    } else {/* interpret msg with predefined format and write into .rval */
	/* tstraumann:	test for valid msg buffer */
	if (!pgpibDpvt->msg) {
	    errlogPrintf("%s() no msg buffer\n",
                "devGpibLib_mbbiDirectGpibFinish");
	} else {/* scan response string, return value will be 1 if successful */
	    if (sscanf(pgpibDpvt->msg, pgpibCmd->format, &value) == 1) {
		pmbbiDirect->rval = value;
	    } else {/* sscanf did not find or assign the parameter */
		recGblSetSevr(pmbbiDirect, READ_ALARM, INVALID_ALARM);
		if (*pdevGpibParmBlock->debugFlag) {
		    if (!pgpibDpvt->msg) {
			errlogPrintf("%s(%s) no msg buffer\n",
                          "devGpibLib_mbbiDirectGpibFinish", pmbbiDirect->name);
		    } else {
			errlogPrintf("%s(%s): can't convert msg >%s<\n",
			     "devGpibLib_mbbiDirectGpibFinish",
                             pmbbiDirect->name, pgpibDpvt->msg);
                    }
		}
	    }
	}
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback, priorityLow, pgpibDpvt->precord);
}

/******************************************************************************
 * devGpibLib_mbboDirectGpibWork()
 ******************************************************************************/
int devGpibLib_mbboDirectGpibWork(gpibDpvt * pgpibDpvt)
{
    int cnvrtStat = 0;
    struct mbboDirectRecord *pmbboDirect =
        ((struct mbboDirectRecord *) (pgpibDpvt->precord));
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pmbboDirect);
    gpibCmd *pgpibCmd  = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    /* generate command string to be sent */
    /* call convert routine, if defined */
    if (pgpibCmd->convert != NULL) {
	cnvrtStat = pgpibCmd->convert(pgpibDpvt,
            pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
    } else {/* generate msg using predefined format and current val */
	/* tstraumann:	better test if there was space allocated */
	if (pgpibCmd->type % GPIBEOS == GPIBWRITE) {/* only if needs formatting */
	    if (pgpibDpvt->msg) {
		sprintf(pgpibDpvt->msg,pgpibCmd->format,(unsigned int) pmbboDirect->rval);
	    } else {	/* alarm if there´s no buffer space */
		recGblSetSevr(pmbboDirect, WRITE_ALARM, INVALID_ALARM);
		errlogPrintf("mbboDirectGpibWork: no buffer space\n");
	    }
	}
    }
    /* go access board with this message, unless convert was unsuccessful */
    /* NOTE the use of val instead of rval for the EFASTO operation index! */
    if ((cnvrtStat == -1)
    || (devGpibLib_xxGpibWork(pgpibDpvt, pgpibCmd->type, pmbboDirect->val) == -1)) {
	recGblSetSevr(pmbboDirect, WRITE_ALARM, INVALID_ALARM);
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback, priorityLow, pgpibDpvt->precord);
    return (IDLE);
}

/******************************************************************************
 * devGpibLib_stringinGpibWork()
 ******************************************************************************/
int devGpibLib_stringinGpibWork(gpibDpvt * pgpibDpvt)
{
    return devGpibLib_xxinGpibWork(pgpibDpvt, devGpibLib_stringinGpibFinish);
}

/******************************************************************************
 * devGpibLib_stringinGpibSrq()
 ******************************************************************************/
int devGpibLib_stringinGpibSrq(gpibDpvt * pgpibDpvt, int statusByte)
{
    return devGpibLib_xxGpibSrq(pgpibDpvt, statusByte, devGpibLib_stringinGpibFinish);
}

/******************************************************************************
 * devGpibLib_stringinGpibFinish()
 ******************************************************************************/
void devGpibLib_stringinGpibFinish(gpibDpvt * pgpibDpvt)
{
    struct stringinRecord *psi = ((struct stringinRecord *) (pgpibDpvt->precord));
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(psi);
    gpibCmd *pgpibCmd = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    if (pgpibCmd->convert != NULL) {
	pgpibCmd->convert(pgpibDpvt, pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
    } else {	/* BUG -- why can't we just read it into the val field? */
	/* BUG -- length should not be hard coded here */
	/* tstraumann:	test whether we have a valid buffer */
	if (pgpibDpvt->msg) {
	    strncpy(psi->val, pgpibDpvt->msg, 39);
	    psi->val[40] = '\0';
	} else {
	    psi->val[0] = '\0';
	}
	psi->udf = FALSE;
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback, priorityLow, pgpibDpvt->precord);
}

/******************************************************************************
 * devGpibLib_stringoutGpibWork()
 ******************************************************************************/
int devGpibLib_stringoutGpibWork(gpibDpvt * pgpibDpvt)
{
    int cnvrtStat = 0;
    struct stringoutRecord *pso = ((struct stringoutRecord *) (pgpibDpvt->precord));
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pso);
    gpibCmd *pgpibCmd  = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    /* generate command string to be sent */
    /* call convert routine, if defined */
    if (pgpibCmd->convert != NULL) {
	cnvrtStat = pgpibCmd->convert(pgpibDpvt,
            pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
    } else {
	/* tstraumann:	better test if there was space allocated */
	if (pgpibCmd->type % GPIBEOS == GPIBWRITE) {/* only if needs formatting */
	    if (pgpibDpvt->msg) {
		sprintf(pgpibDpvt->msg, pgpibCmd->format, pso->val);
	    } else {	/* alarm if there´s no buffer space */
		recGblSetSevr(pso, WRITE_ALARM, INVALID_ALARM);
		errlogPrintf("devGpibLib_stringoutGpibWork: no buffer space\n");
	    }
	}
    }
    /* go access board with this message, unless convert was unsuccessful */
    if ((cnvrtStat == -1)
    || (devGpibLib_xxGpibWork(pgpibDpvt, pgpibCmd->type, -1) == -1)) {
	recGblSetSevr(pso, WRITE_ALARM, INVALID_ALARM);
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback,
        priorityLow, pgpibDpvt->precord);
    return (IDLE);
}

/******************************************************************************
 * devGpibLib_evGpibWork()
 ******************************************************************************/
int devGpibLib_evGpibWork(gpibDpvt * pgpibDpvt)
{
    return devGpibLib_xxinGpibWork(pgpibDpvt, devGpibLib_evGpibFinish);
}

/******************************************************************************
 *
 * devGpibLib_evGpibSrq()
 *
 ******************************************************************************/
int devGpibLib_evGpibSrq(gpibDpvt * pgpibDpvt, int statusByte)
{
    return devGpibLib_xxGpibSrq(pgpibDpvt, statusByte, devGpibLib_evGpibFinish);
}

/******************************************************************************
 * devGpibLib_evGpibFinish()
 ******************************************************************************/
void devGpibLib_evGpibFinish(gpibDpvt * pgpibDpvt)
{
    unsigned short value;
    struct eventRecord *pev = ((struct eventRecord *) (pgpibDpvt->precord));
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pev);
    gpibCmd *pgpibCmd  = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    if (pgpibCmd->convert != NULL) {
	if (*pdevGpibParmBlock->debugFlag)
	    errlogPrintf("%s() calling convert ...\n",
                "devGpibLib_evGpibFinish");
	pgpibCmd->convert(pgpibDpvt, pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
    } else { /* interpret msg with predefined format and write into .val */
	/* scan response string, return value will be 1 if successful */
	/* tstraumann:	test for valid msg buffer */
	if (pgpibDpvt->msg && sscanf(pgpibDpvt->msg, pgpibCmd->format, &value) == 1) {
	    pev->val = value;
	    pev->udf = FALSE;
	} else {	/* sscanf did not find or assign the parameter */
	    recGblSetSevr(pev, READ_ALARM, INVALID_ALARM);
	    if (!pgpibDpvt->msg)
		errlogPrintf("%s() no msg buffer\n", devGpibLib_evGpibFinish);
	}
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback,
        priorityLow, pgpibDpvt->precord);
}

/******************************************************************************
 * devGpibLib_wfGpibWork()
 ******************************************************************************/
int devGpibLib_wfGpibWork(gpibDpvt * pgpibDpvt)
{
    hwpvt *phwpvt = pgpibDpvt->phwpvt;
    int status;
    struct waveformRecord *pwf = (struct waveformRecord *) (pgpibDpvt->precord);
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pwf);
    gpibCmd *pgpibCmd  = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    /* go send predefined cmd msg and read response into msg[] */
    if (*pdevGpibParmBlock->debugFlag)
	errlogPrintf("%s() starting ...command type = %d\n",
            "devGpibLib_wfGpibWork", pgpibCmd->type);
    /**** Handle writes internally... the generic routine will not work ****/
    if (pgpibCmd->type % GPIBEOS == GPIBWRITE) {
	if (timeoutPause(pgpibDpvt, pdevGpibParmBlock)) {
	    if (*pdevGpibParmBlock->debugFlag)
		errlogPrintf("%s() timeout flush\n", "devGpibLib_wfGpibWork");
	    status = -1;
	} else if (pgpibCmd->convert == NULL) {
	    if (*pdevGpibParmBlock->debugFlag)
		errlogPrintf("%s() Waveform write command w/o conversion "
                    "routine spec'd\n", "devGpibLib_wfGpibWork");
	    status = -1;
	} else {
	    if (*pdevGpibParmBlock->debugFlag)
		errlogPrintf("%s() calling convert ...\n",
                    "devGpibLib_wfGpibWork");
	    pgpibCmd->convert(pgpibDpvt,
                pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
	    /* tstraumann:	test for valid buffer space */
	    if (pgpibDpvt->msg) {
		status = drvGpib.write(pgpibDpvt->head.pibLink,
		    pgpibDpvt->head.device, pgpibDpvt->head.secondary,
		    pgpibDpvt->msg, pwf->nord, pgpibDpvt->timeout);
	    } else {
		status = -1;
		errlogPrintf("%s() no msg buffer space\n", "devGpibLib_wfGpibWork");
	    }
            /* code added by P.Mueller: we have to read the response back!*/
	    if (status != -1 && pdevGpibParmBlock->respond2Writes != -1)
		status = readDeviceResponse(pgpibDpvt, pgpibCmd->type);
            /* end of P.Mueller's code */
	    if (*pdevGpibParmBlock->debugFlag)
		errlogPrintf("%s() done, status = %d\n",
                    "devGpibLib_wfGpibWork", status);
	}
	/* if error occurrs then mark it with time */
	if (status == -1) {
	    phwpvt->tmoCount++;	/* count timeouts */
	    if (pdevGpibParmBlock->timeWindow) {
		phwpvt->tmoFlag = 1;
		epicsTimeGetCurrent(&phwpvt->tmoVal);
	    }
	}
    } else {
	status = devGpibLib_xxGpibWork(pgpibDpvt, pgpibCmd->type, -1);
    }
    if (status == -1) {
	recGblSetSevr(pwf, READ_ALARM, INVALID_ALARM);
	callbackRequestProcessCallback(&pgpibDpvt->callback,
            priorityLow, pgpibDpvt->precord);
    } else {
	if (pgpibCmd->type % GPIBEOS != GPIBREADW) {
	    devGpibLib_wfGpibFinish(pgpibDpvt);	/* if not waiting on SRQ,
						 * finish */
	} else {
	    if (*pdevGpibParmBlock->debugFlag || ibSrqDebug)
		errlogPrintf("%s() marking srq Handler for READW operation\n",
                    pdevGpibParmBlock->name);
	    phwpvt->srqCallback =
                (((gDset *) pwf->dset)->funPtr[pwf->dset->number + 2]);
	    phwpvt->parm = pgpibDpvt;	/* mark the handler */
	    return (BUSY);	/* indicate device still in use */
	}
    }
    return (IDLE);	/* indicate device is now idle */
}

/******************************************************************************
 * devGpibLib_wfGpibSrq()
 ******************************************************************************/
int devGpibLib_wfGpibSrq(gpibDpvt * pgpibDpvt, int statusByte)
{
    return devGpibLib_xxGpibSrq(pgpibDpvt, statusByte, devGpibLib_wfGpibFinish);
}

/******************************************************************************
 * devGpibLib_wfGpibFinish()
 ******************************************************************************/
void devGpibLib_wfGpibFinish(gpibDpvt * pgpibDpvt)
{
    struct waveformRecord *pwf = (struct waveformRecord *) (pgpibDpvt->precord);
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pwf);
    gpibCmd *pgpibCmd  = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);

    if (pgpibCmd->type % GPIBEOS != GPIBWRITE) {
	if (pgpibCmd->convert != NULL) {
	    if (*pdevGpibParmBlock->debugFlag)
		errlogPrintf("%s() calling convert ...\n",
                    "devGpibLib_wfGpibFinish");
	    pgpibCmd->convert(pgpibDpvt,
                pgpibCmd->P1, pgpibCmd->P2, pgpibCmd->P3);
	} else {	/* for waveforms no standard conversion is supplied */
	    recGblSetSevr(pwf, READ_ALARM, INVALID_ALARM);
	}
    }
    callbackRequestProcessCallback(&pgpibDpvt->callback,
        priorityLow, pgpibDpvt->precord);
}

/******************************************************************************
 * devGpibLib_xxGpibWork()
 *
 * Generic function used to read and write strings to the GPIB instruments
 * by calling the device driver.
 ******************************************************************************/
int devGpibLib_xxGpibWork(gpibDpvt * pgpibDpvt,
    int cmdType, unsigned short val)
{
    hwpvt *phwpvt = pgpibDpvt->phwpvt;
    int status = 0;
    char *efasto1, *efasto2;
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pgpibDpvt->precord);

    gpibCmd *pgpibCmd = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);
    int addr;	/* some commands need addr */

    if (pgpibDpvt->head.secondary == IBNOSECONDARY) {
	addr = pgpibDpvt->head.device;
    } else {
	addr = pgpibDpvt->head.device * 100 + pgpibDpvt->head.secondary;
    }
    /* check to see if this node has timed out within last <timeWindow> msec */
    if (timeoutPause(pgpibDpvt, pdevGpibParmBlock)) {
	if (*pdevGpibParmBlock->debugFlag)
	    errlogPrintf("%s() timeout flush\n", "devGpibLib_xxGpibWork");
	return (-1);
    }
    switch (cmdType % GPIBEOS) {
    case GPIBWRITE:	/* write the message to the GPIB listen adrs */
	/* tstraumann:	test for valid buffer space */
	if (pgpibDpvt->msg) {
	    status = drvGpib.write(pgpibDpvt->head.pibLink,
	       pgpibDpvt->head.device, pgpibDpvt->head.secondary,
	       pgpibDpvt->msg, strlen(pgpibDpvt->msg), pgpibDpvt->timeout);
	} else {
	    status = -1;
	    errlogPrintf("%s() no msg buffer space\n", "devGpibLib_xxGpibWork");
	}
	if (status != -1 && pdevGpibParmBlock->respond2Writes != -1)
	    status = readDeviceResponse(pgpibDpvt, pgpibCmd->type);
	break;
    case GPIBCMD:	/* write the cmd to the GPIB listen addess */
	status = drvGpib.write(pgpibDpvt->head.pibLink,
	    pgpibDpvt->head.device, pgpibDpvt->head.secondary,
	    pgpibCmd->cmd, strlen(pgpibCmd->cmd), pgpibDpvt->timeout);
	if (status != -1 && pdevGpibParmBlock->respond2Writes != -1)
	    status = readDeviceResponse(pgpibDpvt, pgpibCmd->type);
	break;
    case GPIBACMD:	/* write the cmd to the GPIB listen addess */
	status = drvGpib.addressedCmd(pgpibDpvt->head.pibLink,
	    pgpibDpvt->head.device, pgpibDpvt->head.secondary,
	    pgpibCmd->cmd, strlen(pgpibCmd->cmd), pgpibDpvt->timeout);
	if (status != -1 && pdevGpibParmBlock->respond2Writes != -1)
	    status = readDeviceResponse(pgpibDpvt, pgpibCmd->type);
	break;
    case GPIBREAD:	/* write the command string */
    case GPIBEFASTI:
	status = drvGpib.write(pgpibDpvt->head.pibLink,
	    pgpibDpvt->head.device, pgpibDpvt->head.secondary,
	    pgpibCmd->cmd, strlen(pgpibCmd->cmd), pgpibDpvt->timeout);
	/* this is probably not the best way to do this... */
	/* because the read turnaround time delay should be independant of */
	/* the responds to writes flag. */
	if (status != -1 && pdevGpibParmBlock->respond2Writes != -1)
	    status = readDeviceResponse(pgpibDpvt, pgpibCmd->type);
	if (status == -1)
	    break;
	/* NOTICE -- This falls thru to the raw read code below! */
    case GPIBRAWREAD:	/* for SRQs, read the data w/o a sending a command */
	/* read the instrument */
	/* tstrauman:	test for valid buffer space */
	if (pgpibDpvt->msg) {
	    status = drvGpib.read(pgpibDpvt->head.pibLink,
		pgpibDpvt->head.device, pgpibDpvt->head.secondary,
                pgpibDpvt->msg, pgpibCmd->msgLen, pgpibDpvt->timeout,
		((cmdType & GPIBEOS) ? pgpibCmd->companion : -1));
	} else {
	    status = -1;
	    errlogPrintf("%s() no msg buffer\n", "devGpibLib_xxGpibWork");
	}
	if (status == -1) {
	    break;
	} else if (status > pgpibCmd->msgLen - 1) {
            /* check length of resp. This may or may not be an error */
	    errlogPrintf("%s() Warning! response length (%d) >"
                " allocated space - 1 (%d)"
	        " (Last byte overwritten with null character.)\n",
                "devGpibLib_xxGpibWork",
		 status, pgpibCmd->msgLen - 1);
	    pgpibDpvt->msg[pgpibCmd->msgLen - 1] = '\0'; /* place \0 at end */
	} else {
	    pgpibDpvt->msg[status] = '\0';	/* terminate response with \0 */
	}
	break;
    case GPIBREADW:	/* for SRQs, write the command first */
    case GPIBEFASTIW:
	status = drvGpib.write(pgpibDpvt->head.pibLink,
	    pgpibDpvt->head.device, pgpibDpvt->head.secondary, pgpibCmd->cmd,
	    strlen(pgpibCmd->cmd), pgpibDpvt->timeout);
	if (status != -1 && pdevGpibParmBlock->respond2Writes != -1)
	    status = readDeviceResponse(pgpibDpvt, pgpibCmd->type);
	break;
    case GPIBEFASTO:	/* write the enumerated cmd from the P3 array */
	/* bfr added: cmd is not ignored but evaluated as prefix to
	 * pgpibCmd->P3[val] (cmd is _not_ intended to be an
         * independent command in itself). */
	if (pgpibCmd->cmd != NULL)
	    efasto1 = pgpibCmd->cmd;
	else
	    efasto1 = "";
	if (pgpibCmd->P3[val] != NULL)
	    efasto2 = pgpibCmd->P3[val];
	else
	    efasto2 = "";
	/* tstraumann:	test for valid msg buffer */
	if (pgpibDpvt->msg)
	    sprintf(pgpibDpvt->msg, "%s%s", efasto1, efasto2);
	else {
	    status = -1;
	    errlogPrintf("%s() no msg buffer\n", "devGpibLib_xxGpibWork");
	    break;
	}
	if (pgpibDpvt->msg[0] != 0) {
	    status = drvGpib.write(pgpibDpvt->head.pibLink,
		pgpibDpvt->head.device, pgpibDpvt->head.secondary,
                pgpibDpvt->msg, strlen(pgpibDpvt->msg), pgpibDpvt->timeout);
	    if (status != -1 && pdevGpibParmBlock->respond2Writes != -1)
		status = readDeviceResponse(pgpibDpvt, pgpibCmd->type);
	}
	break;
    case GPIBIFC:
	status = drvGpib.ifc(pgpibDpvt->head.pibLink);
	break;
    case GPIBREN:
	status = drvGpib.ren(pgpibDpvt->head.pibLink, val);
	break;
    case GPIBDCL:
	status = drvGpib.universalCmd(pgpibDpvt->head.pibLink, IBDCL);
	break;
    case GPIBLLO:
	status = drvGpib.universalCmd(pgpibDpvt->head.pibLink, IBLLO);
	break;
    case GPIBSDC:
	status = drvGpib.addressedCmd(pgpibDpvt->head.pibLink,
	 pgpibDpvt->head.device, pgpibDpvt->head.secondary,
             IBSDC, 1, pgpibDpvt->timeout);
	break;
    case GPIBGTL:
	status = drvGpib.addressedCmd(pgpibDpvt->head.pibLink,
	pgpibDpvt->head.device, pgpibDpvt->head.secondary,
            IBGTL, 1, pgpibDpvt->timeout);
	break;
    case GPIBRESETLNK:
	status = drvGpib.reset(pgpibDpvt->head.pibLink);
	break;
    default:
	status = -1;
    }
    if (*pdevGpibParmBlock->debugFlag)
	errlogPrintf("%s() done, status = %d\n",
            "devGpibLib_xxGpibWork", status);
    /* if error occurrs then mark it with time */
    if (status == -1) {
	phwpvt->tmoCount++;	/* count number of timeouts */
	if (pdevGpibParmBlock->timeWindow) {
	    phwpvt->tmoFlag = 1;
	    epicsTimeGetCurrent(&phwpvt->tmoVal);
	}
    }
    return (status);
}

/******************************************************************************
 *
 * readDeviceResponse()
 *
 ******************************************************************************/
static int readDeviceResponse(gpibDpvt * pgpibDpvt, int cmdType)
{
    devGpibParmBlock *pdevGpibParmBlock = devGpibParmBlockGet(pgpibDpvt->precord);
    gpibCmd *pgpibCmd = &(pdevGpibParmBlock->gpibCmds[pgpibDpvt->parm]);
    int status;

    if (pdevGpibParmBlock->respond2Writes > 0)
	epicsThreadSleep(pdevGpibParmBlock->respond2Writes / 1000.0);
    if (pgpibCmd->rspLen == 0) {
        status = 0;
    } else if (pgpibDpvt->rsp) {
        status = drvGpib.read(pgpibDpvt->head.pibLink,
            pgpibDpvt->head.device, pgpibDpvt->head.secondary, pgpibDpvt->rsp,
            pgpibCmd->rspLen, pgpibDpvt->timeout,
            ((cmdType & GPIBEOS) ? pgpibCmd->companion : -1));
        if (*pdevGpibParmBlock->debugFlag)
            errlogPrintf(" readDeviceResponse =>%s<\n", pgpibDpvt->rsp);
    } else {
        status = -1;
        errlogPrintf("%s() no rsp buffer space\n", "devGpibLib_xxGpibWork");
    }
    /* if user specified a secondary convert routine, call it */
    if (pdevGpibParmBlock->wrConversion != NULL)
	status = pdevGpibParmBlock->wrConversion(status, pgpibDpvt);
    return (status);
}

/*
 * devCommonGpib.c,v
 * Revision 1.7  2003/04/10 15:17:01  mrk
 * open source license
 *
 * Revision 1.6  2003/03/24 14:42:14  norume
 * Allow 0-length 'respond-to-write' echoes.  This provides a way to
 * support devices which respond only to some writes.
 *
 * Revision 1.5  2003/03/14 16:46:15  mrk
 * Changes for March 2003 version
 *
 * Revision 1.4  2003/01/15 14:08:06  mrk
 * several places had && instead of &
 *
 * Revision 1.3  2002/11/01 19:29:21  mrk
 * More changes for ioctl replacement
 *
 * Revision 1.2  2002/10/24 18:30:41  mrk
 * changes for ioctl; more coming
 *
 * Revision 1.1  2002/10/18 17:21:02  mrk
 * moved to here
 *
 * Revision 1.5  2001/04/17 18:40:05  mrk
 * Changes to get ready for bitBus support which will be implemented with bitBus
 *
 * Revision 1.4  2001/02/27 15:06:27  norume
 * Clean up handling of dead time after timeout.  There is no longer any
 * chance of wrap-around (well, not for around 3 million years) and the
 * code is faster in the most common case than the old version.
 *
 * Revision 1.3  2001/02/26 14:43:46  mrk
 * prevent conflict with devLib.h
 *
 * Revision 1.2  2001/02/22 19:57:21  norume
 * Many, many R3.14 changes.  Driver tested on RTEMS-gen68360 and Linux.
 *
 * Revision 1.1.1.1  2001/02/15 14:52:50  mrk
 * Import sources
 *
 * Revision 1.2  2000/05/19 12:57:41  franksen
 * use errlogPrintf; fixed bug in drvGpibGetLink;
 * changed type of link/gpibAddr from short to int
 *
 * Revision 1.1.1.1  2000/03/21 18:06:35  franksen
 * unbundled gpib first version
 */
