From 6b58a8bbc9d92291c99e1f6bc310fc77fb2d2ae4 Mon Sep 17 00:00:00 2001 From: John Winans Date: Thu, 24 Oct 1991 16:04:16 +0000 Subject: [PATCH] Initial revision --- src/drv/drvBitBus.c | 810 ++++++++++++++++++++++++++++++++++++++++++++ src/drv/drvBitBus.h | 219 ++++++++++++ 2 files changed, 1029 insertions(+) create mode 100644 src/drv/drvBitBus.c create mode 100644 src/drv/drvBitBus.h diff --git a/src/drv/drvBitBus.c b/src/drv/drvBitBus.c new file mode 100644 index 000000000..5141b72dd --- /dev/null +++ b/src/drv/drvBitBus.c @@ -0,0 +1,810 @@ +/* drvBitBus.c */ +/* share/src/drv $Id$ */ +/* + * Original Author: Ned Arnold + * Author: John Winans + * Date: 09-10-91 + * XVME-402 BitBus driver + * + * 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: + * ----------------- + * .01 09-30-91 jrw Completely redesigned and rewritten + * + * NOTES: + * This driver currently needs work on error message generation. + */ + +/****************************************************************************** + * + * The following defines should be in module_types.h or derived + * from a support functions. + * + ******************************************************************************/ +#define BB_SHORT_OFF 0x1800 /* the first address of link 0's region */ +#define BB_NUM_LINKS 4 /* max number of BB ports allowed */ +#define BB_IVEC_BASE 0x90 /* vectored interrupts (2 used for each link) */ +#define BB_IRQ_LEVEL 3 /* IRQ level */ +/**************** end of stuff that does not belong here **********************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "drvBitBus.h" +#include "drvBitBusInterface.h" + +static long reportBB(); +static long initBB(); +static long qBBReq(); + +static int xvmeTmoHandler(); +static int xvmeLinkTask(); + +static int xvmeIrqTbmt(); +static int xvmeIrqRdav(); +static int xvmeIrqUndef(); +static int xvmeIrqRcmd(); + +void callbackRequest(); + +int bbDebug = 0; + + +/****************************************************************************** + * + * This structure contains a list of the outside-callable functions. + * + ******************************************************************************/ +struct { + long number; + DRVSUPFUN report; /* Report on the status of the Bit Bus links */ + DRVSUPFUN init; /* Init the xvme card */ + DRVSUPFUN qReq; /* Queue a transaction request */ +} drvVme={ + 3, + reportBB, + initBB, + qBBReq +}; + +static char init_called = 0; /* to insure that init is done first */ +static char *short_base; /* base of short address space */ +static char *ram_base; /* base of the ram on the CPU board */ + +static struct xvmeLink *pXvmeLink[BB_NUM_LINKS];/* NULL if link not present */ +static struct bbLink *pBbLink[BB_NUM_LINKS]; /* NULL if link not present */ + +/****************************************************************************** + * + * This function prints a message indicating the presence of each BB + * card found in the system. + * + ******************************************************************************/ +static long +reportBB() +{ + int i; + + logMsg("Debugging flag is set to %d\n", bbDebug); + + if (init_called) + { + for (i=0; i< BB_NUM_LINKS; i++) + { + if (pBbLink[i]) + { + logMsg("Link %d (address 0x%08.8X) present and initialized.\n", i, pXvmeLink[i]->bbRegs); + } + else + { + logMsg("Link %d not installed.\n", i); + } + } + } + else + { + logMsg("BB driver has not yet been initialized.\n"); + } + return(OK); +} + + +/****************************************************************************** + * + * Called by the iocInit processing. + * initBB, probes the bb card addresses and if one is present, it + * is initialized for use. + * + ******************************************************************************/ +static long +initBB() +{ + int i; + int j; + int probeValue; + struct xvmeRegs *pXvmeRegs; + + if (init_called) + { + logMsg("initBB(): BB devices already initialized!\n"); + return(ERROR); + } + /* figure out where the short address space is */ + sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO , 0, &short_base); + + /* figure out where the CPU memory is (when viewed from the backplane) */ + sysLocalToBusAdrs(VME_AM_STD_SUP_DATA, &ram_base, &ram_base); + ram_base = (char *)((ram_base - (char *)&ram_base) & 0x00FFFFFF); + + if (bbDebug) + { + logMsg("BB driver package initializing\n"); + logMsg("short_base 0x%08.8X\n", short_base); + logMsg("ram_base 0x%08.8X\n", ram_base); + logMsg("BB_SHORT_OFF 0x%08.8X\n", BB_SHORT_OFF); + logMsg("BB_NUM_LINKS 0x%08.8X\n", BB_NUM_LINKS); + } + + probeValue = XVME_RESET; + + pXvmeRegs = (struct xvmeRegs *)((unsigned int)short_base + BB_SHORT_OFF); + for (i=0; ififo_stat), WRITE, 1, &probeValue) < OK) + { /* no BB board present here */ + pXvmeLink[i] = (struct xvmeLink *) NULL; + pBbLink[i] = (struct bbLink *) NULL; + + if (bbDebug) + logMsg("Probing of address 0x%08.8X failed\n", pXvmeRegs); + } + else + { /* BB board found... reserve space for structures */ + + xvmeReset(pXvmeRegs, i); /* finish resetting the xvme module */ + + if (bbDebug) + logMsg("BB card found at address 0x%08.8X\n", pXvmeRegs); + + if ((pBbLink[i] = (struct bbLink *) malloc(sizeof(struct bbLink))) == NULL +) + { /* This better never happen! */ + /* errMsg( BUG -- figure out how to use this thing ); */ + logMsg("Can't malloc memory for link data structures!\n"); + return(ERROR); + } + pBbLink[i]->linkType = BITBUS_IO; /* spec'd in link.h */ + pBbLink[i]->linkId = i; /* link number */ + FASTLOCKINIT(&(pBbLink[i]->linkEventSem)); + + lstInit(&(pBbLink[i]->hiPriList)); /* init the high priority list */ + FASTLOCKINIT(&(pBbLink[i]->hiPriSem)); + FASTUNLOCK(&(pBbLink[i]->hiPriSem)); + + lstInit(&(pBbLink[i]->loPriList)); /* init the lo priority list */ + FASTLOCKINIT(&(pBbLink[i]->loPriSem)); + FASTUNLOCK(&(pBbLink[i]->loPriSem)); + + lstInit(&(pBbLink[i]->busyList)); /* init the busy message list */ + + for (j=0; jdeviceStatus[j] = IDLE; /* Assume all nodes are IDLE */ + } + + if ((pXvmeLink[i] = (struct xvmeLink *) malloc(sizeof(struct xvmeLink))) == NULL) + { + /* errMsg( BUG -- figure out how to use this thing ); */ + logMsg("Can't malloc memory for link data structures!\n"); + return(ERROR); + } + + pXvmeLink[i]->bbRegs = pXvmeRegs; + pXvmeLink[i]->watchDogId = wdCreate(); + + pXvmeLink[i]->rxStatus = BB_RXWAIT; /* waiting for a mwssage */ + pXvmeLink[i]->rxMsg = NULL; + pXvmeLink[i]->rxDpvtHead = NULL; + + pXvmeLink[i]->txDpvtHead = NULL; + pXvmeLink[i]->txMsg = NULL; + pXvmeLink[i]->watchDogFlag = 0; + + pXvmeRegs->stat_ctl = 0; /* disable all interupts */ + pXvmeRegs->int_vec = BB_IVEC_BASE + (i*4);/* set the int vector */ + + /* attach the interrupt handler routines */ + intConnect((BB_IVEC_BASE + i*4) * 4, xvmeIrqTbmt, i); + intConnect((BB_IVEC_BASE + 1 + (i*4)) * 4, xvmeIrqRcmd, i); + /* intConnect((BB_IVEC_BASE + 2 + (i*4)) * 4, xvmeIrqUndef, i); */ + intConnect((BB_IVEC_BASE + 3 + (i*4)) * 4, xvmeIrqRdav, i); + + /* start a task to manage the link */ + if (taskSpawn("bbLink", 46, VX_FP_TASK|VX_STDIO, 2000, xvmeLinkTask, i) == ERROR) + { + logMsg("initBB: failed to start link task for link %d\n", i); + /*errMsg()*/ + } + } + pXvmeRegs++; /* ready for next board window */ + } + sysIntEnable(BB_IRQ_LEVEL); + + init_called = 1; /* let reportBB() know init occurred */ + return(OK); +} + +/****************************************************************************** + * + * Reset an xvme-402 BitBus card by cycling the reset bit in the fifo + * status register. + * + ******************************************************************************/ +static int +xvmeReset(xvmeRegs, link) +struct xvmeRegs *xvmeRegs; +int link; +{ + char trash; + int j; + + + if (bbDebug) + logMsg("xvmeReset(%08.8X, %d): Resetting xvme module\n", xvmeRegs, link); + + xvmeRegs->fifo_stat = XVME_RESET; /* assert the reset pulse */ + taskDelay(1); + xvmeRegs->fifo_stat = 0; /* clear the reset pulse */ + taskDelay(4); /* give the 8044 time to self check */ + + j = 100; /* give up after this */ + while ((xvmeRegs->fifo_stat & XVME_RCMD) && --j) + trash = xvmeRegs->cmnd; /* flush command buffer if junk in it */ + + if (!j) + { + logMsg("xvmeReset(%d): Command buffer will not clear after reset!\n", link); + return(ERROR); + } + + j = 100; + while ((xvmeRegs->fifo_stat & XVME_RFNE) && --j) + trash = xvmeRegs->data; /* flush data buffer if junk in it */ + + if (!j) + { + logMsg("xvmeReset(%d): Data buffer will not clear after reset!\n", link); + return(ERROR); + } + + if ((xvmeRegs->fifo_stat & XVME_FSVALID) != XVME_FSIDLE) + { + logMsg("xvmeReset(%d): XVME board not returning to idle status after reset!\n", link); + return(ERROR); + } + return(OK); +} + + +/****************************************************************************** + * + * Interrupt handler that is called when the transmitter silo is ready for + * another byte to be loaded. + * + * Once the first byte of a mesage is sent, the rest are sent via interrupts. + * There is no other processing associated with an out-bound message once + * the first byte is sent. Therfore, this handler must stop itself when the + * last byte is sent. When it is finished, it wakes up the link task because + * there might be messages that are already queued for the link. + * + ******************************************************************************/ +static int +xvmeIrqTbmt(link) +int link; +{ + unsigned char ch; + + if (pXvmeLink[link]->txCount == 0) + { /* we just finished sending a message */ + if (bbDebug) + logMsg("xvmeIrqTbmt(%d): last char of xmit msg sent\n", link); + + pXvmeLink[link]->bbRegs->cmnd = BB_SEND_CMD; /* Tell XVME to xmit it now */ + + ch = pXvmeLink[link]->bbRegs->stat_ctl; /* get current int status */ + ch &= XVME_ENABLE_INT|XVME_RX_INT; /* mask off TX interrupts */ + pXvmeLink[link]->bbRegs->stat_ctl = ch; /* and reset the int mask */ + +/* BUG -- do the callbackRequest in here if the command was a RAC_RESET_SLAVE */ + + FASTUNLOCK(&(pBbLink[link]->linkEventSem)); /* wake up the Link Task */ + } + else + { + if (bbDebug) + logMsg("xvmeIrqTbmt(%d): outputting char %02.2X\n", link, *(pXvmeLink[link]->txMsg)); + + pXvmeLink[link]->txCount--; + ch = *(pXvmeLink[link]->txMsg); + pXvmeLink[link]->txMsg++; + + pXvmeLink[link]->bbRegs->data = ch; /* xmit the character */ + } + return(0); +} + + +/****************************************************************************** + * + * Interrupt handler that is called when the receiver has a byte that should be + * read. + * + ******************************************************************************/ +static int +xvmeIrqRdav(link) +int link; +{ + unsigned char ch; + + switch (pXvmeLink[link]->rxStatus) { + case BB_RXWAIT: /* waiting for the first byte of a new message */ + pXvmeLink[link]->rxHead[0] = pXvmeLink[link]->bbRegs->data; + if (bbDebug) + logMsg("xvmeIrqRdav(%d): >%02.2X< new message\n", link, pXvmeLink[link]->rxHead[0]); + pXvmeLink[link]->cHead = 1; + pXvmeLink[link]->rxStatus = BB_RXGOT1; + break; + + case BB_RXGOT1: /* currently receiving the header of a bitbus message */ + pXvmeLink[link]->rxHead[pXvmeLink[link]->cHead] = pXvmeLink[link]->bbRegs->data; + if (bbDebug) + logMsg("xvmeIrqRdav(%d): >%02.2X<\n", link, pXvmeLink[link]->rxHead[pXvmeLink[link]->cHead]); + pXvmeLink[link]->cHead++; + + if (pXvmeLink[link]->cHead == 3) + pXvmeLink[link]->rxStatus = BB_RXGOT3; + break; + case BB_RXGOT3: /* got 3 bytes of header so far */ + ch = pXvmeLink[link]->bbRegs->data; /* get the 'tasks' byte of the header */ + if (bbDebug) + logMsg("xvmeIrqRdav(%d): >%02.2X<\n", link, pXvmeLink[link]->rxHead[pXvmeLink[link]->cHead]); + pXvmeLink[link]->cHead++; + + /* find the message this is a reply to */ + pXvmeLink[link]->rxDpvtHead = (struct dpvtHead *) lstFirst(&(pBbLink[link]->busyList)); + while (pXvmeLink[link]->rxDpvtHead != NULL) + { /* see if node's match */ + if (pXvmeLink[link]->rxDpvtHead->txMsg->node == pXvmeLink[link]->rxHead[2]) + { /* see if the tasks match */ + if (pXvmeLink[link]->rxDpvtHead->txMsg->tasks == ch) + { /* They match, finish putting the response into the rxMsg buffer */ + if (bbDebug) + logMsg("xvmeIrqRdav(%d): msg is response to 0x%08.8X\n", link, pXvmeLink[link]->rxDpvtHead); + + /* I can do the list operation here, because the linkTask */ + /* disables XVME RX interrupts when using the busy list! */ + lstDelete(&(pBbLink[link]->busyList), pXvmeLink[link]->rxDpvtHead); + + pXvmeLink[link]->rxDpvtHead->rxMsg->length = pXvmeLink[link]->rxHead[0]; + pXvmeLink[link]->rxDpvtHead->rxMsg->route = pXvmeLink[link]->rxHead[1]; + pXvmeLink[link]->rxDpvtHead->rxMsg->node = pXvmeLink[link]->rxHead[2]; + pXvmeLink[link]->rxDpvtHead->rxMsg->tasks = ch; + pXvmeLink[link]->rxMsg = &(pXvmeLink[link]->rxDpvtHead->rxMsg->cmd); + + pXvmeLink[link]->rxDpvtHead->status = OK; /* OK, unless BB_LENGTH */ + break; /* get out of the while() */ + } + } + pXvmeLink[link]->rxDpvtHead = (struct dpvtHead *) lstNext(pXvmeLink[link]->rxDpvtHead); /* Keep looking */ + } + if (pXvmeLink[link]->rxDpvtHead == NULL) + { + logMsg("xvmeIrqRdav(%d): msg from node %d unsolicited!\n", link, pXvmeLink[link]->rxHead[2]); + pXvmeLink[link]->rxStatus = BB_RXIGN; /* nothing waiting... toss it */ + } + else + pXvmeLink[link]->rxStatus = BB_RXREST; /* keep reading till finished */ + break; + + case BB_RXREST: /* reading message body */ + *(pXvmeLink[link]->rxMsg) = pXvmeLink[link]->bbRegs->data; + if (bbDebug) + logMsg("xvmeIrqRdav(%d): >%02.2X<\n", link, *(pXvmeLink[link]->rxMsg)); + pXvmeLink[link]->rxMsg++; + pXvmeLink[link]->cHead++; + if (pXvmeLink[link]->cHead >= pXvmeLink[link]->rxDpvtHead->rxMaxLen) + { + logMsg("xvmeIrqRdav(%d): in-bound message length too long for device support buffer!\n", link); + pXvmeLink[link]->rxStatus = BB_RXIGN; /* toss the rest of the data */ + pXvmeLink[link]->rxDpvtHead->status = BB_LENGTH; /* set driver status */ + } + break; + + case BB_RXIGN: /* dump the rest of the message */ + ch = pXvmeLink[link]->bbRegs->data; + if (bbDebug) + logMsg("xvmeIrqRdav(%d): ignoring >%02.2X<\n", link, ch); + pXvmeLink[link]->cHead++; /* should I bother??? */ + break; + } + return(0); +} + + +static int +xvmeIrqUndef(link) +int link; +{ + logMsg("UNDEFINED BitBus interrupt from link %d\n", link); + return(0); +} + + +/****************************************************************************** + * + * This interrupt handler is invoked when the BitBus controller has completed + * the transfer of a RECEIVED message. + * + * First, a check is made to insure we were in fact receiving a message and + * if so, the message request's callback function is invoked. It then + * wakes up the link-task in case there were messages waiting the same node + * (ie. it was busy, and is now idle.) + * + ******************************************************************************/ +static int +xvmeIrqRcmd(link) +int link; +{ + if(bbDebug) + logMsg("BB RCMD interrupt generated on link %d\n", link); + + /* make sure we have a valid rxDpvtHead pointer first */ + if (pXvmeLink[link]->rxStatus != BB_RXREST) + { + logMsg("xvmeIrqRcmd(%d): ERROR, rxStatus=%d, command=%02.2X\n", link, pXvmeLink[link]->rxStatus, pXvmeLink[link]->bbRegs->cmnd); + } + else + { + pXvmeLink[link]->rxStatus = BB_RXWAIT; /* are now waiting for a new message */ + pXvmeLink[link]->rxDpvtHead->status = BB_OK; + pXvmeLink[link]->rxDpvtHead->rxCmd = pXvmeLink[link]->bbRegs->cmnd; + if (bbDebug) + logMsg("xvmeIrqRcmd(%d): command byte = %02.2X\n", link, pXvmeLink[link]->rxDpvtHead->rxCmd); + + /* decrement the number of outstanding messages to the node */ + (pBbLink[link]->deviceStatus[pXvmeLink[link]->rxDpvtHead->rxMsg->node])--; + + if (pXvmeLink[link]->rxDpvtHead->finishProc != NULL) + { + pXvmeLink[link]->rxDpvtHead->list_callback.callBack = pXvmeLink[link]->rxDpvtHead->finishProc; + pXvmeLink[link]->rxDpvtHead->list_proirity.priority = pXvmeLink[link]->rxDpvtHead->priority; + + if (bbDebug) + logMsg("xvmeIrqRcmd(%d): invoking the callbackRequest\n", link); + /*callbackRequest(pXvmeLink[link]->rxDpvtHead);*/ /* schedule completion processing */ + + (pXvmeLink[link]->rxDpvtHead->list_callback.callBack)(pXvmeLink[link]->rxDpvtHead); + } + } + pXvmeLink[link]->rxDpvtHead = NULL; /* The watch dog agerizer needs this */ + + if (!lstCount(&(pBbLink[link]->busyList))) /* if list empty, stop the watch dog */ + wdCancel(pXvmeLink[link]->watchDogId); + + FASTUNLOCK(&(pBbLink[link]->linkEventSem)); /* wake up the Link Task */ + return(0); +} + + +/****************************************************************************** + * + * Given a link number, make sure it is valid. + * + ******************************************************************************/ +static int +checkLink(link) +int link; +{ + if (link<0 || link>BB_NUM_LINKS) + { + /* link number out of range */ + return(ERROR); + } + if (pBbLink[link] == NULL) + { + /* link number has no card installed */ + return(ERROR); + } + return(OK); +} + + +/****************************************************************************** + * + * Watchdogs are running when ever the busy list has any elements in it. + * The idea here is that the watchdog handler scans thru the busy list, + * looking for old requests that have not been replied to in too long + * a time. If there are any old ones around, they are removed from the + * list and marked as un-replied to. + * + * The actual watchdog handler work is done in the link task when the + * watchDogFlag is set from this routine. + * + ******************************************************************************/ +static int +xvmeTmoHandler(link) +int link; +{ + /* if (bbDebug) */ + logMsg("xvmeTmoHandler(%d): Watch dog interrupt\n", link); + + pXvmeLink[link]->watchDogFlag = 1; /* set the timeout flag for the link */ + FASTUNLOCK(&(pBbLink[link]->linkEventSem)); + return(0); +} + + +/****************************************************************************** + * + * This function is started as a task during driver init time. It's purpose + * is to keep the link busy. It awaits user-calls to qBbReq() with new work + * as well as wake-up calls from the watchdog timer. + * + * At the time this function is started as its own task, the linked list + * structures will have been created and initialized. + * + ******************************************************************************/ +static int +xvmeLinkTask(link) +int link; +{ + struct bbLink *plink; /* a reference to the link structures covered */ + struct dpvtHead *pnode; + struct dpvtHead *npnode; + unsigned char intMask; + + if (bbDebug) + logMsg("xvmeLinkTask started for link %d\n", link); + + plink = pBbLink[link]; + + /* init the interrupts on the XVME board */ + pXvmeLink[link]->bbRegs->stat_ctl = XVME_ENABLE_INT | XVME_RX_INT; + + while (1) + { + FASTLOCK(&(plink->linkEventSem)); + + if (bbDebug) + logMsg("xvmeLinkTask(%d): got an event\n", link); + + if (pXvmeLink[link]->watchDogFlag) + { /* Time to age the busy list members */ + if (bbDebug) + logMsg("xvmeLinkTask(%d): (Watchdog) checking busy list\n", link); + + pXvmeLink[link]->watchDogFlag = 0; + + intMask = pXvmeLink[link]->bbRegs->stat_ctl & (XVME_RX_INT | XVME_ENABLE_INT | XVME_TX_INT); + /* Turn off ints from this link so list integrity is maintained */ + pXvmeLink[link]->bbRegs->stat_ctl = XVME_NO_INT; + + pnode = (struct dpvtHead *) lstFirst(&(plink->busyList)); + while (pnode != NULL) + { + pnode->ageLimit--; + if(bbDebug) + logMsg("xvmeLinkTask(%d): (Watchdog) node 0x%08.8X.ageLimit=%d\n", link, pnode, pnode->ageLimit); + if (pnode->ageLimit <= 0) + { /* This node has been on the busy list for too long */ + npnode = (struct dpvtHead *) lstNext(pnode); + if ((intMask & XVME_TX_INT) && (pXvmeLink[link]->txDpvtHead == pnode)) + { /* Uh oh... Transmitter is stuck while sending this xact */ + logMsg("xvmeLinkTask(%d): transmitter looks stuck, link dead\n", link); + taskDelay(60); /* waste some time / + /* BUG -- This should probably reset the xvme card here */ + } + else + { /* Get rid of the request and set error status etc... */ + lstDelete(&(plink->busyList), pnode); + logMsg("xvmeLinkTask(%d): TIMEOUT on xact 0x%08.8X\n", link, pnode); + pnode->status = BB_TIMEOUT; + (plink->deviceStatus[pnode->txMsg->node])--; /* fix node status */ + if(pnode->finishProc != NULL) + { /* make the callbackRequest to inform message sender */ + pnode->list_callback.callBack = pnode->finishProc; + pnode->list_proirity.priority = pnode->priority; + /* callbackRequest(pnode); */ /* schedule completion processing */ + (pnode->list_callback.callBack)(pnode); + } + } + pnode = npnode; /* Because I deleted the current one */ + } + else + pnode = (struct dpvtHead *) lstNext(pnode); /* check out the rest */ + } + /* check the pnode pointed to by rxDpvtHead to see if rcvr is stuck */ + if (pXvmeLink[link]->rxDpvtHead != NULL) + { /* the rcvr is busy on something... check it out too */ + if (--(pXvmeLink[link]->rxDpvtHead->ageLimit) < 0) + { /* old, but busy... and we even gave it an extra tick. Rcvr stuck. */ + logMsg("xvmeLinkTask(%d): receiver looks stuck, link dead\n", link); + taskDelay(60); + /* BUG -- This should probably reset the xvme card */ + } + } + /* Restart the timer if the list is not empty */ + if (lstCount(&(plink->busyList))) /* If list not empty, start watch dog */ + { + if (bbDebug) + logMsg("xvmeLinkTask(%d): restarting watch dog timer\n", link); + wdStart(pXvmeLink[link]->watchDogId, BB_WD_INTERVAL, xvmeTmoHandler, link); + } + + /* Restore interrupt mask for the link */ + pXvmeLink[link]->bbRegs->stat_ctl = intMask; + } + + /* If transmitter interrupts are enabled, then it is currently busy */ + + if (!(pXvmeLink[link]->bbRegs->stat_ctl & XVME_TX_INT)) + { /* If we are here, the transmitter is idle and the TX ints are disabled */ + + /* see if the Hi priority queue has anything in it */ + FASTLOCK(&(plink->hiPriSem)); + /* disable XVME RX ints for the link while accessing the busy list! */ + intMask = pXvmeLink[link]->bbRegs->stat_ctl & (XVME_RX_INT | XVME_ENABLE_INT); + pXvmeLink[link]->bbRegs->stat_ctl = XVME_NO_INT; /* stop the receiver */ + + if ((pnode = (struct dpvtHead *)lstFirst(&(plink->hiPriList))) != NULL) + { + while (plink->deviceStatus[pnode->txMsg->node] == BUSY) + if ((pnode = (struct dpvtHead *)lstNext(&(plink->hiPriList))) == NULL) + break; + } + if (pnode != NULL) + { + lstDelete(&(plink->hiPriList), pnode); + + if (bbDebug) + logMsg("xvmeLinkTask(%d): got Hi Pri xact, pnode= 0x%08.8X\n", link, pnode); + + /* Count the outstanding messages */ + (plink->deviceStatus[pnode->txMsg->node])++; + + /* ready the structures for the TX int handler */ + pXvmeLink[link]->txDpvtHead = pnode; + pXvmeLink[link]->txCount = pnode->txMsg->length - 2; + pXvmeLink[link]->txMsg = &(pnode->txMsg->length); + + intMask |= XVME_ENABLE_INT | XVME_TX_INT; + + if (!lstCount(&(plink->busyList))) /* if list empty, start watch dog */ + wdStart(pXvmeLink[link]->watchDogId, BB_WD_INTERVAL, xvmeTmoHandler, link); + lstAdd(&(plink->busyList), pnode); /* put request on busy list */ + pXvmeLink[link]->bbRegs->stat_ctl = intMask; /* need before use xmtr */ + intMask = 0; /* I already set the int mask */ + xvmeIrqTbmt(link); /* force first byte out */ + } + if (intMask) + pXvmeLink[link]->bbRegs->stat_ctl = intMask; /* Restore rcvr ints */ + + FASTUNLOCK(&(plink->hiPriSem)); + + /* only continue if nothing was found in the high priority list */ + if (pnode == NULL) + { + FASTLOCK(&(plink->loPriSem)); + /* disable XVME RX ints for the link while accessing the busy list! */ + intMask = pXvmeLink[link]->bbRegs->stat_ctl & (XVME_RX_INT | XVME_ENABLE_INT); + pXvmeLink[link]->bbRegs->stat_ctl = XVME_NO_INT; /* stop the receiver */ + + if ((pnode = (struct dpvtHead *)lstFirst(&(plink->loPriList))) != NULL) + { + while (plink->deviceStatus[pnode->txMsg->node] == BUSY) + if ((pnode = (struct dpvtHead *)lstNext(&(plink->loPriList))) == NULL) + break; + } + if (pnode != NULL) + { + lstDelete(&(plink->loPriList), pnode); + + if(bbDebug) + logMsg("xvmeLinkTask(%d): got Lo Pri xact, pnode= 0x%08.8X\n", link, pnode); + + /* Count the outstanding messages */ + (plink->deviceStatus[pnode->txMsg->node])++; + + /* ready the structures for the TX int handler */ + pXvmeLink[link]->txDpvtHead = pnode; + pXvmeLink[link]->txCount = pnode->txMsg->length - 2; + pXvmeLink[link]->txMsg = &(pnode->txMsg->length); + + intMask |= XVME_ENABLE_INT | XVME_TX_INT; + + if (!lstCount(&(plink->busyList))) /* Start watch dog if list empty */ + wdStart(pXvmeLink[link]->watchDogId, BB_WD_INTERVAL, xvmeTmoHandler, link); + lstAdd(&(plink->busyList), pnode); /* put on busy list */ + pXvmeLink[link]->bbRegs->stat_ctl = intMask; /* need before xmit */ + intMask = 0; /* I already set them */ + xvmeIrqTbmt(link); /* force first byte out */ + } + if (intMask) + pXvmeLink[link]->bbRegs->stat_ctl = intMask; /* restore rcvr ints */ + + FASTUNLOCK(&(plink->loPriSem)); + } + } + } +} + + +/****************************************************************************** + * + * This function is called by user programs to queue an I/O transaction request + * for the BB driver. It is the only user-callable function provided in this + * driver. + * + ******************************************************************************/ +static long +qBBReq(link, pdpvt, prio) +int link; +struct dpvtHead *pdpvt; +int prio; +{ + switch (prio) { + case 1: /* low priority transaction request */ + FASTLOCK(&(pBbLink[link]->loPriSem)); + lstAdd(&(pBbLink[link]->loPriList), pdpvt); + FASTUNLOCK(&(pBbLink[link]->loPriSem)); + FASTUNLOCK(&(pBbLink[link]->linkEventSem)); + break; + case 2: /* high priority transaction request */ + FASTLOCK(&(pBbLink[link]->hiPriSem)); + lstAdd(&(pBbLink[link]->hiPriList), pdpvt); + FASTUNLOCK(&(pBbLink[link]->hiPriSem)); + FASTUNLOCK(&(pBbLink[link]->linkEventSem)); + break; + default: /* invalid priority */ + logMsg("invalid priority requested in call to qbbreq(%d, %08.8X, %d)\n", link, pdpvt, prio); + return(ERROR); + } + if (bbDebug) + logMsg("qbbreq(%d, 0x%08.8X, %d): transaction queued\n", link, pdpvt, prio); + + return(OK); +} diff --git a/src/drv/drvBitBus.h b/src/drv/drvBitBus.h new file mode 100644 index 000000000..39ca00766 --- /dev/null +++ b/src/drv/drvBitBus.h @@ -0,0 +1,219 @@ +/* drvBitBusInterface.h */ +/* share/src/drv $Id$ */ +/* + * Author: John Winans + * Date: 09-10-91 + * XVME-402 BitBus driver + * + * 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: + * ----------------- + * .01 09-30-91 jrw Written + */ + +/****************************************************************************** + * + * Possible states the receiver message handling system can be in. + * + ******************************************************************************/ +#define BB_RXWAIT 0 /* waiting for a new message */ +#define BB_RXGOT1 1 /* receiving header bytes */ +#define BB_RXREST 2 /* finished with header, reading message */ +#define BB_RXIGN 3 /* unsolicited message??? ignoreing rest */ +#define BB_RXGOT3 4 /* got 3 bytes of the header so far */ + +/****************************************************************************** + * Memory Map of XVME-402 BITBUS CARD + * + * This board is rather stupid in that it wastes a whole Kilo of space + * for its 5 1-byte regs. So the dm* fields in the structure below are + * for those filler locations. + * + *****************************************************************************/ +struct xvmeRegs { + unsigned char dm0; + unsigned char stat_ctl; + unsigned char dm2; + unsigned char int_vec; + unsigned char dm4; + unsigned char data; + unsigned char dm6; + unsigned char cmnd; + unsigned char dm8; + unsigned char fifo_stat; + unsigned char dmA[1014]; /* Board occupies 1024 bytes in memory*/ + }; + +#define BB_APERLINK 256 /* node 255 is the master */ +#define BB_SEND_CMD 0x0 /* command to initiate sending of msg */ +#define BB_MAX_MSG_LENGTH 20 /* currently max node configuration */ + +#define XVME_ENABLE_INT 0x08 /* Allow xvme interupts */ +#define XVME_TX_INT 0x20 /* int enable TX only */ +#define XVME_RX_INT 0x80 /* int exable RX only */ +#define XVME_NO_INT 0 /* disable all interrupts */ + +/****************************************************************************** + * + * Fifo status register format + * + * +----+----+----+----+----+----+----+----+ + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Bits + * +----+----+----+----+----+----+----+----+ + * "1" "1" RCMD RFNE "1" "1" "1" TFNF data + * + * RCMD = "1"= command received ( end of message ) + * "0"= No command + * + * RFNE = "1" = Receive Fifo Not Empty + * "0" = Receive Fifo empty + * + * TFNF = "1" = Transmit Fifo Not Full + * "0" = transmit fifo full + * + * NOTE: + * A write to bit 7 of the Fifo status register can be used to reset the + * xvme board. + * + *****************************************************************************/ +#define XVME_RCMD 0x20 /* Command has been received */ +#define XVME_RFNE 0x10 /* Receive Fifo is Not Empty */ +#define XVME_TFNF 0x01 /* Transmit FIFO is Not Full */ +#define XVME_FSVALID 0x31 /* these are the only valid status bits */ +#define XVME_FSIDLE 0x01 /* fifo_stat & FSVALID = 0x1 when it is idle */ +#define XVME_RESET 0x80 /* Write this and you reset the XVME card */ + +/* BitBus Transaction Message Structure */ +typedef struct bitBusMsg { + unsigned short link; /* account for this in length only! */ + unsigned char length; + unsigned char route; + unsigned char node; + unsigned char tasks; + unsigned char cmd; + unsigned char parm[BB_MAX_MSG_LENGTH - 7 + 1]; /* save room for \0 */ +}; + +/* Partial List of RAC Command Definitions (defined in BitBus Specification) */ +#define RAC_RESET_SLAVE 0x00 +#define RAC_CREATE_TASK 0x01 +#define RAC_DELETE_TASK 0x02 +#define RAC_GET_FUNC_IDS 0x03 +#define RAC_PROTECT 0x04 +#define RAC_READ_IO 0x05 +#define RAC_WRITE_IO 0x06 +#define RAC_UPDATE_IO 0x07 +#define RAC_UPLOAD_MEM 0x08 +#define RAC_DOWNLOAD_MEM 0x09 +#define RAC_OR_IO 0x0A +#define RAC_AND_IO 0x0B +#define RAC_XOR_IO 0x0C +#define RAC_STATUS_READ 0x0D +#define RAC_STATUS_WRITE 0x0E +#define RAC_NODE_INFO 0x0F +#define RAC_OFFLINE 0x10 +#define RAC_UPLOAD_CODE 0x11 +#define RAC_DOWNLOAD_CODE 0x12 + + +/* Reply message Command Byte Definitions */ +#define BB_NO_ERROR 0x00 + +/* 0x01 thru 0x7F are user defined */ +#define BB_SEND_TMO 0x70 + +/* 0x80 thru 0xFF are defined by BitBus */ +#define BB_NO_DEST_TASK 0x80 +#define BB_TASK_OVERFLOW 0x81 +#define BB_REG_BANK_OVERFLOW 0x82 +#define BB_DUP_FUNC_ID 0x83 +#define BB_NO_BUFFERS 0x84 + +#define BB_PROTOCOL_ERR 0x91 +#define BB_NO_DEST_DEV 0x93 +#define BB_RAC_PROTECTED 0x95 +#define BB_UNKNOWN_RAC_CMND 0x96 + +/****************************************************************************** + * + * The BUSY/IDLE notion is used to count the number of outstanding + * messages for a specific node. The idea is that up to BUSY messages + * can be sent to a node before waiting before deciding not to send any more. + * According to the BitBus specs, this value is 7. However, it also + * states that responses can come back out of order. If this is even true + * for messages sent to the SAME TASK ON THE SAME NODE, the received messages + * CAN NOT be routed back to their initiators properly. Because the node# + * and task# is all we have to identify what a response message is for, + * I am limiting the per-node maximum to 1. + * + ******************************************************************************/ + +#define BUSY 1 /* deviceStatus value if device is currently busy */ +#define IDLE 0 /* deviceStatus value if device is currently idle */ + +/****************************************************************************** + * + * The bbLink structure holds all the required link-specific queueing + * information. + * + ******************************************************************************/ +struct bbLink { + int linkType; /* the type of link (defined in link.h) */ + int linkId; /* the link number of this structure */ + + FAST_LOCK linkEventSem; /* given when this link requires service */ + + LIST hiPriList; /* list head for high priority queue */ + FAST_LOCK hiPriSem; /* semaphore for high priority queue */ + LIST loPriList; /* list head for low priority queue */ + FAST_LOCK loPriSem; /* semaphore for low priority queue */ + + LIST busyList; /* messages waiting on a response */ + + unsigned char deviceStatus[BB_APERLINK];/* mark a device as idle or busy */ +}; + + +/****************************************************************************** + * + * The xvmeLink structure holds all the xvme-card specific data required + * for the operation of the link. + * + ******************************************************************************/ +struct xvmeLink { + WDOG_ID watchDogId; /* watchdog for timeouts */ + int watchDogFlag; /* set by the watch dog int handler */ + struct xvmeRegs *bbRegs;/* pointer to board registers */ + + int rxStatus; /* current state of the message receiver */ + char rxHead[5]; /* room to keep part of the current message's he +ader information */ + unsigned char *rxMsg; /* where to place next message byte (after heade +r transfer!!!) */ + int cHead; /* byte counter for data in rxHead */ + struct dpvtHead *rxDpvtHead; /* for message currently receiving */ + + int txCount; /* number of bytes sent in current message */ + struct dpvtHead *txDpvtHead; /* for message currently transmitting */ + unsigned char *txMsg; /* next byte to transmit in current msg */ +};