Files
pcas/src/drv/drvAb.c
1995-02-27 15:35:38 +00:00

2506 lines
79 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* drvAb.c - Driver Support Routines for Allen Bradley */
/* base/src/drv $Id$ */
/*
* routines that are used, below the ai, ao, bi and bo drivers to interface
* to the Allen-Bradley Remote Serial IO
*
* Author: Bob Dalesio
* Date: 6-21-88
*
* 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
*
* Notes:
* 1. This driver uses Asynchronous Communications to the AB scanner
* The manual description for this type of interface is
* misleading in the area of interrupt handling.
* 2. We are using double slot addressing as the ab1771il card only works
* in this addressing mode. This dictates that analog io cards alternate
* between input and output. Therefore there are 6 input slots and
* 6 output slots per adapter. The binaries may be placed in either slot.
* Slots 1,3,5,7,9,11 are analog input slots
* Slots 2,4,6,8,10,12 are analog output slots
* The database uses card numbers 0-11 to address slots 1-12
* 3. The binary io is memory mapped and the analog io is handled through
* block transfers of whole cards of data.
* 4. The timing rates for the analog inputs should not be made lower as
* the cards will not respond to the AB scanner any faster.
*
* Modification Log:
* -----------------
* .01 09-15-88 lrd only spawn tasks if Allen-Bradley IO is present
* .02 09-15-88 lrd use the ##open(name,flags,mode)## circuit detect
* from the 6008 for read failure indication
* .03 09-15-88 lrd documentation for data and command flow
* .04 12-18-88 lrd fix the conversions
* .05 02-01-89 lrd changed vxTas to sysBusTas!!!!!!!!!!!!
* delete tasks before spawning again
* add 1771IFE card support
* .06 02-10-89 lrd read OFE at initialization
* .07 02-24-89 lrd modify for vxWorks 4.0
* change spawn to taskSpawn and rearranged args
* change sysSetBCL to sysIntEnable
* move task data to task_params.h
* .08 03-10-89 lrd keep the scan list at initialization
* .09 03-14-89 lrd move interrupt enable to ioc_init
* .10 03-17-89 lrd modify link_init for restart
* provide read routines for outputs
* .11 03-23-89 lrd fixed ab_bidriver address not even numbers only
* and the mask and pvalue args were switched
* .12 03-25-89 lrd routines for the database to read the outputs
* at initialization
* .13 04-22-89 lrd implement binary IO to ignore addressing mode
* implement more than one serial link
* .14 04-24-89 lrd have one outstanding attempt for initialization
* .15 04-26-89 lrd removed card type checking for binary IO
* as the binaries are memory mapped into a
* seperate area
* .16 04-27-89 lrd recover from IO failures and pass the failure
* status back to the database
* .17 04-28-89 lrd fleshed out timeout counters and added check
* for conversion errors on IL and IFE modules
* .18 05-15-89 lrd took out scaling error check and made thre
* consecutive timeouts before a change of status
* .19 08-01-89 lrd changed mr_wait and bt_queue to always set
* length to 0 for compatibility with PLC-5
* communication
* .20 09-29-89 ba/lrd changed the interrupt routine to remove response
* data from the dual port memory and queueing the
* entire response
* .21 09-29-89 lrd add scan_list command and support to build the
* scan list according to the database thus polling
* racks that may not have been powered at
* initialization
* .22 09-29-89 lrd add more error checking
* .23 10-17-89 cbf modified the way interrupts are dispatched in
* ab_intr
* .24 10-17-89 cbf added logic to avoid queueing requests to cards
* which already had a request pending to avoid
* overflowing scanner's queue when I/O card is missing
* .25 10-17-89 cbf added code to grab the operating status word
* in the interrupt routine to determine when the
* status of any scanned adapter has changed
* .26 10-17-89 cbf added logic to see if the scanner was using the
* dual port when we had it locked. it is!
* ****************************************************************************
* * Many of the above were needed to work around problems in the *
* * 6008-SV firmware. These problems have been reported to Allen- *
* * Bradley and fixes are promised. The PROMs we are presently using *
* * are both labeled "91332-501". U63 chksum = A7A1. U64 chksum = 912B *
* * (Series A, Revision C.) *
* ****************************************************************************
* .27 12-11-89 cbf new PROMs implemented which allow output image table
* to be preserved during SYSTEM RESET. This driver
* is still compatible with the old firmware also.
* A piece of code was added to store the firmware
* revision level in array ab_firmware_info.
* .28 12-11-89 cbf With the new firmware, the scanner no longer uses
* the dual port when we have it locked so the tests
* for this condition have been removed.
* .29 01-23-90 cbf In the previous rev of this driver we made an attempt
* to build a scan list automatically by waiting until
* the periodic scan task had asked for data from each
* needed adapter. This scheme caused problems with the
* the initialization of BOs and AOs, because they were
* not initially in the scan list and so could not be read
* at initialization. In this rev, that code has been
* removed and the scan list is always built to include all
* eight possible adapters. This has some performance
* implications because each non-existant adapter which
* appears in the scan list adds an extra 5 ms to each
* I/O scan time for the 6008SV. The decreased performance
* does not appear to be very significant, however. The
* 6008SV front panel "SER" LED blinks if not all adapters
* in the scan list are responding. With this rev of the
* driver, the light will therefore be blinking.
* .30 01-30-90 lrd add plc/adapter distinction
* .31 05-24-90 mk added 4-20Ma IFE support
* .32 05-25-90 jcr added PLC readback support
* .33 07-03-90 lrd fixed PLC readback verification
* .34 07-19-90 mk/lrd fixed the overrange indications (<< -> <<=)
* .35 11-02-90 lrd initialize the adapter buffers whether or not
* the link is there for detection by the bi and
* bo driver interface to the database library
* .36 11-27-90 lrd add support for the 16 BI and BO cards
* .37 12-19-90 lrd added 0-5V IFE support
*****************************************************************************************
** Allen-Bradley has given us a new firmware revision. This revision allows the reset *
** of the scanner without using the sysReset on the VME backplane. It also maintains *
** the output image table for the binary outputs. This reset is used to recover from *
** a scanner fault, where the 6008 firmware deadends - ab_reset provides this function. *
************************************************************************************************
* .38 03-07-91 lrd modify ab_driver_init to not reinitialize semaphores and interrupts
* if this is not the first invocation
* .39 03-07-91 lrd modify the ab_aodriver to only set the update bit if the new value
* is different than the old value
* .40 03-08-91 lrd fix ab_boread to verify card is present
* .41 05-16-91 lrd have the ab_scan_task driver analog outputs
* periodically as well as on change
* .42 08-06-91 rac remove include for alarm.h--it's not used
* .43 09-11-91 joh updated for v5 vxWorks
* .44 11-16-91 bg moved io_report for Allen Bradley to this
* module. Broke io_report up into subroutines.
* Added sysBusToLocalAdrs and SysIntEnable
* .45 1-13-92 bg added level to io_report and added the
* ability to print raw
* values if the level is > 0 .
* .46 05-22-92 lrd added the task that monitors the binary
* inputs and simulates a change
* of state interrupt - wakes up the io event
* scanner
* .47 06-10-92 bg combined drvAb.c and ab_driver.c
* .48 06-26-92 bg added level to the ab_io_report in
* the drvSup structure
* .49 06-29-92 joh removed FILE pointer argument to io report
* .50 06-29-92 joh moved ab reset here
* .51 07-10-92 lrd mode interrupt on change of state to scan once on initialization
* .52 08-11-92 joh io report format cleanup
* .53 08-25-92 mrk made masks a macro
* .54 08-25-92 mrk added support for Type E,T,R,S Tcs
* .55 08-25-92 mrk support epics I/O event scan
* .56 06-30-93 mrk After 3 attempts to queue request Ask to initialize
* .57 07-22-93 mrk For BI call scanIoRequest when adapter status changes
* .58 07-22-93 mrk For AB1771IL sign_bit>>= becomes sign_bit<<=
* .59 07-27-93 mrk Included changes made by Jeff Hill to stop warning messages
* .60 07-27-93 mrk Made changes for vxWorks 5.x semLib
* .61 08-02-93 mrk Added call to taskwdInsert
* .62 08-04-93 mgb Removed V5/V4 and EPICS_V2 conditionals
* .62 09-04-93 mrk for bo and ao change value even if down
* .63 09-15-93 mrk make report shorter.
* .64 09-16-93 mrk ab_reset: all links; only reset scanner.
* .65 05-05-94 kornke IL Differential Fix
*/
/*
* Binary Input Code Portions:
*
* process_bi
* |
* |
* |
* V
* ab_bidriver
* | ^
* | mark card | data
* | as BI |
* V |
* ab_config AB dual ported memory
* ^
* |
* |
* |
* AB 6008 scanner card
*/
/*
* Binary Output Code Portions:
*
* process_bo
* |
* |
* |
* V
* ab_bodriver
* | |
* | mark card | data
* | as BO |
* V V
* ab_config AB dual ported memory
* |
* |
* |
* V
* AB 6008 scanner card
*/
/*
* Analog Input Code Portions:
*
* process_ai
* |
* V
* ab_aidriver
* | ^
* | mark card |
* | as AI | data
* V |
* ab_config ab_btdata
* | ^
* | cards | data
* | present | returned
* V |
* abScanTask abDoneTask
* | ^
* | commands | data
* V |
* bt_queue bt_done
* | ^
* | commands | data
* V |
* AB dual ported memory
* | ^
* | commands | data
* V |
* AB 6008 scanner card
*/
/*
* Analog Output Code Portions:
*
* process_ao
* |
* V
* ab_aidriver
* | |
* | mark card |
* | as AI | data
* V V
* ab_config ab_btdata
* | |
* | cards | data
* | present | returned
* V V
* abScanTask
* |
* | command/data
* V
* bt_queue bt_done
* | ^
* | command/data | acknowledge
* V |
* AB dual ported memory
* | ^
* | command/data | acknowledge
* V |
* AB 6008 scanner card
*/
#include <vxWorks.h>
#include <sysLib.h> /* library for task support */
#include <semLib.h> /* library for semaphore support */
#include <vme.h>
#include <rebootLib.h>
#include <wdLib.h> /* library for watchdog timer support */
#include <rngLib.h> /* library for ring buffer support */
#include <task_params.h>
#include <stdioLib.h>
#include <dbDefs.h>
#include <drvSup.h>
#include <taskwd.h>
#include <module_types.h>
#include <drvAb.h>
/* AllenBradley serial link and ai,ao,bi and bo cards */
#define LOCK -2
/* AllenBradley serial link and ai,ao,bi and bo cards */
/* If any of the following does not exist replace it with #define <> NULL */
static long report();
static long init();
struct {
long number;
DRVSUPFUN report;
DRVSUPFUN init;
} drvAb={
2,
report,
init};
static long report(level)
int level;
{
return(ab_io_report(level));
}
/* forward reference for ab_reboot_hook */
int ab_reboot_hook();
static long init()
{
return(ab_driver_init());
}
short ab_disable=0;
/*jcr */
/* response queue */
struct ab_response{
unsigned short status;
unsigned short link;
unsigned short adapter;
unsigned short card;
unsigned short command;
unsigned short data[64];
};
/* internal timeout variables */
short ab_tout[AB_MAX_LINKS]; /* time out flag */
WDOG_ID wd_id[AB_MAX_LINKS]; /* watchdog task ID */
int ab_timeout(); /* internal timeout handler */
/* message complete variables */
SEM_ID ab_data_sem; /* transfer complete semaphore */
SEM_ID ab_cmd_sem; /* transfer complete semaphore */
/* following flag indicates to interrupt handler that we're
requesting a block xfer (and expect immediate confirmation) */
short ab_requesting_bt[AB_MAX_LINKS];
LOCAL RING_ID ab_cmd_q; /* links ready to be read */
int ab_intr (); /* interrupt service routine */
#define AB_Q_SZ 32*sizeof(struct ab_response) /* AB command Q size */
/*
* The configuration table contains a word to describe each card
* in each adapter. The word contains the following data:
* 0x0000 - not initialized
* 0xffff - no card present
* 0x8000 - initialized
* 0x4000 - updated data flag (used for sending analog outputs)
* 0x2000 - real time scan initialized
* 0x1000 - adapter/plc flag
* 0 - adapter
* 1 - PLC
* 0x0f00 - conversion
* For IXE
* 0 - no conversion (use millivolt range)
* 1 - linear (use millivolt range)
* 2 - K_DGF
* 3 - K_DGC
* 4 - J_DGF
* 5 - J_DGC
* 6 - E_DGF
* 7 - E_DGC
* 8 - T_DGF
* 9 - T_DGC
* 10- R_DGF
* 11- R_DGC
* 12- S_DGF
* 13- S_DGC
* For Ir
* 0- degF
* 1- degC
* 0x00e0 - interface type
* 0 - Not Assigned
* 1 - Binary Input
* 2 - Binary Output
* 3 - Analog Input
* 4 - Analog Output
* 0x001f - the Allen-Bradley card type (from ~gta/h/module_types.h)
* unique only per interface type
*/
unsigned short ab_config[AB_MAX_LINKS][AB_MAX_ADAPTERS][AB_MAX_CARDS];
/*
* Allen-Bradley Raw Data read through the abScanTask. This table
* will contain the analog inputs read using block transfers. It
* is also where analog outputs are placed to affect a change in an
* analog output signal Binary cards read and write directly into the
* Allen-Bradley dual ported memory
*/
short ab_btdata[AB_MAX_LINKS][AB_MAX_ADAPTERS][AB_MAX_CARDS][AB_CHAN_CARD];
/* array of pointers to the Allen-Bradley 6008 serial interface cards present */
struct ab_region *p6008s[AB_MAX_LINKS];
/* Allen-Bradley Block Transfer Task */
int abDoneId; /* id of the Allen-Bradley io complete task */
int abCOSId; /* id of the Allen-Bradley binary input change of state scan wakeup */
int abScanId; /* id of the Allen-Bradley scan task */
/* Timeout counters for outstanding data requests */
/* The driver has to be smart about when the data requests are made because */
/* the Allen-Bradley IO will hold up all requests if an IO card isn't ready */
/* to talk. The thermocouple input card has a max scan rate of .5 second and */
/* if it isn't ready when a request is made, all proceeding block transfers */
/* will be held up. This table is used so that requests are only made at */
/* a rate that the card can accept. */
/* Some of the unpleasant side affects of making this number too small are: */
/* 1. the interrupt for command done comes when the dual port memory is */
/* unlocked - so you have to wait for the lock flag in a loop */
/* 2. timeouts from the scanner occur rendering the data useless */
/* Note that this problem increases exponentially */
/* scan task rate is .1 second */
short ab_timers[AB_MAX_LINKS][AB_MAX_ADAPTERS][AB_MAX_CARDS];
#define AB_IXE_RATE 5 /* .5 seconds */
#define AB_IL_RATE 4 /* .4 seconds */
#define AB_IR_RATE 5 /* .5 seconds */
#define AB_IFE_RATE 1 /* .1 seconds */
#define AB_OFE_RATE 10 /* 1 seconds - written immediately */
#define AB_INT_LEVEL 5
/* status on the Allen-Bradley driver interface */
/* got back read/write info instead of cmd response */
short ab_bad_response[AB_MAX_LINKS];
/* got back cmd info instead of read/write response */
short ab_rw_resp_err[AB_MAX_LINKS];
/* watchdog timeouts through our internal timer */
short ab_comm_to[AB_MAX_LINKS];
/* data transfer timed out */
short ab_data_to[AB_MAX_LINKS][AB_MAX_ADAPTERS][AB_MAX_CARDS];
/* data xfer timeout detected through the AB scanner */
short ab_cmd_to[AB_MAX_LINKS][AB_MAX_ADAPTERS][AB_MAX_CARDS];
/* link status and scan list command timeouts */
short ab_link_to[AB_MAX_LINKS];
/* under range scaling error returned through block xfer */
short ab_scaling_error[AB_MAX_LINKS][AB_MAX_ADAPTERS][AB_MAX_CARDS];
/* over range scaling error returned through block xfer */
short ab_or_scaling_error[AB_MAX_LINKS][AB_MAX_ADAPTERS][AB_MAX_CARDS];
/* awakened but dual ported memory was unlocked */
short ab_post_no_lock[AB_MAX_LINKS];
/* no. time dpm locked when we wanted to talk */
short ab_not_ready[AB_MAX_LINKS];
/* card status 0 - good -1 - bad */
char ab_btsts[AB_MAX_LINKS][AB_MAX_ADAPTERS][AB_MAX_CARDS][AB_CHAN_CARD];
/* scanner operational status word */
short ab_op_stat[AB_MAX_LINKS];
/* Keeps track of how many times we've asked for data
before we actually receive it for a given card. Keeps
us from asking for data faster than card can supply it */
unsigned short ab_btq_cnt[AB_MAX_LINKS][AB_MAX_ADAPTERS][AB_MAX_CARDS];
#include <dbScan.h>
static IOSCANPVT ioscanpvt[AB_MAX_LINKS][AB_MAX_ADAPTERS][AB_MAX_CARDS];
/*
* flags a communication error on a link status
* which in turn flags all channels on the adapter as hardware errors
*/
short ab_adapter_status[AB_MAX_LINKS][AB_MAX_ADAPTERS];
short ab_adapter_status_change[AB_MAX_LINKS][AB_MAX_ADAPTERS];
/* disables the scanner during a restart - required for */
/* successful restart of the IOC */
/* scan list management variables */
/* these are used to build and reset the scan list according to the */
/* database */
/* scan list built from the adapters_present array */
char ab_scan_list[AB_MAX_LINKS][AB_MAX_ADAPTERS];
/* scan attempts while waiting for initialization */
char ab_init_cnt[AB_MAX_LINKS][AB_MAX_ADAPTERS][AB_MAX_CARDS];
/* debug variable (must be >1 to see all messages) */
int ab_debug = 0;
/* location to store scanner firmware revision info which is avail only temporarily */
char ab_firmware_info[AB_MAX_LINKS][96];
static char *ab_stdaddr;
/*
*
* support for ab reset
*
*
*/
unsigned short ab_reset_wait;
unsigned short reset_code[100];
unsigned short reset_cnt = 0;
/* forward references */
static void wtrans();
int abScanTask();
void ab_reset_task();
int ab_reset();
typedef enum{abbtSuccess,abbtCardUsed,abbtBusy,
abbtTimeout,abbtLinkDown,abbtError} abbtStatus;
/*definitions for BT_READ and BT_WRITE */
typedef struct {
void (*callback)();
unsigned short *pbuffer;
abbtStatus status;
unsigned short nwords;
void *userPvt;
} ABBTREQUEST;
ABBTREQUEST *pabbtrequest[AB_MAX_LINKS][AB_MAX_ADAPTERS][AB_MAX_CARDS];
static void abBtCallback(link,adapter,card)
unsigned short link;
unsigned short adapter;
unsigned short card;
{
ABBTREQUEST *preq=pabbtrequest[link][adapter][card];
unsigned short *pcard = &ab_config[link][adapter][card];
if(!preq ) {
printf("Allen Bradley: abBtCallback Logic Error");
return;
}
pabbtrequest[link][adapter][card] = NULL;
pcard = &ab_config[link][adapter][card];
*pcard &= ~(AB_INTERFACE_TYPE|AB_INIT_BIT|AB_SENT_INIT);
(preq->callback)(preq);
}
int ab_bt_read(link,adapter,card,preq)
unsigned short link;
unsigned short adapter;
unsigned short card;
ABBTREQUEST *preq;
{
/* pointer to the Allen-Bradley configuration table */
unsigned short *pcard = &ab_config[link][adapter][card];
abbtStatus status;
/* If card is initialized then error */
if (*pcard & AB_INTERFACE_TYPE) {
if(((*pcard & AB_INTERFACE_TYPE)==AB_BT_READ)
|| ((*pcard & AB_INTERFACE_TYPE)==AB_BT_WRITE)) {
status = abbtBusy;
} else {
status = abbtCardUsed;
}
return(status);
}
if(ab_adapter_status[link][adapter]) return(abbtLinkDown);
pabbtrequest[link][adapter][card] = preq;
ab_btq_cnt[link][adapter][card] = 0;
*pcard |= AB_BT_READ | AB_INIT_BIT;
return(abbtSuccess);
}
int ab_bt_write(link,adapter,card,preq)
unsigned short link;
unsigned short adapter;
unsigned short card;
ABBTREQUEST *preq;
{
/* pointer to the Allen-Bradley configuration table */
unsigned short *pcard = &ab_config[link][adapter][card];
abbtStatus status;
/* If card is initialized then error */
if (*pcard & AB_INTERFACE_TYPE) {
if(((*pcard & AB_INTERFACE_TYPE)==AB_BT_READ)
|| ((*pcard & AB_INTERFACE_TYPE)==AB_BT_WRITE)) {
status = abbtBusy;
} else {
status = abbtCardUsed;
}
return(status);
}
if(ab_adapter_status[link][adapter]) return(abbtLinkDown);
ab_btq_cnt[link][adapter][card] = 0;
pabbtrequest[link][adapter][card] = preq;
*pcard |= AB_BT_WRITE | AB_INIT_BIT;
return(abbtSuccess);
}
/*
* READ_AB_ADAPTER
*
* read an adapter of AB IO
*/
void read_ab_adapter(link,adapter,pass)
register unsigned short link;
register unsigned short adapter;
register short pass;
{
register unsigned short card;
register unsigned short *pcard;
short btq_err;
short msg[64];
/* each card */
for (card = 0, pcard = &ab_config[link][adapter][0];
card < AB_MAX_CARDS;
card++, pcard++){
if (*pcard & AB_INTERFACE_TYPE){
if((*pcard & AB_INTERFACE_TYPE)==AB_BT_READ) {
ABBTREQUEST *preq = pabbtrequest[link][adapter][card];
abbtStatus status = abbtSuccess;
unsigned short count;
count = ab_btq_cnt[link][adapter][card]++ ;
if(count>1 && count<50) continue;
if(count>=50) status = abbtTimeout;
else if(bt_queue(AB_READ,link,adapter,card,
preq->nwords,preq->pbuffer)!=OK) status = abbtError;
preq->status = status;
if(status) abBtCallback(link,adapter,card);
continue;
}
if((*pcard & AB_INTERFACE_TYPE)==AB_BT_WRITE) {
ABBTREQUEST *preq = pabbtrequest[link][adapter][card];
abbtStatus status = abbtSuccess;
unsigned short count;
ab_btq_cnt[link][adapter][card]++;
count = ab_btq_cnt[link][adapter][card]++ ;
if(count>1 && count<50) continue;
if(count>=50) status = abbtTimeout;
else if(bt_queue(AB_WRITE,link,adapter,card,
preq->nwords,preq->pbuffer)!=OK) status = abbtError;
preq->status = status;
if(status) abBtCallback(link,adapter,card);
continue;
}
/* need intialization */ /* jcr */
if ((*pcard & AB_INIT_BIT) == 0){
if ((*pcard & AB_SENT_INIT) == 0){
if (*pcard & AB_PLC) {
*pcard |= AB_INIT_BIT;
*pcard |= AB_SENT_INIT;
}
else if (ab_card_init(pcard,adapter,card,link) == 0){
*pcard |= AB_SENT_INIT;
ab_init_cnt[link][adapter][card] = 0;
}
/* did the init message get lost - try each second */
}else if (ab_init_cnt[link][adapter][card]++ > 10){
if (ab_card_init(pcard,adapter,card,link) == 0){
ab_init_cnt[link][adapter][card] = 0;
}
}
continue;
}
/* need block transfer */
btq_err = OK; /* assume success */
/* don't make another block transfer request if one's outstanding... */
if((ab_btq_cnt[link][adapter][card] % 10) != 0) {
/* ...but try again periodically in case a request got lost. */
ab_btq_cnt[link][adapter][card]++;
continue;
}
/*If 3 queue attempts fail then reinitialize*/
if(ab_btq_cnt[link][adapter][card] >= 30) {
*pcard &= ~(AB_INIT_BIT | AB_SENT_INIT);
ab_btq_cnt[link][adapter][card] = 0;
continue;
}
if ((*pcard & AB_INTERFACE_TYPE) == AB_AI_INTERFACE){
switch (*pcard&AB_CARD_TYPE){
case (AB1771IrPlatinum) :
case (AB1771IrCopper) :
if ((pass % AB_IR_RATE) == 0) {
btq_err = bt_queue(AB_READ,link,adapter,card,8,&msg[0]);
}
break;
case (AB1771IL):
if ((pass % AB_IL_RATE) == 0) {
btq_err = bt_queue(AB_READ,link,adapter,card,12,&msg[0]);
}
break;
case (AB1771IXE ):
if ((pass % AB_IXE_RATE) == 0) {
btq_err = bt_queue(AB_READ,link,adapter,card,12,&msg[0]);
}
break;
case (AB1771IFE):
case (AB1771IFE_4to20MA):
case (AB1771IFE_0to5V):
btq_err = bt_queue(AB_READ,link,adapter,card,12,&msg[0]);
break;
case (AB1771IFE_SE):
btq_err = bt_queue(AB_READ,link,adapter,card,20,&msg[0]);
break;
}
if(btq_err != OK){
if(ab_debug)
logMsg("Error on AI BT request for L%x A%x C%x\n",
link,adapter,card);
}
}else if ((*pcard&AB_INTERFACE_TYPE)==AB_AO_INTERFACE){
if (((pass % AB_OFE_RATE) == 0) || (*pcard & AB_UPDATE)){
ab_btwrite(pcard,adapter,card,link);
}
}
}
}
}
/*
* ab_card_init
*
* Allen-Bradley card initialization routines
*/
ab_card_init(pcard,adapter,card,link)
register unsigned short *pcard; /* AB configuration word */
register unsigned short adapter;
register unsigned short card;
short link;
{
short msg[64];
register short *pmsg = &msg[0];
register short length;
register short i;
register short *pab_table;
bfill(pmsg,64*2,0);
*pmsg = 0;
switch (*pcard & AB_INTERFACE_TYPE){
case (AB_AI_INTERFACE):
switch (*pcard & AB_CARD_TYPE){
case (AB1771IrCopper) :
*pmsg = IR_COPPER;
case (AB1771IrPlatinum) :
i = (*pcard & AB_CONVERSION) >> 8;
if(i==IR_degF) *pmsg |= IR_UNITS_DEGF;
if(i==IR_Ohms) *pmsg |= IR_UNITS_OHMS;
*pmsg |= IR_SIGNED;
length = 1;
break;
case (AB1771IXE): /* millivolt module */
/* need to base this on conversion */
switch ((*pcard & AB_CONVERSION) >> 8){
case (K_DGF):
*pmsg = IXE_K | IXE_DEGF | IXE_SIGNED | IXE_HALFSEC;
break;
case (K_DGC):
*pmsg = IXE_K | IXE_DEGC | IXE_SIGNED | IXE_HALFSEC;
break;
case (J_DGF):
*pmsg = IXE_J | IXE_DEGF | IXE_SIGNED | IXE_HALFSEC;
break;
case (J_DGC):
*pmsg = IXE_J | IXE_DEGC | IXE_SIGNED | IXE_HALFSEC;
break;
case (E_DGF):
*pmsg = IXE_E | IXE_DEGF | IXE_SIGNED | IXE_HALFSEC;
break;
case (E_DGC):
*pmsg = IXE_E | IXE_DEGC | IXE_SIGNED | IXE_HALFSEC;
break;
case (T_DGF):
*pmsg = IXE_T | IXE_DEGF | IXE_SIGNED | IXE_HALFSEC;
break;
case (T_DGC):
*pmsg = IXE_T | IXE_DEGC | IXE_SIGNED | IXE_HALFSEC;
break;
case (R_DGF):
*pmsg = IXE_R | IXE_DEGF | IXE_SIGNED | IXE_HALFSEC;
break;
case (R_DGC):
*pmsg = IXE_R | IXE_DEGC | IXE_SIGNED | IXE_HALFSEC;
break;
case (S_DGF):
*pmsg = IXE_S | IXE_DEGF | IXE_SIGNED | IXE_HALFSEC;
break;
case (S_DGC):
*pmsg = IXE_S | IXE_DEGC | IXE_SIGNED | IXE_HALFSEC;
break;
default:
*pmsg = IXE_MILLI | IXE_DEGC | IXE_SIGNED | IXE_HALFSEC;
break;
}
length = 27;
break;
case (AB1771IL):
*pmsg = IL_RANGE; /* -10 to +10 volts */
*(pmsg+1) = IL_DATA_FORMAT; /* signed magnitude */
*(pmsg+2) = 0x0ff;
for (i = 4; i <= 18; i+=2) *(pmsg+i) = 0x4095;
length = 19;
break;
case (AB1771IFE):
*pmsg = 0xffff;/*IFE_RANGE; -10 to +10 volts */
*(pmsg+1) = 0xffff;/*IFE_RANGE; -10 to +10 volts */
*(pmsg+2) = 0x0700;/*IFE_DATA_FORMAT; signed magnitude, differential */
*(pmsg+3) = 0x0ffff;
for (i = 6; i <= 36; i+=2) *(pmsg+i) = 0x4095;
length = 37;
break;
case (AB1771IFE_SE):
*pmsg = 0xffff;/*IFE_RANGE; -10 to +10 volts */
*(pmsg+1) = 0xffff;/*IFE_RANGE; -10 to +10 volts */
*(pmsg+2) = 0x0600;/*IFE_SE_DATA_FORMAT; signed magnitude, single-ended */
*(pmsg+3) = 0x0ffff;
for (i = 6; i <= 36; i+=2) *(pmsg+i) = 0x4095;
length = 37;
break;
case (AB1771IFE_4to20MA):
*pmsg = 0x0000;/*IFE_RANGE; 4to20 MilliAmps */
*(pmsg+1) = 0x0000;/*IFE_RANGE; 4to20 MilliApms */
*(pmsg+2) = 0x0700;/*IFE_DATA_FORMAT; signed magnitude, double */
for (i = 6; i <= 36; i+=2) *(pmsg+i) = 0x4095;
length = 37;
break;
case (AB1771IFE_0to5V):
*pmsg = 0x5555;/*IFE_RANGE; 0 to 5 Volts */
*(pmsg+1) = 0x5555;/*IFE_RANGE; 0 to 5 Volts */
*(pmsg+2) = 0x0700;/*IFE_DATA_FORMAT; signed magnitude, double */
for (i = 6; i <= 36; i+=2) *(pmsg+i) = 0x4095;
length = 37;
break;
default:
return(-1);
}
break;
case (AB_AO_INTERFACE):
/* initializing an analog output card includes writing the values */
/* see also ab_aoread below */
/* analog output reads the current outputs at initialization */
if ((*pcard & AB_CARD_TYPE) == AB1771OFE){
/* configuration data */
*(pmsg+4) = OFE_BINARY | OFE_SCALING;
for (i = 6; i <= 12; i+=2) *(pmsg+i) = 0x4095;
length = 5;
return(bt_queue(AB_READ,link,adapter,card,length,pmsg));
}else
return(-1);
break;
default:
return(-1);
}
/* initate block tranfer */
return(bt_queue(AB_WRITE,link,adapter,card,length,pmsg));
}
/*
* ab_btwrite
*
* block transfer - to write data
*/
ab_btwrite(pcard,adapter,card,link)
unsigned short *pcard;
register unsigned short adapter;
register unsigned short card;
register unsigned short link;
{
short msg[64];
register short *pmsg = &msg[0];
register i;
register short *pab_table = &ab_btdata[link][adapter][card][0];
short btq_err;
/* clear out the dual ported memory */
bfill(&msg[0],64*2,0);
if ((*pcard & AB_CARD_TYPE) == AB1771OFE){
/* take the data from the Allen-Bradley data table */
for (i = 0; i < 4; i++, pab_table++)
*(pmsg+i) = *pab_table;
/* configuration data */
*(pmsg+4) = OFE_BINARY | OFE_SCALING;
for (i = 6; i <= 12; i+=2) *(pmsg+i) = 0x4095;
/* initate block tranfer */
if(btq_err = bt_queue(AB_WRITE,link,adapter,card,5,pmsg)){
if(ab_debug)
logMsg("link %x, ab_btwrite error %x\n",link,btq_err);
return (btq_err);
}
}else{
return(-1);
}
return(0);
}
/*
* bt_queue
*
* queue a block transfer request
*/
bt_queue(command,link,adapter,card,length,pmsg)
register unsigned short command;
register unsigned short link;
register unsigned short adapter;
unsigned short card;
unsigned short length;
unsigned short *pmsg;
{
unsigned short *pcard = &ab_config[link][adapter][card];
register struct ab_region *p6008 = p6008s[link];
register struct dp_mbox *pmb = (struct dp_mbox *)(&p6008->mail);
register unsigned short *pmb_msg = (unsigned short *)&pmb->da;
register unsigned short lock_stat,i;
int status;
register unsigned short command_back;
/* try to get access to (lock) the dual port memory */
/* This prevents the Allen-Bradley scanner from accessing the dual */
/* ported memory while a request is being made. */
for(i=0; i<100; i++) {
if ((lock_stat = sysBusTas (&pmb->fl_lock)) == TRUE) break;
taskDelay(1);
}
if(lock_stat == FALSE) {
if(ab_debug != 0)
logMsg("link %x BTQ-dpm locked on %x cmd\n",
link,command);
ab_not_ready[link] += 1;
return (LOCK);
}
/* As the scanner will not be sending us data during this command */
/* request, we direct the interrupts here and wait for command */
/* accepted. */
ab_requesting_bt[link] = TRUE;
/* clear the timeout flag */
ab_tout[link] = OK;
/* copy the message into the dual-ported memory */
/* clear the semaphore to make sure we stay in sync */
if((semTake(ab_cmd_sem,NO_WAIT) == OK) && ab_debug)
logMsg("link %x, semaphore set before bt_queue cmd %x,%x,%x\n",
link,command,pmb->command,pmb->conf_stat);
pmb->command = command;
/* jcr */
if ((*pcard & AB_PLC)==0) {
pmb->address = (adapter << 4) + card;
pmb->data_len = length;
}else{
pmb->address = (adapter << 4); /* always writes to card 0 */
pmb->data_len = 0; /* PLCs govern the length */
/* used by the PLC to determine where the data comes from */
/* the PLC programmers have to use this knowledge fopr the */
/* interface */
pmsg[62] = card;
}
wtrans(pmsg,pmb_msg);
/* initiate the timeout clock */
wdStart(wd_id[link],vxTicksPerSecond,ab_timeout,link);
/* alert the scanner to the new request */
p6008->sc_intr = 1;
/* wait for the command to be accepted */
semTake(ab_cmd_sem, WAIT_FOREVER);
/* determine if the command was accepted */
if (ab_tout[link] == ERROR){
if(ab_debug)
logMsg("link %x, BTQ cmd %x...timeout\n",link,command);
ab_cmd_to[link][adapter][card]++;
ab_requesting_bt[link] = FALSE;
/* unlock the dual ported memory */
pmb->fl_lock = 0;
return(ERROR);
}
/* get the status & command from the dual-ported memory */
status = pmb->conf_stat;
command_back = pmb->command;
if(command_back != command){
if (ab_debug)
logMsg("link %x, BTQ cmd %x, resp=%x\n"
,link,command,command_back);
ab_bad_response[link]++;
status = ERROR;
}
if (status != BT_ACCEPTED){
if (status == 0x14){
taskDelay(vxTicksPerSecond/10);
if(ab_debug >1)
logMsg("link %x BTQ full, delaying...\n",link);
}else{
if(ab_debug != 0)
logMsg("link %x BTQ failure %x\n",link,status);
}
}else
status = OK;
/* return the interrupts to the data received state */
ab_requesting_bt[link] = FALSE;
/* unlock the dual ported memory */
pmb->fl_lock = 0;
/* indicate that a response is queued for this card */
if(command == AB_READ) ab_btq_cnt[link][adapter][card]++;
return(0);
}
/*
* abDoneTask
*
* This task handles block transfer data returned from the Allen-Bradley
* scan task through the dual ported memory.
* It needs to acknowledge that a block tranfer card (analog io) has been
* successfully configured or place the read data into the block transfer
* memory.
*/
abDoneTask(){
register short i,sign_bit,card,plc_card;
unsigned short adapter,link;
register unsigned short *pcard; /* ptr to configuration word */
register short *pab_table;
char *pab_sts;
struct ab_response response;
struct ab_response *presponse = &response;
while(TRUE){
/* wait for block transfer completion */
semTake(ab_data_sem, WAIT_FOREVER);
while (rngBufGet(ab_cmd_q,&response,sizeof(struct ab_response))
== sizeof(struct ab_response)){
link = presponse->link;
/* make sure we got a response to a read or write command */
if ((presponse->command != AB_READ)
&& (presponse->command != AB_WRITE)){
ab_rw_resp_err[link]++;
continue;
}
/* find whose data this is */
/* unpacks the card pack out of the message */
/* this codes determines if it is a PLC or */
/* adapter response */
adapter = presponse->adapter;
card = presponse->card;
plc_card = presponse->data[62];
/* let's determine if this is a PLC - verify with config table */
if ((plc_card <= 16) && (plc_card >= 0)
&& (ab_config[link][adapter][plc_card] & AB_PLC))
card = plc_card;
pcard = &ab_config[link][adapter][card];
/* zero counter to indicate that a requested BT was received */
ab_btq_cnt[link][adapter][card] = 0;
if(((*pcard & AB_INTERFACE_TYPE)==AB_BT_READ)
|| ((*pcard & AB_INTERFACE_TYPE)==AB_BT_WRITE)) {
ABBTREQUEST *preq = pabbtrequest[link][adapter][card];
/* block transfer timeout */
if (presponse->status == 0x23) {
preq->status = abbtTimeout;
} else {
preq->status = abbtSuccess;
}
if((*pcard & AB_INTERFACE_TYPE)==AB_BT_READ) {
for(i=0; i<preq->nwords; i++)
preq->pbuffer[i] = presponse->data[i];
}
abBtCallback(link,adapter,card);
continue;
}
/* block transfer timeout */
if (presponse->status == 0x23){
if (ab_debug >1)
logMsg("link %x adapter %x card %x timeout %x\n"
,link,adapter,card,presponse->status);
ab_data_to[link][adapter][card]++;
/* mark the channels on this card as harware error */
pab_sts = &ab_btsts[link][adapter][card][0];
for (i=0; i<AB_CHAN_CARD; i++,pab_sts++)
*pab_sts -= 1;
/* mark for reinitialization */
if (ab_btsts[link][adapter][card][0] <= 0)
*pcard &= ~(AB_INIT_BIT | AB_SENT_INIT);
continue;
}else if (presponse->status != 0){
if (ab_debug != 0)
logMsg("L%x A%x C%x, Bad Done Status=%x\n"
,link,adapter,card,presponse->status);
continue;
}
/* was it a response to configuration */
if ((*pcard & AB_INIT_BIT) == 0){
*pcard |= AB_INIT_BIT;
ab_init_cnt[link][adapter][card] = 0;
/* analog outputs return values on init */
if ((*pcard & AB_INTERFACE_TYPE)==AB_AO_INTERFACE)
{
register struct ab1771ofe_write *pmsg
= (struct ab1771ofe_write *)presponse->data;
pab_table = &ab_btdata[link][adapter][card][0];
for (i = 0; i < 4; i++,pab_table++)
*pab_table = pmsg->data[i];
}
/* it was a response to a command */
/* analog input response */
}else if ((*pcard & AB_INTERFACE_TYPE) == AB_AI_INTERFACE){
if((*pcard & AB_CARD_TYPE) == AB1771IrPlatinum
|| (*pcard & AB_CARD_TYPE) == AB1771IrCopper)
{
struct ab1771ir_read *pmsg = (struct ab1771ir_read *) presponse->data;
short under,over,overflow,polarity;
pab_table = &ab_btdata[link][adapter][card][0];
pab_sts = &ab_btsts[link][adapter][card][0];
for(i=0, under = 0x1, over=0x100, overflow=0x1, polarity=0x100;
i<ai_num_channels[AB1771IrPlatinum];
i++, under<<=1, over<<=1, overflow<<=1, polarity<<=1) {
*pab_table = pmsg->data[i];
if(pmsg->pol_over&polarity) *pab_table = -*pab_table;
if((pmsg->status&under) || (pmsg->status&over)
|| (pmsg->pol_over&overflow)) {
*pab_sts = -3;
ab_scaling_error[link][adapter][card]++;
}else{
*pab_sts = 0;
}
pab_sts++;
pab_table++;
}
}
else if ((*pcard & AB_CARD_TYPE) == AB1771IL)
{
register struct ab1771il_read *pmsg
= (struct ab1771il_read *)presponse->data;
pab_table = &ab_btdata[link][adapter][card][0];
pab_sts = &ab_btsts[link][adapter][card][0];
for (i=0, sign_bit=1; i < ai_num_channels[AB1771IL]; i++, sign_bit<<= 1){
/* status */
if((pmsg->urange & sign_bit) || (pmsg->orange & sign_bit)){
*pab_sts = -3;
ab_scaling_error[link][adapter][card]++;
}else{
*pab_sts = 0;
/* data */
*pab_table = pmsg->data[i];
}
pab_sts++;
pab_table++;
}
}
else if ((*pcard & AB_CARD_TYPE) == AB1771IXE)
{
register struct ab1771ixe_read *pmsg
= (struct ab1771ixe_read *)presponse->data;
pab_table = &ab_btdata[link][adapter][card][0];
pab_sts = &ab_btsts[link][adapter][card][0];
for (i=0, sign_bit=0x1; i<ai_num_channels[AB1771IXE]; i++, sign_bit<<=1){
/* status */
if ((pmsg->out_of_range & sign_bit)
|| (pmsg->out_of_range & (sign_bit << 8))){
*pab_sts = -3;
ab_scaling_error[link][adapter][card]++;
}else{
*pab_sts = 0;
/* data */
*pab_table = pmsg->data[i];
if(pmsg->pol_stat & (sign_bit << 8)) *pab_table = -*pab_table;
}
pab_sts++;
pab_table++;
}
}
else if ((*pcard & AB_CARD_TYPE) == AB1771IFE)
{
register struct ab1771ife_read *pmsg
= (struct ab1771ife_read *)presponse->data;
pab_table = &ab_btdata[link][adapter][card][0];
pab_sts = &ab_btsts[link][adapter][card][0];
/* check each channel as overrange */
for (i=0, sign_bit=0x1; i<ai_num_channels[AB1771IFE]; i++, sign_bit<<=1){
/* status */
if (pmsg->urange & sign_bit){
*pab_sts = -3;
ab_scaling_error[link][adapter][card]++;
}else if(pmsg->orange & sign_bit){
*pab_sts = -3;
ab_or_scaling_error[link][adapter][card]++;
}else if ((pmsg->data[i] & 0xf000) > 0){
ab_or_scaling_error[link][adapter][card]++;
}else{
*pab_sts = 0;
*pab_table = pmsg->data[i]; /* put data in table */
}
pab_sts++;
pab_table++;
}
}
else if ((*pcard & AB_CARD_TYPE) == AB1771IFE_SE)
{
register struct ab1771ife_read *pmsg
= (struct ab1771ife_read *)presponse->data;
pab_table = &ab_btdata[link][adapter][card][0];
pab_sts = &ab_btsts[link][adapter][card][0];
/* check each channel as overrange */
for (i=0, sign_bit=0x1; i<ai_num_channels[AB1771IFE_SE]; i++, sign_bit<<=1){
/* status */
if (pmsg->urange & sign_bit){
*pab_sts = -3;
ab_scaling_error[link][adapter][card]++;
}else if(pmsg->orange & sign_bit){
*pab_sts = -3;
ab_or_scaling_error[link][adapter][card]++;
}else if ((pmsg->data[i] & 0xf000) > 0){
ab_or_scaling_error[link][adapter][card]++;
}else{
*pab_sts = 0;
*pab_table = pmsg->data[i]; /* put data in table */
}
pab_sts++;
pab_table++;
}
}
else if ( ((*pcard & AB_CARD_TYPE) == AB1771IFE_4to20MA)
|| ((*pcard & AB_CARD_TYPE) == AB1771IFE_0to5V ) )
{
register struct ab1771ife_read *pmsg
= (struct ab1771ife_read *)presponse->data;
pab_table = &ab_btdata[link][adapter][card][0];
pab_sts = &ab_btsts[link][adapter][card][0];
/* check each channel as overrange */
for (i=0, sign_bit=0x1;
i<ai_num_channels[AB1771IFE_4to20MA]; i++, sign_bit<<=1){
/* status */
if (pmsg->urange & sign_bit){
*pab_sts = -3;
ab_scaling_error[link][adapter][card]++;
}else if(pmsg->orange & sign_bit){
*pab_sts = -3;
ab_or_scaling_error[link][adapter][card]++;
}else if ((pmsg->data[i] & 0xf000) > 0){
ab_or_scaling_error[link][adapter][card]++;
}else{
*pab_sts = 0;
*pab_table = pmsg->data[i]; /* put data in table */
}
pab_sts++;
pab_table++;
}
}
/* analog output response */
}else if ((*pcard & AB_CARD_TYPE) == AB1771OFE){
*pcard = *pcard & (~AB_UPDATE); /* mark as updated */
}
}
}
}
/*
* AB_BI_COS_SIMULATOR
*
* simulate a change of state interrupt from the Allen-Bradley
*/
unsigned short ab_old_binary_ins[AB_MAX_LINKS][AB_MAX_ADAPTERS][AB_MAX_CARDS];
ab_bi_cos_simulator()
{
register struct ab_region *p6008;
register unsigned short link;
register unsigned short *pcard;
unsigned short new;
unsigned short *pold;
short adapter,card,inpinx;
short first_scan,first_scan_complete;
short adapter_status_change;
/* dont do anything until interruptAccept */
for(;;){
if(interruptAccept) break;
taskDelay(vxTicksPerSecond/15);
}
first_scan_complete = FALSE;
first_scan = TRUE;
for(;;){
for (link = 0; link < AB_MAX_LINKS; link++){
if ((p6008 = p6008s[link]) == 0) continue;
for (adapter = 0; adapter < AB_MAX_ADAPTERS; adapter++){
adapter_status_change = ab_adapter_status_change[link][adapter];
ab_adapter_status_change[link][adapter] = 0;
/* check each card that is configured to be a binary input card */
for (card = 0; card < AB_MAX_CARDS; card++){
pcard = &ab_config[link][adapter][card];
inpinx = (adapter * AB_CARD_ADAPTER) + card;
pold = &ab_old_binary_ins[link][adapter][card];
if ((*pcard & AB_INTERFACE_TYPE) != AB_BI_INTERFACE) continue;
if ((*pcard & AB_CARD_TYPE) == ABBI_16_BIT){
/* sixteen bit byte ordering in dual ported memory */
/* byte 0011 2233 4455 6677 8899 AABB */
/* card 0000 2222 4444 6666 8888 AAAA */
if (inpinx & 0x1) continue;
new = *(unsigned short *)&(p6008->iit[inpinx]);
}else{
/* eight bit byte ordering in dual ported memory */
/* 1100 3322 5544 7766 9988 BBAA */
if (inpinx & 0x1) inpinx--; /* shuffle those bytes */
else inpinx++;
new = (unsigned short)(p6008->iit[inpinx]);
}
if((new!=*pold) || first_scan || adapter_status_change){
scanIoRequest(ioscanpvt[link][adapter][card]);
*pold = new;
}
}
}
}
/* turn off first scan */
if (first_scan){
first_scan_complete = TRUE;
first_scan = FALSE;
}
/* check for changes at about 15 Hertz */
taskDelay(vxTicksPerSecond/15);
}
}
int ab_bi_getioscanpvt(link,adapter,card,scanpvt)
unsigned short link;
unsigned short adapter;
unsigned short card;
IOSCANPVT *scanpvt;
{
*scanpvt = ioscanpvt[link][adapter][card];
return(0);
}
/*
* ALLEN-BRADLEY DRIVER INITIALIZATION CODE
*/
ab_driver_init()
{
unsigned short cok,got_one;
register unsigned short err_cnt;
register unsigned short not_init;
register unsigned short link;
struct ab_region *p6008;
int status;
/* initialize the Allen-Bradley 'done' semaphore */
if (abScanId == 0){
if(!(ab_cmd_sem=semBCreate(SEM_Q_FIFO,SEM_EMPTY)))
errMessage(0,"semBcreate failed in initEvent");
if(!(ab_data_sem=semBCreate(SEM_Q_FIFO,SEM_EMPTY)))
errMessage(0,"semBcreate failed in initEvent");
if ((ab_cmd_q = rngCreate(AB_Q_SZ)) == (RING_ID)NULL)
panic ("ab_init: ab_cmd_q\n");
}
got_one = FALSE;
if ((status = sysBusToLocalAdrs(VME_AM_STD_SUP_DATA,AB_BASE_ADDR,&ab_stdaddr)) != OK){
logMsg("Addressing error in ab driver\n");
return ERROR;
}
p6008 =(struct ab_region *)ab_stdaddr;
for (link=0;link < AB_MAX_LINKS; link++,p6008++){
/* initialize the AB config buffer for this link */
bfill(&ab_config[link][0][0],
AB_MAX_ADAPTERS*AB_MAX_CARDS*sizeof(short),
0);
bfill(&ab_btdata[link][0][0][0],
AB_MAX_ADAPTERS*AB_MAX_CARDS*AB_CHAN_CARD*sizeof(short),
0);
bfill(&pabbtrequest[link][0][0],
AB_MAX_ADAPTERS*AB_MAX_CARDS*sizeof(ABBTREQUEST *),
0);
/* initialize each 6008 that exists */
if (vxMemProbe(p6008,READ,1,&cok) != ERROR){
p6008s[link] = p6008;
err_cnt = 0;
not_init = TRUE;
/* create the internal watchdog timer */
if (wd_id[link] == 0)
wd_id[link] = wdCreate();
/*connect intr service routine to intr vector */
intConnect((AB_VEC_BASE+link)*4,ab_intr,link);
sysIntEnable(AB_INT_LEVEL);
while ((err_cnt < 5) && (not_init)){
if (link_init(link) >=0) not_init = FALSE;
else {
err_cnt++;
logMsg("link %x, link_init error\n",link);
}
}
if (err_cnt >= 5){
p6008s[link] = 0;
}else{
got_one = TRUE;
}
}else{
p6008s[link] = 0;
}
if(p6008s[link]) {
int adapter,card;
for(adapter=0; adapter<AB_MAX_ADAPTERS; adapter++)
for(card=0; card<AB_MAX_CARDS; card++)
scanIoInit(&ioscanpvt[link][adapter][card]);
}
}
if (got_one){
/* spawn the scan task to handle block transfer requests */
if (abScanId) td(abScanId);
abScanId = taskSpawn(ABSCAN_NAME,ABSCAN_PRI,ABSCAN_OPT,
ABSCAN_STACK,abScanTask);
taskwdInsert(abScanId,NULL,NULL);
/* spawn the done task to handle block transfer responses */
if (abDoneId) td(abDoneId);
abDoneId = taskSpawn(ABDONE_NAME,ABDONE_PRI,ABDONE_OPT,
ABDONE_STACK,abDoneTask);
taskwdInsert(abDoneId,NULL,NULL);
/* spawn the ab change of state interrupt simulator */
if (abCOSId) td(abCOSId);
abCOSId = taskSpawn(ABCOS_NAME,ABCOS_PRI,ABCOS_OPT,ABCOS_STACK,ab_bi_cos_simulator);
taskwdInsert(abCOSId,NULL,NULL);
rebootHookAdd(ab_reboot_hook);
}
return(0);
}
/*
* link_init
*
* establish the communication link with the AB scanner
*/
link_init(link)
register short link;
{
register struct ab_region *p6008 = p6008s[link];
char buffer[80];
char *pbuffer = &buffer[0];
register short length;
short mr_w_err,i;
register struct dp_mbox *pmb = (struct dp_mbox *)(&p6008->mail);
char *pfirmware_info = &ab_firmware_info[link][0];
ab_bad_response[link]=0;
ab_rw_resp_err[link]=0;
/* the scanner comes up with the dual ported memory locked */
pmb->fl_lock = 0; /* so unlock it */
/* initialize the scanner on power up */
if (pmb->conf_stat == SCANNER_POWERUP){
logMsg("link %x, powerup...\n",link);
/* on initialization the scanner puts its firmware revision info */
/* into the general data are of the dual-port. We save it here. */
/* (The most current revision is Series A, Revision D.) */
strcpy(pfirmware_info,(char *)&pmb->da);
if(ab_debug != 0)
logMsg("link %x f/w = :\n\t%s\n",link,ab_firmware_info[link]);
/*scanner comes up in program_mode */
ab_op_stat[link] = PROGRAM_MODE;
/*interrupt the scanner to wake it up */
p6008->sc_intr = 1;
}
logMsg("Link %x, initial operating status word = %x\n",link,ab_op_stat[link]);
if((ab_op_stat[link] & PROGRAM_MODE) != PROGRAM_MODE){
/* This link must already be initialized. We're done */
logMsg("Link %x already initialized\n",link);
return(0);
}
/* setup scanner */
bfill(pbuffer,80,0);
*(pbuffer) = DEF_RATE; /* baud rate */
*(pbuffer+2) = DEBUG; /* always in debug mode - don't know why */
*(pbuffer+3) = AB_IL; /* interrupt level */
*(pbuffer+4) = AB_VEC_BASE+link; /* interrupt vector */
*(pbuffer+5) = AB_INT_ENABLE; /* enable interrupt */
*(pbuffer+6) = AB_SYSFAIL_DISABLE; /* disable VMEbus SYSFAIL sig */
length = 7;
if((mr_w_err = mr_wait(SET_UP,length,pbuffer,link)) != OK){
if (ab_debug != 0)
logMsg("link %x link_init set_up cmd - mr_wait error %x\n"
,link,mr_w_err);
return(-1);
}
/* Once scanner has been placed in RUN_MODE, putting it back into *
* PROGRAM_MODE will disable binary outputs until it is placed back in *
* RUN_MODE. Some scanner commands, such as SCAN_LIST, can only be *
* performed in PROGRAM_MODE. These commands should only be issued *
* immediately after initialization. Re-booting an IOC (without powering
* it down) is the presently the only way of getting it into PROGRAM_MODE
* without disabling binary outputs */
/* initialize scan list for each link present */
if(scan_list(link) != OK){
logMsg(" scan_list error on link %x\n",link);
return(-1);
}
return(0);
}
/*
* MR_WAIT
*
* scanner management command send
*/
mr_wait(command,length,pmsg,link)
unsigned short command;
short length;
short *pmsg;
unsigned short link;
{
register struct ab_region *p6008 = p6008s[link];
register struct dp_mbox *pmb = (struct dp_mbox *)&p6008->mail;
register short *pmb_msg, i, lock_stat;
short rtn_val;
/* pointer to the message portion of the mailbox */
pmb_msg = (short *)&pmb->da;
/* try to get access to (lock) the dual port memory */
for(i=0; i<100; i++) {
if ((lock_stat = sysBusTas (&pmb->fl_lock)) == TRUE) break;
taskDelay(1);
}
if(lock_stat == FALSE) {
if(ab_debug != 0)
logMsg("link %x mr_wait-dpm locked on %x cmd\n",
link,command);
return (LOCK);
}
/* reset timeout */
ab_tout[link] = OK;
/* clear the semaphore to make sure we stay in sync */
if((semTake(ab_cmd_sem,NO_WAIT) == OK) && ab_debug)
logMsg("link %x, semaphore set before mr_wait cmd %x,%x,%x\n",
link,command,pmb->command,pmb->conf_stat);
/* process commands */
pmb->command = command;
pmb->data_len = length;
rtn_val = OK; /* assume success */
switch (command) {
/* commands with response */
case AUTO_CONF:
case LINK_STATUS:
wdStart(wd_id[link],vxTicksPerSecond*10,ab_timeout,link);
p6008->sc_intr = 1; /* wakeup scanner tsk */
semTake(ab_cmd_sem, WAIT_FOREVER);
/* was this a timeout? */
if(ab_tout[link] == ERROR){
if(ab_debug != 0)
logMsg("link %x mr_wait - timeout on %x cmd\n"
,link,command);
rtn_val = ERROR;
break;
}
if ((pmb->conf_stat != 0) && (ab_debug != 0)){
logMsg("link %x mr_wait cmd %x failed, status=%x\n"
,link,pmb->command,pmb->conf_stat);
rtn_val = ERROR;
break;
}
if (pmb->command == command){
wtrans(pmb_msg,pmsg); /* xfer data to local mailbox */
}else{
ab_bad_response[link]++;
if(ab_debug != 0)
logMsg("link %x mr_wait - bad resp on %x cmd\n"
,link,command);
rtn_val = ERROR;
}
break;
/* commands which send data */
case SCAN_LIST:
case SET_MODE:
case SET_UP:
wtrans(pmsg,pmb_msg); /* xfer data to local mailbox */
wdStart(wd_id[link],vxTicksPerSecond*5,ab_timeout,link);
p6008->sc_intr = 1; /* wakeup scanner */
semTake(ab_cmd_sem, WAIT_FOREVER);
/* was this a timeout? */
if(ab_tout[link] == ERROR){
if(ab_debug != 0)
logMsg("link %x mr_wait - timeout on %x cmd\n"
,link,command);
rtn_val = ERROR;
break;
}
if ((pmb->conf_stat != 0) && (ab_debug != 0)){
logMsg("link %x mr_wait cmd %x failed status %x\n"
,link,pmb->command,pmb->conf_stat);
rtn_val = ERROR;
}
break;
}
/* unlock the dual port memory */
pmb->fl_lock = 0;
return (rtn_val);
}
int ab_intr_cnt[AB_MAX_LINKS];
/*
* AB_INTR
*
* interrupt service routine
*
* The Allen-Bradley protocol requires that an interrupt be received when
* a block transfer request is given to the scanner board through the dual
* ported memory and then another when the command is complete. As the command
* accept interrupt occurs while the Allen-Bradley scanner is locked out,
* there is no danger of getting a data interrupt back. The documentation is
* very misleading in this area: It gives no indication of how the interrupts
* are used to accomplish asynchronous communication and implies that the
* dual-ported memory lock is controlled in a much different fashion than it is.
*/
ab_intr(link)
short link;
{
struct ab_response response;
register struct ab_response *presponse;
register unsigned short *presp_data;
register unsigned short *pdata;
register struct dp_mbox *pmb;
register unsigned short i;
register short tmp_op_stat;
register struct ab_region *p6008 = p6008s[link];
pmb = &p6008s[link]->mail;
/* Test the operating status word of link to see if adapter *
* status has changed since the last time the scanner interrupted us. */
tmp_op_stat = (p6008->osw & OSW_MASK);
if (tmp_op_stat != ab_op_stat[link]){
/* logMsg("old stat %x, new stat %x\n",ab_op_stat[link], tmp_op_stat); */
ab_op_stat[link] = tmp_op_stat;
}
ab_intr_cnt[link]++;
if (((pmb->command != AB_WRITE) && (pmb->command != AB_READ)) ||
(ab_requesting_bt[link])){
semGive(ab_cmd_sem);
wdCancel(wd_id[link]);
}else{
presponse = &response;
/* wait for the genius, who posted us, to lock the data area */
ab_post_no_lock[link] = 0;
while(TRUE) {
unsigned char fl_lock;
fl_lock = pmb->fl_lock;
if(fl_lock&0x80) break;
ab_post_no_lock[link] += 1;
if(ab_post_no_lock[link]>=1000) {
logMsg("link %x exceeded stop count\n",link);
return;
}
}
/* put the response on the queue for abDoneTask */
presponse->link = link;
presponse->card = pmb->address & 0x0f;
presponse->adapter = (pmb->address & 0x70) >> 4;
presponse->command = pmb->command;
presponse->status = pmb->conf_stat;
presp_data = &presponse->data[0];
pdata = &pmb->da.wd[0];
for (i = 0; i < 64; i++,pdata++,presp_data++)
*presp_data = *pdata;
if (rngBufPut(ab_cmd_q,&response,sizeof(struct ab_response))
!= sizeof(struct ab_response))
logMsg("ab_cmd_q - Full\n");
/* wake up abDoneTask */
semGive(ab_data_sem);
pmb->fl_lock = 0;
}
}
/*
* AB_TIMEOUT
*
* internal watchdog timer
*/
ab_timeout(link)
int link;
{
/* mark a timeout error */
ab_tout[link] = ERROR;
/* only the command receive gives a damn */
semGive(ab_cmd_sem);
/* keep track in case someone wants to snoop */
ab_comm_to[link]++;
}
static void wtrans (from, to)
register short *from, *to;
{
register i;
for (i=0;i<64;i++)
*to++ = *from++;
}
/*
* AB_AIDRIVER
*
* data request from the Analog Input driver
*/
ab_aidriver(card_type,link,adapter,card,signal,adapter_plc,pvalue,conversion)
unsigned short card_type;
register unsigned short link;
register unsigned short adapter;
register unsigned short card;
unsigned short signal;
unsigned short *pvalue;
unsigned short conversion;
unsigned short adapter_plc;
{
/* pointer to the Allen-Bradley configuration table */
register unsigned short *pcard = &ab_config[link][adapter][card];
/* verify that the card is initialized */
if ((*pcard & AB_INIT_BIT) == 0){
if ((*pcard & AB_SENT_INIT) == 0){
/* configuration data - the scan task takes over from here */
*pcard = AB_AI_INTERFACE | card_type
| ((conversion << 8) & AB_CONVERSION);
if (adapter_plc) *pcard |= AB_PLC;
}
/* sorry, but the initalization is not immediate */
return(-1);
}
/* verify that link is ok and card type is correct */
if (ab_adapter_status[link][adapter] == ERROR) return(-1);
if ((*pcard & AB_INTERFACE_TYPE) != AB_AI_INTERFACE) return(-1);
if ((*pcard & AB_CARD_TYPE) != card_type) return(-1);
/* get the value from the table */
*pvalue = ab_btdata[link][adapter][card][signal];
if (ab_btsts[link][adapter][card][signal] < -2) return(-1);
return(0);
}
/*
* AB_AODRIVER
*
* data send from the Analog Output driver
*/
ab_aodriver(card_type,link,adapter,card,signal,adapter_plc,value)
unsigned short card_type;
register unsigned short link;
register unsigned short adapter;
register unsigned short card;
unsigned short signal;
unsigned short adapter_plc;
unsigned short value;
{
register unsigned short *pcard = &ab_config[link][adapter][card];
/* verify that the card is initialized */
if ((*pcard & AB_INIT_BIT) == 0){
if ((*pcard & AB_SENT_INIT) == 0){
/* configuration data - the scan task takes over from here */
/* we assume the write will work and return 0 */
*pcard = AB_AO_INTERFACE | card_type | AB_UPDATE;
if (adapter_plc) *pcard |= AB_PLC;
/* put the value into the table */
ab_btdata[link][adapter][card][signal] = value;
}
return(-1);
}
/* put the value into the table */
if ((unsigned short)ab_btdata[link][adapter][card][signal] == value) return(0);
ab_btdata[link][adapter][card][signal] = value;
*pcard |= AB_UPDATE;
/* verify that link is ok and card type is correct */
if (ab_adapter_status[link][adapter] == ERROR) return(-1);
if ((*pcard & AB_INTERFACE_TYPE) != AB_AO_INTERFACE) return(-1);
if ((*pcard & AB_CARD_TYPE) != card_type) return(-1);
return(0);
}
/*
* AB_AOREAD
*
* read the analog output value
* called from read_ao in ../../dblib/src/iocao.c
* used to initialize AOs
*/
ab_aoread(card_type,link,adapter,card,signal,adapter_plc,pvalue)
register unsigned short card_type;
unsigned short link;
register unsigned short adapter;
register unsigned short card;
unsigned short signal;
unsigned short adapter_plc;
unsigned short *pvalue;
{
register unsigned short *pcard = &ab_config[link][adapter][card];
if ((*pcard & AB_INIT_BIT) == 0){
/* add adapter to the scan list if neccessary */
if ((*pcard & AB_SENT_INIT) == 0){
if (*pcard == 0){
/* configuration data-scan task takes over from here */
*pcard = AB_AO_INTERFACE | card_type;
if (adapter_plc) *pcard |= AB_PLC;
}
}
return(-1);
}
*pvalue = ab_btdata[link][adapter][card][signal];
return(0);
}
/*
* AB_BIDRIVER
*
* data read from the Binary Input driver
*/
ab_bidriver(card_type,link,adapter,card,adapter_plc,mask,pvalue)
unsigned short card_type;
register unsigned short link;
register unsigned short adapter;
register unsigned short card;
unsigned short adapter_plc;
unsigned long *pvalue;
unsigned long mask;
{
register struct ab_region *p6008 = p6008s[link];
register unsigned short *pcard = &ab_config[link][adapter][card];
register unsigned short inpinx;
register unsigned short *pshort;
/* verify there is a scanner */
if (p6008 == 0) return(-1);
/* claim the card as a binary input */
if (((*pcard & AB_INIT_BIT) == 0) && ((*pcard & AB_SENT_INIT) == 0)){
*pcard = AB_BI_INTERFACE | card_type | AB_INIT_BIT;
if (adapter_plc) *pcard |= AB_PLC;
}
/* verify link communication is OK */
/* This doesn't work in a box with no analog IO */
if (ab_adapter_status[link][adapter] == ERROR) return(-1);
/* eight bit byte ordering in dual ported memory */
/* 1100 3322 5544 7766 9988 BBAA */
if (card_type == ABBI_08_BIT){
/* eight bit byte ordering in dual ported memory */
/* byte 1100 3322 5544 7766 9988 BBAA */
inpinx = (adapter * AB_CARD_ADAPTER) + card;
if (card & 0x1) inpinx--; /* shuffle those bytes */
else inpinx++;
*pvalue = p6008->iit[inpinx] & mask;
}else{
/* sixteen bit byte ordering in dual ported memory */
/* byte 0011 2233 4455 6677 8899 AABB */
/* card 0000 2222 4444 6666 8888 AAAA */
inpinx = (adapter * AB_CARD_ADAPTER) + card;
if (card & 0x1) return(-1);
pshort = (unsigned short *)&(p6008->iit[inpinx]);
*pvalue = *pshort & mask;
}
return(0);
}
/*
* AB_BODRIVER
*
* data send from the Binary Output driver
*/
ab_bodriver(card_type,link,adapter,card,adapter_plc,value,mask)
unsigned short card_type;
register unsigned short link;
unsigned short adapter;
register unsigned short card;
unsigned short adapter_plc;
unsigned long value;
unsigned long mask;
{
register struct ab_region *p6008 = p6008s[link];
register unsigned short *pcard = &ab_config[link][adapter][card];
register unsigned short outinx;
register unsigned short *pshort;
/* verify there is a scanner */
if (p6008 == 0) return(-1);
/* claim the card as a binary output */
if (((*pcard & AB_INIT_BIT) == 0) && ((*pcard & AB_SENT_INIT) == 0)){
*pcard = AB_BO_INTERFACE | card_type | AB_INIT_BIT;
if (adapter_plc) *pcard |= AB_PLC;
}
/* eight bit byte ordering in dual ported memory */
/* 1100 3322 5544 7766 9988 BBAA */
if (card_type == ABBO_08_BIT){
/* eight bit byte ordering in dual ported memory */
/* byte 1100 3322 5544 7766 9988 BBAA */
outinx = (adapter * AB_CARD_ADAPTER) + card;
if (card & 0x1) outinx--; /* shuffle those bytes */
else outinx++;
p6008->oit[outinx] = (p6008->oit[outinx] & ~mask) | value;
}else{
/* sixteen bit byte ordering in dual ported memory */
/* byte 0011 2233 4455 6677 8899 AABB */
/* card 1111 3333 5555 7777 9999 BBBB */
outinx = (adapter * AB_CARD_ADAPTER) + card;
if (card & 0x1) outinx--; /* shuffle those bytes */
else return(-1);
pshort = (unsigned short *)&(p6008->oit[outinx]);
*pshort = (*pshort & ~mask) | value;
}
/* verify link communication is OK */
/* This doesn't work in a box with no analog IO */
if (ab_adapter_status[link][adapter] == ERROR) return(-1);
return(0);
}
/*
* AB_BOREAD
*
* read the binary output channel
*/
ab_boread(card_type,link,adapter,card,pvalue,mask)
unsigned short card_type;
unsigned short link;
register unsigned short adapter;
register unsigned short card;
register unsigned long *pvalue;
unsigned long mask;
{
register struct ab_region *p6008 = p6008s[link];
register unsigned short outinx;
register unsigned short *pshort;
/* verify there is a scanner */
if (p6008 == 0) return(-1);
/* verify link communication is OK */
/* This doesn't work in a box with no analog IO */
if (ab_adapter_status[link][adapter] == ERROR) return(-1);
/* eight bit byte ordering in dual ported memory */
/* 1100 3322 5544 7766 9988 BBAA */
if (card_type == ABBO_08_BIT){
/* eight bit byte ordering in dual ported memory */
/* byte 1100 3322 5544 7766 9988 BBAA */
outinx = (adapter * AB_CARD_ADAPTER) + card;
if (card & 0x1) outinx--; /* shuffle those bytes */
else outinx++;
*pvalue = p6008->oit[outinx] & mask;
}else{
/* sixteen bit byte ordering in dual ported memory */
/* byte 0011 2233 4455 6677 8899 AABB */
/* card 1111 3333 5555 7777 9999 BBBB */
outinx = (adapter * AB_CARD_ADAPTER) + card;
if (card & 0x1) outinx--; /* shuffle those bytes */
else return(-1);
pshort = (unsigned short *)(&p6008->oit[outinx]);
*pvalue = *pshort & mask;
}
return(0);
}
/*
* LINK_STATUS
*
* Fetches the status of the adapters on the specified link
* The ab_adapter_status table is used to determine hardware communication
* errors and convey them to the database
*/
link_status(link)
unsigned short link;
{
short buffer[200];
register short *pbuffer = &buffer[0];
register short i, mr_w_err;
/* for no card present - mark all adapters as offline */
if (p6008s[link] == 0){
for (i = 0; i < AB_MAX_ADAPTERS; i++){
ab_adapter_status[link][i] = -1;
}
return(0);
}
/* get the link status */
if((mr_w_err = mr_wait(LINK_STATUS,0,pbuffer,link)) != OK){
if (ab_debug != 0)
logMsg(" link_status cmd error\n");
ab_link_to[link]++;
return(-1);
}else{
/* check each adapter on this link */
for (i = 0; i< AB_MAX_ADAPTERS; i++){
/* good status */
if (*(pbuffer+(i*4)) & 0x70){
if (ab_adapter_status[link][i] == -1) {
printf("link %d adapter %d change bad to good\n"
,link,i);
ab_adapter_status_change[link][i]=1;
}
ab_adapter_status[link][i] = 0;
/* bad status */
}else {
if (ab_adapter_status[link][i] == 0){
printf("link %d adapter %d change good to bad\n"
,link,i);
ab_adapter_status_change[link][i]=1;
}
ab_adapter_status[link][i] = -1;
}
}
}
return(0);
}
/*
* SCAN_LIST
*
* sets the scan list for the Allen-Bradley scanner
*/
scan_list(link)
unsigned short link;
{
register short i,sl_inx,fail,mr_w_err;
char buffer[80];
register char *pbuffer = &buffer[0];
/* for no card present - mark all adapters as offline */
if (p6008s[link] == 0) return(0);
/* A promised firmware change will allow us to RESET
the scanner over the vmeBus. For now, the only way
to get the scanner into prog-mode without having
the BO's glitch is with the vmeBus SYSRESET signal,
which occurs when the RESET switch on the VME chassis
is used. Until the f/w change is made, changing the
scanner from run mode to program mode (to modify the
scan list, for instance) will cause the BO's to turn
off until the scanner is returned to run mode. It's
not nice, but for now we'll have to assume that all
adapters are needed and put them all in the scan list. */
/* build the scan list */
bfill(&ab_scan_list[link][0],AB_MAX_ADAPTERS,0);
for (sl_inx = 0, i = 0; i < AB_MAX_ADAPTERS; i++){
ab_scan_list[link][sl_inx] = (i << 2);
sl_inx++;
}
/* set new scan list - assume we're in program mode */
if((mr_w_err = mr_wait(SCAN_LIST,sl_inx,&ab_scan_list[link][0],link)) != OK){
if (ab_debug != 0)
logMsg(" scan_list SCAN_LIST cmd error\n");
return(-1);
}
/* place the scanner into run mode */
bfill(pbuffer,80,0);
*pbuffer = RUN_MODE;
if((mr_w_err = mr_wait(SET_MODE,0,pbuffer,link)) != OK){
if (ab_debug != 0)
logMsg(" scan_list SET_MODE cmd error\n");
return(-1);
}
return(0);
}
#define MAX_8BIT 8
ab_io_report(level)
short int level;
{
short i,j,k,l,m,card,adapter,plc_card,x,type;
unsigned short jrval,krval,lrval,mrval;
unsigned long val,jval,kval,lval,mval;
/* report all of the Allen-Bradley Serial Links present */
for (i = 0; i < AB_MAX_LINKS; i++){
if(!p6008s[i]) continue;
printf("AB-6008SV:\tcard %d ",i);
for(adapter=0; adapter<AB_MAX_ADAPTERS; adapter++) {
if(ab_adapter_status[i][adapter]==0)
printf("U");else printf("D");
}
printf(" cto: %d lto: %d badres: %d\n",
ab_comm_to[i],ab_link_to[i],ab_bad_response[i]);
if(level>0) printf("%s\n",ab_firmware_info[i]);
/* report all cards to which the database has interfaced */
/* as there is no way to poll the Allen-Bradley IO to */
/* determine which card is there we assume that any interface */
/* which is successful implies the card type is correct */
/* since all binaries succeed and some analog inputs will */
/* succeed for either type this is a shakey basis */
for (adapter = 0; adapter < AB_MAX_ADAPTERS; adapter++){
for (card = 0; card < AB_MAX_CARDS; card++){
/* Determine whether the card is in a plc or not. */
if(ab_config[i][adapter][card] & AB_PLC){
plc_card = 1;
/* printf("This card is a plc card.\n"); */
} else {
plc_card = 0;
/* printf("This card is not a plc card.\n"); */
}
switch (ab_config[i][adapter][card] & AB_INTERFACE_TYPE){
case (AB_BI_INTERFACE):
printf("\tBI: AB\tadapter %d card %d",adapter,card);
ab_bi_report(i,adapter,card,plc_card);
break;
case (AB_BO_INTERFACE):
printf("\tBO: AB\tadapter %d card %d",adapter,card);
ab_bo_report(i,adapter,card,plc_card);
break;
case (AB_AI_INTERFACE):
type = ab_config[i][adapter][card]&AB_CARD_TYPE;
if ((ab_config[i][adapter][card]&AB_CARD_TYPE)==AB1771IXE){
printf("\tAI: AB1771IXE\tadapter %d card %d:\tcto: %d dto: %d sclerr: %d %d",
adapter,card,ab_cmd_to[i][adapter][card],
ab_data_to[i][adapter][card],
ab_scaling_error[i][adapter][card],
ab_or_scaling_error[i][adapter][card]);
if (level > 0){
ab_ai_report(type,i,adapter,card,plc_card);
}
} else if ((ab_config[i][adapter][card] & AB_CARD_TYPE) == AB1771IL){
printf("\tAI: AB1771IL\tadapter %d card %d:\tcto: %d dto: %d sclerr: %d %d",
adapter,card,ab_cmd_to[i][adapter][card],
ab_data_to[i][adapter][card],
ab_scaling_error[i][adapter][card],
ab_or_scaling_error[i][adapter][card]);
if (level > 0){
ab_ai_report(type,i,adapter,card,plc_card);
}
} else if ((ab_config[i][adapter][card] & AB_CARD_TYPE) == AB1771IrPlatinum){
printf("\tAI: AB1771IrPlatinum\tadapter %d card %d:\tcto: %d dto: %d sclerr: %d %d",
adapter,card,ab_cmd_to[i][adapter][card],
ab_data_to[i][adapter][card],
ab_scaling_error[i][adapter][card],
ab_or_scaling_error[i][adapter][card]);
if (level > 0){
ab_ai_report(type,i,adapter,card,plc_card);
}
} else if ((ab_config[i][adapter][card] & AB_CARD_TYPE) == AB1771IrCopper){
printf("\tAI: AB1771IrPlatinum\tadapter %d card %d:\tcto: %d dto: %d sclerr: %d %d",
adapter,card,ab_cmd_to[i][adapter][card],
ab_data_to[i][adapter][card],
ab_scaling_error[i][adapter][card],
ab_or_scaling_error[i][adapter][card]);
if (level > 0){
ab_ai_report(type,i,adapter,card,plc_card);
}
} else if ((ab_config[i][adapter][card] & AB_CARD_TYPE) == AB1771IFE_SE){
printf("\tAI: AB1771IFE_SE\tadapter %d card %d:\tcto: %d dto: %d sclerr: %d %d",
adapter,card,ab_cmd_to[i][adapter][card],
ab_data_to[i][adapter][card],
ab_scaling_error[i][adapter][card],
ab_or_scaling_error[i][adapter][card]);
if (level > 0){
ab_ai_report(type,i,adapter,card,plc_card);
}
} else if ((ab_config[i][adapter][card] & AB_CARD_TYPE) == AB1771IFE_4to20MA){
printf("\tAI: AB1771IFE_4to20MA\tadapter %d card %d:\tcto: %d dto: %d sclerr: %d %d",
adapter,card,ab_cmd_to[i][adapter][card],
ab_data_to[i][adapter][card],
ab_scaling_error[i][adapter][card],
ab_or_scaling_error[i][adapter][card]);
if (level > 0){
ab_ai_report(type,i,adapter,card,plc_card);
}
} else if ((ab_config[i][adapter][card] & AB_CARD_TYPE) == AB1771IFE){
printf("\tAI: AB1771IFE\tadapter %d card %d:\tcto: %d dto: %d sclerr: %d %d",
adapter,card,ab_cmd_to[i][adapter][card],
ab_data_to[i][adapter][card],
ab_scaling_error[i][adapter][card],
ab_or_scaling_error[i][adapter][card]);
if (level > 0){
ab_ai_report(type,i,adapter,card,plc_card);
}
} else if ((ab_config[i][adapter][card] & AB_CARD_TYPE) == AB1771IFE_0to5V){
printf("\tAI: AB1771IFE_0to5V\tadapter %d card %d:\tcto: %d dto: %d sclerr: %d %d",
adapter,card,ab_cmd_to[i][adapter][card],
ab_data_to[i][adapter][card],
ab_scaling_error[i][adapter][card],
ab_or_scaling_error[i][adapter][card]);
if (level > 0){
ab_ai_report(type,i,adapter,card,plc_card);
}
}
break;
case (AB_AO_INTERFACE):
printf("\tAO: AB1771OFE\tadapter %d card %d:\tcto: %d dto: %d",
adapter,card,ab_cmd_to[i][adapter][card],
ab_data_to[i][adapter][card]);
if (level > 0 ){
ab_ao_report(AB1771OFE,i,adapter,card,plc_card,&jrval,0);
}
break;
default:
continue;
}
if ((ab_config[i][adapter][card] & AB_INIT_BIT) == 0)
printf(" NOT INITIALIZED\n");
else {
printf("\n");
}
}
}
}
}
ab_bi_report(link,adapter,card,plc_card)
short link,adapter,card,plc_card;
{
unsigned short type;
unsigned long value;
type = ab_config[link][adapter][card] & AB_CARD_TYPE;
ab_bidriver(type,link,adapter,card,plc_card,0xffffffff,&value);
printf(" value=%08.8x",value);
}
ab_bo_report(link,adapter,card)
short link,adapter,card;
{
unsigned short type;
unsigned long value;
type = ab_config[link][adapter][card] & AB_CARD_TYPE;
ab_boread(type,link,adapter,card,&value,0xffffffff);
}
ab_ai_report(type,link,adapter,card,plc_card)
unsigned short type;
short link,adapter,card,plc_card;
{
short i,num_chans;
unsigned short value;
num_chans = ai_num_channels[type];
for(i=0; i<num_chans; i++) {
if(i%8 == 0) printf("\n\t");
ab_aidriver(type,link,adapter,card,i,plc_card,&value,0);
printf(" %4x",value);
}
}
ab_ao_report(type,link,adapter,card,plc_card)
unsigned short type;
short link,adapter,card,plc_card;
{
short i,num_chans;
unsigned short value;
printf("\n\t");
num_chans = ao_num_channels[type];
for(i=0; i<num_chans; i++) {
ab_aoread(type,link,adapter,card,i,plc_card,&value);
printf("%4x",value);
}
}
/*
* abScanTask
*
* Scans the AB IO according to the AB configuration table.
* Entries are made in the AB configuration table when an IO
* interface is attempted from the database scan tasks.
* The sleep time assures that there is at least 1/10 second between the passes.
* The time through the scan loop seems to be minimal so there are no provisions
* for excluding the scan time from the sleep time.
*/
abScanTask(){
register unsigned short adapter,link;
register short pass=0;
register int tmp_op_stat;
struct ab_region *p6008;
int status;
while(TRUE){
pass++;
p6008 =(struct ab_region *)ab_stdaddr;
/* check each link */
for (link=0; link < AB_MAX_LINKS; link++,p6008++){
if(ab_disable) break;
if (p6008s[link] == 0) continue; /* no link */
/* See if we've detected any unsolicited block transfers. */
/* These can result if scanner writes discrete info to a slot */
/* requiring a block transfer. */
if(ab_op_stat[link] & UNSOLICITED_BT){
if (ab_debug != 0)
logMsg("link %x, unsolicited_block xfer\n",link);
p6008->osw = 0; /* scanner depends on us to clear some bits */
}
/* check each adapter */
for (adapter = 0; adapter < AB_MAX_ADAPTERS; adapter++){
if (ab_adapter_status[link][adapter] >= 0)
read_ab_adapter(link,adapter,pass);
}
/* Every second perform a link check to see if any adapters */
/* have changed state. (Don't want to queue up requests if*/
/* they're off) */
if((pass % 10) == 0){
if (link_status(link) != 0){
if(ab_debug)
logMsg("%x link_stat error %x\n",link);
}
}
}
/* run every 1/10 second */
taskDelay(vxTicksPerSecond/10);
}
}
/* ab_reboot_hook - routine to call when IOC is rebooted with a control-x */
int ab_reboot_hook(boot_type)
int boot_type;
{
short i;
static char wait_msg[] = {"I Hate to WAIT"};
register char *pmsg = &wait_msg[0];
/* Stop communication to the Allen-Bradley Scanner Cards */
if (abScanId != 0){
/* flag the analog output and binary IO routines to stop */
ab_disable = 1;
/* delete the scan task stops analog input communication */
taskDelete(abScanId);
/* this seems to be necessary for the AB card to stop talking */
printf("\nReboot: delay ");
for(i = 0; i <= 14; i++){
printf("%c",*(pmsg+i));
taskDelay(20);
}
}
}
/*
*
* ab_reset_task()
*
*
*
*/
void
ab_reset_task()
{
short link,adapter,card;
struct ab_region *pab_region=0;
/* keep track of the status and frequency */
if (reset_cnt < 100){
reset_code[reset_cnt] = pab_region->mail.conf_stat;
reset_cnt++;
}
/* disable scanning during reset */
ab_disable = 1;
printf("Disabled AB Scanner Task\n");
taskDelay(vxTicksPerSecond*2);
/* Signal the Scanner to Reset */
for(link=0; link<AB_MAX_LINKS; link++){
pab_region = p6008s[link];
if(!pab_region) continue;
pab_region->sys_fail_set2 = 0xa0a0;
pab_region->sys_fail_set1 = 0x0080;
printf("Card %d Reset\n",0);
}
ab_reset_wait = 0;
for(link=0; link<AB_MAX_LINKS; link++){
pab_region = p6008s[link];
if(!pab_region) continue;
/*mark all block transfer cards for initialization*/
for(adapter = 0; adapter < AB_MAX_ADAPTERS; adapter++){
for (card = 0; card < AB_MAX_CARDS; card++){
switch (ab_config[link][adapter][card] & AB_INTERFACE_TYPE){
case (AB_AO_INTERFACE):
ab_config[link][adapter][card] |= AB_UPDATE;
break;
case (AB_AI_INTERFACE):
ab_config[link][adapter][card]
&= ~(AB_INIT_BIT | AB_SENT_INIT);
ab_btq_cnt[link][adapter][card] = 0;
break;
default:
break;
}
}
}
while(pab_region->mail.conf_stat != SCANNER_POWERUP){
taskDelay(1);
ab_reset_wait++;
}
}
printf("Link Power Up After %d Ticks\n",ab_reset_wait);
/* Reinitialize the Link */
for(link=0; link<AB_MAX_LINKS; link++){
pab_region = p6008s[link];
if(!pab_region) continue;
link_init(link);
}
/* enable the scanner */
ab_disable = 0;
}
/*
*
* ab_reset()
*
*/
int
ab_reset()
{
int status;
status = taskSpawn("ab_reset",38,0,8000,ab_reset_task);
return status;
}