Files
pcas/src/drv/old/drvBitBus.c
John Winans e1042ca5ee Fixed a reversed set of parms to a semBCreate().
Changed the data type of a parameter to memory probe call.
1996-05-03 19:04:44 +00:00

2638 lines
81 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* #define XYCOM_DO_RESET_AND_OFFLINE */
/* #define PEP_DO_RESET_AND_OFFLINE */
/*
changes to UI:
XYCOM_BB_MAX_OUTSTAND_MSGS -> XycomMaxOutstandMsgs
TODO:
1) Synthetic delays after 91s.
2) Rebuild the link kill stuff so that it does not mess with the WDtask.
3) Verify race conditions on the slave reset function.
4) Write a callable bug-syncer that uses the RAC get function ID command.
5) Merge the watchdog tasks into one.
6) Try merging the TX and RX tasks.
*/
/*
* Original Author: Ned Arnold
* Author: John Winans
* Date: 09-10-91
* XVME-402 and 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 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
* .09 06-28-94 jrw Major work on RAC_RESET and NODE_OFFLINE stuff.
* .10 07-01-94 jrw Merged PEP and Xycom versions together.
* ANSIficated this code
*
* NOTES:
* This driver currently needs work on error message generation.
*
* $Log$
* Revision 1.2 1995/09/26 14:50:31 winans
* Added code to send last byte of a RAC_RESET in pepTxTask(). It was left
* out in a previous mod that modes the last byte transmission into a locked
* region in order to prevent a TxTask/RxTask race condition.
*
* Revision 1.1 1995/03/30 19:34:56 jba
* Seperated drv files into ansi and old dirs. Added combine dir.
*
* Revision 1.37 1995/03/24 21:24:25 winans
* Probable race condition in PEP TX task. Moved the final transmission
* byte assignment into the point where the busy list is locked. This
* is how the Xycom TX task has worked all along. This change fixed
* seems to have fixed an apparant race condition where the receive task
* gets a response to a transmitted message BEFORE the TX task gets
* it on the busy list.
*
* Revision 1.36 1994/12/16 16:11:26 winans
* Added debug flag guards to ALL printing. The default debug level is set
* to 1 -- this provides the same output as the old version.
*
* Revision 1.35 1994/12/12 16:02:57 winans
* Rewrote the init code so that it always returns a zero (don't kill the
* startup.cmd file.) It is possible that this could cause some confusion
* to the database, should it decide to then use a link that did not init
* properly.
*
* Revision 1.34 1994/10/19 18:31:22 winans
* ANSIfied the bitbus driver so that the compiler stopped warning about
* exery third line of code.
*
* Revision 1.33 1994/10/04 18:42:42 winans
* Added an extensive debugging facility.
*
*
*/
#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 <tickLib.h>
#include <vme.h>
#include <iv.h>
#include <rebootLib.h>
#include <logLib.h>
#include <sysLib.h>
#include <intLib.h>
#include <stdio.h>
#include <vxLib.h>
#include <string.h>
#include <task_params.h>
#include <module_types.h>
#include <drvSup.h>
#include <dbDefs.h>
#include <link.h>
#include <callback.h>
#include <taskwd.h>
#include <devLib.h>
#include <drvBitBusInterface.h>
#include "drvBitBus.h"
#define STATIC static
#define BB_DEFAULT_RETIRE_TIME 5 /* Seconds to wait for a response */
STATIC long reportBB(void);
STATIC long initBB(void);
STATIC long qBBReq(struct dpvtBitBusHead *pdpvt, int prio);
STATIC int xvmeReset(int link);
STATIC int xvmeTmoHandler(int link);
STATIC int xvmeRxTask(int link);
STATIC int xvmeTxTask(int link);
STATIC int xvmeWdTask(int link);
STATIC int xvmeIrqRdav(int link);
STATIC int xvmeIrqRcmd(int link);
STATIC int pepReset(int link);
STATIC int pepTmoHandler(int link);
STATIC int pepRxTask(int link);
STATIC int pepTxTask(int link);
STATIC int pepWdTask(int link);
STATIC int pepIrqRdav(int link);
STATIC int bbKill(int link);
STATIC void BBrebootFunc(void);
STATIC int txStuck(int link);
int BBConfig(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
int __BBConfig(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
#ifdef BB_SUPER_DEBUG
int BBHistDump(int link);
STATIC int BBDumpXactHistory(XactHistStruct *pXact);
#endif
/*****************************************************************************
*
* Used to limit the TOTAL number of simultaneous messages that can be
* outstanding on a single Xycom link. (Shell settable.)
*
*****************************************************************************/
int XycomMaxOutstandMsgs = XYCOM_BB_MAX_OUTSTAND_MSGS;
/*****************************************************************************
*
* Debugging flags that may be set from the shell.
*
* bbDebug Used to get status information from the driver's internals.
* May be set with a value from 0 to 40. the higher the number
* the more information. 0 will provide no internal debugging.
*
* bbDebugLink Used to get general status information about a single slave
* bbDebugNode node.
* To disable this feature, set one of them to -1.
*
*****************************************************************************/
int bbDebug = 1;
int bbDebugLink=-1;
int bbDebugNode=-1;
#ifdef BB_SUPER_DEBUG
static int BBSetHistEvent(int link, struct dpvtBitBusHead *pXact, int State);
#endif
/******************************************************************************
*
* This structure contains a list of the outside-callable functions.
*
******************************************************************************/
struct drvBitBusEt drvBitBus = {
3,
reportBB,
initBB,
qBBReq
};
#if 0
/* JRW --> get rid of this crap */
STATIC char BitbusInitCalled = 0; /* to insure that init is done first */
STATIC void *short_base; /* base of short address space */
#endif
STATIC BitbusLinkStruct *pBBLink[BB_NUM_LINKS]; /* NULL if link not config'd */
/***************************************************************************
*
* User-callable configuration function.
*
* This function is used to configure the type of link as well as to
* initialize it for operation.
*
*****************************************************************************/
int BBConfig(unsigned long Link,
unsigned long LinkType,
unsigned long BaseAddr,
unsigned long IrqVector,
unsigned long IrqLevel)
{
__BBConfig(Link, LinkType, BaseAddr, IrqVector, IrqLevel);
return(0);
}
int __BBConfig(unsigned long Link,
unsigned long LinkType,
unsigned long BaseAddr,
unsigned long IrqVector,
unsigned long IrqLevel)
{
void *pVoid;
int j;
static int FirstTime = 1;
char nameTemp[30];
int taskId;
if (FirstTime)
{
rebootHookAdd(BBrebootFunc);
FirstTime = 0;
}
if (Link >= BB_NUM_LINKS)
{
logMsg("Error: Invalid link (%d) specified in BBConfig()\n", Link);
return(-1);
}
if ((LinkType != BB_CONF_TYPE_XYCOM) && (LinkType != BB_CONF_TYPE_PEP))
{
logMsg("Error: Invalid link type (%d) specified in BBConfig()\n", LinkType);
return(-1);
}
if (pBBLink[Link] != NULL)
{
logMsg("Error: BBConfig() Attempt to reconfigure link %d!\n", Link);
return(-1);
}
if ((pBBLink[Link] = (BitbusLinkStruct *) malloc(sizeof(BitbusLinkStruct))) == NULL)
{
logMsg("Error: BBConfig() cannot malloc\n");
return(-1);
}
pBBLink[Link]->LinkType = BB_CONF_HOSED; /* Changed if all goes well */
pBBLink[Link]->BaseAddr = BaseAddr;
pBBLink[Link]->IrqVector = IrqVector;
pBBLink[Link]->IrqLevel = IrqLevel;
#ifdef BB_SUPER_DEBUG
/* Init the history FIFO to empty */
pBBLink[Link]->History.sem = semBCreate(SEM_Q_PRIORITY, SEM_FULL);
pBBLink[Link]->History.Next = 0;
pBBLink[Link]->History.Num = 0;
#endif
pBBLink[Link]->linkEventSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
if (sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO , BaseAddr, &pVoid) != OK)
{
logMsg("Error: BBConfig() can not translate requested A16 address(0x%8.8X\n", BaseAddr);
pBBLink[Link] = NULL;
return(-1);
}
/*
* Interface specific I/O mapping and registration
*/
switch (LinkType)
{
case BB_CONF_TYPE_XYCOM:
pBBLink[Link]->l.XycomLink.rxInt = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
pBBLink[Link]->l.XycomLink.bbRegs = (XycomBBRegsStruct *)pVoid;
if (devRegisterAddress("Xycom Bitbus", atVMEA16, (void*)BaseAddr, sizeof(XycomBBRegsStruct), NULL) != 0)
{
logMsg("Error: BBConfig() can not register address %8.8X\n", pVoid);
pBBLink[Link] = NULL;
return(-1); /* BUG */
}
break;
case BB_CONF_TYPE_PEP:
pBBLink[Link]->l.PepLink.rxInt = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
pBBLink[Link]->l.PepLink.bbRegs = (PepBBRegsStruct *)pVoid;
if (devRegisterAddress("PEP Bitbus", atVMEA16, (void*)BaseAddr, sizeof(PepBBRegsStruct), NULL) != 0)
{
logMsg("Error: BBConfig() can not register address %8.8X\n", pVoid);
pBBLink[Link] = NULL;
return(-1); /* BUG */
}
break;
}
/*
* Common data structure initialization.
*/
/* Init the prioritized queue lists */
for (j=0; j<BB_NUM_PRIO; j++)
{
pBBLink[Link]->queue[j].head = NULL;
pBBLink[Link]->queue[j].tail = NULL;
pBBLink[Link]->queue[j].sem = semBCreate(SEM_Q_PRIORITY, SEM_FULL);
pBBLink[Link]->queue[j].elements = 0;
}
/* Init the busy message list */
pBBLink[Link]->busyList.sem = semBCreate(SEM_Q_PRIORITY, SEM_FULL);
pBBLink[Link]->busyList.head = NULL;
pBBLink[Link]->busyList.tail = NULL;
pBBLink[Link]->busyList.elements = 0;
for (j=0; j<BB_APERLINK; j++)
{ /* Assume all nodes are IDLE with no delay imposed */
pBBLink[Link]->deviceStatus[j] = BB_IDLE;
pBBLink[Link]->syntheticDelay[j] = 0;
}
pBBLink[Link]->DelayCount = 0;
pBBLink[Link]->watchDogId = wdCreate();
pBBLink[Link]->watchDogSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
/* Clear the link abort status */
pBBLink[Link]->abortFlag = 0;
pBBLink[Link]->txAbortAck = 0;
pBBLink[Link]->rxAbortAck = 0;
/*
* Interface type specific initialization of management tasks.
*/
if (LinkType == BB_CONF_TYPE_XYCOM)
{
/* BOARD SPECIFIC INIT CODE for XYCOM */
if (xvmeReset(Link) != 0)
{
pBBLink[Link] = NULL;
return(-1);
}
pBBLink[Link]->LinkType = LinkType;
/* attach the interrupt handler routines */
intConnect((BB_IVEC_BASE + 1 + (Link*4)) * 4, xvmeIrqRcmd, Link);
intConnect((BB_IVEC_BASE + 3 + (Link*4)) * 4, xvmeIrqRdav, Link);
/* Start a task to manage the TX link */
sprintf(nameTemp, "%s%d-xy", BBTXLINK_NAME, Link);
if ((taskId=taskSpawn(nameTemp, BBTXLINK_PRI, BBTXLINK_OPT, BBTXLINK_STACK, xvmeTxTask, Link)) == ERROR)
{
logMsg("BBConfig(): failed to start TX link task for link %d\n", Link);
}
taskwdInsert(taskId,NULL,NULL);
/* Start a task to manage the RX link */
sprintf(nameTemp, "%s%d-xy", BBRXLINK_NAME, Link);
if ((taskId=taskSpawn(nameTemp, BBRXLINK_PRI, BBRXLINK_OPT, BBRXLINK_STACK, xvmeRxTask, Link)) == ERROR)
{
logMsg("BBConfig(): failed to start RX link task for link %d\n", Link);
}
taskwdInsert(taskId,NULL,NULL);
/* Start a task to keep an eye on the busy list */
sprintf(nameTemp, "%s%d-xy", BBWDTASK_NAME, Link);
if ((taskId=taskSpawn(nameTemp, BBWDTASK_PRI, BBWDTASK_OPT, BBWDTASK_STACK, xvmeWdTask, Link)) == ERROR)
{
logMsg("BBConfig(): failed to start watchdog task for link %d\n", Link);
}
taskwdInsert(taskId,NULL,NULL);
}
else if (LinkType == BB_CONF_TYPE_PEP)
{
/* PEP specific init code */
if (pepReset(Link) != 0)
{
pBBLink[Link] = NULL;
return(-1);
}
pBBLink[Link]->LinkType = LinkType;
/* attach the interrupt handler routines */
intConnect(INUM_TO_IVEC(PEP_BB_IVEC_BASE + (Link*2)), pepIrqRdav, Link);
/* Start a task to manage the TX link */
sprintf(nameTemp, "%s%d-pep", BBTXLINK_NAME, Link);
if ((taskId=taskSpawn(nameTemp, BBTXLINK_PRI, BBTXLINK_OPT, BBTXLINK_STACK, pepTxTask, Link)) == ERROR)
{
logMsg("BBConfig(): failed to start TX link task for link %d\n", Link);
}
taskwdInsert(taskId,NULL,NULL);
/* Start a task to manage the RX link */
sprintf(nameTemp, "%s%d-pep", BBRXLINK_NAME, Link);
if ((taskId=taskSpawn(nameTemp, BBRXLINK_PRI, BBRXLINK_OPT, BBRXLINK_STACK, pepRxTask, Link)) == ERROR)
{
logMsg("BBConfig(): failed to start RX link task for link %d\n", Link);
}
taskwdInsert(taskId,NULL,NULL);
/* Start a task to keep an eye on the busy list */
sprintf(nameTemp, "%s%d-pep", BBWDTASK_NAME, Link);
if ((taskId=taskSpawn(nameTemp, BBWDTASK_PRI, BBWDTASK_OPT, BBWDTASK_STACK, pepWdTask, Link)) == ERROR)
{
logMsg("BBConfig(): failed to start watchdog task for link %d\n", Link);
}
taskwdInsert(taskId,NULL,NULL);
}
sysIntEnable(IrqLevel);
return(0);
}
/******************************************************************
*
* The EPICS init routine is not needed.
*
******************************************************************/
STATIC long initBB(void)
{
return(0);
}
/******************************************************************
* FUNCTION: reportBB()
* PURPOSE : Prints a message indicating the presence of each
* bitbus module found in system.
* ARGS IN : none
* ARGS OUT: none
* GLOBALS: checks pBBLink[i] for NULL
******************************************************************/
STATIC long reportBB(void)
{
int i;
if (bbDebug>1)
printf("Bitbus debugging flag is set to %d\n", bbDebug);
for (i=0; i< BB_NUM_LINKS; i++)
{
if (pBBLink[i] != NULL)
{
if (pBBLink[i]->LinkType == BB_CONF_TYPE_XYCOM)
printf("Bitbus link %d present at %p IV=0x%2.2X IL=%d (XYCOM)\n", i, pBBLink[i]->l.XycomLink.bbRegs, pBBLink[i]->IrqVector, pBBLink[i]->IrqLevel);
else if (pBBLink[i]->LinkType == BB_CONF_TYPE_PEP)
printf("Bitbus link %d present at %p IV=0x%2.2X IL=%d (PEP)\n", i, pBBLink[i]->l.PepLink.bbRegs, pBBLink[i]->IrqVector, pBBLink[i]->IrqLevel);
}
}
return(OK);
}
/******************************************************************
* FUNCTION: pepReset()
* 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
pepReset(int link)
{
char trash;
int j;
int lockKey;
int probeValue;
if (bbDebug)
printf("pepReset(%d): Resetting pep module\n", link);
probeValue = 0;
if (vxMemProbe(&(pBBLink[link]->l.PepLink.bbRegs->int_vec), WRITE, 1, &probeValue) < OK)
{ /* no BB board present here */
logMsg("ERROR: PEP Bitbus link %d not present at 0x%8.8X\n", link, pBBLink[link]->l.PepLink.bbRegs);
return(-1);
}
/* Write firmware reset package (2 bytes) to board */
pBBLink[link]->l.PepLink.bbRegs->data = 0x83;
pBBLink[link]->l.PepLink.bbRegs->stat_ctl = 0x01;
taskDelay(20); /* give the 80152 time to self check */
if ((pBBLink[link]->l.PepLink.bbRegs->stat_ctl & 0x10) != 0x0)
{
if (bbDebug)
printf("pepReset(%d): PB-BIT firmware reset failed!\n", link);
return(ERROR);
}
j = 1026; /* 1K deep receive fifo */
/* flush receive fifo if junk in it */
while ((pBBLink[link]->l.PepLink.bbRegs->stat_ctl & PEP_BB_RFNE) && --j)
trash = pBBLink[link]->l.PepLink.bbRegs->data;
if (!j)
{
if (bbDebug)
printf("pepReset(%d): receive fifo will not clear after reset!\n", link);
return(ERROR);
}
/* Disable interrupts */
lockKey = intLock();
pBBLink[link]->l.PepLink.bbRegs->int_vec = 0;
intUnlock(lockKey);
/* Tell pepRxTask to Re-enable ints */
semGive(pBBLink[link]->l.PepLink.rxInt);
return(OK);
}
/****************************************************************************
*
* Reset an xvme-402 BitBus card by cycling the reset bit in the fifo
* status register.
*
****************************************************************************/
STATIC int xvmeReset(int link)
{
char trash;
int j;
int lockKey;
unsigned char probeValue;
if (bbDebug)
printf("xvmeReset(%d): Resetting xvme module\n", link);
probeValue = XVME_RESET;
if (vxMemProbe(&(pBBLink[link]->l.XycomLink.bbRegs->fifo_stat), WRITE, 1, &probeValue) < OK)
{ /* no BB board present here */
logMsg("ERROR: Xycom Bitbus link %d not present at 0x%8.8X\n", link, pBBLink[link]->l.XycomLink.bbRegs);
return(-1);
}
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, NULL, XACT_HIST_STATE_RESET);
#endif
taskDelay(2);
pBBLink[link]->l.XycomLink.bbRegs->fifo_stat = 0; /* clear reset pulse */
taskDelay(30); /* give the 8044 time to self check */
j = 100; /* give up after this */
while ((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_RCMD) && --j)
trash = pBBLink[link]->l.XycomLink.bbRegs->cmnd; /* flush command buffer if junk in it */
if (!j)
{
if (bbDebug)
printf("xvmeReset(%d): Command buffer will not clear after reset!\n", link);
return(ERROR);
}
j = 100;
while ((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_RFNE) && --j)
trash = pBBLink[link]->l.XycomLink.bbRegs->data; /* flush data buffer if junk in it */
if (!j)
{
if (bbDebug)
printf("xvmeReset(%d): Data buffer will not clear after reset!\n", link);
return(ERROR);
}
if ((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_FSVALID) != XVME_FSIDLE)
{
if (bbDebug)
printf("xvmeReset(%d): XVME board not returning to idle status after reset!\n", link);
return(ERROR);
}
/* set the interrupt vector */
lockKey = intLock();
pBBLink[link]->l.XycomLink.bbRegs->stat_ctl = 0; /* disable all interupts */
pBBLink[link]->l.XycomLink.bbRegs->int_vec = BB_IVEC_BASE + (link*4);/* set the int vector */
intUnlock(lockKey);
semGive(pBBLink[link]->l.XycomLink.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(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(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(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);
}
/****************************************************************************
*
* xycom IRQ handler for receiver data bytes.
*
****************************************************************************/
static int
xvmeIrqRdav(int link)
{
if (bbDebug > 30)
logMsg("bitbus rx IRQ on link %d\n", link);
semGive(pBBLink[link]->l.XycomLink.rxInt); /* deliver the groceries */
return(0);
}
/****************************************************************************
*
* xycom IRQ handler invoked when the BitBus controller has completed the
* transfer of a RECEIVED message.
*
****************************************************************************/
static int
xvmeIrqRcmd(int link)
{
if (bbDebug > 30)
logMsg("bitbus rcmd IRQ on link %d\n", link);
semGive(pBBLink[link]->l.XycomLink.rxInt); /* deliver the groceries */
return(0);
}
/******************************************************************
* FUNCTION: pepIrqRdav()
* 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
pepIrqRdav(int link)
{
pBBLink[link]->l.PepLink.bbRegs->int_vec = 0; /* disable IRQs */
if (bbDebug > 30)
logMsg("PEP bitbus rx IRQ on link %d\n", link);
semGive(pBBLink[link]->l.PepLink.rxInt); /* unblock it */
return(0);
}
/******************************************************************
* FUNCTION: pepTmoHandler()
* 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
pepTmoHandler(int link)
{
if (bbDebug > 25)
logMsg("pepTmoHandler(%d): Watch dog interrupt\n", link);
semGive(pBBLink[link]->watchDogSem); /* unblock Wd task */
return(0);
}
/******************************************************************************
*
* 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(int link)
{
if (bbDebug > 25)
logMsg("xvmeTmoHandler(%d): Watch dog interrupt\n", link);
semGive(pBBLink[link]->watchDogSem);
return(0);
}
/****************************************************************************
*
* Given a link number, make sure it is valid.
*
****************************************************************************/
static int
checkLink(int link)
{
if ((link<0) || (link>BB_NUM_LINKS))
return(ERROR); /* link number out of range */
if (pBBLink[link] == NULL)
return(ERROR); /* link number has no card installed */
return(OK);
}
/***************************************************************************
*
* 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(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 */
struct dpvtBitBusHead UselessMsg; /* to hold unsolicited responses */
unsigned char UselessData[BB_MAX_MSG_LENGTH];
int lockKey; /* used for intLock calls */
rxMsg = (unsigned char *) NULL;
rxState = BBRX_HEAD;
rxTCount = 0;
rxDpvtHead = (struct dpvtBitBusHead *) NULL;
/* Dummy up the UselessMsg fields so we can use it */
UselessMsg.rxMaxLen = BB_MAX_MSG_LENGTH;
UselessMsg.rxMsg.data = UselessData;
UselessMsg.link = link;
UselessMsg.ageLimit = 0;
UselessMsg.retire = 0;
while (1)
{
/* Wait for RX interrupts, but only if no chars are ready */
if ((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_RFNE) == 0)
{
/* Enable interrupts and check again because xycom blew it */
lockKey = intLock();
pBBLink[link]->l.XycomLink.bbRegs->stat_ctl = XVME_ENABLE_INT | XVME_RX_INT;
intUnlock(lockKey);
while (((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_RFNE) == 0) && (pBBLink[link]->abortFlag == 0))
{
/* Re-enable ints here each time in case board got reset */
lockKey = intLock();
pBBLink[link]->l.XycomLink.bbRegs->stat_ctl = XVME_ENABLE_INT | XVME_RX_INT;
intUnlock(lockKey);
/* Wait for groceries */
semTake(pBBLink[link]->l.XycomLink.rxInt, WAIT_FOREVER);
}
/* Disable RX Interrupts (prevents unnecessary context switching) */
lockKey = intLock();
pBBLink[link]->l.XycomLink.bbRegs->stat_ctl = 0;
intUnlock(lockKey);
}
if (pBBLink[link]->abortFlag == 0)
{
/* check to see if we got a data byte or a command byte */
if ((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_RCMD) == XVME_RCMD)
rxState = BBRX_RCMD;
switch (rxState) {
case BBRX_HEAD: /* getting the head of a new message */
rxHead[rxTCount] = pBBLink[link]->l.XycomLink.bbRegs->data;
if (bbDebug>21)
printf("xvmeRxTask(%d): >%2.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(pBBLink[link]->busyList.sem, WAIT_FOREVER);
rxDpvtHead = pBBLink[link]->busyList.head;
while (rxDpvtHead != NULL)
{
if (bbDebug>19)
printf("xvmeRxTask(%d): checking reply against %p\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 %p\n", link, rxDpvtHead);
/* Delete the node from the list */
listDel(&(pBBLink[link]->busyList), rxDpvtHead);
/* If busy list is empty, stop the dog */
if (pBBLink[link]->busyList.head == NULL)
wdCancel(pBBLink[link]->watchDogId);
if (rxHead[4] == 0x91)
{ /* something bad happened... inject a delay to the */
/* requested timeout duration. */
if (bbDebug)
printf("xvmeRxTask(%d): 0x91 from node %d, invoking synthetic delay\n", link, rxHead[2]);
(pBBLink[link]->syntheticDelay[rxDpvtHead->txMsg.node]) = rxDpvtHead->retire;
pBBLink[link]->DelayCount++;
}
else
{ /* decrement the number of outstanding messages to the node */
(pBBLink[link]->deviceStatus[rxDpvtHead->txMsg.node])--;
}
/* Wake up Link Task in case was waiting on "this" node */
semGive(pBBLink[link]->linkEventSem);
#if 0
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 */
#endif
break; /* get out of the while() */
}
}
rxDpvtHead = rxDpvtHead->next; /* Keep looking */
}
semGive(pBBLink[link]->busyList.sem);
/* Couldn't find a match... fake one so can print the bad message */
if (rxDpvtHead == NULL)
rxDpvtHead = &UselessMsg;
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;
case BBRX_DATA: /* finish reading data portion of message */
ch = pBBLink[link]->l.XycomLink.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): %2.2X (Ignored)\n", link, ch);
}
else
{
*rxMsg = ch;
if (bbDebug>22)
printf("xvmeRxTask(%d): %2.2X (Data)\n", link, ch);
rxMsg++;
rxTCount++;
}
break;
case BBRX_IGN:
ch = pBBLink[link]->l.XycomLink.bbRegs->data;
if (bbDebug>22)
printf("xvmeRxTask(%d): %2.2X (Ignored)\n", link, ch);
break;
case BBRX_RCMD:
if (rxDpvtHead == NULL)
{
ch = pBBLink[link]->l.XycomLink.bbRegs->cmnd;
if (bbDebug)
printf("xvmeRxTask(%d): got unexpected XVME_RCMD\n", link);
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, NULL, XACT_HIST_STATE_RX_URCMD);
#endif
}
else if (rxDpvtHead == &UselessMsg)
{
rxDpvtHead->status = BB_OK; /* XXX -- ??? */
rxDpvtHead->rxCmd = pBBLink[link]->l.XycomLink.bbRegs->cmnd;
if (bbDebug)
{
printf("xvmeRxTask(%d): msg from node %d unsolicited:", link, rxDpvtHead->rxMsg.node);
drvBitBusDumpMsg(&(rxDpvtHead->rxMsg));
}
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, rxDpvtHead, XACT_HIST_STATE_RX_UNSOLICITED);
#endif
}
else
{
rxDpvtHead->status = BB_OK; /* XXX -- ??? */
rxDpvtHead->rxCmd = pBBLink[link]->l.XycomLink.bbRegs->cmnd;
if (bbDebug>24)
printf("xvmeRxTask(%d):RX command byte = %2.2X\n", link, rxDpvtHead->rxCmd);
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, rxDpvtHead, XACT_HIST_STATE_RX);
#endif
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(pBBLink[link]->busyList.sem, WAIT_FOREVER);
(pBBLink[link]->deviceStatus[rxDpvtHead->txMsg.node])++;
listAddTail(&(pBBLink[link]->busyList), rxDpvtHead);
semGive(pBBLink[link]->busyList.sem);
}
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, rxDpvtHead, XACT_HIST_STATE_RX_ABORT);
#endif
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) */
pBBLink[link]->rxAbortAck = 1;
if (bbDebug)
printf("xvmeRxTask(%d): resetting due to abort status\n", link);
/* wait for link state to become active again */
while (pBBLink[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(int link)
{
if (checkLink(link) != ERROR)
{
pBBLink[link]->nukeEm = 1;
semGive(pBBLink[link]->watchDogSem);
}
else
printf("Link %d not installed.\n", link);
return(0);
}
/******************************************************************************
*
* BUG -- this does not do the nuke=2 reset and kill link operations like
* the PEP does.
*
******************************************************************************/
static int
xvmeWdTask(int link)
{
struct dpvtBitBusHead *pnode;
struct dpvtBitBusHead *npnode;
unsigned long now;
#ifdef XYCOM_DO_RESET_AND_OFFLINE
SEM_ID syncSem;
struct dpvtBitBusHead resetNode;
unsigned char resetNodeData; /* 1-byte data field for RAC_OFFLINE */
/* init the SEM used when sending the reset message */
syncSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
/*
* 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
pBBLink[link]->nukeEm = 0; /* Make sure the nuke status is clear */
while(1)
{
semTake(pBBLink[link]->watchDogSem, WAIT_FOREVER);
now = tickGet(); /* what time is it? */
if (pBBLink[link]->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);
pBBLink[link]->rxAbortAck = 0; /* In case we need to use them */
pBBLink[link]->txAbortAck = 0;
if (pBBLink[link]->nukeEm != 0)
{ /* set abort status and wait for the abort acks */
pBBLink[link]->abortFlag = 1;
/* wake up the Tx task so it can observe the abort status */
semGive(pBBLink[link]->linkEventSem);
/* wake up the Rx task so it can observe the abort ststus */
semGive(pBBLink[link]->l.XycomLink.rxInt);
/* sleep until abort ack from Tx & Rx tasks */
while ((pBBLink[link]->rxAbortAck == 0) && (pBBLink[link]->txAbortAck == 0))
taskDelay(RESET_POLL_TIME);
#ifdef BB_SUPER_DEBUG
if (pBBLink[link]->nukeEm < 2)
{ /* Do a history dump since this is not supposed to happen */
BBHistDump(link);
}
#endif
}
/*
* 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(pBBLink[link]->busyList.sem, WAIT_FOREVER);
pnode = pBBLink[link]->busyList.head;
while (pnode != NULL)
{
npnode = pnode->next; /* remember where we were in the list */
if ((pBBLink[link]->nukeEm != 0) || (pnode->retire <= now))
{
/* Get rid of the request and set error status etc... */
listDel(&(pBBLink[link]->busyList), pnode);
if (bbDebug)
{
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, pnode, XACT_HIST_STATE_WD_TIMEOUT);
#endif
printf("xvmeWdTask(%d): TIMEOUT on bitbus message:\n", link);
drvBitBusDumpMsg(&pnode->txMsg);
}
/* BUG -- do this here or defer until RX gets a response? */
(pBBLink[link]->deviceStatus[pnode->txMsg.node])--; /* fix device status */
pnode->status = BB_TIMEOUT;
#ifdef XYCOM_DO_RESET_AND_OFFLINE
/* BUG ----- wait for the response from the last one first? */
/* 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 XYCOM_DO_RESET_AND_OFFLINE
/* If we are not going to reboot the link... */
if (pBBLink[link]->nukeEm == 0)
{ /* Send out a RAC_NODE_OFFLINE to the controller */
semGive(pBBLink[link]->busyList.sem);
if (bbDebug)
printf("issuing a node offline for link %d node %d\n", link, resetNodeData);
semTake(pBBLink[link]->queue[BB_Q_HIGH].sem, WAIT_FOREVER);
listAddHead(&(pBBLink[link]->queue[BB_Q_HIGH]), &resetNode);
semGive(pBBLink[link]->queue[BB_Q_HIGH].sem);
semGive(pBBLink[link]->linkEventSem); /* Tell TxTask to send the message */
if (semTake(syncSem, sysClkRateGet()/4) == ERROR)
{
if (bbDebug)
printf("xvmeWdTask(%d): link dead, trying manual reboot\n", link);
pBBLink[link]->nukeEm = 1;
pBBLink[link]->abortFlag = 1; /* Start the abort sequence */
semGive(pBBLink[link]->linkEventSem); /* Let Tx task observe abort status */
semGive(pBBLink[link]->l.XycomLink.rxInt); /* Let Rx task observe abort ststus */
/* sleep until abort ack from Tx & Rx tasks */
while ((pBBLink[link]->rxAbortAck == 0) && (pBBLink[link]->txAbortAck == 0))
taskDelay(RESET_POLL_TIME);
}
/* Start over since released the busy list */
semTake(pBBLink[link]->busyList.sem, WAIT_FOREVER);
npnode = pBBLink[link]->busyList.head;
}
#else
semGive(pBBLink[link]->linkEventSem);
#endif
}
pnode = npnode;
}
if (pBBLink[link]->busyList.head != NULL)
{ /* Restart the dog timer */
if (bbDebug>5)
printf("xvmeWdTask(%d): restarting watch dog timer\n", link);
wdStart(pBBLink[link]->watchDogId, pBBLink[link]->busyList.head->retire - now, xvmeTmoHandler, link);
}
semGive(pBBLink[link]->busyList.sem);
/* Finish the link reboot if necessary */
if (pBBLink[link]->nukeEm != 0)
{
xvmeReset(link);
/* clear the abort_flag */
pBBLink[link]->abortFlag = 0;
pBBLink[link]->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(int link)
{
struct dpvtBitBusHead *pnode;
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 = sysClkRateGet();
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;
while(1)
{
if (pBBLink[link]->abortFlag != 0)
{
pBBLink[link]->txAbortAck = 1; /* let the dog know we are ready */
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, NULL, XACT_HIST_STATE_TX_ABORT);
#endif
if (bbDebug)
printf("xvmeTxTask(%d): resetting due to abort status\n", link);
while (pBBLink[link]->abortFlag != 0)
taskDelay(RESET_POLL_TIME); /* wait for link to reset */
if (bbDebug)
printf("xvmeTxTask(%d): restarting after abort\n", link);
}
else
{
if (pBBLink[link]->DelayCount)
semTake(pBBLink[link]->linkEventSem, 2 * sysClkRateGet());
else
semTake(pBBLink[link]->linkEventSem, WAIT_FOREVER);
}
if (bbDebug>5)
printf("xvmeTxTask(%d): got an event\n", link);
working = 1;
while ((working != 0) && (pBBLink[link]->abortFlag == 0) && (pBBLink[link]->busyList.elements < XycomMaxOutstandMsgs))
{
working = 0;
prio = BB_NUM_PRIO-1;
while ((prio >= 0) && (pBBLink[link]->abortFlag == 0))
{
/* see if the queue has anything in it */
semTake(pBBLink[link]->queue[prio].sem, WAIT_FOREVER);
if ((pnode = pBBLink[link]->queue[prio].head) != NULL)
{
now = tickGet();
while (pBBLink[link]->deviceStatus[pnode->txMsg.node] == BB_BUSY)
{
if ((pBBLink[link]->syntheticDelay[pnode->txMsg.node] != 0)
&& (pBBLink[link]->syntheticDelay[pnode->txMsg.node] < now))
{
if (bbDebug)
printf("xvmeTxTask(%d): terminating synthetic idle on node %d\n", link, pnode->txMsg.node);
(pBBLink[link]->deviceStatus[pnode->txMsg.node])--;
pBBLink[link]->syntheticDelay[pnode->txMsg.node] = 0;
pBBLink[link]-> DelayCount--;
}
else
{
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(&(pBBLink[link]->queue[prio]), pnode);
semGive(pBBLink[link]->queue[prio].sem);
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX);
#endif
if (bbDebug>3)
printf("xvmeTxTask(%d): got xact, pnode=%p\n", link, pnode);
/* Send the message in polled mode */
txTCount = pnode->txMsg.length - 2;
txCCount = 0;
txMsg = &(pnode->txMsg.length);
while ((txCCount <= txTCount) && (pBBLink[link]->abortFlag == 0))
{
stuck = 1000;
while (((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_TFNF) != XVME_TFNF) && (pBBLink[link]->abortFlag == 0) && --stuck)
for(x=0;x<100;x++); /* wait for TX ready */
if (!stuck)
{
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX_STUCK);
#endif
txStuck(link); /* will end up setting abortFlag */
}
else if (txCCount < txTCount) /* on last time, just wait, no data */
{
pBBLink[link]->l.XycomLink.bbRegs->data = *txMsg; /* send next byte */
if (bbDebug>30)
printf("xvmeTxTask(%d): outputting %2.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 (pBBLink[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) && (pnode->txMsg.tasks == 0))
{ /* Finish the transaction here if was a RAC_RESET_SLAVE */
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX_RESET);
#endif
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(pBBLink[link]->queue[BB_Q_HIGH].sem, WAIT_FOREVER);
listAddHead(&(pBBLink[link]->queue[BB_Q_HIGH]), &resetNode);
semGive(pBBLink[link]->queue[BB_Q_HIGH].sem);
/* taskDelay(15); */ /* wait while bug is resetting */
}
else
{
/* Lock the busy list */
semTake(pBBLink[link]->busyList.sem, WAIT_FOREVER);
/* set the retire time */
pnode->retire = tickGet();
if (pnode->ageLimit)
pnode->retire += pnode->ageLimit;
else
pnode->retire += BB_DEFAULT_RETIRE_TIME * sysClkRateGet();
if (pBBLink[link]->busyList.head == NULL)
dogStart = 1;
else
dogStart = 0;
/* Add pnode to the busy list */
listAddTail(&(pBBLink[link]->busyList), pnode);
/* Count the outstanding messages */
(pBBLink[link]->deviceStatus[pnode->txMsg.node])++;
semGive(pBBLink[link]->busyList.sem);
/* If just added something to an empty busy list, start the dog */
if (dogStart)
{
now = tickGet();
wdStart(pBBLink[link]->watchDogId, pBBLink[link]->busyList.head->retire - now, xvmeTmoHandler, link);
}
}
/* Tell the 8044 to fire out the message now */
pBBLink[link]->l.XycomLink.bbRegs->cmnd = BB_SEND_CMD; /* forward it now */
}
else
{ /* Aborted transmission operation, re-queue the message */
semTake(pBBLink[link]->queue[BB_Q_HIGH].sem, WAIT_FOREVER);
listAddHead(&(pBBLink[link]->queue[BB_Q_HIGH]), pnode);
semGive(pBBLink[link]->queue[BB_Q_HIGH].sem);
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX_ABORT);
#endif
if (bbDebug)
{
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(pBBLink[link]->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(int link)
{
if (bbDebug)
printf("bitbus transmitter task stuck, resetting link %d\n", link);
bbReset(link);
while (pBBLink[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(struct dpvtBitBusHead *pdpvt, int prio)
{
static linkErrFlags[BB_NUM_LINKS]; /* Supposedly init'd to zero */
char message[200];
if ((prio < 0) || (prio >= BB_NUM_PRIO))
{
sprintf(message, "invalid priority requested in call to qbbreq(%p, %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(%p, %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(%p, %d) %d... card not present\n", pdpvt, prio, pdpvt->link);
errMessage(S_BB_badlink, message);
}
return(ERROR);
}
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(pdpvt->link, pdpvt, XACT_HIST_STATE_QUEUE);
#endif
if (bbDebug>5)
printf("qbbreq(%p, %d): transaction queued\n", pdpvt, prio);
if (bbDebug>6)
drvBitBusDumpMsg(&(pdpvt->txMsg));
semTake(pBBLink[pdpvt->link]->queue[prio].sem, WAIT_FOREVER);
/* Add to the end of the queue of waiting transactions */
listAddTail(&(pBBLink[pdpvt->link]->queue[prio]), pdpvt);
semGive(pBBLink[pdpvt->link]->queue[prio].sem);
semGive(pBBLink[pdpvt->link]->linkEventSem);
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;
}
/*******************************************************************
*
* Same as bbReset() but it takes the link down and leaves it down.
*
*******************************************************************/
int bbKill(int link)
{
if (checkLink(link) != ERROR)
{
pBBLink[link]->nukeEm = 2;
semGive(pBBLink[link]->watchDogSem);
}
else
printf("Link %d not installed.\n", link);
return(0);
}
/******************************************************************
*
* Super debugging facility.
*
******************************************************************/
int
drvBitBusDumpMsg(struct bitBusMsg *pbbMsg)
{
char ascBuf[15]; /* for ascii xlation part of the dump */
int x;
int y;
int z;
printf("Link 0x%4.4X, length 0x%2.2X, route 0x%2.2X, node 0x%2.2X, tasks 0x%2.2X, cmd %2.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("%2.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);
}
#ifdef BB_SUPER_DEBUG
/*
* Place an event into the history buffer.
*/
static int BBSetHistEvent(int link, struct dpvtBitBusHead *pXact, int State)
{
semTake(pBBLink[link]->History.sem, WAIT_FOREVER);
pBBLink[link]->History.Num++;
pBBLink[link]->History.Xact[pBBLink[link]->History.Next].Time = tickGet();
pBBLink[link]->History.Xact[pBBLink[link]->History.Next].State = State;
if (pXact != NULL)
memcpy (&pBBLink[link]->History.Xact[pBBLink[link]->History.Next].Xact, pXact, sizeof(struct dpvtBitBusHead));
if (link==bbDebugLink && pBBLink[link]->History.Xact[pBBLink[link]->History.Next].Xact.txMsg.node==bbDebugNode)
BBDumpXactHistory(&pBBLink[link]->History.Xact[pBBLink[link]->History.Next]);
if (++pBBLink[link]->History.Next == BB_SUPER_DEBUG_HIST_SIZ)
pBBLink[link]->History.Next = 0;
semGive(pBBLink[link]->History.sem);
return(0);
}
int BBHistDump(int link)
{
int count;
int ix;
if (checkLink(link) == ERROR)
{
printf(" Link %d is not valid\n", link);
return(-1);
}
printf("Dumping bitbus history for link %d\n", link);
semTake(pBBLink[link]->History.sem, WAIT_FOREVER);
if (pBBLink[link]->History.Num < BB_SUPER_DEBUG_HIST_SIZ)
{
count = pBBLink[link]->History.Num;
ix = 0;
}
else
{
count = BB_SUPER_DEBUG_HIST_SIZ;
ix = pBBLink[link]->History.Next;
}
while(count)
{
printf("%8.8d:", pBBLink[link]->History.Xact[ix].Time);
if (BBDumpXactHistory(&pBBLink[link]->History.Xact[ix]) != 0)
count = 0;
else
--count;
if (++ix >= BB_SUPER_DEBUG_HIST_SIZ)
ix = 0;
}
semGive(pBBLink[link]->History.sem);
return(0);
}
STATIC int BBDumpXactHistory(XactHistStruct *pXact)
{
switch (pXact->State)
{
/* No BB message present */
case XACT_HIST_STATE_RESET:
printf("Link Reset:\n");
break;
case XACT_HIST_STATE_RX_ABORT:
printf("Link RX aborted\n");
break;
case XACT_HIST_STATE_TX_ABORT:
printf("Link TX aborted\n");
break;
case XACT_HIST_STATE_RX_URCMD:
printf("Link RX got unexpected RCMD\n");
break;
/* TX message only */
case XACT_HIST_STATE_QUEUE:
printf("Message Queued:\n");
goto do_tx_only;
case XACT_HIST_STATE_TX:
printf("Message Transmitted:\n");
goto do_tx_only;
case XACT_HIST_STATE_WD_TIMEOUT:
printf("Watchdog Timeout:\n");
goto do_tx_only;
case XACT_HIST_STATE_TX_STUCK:
printf("Transmitter stuck:\n");
goto do_tx_only;
case XACT_HIST_STATE_TX_RESET:
printf("TX just sent a RAC RESET:\n");
do_tx_only:
drvBitBusDumpMsg(&pXact->Xact.txMsg);
break;
/* RX message only */
case XACT_HIST_STATE_RX_UNSOLICITED:
printf("RX unsolicited message:\n");
drvBitBusDumpMsg(&pXact->Xact.rxMsg);
break;
/* TX and RX messages present */
case XACT_HIST_STATE_RX:
printf("RX response... complete transaction:\n");
drvBitBusDumpMsg(&pXact->Xact.txMsg);
drvBitBusDumpMsg(&pXact->Xact.rxMsg);
break;
default:
printf("Corrupt bitbus debug state!\n");
return(-1);
}
return(0);
}
#endif
/******************************************************************************
*
* A user callable function that may be used to reset the specified slave
* node.
*
* This routine is NOT reenterant but contains a monitor so that it always
* operates properly.
*
******************************************************************************/
int ResetBug(int node, int link)
{
static int FirstTime = 1;
static SEM_ID monLock;
static SEM_ID syncSem; /* Semaphore to use for completion */
struct dpvtBitBusHead resetNode; /* actual message to send out */
unsigned char resetNodeData; /* 1-byte data field for RAC_RESET */
if (FirstTime)
{
monLock = semBCreate(SEM_Q_PRIORITY, SEM_FULL);
syncSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
FirstTime = 0;
}
semTake(monLock, WAIT_FOREVER);
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()*2;
resetNode.txMsg.length = 8;
resetNode.txMsg.route = BB_STANDARD_TX_ROUTE;
resetNode.txMsg.node = node;
resetNode.txMsg.tasks = 0x0;
resetNode.txMsg.cmd = RAC_RESET_SLAVE;
resetNode.txMsg.data = &resetNodeData;
qBBReq(&resetNode, 0);
semTake(syncSem, WAIT_FOREVER);
semGive(monLock);
return(0);
}
/******************************************************************
* FUNCTION: pepRxTask()
* 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
pepRxTask(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 */
struct dpvtBitBusHead UselessMsg; /* to hold unsolicited responses */
unsigned char UselessData[BB_MAX_MSG_LENGTH];
int lockKey; /* used for intLock calls */
int packageComplete; /* indicates end of package read */
rxMsg = (unsigned char *) NULL;
rxState = BBRX_HEAD;
rxTCount = 0;
rxDpvtHead = (struct dpvtBitBusHead *) NULL;
packageComplete = 0;
/* Dummy up the UselessMsg fields so we can use it */
UselessMsg.rxMaxLen = BB_MAX_MSG_LENGTH;
UselessMsg.rxMsg.data = UselessData;
UselessMsg.link = link;
UselessMsg.ageLimit = 0;
UselessMsg.retire = 0;
while (1)
{
/* Wait for RX interrupts, but only if no chars are ready */
if ((pBBLink[link]->l.PepLink.bbRegs->stat_ctl & PEP_BB_RFNE) == 0)
{
/* Enable interrupts and check again in case PB-BIT blew it */
lockKey = intLock();
pBBLink[link]->l.PepLink.bbRegs->int_vec = PEP_BB_IVEC_BASE +(link*2);
intUnlock(lockKey);
while (((pBBLink[link]->l.PepLink.bbRegs->stat_ctl & PEP_BB_RFNE) == 0)
&& (pBBLink[link]->abortFlag == 0))
{
/* Re-enable ints here each time in case board got reset */
lockKey = intLock();
pBBLink[link]->l.PepLink.bbRegs->int_vec = PEP_BB_IVEC_BASE +(link*2);
intUnlock(lockKey);
if (bbDebug>35)
printf("pepRxTask(%d): waiting on IRQ\n", link);
/* wait for data bytes */
semTake(pBBLink[link]->l.PepLink.rxInt, WAIT_FOREVER);
}
/* Disable RX Interrupts (prevents unnecessary context switching) */
lockKey = intLock();
pBBLink[link]->l.PepLink.bbRegs->int_vec = 0;
intUnlock(lockKey);
}
if (pBBLink[link]->abortFlag == 0)
{
/* READ ONE CHAR FROM RECEIVE FIFO */
ch = pBBLink[link]->l.PepLink.bbRegs->data;
/* check to see if we got a data byte or a command byte */
if ((pBBLink[link]->l.PepLink.bbRegs->stat_ctl & PEP_BB_RCMD) == PEP_BB_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("pepRxTask(%d): >%2.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(pBBLink[link]->busyList.sem, WAIT_FOREVER);
rxDpvtHead = pBBLink[link]->busyList.head;
while (rxDpvtHead != NULL) {
if (bbDebug>19)
printf("pepRxTask(%d): checking reply against %p\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("pepRxTask(%d): reply to %p\n",
link, rxDpvtHead);
/* Delete the node from the list */
listDel(&(pBBLink[link]->busyList), rxDpvtHead);
/* If busy list is empty, stop the dog */
if (pBBLink[link]->busyList.head == NULL)
wdCancel(pBBLink[link]->watchDogId);
if (rxHead[6] == 0x91)
{ /* something bad happened... inject a delay to the */
/* requested timeout duration. */
if (bbDebug)
printf("pepRxTask(%d): 0x91 from node %d, invoking synthetic delay\n", link, rxHead[4]);
(pBBLink[link]->syntheticDelay[rxDpvtHead->txMsg.node]) = rxDpvtHead->retire;
pBBLink[link]->DelayCount++;
}
else
{ /* decrement the number of outstanding messages to the node */
(pBBLink[link]->deviceStatus[rxDpvtHead->txMsg.node])--;
}
/* Wake up Link Task in case was waiting on "this" node */
semGive(pBBLink[link]->linkEventSem);
#if 0
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 */
#endif
break; /* get out of the while() */
}
}
rxDpvtHead = rxDpvtHead->next; /* Keep looking */
}
semGive(pBBLink[link]->busyList.sem);
/* Couldn't find a match... fake one so can print the bad message */
if (rxDpvtHead == NULL)
rxDpvtHead = &UselessMsg;
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;
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("pepRxTask(%d): %2.2X (Ignored)\n", link, ch);
}
else {
*rxMsg = ch;
if (bbDebug>22)
printf("pepRxTask(%d): %2.2X (Data)\n", link, ch);
rxMsg++;
rxTCount++;
}
break;
case BBRX_IGN:
if (bbDebug>22)
printf("pepRxTask(%d): %2.2X (Ignored)\n", link, ch);
break;
}
if (packageComplete)
{
if (rxDpvtHead == NULL)
{
if (bbDebug > 22)
printf("pepRxTask(%d): got unexpected completion status\n", link);
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, NULL, XACT_HIST_STATE_RX_URCMD);
#endif
}
else if (rxDpvtHead == &UselessMsg)
{
rxDpvtHead->status = BB_OK;
if (bbDebug)
{
printf("pepRxTask(%d): msg from node %d unsolicited:\n", link, rxDpvtHead->rxMsg.node);
drvBitBusDumpMsg(&(rxDpvtHead->rxMsg));
}
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, rxDpvtHead, XACT_HIST_STATE_RX_UNSOLICITED);
#endif
}
else
{
rxDpvtHead->status = BB_OK;
if (bbDebug>24)
printf("pepRxTask(%d): RX command byte = %2.2X\n", link, ch);
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, rxDpvtHead, XACT_HIST_STATE_RX);
#endif
if (rxDpvtHead->finishProc != NULL)
{
if (bbDebug>8)
printf("pepRxTask(%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(pBBLink[link]->busyList.sem, WAIT_FOREVER);
(pBBLink[link]->deviceStatus[rxDpvtHead->txMsg.node])++;
listAddTail(&(pBBLink[link]->busyList), rxDpvtHead);
semGive(pBBLink[link]->busyList.sem);
}
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, rxDpvtHead, XACT_HIST_STATE_RX_ABORT);
#endif
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) */
pBBLink[link]->rxAbortAck = 1;
if (bbDebug)
printf("pepRxTask(%d): resetting due to abort status\n", link);
/* wait for link state to become active again */
while (pBBLink[link]->abortFlag != 0)
taskDelay(RESET_POLL_TIME);
if (bbDebug)
printf("pepRxTask(%d): restarting after abort\n", link);
}
}
}
/******************************************************************
* FUNCTION: pepWdTask()
* PURPOSE :
*
* ARGS IN : none
* ARGS OUT: none
* GLOBALS: none
******************************************************************/
STATIC int pepWdTask(int link)
{
struct dpvtBitBusHead *pnode;
struct dpvtBitBusHead *npnode;
unsigned long now;
#ifdef PEP_DO_RESET_AND_OFFLINE
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_OFFLNE 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;
#endif
pBBLink[link]->nukeEm = 0; /* Make sure the nuke status is clear */
while(1) {
/* SLEEP UNTIL WATCHDOG TIMER ROUTINE GIVES SEMAPHORE */
semTake(pBBLink[link]->watchDogSem, WAIT_FOREVER);
now = tickGet(); /* what time is it? */
if (pBBLink[link]->nukeEm != 0)
printf("Bitbus manual reset being issued on link %d\n", link);
if (bbDebug>4)
printf("pepWdTask(%d): (Watchdog) checking busy list\n", link);
pBBLink[link]->rxAbortAck = 0;
pBBLink[link]->txAbortAck = 0;
if (pBBLink[link]->nukeEm != 0) {
/* set abort status and wait for the abort acks */
pBBLink[link]->abortFlag = 1;
/* wake up the Tx task so it can observe the abort status */
semGive(pBBLink[link]->linkEventSem);
/* wake up the Rx task so it can observe the abort status */
semGive(pBBLink[link]->l.PepLink.rxInt);
/* sleep until abort ack from Tx & Rx tasks */
while ((pBBLink[link]->rxAbortAck == 0) &&
(pBBLink[link]->txAbortAck == 0))
taskDelay(RESET_POLL_TIME);
#ifdef BB_SUPER_DEBUG
if (pBBLink[link]->nukeEm < 2)
{ /* Do a history dump since this is not supposed to happen */
BBHistDump(link);
}
#endif
}
/*
* 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(pBBLink[link]->busyList.sem, WAIT_FOREVER);
pnode = pBBLink[link]->busyList.head;
while (pnode != NULL) {
npnode = pnode->next; /* remember where we were in the list */
if ((pBBLink[link]->nukeEm != 0) || (pnode->retire <= now)) {
/* Get rid of the request and set error status etc... */
listDel(&(pBBLink[link]->busyList), pnode);
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, pnode, XACT_HIST_STATE_WD_TIMEOUT);
#endif
if (bbDebug)
{
printf("pepWdTask(%d): TIMEOUT on bitbus message:\n", link);
drvBitBusDumpMsg(&pnode->txMsg);
}
/* BUG -- do this here or defer until RX gets a response? */
(pBBLink[link]->deviceStatus[pnode->txMsg.node])--; /* fix device status */
pnode->status = BB_TIMEOUT;
#ifdef PEP_DO_RESET_AND_OFFLINE
/* 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 */
#endif
/* Make the callbackRequest if one was spec'd */
if(pnode->finishProc != NULL)
{
if (bbDebug>2)
{
printf("pepWdTask(%d): invoking the callbackRequest %p %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 && pBBLink[link]->nukeEm == 0)
semGive(*(pnode->psyncSem));
}
#ifdef PEP_DO_RESET_AND_OFFLINE
/* If we are not going to reboot the link... */
if ( pBBLink[link]->nukeEm == 0 ) {
/* Send out a RAC_RESET/RAC_OFFLINE pair */
semGive(pBBLink[link]->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(pBBLink[link]->queue[BB_Q_HIGH].sem, WAIT_FOREVER);
listAddHead(&(pBBLink[link]->queue[BB_Q_HIGH]), &offlnNode);
/* listAddHead(&(pBBLink[link]->queue[BB_Q_HIGH]), &resetNode); */
semGive(pBBLink[link]->queue[BB_Q_HIGH].sem);
semGive(pBBLink[link]->linkEventSem); /* Tell TxTask to send the messages */
if (semTake(syncSem, (sysClkRateGet()) ) == ERROR)
{
if (bbDebug)
printf("pepWdTask(%d): link dead, trying manual reboot\n", link);
pBBLink[link]->nukeEm = 1;
pBBLink[link]->abortFlag = 1;
semGive(pBBLink[link]->linkEventSem);
semGive(pBBLink[link]->rxInt);
/* sleep until abort ack from Tx & Rx tasks */
while ((pBBLink[link]->rxAbortAck == 0) &&
(pBBLink[link]->txAbortAck == 0))
taskDelay(RESET_POLL_TIME);
}
/* Start over since released the busy list */
semTake(pBBLink[link]->busyList.sem, WAIT_FOREVER);
npnode = pBBLink[link]->busyList.head;
}
#endif
}
pnode = npnode;
}
/* Finish the link reboot if necessary */
if (pBBLink[link]->nukeEm != 0)
{
/* shut down the bitbus card. */
pepReset(link);
if (pBBLink[link]->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.
*/
abort();
}
/* clear the abort_flag */
pBBLink[link]->abortFlag = 0;
pBBLink[link]->nukeEm = 0;
}
if (pBBLink[link]->busyList.head != NULL) {
/* Restart the dog timer */
if (bbDebug>5)
printf("pepWdTask(%d): restarting watch dog timer\n", link);
wdStart(pBBLink[link]->watchDogId, pBBLink[link]->busyList.head->retire - now,
pepTmoHandler, link);
}
semGive(pBBLink[link]->busyList.sem);
/* In case the TX is waiting on a node that just timed out */
semGive(pBBLink[link]->linkEventSem);
}
}
/******************************************************************
* FUNCTION: pepTxTask()
* 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 pepTxTask(int link)
{
struct dpvtBitBusHead *pnode;
int prio;
int working;
int dogStart;
int stuck;
int txTCount;
int txCCount;
unsigned char *txMsg;
register int x;
unsigned long now;
if (bbDebug>1)
printf("pepTxTask started for link %d\n", link);
while(1) {
if (pBBLink[link]->abortFlag != 0) {
/* let the dog know we are ready */
pBBLink[link]->txAbortAck = 1;
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, NULL, XACT_HIST_STATE_TX_ABORT);
#endif
if (bbDebug)
printf("pepTxTask(%d): resetting due to abort status\n", link);
while (pBBLink[link]->abortFlag != 0)
taskDelay(RESET_POLL_TIME); /* wait for link to reset */
if (bbDebug)
printf("pepTxTask(%d): restarting after abort\n", link);
}
else
{
if (pBBLink[link]->DelayCount)
semTake(pBBLink[link]->linkEventSem, 2 * sysClkRateGet());
else
semTake(pBBLink[link]->linkEventSem, WAIT_FOREVER);
}
if (bbDebug>5)
printf("pepTxTask(%d): got an event\n", link);
working = 1;
while ((working != 0) && (pBBLink[link]->abortFlag == 0)) {
working = 0;
prio = BB_NUM_PRIO-1;
while ((prio >= 0) && (pBBLink[link]->abortFlag == 0)) {
/* see if the queue has anything in it */
semTake(pBBLink[link]->queue[prio].sem, WAIT_FOREVER);
if ((pnode = pBBLink[link]->queue[prio].head) != NULL)
{
now = tickGet();
while (pBBLink[link]->deviceStatus[pnode->txMsg.node] == BB_BUSY)
{
if ((pBBLink[link]->syntheticDelay[pnode->txMsg.node] != 0)
&& (pBBLink[link]->syntheticDelay[pnode->txMsg.node] < now))
{
if (bbDebug)
printf("pepTxTask(%d): terminating synthetic idle on node %d\n", link, pnode->txMsg.node);
(pBBLink[link]->deviceStatus[pnode->txMsg.node])--;
pBBLink[link]->syntheticDelay[pnode->txMsg.node] = 0;
pBBLink[link]->DelayCount--;
}
else
{
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(&(pBBLink[link]->queue[prio]), pnode);
semGive(pBBLink[link]->queue[prio].sem);
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX);
#endif
if (bbDebug>3)
printf("pepTxTask(%d): got xact, pnode=%p\n", link, pnode);
/* Send the message in polled mode */
txTCount = pnode->txMsg.length;
/* BUG -- would be nice if we verify the length >6 here */
txCCount = 0;
txMsg = &(pnode->txMsg.length);
while ((txCCount < txTCount -1) && (pBBLink[link]->abortFlag == 0)) {
stuck = 1000;
while (((pBBLink[link]->l.PepLink.bbRegs->stat_ctl & PEP_BB_TFNF)
!= PEP_BB_TFNF) &&
(pBBLink[link]->abortFlag == 0) &&
--stuck)
for(x=0;x<100;x++); /* wait for TX ready */
if (!stuck)
{
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX_STUCK);
#endif
txStuck(link);
}
else if (txCCount == 0) { /* first byte is package type */
if (bbDebug>30)
printf("pepTxTask(%d): outputting %2.2X\n",link,0x81);
pBBLink[link]->l.PepLink.bbRegs->data = 0x81;
}
else if (txCCount == 1) { /* unused 2nd byte of package */
if (bbDebug>30)
printf("pepTxTask(%d): outputting %2.2X\n",link,0x00);
pBBLink[link]->l.PepLink.bbRegs->data = 0x00;
#if 0
} else if (txCCount == (txTCount -1)) { /* last byte of package */
pBBLink[link]->l.PepLink.bbRegs->stat_ctl = *txMsg;
if (bbDebug>30)
printf("pepTxTask(%d): outputting last byte %2.2X\n",
link,*txMsg);
#endif
} else { /* regular ol' message byte */
pBBLink[link]->l.PepLink.bbRegs->data = *txMsg;
if (bbDebug>30)
printf("pepTxTask(%d): outputting %2.2X\n",link,*txMsg);
if (txCCount != 6)
txMsg++;
else
txMsg = pnode->txMsg.data;
}
txCCount++;
}
if (pBBLink[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))
{
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX_RESET);
#endif
if (bbDebug)
printf("pepTxTask(%d): RAC_RESET_SLAVE sent\n", link);
pBBLink[link]->l.PepLink.bbRegs->stat_ctl = *txMsg;
if (bbDebug>30)
printf("pepTxTask(%d): outputting last byte %2.2X\n",
link,*txMsg);
pnode->status = BB_OK;
if (pnode->finishProc != NULL) {
if (bbDebug>4)
printf("pepTxTask(%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(pBBLink[link]->busyList.sem, WAIT_FOREVER);
pBBLink[link]->l.PepLink.bbRegs->stat_ctl = *txMsg;
if (bbDebug>30)
printf("pepTxTask(%d): outputting last byte %2.2X\n",
link,*txMsg);
/* set the retire time */
pnode->retire = tickGet();
if (pnode->ageLimit)
pnode->retire += pnode->ageLimit;
else
pnode->retire += BB_DEFAULT_RETIRE_TIME * sysClkRateGet();
if (pBBLink[link]->busyList.head == NULL)
dogStart = 1;
else
dogStart = 0;
/* Add pnode to the busy list */
listAddTail(&(pBBLink[link]->busyList), pnode);
/* Count the outstanding messages */
(pBBLink[link]->deviceStatus[pnode->txMsg.node])++;
semGive(pBBLink[link]->busyList.sem);
/* If something just added to empty busy list, start the dog */
if (dogStart) {
now = tickGet();
wdStart(pBBLink[link]->watchDogId,
pBBLink[link]->busyList.head->retire - now,
pepTmoHandler, link);
}
}
} else { /* if abortFlag != 0 */
/* Aborted transmission operation, re-queue the message */
semTake(pBBLink[link]->queue[BB_Q_HIGH].sem, WAIT_FOREVER);
listAddHead(&(pBBLink[link]->queue[BB_Q_HIGH]), pnode);
semGive(pBBLink[link]->queue[BB_Q_HIGH].sem);
#ifdef BB_SUPER_DEBUG
BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX_ABORT);
#endif
}
} else { /* if pnode == NULL */
/* we have no xacts that can be processed at this time */
semGive(pBBLink[link]->queue[prio].sem);
}
prio--; /* look at the next prio queue */
}
}
}
}
/******************************************************************
* 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 pepDumpStat(int link)
{
unsigned char stat_ctl;
stat_ctl = pBBLink[link]->l.PepLink.bbRegs->stat_ctl;
printf("stat_ctl reg: %2X\n",stat_ctl);
return(OK);
}