#define DO_RESET_AND_OFFLINE /* 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 * .02 12-02-91 jrw Changed priority info to arrays * .03 12-16-91 jrw Made the data portion of the message a pointer * .04 01-21-91 jrw moved the task parameters into task_params.h * .05 02-12-92 jrw removed IRQ based transmission. * .06 04-08-92 jrw moved the device configs into module_types.h * .07 09-28-92 jrw upped the reset delay time to 1/2 second * .08 08-02-93 mrk Added call to taskwdInsert * * NOTES: * This driver currently needs work on error message generation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "drvBitBus.h" static long reportBB(), initBB(), qBBReq(); static int xvmeTmoHandler(), xvmeRxTask(), xvmeTxTask(), xvmeWdTask(); static int xvmeIrqRdav(), xvmeIrqRcmd(); int BB_MAX_OUTSTANDING_MSGS = 4; 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 */ /****************************************************************************** * * This function prints a message indicating the presence of each BB * card found in the system. * ******************************************************************************/ 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); printf(" %d mesages on busy list\n", pXvmeLink[i]->pbbLink->busyList.elements); } else { printf("Link %d not installed.\n", i); } } } else { printf("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; char *nameTemp[50]; int taskId; if (init_called) { printf("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); if (bbDebug) { printf("BB driver package initializing\n"); printf("short_base 0x%08.8X\n", short_base); printf("BB_SHORT_OFF 0x%08.8X\n", BB_SHORT_OFF); printf("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; if (bbDebug) printf("Probing of address 0x%08.8X failed\n", pXvmeRegs); } else { /* BB board found... reserve space for structures */ #if FALSE xvmeReset(pXvmeRegs, i); /* finish resetting the xvme module */ #endif 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 */ pXvmeLink[i]->pbbLink->linkEventSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); 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); pXvmeLink[i]->pbbLink->queue[j].elements = 0; } /* 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; pXvmeLink[i]->pbbLink->busyList.elements = 0; for (j=0; jpbbLink->deviceStatus[j] = BB_IDLE; } pXvmeLink[i]->bbRegs = pXvmeRegs; pXvmeLink[i]->watchDogId = wdCreate(); 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 resetting the xvme module */ /* attach the interrupt handler routines */ intConnect((BB_IVEC_BASE + 1 + (i*4)) * 4, xvmeIrqRcmd, i); intConnect((BB_IVEC_BASE + 3 + (i*4)) * 4, xvmeIrqRdav, i); /* start a task to manage the TX link */ sprintf(nameTemp, "%s%d", BBTXLINK_NAME, i); if ((taskId=taskSpawn(nameTemp, BBTXLINK_PRI, BBTXLINK_OPT, BBTXLINK_STACK, xvmeTxTask, i)) == ERROR) { printf("initBB: failed to start TX link task for link %d\n", i); } taskwdInsert(taskId,NULL,NULL); /* start a task to manage the RX link */ sprintf(nameTemp, "%s%d", BBRXLINK_NAME, i); if ((taskId=taskSpawn(nameTemp, BBRXLINK_PRI, BBRXLINK_OPT, BBRXLINK_STACK, xvmeRxTask, i)) == ERROR) { printf("initBB: failed to start RX link task for link %d\n", i); } taskwdInsert(taskId,NULL,NULL); /* start a task to keep an eye on the busy list */ sprintf(nameTemp, "%s%d", BBWDTASK_NAME, i); if ((taskId=taskSpawn(nameTemp, BBWDTASK_PRI, BBWDTASK_OPT, BBWDTASK_STACK, xvmeWdTask, i)) == ERROR) { printf("initBB: failed to start watchdog task for link %d\n", i); } taskwdInsert(taskId,NULL,NULL); } pXvmeRegs++; /* ready for next board window */ } sysIntEnable(BB_IRQ_LEVEL); init_called = 1; /* let reportBB() know init occured */ 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; int lockKey; if (bbDebug) printf("xvmeReset(%08.8X, %d): Resetting xvme module\n", xvmeRegs, link); xvmeRegs->fifo_stat = XVME_RESET; /* assert the reset pulse */ taskDelay(2); xvmeRegs->fifo_stat = 0; /* clear the reset pulse */ taskDelay(30); /* 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) { printf("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) { printf("xvmeReset(%d): Data buffer will not clear after reset!\n", link); return(ERROR); } if ((xvmeRegs->fifo_stat & XVME_FSVALID) != XVME_FSIDLE) { printf("xvmeReset(%d): XVME board not returning to idle status after reset!\n", link); return(ERROR); } /* set the interrupt vector */ lockKey = intLock(); xvmeRegs->stat_ctl = 0; /* disable all interupts */ xvmeRegs->int_vec = BB_IVEC_BASE + (link*4);/* set the int vector */ intUnlock(lockKey); semGive(pXvmeLink[link]->rxInt); /* Tell xvmeRxTask to Re-enable interrupts */ 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; plist->elements++; 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 */ plist->elements++; 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; plist->elements--; return(0); } /****************************************************************************** * * Interrupt handler that is called when the receiver has a byte that should be * read. * ******************************************************************************/ static int xvmeIrqRdav(link) int link; { if (bbDebug > 30) logMsg("bitbus rx IRQ on link %d\n", link); semGive(pXvmeLink[link]->rxInt); /* deliver the groceries */ return(0); } /****************************************************************************** * * This interrupt handler is invoked when the BitBus controller has completed * the transfer of a RECEIVED message. * ******************************************************************************/ static int xvmeIrqRcmd(link) int link; { if (bbDebug > 30) logMsg("bitbus rcmd IRQ on link %d\n", link); semGive(pXvmeLink[link]->rxInt); /* deliver the groceries */ return(0); } /****************************************************************************** * * Given a link number, make sure it is valid. * ******************************************************************************/ 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); } /****************************************************************************** * * 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. * ******************************************************************************/ static int xvmeTmoHandler(link) int link; { if (bbDebug > 25) logMsg("xvmeTmoHandler(%d): Watch dog interrupt\n", link); semGive(pXvmeLink[link]->watchDogSem); return(0); } /****************************************************************************** * * This function is started as a task during driver init time. It's purpose * is to read messages that are received from the bitbus link. After a message * is received, the soliciting message's completion routines are started by * the use of semaphores and/or a callbackRequest(). * ******************************************************************************/ 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[5]; /* 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 */ rxMsg = (unsigned char *) NULL; rxState = BBRX_HEAD; rxTCount = 0; rxDpvtHead = (struct dpvtBitBusHead *) NULL; while (1) { /* Wait for RX interrupts, but only if no chars are ready */ if ((pXvmeLink[link]->bbRegs->fifo_stat & XVME_RFNE) == 0) { /* Enable interrupts and check again because xycom blew it */ lockKey = intLock(); pXvmeLink[link]->bbRegs->stat_ctl = XVME_ENABLE_INT | XVME_RX_INT; intUnlock(lockKey); while (((pXvmeLink[link]->bbRegs->fifo_stat & XVME_RFNE) == 0) && (pXvmeLink[link]->abortFlag == 0)) { /* Re-enable ints here each time in case board got reset */ lockKey = intLock(); pXvmeLink[link]->bbRegs->stat_ctl = XVME_ENABLE_INT | XVME_RX_INT; intUnlock(lockKey); semTake(pXvmeLink[link]->rxInt, WAIT_FOREVER); /* wait for groceries */ } /* Disable RX Interrupts (prevents unnecessary context switching) */ lockKey = intLock(); pXvmeLink[link]->bbRegs->stat_ctl = 0; intUnlock(lockKey); } if (pXvmeLink[link]->abortFlag == 0) { /* check to see if we got a data byte or a command byte */ if ((pXvmeLink[link]->bbRegs->fifo_stat & XVME_RCMD) == XVME_RCMD) rxState = BBRX_RCMD; switch (rxState) { case BBRX_HEAD: /* getting the head of a new message */ rxHead[rxTCount] = pXvmeLink[link]->bbRegs->data; if (bbDebug>21) printf("xvmeRxTask(%d): >%02.2X< (Header)\n", link, rxHead[rxTCount]); if (++rxTCount == 5) { /* find the message this is a reply to */ rxTCount += 2; /* adjust for the link field space */ /* 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[2]) { /* see if the tasks match */ if (rxDpvtHead->txMsg.tasks == rxHead[3]) { /* 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 */ /* BUG -- this may require moving into the BB_RCMD state */ semGive(pXvmeLink[link]->pbbLink->linkEventSem); semGive(pXvmeLink[link]->pbbLink->busyList.sem); rxDpvtHead->rxMsg.length = rxHead[0]; rxDpvtHead->rxMsg.route = rxHead[1]; rxDpvtHead->rxMsg.node = rxHead[2]; rxDpvtHead->rxMsg.tasks = rxHead[3]; rxDpvtHead->rxMsg.cmd = rxHead[4]; 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) { printf("xvmeRxTask(%d): msg from node %d unsolicited!\nHeader:", link, rxHead[2]); rxDpvtHead->rxMsg.length = 7; /* we just have the header now */ drvBitBusDumpMsg(&(rxDpvtHead->rxMsg)); } semGive(pXvmeLink[link]->pbbLink->busyList.sem); rxState = BBRX_IGN; /* nothing waiting... toss it */ } } break; case BBRX_DATA: /* finish reading data portion of message */ ch = pXvmeLink[link]->bbRegs->data; 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: ch = pXvmeLink[link]->bbRegs->data; if (bbDebug>22) printf("xvmeRxTask(%d): %02.2X (Ignored)\n", link, ch); break; case BBRX_RCMD: if (rxDpvtHead == NULL) { ch = pXvmeLink[link]->bbRegs->cmnd; printf("xvmeRxTask(%d): got unexpected XVME_RCMD\n", link); } else { rxDpvtHead->status = BB_OK; rxDpvtHead->rxCmd = pXvmeLink[link]->bbRegs->cmnd; if (bbDebug>24) printf("xvmeRxTask(%d):RX command byte = %02.2X\n", link, rxDpvtHead->rxCmd); 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; break; } } 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); } } } /****************************************************************************** * * A user callable link resetter. This sets a flag and releases the dog * task to reset the link. * ******************************************************************************/ 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); } /****************************************************************************** * ******************************************************************************/ static int xvmeWdTask(link) int link; { struct bbLink *plink; /* a reference to the link structures covered */ struct dpvtBitBusHead *pnode; struct dpvtBitBusHead *npnode; unsigned long now; SEM_ID syncSem; #ifdef DO_RESET_AND_OFFLINE struct dpvtBitBusHead resetNode; unsigned char resetNodeData; /* 1-byte data field for RAC_OFFLINE */ #endif /* init the SEM used when sending the reset message */ syncSem = semBCreate(SEM_EMPTY, SEM_Q_PRIORITY); #ifdef DO_RESET_AND_OFFLINE /* * Hand-craft a 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 = &syncSem;/* do a semGive on this SEM when done sending */ resetNode.link = link; /* which bitbus link to send message out on */ resetNode.rxMaxLen = 7; /* Chop off the response... we don't care */ resetNode.ageLimit = sysClkRateGet()*100; /* make sure this never times out */ resetNode.txMsg.length = 8; resetNode.txMsg.route = BB_STANDARD_TX_ROUTE; resetNode.txMsg.node = 0xff; resetNode.txMsg.tasks = 0x0; resetNode.txMsg.cmd = RAC_OFFLINE; resetNode.txMsg.data = &resetNodeData; #endif plink = pXvmeLink[link]->pbbLink; plink->nukeEm = 0; /* Make sure the nuke status is clear */ while(1) { 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; /* In case we need to use them */ 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); /*if (bbDebug)*/ { printf("xvmeWdTask(%d): TIMEOUT on xact 0x%08.8X\n", link, pnode); drvBitBusDumpMsg(&pnode->txMsg); } (plink->deviceStatus[pnode->txMsg.node])--; /* fix device status */ pnode->status = BB_TIMEOUT; #ifdef DO_RESET_AND_OFFLINE /* Do now in case we need it later */ resetNodeData = pnode->txMsg.node; /* mark the node number */ #endif /* Make the callbackRequest if one was spec'd */ if(pnode->finishProc != NULL) { callbackRequest(pnode); /* schedule completion processing */ } else { /* Release a completion lock if one was spec'd */ if (pnode->psyncSem != NULL) semGive(*(pnode->psyncSem)); } #ifdef DO_RESET_AND_OFFLINE /* If we are not going to reboot the link... */ if (plink->nukeEm == 0) { /* Send out a RAC_NODE_OFFLINE to the controller */ semGive(plink->busyList.sem); printf("issuing a node offline for link %d node %d\n", link, resetNodeData); semTake(plink->queue[BB_Q_HIGH].sem, WAIT_FOREVER); listAddHead(&(plink->queue[BB_Q_HIGH]), &resetNode); semGive(plink->queue[BB_Q_HIGH].sem); semGive(plink->linkEventSem); /* Tell TxTask to send the message */ if (semTake(syncSem, sysClkRateGet()/4) == ERROR) { printf("xvmeWdTask(%d): link dead, trying manual reboot\n", link); plink->nukeEm = 1; pXvmeLink[link]->abortFlag = 1; /* Start the abort sequence */ semGive(plink->linkEventSem); /* Let Tx task observe abort status */ semGive(pXvmeLink[link]->rxInt); /* Let Rx task observe abort ststus */ /* 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; } #else semGive(plink->linkEventSem); #endif } pnode = npnode; } 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); /* Finish the link reboot if necessary */ if (plink->nukeEm != 0) { xvmeReset(pXvmeLink[link]->bbRegs, link); /* clear the abort_flag */ pXvmeLink[link]->abortFlag = 0; plink->nukeEm = 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 xvmeTxTask(link) int link; { struct bbLink *plink; /* a reference to the link structures covered */ 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; SEM_ID resetNodeSem; struct dpvtBitBusHead resetNode; unsigned char resetNodeData; /* 1-byte data field for RAC_OFFLINE */ if (bbDebug) printf("xvmeTxTask started for link %d\n", link); /* hand-craft a RAC_OFFLINE message for use with RAC_RESET_SLAVE commands */ /* NOTE that having only one copy is OK provided that this message is */ /* sent immediately following the RAC_RESET_SLAVE message. */ resetNodeSem = semBCreate(SEM_Q_PRIORITY, SEM_FULL); resetNode.finishProc = NULL; resetNode.psyncSem = &resetNodeSem; resetNode.link = link; resetNode.rxMaxLen = 7; /* Chop it off, we don't care */ resetNode.ageLimit = 10; resetNode.txMsg.length = 8; resetNode.txMsg.route = BB_STANDARD_TX_ROUTE; resetNode.txMsg.node = 0xff; resetNode.txMsg.tasks = 0x0; resetNode.txMsg.cmd = RAC_OFFLINE; resetNode.txMsg.data = &resetNodeData; plink = pXvmeLink[link]->pbbLink; while(1) { if (pXvmeLink[link]->abortFlag != 0) { pXvmeLink[link]->txAbortAck = 1; /* let the dog know we are ready */ /* if (bbDebug) */ printf("xvmeTxTask(%d): resetting due to abort status\n", link); while (pXvmeLink[link]->abortFlag != 0) taskDelay(RESET_POLL_TIME); /* wait for link to reset */ /* if (bbDebug) */ 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) && (plink->busyList.elements < BB_MAX_OUTSTANDING_MSGS)) { 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; /* indicate work being done */ /* 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 - 2; txCCount = 0; txMsg = &(pnode->txMsg.length); while ((txCCount <= txTCount) && (pXvmeLink[link]->abortFlag == 0)) { stuck = 1000; while (((pXvmeLink[link]->bbRegs->fifo_stat & XVME_TFNF) != XVME_TFNF) && (pXvmeLink[link]->abortFlag == 0) && --stuck) for(x=0;x<100;x++); /* wait for TX ready */ if (!stuck) txStuck(link); /* will end up setting abortFlag */ else if (txCCount < txTCount) /* on last time, just wait, no data */ { pXvmeLink[link]->bbRegs->data = *txMsg; /* send next byte */ if (bbDebug>30) printf("xvmeTxTask(%d): outputting %02.2X\n", link, *txMsg); /* On 5th byte, we are dun w/header, set start w/data buffer */ if (txCCount != 4) txMsg++; else txMsg = pnode->txMsg.data; } txCCount++; } if (pXvmeLink[link]->abortFlag == 0) { /* All data bytes have been sent, put on busy list and release */ /* Don't add to busy list if was a RAC_RESET_SLAVE */ if (pnode->txMsg.cmd != RAC_RESET_SLAVE) { /* 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 just added something to an empty busy list, start the dog */ if (dogStart) { now = tickGet(); wdStart(pXvmeLink[link]->watchDogId, plink->busyList.head->retire - now, xvmeTmoHandler, link); } } else { /* Finish the transaction here if was a RAC_RESET_SLAVE */ /* if (bbDebug) */ printf("xvmeTxTask(%d): RAC_RESET_SLAVE sent, resetting node %d\n", link, pnode->txMsg.node); pnode->status = BB_OK; if (pnode->finishProc != NULL) { if (bbDebug>4) printf("xvmeTxTask(%d): invoking 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)); } /* Wait for last NODE_OFFLINE to finish (if still pending) */ semTake(resetNodeSem, WAIT_FOREVER); /* have to reset the master so it won't wait on a response */ resetNodeData = pnode->txMsg.node; /* mark the node number */ semTake(plink->queue[BB_Q_HIGH].sem, WAIT_FOREVER); listAddHead(&(plink->queue[BB_Q_HIGH]), &resetNode); semGive(plink->queue[BB_Q_HIGH].sem); /* -- BUG -- I don't really need this */ /* taskDelay(15); */ /* wait while bug is resetting */ } /* Tell the 8044 to fire out the message now */ pXvmeLink[link]->bbRegs->cmnd = BB_SEND_CMD; /* forward it now */ } else { /* 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); printf("Message in progress when abort issued:\n"); drvBitBusDumpMsg(&pnode->txMsg); } /* BUG -- I don't really need this */ /* break;*/ /* stop checking the fifo queues */ } else { /* we have no xacts that can be processed at this time */ semGive(plink->queue[prio].sem); } prio--; /* look at the next prio queue */ } } } } /****************************************************************************** * * This gets called by the transmit task if it gets stuck waiting to send a * byte to the transmit fifo. * ******************************************************************************/ static int txStuck(link) int link; { /* if (bbDebug) */ printf("xvmeTxTask(%d): transmitter stuck, resetting link\n", link); bbReset(link); while (pXvmeLink[link]->abortFlag == 0) taskDelay(1); return(OK); } /****************************************************************************** * * 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(pdpvt, prio) struct dpvtBitBusHead *pdpvt; int prio; { char message[100]; 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) { sprintf(message, "invalid link requested in call to qbbreq(%08.8X, %d)\n", pdpvt, prio); errMessage(S_BB_rfu1, message); return(ERROR); } if (bbDebug>5) printf("qbbreq(0x%08.8X, %d): transaction queued\n", pdpvt, prio); if (bbDebug>6) drvBitBusDumpMsg(&(pdpvt->txMsg)); 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); return(OK); } /****************************************************************************** * * This is used for debugging purposes. * ******************************************************************************/ 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); }