/* drvPEPBitBus.c */ /* base/src/drv $Id$ */ /* * Original Author: Ned Arnold * Author: John Winans * Current: Claude Saunders * Date: 8-4-93 * PEP Modular PB-BIT 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 mm-dd-yy name note * .01 08-04-93 saunders removed automatic RAC_RESET on timeout * done by device support for compat. w/ xycom * NOTES: * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "drvBitBusInterface.h" #include "drvPEPBitBus.h" #define STATIC #define PEP_BB_BOARD_OFF 0x800 STATIC long reportBB(), initBB(), qBBReq(); STATIC int xvmeTmoHandler(), xvmeRxTask(), xvmeTxTask(), xvmeWdTask(); STATIC int xvmeIrqRdav(), xvmeIrqRcmd(); int bbKill(int link); int bbDebug = 0; /* set to 1 from the shell to print debugging info */ /****************************************************************************** * * This structure contains a list of the outside-callable functions. * *****************************************************************************/ struct drvBitBusEt drvBitBus = { 3, reportBB, initBB, qBBReq }; STATIC char init_called = 0; /* to insure that init is done first */ STATIC void *short_base; /* base of short address space */ STATIC struct xvmeLink *pXvmeLink[BB_NUM_LINKS]; /* NULL if link not present */ /****************************************************************** * FUNCTION: reportBB() * PURPOSE : Prints a message indicating the presence of each * PB-BIT module found in system. * ARGS IN : none * ARGS OUT: none * GLOBALS: checks pXvmeLink[i] for NULL ******************************************************************/ STATIC long reportBB() { int i; printf("Debugging flag is set to %d\n", bbDebug); if (init_called) { for (i=0; i< BB_NUM_LINKS; i++) { if (pXvmeLink[i] != NULL) { printf("Link %d (address 0x%08.8X) present and initialized.\n", i, pXvmeLink[i]->bbRegs); } else { printf("Link %d not installed.\n", i); } } } else { printf("BB driver has not yet been initialized.\n"); } return(OK); } /****************************************************************** * * Reset hook function... takes down ALL the bitbus links and leaves * them down. * ******************************************************************/ STATIC void BBrebootFunc(void) { int i; for (i=0; iint_vec), WRITE, 1, &probeValue) < OK) { /* no BB board present here */ pXvmeLink[i] = (struct xvmeLink *) NULL; if (bbDebug) printf("Probing of address 0x%08.8X failed\n", pXvmeRegs); } else { /* BB board found... reserve space for structures */ if (bbDebug) printf("BB card found at address 0x%08.8X\n", pXvmeRegs); if ((pXvmeLink[i] = (struct xvmeLink *)malloc(sizeof(struct xvmeLink))) == NULL) { printf("Can't malloc memory for link data structures!\n"); return(ERROR); } if ((pXvmeLink[i]->pbbLink = (struct bbLink *)malloc(sizeof(struct bbLink))) == NULL) { printf("Can't malloc memory for link data structures!\n"); return(ERROR); } pXvmeLink[i]->pbbLink->linkType = BITBUS_IO; /* spec'd in link.h */ pXvmeLink[i]->pbbLink->linkId = i; /* link number */ /* Semaphore for blocking/unblocking Tx task */ pXvmeLink[i]->pbbLink->linkEventSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); /* Semaphore for blocking/unblocking Rx task */ pXvmeLink[i]->rxInt = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); /* init all the prioritized queue lists */ for (j=0; jpbbLink->queue[j].head = NULL; pXvmeLink[i]->pbbLink->queue[j].tail = NULL; pXvmeLink[i]->pbbLink->queue[j].sem = semBCreate(SEM_Q_PRIORITY, SEM_FULL); } /* init the busy message list */ pXvmeLink[i]->pbbLink->busyList.sem = semBCreate(SEM_Q_PRIORITY, SEM_FULL); pXvmeLink[i]->pbbLink->busyList.head = NULL; pXvmeLink[i]->pbbLink->busyList.tail = NULL; for (j=0; jpbbLink->deviceStatus[j] = BB_IDLE; } pXvmeLink[i]->bbRegs = pXvmeRegs; pXvmeLink[i]->watchDogId = wdCreate(); /* Semaphore for blocking/unblocking Wd task */ pXvmeLink[i]->watchDogSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); /* clear the link abort status */ pXvmeLink[i]->abortFlag = 0; pXvmeLink[i]->txAbortAck = 0; pXvmeLink[i]->rxAbortAck = 0; xvmeReset(pXvmeRegs, i); /* finish reset the PB-BIT module */ /* Attach the Rx interrupt handler routine. Vector based on link # */ intConnect(INUM_TO_IVEC(PEP_BB_IVEC_BASE + (i*2)), xvmeIrqRdav, i); /* start a task to manage the TX link */ sprintf(nameTemp, "%s%d", BBTXLINK_NAME, i); if (taskSpawn(nameTemp,BBTXLINK_PRI,BBTXLINK_OPT,BBTXLINK_STACK, xvmeTxTask, i) == ERROR) { printf("initBB: failed to start TX link task for link %d\n", i); } /* start a task to manage the RX link */ sprintf(nameTemp, "%s%d", BBRXLINK_NAME, i); if (taskSpawn(nameTemp,BBRXLINK_PRI,BBRXLINK_OPT,BBRXLINK_STACK, xvmeRxTask, i) == ERROR) { printf("initBB: failed to start RX link task for link %d\n", i); } /* start a watchdog (Wd) task to keep an eye on the busy list */ sprintf(nameTemp, "%s%d", BBWDTASK_NAME, i); if (taskSpawn(nameTemp,BBWDTASK_PRI,BBWDTASK_OPT,BBWDTASK_STACK, xvmeWdTask, i) == ERROR) { printf("initBB: failed to start watchdog task for link %d\n", i); } } /* set pXvmeRegs to point to next piggyback or board window */ if (((unsigned)pXvmeRegs & 0xff) == 0x80) pXvmeRegs = (struct xvmeRegs *) ((unsigned char *)pXvmeRegs - 0x80 + PEP_BB_BOARD_OFF); else pXvmeRegs = (struct xvmeRegs *)((unsigned char *)pXvmeRegs + 0x80); } sysIntEnable(BB_IRQ_LEVEL); init_called = 1; /* let reportBB() know init occured */ return(OK); } /****************************************************************** * FUNCTION: xvmeReset() * PURPOSE : Performs firmware reset of PB-BIT module corresponding * to link. Attempts to empty any data sitting in receive * FIFO. Interrupts are disabled and Rx task is unblocked. * ARGS IN : xvmeRegs ptr to register structure of PB-BIT module * link link number serviced by PB-BIT module * ARGS OUT: none * GLOBALS: twiddles board ******************************************************************/ STATIC int xvmeReset(xvmeRegs, link) struct xvmeRegs *xvmeRegs; int link; { char trash; int j; int lockKey; if (bbDebug) printf("xvmeReset(%08.8X, %d): Resetting xvme module\n", xvmeRegs, link); /* Write firmware reset package (2 bytes) to board */ xvmeRegs->data = 0x83; xvmeRegs->stat_ctl = 0x01; taskDelay(20); /* give the 80152 time to self check */ if ((xvmeRegs->stat_ctl & 0x10) != 0x0) { printf("xvmeReset(%d): PB-BIT firmware reset failed!\n", link); return(ERROR); } j = 1026; /* 1K deep receive fifo */ while ((xvmeRegs->stat_ctl & XVME_RFNE) && --j) trash = xvmeRegs->data; /* flush receive fifo if junk in it */ if (!j) { printf("xvmeReset(%d): receive fifo will not clear after reset!\n"); return(ERROR); } /* Disable interrupts */ lockKey = intLock(); xvmeRegs->int_vec = 0; intUnlock(lockKey); semGive(pXvmeLink[link]->rxInt); /* Tell xvmeRxTask to Re-enable ints */ return(OK); } /************************************************************************** * * This function is used to add a node to the HEAD of a list. * **************************************************************************/ static int listAddHead(plist, pnode) struct bbList *plist; struct dpvtBitBusHead *pnode; { pnode->prev = NULL; pnode->next = plist->head; if (plist->head != NULL) plist->head->prev = pnode; if (plist->tail == NULL) plist->tail = pnode; plist->head = pnode; return(0); } /****************************************************************************** * * This function is used to add a node to the TAIL of a list. * ******************************************************************************/ static int listAddTail(plist, pnode) struct bbList *plist; struct dpvtBitBusHead *pnode; { pnode->next = NULL; /* No next node if this is the TAIL */ pnode->prev = plist->tail; /* previous node is the 'old' TAIL node */ if (plist->tail != NULL) plist->tail->next = pnode; /* link the 'old' tail to the 'new' tail node */ if (plist->head == NULL) plist->head = pnode; plist->tail = pnode; /* this is the 'new' tail node */ return(0); } /****************************************************************************** * * This function is used to delete a node from a list. * ******************************************************************************/ static int listDel(plist, pnode) struct bbList *plist; struct dpvtBitBusHead *pnode; { if (pnode->next != NULL) pnode->next->prev = pnode->prev; if (pnode->prev != NULL) pnode->prev->next = pnode->next; if (plist->head == pnode) plist->head = pnode->next; if (plist->tail == pnode) plist->tail = pnode->prev; return(0); } /****************************************************************** * FUNCTION: xvmeIrqRdav() * PURPOSE : Invoked when PB-BIT module has received a complete * bitbus message (ie. not on a byte-by-byte basis). * ARGS IN : link link number upon which message has arrived * ARGS OUT: none * GLOBALS: unblocks Rx task (if it is not already running) ******************************************************************/ STATIC int xvmeIrqRdav(link) int link; { pXvmeLink[link]->bbRegs->int_vec = 0; /* disable interrupts */ if (bbDebug > 30) logMsg("bitbus rx IRQ on link %d\n", link); semGive(pXvmeLink[link]->rxInt); /* unblock it */ return(0); } /****************************************************************** * FUNCTION: checkLink() * PURPOSE : Checks to make sure driver is initialized to handle link. * ARGS IN : link * ARGS OUT: none * GLOBALS: none ******************************************************************/ static int checkLink(link) int link; { if (link<0 || link>BB_NUM_LINKS) return(ERROR); /* link number out of range */ if (pXvmeLink[link] == NULL) return(ERROR); /* link number has no card installed */ return(OK); } /****************************************************************** * FUNCTION: xvmeTmoHandler() * PURPOSE : Invoked whenever a watchdog timer times out. Watchdogs * are running whenever the busyList has any elements on * it. The idea here is that the watchdog handler scans * through the busyList, 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 the associated node is reset and marked offline. * * ARGS IN : link * ARGS OUT: none * GLOBALS: none ******************************************************************/ STATIC int xvmeTmoHandler(link) int link; { if (bbDebug > 25) logMsg("xvmeTmoHandler(%d): Watch dog interrupt\n", link); semGive(pXvmeLink[link]->watchDogSem); /* unblock Wd task */ return(0); } /****************************************************************** * FUNCTION: xvmeRxTask() * PURPOSE : This function is started as a task during driver init * time. It's purpose is to read messages off the receive * FIFO. After a complete message is read, the soliciting * message's completion routines are started by the use * of semaphores and/or a callbackRequest(). * ARGS IN : link * ARGS OUT: none * GLOBALS: none ******************************************************************/ STATIC int xvmeRxTask(link) int link; { int rxState; /* current state of the receiver */ #define BBRX_HEAD 1 #define BBRX_DATA 2 #define BBRX_RCMD 3 #define BBRX_IGN 4 unsigned char rxHead[7]; /* room for header of current rx msg */ unsigned char *rxMsg; /* where to place next byte (after header) */ int rxTCount; /* byte counter for data in rxHead */ unsigned char ch; struct dpvtBitBusHead *rxDpvtHead; /* for message currently receiving */ int lockKey; /* used for intLock calls */ int packageComplete; /* indicates last byte of package was read */ rxMsg = (unsigned char *) NULL; rxState = BBRX_HEAD; rxTCount = 0; rxDpvtHead = (struct dpvtBitBusHead *) NULL; packageComplete = 0; while (1) { /* Wait for RX interrupts, but only if no chars are ready */ if ((pXvmeLink[link]->bbRegs->stat_ctl & XVME_RFNE) == 0) { /* Enable interrupts and check again in case PB-BIT blew it */ lockKey = intLock(); pXvmeLink[link]->bbRegs->int_vec = PEP_BB_IVEC_BASE +(link*2); intUnlock(lockKey); while (((pXvmeLink[link]->bbRegs->stat_ctl & XVME_RFNE) == 0) && (pXvmeLink[link]->abortFlag == 0)) { /* Re-enable ints here each time in case board got reset */ lockKey = intLock(); pXvmeLink[link]->bbRegs->int_vec = PEP_BB_IVEC_BASE +(link*2); intUnlock(lockKey); semTake(pXvmeLink[link]->rxInt, WAIT_FOREVER); /* wait for message */ } /* Disable RX Interrupts (prevents unnecessary context switching) */ lockKey = intLock(); pXvmeLink[link]->bbRegs->int_vec = 0; intUnlock(lockKey); } if (pXvmeLink[link]->abortFlag == 0) { /* READ ONE CHAR FROM RECEIVE FIFO */ ch = pXvmeLink[link]->bbRegs->data; /* check to see if we got a data byte or a command byte */ if ((pXvmeLink[link]->bbRegs->stat_ctl & XVME_RCMD) == XVME_RCMD) packageComplete = 1; switch (rxState) { case BBRX_HEAD: /* getting the head of a new message */ if (rxTCount > 1) /* Toss the 2 PEP specific header bytes */ rxHead[rxTCount] = ch; if (bbDebug>21) printf("xvmeRxTask(%d): >%02.2X< (Header)\n", link, ch); if (++rxTCount == 7) { /* find the message this is a reply to */ /* rxTCount += 2; PEP header bytes already messed up the count (jrw)*/ /* Lock the busy list */ semTake(pXvmeLink[link]->pbbLink->busyList.sem, WAIT_FOREVER); rxDpvtHead = pXvmeLink[link]->pbbLink->busyList.head; while (rxDpvtHead != NULL) { if (bbDebug>19) printf("xvmeRxTask(%d): checking reply against 0x%08.8X\n", link, rxDpvtHead); /* see if node's match */ if (rxDpvtHead->txMsg.node == rxHead[4]) { /* see if the tasks match */ /* if (rxDpvtHead->txMsg.tasks == rxHead[5]) */ { /* They match, finish putting response into the rxMsg buffer */ if (bbDebug>4) printf("xvmeRxTask(%d): reply to 0x%08.8X\n", link, rxDpvtHead); /* Delete the node from the list */ listDel(&(pXvmeLink[link]->pbbLink->busyList), rxDpvtHead); /* If busy list is empty, stop the dog */ if (pXvmeLink[link]->pbbLink->busyList.head == NULL) wdCancel(pXvmeLink[link]->watchDogId); /* decrement the number of outstanding messages to the node */ (pXvmeLink[link]->pbbLink->deviceStatus[rxDpvtHead->txMsg.node])--; /* Wake up Link Task in case was waiting on "this" node */ semGive(pXvmeLink[link]->pbbLink->linkEventSem); semGive(pXvmeLink[link]->pbbLink->busyList.sem); rxDpvtHead->rxMsg.length = rxHead[2]; rxDpvtHead->rxMsg.route = rxHead[3]; rxDpvtHead->rxMsg.node = rxHead[4]; rxDpvtHead->rxMsg.tasks = rxHead[5]; rxDpvtHead->rxMsg.cmd = rxHead[6]; rxMsg = rxDpvtHead->rxMsg.data; rxDpvtHead->status = BB_OK; /* OK, unless BB_LENGTH */ rxState = BBRX_DATA; /* finish reading till RCMD */ break; /* get out of the while() */ } } rxDpvtHead = rxDpvtHead->next; /* Keep looking */ } if (rxDpvtHead == NULL) { if (bbDebug > 9) { printf("xvmeRxTask(%d): msg from node %d unsolicited!\n", link, rxHead[4]); printf("contents: %02.2x %02.2x %02.2x %02.2x %02.2x\n",rxHead[2], rxHead[3],rxHead[4],rxHead[5],rxHead[6]); } semGive(pXvmeLink[link]->pbbLink->busyList.sem); rxState = BBRX_IGN; /* nothing waiting... toss it */ } } break; case BBRX_DATA: /* finish reading data portion of message */ if (rxTCount >= rxDpvtHead->rxMaxLen) { rxState = BBRX_IGN; /* toss the rest of the data */ rxDpvtHead->status = BB_LENGTH; /* set driver status */ if (bbDebug>22) printf("xvmeRxTask(%d): %02.2X (Ignored)\n", link, ch); } else { *rxMsg = ch; if (bbDebug>22) printf("xvmeRxTask(%d): %02.2X (Data)\n", link, ch); rxMsg++; rxTCount++; } break; case BBRX_IGN: if (bbDebug>22) printf("xvmeRxTask(%d): %02.2X (Ignored)\n", link, ch); break; } if (packageComplete) { if (rxDpvtHead == NULL) { if (bbDebug > 22) printf("xvmeRxTask(%d): got unexpected XVME_RCMD\n", link); } else { rxDpvtHead->status = BB_OK; if (bbDebug>24) printf("xvmeRxTask(%d): RX command byte = %02.2X\n", link, ch); if (rxDpvtHead->finishProc != NULL) { if (bbDebug>8) printf("xvmeRxTask(%d): invoking the callbackRequest\n", link); callbackRequest(rxDpvtHead); /* schedule completion processing */ } else { /* If there is a semaphore for synchronous I/O, unlock it */ if (rxDpvtHead->psyncSem != NULL) semGive(*(rxDpvtHead->psyncSem)); } } /* Reset the state of the RxTask to expect a new message */ rxMsg = (unsigned char *) NULL; rxState = BBRX_HEAD; rxTCount = 0; rxDpvtHead = (struct dpvtBitBusHead *) NULL; packageComplete = 0; } } else { /* Link abort state is active reset receiver link status now */ if (rxDpvtHead != NULL) { /* This xact is not on the busy list, put it back on */ semTake(pXvmeLink[link]->pbbLink->busyList.sem, WAIT_FOREVER); (pXvmeLink[link]->pbbLink->deviceStatus[rxDpvtHead->txMsg.node])++; listAddTail(&(pXvmeLink[link]->pbbLink->busyList), rxDpvtHead); semGive(pXvmeLink[link]->pbbLink->busyList.sem); } rxMsg = (unsigned char *) NULL; rxState = BBRX_HEAD; rxTCount = 0; rxDpvtHead = (struct dpvtBitBusHead *) NULL; /* Tell the watch dog I am ready for the reset (reset in the dog task) */ pXvmeLink[link]->rxAbortAck = 1; /* if (bbDebug) */ printf("xvmeRxTask(%d): resetting due to abort status\n", link); /* wait for link state to become active again */ while (pXvmeLink[link]->abortFlag != 0) taskDelay(RESET_POLL_TIME); /* if bbDebug) */ printf("xvmeRxTask(%d): restarting after abort\n", link); } } } /****************************************************************** * FUNCTION: bbReset() * PURPOSE : Induces a link reset by setting nukeEm flag and * unblocking the watchDog task. * * ARGS IN : link * ARGS OUT: none * GLOBALS: nukeEm set and Wd unblocked ******************************************************************/ int bbReset(link) int link; { if (checkLink(link) != ERROR) { pXvmeLink[link]->pbbLink->nukeEm = 1; semGive(pXvmeLink[link]->watchDogSem); } else printf("Link %d not installed.\n", link); return(0); } /******************************************************************* * * Same as bbReset() but it takes the link down and leaves it down. * *******************************************************************/ int bbKill(int link) { if (checkLink(link) != ERROR) { pXvmeLink[link]->pbbLink->nukeEm = 2; semGive(pXvmeLink[link]->watchDogSem); } else printf("Link %d not installed.\n", link); return(0); } /****************************************************************** * FUNCTION: xvmeWdTask() * PURPOSE : * * ARGS IN : none * ARGS OUT: none * GLOBALS: none ******************************************************************/ STATIC int xvmeWdTask(link) int link; { struct bbLink *plink; struct dpvtBitBusHead *pnode; struct dpvtBitBusHead *npnode; unsigned long now; SEM_ID syncSem; struct dpvtBitBusHead resetNode; unsigned char resetNodeData; /* 1-byte data field for RAC_RESET */ unsigned char responseData; struct dpvtBitBusHead offlnNode; unsigned char offlnNodeData; /* 1-byte data field for RAC_OFFLN */ unsigned char response1Data; /* 1-byte response data field */ /* init the SEM used when sending the RAC_OFFLN message */ syncSem = semBCreate(SEM_Q_PRIORITY,SEM_EMPTY); /* * Hand-craft a RAC_RESET and RAC_OFFLINE message to use when * a message times out. * NOTE that having only one copy is OK provided that the dog waits for * a response before sending it again! */ resetNode.finishProc = NULL; /* no callback routine used */ resetNode.psyncSem = NULL; resetNode.link = link; /* which bitbus link to send message out on */ resetNode.rxMaxLen = 10; resetNode.ageLimit = sysClkRateGet()*100; /* make sure this never times out */ resetNode.txMsg.length = 8; resetNode.txMsg.route = BB_STANDARD_TX_ROUTE; resetNode.txMsg.tasks = 0x0; resetNode.txMsg.data = &resetNodeData; resetNode.rxMsg.data = &responseData; offlnNode.finishProc = NULL; /* no callback routine used */ offlnNode.psyncSem = &syncSem;/* do a semGive on this SEM when responded to*/ offlnNode.link = link; /* which bitbus link to send message out on */ offlnNode.rxMaxLen = 10; offlnNode.ageLimit = sysClkRateGet()*100; /* make sure this never times out */ offlnNode.txMsg.length = 8; offlnNode.txMsg.route = BB_STANDARD_TX_ROUTE; offlnNode.txMsg.tasks = 0x0; offlnNode.txMsg.data = &offlnNodeData; offlnNode.rxMsg.data = &response1Data; plink = pXvmeLink[link]->pbbLink; plink->nukeEm = 0; /* Make sure the nuke status is clear */ while(1) { /* SLEEP UNTIL WATCHDOG TIMER ROUTINE GIVES SEMAPHORE */ semTake(pXvmeLink[link]->watchDogSem, WAIT_FOREVER); now = tickGet(); /* what time is it? */ if (plink->nukeEm != 0) printf("Bitbus manual reset being issued on link %d\n", link); if (bbDebug>4) printf("xvmeWdTask(%d): (Watchdog) checking busy list\n", link); pXvmeLink[link]->rxAbortAck = 0; pXvmeLink[link]->txAbortAck = 0; if (plink->nukeEm != 0) { /* set abort status and wait for the abort acks */ pXvmeLink[link]->abortFlag = 1; /* wake up the Tx task so it can observe the abort status */ semGive(plink->linkEventSem); /* wake up the Rx task so it can observe the abort ststus */ semGive(pXvmeLink[link]->rxInt); /* sleep until abort ack from Tx & Rx tasks */ while ((pXvmeLink[link]->rxAbortAck == 0) && (pXvmeLink[link]->txAbortAck == 0)) taskDelay(RESET_POLL_TIME); } /* * Run thru entire busy list to see if there are any transactions * that have been waiting on a response for too long a period. */ semTake(plink->busyList.sem, WAIT_FOREVER); pnode = plink->busyList.head; while (pnode != NULL) { npnode = pnode->next; /* remember where we were in the list */ if ((plink->nukeEm != 0) || (pnode->retire <= now)) { /* Get rid of the request and set error status etc... */ listDel(&(plink->busyList), pnode); printf("xvmeWdTask(%d): TIMEOUT on xact 0x%08.8X\n", link, pnode); (plink->deviceStatus[pnode->txMsg.node])--; /* fix device status */ pnode->status = BB_TIMEOUT; /* Gotta do this now in case we need the info after the completion */ resetNode.txMsg.node = pnode->txMsg.node; offlnNodeData = pnode->txMsg.node; /* mark the node number */ /* Make the callbackRequest if one was spec'd */ if(pnode->finishProc != NULL) { if (bbDebug>2) { printf("xvmeWdTask(%d): invoking the callbackRequest %8.8x %d\n", link, pnode->finishProc, pnode->priority); } callbackRequest(pnode); /* schedule completion processing */ } else { /* Release a completion lock if one was spec'd */ if (pnode->psyncSem != NULL && plink->nukeEm == 0) semGive(*(pnode->psyncSem)); } /* If we are not going to reboot the link... */ if ( plink->nukeEm == 0 ) { /* Send out a RAC_RESET/RAC_OFFLINE pair */ semGive(plink->busyList.sem); /* Configure message for a RAC_RESET */ resetNode.txMsg.cmd = 0x00; /* Configure message for a RAC_OFFLINE */ offlnNode.txMsg.cmd = RAC_OFFLINE; offlnNode.txMsg.node = 0xff; /* Queue the messages (high priority, reset first) */ semTake(plink->queue[BB_Q_HIGH].sem, WAIT_FOREVER); listAddHead(&(plink->queue[BB_Q_HIGH]), &offlnNode); /* listAddHead(&(plink->queue[BB_Q_HIGH]), &resetNode); */ semGive(plink->queue[BB_Q_HIGH].sem); semGive(plink->linkEventSem); /* Tell TxTask to send the messages */ if (semTake(syncSem, (sysClkRateGet()) ) == ERROR) { printf("xvmeWdTask(%d): link dead, trying manual reboot\n", link); plink->nukeEm = 1; pXvmeLink[link]->abortFlag = 1; semGive(plink->linkEventSem); semGive(pXvmeLink[link]->rxInt); /* sleep until abort ack from Tx & Rx tasks */ while ((pXvmeLink[link]->rxAbortAck == 0) && (pXvmeLink[link]->txAbortAck == 0)) taskDelay(RESET_POLL_TIME); } /* Start over since released the busy list */ semTake(plink->busyList.sem, WAIT_FOREVER); npnode = plink->busyList.head; } } pnode = npnode; } /* Finish the link reboot if necessary */ if (plink->nukeEm != 0) { /* shut down the bitbus card. */ xvmeReset(pXvmeLink[link]->bbRegs, link); if (plink->nukeEm == 2) { /* * Stop the watchdog task so the link stays dead. * Since the busy list is locked and the nukeEm flag is still set, * this link can not do any more work. */ exit(); } /* clear the abort_flag */ pXvmeLink[link]->abortFlag = 0; plink->nukeEm = 0; } if (plink->busyList.head != NULL) { /* Restart the dog timer */ if (bbDebug>5) printf("xvmeWdTask(%d): restarting watch dog timer\n", link); wdStart(pXvmeLink[link]->watchDogId, plink->busyList.head->retire - now, xvmeTmoHandler, link); } semGive(plink->busyList.sem); } } /****************************************************************** * FUNCTION: xvmeTxTask() * PURPOSE : This function is started as a task during driver init * time. It's purpose is to keep the link busy with * outgoing messages. It awaits user-calls to qBBReq() * (new message to send). It also awaits wake up calls * from the Wd timer (a timed out message to a node, * once handled, means Tx is free to send again to that * node). * * ARGS IN : link * ARGS OUT: none * GLOBALS: none ******************************************************************/ STATIC int xvmeTxTask(link) int link; { struct bbLink *plink; struct dpvtBitBusHead *pnode; struct dpvtBitBusHead *npnode; int prio; int working; int dogStart; int stuck; int txTCount; int txCCount; unsigned char *txMsg; register int x; unsigned long now; if (bbDebug) printf("xvmeTxTask started for link %d\n", link); plink = pXvmeLink[link]->pbbLink; while(1) { if (pXvmeLink[link]->abortFlag != 0) { /* let the dog know we are ready */ pXvmeLink[link]->txAbortAck = 1; printf("xvmeTxTask(%d): resetting due to abort status\n", link); while (pXvmeLink[link]->abortFlag != 0) taskDelay(RESET_POLL_TIME); /* wait for link to reset */ printf("xvmeTxTask(%d): restarting after abort\n", link); } else semTake(plink->linkEventSem, WAIT_FOREVER); if (bbDebug>5) printf("xvmeTxTask(%d): got an event\n", link); working = 1; while ((working != 0) && (pXvmeLink[link]->abortFlag == 0)) { working = 0; prio = BB_NUM_PRIO-1; while ((prio >= 0) && (pXvmeLink[link]->abortFlag == 0)) { /* see if the queue has anything in it */ semTake(plink->queue[prio].sem, WAIT_FOREVER); if ((pnode = plink->queue[prio].head) != NULL) { while (plink->deviceStatus[pnode->txMsg.node] == BB_BUSY) { if ((pnode = pnode->next) == NULL) break; } } if (pnode != NULL) { /* have an xact to start processing */ working = 1; /* delete the node from the inbound fifo queue */ listDel(&(plink->queue[prio]), pnode); semGive(plink->queue[prio].sem); if (bbDebug>3) printf("xvmeTxTask(%d): got xact, pnode=0x%08.8X\n", link, pnode); /* Send the message in polled mode */ txTCount = pnode->txMsg.length; txCCount = 0; txMsg = &(pnode->txMsg.length); while ((txCCount < txTCount) && (pXvmeLink[link]->abortFlag == 0)) { stuck = 1000; while (((pXvmeLink[link]->bbRegs->stat_ctl & XVME_TFNF) != XVME_TFNF) && (pXvmeLink[link]->abortFlag == 0) && --stuck) for(x=0;x<100;x++); /* wait for TX ready */ if (!stuck) txStuck(link); else if (txCCount == 0) { /* first byte is package type */ if (bbDebug>30) printf("xvmeTxTask(%d): outputting %02.2X\n",link,0x81); pXvmeLink[link]->bbRegs->data = 0x81; } else if (txCCount == 1) { /* unused 2nd byte of package */ if (bbDebug>30) printf("xvmeTxTask(%d): outputting %02.2X\n",link,0x00); pXvmeLink[link]->bbRegs->data = 0x00; } else if (txCCount == (txTCount -1)) { /* last byte of package */ pXvmeLink[link]->bbRegs->stat_ctl = *txMsg; if (bbDebug>30) printf("xvmeTxTask(%d): outputting last byte %02.2X\n", link,*txMsg); } else { /* regular ol' message byte */ pXvmeLink[link]->bbRegs->data = *txMsg; if (bbDebug>30) printf("xvmeTxTask(%d): outputting %02.2X\n",link,*txMsg); if (txCCount != 6) txMsg++; else txMsg = pnode->txMsg.data; } txCCount++; } if (pXvmeLink[link]->abortFlag == 0) { /* don't add to busy list if was a RAC_RESET_SLAVE */ if ((pnode->txMsg.cmd == RAC_RESET_SLAVE) && (pnode->txMsg.tasks == 0)) { printf("xvmeTxTask(%d): RAC_RESET_SLAVE sent\n", link); pnode->status = BB_OK; if (pnode->finishProc != NULL) { if (bbDebug>4) printf("xvmeTxTask(%d): invoking the callbackRequest\n", link); callbackRequest(pnode); /* schedule completion processing */ } else { /* If there is a semaphore for synchronous I/O, unlock it */ if (pnode->psyncSem != NULL) semGive(*(pnode->psyncSem)); } taskDelay(15); } else { /* Lock the busy list */ semTake(plink->busyList.sem, WAIT_FOREVER); /* set the retire time */ pnode->retire = tickGet(); if (pnode->ageLimit) pnode->retire += pnode->ageLimit; else pnode->retire += 5*sysClkRateGet(); if (plink->busyList.head == NULL) dogStart = 1; else dogStart = 0; /* Add pnode to the busy list */ listAddTail(&(plink->busyList), pnode); /* Count the outstanding messages */ (plink->deviceStatus[pnode->txMsg.node])++; semGive(plink->busyList.sem); /* If something just added to empty busy list, start the dog */ if (dogStart) { now = tickGet(); wdStart(pXvmeLink[link]->watchDogId, plink->busyList.head->retire - now, xvmeTmoHandler, link); } } } else { /* if abortFlag != 0 */ /* Aborted transmission operation, re-queue the message */ semTake(plink->queue[BB_Q_HIGH].sem, WAIT_FOREVER); listAddHead(&(plink->queue[BB_Q_HIGH]), pnode); semGive(plink->queue[BB_Q_HIGH].sem); } } else { /* if pnode == NULL */ /* we have no xacts that can be processed at this time */ semGive(plink->queue[prio].sem); } prio--; /* look at the next prio queue */ } } } } /****************************************************************** * FUNCTION: txStuck() * PURPOSE : Invoked by Tx task when PB-BIT xmit FIFO won't * accept bytes after a reasonable time. * ARGS IN : link * ARGS OUT: none * GLOBALS: none ******************************************************************/ STATIC int txStuck(link) int link; { printf("xvmeTxTask(%d): transmitter stuck, resetting link\n",link); bbReset(link); while(pXvmeLink[link]->abortFlag == 0) /* wait until reset complete */ taskDelay(1); return(OK); } /****************************************************************** * FUNCTION: qBBReq() * PURPOSE : This function is called by user programs to queue an io * request for the BB driver. It is the only user-callable * function provided in this driver. * ARGS IN : pdpvt ptr to bitbus message struct in record dpvt area * prio requested priority of message * ARGS OUT: none * GLOBALS: none ******************************************************************/ STATIC long qBBReq(pdpvt, prio) struct dpvtBitBusHead *pdpvt; int prio; { char message[200]; static linkErrFlags[BB_NUM_LINKS]; /* Supposedly init'd to zero */ if ((prio < 0) || (prio >= BB_NUM_PRIO)) { sprintf(message, "invalid priority requested in call to qbbreq(%08.8X, %d)\n", pdpvt, prio); errMessage(S_BB_badPrio, message); return(ERROR); } if (checkLink(pdpvt->link) == ERROR) { if (pdpvt->link >= BB_NUM_LINKS) { sprintf(message, "qbbreq(%08.8X, %d) %d\n", pdpvt, prio, pdpvt->link); errMessage(S_BB_badlink, message); } else if (linkErrFlags[pdpvt->link] == 0) { /* Anti-message swamping check */ linkErrFlags[pdpvt->link] = 1; sprintf(message, "qbbreq(%08.8X, %d) %d... card not present\n", pdpvt, prio, pdpvt->link); errMessage(S_BB_badlink, message); } return(ERROR); } semTake(pXvmeLink[pdpvt->link]->pbbLink->queue[prio].sem, WAIT_FOREVER); /* Add to the end of the queue of waiting transactions */ listAddTail(&(pXvmeLink[pdpvt->link]->pbbLink->queue[prio]), pdpvt); semGive(pXvmeLink[pdpvt->link]->pbbLink->queue[prio].sem); semGive(pXvmeLink[pdpvt->link]->pbbLink->linkEventSem); if (bbDebug>5) printf("qbbreq(0x%08.8X, %d): transaction queued\n", pdpvt, prio); return(OK); } /****************************************************************** * FUNCTION: drvBitBusDumpMsg() * PURPOSE : Print out bitbus message structure. * ARGS IN : none * ARGS OUT: none * GLOBALS: none ******************************************************************/ int drvBitBusDumpMsg(pbbMsg) struct bitBusMsg *pbbMsg; { char ascBuf[15]; /* for ascii xlation part of the dump */ int x; int y; int z; printf("Link 0x%04.4X, length 0x%02.2X, route 0x%02.2X, node 0x%02.2X, tasks 0x%02.2X, cmd %02.2X\n", pbbMsg->link, pbbMsg->length, pbbMsg->route, pbbMsg->node, pbbMsg->tasks, pbbMsg->cmd); x = BB_MSG_HEADER_SIZE; y = pbbMsg->length; z = 0; while (x < y) { printf("%02.2X ", pbbMsg->data[z]); ascBuf[z] = pbbMsg->data[z]; if (!((ascBuf[z] >= 0x20) && (ascBuf[z] <= 0x7e))) ascBuf[z] = '.'; x++; z++; } while (x < BB_MAX_MSG_LENGTH) { printf(" "); x++; } ascBuf[z] = '\0'; printf(" *%s*\n", ascBuf); return(OK); } /****************************************************************** * FUNCTION: dumpStat() * PURPOSE : Temporary function. Shows contents of status * register on PB-BIT module corresponding to link. * ARGS IN : link * ARGS OUT: none * GLOBALS: none ******************************************************************/ int dumpStat(link) int link; { unsigned char stat_ctl; stat_ctl = pXvmeLink[link]->bbRegs->stat_ctl; printf("stat_ctl reg: %02x\n",stat_ctl); return(OK); }