2638 lines
81 KiB
C
2638 lines
81 KiB
C
/* #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);
|
||
}
|