/* * Library for Allen-Bradley #6008-SV VMEbus I/O Scanner * based on examples given in the Allen-Bradley users manual * with kludges added as needed. * */ /* * Author: Bill Brown * Date: as noted in change history * * Copyright 1992, 1993, the Regents of the University of California. * * This software was produced under U.S. Government contract: * (DE-AC03-76SF00098) at the Lawrence Berkeley Laboratory. * * */ #ifndef lint static char rcsid[] = "@(#) $Header: vmeIOScannerLib.c,v 3.7 93/05/06 13:06:02 wbrown Exp $(LBL)"; #endif /* @(#) $Log: vmeIOScannerLib.c,v $ * Revision 3.7 93/05/06 13:06:02 wbrown * added copyright notice * * Revision 3.6 92/07/09 14:06:48 wbrown * Moved to new environment * added call to sysIntEnable() * renamed probe * tested on mv167 * * Revision 3.5 92/04/07 14:21:48 wbrown * added adc support, digital I/O * getAbPlcbit() setAbPlcBit() plcIrqDaemon() plcQueueBTMsg() * createAb1771_IFE_dev() initAb1771_IFE_dev() readAb1771Ife() * adcReader() addAdcToReadList() * * Revision 3.4 92/03/04 13:56:46 wbrown * routine checkin during developement * everything up except "block transfers" (startup, echoBits, etc) * seems to be working. * * Revision 3.3 92/02/24 10:29:54 wbrown * routine checkin during development. * * * Revision 3.2 92/02/03 13:30:46 wbrown * preliminary testing of #1771-IFE adc module, * misc. clean-up. * * Revision 3.1 92/01/15 09:52:48 wbrown * twidged various things to compile in new ($aip_3) * environment * * Revision 3.0 91/12/19 16:44:06 bordua * Baseline for new aip project. * * Revision 1.4 91/12/12 10:42:34 wbrown * added features to deal with SYSFAIL asserted * at "power-on" time. * * * Revision 1.3 91/10/23 13:50:30 wbrown * replaced calls to logMsg() with utility library calls * except at interrupt level. * * Revision 1.2 91/10/15 09:02:43 wbrown * first cut at "beta" version * * Revision 1.1 91/10/11 11:05:18 wbrown * Initial revision * * */ #include "vxWorks.h" #include "intLib.h" #include "68k/iv.h" #include "msgQLib.h" #include "sysLib.h" #include "taskLib.h" #include "types.h" #include "vme.h" #include "wdLib.h" #include "utility_lib.h" #define DEBUG 1 #undef DEBUG #define PLC_SCANNER_DRVR 1 #include "vmeIOScannerLib.h" #include "ab_1771_ifeLib.h" PLC_SCANNER_LINK plcScnrLinkTbl[ MAX_NR_PLC_SCANNERS ]; MSG_Q_ID plcBTMsgQ = NULL; VOID plcIrqService( plcIrqId ) /* * "give" specified semaphore in response to interrupt * */ int plcIrqId; { LINK_ID_FORM linkIdForm; STATUS status; #ifdef DEBUG logMsg("+++ Interrupt received, SEM_ID = 0x%x\n", plcIrqId); #endif linkIdForm.linkIdInt = plcIrqId; { if ( plcBTMsgQ != NULL ) { if ( (status = ( msgQSend( plcBTMsgQ, linkIdForm.linkIdChar, 4, NO_WAIT ) )) != OK ) { logMsg("### msgQSend() returned ERROR\n"); } } else logMsg("### Interrupt received but \"plcBTMsgQ\" = NULL.\n"); } return; } PLC_SCANNER_LINK ( *vmePlcScannerLinkCreate( scannerAddress, scannerAM ) ) /* * * build and initialize vme plc scanner connection * returns pointer to link description structure * or NULL. If pointer is returned, check status in * descriptor structure for status. */ char *scannerAddress; /* address of scanner 2-port menory. */ char *scannerAM; /* address modifier for scanner address */ { u_char tVec; long i, index = -1; long waitCounter; PLC_SCANNER_DUAL_PORT *scanBusAdrs; STATUS status; static SEM_ID plcLinkCreatBusy; VOID plcScannerReset(); static int daemonTID; extern VOID plcIrqDaemon(); #ifdef DEBUG_DUMP extern VOID d(); #endif# DP_MAILBOX setUpImage = { 0, /* 102-03, confirmation status */ SET_UP, /* 104-05, command */ 0, /* 106-07, mod. adrs. (unused) */ 0,0,0,0, /* 108-09, bt_tag (unused) */ 0, /* 10A-0B, RESERVED */ 0, /* 10C-0D, " */ 0, /* 10E-0F, " */ 0, /* 110-11, " */ 0, /* 112-13, " */ 0, /* 114-15, " */ 0, /* 116-17, " */ 0, /* 118-19, " */ 0, /* 11A-1B, " */ (1 << 7), /* 11C-1D, semaphore */ 0, /* 11E-1F, data length (unused) */ #ifdef DO_IT_RIGHT (HI_BAUD_RATE << 8), /* baud rate, watchdog */ (PLC_DBG_ON << 8) + DEF_IRQ_LVL, /* debug mode, irq level */ #else (u_short)0x200, /* 120-21, baud rate; watchdog variable */ (u_short)0x101, /* 122-23, debug mode, irq level */ #endif 0, /* 124-25, interrupt vector goes here */ IGNORE_SYSFAIL, /* 126-27, don't wanna die on SYSFAIL */ }; /* set up interlock first time thru */ status = taskLock(); if ( plcLinkCreatBusy == NULL ) { if ( (plcLinkCreatBusy = semBCreate( SEM_Q_PRIORITY, SEM_FULL )) == NULL ) { ul_fatal("Attempt to create \"plcLinkCreatBusy\" failed."); status = taskUnlock(); return( (PLC_SCANNER_LINK *)ERROR ); } else { ul_debug("\"plcLinkCreatBusy\" created., SEM_ID = 0x%x", plcLinkCreatBusy); } } status = taskUnlock(); if ( status = (semTake( plcLinkCreatBusy, MAX_PLC_SCANNER_BUSY_WAIT * sysClkRateGet() )) == ERROR ) { ul_warn("\"(semTake( plcLinkCreatBusy )\" time out."); return( NULL ); } /* spawn interrupt daemon if not already running */ if ( daemonTID != 0 ) { if ( taskIsSuspended( daemonTID ) ) { status = taskDelete( daemonTID ); } } if ( (daemonTID = taskNameToId( "plcIrqDaemon" )) == ERROR ) { if ( (status = taskSpawn( "plcIrqDaemon", 60, VX_SUPERVISOR_MODE + VX_DEALLOC_STACK + VX_STDIO, 0x10000, plcIrqDaemon )) == ERROR ) { ul_fatal("attempt to spawn \"plcIrqDaemon\" failed, status = 0x%x.", status); } } /* calculate bus address and check if scanner is initialized */ if ( (status = sysBusToLocalAdrs( scannerAM, scannerAddress, &scanBusAdrs )) == ERROR ) { return( (PLC_SCANNER_LINK *)ERROR ); } for ( i = 0; i < MAX_NR_PLC_SCANNERS; i++ ) { if ( ( plcScnrLinkTbl[i].plcScanner2Port == scanBusAdrs ) || ( plcScnrLinkTbl[i].plcScanner2Port == NULL ) ) { index = i; break; } } if ( index == MAX_NR_PLC_SCANNERS ) { ul_critical("\"scanner logical descriptor\" table full."); semGive(plcLinkCreatBusy); return( NULL ); } if ( plcScnrLinkTbl[index].plcScanner2Port == scanBusAdrs ) { /* entry found - re-initialize the hardware */ ul_critical("Scanner at 0x%x is already init. Reset & Re-initializing", scannerAddress); ul_debug("\"index\" = %d", index); } else { /* entry not found - configure table */ plcScnrLinkTbl[index].plcScanner2Port = scanBusAdrs; /* table entry has been allocated */ ul_debug(" index = %d, plcScanner2Port = 0x%x", index, plcScnrLinkTbl[i].plcScanner2Port); /* initialize allocation table */ plcScnrLinkTbl[index].plcResponse = semCCreate( SEM_Q_PRIORITY, SEM_EMPTY ); ul_debug("\"semBCreate\" for \"plcScannerStatus\" returned 0x%x"); if ( plcScnrLinkTbl[index].plcResponse == NULL ) { plcScnrLinkTbl[index].plcScannerStatus = PLC_SEMCREATE_FAIL; semGive(plcLinkCreatBusy); ul_critical("\"semBCreate()\" for \"plcScannerStatus\" failed."); return( &plcScnrLinkTbl[index] ); } else { ul_debug("plcResponse semaphore created = 0x%x", plcScnrLinkTbl[index].plcResponse); } /* allocate interrupt vector */ if ( ( setUpImage.data_area[2] = iVecAlloc( plcIrqService, (int)&plcScnrLinkTbl[index]) ) == NULL ) { ul_critical("Allocation of interrupt vector failed."); plcScnrLinkTbl[index].plcScannerStatus = PLC_IVECALLOC_FAIL; semGive(plcLinkCreatBusy); return( &plcScnrLinkTbl[index] ); } else { /* put vector in EVEN byte */ setUpImage.data_area[2] = setUpImage.data_area[2] << 8; } ul_debug("plcScnrLinkTbl entry completed."); } while ( sysBusTas( scanBusAdrs->mailBox.fl_lock ) != OK ) { if ( waitCounter++ >= (MAX_PLC_SCANNER_BUSY_WAIT * sysClkRateGet()) ) { ul_critical("\"vmePlcScannerLinkCreate()\". PLC interface busy."); return( (PLC_SCANNER_LINK *)ERROR ); } else { taskDelay( 2 ); } } if ( scanBusAdrs->mailBox.conf_stat != SCAN_STAT_SCANNER_PWR_UP ) /* issue a reset to the scanner */ { plcScannerReset( &(plcScnrLinkTbl[index]) ); taskDelay( sysClkRateGet() ); } /* Initialize the Scanner */ semGive(plcLinkCreatBusy); i = 0; while ( scanBusAdrs->mailBox.conf_stat != SCAN_STAT_SCANNER_PWR_UP ) { if ( i++ >= MAX_PLC_SCANNER_BUSY_WAIT ) { ul_critical("Scanner not responding to RESET."); plcScnrLinkTbl[index].plcScannerStatus = PLC_RESET_FAIL; return( NULL ); } taskDelay( sysClkRateGet() ); } ul_debug("Interrupting Scanner for \"wakeup\"."); scanBusAdrs->mailBox.interruptScanner = 0; #ifdef DEBUG_DUMP d( &setUpImage, 32 ); #endif if ( (status = sysIntEnable( setUpImage.data_area[ 1 ] & 0x7 )) != OK ) ul_fatal("Attempt to enable interrupt level failed."); status = sndCmndAndWait4Done( &plcScnrLinkTbl[index], &setUpImage ); #ifdef DEBUG_DUMP d( &setUpImage, 32 ); #endif if ( status == OK ) { return( &plcScnrLinkTbl[index] ); } else { return( NULL ); } } VOID plcScannerReset( linkId ) /* * * issue a RESET to the scanner */ PLC_SCANNER_LINK *linkId; { PLC_SCANNER_DUAL_PORT *hwPtr = linkId->plcScanner2Port; hwPtr->mailBox.data_area[RESET_PREP_ADRS] = RESET_PREP; hwPtr->mailBox.data_area[RESET_XEQ_ADRS] = RESET_XEQ; return; } STATUS plcAutoConfig( linkId, configData ) /* * * autoconfiguer the plc scanner */ PLC_SCANNER_LINK *linkId; DATA_AREA *configData; { long j; PLC_SCANNER_DUAL_PORT *hw2Port; STATUS status; DP_MAILBOX autoConfigImage = { 0, /* 102-03, confirmation status */ AUTOCONFIGURE, /* 104-05, command */ 0, /* 106-07, */ 0,0,0,0, /* 108-09, bt_tag (unused) */ 0, /* 10A-0B, RESERVED */ 0, /* 10C-0D, " */ 0, /* 10E-0F, " */ 0, /* 110-11, " */ 0, /* 112-13, " */ 0, /* 114-15, " */ 0, /* 116-17, " */ 0, /* 118-19, " */ 0, /* 11A-1B, " */ 0x80 /* semaphore */ }; hw2Port = linkId->plcScanner2Port; if ( (status = sndCmndAndWait4Done( linkId, &autoConfigImage )) == OK) { if ( configData != NULL ) { /* copy configuration to application's buffer */ /* for (j = 0; j < sizeof( *configData )/2; j++ ) */ for (j = 0; j < 64; j++ ) { configData->all[ j ] = hw2Port->mailBox.data_area[ j ]; } } return( OK ); } else { return( status ); } } STATUS plcEnableOutput( linkId ) /* * * enable plc scanner outputs */ PLC_SCANNER_LINK *linkId; { STATUS status; DP_MAILBOX plcOutputsEnableImage = { 0, /* 102-03, confirmation status */ SET_MODE, /* 104-05, command */ 0, /* 106-07, */ 0,0,0,0, /* 108-09, bt_tag (unused) */ 0, /* 10A-0B, RESERVED */ 0, /* 10C-0D, " */ 0, /* 10E-0F, " */ 0, /* 110-11, " */ 0, /* 112-13, " */ 0, /* 114-15, " */ 0, /* 116-17, " */ 0, /* 118-19, " */ 0, /* 11A-1B, " */ 0x80, /* 11C-1D, semaphore */ 0, /* 11E-1F, */ MODE_RUN << 8 /* 120-21, mode */ }; if ( (status = ( sndCmndAndWait4Done( linkId, &plcOutputsEnableImage ) )) == OK) linkId->plcScannerState = PLC_STATE_RUN_MODE; return( status ); } /*************************************************************************/ /* Single-bit DIGITAL I/O functions */ /* Allen-Bradley uses the following bit-addressing convention: 16 BITS per GROUP (numbered 0-15 decimal, 0-17 octal, 1-f hex) 8 GROUPs per RACK (numbered 0-7) 8 RACKS per SCANNER (numbered 0-7) We will identify bits by SCANNERID, which will point to the particular scanner, and by BIT_NUMBER which is calculated as BIT_# * GROUP_# * RACK_#. */ int setAbPlcBit( linkId, bitNr, bitState) /* * * set specified output bit to specified state * returns: * 1 = bit set TRUE * 0 = bit set FALSE * -1 = ERROR */ PLC_SCANNER_LINK *linkId; /* points to allocation table entry */ long bitNr, bitState; { int reply = -1; int shiftCount, offset; PLC_SCANNER_DUAL_PORT *hwPtr; if ( (hwPtr = linkId->plcScanner2Port) != NULL ) { offset = bitNr >> 4; if ( offset < 64 ) { shiftCount = bitNr & 0xf; switch ( bitState ) { case 0: { hwPtr->g_oit[ offset] &= ~( 1 << shiftCount ); reply = 0; break; } case 1: { hwPtr->g_oit[ offset] |= ( 1 << shiftCount ); reply = 1; break; } } } } return( reply ); } int getAbPlcbit( linkId, bitNr ) /* * * get state of specified input bit * returns: * 1 = bit was TRUE * 0 = bit was FALSE * -1 = ERROR */ PLC_SCANNER_LINK *linkId; /* points to allocation table entry */ long bitNr; { int reply = -1; int shiftCount, offset; PLC_SCANNER_DUAL_PORT *hwPtr; if ( (hwPtr = linkId->plcScanner2Port) != NULL ) { offset = bitNr >> 4; if ( offset < 64 ) { shiftCount = bitNr & 0xf; reply = ( ( hwPtr->g_iit[ offset] ) >> shiftCount ) & 0x01; } } return( reply ); } /*************************************************************************/ STATUS sndCmnd( linkId, cmndImagePtr ) /* * * send specified command to plc scanner */ PLC_SCANNER_LINK *linkId; /* points to allocation table entry */ DP_MAILBOX *cmndImagePtr; /* points to command image */ { PLC_SCANNER_DUAL_PORT *hwPtr; STATUS status; long j; u_short waitCounter = 0; ul_debug("\"sndCmnd( 0x%x, 0x%x )\" call received.", linkId, cmndImagePtr); if ( linkId == NULL ) { /* NULL implies link not established */ return( ERROR ); } else { hwPtr = linkId->plcScanner2Port; } /* in any case, we need to wait until scanner is available */ while ( sysBusTas( hwPtr->mailBox.fl_lock ) != OK ) { if ( waitCounter++ >= (MAX_PLC_SCANNER_BUSY_WAIT * sysClkRateGet()) ) { linkId->plcScannerStatus = PLC_INTERFACE_BUSY; ul_critical("PLC interface busy."); return( ERROR ); } else { taskDelay( 2 ); } } switch( (int)(cmndImagePtr->command) ) /* save time by handling special cases explicitly */ { case SET_UP: ul_debug(" \"SET_UP\" command recognized."); hwPtr->mailBox.command = cmndImagePtr->command; for ( j = 0; j < 4; j++ ) { hwPtr->mailBox.data_area[j] = cmndImagePtr->data_area[j]; } break; case AUTOCONFIGURE: ul_debug(" \"AUTOCONFIGURE\" command recognized."); hwPtr->mailBox.command = cmndImagePtr->command; break; case SET_MODE: ul_debug(" \"SET_MODE\" command recognized."); hwPtr->mailBox.command = cmndImagePtr->command; hwPtr->mailBox.data_area[0] = cmndImagePtr->data_area[0]; break; default : ul_debug(" \"default\" command recognized."); hwPtr->mailBox.command = cmndImagePtr->command; hwPtr->mailBox.address = cmndImagePtr->address; hwPtr->mailBox.bt_tag = cmndImagePtr->bt_tag; hwPtr->mailBox.data_len = cmndImagePtr->data_len; if ( cmndImagePtr->data_len != 0 ) { for ( j = 0; j < cmndImagePtr->data_len; j++ ) { hwPtr->mailBox.data_area[j] = cmndImagePtr->data_area[j]; } } } ul_debug("Interrupting scanner for \"COMMAND\" = 0x%x.", cmndImagePtr->command); hwPtr->mailBox.interruptScanner = 0; /* interrupt controller */ return( OK ); } STATUS sndCmndAndWait4Done( linkId, cmndImagePtr ) /* * * send specified command to plc scanner and await reply. */ PLC_SCANNER_LINK *linkId; /* points to allocation table entry */ DP_MAILBOX *cmndImagePtr; /* points to command image */ { STATUS status; PLC_SCANNER_DUAL_PORT *hwPtr; ul_debug("\"sndCmndAndWait4Done( 0x%x, 0x%x )\" call received.", linkId, cmndImagePtr); if ( linkId == NULL ) { /* NULL implies link not established */ return( ERROR ); } else { hwPtr = linkId->plcScanner2Port; } if( (status = sndCmnd( linkId, cmndImagePtr )) != OK) ul_fatal("\"sndCmnd( linkId, cmndImagePtr )\" did not return OK"); if ( (status = semTake( linkId->plcResponse, (MAX_PLC_SCANNER_BUSY_WAIT * sysClkRateGet()) )) != OK ) { ul_critical("PLC Timeout waiting for End of Op, \"linkId\" = 0x%x.", linkId); linkId->plcScannerStatus = PLC_NO_REPLY; return( ERROR ); } switch( linkId->plcConfStatus ) { case CONF_STATUS_OK: linkId->plcScannerStatus = PLC_OK; ul_debug("Daemon returned \"PLC_DONE\" semaphore."); return( OK ); break; default: linkId->plcScannerStatus = PLC_ERROR; ul_debug("PLC returned \"ERROR = %x\" status.", hwPtr->mailBox.conf_stat); return( ERROR ); } } PLC_CONFIG_BUFFER configInfo; PLC_SCANNER_LINK ( *startPLC( scannerAddress, scannerAM ) ) /* * start up and enable the plc controller with a generic configuration * */ char *scannerAddress; /* address of scanner 2-port menory. */ char *scannerAM; /* address modifier for scanner address */ { STATUS status; PLC_SCANNER_LINK *linkId; linkId = vmePlcScannerLinkCreate( scannerAddress, scannerAM ); if ( linkId != NULL ) { linkId->cFigBufPtr = &configInfo; if ( (status = plcAutoConfig( linkId, &configInfo )) != OK ) { ul_fatal("### \"plcAutoConfig()\" failed ###"); } if ( (status = plcEnableOutput( linkId )) != OK ) { ul_fatal("### \"plcEnableOutput()\" failed ###"); } ul_info("*** PLC Scanner startup complete. ***"); } else { ul_fatal("### \"vmePlcScannerLinkCreate()\" failed ###"); } return( linkId ); } VOID announceError( linkId) PLC_SCANNER_LINK *linkId; /* * * put all the long-winded error stuff in on place */ { PLC_SCANNER_DUAL_PORT *scannerLoc; scannerLoc = linkId->plcScanner2Port; ul_critical("ERROR - PLC_STATUS = 0x%x, PLC_STATE = 0x%x, CONF_STAT. = 0x%x.", linkId->plcScannerStatus, linkId->plcScannerState, scannerLoc->mailBox.conf_stat); return; } BT_REQUEST *btRequestQueue[ 256 ]; int lastBTID = 0; VOID plcIrqDaemon() /* * Spawned as a task, this function takes care of interrupt responses * to requests. * */ { extern MSG_Q_ID plcBTMsgQ; long j; u_short idTag; LINK_ID_FORM isrMsg; PLC_SCANNER_LINK *linkId; PLC_SCANNER_DUAL_PORT *hwPtr; STATUS status; PLC_STATE scannerState; u_int bytesFromISR; PLC_COMMAND cmndInt; BT_REQUEST *btPtr; extern u_short blocxXferID[]; extern MSG_Q_ID plcBTMsgQ; ul_error_name( "plcIrqDaemon()" ); ul_error_log( UL_FDEBUG, UL_FNONE ); /* on startup, create the message queue, and */ if ( (plcBTMsgQ = msgQCreate( MAX_PLC_MSG_CNT, PLC_MSG_SIZE, MSG_Q_FIFO )) == NULL ) ul_fatal("\"msgQcreate()\", called from \"plcIrqDaemon()\", failed"); /* stand-by to receive messages. */ for ( ; ; ) { bytesFromISR = msgQReceive( plcBTMsgQ, &isrMsg.linkIdChar, PLC_MSG_SIZE, WAIT_FOREVER ); if ( bytesFromISR != PLC_MSG_SIZE ) ul_critical("Incorrect byte count in \"isrMsg\" = %d", bytesFromISR); else { linkId = isrMsg.linkIdPtr; hwPtr = (isrMsg.linkIdPtr)->plcScanner2Port; cmndInt = ( PLC_COMMAND )(hwPtr->mailBox.command); linkId->plcConfStatus = hwPtr->mailBox.conf_stat; switch( cmndInt ) { case SETUP_PLC : { switch( hwPtr->mailBox.conf_stat ) { case CONF_STATUS_OK: linkId->plcScannerStatus = PLC_OK; linkId->plcScannerState = PLC_STATE_LINK_CREATED; hwPtr->mailBox.fl_lock = 0; ul_debug("PLC returned \"DONE\", SETUP_PLC."); semGive( linkId->plcResponse ); break; default: hwPtr->mailBox.fl_lock = 0; announceError( linkId ); break; } break; } case AUTOCONFIG_PLC : { switch( hwPtr->mailBox.conf_stat ) { case CONF_STATUS_OK: linkId->plcScannerStatus = PLC_OK; linkId->plcScannerState = PLC_STATE_CONFIG; hwPtr->mailBox.fl_lock = 0; ul_debug("PLC returned \"DONE\", AUTOCONFIG."); semGive( linkId->plcResponse ); break; case CONF_STATUS_BLOCK_XFER_QUEUED: linkId->plcScannerStatus = PLC_COMMAND_QUEUED; ul_debug ("PLC returned \"QUEUED\" status, AUTOCONFIG."); break; default: hwPtr->mailBox.fl_lock = 0; announceError( linkId ); break; } break; } case SCAN_LIST_PLC : { switch( hwPtr->mailBox.conf_stat ) { case CONF_STATUS_OK: { PLC_CONFIG_BUFFER *cFigPtr; linkId->plcScannerStatus = PLC_OK; linkId->plcScannerState = PLC_STATE_PROG_MODE; if ( linkId->cFigBufPtr != NULL ) { cFigPtr = linkId->cFigBufPtr; /* copy configuration to application's buffer */ for (j = 0; j < 72; j++ ) { cFigPtr->cFigBuffer[ j ] = hwPtr->mailBox.data_area[ j ]; } } hwPtr->mailBox.fl_lock = 0; semGive( linkId->plcResponse ); ul_debug("PLC returned \"DONE\"for CONFIG."); break; } default: hwPtr->mailBox.fl_lock = 0; announceError( linkId ); break; } break; } case SET_MODE_PLC : { switch( hwPtr->mailBox.conf_stat ) { case CONF_STATUS_OK: linkId->plcScannerStatus = PLC_OK; linkId->plcScannerState = PLC_STATE_RUN_MODE; hwPtr->mailBox.fl_lock = 0; ul_debug("PLC returned \"DONE\", SET_MODE."); semGive( linkId->plcResponse ); break; default: hwPtr->mailBox.fl_lock = 0; linkId->plcScannerStatus = PLC_SET_MODE_FAIL, /*linkId->plcScannerState = ; */ announceError( linkId ); break; } break; } case LINK_STATUS_PLC : { switch( hwPtr->mailBox.conf_stat ) { case CONF_STATUS_OK: linkId->plcScannerStatus = PLC_OK; hwPtr->mailBox.fl_lock = 0; ul_debug ("PLC returned \"DONE\" status, LINK_STATUS."); semGive( linkId->plcResponse ); break; default: hwPtr->mailBox.fl_lock = 0; linkId->plcScannerStatus = PLC_LINK_STATUS_ERROR, linkId->plcScannerState = PLC_LINK_STATUS_REQ; announceError( linkId ); break; } break; } case BT_WRITE_PLC: { idTag = hwPtr->mailBox.bt_tag; if ( (btPtr = btRequestQueue[ idTag ]) == NULL ) { ul_critical( "NULL idTag received by \"plcIrqDaemon()\"."); break; } switch ( (int)(hwPtr->mailBox.conf_stat) ) { case CONF_STATUS_OK: case CONF_STATUS_QUEUE_FULL: case CONF_STATUS_XFER_TIMEOUT: { btPtr->confStatWd = hwPtr->mailBox.conf_stat; btRequestQueue[ idTag ] = NULL; /* release queue */ if ( (btPtr->btCompleteSem) != NULL) semGive( btPtr->btCompleteSem ); break; } case CONF_STATUS_BLOCK_XFER_QUEUED: { btPtr->confStatWd = hwPtr->mailBox.conf_stat; break; } default: { btPtr->confStatWd = hwPtr->mailBox.conf_stat; ul_critical( "plc scanner errorcode = 0x%2x, ID = 0x%8x.", hwPtr->mailBox.conf_stat, isrMsg.linkIdPtr); btRequestQueue[ idTag ] = NULL; /* release queue */ break; } break; } break; } case BT_READ_PLC: { idTag = hwPtr->mailBox.bt_tag; if ( (btPtr = btRequestQueue[ idTag ]) == NULL ) { ul_critical( "NULL idTag received by \"plcIrqDaemon()\"."); break; } switch ( (int)(hwPtr->mailBox.conf_stat) ) { case CONF_STATUS_OK: { long dataSize; btPtr->confStatWd = hwPtr->mailBox.conf_stat; btPtr->wordCount = hwPtr->mailBox.data_len; if ( (dataSize = hwPtr->mailBox.data_len) > 64 ) dataSize = 64; for ( j = 0; j < dataSize; j++) btPtr->dataBuffer[ j ] = hwPtr->mailBox.data_area[ j ]; if ( (btPtr->btCompleteSem) != NULL) semGive( btPtr->btCompleteSem ); /* release queue */ btRequestQueue[ idTag ] = NULL; break; } case CONF_STATUS_QUEUE_FULL: case CONF_STATUS_XFER_TIMEOUT: { btPtr->confStatWd = hwPtr->mailBox.conf_stat; if ( (btPtr->btCompleteSem) != NULL) semGive( btPtr->btCompleteSem ); btRequestQueue[ idTag ] = NULL; /* release queue */ break; } case CONF_STATUS_BLOCK_XFER_QUEUED: { btPtr->confStatWd = hwPtr->mailBox.conf_stat; break; } default: { ul_critical( "plc scanner error = 0x%2x, ID = 0x%8x.", hwPtr->mailBox.conf_stat, isrMsg.linkIdPtr); btRequestQueue[ idTag ] = NULL; /* release queue */ break; } break; } break; } default: ul_critical("Non-BLOCK_TRANSFER oper det. in daemon = 0x%x", cmndInt); } hwPtr->mailBox.fl_lock = 0; /* clear interlock semaphore */ } } } STATUS plcQueueBTMsg( btRequest ) /* * * queue a block-transfer request */ BT_REQUEST *btRequest; { PLC_SCANNER_LINK *linkId; PLC_SCANNER_DUAL_PORT *hwPtr; STATUS status = OK; long j, k; long maxBTID = (sizeof( btRequestQueue ))/(sizeof( btRequestQueue[ 0 ] )); u_short waitCounter = 0; ul_debug("\"plcQueueBTMag( 0x%x, )\" call received.", btRequest); if ( (linkId = btRequest->scannerID) != NULL ) { hwPtr = linkId->plcScanner2Port; if ( (btRequest->commandWd == BLOCK_RD) || (btRequest->commandWd == BLOCK_WT) ) { /* in any case, we need to wait until scanner is available */ while ( sysBusTas( hwPtr->mailBox.fl_lock ) != OK ) { if ( waitCounter++ >= (MAX_PLC_SCANNER_BUSY_WAIT * sysClkRateGet()) ) { linkId->plcScannerStatus = PLC_INTERFACE_BUSY; ul_critical("PLC interface busy."); return( ERROR ); } else { taskDelay( 2 ); } } /* pick an ID for the BLOCK TRANSFER */ taskLock(); j = (lastBTID + 1) % maxBTID; while( j != lastBTID ) { if ( btRequestQueue[ j ] == NULL ) break; else j = ( j + 1 ) % maxBTID; } if ( j != lastBTID ) { lastBTID = j; btRequestQueue[ j ] = btRequest; taskUnlock(); /* queue the request to the scanner */ hwPtr = linkId->plcScanner2Port; hwPtr->mailBox.command = btRequest->commandWd; hwPtr->mailBox.address = btRequest->devAdrs; hwPtr->mailBox.bt_tag = (u_short)j; if ( btRequest->commandWd == BLOCK_WT ) { hwPtr->mailBox.data_len = btRequest->wordCount; if ( hwPtr->mailBox.data_len != 0 ) j = hwPtr->mailBox.data_len - 1; else j = 63; for ( ; j >= 0 ; j-- ) hwPtr->mailBox.data_area[ j ] = btRequest->dataBuffer[ j ]; } else hwPtr->mailBox.data_len = 0; /* interrupt controller */ hwPtr->mailBox.interruptScanner = 0; status = OK; } else { taskUnlock(); /* clear interlock semaphore */ hwPtr->mailBox.fl_lock = 0; ul_critical("BLOCK_TRANSFER_ID table currently full."); status = ERROR; } } else { ul_critical ("Non-BLOCK_TRANSFER opcode sent to \"plcQueueBTMsg()\", 0x%x.", btRequest->commandWd); #ifdef DEBUG d( btRequest, 64 ); #endif /* DEBUG */ status = ERROR; } } else { /* NULL implies link not established */ ul_critical("Request with NULL scannerId passed to \"plcQueueBTMsg\""); status = ERROR; } return( status ); /* Daemon takes care of replies. */ } /* **************************************************************** * * * utilities for Allen Bradley # 1771-IFE A.D.C. module * * * **************************************************************** */ PLC_BT_DEV_DESC ( *createAb1771_IFE_dev( scannerId, devDesc ) ) /* * * initialize device descriptor and return device identifier */ PLC_SCANNER_LINK *scannerId; AB_1771_IFE_TBL *devDesc; { STATUS status; PLC_BT_DEV_DESC *newDescriptor; if ( scannerId->plcScannerState != PLC_STATE_RUN_MODE ) { ul_critical ("BLOCK_XFER req. via un-init. scanner, \"createAb1771-IFE_dev\""); return( (PLC_BT_DEV_DESC *) ERROR ); } if ( (newDescriptor = (PLC_BT_DEV_DESC *)(malloc( sizeof( *newDescriptor )) )) == NULL ) { ul_critical ("\"malloc()\" called by \"*createAb1771-IFE_dev()\" returned NULL."); return( (PLC_BT_DEV_DESC *) ERROR ); } newDescriptor->scannerID = scannerId; newDescriptor->operStatus = PLC_BT_UNINIT; newDescriptor->btDevType = AB_1771_IFE; newDescriptor->devSpecStruc = (char *)devDesc; if ( (newDescriptor->adcBtDone = semBCreate( SEM_Q_FIFO, SEM_EMPTY )) == NULL ) ul_critical("cannot create semaphore for BT device." ); return( newDescriptor ); } STATUS initAb1771_IFE_dev( devTblPtr ) /* * * initialize the A.D.C. hardware * NOTE: At this time, the commands necessary to take advantage * of the adcs' built in scaling feature have not been * implemented. */ PLC_BT_DEV_DESC *devTblPtr; { long j, jLimit; STATUS status; AB_1771_IFE_TBL *cfigTblPtr; PLC_SCANNER_LINK *scnIdent; BT_REQUEST initMsg; /* build initialization message here */ cfigTblPtr = (AB_1771_IFE_TBL *)(devTblPtr->devSpecStruc); scnIdent = devTblPtr->scannerID; /* build the initialization message */ /* first, the boiler-plate */ initMsg.scannerID = devTblPtr->scannerID; initMsg.btCompleteSem = devTblPtr->adcBtDone; initMsg.commandWd = BLOCK_WT; initMsg.devAdrs = cfigTblPtr->adcPlcSdrs; initMsg.wordCount = 3; /* set to 3 - no scaling */ /* now build the adc configuration */ /* Range Selection */ initMsg.dataBuffer[ 0 ] = 0; initMsg.dataBuffer[ 1 ] = 0; for ( j = 0; j < 8; j++ ) initMsg.dataBuffer[ 0 ] = initMsg.dataBuffer[ 0 ] | (cfigTblPtr->adcRange[ j ] << ( 2 * j) ); if ( cfigTblPtr->inputConfig == SINGLE_ENDED ) { for ( j = 8; j < 16; j++ ) initMsg.dataBuffer[ 1 ] = initMsg.dataBuffer[ 1 ] | (cfigTblPtr->adcRange[ j ] << 2); } /* sampling, data format, input type, digital filter */ initMsg.dataBuffer[ 2 ] = 0 | (u_short)( cfigTblPtr->adcSmplRate << 11 | cfigTblPtr->adcDataFmt << 9 | cfigTblPtr->inputConfig << 8 | cfigTblPtr->abAdcDigFiltTC); #ifdef AUTO_SCALING_USED /* Sign bits for MinimumMaximum scaling values */ /* Scale values */ /* initialize the hardware. */ #endif /* AUTO_SCALING_USED */ if ( (status = plcQueueBTMsg( &initMsg )) == OK ) { if ( (status = semTake( initMsg.btCompleteSem, sysClkRateGet() * 6 )) != OK ) /* if ( (status = semTake( initMsg, WAIT_FOREVER ) != OK ) ) */ { ul_critical("attempt to initialize ADC timed out."); } } else { ul_critical("attempt to initialize ADC failed"); } return( status ); } #define ADC_STUFF_DUN 1 #ifdef ADC_STUFF_DUN STATUS readAb1771Ife( devTblPtr ) /* * * read adc, put values in specified array of structures. */ PLC_BT_DEV_DESC *devTblPtr; { long j; STATUS status = OK; AB_1771_IFE_TBL *specificDevTblPtr; PLC_SCANNER_LINK *linkId; BT_REQUEST reqAdcReadMsg; /* build read request message here */ AB_ADC_DATA *usrDataBufPtr; if ( devTblPtr->btDevType == AB_1771_IFE) { linkId = devTblPtr->scannerID; if ( linkId->plcScannerState == PLC_STATE_RUN_MODE) { specificDevTblPtr = (AB_1771_IFE_TBL * )(devTblPtr->devSpecStruc); reqAdcReadMsg.scannerID = devTblPtr->scannerID; reqAdcReadMsg.btCompleteSem = devTblPtr->adcBtDone; reqAdcReadMsg.commandWd = BLOCK_RD; reqAdcReadMsg.devAdrs = specificDevTblPtr->adcPlcSdrs; if ( (status = plcQueueBTMsg( &reqAdcReadMsg )) == OK ) { if ( (status = semTake( reqAdcReadMsg.btCompleteSem, sysClkRateGet() * 6 )) == OK ) { /* untangle values and flags from returned message */ specificDevTblPtr->diagnostics.all = reqAdcReadMsg.dataBuffer[ 0 ]; usrDataBufPtr = specificDevTblPtr->abAdcDataBuffP; for ( j = 0; j < reqAdcReadMsg.wordCount; j++ ) { usrDataBufPtr[ j ].underRange = ( reqAdcReadMsg.dataBuffer[ 1 ] >> j) & 0x1; usrDataBufPtr[ j ].overRange = ( reqAdcReadMsg.dataBuffer[ 2 ] >> j) & 0x1; usrDataBufPtr[ j ].sign = ( reqAdcReadMsg.dataBuffer[ 3 ] >> j) & 0x1; usrDataBufPtr[ j ].adcValue = reqAdcReadMsg.dataBuffer[ j + 4 ]; if ( specificDevTblPtr->adcDataFmt == DATA_FMT_2s_COMP ) { usrDataBufPtr[ j ].adcValue = ( usrDataBufPtr[ j ].adcValue << 3 ); } } } else { ul_critical("attempt to read ADC timed out."); status = ERROR; } } else { ul_critical("attempt to read ADC failed"); status = ERROR; } } else { ul_critical( "\"readAb1771Ife()\", scanner not in \"PLC_STATE_RUN_MODE\""); status = ERROR; } } else { ul_critical("call to \"readAb1771Ife()\" specifies bad device type"); status = ERROR; } return( status ); } PLC_BT_DEV_DESC *adcSvcList[ MAX_NR_ADC ]; VOID adcReader( readPeriod ) /* * * spawn as a task to periodically read A-B 1771-IFE adc from list */ int readPeriod; /* period in seconds */ { int lastReadTime, nextReadDelay; long j; STATUS status; ul_error_name( "adcReader()" ); ul_error_log( UL_FDEBUG, UL_FNONE ); for ( ; ; ) { lastReadTime = (int)tickGet(); for ( j = 0; j < MAX_NR_ADC; j++ ) { if ( adcSvcList[ j ] != NULL ) { if ( (status = readAb1771Ife( adcSvcList[ j ] )) == ERROR) { ul_critical("\"readAb1771Ife( 0x%x )\" = ERROR.", adcSvcList[ j ]); } } } nextReadDelay = ( sysClkRateGet() * readPeriod ) - ( (int)tickGet() - lastReadTime); status = taskDelay( nextReadDelay ); } } STATUS addAdcToReadList( adcDescriptPtr ) /* * * Add an ADC to the list of adc's to be scanned. * * if the reader is not already running, spawn it. * * if pointer to adc descriptor is NULL, simply check reader, spawn * if necessary, and exit. */ PLC_BT_DEV_DESC *adcDescriptPtr; { BOOL readerRunning; char *adcRdTaskName = "adcRdr\0"; int adcReaderTID; long j; STATUS status; /* if pointer to adc descriptor != null, add it to list */ if ( adcDescriptPtr != NULL ) { status = ERROR; if ( adcDescriptPtr->btDevType == AB_1771_IFE ) { for ( j = 0; j < MAX_NR_ADC; j++ ) { if ( adcSvcList[ j ] == adcDescriptPtr ) { ul_critical( "attempt to re-install adcID = 0x%x in reader list", adcDescriptPtr); break; } if ( adcSvcList[ j ] == NULL ) { adcSvcList[ j ] = adcDescriptPtr; status = OK; ul_info("adcID = 0x%x installed in reader list."); break; } } if ( j == MAX_NR_ADC ) ul_critical("adc reader list overflow"); } else ul_critical( "ptr to wrong device type passed to \"addAdcToReadList()\""); } /* if reader is not running, start it up */ if( (adcReaderTID = taskNameToId( "adcRdr" )) != ERROR ) { if ( (readerRunning = taskIsReady( adcReaderTID )) == FALSE ) { adcReaderTID = ERROR; if ( (status = taskDelete( adcReaderTID )) != OK ) ul_fatal("attempt to delete stalled adc reader failed"); else ul_critical("stalled adc reader task deleted"); } } if ( adcReaderTID == ERROR ) { if ( (adcReaderTID = taskSpawn( "adcRdr", ADC_RDR_PRIORITY, VX_FP_TASK + VX_DEALLOC_STACK + VX_STDIO, 0x18000, adcReader, ADC_READ_PERIOD, 0, 0, 0, 0, 0, 0, 0, 0, 0 )) == ERROR ) ul_fatal("attempt to spawn adc reader failed"); else { ul_info( "adc reader spawned"); status = OK; } } return( status ); } #endif