diff --git a/src/drv/drvMvme162.c b/src/drv/drvMvme162.c new file mode 100644 index 000000000..48563c536 --- /dev/null +++ b/src/drv/drvMvme162.c @@ -0,0 +1,684 @@ +/* share/src/drv/drvMvme162.c $Id$ */ + +/* Author: John Winans + * Date: 06-28-93 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "drvGpib.h" + +struct IB162Link { + struct ibLink ibLink; + SEM_ID IOsem; + int IrqLevel; + int IrqVector; + Mvme162LinkStruct *pMvme162Link; +}; + +static struct IB162Link *pIB162[MVME162_NUM_LINKS*4]; +static int linkTask(struct IB162Link *plink); +int mv167Debug = 0; + +/****************************************************************************** + * + * IRQ handler that is invoked when the default IRQ vector is generated + * from the MVME162 board. + * + ******************************************************************************/ +/* BUG have to add new extended IRQ handshake code in here */ +static void my162Handler(Mvme162LinkStruct *pMvme162) +{ + if (semGive(*(SEM_ID*)(pMvme162->pIrqParm)) != OK) + { + logMsg("my162Handler(0x%08.8X) semGive failed!!!!!\n", *(SEM_ID*)(pMvme162->pIrqParm)); + } + pMvme162->IrqHandshake = 0; /* Clear the IRQ status */ + return; +} +/****************************************************************************** + * + * Initialize the specified GPIB link number for future use. + * + * Should only be called by GPIB library code. + ******************************************************************************/ +long +drv162IB_InitLink(int link) +{ + int j; + + link -= MVME162_LINK_NUM_BASE; + + if ((link < 0) || (link >= MVME162_NUM_LINKS*4)) + return(-1); + + if (pIB162[link] == NULL) + { + /* Allocate the memory for the link descriptor. */ + pIB162[link] = malloc(sizeof(struct IB162Link)); + if (pIB162[link] == NULL) + return(-1); + + pIB162[link]->ibLink.linkType = GPIB_IO; + pIB162[link]->ibLink.linkId = link; + pIB162[link]->ibLink.bug = -1; + pIB162[link]->ibLink.linkEventSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY); + lstInit(&pIB162[link]->ibLink.hiPriList); + pIB162[link]->ibLink.hiPriSem = semBCreate(SEM_Q_FIFO, SEM_FULL); + lstInit(&pIB162[link]->ibLink.loPriList); + pIB162[link]->ibLink.loPriSem = semBCreate(SEM_Q_FIFO, SEM_FULL); + pIB162[link]->ibLink.srqIntFlag = 0; + + for (j=0; jibLink.srqHandler[j] = NULL; + pIB162[link]->ibLink.srqParm[j] = NULL; + pIB162[link]->ibLink.deviceStatus[j] = 0; + pIB162[link]->ibLink.pollInhibit[j] = 0; + } + + pIB162[link]->IOsem = semBCreate(SEM_Q_FIFO, SEM_EMPTY); + if (pIB162[link]->IOsem == NULL) + { + printf("drv162IB_InitLink(%d) IOsem creation failed\n", link); + return(-1); + } + printf("IOsem is %08.8X\n", pIB162[link]->IOsem); + + /* BUG Should be using sysBusToLocal() in here */ + j = link>>2; + + pIB162[link]->pMvme162Link = (Mvme162LinkStruct *)(MVME162_EXT_OFF+(j*MVME162_EXT_SIZE)); + pIB162[link]->IrqLevel = MVME162_IRQ_LEVEL; + pIB162[link]->IrqVector = MVME162_IVEC_BASE+j; + + printf("drvMvme162_InitLink(%d) at 0x%08.8X - 0x%08.8X\n", link, pIB162[link]->pMvme162Link, &(pIB162[link]->pMvme162Link->pIrqParm)); + + if (intConnect(INUM_TO_IVEC(MVME162_IVEC_BASE+j), my162Handler, pIB162[link]->pMvme162Link) != OK) + { + printf("drv162IB_InitLink(%d) intConnect() failed\n", link); + return(-1); + } + sysIntEnable(MVME162_IRQ_LEVEL); + + { /* Send a message out to do GPIB link initialization */ + CommandStruct cmd; + Mvme162GpibParmStruct parm; + + cmd.Command = MVME162_GPIB_ALLINIT; + cmd.pOutput = NULL; + cmd.pInput = NULL; + parm.OutLength = 0; + parm.InLength = 0; + parm.Address = 0; + parm.Time = 0; + + sysLocalToBusAdrs(VME_AM_EXT_SUP_DATA, &parm, &cmd.pParms); + + cmd.IrqLevel = pIB162[link]->IrqLevel; + cmd.IrqVector = pIB162[link]->IrqVector; + cmd.pIrqParm = &(pIB162[link]->IOsem); + + Send162(pIB162[link]->pMvme162Link, &cmd); + printf("init() Ready to take semaphore\n"); + semTake(pIB162[link]->IOsem, WAIT_FOREVER); + printf("Got Semaphore, status = %08.8X\n", parm.Status); + } + /* Spawn the link task */ + if (taskSpawn("162IB", GPIBLINK_PRI, GPIBLINK_OPT, GPIBLINK_STACK, linkTask, &pIB162[link]->ibLink) == ERROR) + { + printf("drv162IB_InitLink(%d): can't start link task\n", link); + return(-1); + } + } + + + return(0); +} +/****************************************************************************** + * + * Return the address of an iblink structure. + * + * Should only be called by GPIB library code. + ******************************************************************************/ +long +drv162IB_GetLink(int link, struct ibLink **pplink) +{ + link -= MVME162_LINK_NUM_BASE; + + if(mv167Debug) + printf("drvMvme162_GetLink(%d, 0x%08.8X)\n", link, pplink); + + if ((link < 0) || (link >= MVME162_NUM_LINKS*4)) + return(-1); + + *pplink = &(pIB162[link]->ibLink); + return(0); +} +/****************************************************************************** + * + * This allows a device support module to register an SRQ event handler. + * + * It is used to specify a function to call when an SRQ event is detected + * on the specified link and device. When the SRQ handler is called, it is + * passed the requested parm and the poll-status from the gpib device. + * + ******************************************************************************/ +long +drv162IB_RegisterSrq(struct ibLink *pibLink, int device, int (*handler)(), void *parm) +{ + if(mv167Debug) + printf("registerSrqCallback(%08.8X, %d, 0x%08.8X, %08.8X)\n", pibLink, device, handler, parm); + + pibLink->srqHandler[device] = handler; + pibLink->srqParm[device] = parm; + return(0); +} +/****************************************************************************** + * + * + * Should only be called by GPIB library code. + ******************************************************************************/ +long +drv162IB_QueueReq(struct dpvtGpibHead *pGpibHead, int prio) +{ + if(mv167Debug) + printf("drv162IB_QueueReq()\n"); + + if (pGpibHead->pibLink == NULL) + { + printf("drv162IB_QueueReq(%08.8X, %d): dpvt->pibLink=NULL!\n", pGpibHead, prio); + return(-1); + } + + switch (prio) { + case IB_Q_LOW: /* low priority transaction request */ + semTake(pGpibHead->pibLink->loPriSem, WAIT_FOREVER); + lstAdd(&(pGpibHead->pibLink->loPriList), pGpibHead); + semGive(pGpibHead->pibLink->loPriSem); + semGive(pGpibHead->pibLink->linkEventSem); + break; + case IB_Q_HIGH: /* high priority transaction request */ + semTake(pGpibHead->pibLink->hiPriSem, WAIT_FOREVER); + lstAdd(&(pGpibHead->pibLink->hiPriList), pGpibHead); + semGive(pGpibHead->pibLink->hiPriSem); + semGive(pGpibHead->pibLink->linkEventSem); + break; + default: /* invalid priority */ + printf("invalid priority requested in call to drv162IB_QueueReq(%08.8X, %d)\n", pGpibHead, prio); + return(-1); + } + if (mv167Debug) + printf("drv162IB_QueueReq(%d, 0x%08.8X, %d): xact queued\n", pGpibHead, prio); + return(0); +} + +/****************************************************************************** + * + * At the time this function is started as its own task, the linked list + * structures will have been created and initialized. + * + * This function is spawned as a task for each GPIB bus present in the + * system. That is one for each Ni card port, and one for each Bit Bus + * bug that contains a GPIB port on it. + * + * All global data areas referenced by this task are limited to the non-port + * specific items (no niLink[] references allowed.) so that the same task + * can operate all forms of GPIB busses. + * + ******************************************************************************/ +static int +linkTask(struct ibLink *plink) +{ + struct dpvtGpibHead *pnode; + int working; + + if (mv167Debug) + printf("IB162 link task started for link link %d\n", plink->linkId); + + working = 1; /* check queues for work the first time */ + while (1) + { + if (!working) + { +#if 0 + if (ibSrqLock == 0) + { + /* Enable SRQ interrupts while waiting for an event */ + srqIntEnable(plink->linkType, plink->linkId, plink->bug); + } +#endif + /* wait for an event associated with this GPIB link */ + semTake(plink->linkEventSem, WAIT_FOREVER); +#if 0 + /* Disable SRQ interrupts while processing an event */ + srqIntDisable(plink->linkType, plink->linkId, plink->bug); +#endif + if (mv167Debug) + { + printf("IB152LinkTask(%d, %d): got an event\n", plink->linkType, plink->linkId); + } + } + working = 0; /* Assume will do nothing */ +#if 0 + if ((plink->srqIntFlag) && (ibSrqLock == 0)) + { + if (mv167Debug || ibSrqDebug) + printf("IB152LinkTask(%d, %d): srqIntFlag set.\n", plink->linkType, plink->linkId); + + /* Read the SRQ message buffer and callback the handler for each + device. */ + + printf("IB152LinkTask(%d, %d): dispatching srq handler for device %d\n", plink->linkType, plink->linkId, ringData.device); + plink->deviceStatus[ringData.device] = (*(plink->srqHandler)[ringData.device])(plink->srqParm[ringData.device], ringData.status); + working=1; + } +#endif + /* + * see if the Hi priority queue has anything in it + */ + semTake(plink->hiPriSem, WAIT_FOREVER); + + if ((pnode = (struct dpvtGpibHead *)lstFirst(&(plink->hiPriList))) != NULL) + { + while (plink->deviceStatus[pnode->device] == BUSY) + if ((pnode = (struct dpvtGpibHead *)lstNext(pnode)) == NULL) + break; + } + if (pnode != NULL) + lstDelete(&(plink->hiPriList), pnode); + + semGive(plink->hiPriSem); + + if (pnode != NULL) + { + if (mv167Debug) + printf("IB152LinkTask(%d, %d): got Hi Pri xact, pnode= 0x%08.8X\n", plink->linkType, plink->linkId, pnode); + + plink->deviceStatus[pnode->device] = (*(pnode->workStart))(pnode); + working=1; + } + else + { + semTake(plink->loPriSem, WAIT_FOREVER); + if ((pnode = (struct dpvtGpibHead *)lstFirst(&(plink->loPriList))) != NULL) + { + while (plink->deviceStatus[pnode->device] == BUSY) + if ((pnode = (struct dpvtGpibHead *)lstNext(pnode)) == NULL) + break; + } + if (pnode != NULL) + lstDelete(&(plink->loPriList), pnode); + + semGive(plink->loPriSem); + + if (pnode != NULL) + { + if(mv167Debug) + printf("IB152LinkTask(%d, %d): got Lo Pri xact, pnode= 0x%08.8X\n", plink->linkType, plink->linkId, pnode); + plink->deviceStatus[pnode->device] = (*(pnode->workStart))(pnode); + working=1; + } + } + } +} +#if 0 +/****************************************************************************** + * + * The following are functions used to take care of serial polling. They + * are called from the ibLinkTask. + * + ******************************************************************************/ +/****************************************************************************** + * + * Pollib sends out an SRQ poll and returns the poll response. + * If there is an error during polling (timeout), the value -1 is returned. + * + ******************************************************************************/ +static int +pollIb(plink, gpibAddr, verbose, time) +struct ibLink *plink; +int gpibAddr; +int verbose; /* set to 1 if should log any errors */ +int time; +{ + char pollCmd[4]; + unsigned char pollResult[3]; + int status; + int tsSave; + + if(verbose && (mv167Debug || ibSrqDebug)) + printf("pollIb(0x%08.8X, %d, %d, %d)\n", plink, gpibAddr, verbose, time); + + tsSave = timeoutSquelch; + timeoutSquelch = !verbose; /* keep the I/O routines quiet if desired */ + + /* raw-read back the response from the instrument */ + if (readIb(plink, gpibAddr, pollResult, sizeof(pollResult), time) == ERROR) + { + if(verbose) + printf("pollIb(%d, %d): data read error\n", plink->linkId, gpibAddr); + status = ERROR; + } + else + { + status = pollResult[0]; + if (mv167Debug || ibSrqDebug) + { + printf("pollIb(%d, %d): poll status = 0x%02.2X\n", plink->linkId, gpibAddr, status); + } + } + + timeoutSquelch = tsSave; /* return I/O error logging to normal */ + return(status); +} +/****************************************************************************** + * + * Functions used to enable and disable SRQ interrupts. These only make + * sense on a Ni based link, so they are ignored in the BitBus case. + * (In the BitBus, SRQ status is passed back via query. So there is no + * asynchronous interupt associated with it.) + * + * The interrupts referred to here are the actual VME bus interrupts that are + * generated by the GPIB interface when it sees the SRQ line go high. + * + ******************************************************************************/ +static int +srqIntEnable(linkType, link, bug) +int linkType; +int link; +{ + if (linkType == GPIB_IO) + return(niSrqIntEnable(link)); + + if (linkType == BBGPIB_IO) + return(OK); /* Bit Bus does not use interrupts for SRQ handeling */ + + return(ERROR); /* Invalid link type specified on the call */ +} + +static int +srqIntDisable(linkType, link, bug) +int linkType; +int link; +{ + if (linkType == GPIB_IO) + return(niSrqIntDisable(link)); + + if (linkType == BBGPIB_IO) + return(0); /* Bit Bus does not use interrupts for SRQ handeling */ + + return(ERROR); /* Invlaid link type specified on the call */ +} +/**************************************************************************** + * + * The following routines are the user-callable entry points to the GPIB + * driver. + * + ****************************************************************************/ +/****************************************************************************** + * + * A device support module may call this function to request that the GPIB + * driver NEVER poll a given device. + * + * Devices are polled when an SRQ event is present on the GPIB link. Some + * devices are too dumb to deal with being polled. + * + * This is NOT a static function, because it must be invoked from the startup + * script BEFORE iocInit is called. + * + * BUG -- + * This could change if we decide to poll them during the second call to init() + * when epics 3.3 is available. + * + ******************************************************************************/ +/* static */ int +srqPollInhibit(linkType, link, bug, gpibAddr) +int linkType; /* link type (defined in link.h) */ +int link; /* the link number the handler is related to */ +int bug; /* the bug node address if on a bitbus link */ +int gpibAddr; /* the device address the handler is for */ +{ + if (mv167Debug || ibSrqDebug) + printf("srqPollInhibit(%d, %d, %d, %d): called\n", linkType, link, bug, gpibAddr); + + if (linkType == GPIB_IO) + { + return(niSrqPollInhibit(link, gpibAddr)); + } + + if (linkType == BBGPIB_IO) + { + return(bbSrqPollInhibit(link, bug, gpibAddr)); + } + + printf("drvGpib: srqPollInhibit(%d, %d, %d, %d): invalid link type specified\n", linkType, link, bug, gpibAddr); + return(ERROR); +} +/****************************************************************************** + * + * Allow users to operate the internal functions of the driver. + * + * This can be fatal to the driver... make sure you know what you are doing! + * + ******************************************************************************/ +static int +ioctlIb(linkType, link, bug, cmd, v, p) +int linkType; /* link type (defined in link.h) */ +int link; /* the link number to use */ +int bug; /* node number if is a bitbus -> gpib link */ +int cmd; +int v; +caddr_t p; +{ + int stat; + + if (linkType == GPIB_IO) + return(niGpibIoctl(link, cmd, v, p));/* link checked in niGpibIoctl */ + + if (linkType == BBGPIB_IO) + return(bbGpibIoctl(link, bug, cmd, v, p));/* link checked in bbGpibIoctl */ + + if (mv167Debug || bbmv167Debug) + printf("ioctlIb(%d, %d, %d, %d, %08.8X, %08.8X): invalid link type\n", linkType, link, bug, cmd, v, p); + + return(ERROR); +} +#endif + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +/* BUG -- this has to be converted into a FIFO list insertion!!! */ +static int Send162(Mvme162LinkStruct *pMvme162Link, CommandStruct *pCmd) +{ + Mvme162GpibParmStruct *pParms; + + if(mv167Debug>20) + { + printf("Send162() sending command:\n"); + printf(" Command %d\n", pCmd->Command); + printf(" pParms 0x%08.8X\n", pCmd->pParms); + printf(" pOutput 0x%08.8X\n", pCmd->pOutput); + printf(" pInput 0x%08.8X\n", pCmd->pInput); + printf(" IrqLevel %d\n", pCmd->IrqLevel); + printf(" IrqVector 0x%02.2X\n", (unsigned char)(pCmd->IrqVector)); + printf(" pIrqParm 0x%08.8X\n", pCmd->pIrqParm); + + if (pCmd->pParms != NULL) + { + /* vxWorks is a pain in the bung */ + pParms = (Mvme162GpibParmStruct *)((unsigned long)(pCmd->pParms) - ((unsigned long)(0x00800000))); + printf(" parms on command:\n"); + printf(" OutLength %d\n", pParms->OutLength); + printf(" InLength %d\n", pParms->InLength); + printf(" Address %d\n", pParms->Address); + printf(" Time %d\n", pParms->Time); + } + } + + /* Lock the command queue */ + while(!vxTas(&pMvme162Link->DRListLock)); + + /* Insert the command */ + pCmd->pNextCommand = pMvme162Link->pDRList; + sysLocalToBusAdrs(VME_AM_EXT_SUP_DATA, pCmd, &pMvme162Link->pDRList); + + /* unlock the command queue */ + pMvme162Link->DRListLock = 0; + + return(0); +} +/****************************************************************************** + * + * A device support callable entry point used to write data to GPIB devices. + * + * This function returns the number of bytes written out. + * + ******************************************************************************/ +int +drv162IB_write(struct ibLink *pibLink, int gpibAddr, char *data, int length, int time) +{ + CommandStruct cmd; + Mvme162GpibParmStruct parm; + + if(mv167Debug) + printf("drv162IB_write(%08.8X, %d, 0x%08.8X, %d, %d)\n", pibLink, gpibAddr, data, length, time); + + cmd.Command = MVME162_GPIB_WRITE_X; + parm.OutLength = length; + parm.InLength = 0; + parm.Address = gpibAddr; + parm.Time = time; + + sysLocalToBusAdrs(VME_AM_EXT_SUP_DATA, data, &cmd.pOutput); + sysLocalToBusAdrs(VME_AM_EXT_SUP_DATA, &parm, &cmd.pParms); + + cmd.IrqLevel = pIB162[pibLink->linkId]->IrqLevel; + cmd.IrqVector = pIB162[pibLink->linkId]->IrqVector; + cmd.pIrqParm = &(pIB162[pibLink->linkId]->IOsem); + + Send162(pIB162[pibLink->linkId]->pMvme162Link, &cmd); + semTake(pIB162[pibLink->linkId]->IOsem, WAIT_FOREVER); + + return(parm.Status); +} + +/****************************************************************************** + * + * A device support callable entry point used to read data from GPIB devices. + * + * This function returns the number of bytes read from the device, or ERROR + * if the read operation failed. + * + ******************************************************************************/ +int +drv162IB_read(struct ibLink *pibLink, int gpibAddr, char *data, int length, int time) +{ + CommandStruct cmd; + Mvme162GpibParmStruct parm; + + if(mv167Debug) + printf("readIb(%08.8X, %d, 0x%08.8X, %d)\n", pibLink, gpibAddr, data, length); + + cmd.Command = MVME162_GPIB_READ_X; + parm.OutLength = 0; + parm.InLength = length; + parm.Address = gpibAddr; + parm.Time = time; + + sysLocalToBusAdrs(VME_AM_EXT_SUP_DATA, data, &cmd.pInput); + sysLocalToBusAdrs(VME_AM_EXT_SUP_DATA, &parm, &cmd.pParms); + + cmd.IrqLevel = pIB162[pibLink->linkId]->IrqLevel; + cmd.IrqVector = pIB162[pibLink->linkId]->IrqVector; + cmd.pIrqParm = &(pIB162[pibLink->linkId]->IOsem); + + Send162(pIB162[pibLink->linkId]->pMvme162Link, &cmd); + semTake(pIB162[pibLink->linkId]->IOsem, WAIT_FOREVER); + + return(parm.Status); +} +#if 0 + +/****************************************************************************** + * + * A device support callable entry point that is used to write commands + * to GPIB devices. (this is the same as a regular write except that the + * ATN line is held high during the write. + * + * This function returns the number of bytes written out. + * + ******************************************************************************/ +static int +writeIbCmd(pibLink, data, length) +struct ibLink *pibLink; +char *data; /* the data buffer to write out */ +int length; /* number of bytes to write out from the data buffer */ +{ + + if(ibDebug || (bbibDebug & (pibLink->linkType == BBGPIB_IO))) + printf("writeIbCmd(%08.8X, %08.8X, %d)\n", pibLink, data, length); + + if (pibLink->linkType == GPIB_IO) + { + /* raw-write the data */ + return(niGpibCmd(pibLink->linkId, data, length)); + } + if (pibLink->linkType == BBGPIB_IO) + { + /* raw-write the data */ + return(bbGpibCmd(pibLink, data, length)); + } + return(ERROR); +} +#endif