Files
pcas/src/drv/drvPEPBitBus.c
1994-06-22 15:35:59 +00:00

1301 lines
39 KiB
C

/* 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 <vxWorks.h>
#include <types.h>
#include <iosLib.h>
#include <taskLib.h>
#include <memLib.h>
#include <semLib.h>
#include <rngLib.h>
#include <wdLib.h>
#include <wdLib.h>
#include <tickLib.h>
#include <vme.h>
#include <vxLib.h>
#include <task_params.h>
#include <module_types.h>
#include <drvSup.h>
#include <dbDefs.h>
#include <link.h>
#include <callback.h>
#include <iv.h>
#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; i<BB_NUM_LINKS; i++)
{
if (checkLink(i) == OK)
bbKill(i);
}
taskDelay(sysClkRateGet()); /* Give it a second in case prints something */
return;
}
/******************************************************************
* FUNCTION: initBB()
* PURPOSE : Called by iocInit processing. Initializes PB-BIT
* hardware for each board present on bus. Although
* interrupts are disabled in xvmeReset, when the
* xvmeRxTask for that link is started, interrupts
* are enabled. Data structures are malloc'ed and
* the Rx/Tx/Wd tasks are started for each BB link
* present.
* ARGS IN : none
* ARGS OUT: none
* GLOBALS: pXvmeLink[i] updated for each link i present
******************************************************************/
STATIC long
initBB()
{
int i;
int j;
int probeValue; /* dummy value to see if board is present on bus */
struct xvmeRegs *pXvmeRegs; /* ptr to PB-BIT hardware register structure */
char *nameTemp[50]; /* used to build up name of invoked tasks */
if (init_called) {
printf("initBB(): BB devices already initialized!\n");
return(ERROR);
}
rebootHookAdd(BBrebootFunc);
/* Figure out where the short io 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("PEP_BB_SHORT_OFF 0x%08.8X\n", PEP_BB_SHORT_OFF);
printf("BB_NUM_LINKS 0x%08.8X\n", BB_NUM_LINKS);
printf("PEP_BB_IVEC_BASE 0x%08.8X\n", PEP_BB_IVEC_BASE);
}
probeValue = 0x00; /* actual value doesn't matter */
/* Get ptr to first possible location for PB-BIT board on bus */
pXvmeRegs = (struct xvmeRegs *)((unsigned int)short_base + PEP_BB_SHORT_OFF);
/* Do once each for allowed number of links on one bus */
for (i=0; i<BB_NUM_LINKS; i++) {
if (vxMemProbe(&(pXvmeRegs->int_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; j<BB_NUM_PRIO; j++) {
pXvmeLink[i]->pbbLink->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; j<BB_APERLINK; j++) {
/* Assume all nodes are IDLE */
pXvmeLink[i]->pbbLink->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);
}