diff --git a/src/drv/Makefile b/src/drv/Makefile index 62888d534..73502a4cf 100644 --- a/src/drv/Makefile +++ b/src/drv/Makefile @@ -1,15 +1,12 @@ # # $Id$ # -# Lowest Level Directroy Makefile -# by Janet Anderson -# -# $Log$ -# EPICS=../../.. include $(EPICS)/config/CONFIG_BASE -include $(EPICS)/config/RULES_ARCHS +DIRS = ansi old combine + +include $(EPICS)/config/RULES_DIRS diff --git a/src/drv/ansi/Makefile b/src/drv/ansi/Makefile new file mode 100644 index 000000000..76b94a7bf --- /dev/null +++ b/src/drv/ansi/Makefile @@ -0,0 +1,12 @@ +# +# $Id$ +# +# +# + +EPICS=../../../.. + +include $(EPICS)/config/CONFIG_BASE + +include $(EPICS)/config/RULES_ARCHS + diff --git a/src/drv/ansi/Makefile.Vx b/src/drv/ansi/Makefile.Vx new file mode 100644 index 000000000..8e80df765 --- /dev/null +++ b/src/drv/ansi/Makefile.Vx @@ -0,0 +1,18 @@ +# +# $Id$ +# + +EPICS = ../../../../.. +include Target.include +include $(EPICS)/config/CONFIG_BASE + +USR_CFLAGS = -ansi + +VX_WARN_YES = -Wall -pedantic + +SRCS.c += ../drvAb.c + +TARGETS += drvAb.o + +include $(EPICS)/config/RULES.Vx + diff --git a/src/drv/ansi/drvAb.c b/src/drv/ansi/drvAb.c new file mode 100644 index 000000000..294d3546a --- /dev/null +++ b/src/drv/ansi/drvAb.c @@ -0,0 +1,1714 @@ +/* drvAb.c - Driver Support Routines for Allen Bradley */ +/* + * Interface to the Allen-Bradley Remote Serial IO + * + * Author: Bob Dalesio + * Date: 6-21-88 + * Major Revision March 1995. Bob Dalesio and Marty Kraimer + * + * 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 + * The database uses card numbers 0-11 to address slots 1-12 + * It is possible to use 1 slot and 1/2 slot addressing. + * For 1 slot addressing assign card=slot*2 + * For 1/2 slot addressing assign card = slot*4 + * 3. The binary io is memory mapped and the analog io is handled through + * block transfers of whole cards of data. + * 4. The driver spawns a task for each 6008 card in VME and scans cards as + * the database claims they exist - there is no way to poll the hardware + * to determine what is present. + * 4. A flag is used to limit the requests to the analog input modules. This + * assures that only one request is outstanding at any time. + * + * Modification Log: + * ----------------- + * .19 08-01-89 lrd changed sc_queue and bt_queue to always set + * length to 0 for compatibility with PLC-5 + * communication + * .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 12 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. + **************************************************************************** + ** 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. * + ***************************************************************************** + * .41 05-16-91 lrd have the ab_scan_task driver analog outputs + * periodically as well as on change + * .56 06-30-93 mrk After 3 attempts to queue request reinitialize + * .64 09-16-93 mrk ab_reset: all links; only reset scanner. + * ???????????? ??? fix scaling on IXE 4-20 Milli amp + * .65 04-04-94 lrd return the value for overrange and underrange + * .66 04-04-94 lrd put the binary change of state into the AB scan task + * .67 04-04-94 lrd support N serial cards + * - put all link and adapter information into structure + * - allocate structures as needed + * .68 09-29-94 lrd removed historical comments of no current significance + * .69 10-20-94 lrd moved all I/O board specific stuff up to the device layer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +#undef LOCAL +#define LOCAL + +#define maxCmdTrys 3 +#define AB_INT_LEVEL 5 +#define MAX_AB_ADAPTERS 8 +#define MAX_GROUPS_PER_ADAPTER 8 +#define MAX_CARDS_PER_ADAPTER 16 + +/*Definitions for dual ported memory on scanner interface card*/ +/* Command Word */ +#define AUTO_CONF 0x10 +#define SCAN_LIST 0x11 +#define SET_UP 0x13 +#define SET_MODE 0x20 +#define LINK_STATUS 0x21 +#define AB_READ 0x01 +#define AB_WRITE 0x02 +/* osw - operating status word definitions */ +#define PROGRAM_MODE 0x01 /* program/reset mode */ +#define TEST_MODE 0x02 /* test/reset mode */ +#define RUN_MODE 0x04 /* run mode */ +#define DEBUG_MODE 0x08 /* we are debugging */ +#define UNSOLICITED_BT 0x10 /* detected block xfer we didn't want */ +#define BTS_QUEUED 0x20 /* block xfers queued */ +#define ADAPTER_FAULT 0x40 /* at least one faulted adapter list */ +#define ADAP_FLT_CHNG 0x80 /* Adapter status change*/ +/* Confirmation Status Word */ +#define SCANNER_POWERUP 0x90 /* power up status */ +#define BT_ACCEPTED 0x2f /* block trans accepted by scanner */ +#define BT_QUEUE_FULL 0x14 /* Queue Full*/ +#define BT_TIMEOUT 0x23 /* block transfer timeout */ +/* these are used for the SET_UP command */ +#define DEF_RATE 0x01 /* default baud rate 57.6K */ +#define FAST_RATE 0x02 /* 115.2 KB */ +#define NO_CHANGE 0xff /* no change */ +#define DEBUG 0x01 /* debug - turn off watchdog timer */ +#define AB_INT_ENABLE 0x00 /* interrupt enabled */ +#define AB_SYSFAIL_DISABLE 0x01 /* disable VMEbus SYSFAIL signal */ + +/* DUAL PORTED MEMORY AREA DEFINITION */ +/* mail box definition */ +typedef volatile struct dp_mbox{ + unsigned short conf_stat; /* confirmation status word */ + unsigned short command; /* command word */ + unsigned short address; /* module address */ + unsigned short bt_tag; /* block transfer tag number */ + unsigned short resv[9]; /* 18 bytes reserved */ + unsigned char dummy; + unsigned char fl_lock; /* semaphore word byte */ + unsigned short data_len; /* length of data sent/returned */ + unsigned short msg[64]; /*mail box message */ +}dp_mbox; + +/* entire region with the mailbox */ +typedef volatile struct ab_region { + unsigned short oit[64]; /* output image table */ + unsigned short iit[64]; /* input image table */ + unsigned short osw; /* operating status word */ + dp_mbox mail; /* dual port mail box */ + unsigned short gda[1872-66]; /* unused part gen data area */ + unsigned short sys_fail_set1; /* 1st byte to recover from SYSFAIL */ + unsigned short sys_fail_set2; /* 2nd byte to recover from SYSFAIL */ + unsigned char vmeid[60]; /* scanner id */ + unsigned short sc_intr; /* to interrupt the scanner */ + unsigned short sc_pad; /* last word in scanner shared mem */ +}ab_region; + +typedef struct { + abStatus btStatus; + unsigned short cmd; + unsigned short *pmsg; + unsigned short msg_len; +}btInfo; + +typedef enum { + stateInit,stateInitBTwait,stateCountdown,stateBT,stateBTwait +}scanState; +typedef struct { + scanState state; + unsigned short update_rate; + unsigned short *pread_msg; + unsigned short *pwrite_msg; + unsigned short read_msg_len; + unsigned short write_msg_len; + unsigned short state_cnt; /* number of consecutive time in state*/ + unsigned short down_cnt; /* down_counter for scanning */ + unsigned short bt_to; /* data transfer timed out */ + unsigned short bt_fail; /*block transfer bad status*/ + unsigned short init_ctr; /* number times the card initialized*/ +}scanInfo; + +typedef struct { + unsigned short link; + unsigned short adapter; + unsigned short card; + cardType type; + const char *card_name; + abNumBits nBits; + BOOL needsUpdate; + BOOL active; + abStatus status; + SEM_ID card_sem; + btInfo *pbtInfo; + scanInfo *pscanInfo; + void *userPvt; + void (*callback)(void *pcard); /*user's callback routine*/ + void (*bocallback)(void *pcard); /*user's callback routine*/ + unsigned long diprev; +}ab_card; + +typedef struct{ + unsigned short adapter; + BOOL adapter_online; + BOOL adapter_status_change; + ab_card **papcard; /*pointer to array of pointers*/ + SEM_ID adapter_sem; /* request talk to scanner*/ +}ab_adapter; + +typedef struct { + void *base_address; + unsigned short baud_rate; + unsigned short int_vector; + unsigned short int_level; + unsigned short scan_list_len; + unsigned char scan_list[64]; +} ab_config; + +typedef struct { + unsigned short list_len; + unsigned short status[32]; + unsigned char list[64]; +}ab_link_status; + +typedef struct{ + unsigned short link; + unsigned short ab_disable; + SEM_ID request_sem; /* request talk to scanner*/ + SEM_ID ab_cmd_sem; /* transfer complete semaphore */ + SEM_ID ab_data_sem; /* transfer complete semaphore */ + int abScanId; /* id of the Allen-Bradley scan task */ + int abDoneId; /* id of the Allen-Bradley done task */ + unsigned short sclock_to; /* Timeout trying to lock mailbox*/ + unsigned short sccmd_to; /* command timeout on semTake*/ + unsigned short sc_fail; /* Scanner Command Failure*/ + unsigned short intr_cnt; /* interrupt counter*/ + unsigned short intrSec; /* interrupts per second */ + unsigned short bt_cnt; /* Block Transfer counter*/ + unsigned short btSec; /* block transfers per second*/ + ab_config *pconfig; + char firmware_info[96]; /* NOTE: available only temporarily */ + ab_region *vme_addr; /* ptr to the memory of interface */ + ab_adapter **papadapter; + ab_link_status *plink_status; + BOOL initialCallback; + BOOL initialized; +}ab_link; + +/*Local variables and function prototypes*/ +LOCAL ab_link **pab_links=NULL; +LOCAL unsigned int max_ab_6008s = 2; + +/* forward references */ +LOCAL void *abCalloc(size_t nobj,size_t size); +LOCAL ab_link *allocLink(unsigned short link); +LOCAL void ab_intr(int link); +LOCAL int sc_lock(ab_link *plink); +LOCAL int sc_waitcmd(ab_link *plink); +LOCAL void sc_conferr(ab_link *plink); +LOCAL void sc_unlock(ab_link *plink); +LOCAL void uctrans (unsigned char *from,unsigned char *to,unsigned short n); +LOCAL void ustrans (unsigned short *from,unsigned short *to, + unsigned short nwords); +LOCAL void di_read(ab_card *pcard,unsigned long *pvalue); +LOCAL void do_read(ab_card *pcard,unsigned long *pvalue); +LOCAL void do_write(ab_card *pcard,unsigned long value); +LOCAL abStatus bt_queue(unsigned short command,ab_card *pcard, + unsigned short *pmsg, unsigned short msg_len); +LOCAL int link_status(ab_link *plink); +LOCAL int ab_reboot_hook(int boot_type); +LOCAL void config_init(ab_link *plink); +LOCAL int ab_driver_init(); +LOCAL int link_init(ab_link *plink); +LOCAL void abScanTask(ab_link *plink); +LOCAL void abDoneTask(ab_link *plink); +LOCAL void read_ab_adapter(ab_link *plink, ab_adapter *padapter); +LOCAL void ab_reset_task(ab_link *plink); +LOCAL long ab_io_report(int level); + +/*Global variables */ +unsigned int ab_debug=0; + +LOCAL char *statusMessage[] = { + "Success","New Card","Card Conflict","No Card", + "Card not initialized","block transfer queued", + "Card Busy","Timeout","Link Down","Failure"}; +char **abStatusMessage = statusMessage; + +LOCAL char *numBitsMessage[] = { + "nbits_Not_defined"," 8_Bit","16_Bit","32_Bit"}; +char **abNumBitsMessage = numBitsMessage; +/*End Global variables*/ + +/*Beginning of DSET routines */ + +LOCAL long report(); +LOCAL long init(); +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvAb={ + 2, + report, + init +}; + +LOCAL long report(int level) +{ + return(ab_io_report(level)); +} +LOCAL long init() +{ + + return(ab_driver_init()); +} + +LOCAL void *abCalloc(size_t nobj,size_t size) +{ + void *p; + + p=calloc(nobj,size); + if(p) return(p); + logMsg("drvAb: calloc failure\n",0,0,0,0,0,0); + taskSuspend(0); + return(NULL); +} +LOCAL ab_link *allocLink(unsigned short link) +{ + ab_link *plink; + + if(pab_links[link]) return(pab_links[link]); + plink = abCalloc(1,sizeof(ab_link)); + plink->ab_disable = TRUE; + plink->papadapter = abCalloc(MAX_AB_ADAPTERS,sizeof(ab_adapter *)); + plink->plink_status = abCalloc(1,sizeof(ab_link_status)); + plink->link = link; + config_init(plink); + pab_links[link] = plink; + return(plink); +} + +/* + * AB_INTR + * + * 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. + * dual-ported memory lock is controlled in a much different fashion than it is. + */ +LOCAL void ab_intr(int link) +{ + ab_link *plink; + ab_region *p6008; + dp_mbox *pmb; + + if(!pab_links) return; /*Called before init complete*/ + plink = pab_links[link]; + if(!plink) return; /*Called before init complete*/ + p6008 = plink->vme_addr; + if(!p6008) return; + pmb = &p6008->mail; + if((pmb->fl_lock&0x80)==0) {/*Should NEVER be True*/ + logMsg("drvAb: Interrupt but fl_lock not locked\n", 0,0,0,0,0,0); + } + if(p6008->osw & UNSOLICITED_BT){ + if (ab_debug != 0) + logMsg("link %x, unsolicited_block xfer\n",plink->link,0,0,0,0,0); + /* scanner depends on us to clear some bits */ + p6008->osw = 0; + } + plink->intr_cnt++; + if((pmb->command==AB_WRITE || pmb->command==AB_READ) + && (pmb->conf_stat != BT_ACCEPTED) && (pmb->conf_stat != BT_QUEUE_FULL)) { + semGive(plink->ab_data_sem); + } else { + semGive(plink->ab_cmd_sem); + } +} + +/*Routines for communication with scanner*/ +LOCAL int sc_lock(ab_link *plink) +{ + ab_region *p6008 = plink->vme_addr; + dp_mbox *pmb = &p6008->mail; + unsigned short lock_stat = 0; + int i; + + if(ab_debug>3)printf("sc_lock\n"); + semTake(plink->request_sem,WAIT_FOREVER); + /* try to to lock the dual port memory */ + for(i=0; ifl_lock)) == TRUE) break; + taskDelay(1); + } + if(lock_stat == FALSE) { + if(ab_debug) + printf("drvAb: link %x sc_lock failure\n", plink->link); + plink->sclock_to += 1; + pmb->fl_lock = 0; /*Unlock to attempt to recover. May not work*/ + semGive(plink->request_sem); + return (ERROR); + } + return(0); +} + +LOCAL int sc_waitcmd(ab_link *plink) +{ + ab_region *p6008 = plink->vme_addr; + dp_mbox *pmb = &p6008->mail; + unsigned short savecmd; + int status; + int ntry; + + if(ab_debug>3)printf("sc_waitcmd\n"); + savecmd = pmb->command; + pmb->conf_stat = 0x000f; + ntry = 0; + p6008->sc_intr = 1; /*Wake up scanner*/ + status = semTake(plink->ab_cmd_sem,vxTicksPerSecond); + if(status!=OK) { + plink->sccmd_to++; + if(ab_debug) printf( + "drvAb: sc_waitcmd timeout link %hu command %4x\n", + plink->link,savecmd); + sc_unlock(plink); + return(ERROR); + } + if(pmb->command != savecmd){ + plink->sc_fail++; + if(ab_debug) printf( + "drvAb: bad cmd response. link %hu sent %4x received %4x\n", + plink->link,savecmd,pmb->command); + sc_unlock(plink); + return(ERROR); + } + return(0); +} + +LOCAL void sc_conferr(ab_link *plink) +{ + ab_region *p6008 = plink->vme_addr; + dp_mbox *pmb = &p6008->mail; + + if(ab_debug>3)printf("sc_conferr\n"); + plink->sc_fail++; + if(ab_debug) printf("drvAb: link %hu conf_stat error %4x\n", + plink->link,pmb->conf_stat); + sc_unlock(plink); + return; +} + +LOCAL void sc_unlock(ab_link *plink) +{ + ab_region *p6008 = plink->vme_addr; + dp_mbox *pmb = &p6008->mail; + + if(ab_debug>3)printf("sc_unlock\n"); + pmb->fl_lock = 0; + semGive(plink->request_sem); +} + +/*Routines to transfer data to/from mailbox*/ +/* Unsigned character transfer*/ +LOCAL void uctrans (unsigned char *from,unsigned char *to,unsigned short n) +{ + int i; + for (i=0;ilink; + unsigned short adapter = pcard->adapter; + unsigned short card = pcard->card; + unsigned short group,slot; + unsigned short rack_offset; + unsigned short *pimage; + unsigned long value; + + rack_offset = (adapter * MAX_GROUPS_PER_ADAPTER); + group = card/2; slot = card - group*2; + pimage = &(pab_links[link]->vme_addr->iit[rack_offset + group]); + if (pcard->nBits == abBit8) { + value = *pimage; + if(slot==1) value >>= 8; + value &= 0x00ff; + *pvalue = value; + }else if(pcard->nBits == abBit16){ + *pvalue = *pimage; + } else if (pcard->nBits == abBit32) { + value = *(pimage+1); + value <<= 16; + value += *pimage; + *pvalue = value; + } +} + +/* read the values from the hardware into the local word */ +LOCAL void do_read(ab_card *pcard,unsigned long *pvalue) +{ + unsigned short link = pcard->link; + unsigned short adapter = pcard->adapter; + unsigned short card = pcard->card; + unsigned short group,slot; + unsigned short rack_offset; + unsigned short *pimage; + unsigned long value; + + rack_offset = (adapter * MAX_GROUPS_PER_ADAPTER); + group = card/2; slot = card - group*2; + pimage = &(pab_links[link]->vme_addr->oit[rack_offset + group]); + if (pcard->nBits == abBit8) { + value = *pimage; + if(slot==1) value >>= 8; + value &= 0x00ff; + *pvalue = value; + }else if(pcard->nBits == abBit16){ + *pvalue = *pimage; + } else if (pcard->nBits == abBit32) { + value = *(pimage+1); + value <<= 16; + value += *pimage; + *pvalue = value; + } +} + +/* write the values to the hardware from the local word */ +LOCAL void do_write(ab_card *pcard,unsigned long value) +{ + unsigned short link = pcard->link; + unsigned short adapter = pcard->adapter; + unsigned short card = pcard->card; + unsigned short group,slot; + unsigned short rack_offset; + unsigned short *pimage; + + rack_offset = (adapter * MAX_GROUPS_PER_ADAPTER); + group = card/2; slot = card - group*2; + pimage = &(pab_links[link]->vme_addr->oit[rack_offset + group]); + if (pcard->nBits == abBit8) { + if(slot==0) { + *pimage = (*pimage & 0xff00) | ((unsigned short)value & 0x00ff); + } else { + value <<= 8; + *pimage = (*pimage & 0x00ff) | ((unsigned short)value & 0xff00); + } + }else if(pcard->nBits == abBit16){ + *pimage = (unsigned short)value; + } else if (pcard->nBits == abBit32) { + *pimage = (unsigned short)value; + *(pimage+1) = (unsigned short)(value >> 16); + } +} + +LOCAL abStatus bt_queue(unsigned short command,ab_card *pcard, + unsigned short *pmsg, unsigned short msg_len) +{ + ab_link *plink = pab_links[pcard->link]; + ab_adapter *padapter = plink->papadapter[pcard->adapter]; + btInfo *pbtInfo = pcard->pbtInfo; + ab_region *p6008 = plink->vme_addr; + dp_mbox *pmb = &p6008->mail; + unsigned short *pmb_msg = &pmb->msg[0]; + int status; + + if(!padapter->adapter_online) return(abLinkDown); + pbtInfo->cmd = command; + pbtInfo->pmsg = pmsg; + pbtInfo->msg_len = msg_len; + status = sc_lock(plink); + if(status) return(abFailure); + pmb->command = command; + pmb->address = (padapter->adapter << 4) + pcard->card; + pmb->data_len = msg_len; + if(pbtInfo->cmd==AB_WRITE) ustrans(pmsg,pmb_msg,msg_len); + status = sc_waitcmd(plink); + if(status) return(abFailure); + status = pmb->conf_stat; + if(status != BT_ACCEPTED){ + sc_conferr(plink); + if (status == BT_QUEUE_FULL){ + printf("drvAb: BT_QUEUE_FULL link %hu\n",plink->link); + taskDelay(vxTicksPerSecond/10); + } + return(abFailure); + } + pbtInfo->btStatus = abBtqueued; + sc_unlock(plink); + return(abBtqueued); +} + +/* + * 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 + */ +LOCAL int link_status(ab_link *plink) +{ + short i; + ab_adapter *padapter; + dp_mbox *pmb; + ab_link_status *plink_status = plink->plink_status; + int status; + int ntry; + + /* initialize the pointer to the dual ported memory */ + pmb = (dp_mbox *)(&plink->vme_addr->mail); + /* get the link status */ + for(ntry=0; ntrycommand = LINK_STATUS; + pmb->data_len = 0; + status = sc_waitcmd(plink); + if(status) continue; + if(pmb->conf_stat != 0) { + sc_conferr(plink); + continue; + } + plink_status->list_len = pmb->data_len; + ustrans(pmb->msg,plink_status->status,32); + uctrans((unsigned char *)(pmb->msg+32),plink_status->list,64); + sc_unlock(plink); + break; + } + if(ntry>=maxCmdTrys) { + if(ab_debug)printf("abDrv: link_status failed link %hu\n",plink->link); + return(ERROR); + } + /* check each adapter on this link */ + for (i = 0; i< AB_MAX_ADAPTERS; i++){ + padapter = plink->papadapter[i]; + + if(!padapter) continue; + /* good status */ + if (plink_status->status[i*4] & 0x70){ + if (!padapter->adapter_online) { + printf("link %d adapter %d change bad to good\n", + plink->link,i); + padapter->adapter_status_change=TRUE; + } + padapter->adapter_online = TRUE; + }else { /* bad status */ + if (padapter->adapter_online){ + printf("link %d adapter %d change good to bad\n", + plink->link,i); + padapter->adapter_status_change=TRUE; + } + padapter->adapter_online = FALSE; + } + } + return(0); +} + +/* ab_reboot_hook - routine to call when IOC is rebooted with a control-x */ +LOCAL int ab_reboot_hook(int boot_type) +{ + short i; + ab_link *plink; + + /* Stop communication to the Allen-Bradley Scanner Cards */ + /* delete the scan task stops analog input communication */ + for (i=0; iab_disable = 1; + taskDelete(pab_links[i]->abScanId); + } + } + /* this seems to be necessary for the AB card to stop talking */ + printf("\nReboot: drvAb delay to clear interrupts off backplane\n"); + taskDelay(vxTicksPerSecond*2); + return(0); +} + + +LOCAL void config_init(ab_link *plink) +{ + ab_config *pconfig = plink->pconfig; + int i; + ab_region *p6008; + + if(!pconfig) plink->pconfig = pconfig = abCalloc(1,sizeof(ab_config)); + p6008 = (ab_region *)AB_BASE_ADDR; + p6008 += plink->link; + pconfig->base_address = (void *)p6008; + pconfig->baud_rate = DEF_RATE; + pconfig->int_vector = AB_VEC_BASE + plink->link; + pconfig->int_level = AB_INT_LEVEL; + pconfig->scan_list_len = 8; + for(i=0; i< AB_MAX_ADAPTERS; i++) pconfig->scan_list[i] = (i<<2); +} + +int abConfigNlinks(int nlinks) +{ + if(pab_links) { + printf("abConfigNlinks Illegal call. Must be first abDrv related call"); + return(-1); + } + max_ab_6008s = nlinks; + pab_links = abCalloc(max_ab_6008s,sizeof(ab_link *)); + return(0); +} + +int abConfigVme(int link, int base, int vector, int level) +{ + ab_link *plink; + ab_config *pconfig; + + if(link<0 || link>=max_ab_6008s) return(-1); + plink = pab_links[link]; + if(!plink) plink = allocLink(link); + pconfig = plink->pconfig; + pconfig->base_address = (void *)base; + pconfig->int_vector = vector; + pconfig->int_level = level; + return(0); +} + +int abConfigBaud(int link, int baud) +{ + ab_link *plink; + ab_config *pconfig; + + if(link<0 || link>=max_ab_6008s) return(-1); + plink = pab_links[link]; + if(!plink) plink = allocLink(link); + pconfig = plink->pconfig; + if(baud = 0) pconfig->baud_rate = DEF_RATE; + else pconfig->baud_rate = FAST_RATE; + return(0); +} + +int abConfigScanList(int link, int scan_list_len, char *scan_list) +{ + ab_link *plink; + ab_config *pconfig; + + if(link<0 || link>=max_ab_6008s) return(-1); + plink = pab_links[link]; + if(!plink) plink = allocLink(link); + pconfig = plink->pconfig; + pconfig->scan_list_len = scan_list_len; + memset(pconfig->scan_list,'\0',64); + memcpy(pconfig->scan_list,scan_list,scan_list_len); + return(0); +} + +LOCAL int ab_driver_init() +{ + unsigned short cok; + unsigned short link; + ab_link *plink; + ab_region *p6008; + ab_config *pconfig; + int vxstatus; + long status; + int got_one; + + if(!pab_links) pab_links = abCalloc(max_ab_6008s,sizeof(ab_link *)); + /* check if any of the cards are there */ + got_one = 0; + for (link = 0; link < max_ab_6008s; link++,p6008++){ + plink = pab_links[link]; + if(plink) { + p6008 = plink->pconfig->base_address; + } else { + p6008 = (void *)AB_BASE_ADDR; + p6008 += link; + } + status = devRegisterAddress("drvAb",atVMEA24,(void *)p6008, + sizeof(ab_region),(void **)&p6008); + if(status) { + errMessage(status,"drvAb"); + return(status); + } + if (vxMemProbe((char *)p6008,READ,1,(char *)&cok)==ERROR) { + continue; + } + got_one = 1; + if(!plink) plink = allocLink(link); + plink->vme_addr = p6008; /* set AB card address */ + pconfig = plink->pconfig; + if(!(plink->request_sem = semBCreate(SEM_Q_FIFO,SEM_EMPTY))){ + printf("AB_DRIVER_INIT: semBcreate failed"); + taskSuspend(0); + } + if(!(plink->ab_cmd_sem = semBCreate(SEM_Q_FIFO,SEM_EMPTY))){ + printf("AB_DRIVER_INIT: semBcreate failed"); + taskSuspend(0); + } + if(!(plink->ab_data_sem = semBCreate(SEM_Q_FIFO,SEM_EMPTY))){ + printf("AB_DRIVER_INIT: semBcreate failed"); + taskSuspend(0); + } + status = devConnectInterrupt(intVME,pconfig->int_vector, + ab_intr,(void *)(int)link); + if(status) { + errMessage(status,"drvAb"); + return(status); + } + status = devEnableInterruptLevel(intVME,pconfig->int_level); + if(status) { + errMessage(status,"drvAb"); + taskSuspend(0); + } + /* initialize the serial link */ + if(link_init(plink)) { + /*No use proceeding*/ + continue; + } + plink->initialized = TRUE; + vxstatus = taskSpawn(ABSCAN_NAME,ABSCAN_PRI,ABSCAN_OPT, + ABSCAN_STACK,(FUNCPTR)abScanTask,(int)plink, + 0,0,0,0,0,0,0,0,0); + if(vxstatus < 0){ + printf("AB_DRIVER_INIT: failed taskSpawn"); + taskSuspend(0); + } + plink->abScanId = vxstatus; + taskwdInsert(plink->abScanId,NULL,NULL); + vxstatus = taskSpawn(ABDONE_NAME,ABDONE_PRI,ABDONE_OPT, + ABDONE_STACK,(FUNCPTR)abDoneTask,(int)plink, + 0,0,0,0,0,0,0,0,0); + if(vxstatus < 0){ + printf("AB_DRIVER_INIT: failed taskSpawn"); + taskSuspend(0); + } + plink->abDoneId = vxstatus; + taskwdInsert(plink->abDoneId,NULL,NULL); + plink->ab_disable = FALSE; + } + /* put in hook for disabling communication for a reboot */ + if (got_one) rebootHookAdd(ab_reboot_hook); + return(0); +} + +/* + * link_init + * + * establish the communication link with the AB scanner + */ +LOCAL int link_init(ab_link *plink) +{ + ab_region *p6008 = plink->vme_addr; + dp_mbox *pmb = &p6008->mail; + ab_config *pconfig=plink->pconfig; + int status; + int ntry; + + /* the scanner comes up with the dual ported memory locked */ + pmb->fl_lock = 0; /* so unlock it */ + /*clear request semaphore*/ + semGive(plink->request_sem); + if(pmb->conf_stat != SCANNER_POWERUP){ + /* This link must already be initialized. We're done */ + if (ab_debug){ + printf("Link %x already initialized\n", plink->link); + } + return(0); + } + if(ab_debug) printf("drvAb: link %x, powerup...\n",plink->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(plink->firmware_info,(char *)&pmb->msg[0]); + /* setup scanner */ + for(ntry=0; ntrycommand = SET_UP; + pmb->data_len = 4; + pmb->msg[0] = (pconfig->baud_rate<<8) | 0; + pmb->msg[1] = (DEBUG<<8) | pconfig->int_level; + pmb->msg[2] = (pconfig->int_vector<<8) | AB_INT_ENABLE; + pmb->msg[3] = (AB_SYSFAIL_DISABLE<<8) | 0; + status = sc_waitcmd(plink); + if(status) continue; + if(pmb->conf_stat != 0) { + sc_conferr(plink); + continue; + } + break; + } + if(ntry>=maxCmdTrys) { + printf("abDrv: SET_UP failed link %hu\n",plink->link); + return(ERROR); + } + sc_unlock(plink); + /* 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 */ + /* 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. */ + /* set scan list*/ + for(ntry=0; ntrycommand = SCAN_LIST; + pmb->data_len = pconfig->scan_list_len; + uctrans(pconfig->scan_list,(unsigned char *)pmb->msg, + pconfig->scan_list_len); + status = sc_waitcmd(plink); + if(status) continue; + if(pmb->conf_stat != 0) { + sc_conferr(plink); + continue; + } + break; + } + if(ntry>=maxCmdTrys) { + printf("abDrv: SCAN_LIST failed link %hu\n",plink->link); + return(ERROR); + } + sc_unlock(plink); + /* place the scanner into run mode */ + for(ntry=0; ntrycommand = SET_MODE; + pmb->msg[0] = (RUN_MODE<<8); + status = sc_waitcmd(plink); + if(status) continue; + if(pmb->conf_stat != 0) { + sc_conferr(plink); + continue; + } + break; + } + if(ntry>=maxCmdTrys) { + printf("abDrv: SET_MODE failed link %hu\n",plink->link); + return(ERROR); + } + sc_unlock(plink); + return(0); +} + +/* + * 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. + */ +LOCAL void abScanTask(ab_link *plink) +{ + unsigned short adapter; + ab_adapter *padapter; + unsigned int pass = 0; + BOOL madeInitialCallback = FALSE; + unsigned long tickNow,tickBeg,tickDiff; + + tickBeg = tickGet(); + while(TRUE){ + /* run every 1/10 second */ + taskDelay(vxTicksPerSecond/10); + if(plink->ab_disable) continue; + /* 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(plink) != 0){ + if(ab_debug) printf("%x link_stat error\n",plink->link); + } + } + if(!madeInitialCallback && interruptAccept) + plink->initialCallback = TRUE; + pass++; + /*recompute intrSec and btSec about every 10 seconds*/ + if((pass %100) == 0) { + tickNow = tickGet(); + if(tickNow > tickBeg){/*skip overflows*/ + tickDiff = tickNow - tickBeg; + /*round to nearest counts/sec */ + plink->intrSec=((plink->intr_cnt*vxTicksPerSecond)+5)/tickDiff; + plink->btSec=((plink->bt_cnt*vxTicksPerSecond)+5)/tickDiff; + } + tickBeg = tickNow; + plink->intr_cnt = 0; + plink->bt_cnt = 0; + } + for (adapter = 0; adapter < AB_MAX_ADAPTERS; adapter++){ + padapter = plink->papadapter[adapter]; + if(!padapter) continue; + read_ab_adapter(plink,padapter); + padapter->adapter_status_change = FALSE; + } + if(plink->initialCallback) { + plink->initialCallback = FALSE; + madeInitialCallback = TRUE; + } + } +} + +LOCAL void read_ab_adapter(ab_link *plink, ab_adapter *padapter) +{ + unsigned short card; + unsigned long value; + ab_card *pcard; + scanInfo *pscanInfo; + btInfo *pbtInfo; + abStatus btStatus=0; + + /* each card */ + for (card = 0; card < AB_MAX_CARDS; card++){ + pcard = padapter->papcard[card]; + if (!pcard) continue; + if(!pcard->active) continue; + if(pcard->type==typeBt) continue; + if(pcard->type==typeBi || pcard->type==typeBo || pcard->type==typeBiBo){ + if(pcard->type==typeBi || pcard->type==typeBiBo) { + di_read(pcard,&value); + if ((value != pcard->diprev)||padapter->adapter_status_change){ + pcard->diprev = value; + if(pcard->callback) (*pcard->callback)((void *)pcard); + } + } + if(pcard->type==typeBo || pcard->type==typeBiBo) { + if(padapter->adapter_status_change) { + if(pcard->bocallback) (*pcard->bocallback)((void *)pcard); + } + } + if(plink->initialCallback) { + if(pcard->callback) (*pcard->callback)((void *)pcard); + if(pcard->bocallback) (*pcard->bocallback)((void *)pcard); + } + continue; + } + /*If we get here card is typeAi or typeAo*/ + pscanInfo = pcard->pscanInfo; + if(!pscanInfo) continue; + pbtInfo = pcard->pbtInfo; + if(!pbtInfo) continue; + if(padapter->adapter_status_change){ + if(!padapter->adapter_online) { + pcard->status = abNotInitialized; + if(pcard->callback) (*pcard->callback)((void *)pcard); + continue; + } + pscanInfo->state = stateInit; + } + if(!padapter->adapter_online) continue; + switch(pscanInfo->state) { + case stateInit: + if(pcard->status !=abNotInitialized) { + pcard->status = abNotInitialized; + if(pcard->callback) (*pcard->callback)((void *)pcard); + } + if(pcard->type==typeAo) { + btStatus = bt_queue(AB_READ,pcard,pscanInfo->pread_msg, + pscanInfo->read_msg_len); + }else { + btStatus = bt_queue(AB_WRITE,pcard,pscanInfo->pwrite_msg, + pscanInfo->write_msg_len); + } + if(btStatus==abBtqueued) { + pscanInfo->state_cnt = 0; + pscanInfo->state = stateInitBTwait; + pscanInfo->init_ctr++; + } else { + pscanInfo->bt_fail++; + pscanInfo->state_cnt++; + } + break; + case stateInitBTwait: + if(pbtInfo->btStatus==abBtqueued) { + pscanInfo->state_cnt++; + if(pscanInfo->state_cnt < 15) break; + } + if(pbtInfo->btStatus!=abSuccess) { + if(btStatus==abTimeout) pscanInfo->bt_to++; + else pscanInfo->bt_fail++; + pscanInfo->state_cnt = 0; + pscanInfo->state = stateInit; + break; + } + pscanInfo->state_cnt = 0; + pscanInfo->state = stateBT; + if(pcard->type==typeAi) { + pcard->needsUpdate = TRUE; + } else { + /*Give device support time to set output words*/ + pscanInfo->down_cnt = pscanInfo->update_rate; + } + /*break left out on purpose*/ +repeat_countdown: + case stateCountdown: + if(pcard->needsUpdate) pscanInfo->down_cnt = 0; + if(pscanInfo->down_cnt>0) pscanInfo->down_cnt--; + if(pscanInfo->down_cnt>0) continue; + pscanInfo->state_cnt = 0; + pscanInfo->state = stateBT; + /*break left out on purpose*/ + case stateBT: + if(pcard->type==typeAi) { + btStatus = bt_queue(AB_READ,pcard,pscanInfo->pread_msg, + pscanInfo->read_msg_len); + }else { + btStatus = bt_queue(AB_WRITE,pcard,pscanInfo->pwrite_msg, + pscanInfo->write_msg_len); + } + if(btStatus!=abBtqueued) { + pscanInfo->state_cnt++; + /*After 15 trys reinitialize*/ + if(pscanInfo->state_cnt > 15) { + pcard->status = abFailure; + pscanInfo->bt_fail++; + pscanInfo->state_cnt = 0; + pscanInfo->state = stateInit; + } + break; + } + pscanInfo->state_cnt = 0; + pscanInfo->state = stateBTwait; + break; + case stateBTwait: + if(pbtInfo->btStatus==abBtqueued) { + pscanInfo->state_cnt++; + if(pscanInfo->state_cnt < 30) break; + } + if(pbtInfo->btStatus!=abSuccess) { + pcard->status = abFailure; + if(btStatus==abTimeout) pscanInfo->bt_to++; + else pscanInfo->bt_fail++; + pscanInfo->state_cnt = 0; + pscanInfo->state = stateInit; + break; + } + pcard->status = abSuccess; + pscanInfo->state_cnt = 0; + pscanInfo->down_cnt = pscanInfo->update_rate; + pscanInfo->state = stateCountdown; + if(pcard->callback) (*pcard->callback)((void *)pcard); + goto repeat_countdown; + } + } +} + +LOCAL void abDoneTask(ab_link *plink) +{ + unsigned short adapter; + unsigned short card; + ab_adapter *padapter; /* adapter data structure */ + ab_card *pcard; /* card data structure */ + btInfo *pbtInfo; + dp_mbox *pmb = &plink->vme_addr->mail; + unsigned short *pmb_msg = &pmb->msg[0]; + + while(TRUE) { + semTake(plink->ab_data_sem,WAIT_FOREVER); + /* Must check all data returned by hardware interface*/ + card = pmb->address & 0x0f; + adapter = (pmb->address & 0x70) >> 4; + if(card >= MAX_CARDS_PER_ADAPTER || adapter>=MAX_AB_ADAPTERS) { + if(ab_debug) + printf("drvAb: scanner returned bad address %4x\n", + pmb->address); + pmb->fl_lock = 0; + continue; + } + padapter = plink->papadapter[adapter]; + if(!padapter) { + if(ab_debug) + printf("abDrv:abDoneTask padapter=NULL adapter=%d\n", + adapter); + pmb->fl_lock = 0; + continue; + } + pcard = padapter->papcard[card]; + if(!pcard) { + if(ab_debug) + printf("abDrv: abDoneTask pcard=NULL card=%d adapter=%d\n", + card,adapter); + pmb->fl_lock = 0; + continue; + } + plink->bt_cnt++; + pbtInfo = pcard->pbtInfo; + /* block transfer failure */ + if (pmb->conf_stat != 0){ + if (pmb->conf_stat == BT_TIMEOUT) { + pbtInfo->btStatus = abTimeout; + }else{ + pbtInfo->btStatus = abFailure; + } + }else{ + /* successful */ + pbtInfo->btStatus = abSuccess; + /* was it the response to a read command */ + if (pbtInfo->cmd == AB_READ){ + ustrans(pmb_msg,pbtInfo->pmsg,pbtInfo->msg_len); + } + } + pmb->fl_lock = 0; + if(pcard->type==typeBt) { + pcard->active = FALSE; + pcard->status = pbtInfo->btStatus; + if(pcard->callback) (*pcard->callback)((void *)pcard); + } + } +} + +LOCAL void ab_reset_task(ab_link *plink) +{ + unsigned short link,adapter,card,ab_reset_wait; + ab_adapter *padapter; + ab_region *pab_region=0; + ab_card *pcard; + btInfo *pbtInfo; + + link = plink->link; + pab_region = plink->vme_addr; + plink->ab_disable = 1; + printf("Disabled AB Scanner Task\n"); + taskDelay(vxTicksPerSecond*2); + /* Signal the Scanner to Reset */ + pab_region->sys_fail_set2 = 0xa0a0; + pab_region->sys_fail_set1 = 0x0080; + printf("Card %d Reset\n",link); + /*mark all block transfer cards for initialization*/ + for(adapter = 0; adapter < AB_MAX_ADAPTERS; adapter++){ + padapter = plink->papadapter[adapter]; + if(!padapter) continue; + for (card = 0; card < AB_MAX_CARDS; card++){ + pcard = padapter->papcard[card]; + if(!pcard) continue; + pbtInfo = pcard->pbtInfo; + if(pcard->type==typeAo || pcard->type==typeAi) + pcard->needsUpdate = TRUE; + } + } + ab_reset_wait = 0; + while((pab_region->mail.conf_stat != SCANNER_POWERUP) + && (ab_reset_wait < 600)){ + taskDelay(1); + ab_reset_wait++; + } + if (ab_reset_wait < 600) + printf("Link %d Power Up After %d Ticks\n",link,ab_reset_wait); + else + printf("Link %d Failed to Reinitialize After %d Ticks\n", + link,ab_reset_wait); + + link_init(plink); + /* enable the scanner */ + plink->ab_disable = 0; +} + +int ab_reset_link(int link) +{ + ab_link *plink; + + if(link>=max_ab_6008s) return(0); + plink = pab_links[link]; + if(!plink) return(0); + taskSpawn("ab_reset",38,0,8000,(FUNCPTR)ab_reset_task,(int)plink, + 0,0,0,0,0,0,0,0,0); + return(0); +} + +int ab_reset(void) +{ + int link; + + for(link=0; linkinitialized) { + printf("AB-6008SV link %hu not initialized\n",link); + continue; + } + printf("AB-6008SV link: %hu osw %4.4x vme: %p\n", + link,plink->vme_addr->osw,plink->vme_addr); + if(plink->firmware_info[0]) printf(" %s\n",plink->firmware_info); + printf(" Mailbox lock timeouts %hu\n",plink->sclock_to); + printf(" Command timeouts %hu\n", plink->sccmd_to); + printf(" Command Failure %hu\n", plink->sc_fail); + printf(" Interrupts per second %hu\n", plink->intrSec); + printf(" Block Transfers per second %hu\n", plink->btSec); + if(level>2) { + ab_link_status *plink_status = plink->plink_status; + + printf(" Adapter Status Words"); + for(i=0; i<32; i++) { + if((i%8)==0) printf("\n "); + printf("%4.4x ",plink_status->status[i]); + } + printf("\n scan list"); + for(i=0; ilist_len; i++) { + if((i%16)==0) printf("\n "); + printf("%2.2x ",plink_status->list[i]); + } + printf("\n"); + } + /* adapter information */ + for (adapter = 0; adapter < AB_MAX_ADAPTERS; adapter++){ + padapter = plink->papadapter[adapter]; + if (!padapter) continue; + if (padapter->adapter_online) + printf(" Adapter %hu ONLINE\n",adapter); + else + printf(" Adapter %hu OFFLINE\n",adapter); + + /* card information */ + for (card = 0; card < AB_MAX_CARDS; card++){ + pcard = padapter->papcard[card]; + if(!pcard) continue; + pscanInfo = pcard->pscanInfo; + switch (pcard->type){ + case (typeBi): + di_read(pcard,&divalue); + printf(" CARD %hu: BI %s %s 0x%x\n", + card,abNumBitsMessage[pcard->nBits], + activeMessage[pcard->active],divalue); + break; + case (typeBo): + do_read(pcard,&dovalue); + printf(" CARD %hu: BO %s %s 0x%x\n", + card,abNumBitsMessage[pcard->nBits], + activeMessage[pcard->active],dovalue); + break; + case (typeBiBo): + di_read(pcard,&divalue); + do_read(pcard,&dovalue); + printf(" CARD %hu: BIBO %s %s in 0x%x out 0x%x\n", + card,abNumBitsMessage[pcard->nBits], + activeMessage[pcard->active],divalue,dovalue); + break; + case (typeAi): + case (typeAo): + if(pcard->type==typeAi) + printf(" CARD %hu: AI ",card); + else + printf(" CARD %hu: AO ",card); + if(!pscanInfo || pscanInfo->state==stateInit) + printf(" NOT INITIALIZED "); + printf(" %s %s",activeMessage[pcard->active], + pcard->card_name); + /* error reporting */ + if(!pscanInfo) { + printf("\n"); + break; + } + if (pscanInfo->bt_to) + printf(" bt timeout: %hu",pscanInfo->bt_to); + if (pscanInfo->bt_fail) + printf(" bt fail: %hu",pscanInfo->bt_fail); + if (pscanInfo->init_ctr) + printf(" initialized %hu times",pscanInfo->init_ctr); + printf("\n"); + if (level > 0){ + if(pcard->type==typeAo || level>1) { + printf("\tWrite"); + for (i=0; iwrite_msg_len; i++) { + if((i%10)==0) printf("\n\t"); + printf("%4.4x ",pscanInfo->pwrite_msg[i]); + } + printf("\n"); + } + if(pcard->type==typeAi || level>1) { + printf("\tRead"); + for (i=0; iread_msg_len; i++) { + if((i%10)==0) printf("\n\t"); + printf("%4.4x ",pscanInfo->pread_msg[i]); + } + printf("\n"); + } + } + break; + default: + continue; + } + } + } + } + return(0); + +} + +LOCAL abStatus registerCard( + unsigned short link,unsigned short adapter, unsigned short card, + cardType type, const char *card_name, void (*callback)(void *drvPvt), + void **pdrvPvt) +{ + ab_adapter *padapter; + ab_card *pcard = NULL; + ab_link *plink; + ab_card **ppcard = (ab_card **)pdrvPvt; + + + if(link>=max_ab_6008s) { + printf("abDrv(registerCard) bad link %hu\n",link); + return(abFailure); + } + if(adapter>=MAX_AB_ADAPTERS) { + printf("abDrv(registerCard) bad adapter %hu\n",adapter); + return(abFailure); + } + if(card>=MAX_CARDS_PER_ADAPTER) { + printf("abDrv(registerCard) bad card %hu\n",card); + return(abFailure); + } + plink = pab_links[link]; + if(!plink || !plink->initialized) { + printf("abDrv(registerCard) link %hu not initialized\n",link); + return(abFailure); + } + padapter = plink->papadapter[adapter]; + if(padapter) pcard = padapter->papcard[card]; + if(pcard) { + if(strcmp(pcard->card_name,card_name)!=0) return(abFailure); + if(pcard->type==type) { + *ppcard = pcard; + return(abSuccess); + } + if(type==typeBi || type==typeBo) { + if(pcard->type==typeBo || pcard->type==typeBi + || pcard->type==typeBiBo) { + if(!pcard->callback && type==typeBi ) + pcard->callback = callback; + if(!pcard->bocallback && type==typeBo ) + pcard->bocallback = callback; + pcard->type = typeBiBo; + *ppcard = pcard; + return(abSuccess); + } + } + return(abFailure); + } + /*New Card*/ + pcard = abCalloc(1,sizeof(ab_card)); + pcard->link = link; + pcard->adapter = adapter; + pcard->card = card; + pcard->type = type; + pcard->card_name = card_name; + if(!(pcard->card_sem = semBCreate(SEM_Q_FIFO,SEM_FULL))){ + printf("abDrv register card: semBcreate failed"); + taskSuspend(0); + } + if(type==typeBo) pcard->bocallback = callback; + else pcard->callback = callback; + if(type==typeAi || type ==typeAo || type == typeBt) + pcard->pbtInfo = abCalloc(1,sizeof(btInfo)); + *ppcard = pcard; + if(!padapter) { + padapter = abCalloc(1,sizeof(ab_adapter)); + padapter->papcard = abCalloc(MAX_CARDS_PER_ADAPTER,sizeof(ab_card *)); + if(!(padapter->adapter_sem = semBCreate(SEM_Q_FIFO,SEM_FULL))){ + printf("abDrv: abRegister: semBcreate failed"); + taskSuspend(0); + } + padapter->adapter = adapter; + } + padapter->papcard[card] = pcard; + if(!plink->papadapter[adapter]) plink->papadapter[adapter] = padapter; + link_status(plink); + return(abNewCard); +} + +LOCAL void getLocation(void *drvPvt,unsigned short *link, + unsigned short *adapter,unsigned short *card) +{ + ab_card *pcard = drvPvt; + + *link = pcard->link; + *adapter = pcard->adapter; + *card = pcard->card; + return; +} + +LOCAL abStatus setNbits(void *drvPvt, abNumBits nBits) +{ + ab_card *pcard = drvPvt; + + if(pcard->nBits == abBitNotdefined) { + pcard->nBits = nBits; + pcard->active = TRUE; + return(abSuccess); + } + if(pcard->nBits == nBits) return(abSuccess); + return(abFailure); +} + +LOCAL void setUserPvt(void *drvPvt,void *userPvt) +{ + ab_card *pcard = drvPvt; + + pcard->userPvt = userPvt; + return; +} + +LOCAL void *getUserPvt(void *drvPvt) +{ + ab_card *pcard = drvPvt; + + return(pcard->userPvt); +} + +LOCAL abStatus getStatus(void *drvPvt) +{ + ab_card *pcard = drvPvt; + + unsigned short link = pcard->link; + unsigned short adapter = pcard->adapter; + ab_adapter *padapter = pab_links[link]->papadapter[adapter]; + + if(!padapter->adapter_online) return(abLinkDown); + return(pcard->status); +} + +LOCAL abStatus startScan(void *drvPvt, unsigned short update_rate, + unsigned short *pwrite_msg, unsigned short write_msg_len, + unsigned short *pread_msg, unsigned short read_msg_len) +{ + ab_card *pcard = drvPvt; + scanInfo *pscanInfo; + + pscanInfo = pcard->pscanInfo; + if(!pscanInfo) pcard->pscanInfo = pscanInfo = abCalloc(1,sizeof(scanInfo)); + pscanInfo->update_rate = update_rate; + pscanInfo->pwrite_msg = pwrite_msg; + pscanInfo->write_msg_len = write_msg_len; + pscanInfo->pread_msg = pread_msg; + pscanInfo->read_msg_len = read_msg_len; + pscanInfo->state = stateInit; + pcard->active = TRUE; + pcard->status = abNotInitialized; + return(abSuccess); +} + +LOCAL abStatus updateAo(void *drvPvt) +{ + ab_card *pcard = drvPvt; + + pcard->needsUpdate = TRUE; + return(getStatus(drvPvt)); +} + +LOCAL abStatus updateBo(void *drvPvt,unsigned long value,unsigned long mask) +{ + ab_card *pcard = drvPvt; + unsigned long imagevalue; + + semTake(pcard->card_sem,WAIT_FOREVER); + do_read(pcard,&imagevalue); + imagevalue = (imagevalue & ~mask) | (value & mask); + do_write(pcard,imagevalue); + semGive(pcard->card_sem); + if(pcard->bocallback) (*pcard->bocallback)((void *)pcard); + return(getStatus(drvPvt)); +} + +LOCAL abStatus readBo(void *drvPvt,unsigned long *pvalue,unsigned long mask) +{ + ab_card *pcard = drvPvt; + unsigned long value; + + do_read(pcard,&value); + *pvalue = value & mask; + return(getStatus(drvPvt)); +} + +LOCAL abStatus readBi(void *drvPvt,unsigned long *pvalue,unsigned long mask) +{ + ab_card *pcard = drvPvt; + unsigned long value; + + di_read(pcard,&value); + *pvalue = value & mask; + return(getStatus(drvPvt)); +} + +LOCAL abStatus btRead(void *drvPvt,unsigned short *pread_msg, unsigned short read_msg_len) +{ + ab_card *pcard = drvPvt; + int btStatus; + + if(pcard->active) return(abBusy); + pcard->active = TRUE; + btStatus=bt_queue(AB_READ,pcard,pread_msg,read_msg_len); + if(btStatus!=abBtqueued) pcard->active = FALSE; + return(btStatus); +} +LOCAL abStatus btWrite(void *drvPvt,unsigned short *pwrite_msg, unsigned short write_msg_len) +{ + ab_card *pcard = drvPvt; + int btStatus; + + if(pcard->active) return(abBusy); + pcard->active = TRUE; + btStatus=bt_queue(AB_WRITE,pcard,pwrite_msg,write_msg_len); + if(btStatus!=abBtqueued) pcard->active = FALSE; + return(btStatus); +} + +LOCAL abDrv abDrvTable= { + registerCard,getLocation,setNbits,setUserPvt,getUserPvt, + getStatus,startScan,updateAo,updateBo,readBo,readBi,btRead,btWrite +}; +abDrv *pabDrv = &abDrvTable; + + +/*Time how long it takes to issue link_status */ +int ab_time_link_status(int link) +{ + ab_link *plink = pab_links[link]; + int startTicks; + int stopTicks; + int pass; + double tdiff; + + if(!plink) return(-1); + startTicks = tickGet(); + for (pass=0; pass<100; pass++) link_status(plink); + stopTicks = tickGet(); + tdiff = (stopTicks - startTicks)/60.0; + printf("pass/sec %f\n",(float)pass/tdiff); + return(0); +} diff --git a/src/drv/ansi/drvAb.h b/src/drv/ansi/drvAb.h new file mode 100644 index 000000000..aa66d4b1d --- /dev/null +++ b/src/drv/ansi/drvAb.h @@ -0,0 +1,87 @@ +/* drvAb.h */ +/* header file for the Allen-Bradley Remote Serial IO + * This defines interface between driver and device support + * + * Author: Marty Kraimer + * Date: 03-06-95 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * 01 03-06-95 mrk Moved all driver specific code to drvAb.c + */ + +#ifndef INCdrvAbh +#define INCdrvAbh 1 +#include "dbScan.h" + + +/* interface types */ +typedef enum {typeNotAssigned,typeBi,typeBo,typeBiBo,typeAi,typeAo,typeBt} + cardType; +/* status values*/ +typedef enum{abSuccess,abNewCard,abCardConflict,abNoCard,abNotInitialized, + abBtqueued,abBusy,abTimeout,abLinkDown,abFailure} abStatus; +extern char **abStatusMessage; + +typedef enum{abBitNotdefined,abBit8,abBit16,abBit32} abNumBits; +extern char **abNumBitsMessage; + +/*entry table for dev to drv routines*/ +typedef struct { + abStatus (*registerCard) + (unsigned short link,unsigned short adapter, unsigned short card, + cardType type, const char *card_name, + void (*callback)(void *drvPvt), + void **drvPvt); + void (*getLocation) + (void *drvPvt, + unsigned short *link, unsigned short *adapter,unsigned short *card); + abStatus (*setNbits)(void *drvPvt, abNumBits nbits); + void (*setUserPvt)(void *drvPvt, void *userPvt); + void *(*getUserPvt)(void *drvPvt); + abStatus (*getStatus)(void *drvPvt); + abStatus(*startScan) + (void *drvPvt, unsigned short update_rate, + unsigned short *pwrite_msg, unsigned short write_msg_len, + unsigned short *pread_msg, unsigned short read_msg_len); + abStatus(*updateAo)(void *drvPvt); + abStatus(*updateBo) (void *drvPvt,unsigned long value,unsigned long mask); + abStatus(*readBo) (void *drvPvt,unsigned long *value,unsigned long mask); + abStatus(*readBi) (void *drvPvt,unsigned long *value,unsigned long mask); + abStatus(*btRead)(void *drvPvt,unsigned short *pread_msg, + unsigned short read_msg_len); + abStatus(*btWrite)(void *drvPvt,unsigned short *pwrite_msg, + unsigned short write_msg_len); +}abDrv; + +extern abDrv *pabDrv; + +int ab_reset(void); +int ab_reset_link(int link); +int abConfigNlinks(int nlinks); +int abConfigVme(int link, int base, int vector, int level); +int abConfigBaud(int link, int baud); +int abConfigScanList(int link, int scan_list_len, char *scan_list); + +#endif /*INCdrvAbh*/ diff --git a/src/drv/old/Makefile b/src/drv/old/Makefile new file mode 100644 index 000000000..685f5d2fc --- /dev/null +++ b/src/drv/old/Makefile @@ -0,0 +1,10 @@ +# +# $Id$ +# + +EPICS=../../../.. + +include $(EPICS)/config/CONFIG_BASE + +include $(EPICS)/config/RULES_ARCHS + diff --git a/src/drv/old/Makefile.Vx b/src/drv/old/Makefile.Vx new file mode 100644 index 000000000..fd9a1db7c --- /dev/null +++ b/src/drv/old/Makefile.Vx @@ -0,0 +1,80 @@ +# +# $Id$ +# + +EPICS = ../../../../.. +include Target.include +include $(EPICS)/config/CONFIG_BASE + +USR_CFLAGS = -fshared-data -fvolatile -mnobitfield -traditional + +SRCS.c += ../module_types.c +# SRCS.c += ../drvAb.c +SRCS.c += ../drvAt5Vxi.c +SRCS.c += ../drvBB232.c +SRCS.c += ../drvBb902.c +SRCS.c += ../drvBb910.c +SRCS.c += ../drvBitBus.c +# SRCS.c += ../drvCaenV265.c +SRCS.c += ../drvComet.c +SRCS.c += ../drvCompuSm.c +SRCS.c += ../drvDvx.c +SRCS.c += ../drvEpvxi.c +SRCS.c += ../drvEpvxiMsg.c +SRCS.c += ../drvFp.c +SRCS.c += ../drvFpm.c +SRCS.c += ../drvGpib.c +SRCS.c += ../drvHp1404a.c +SRCS.c += ../drvHpe1368a.c +SRCS.c += ../drvHpe1445a.c +SRCS.c += ../drvJgvtr1.c +SRCS.c += ../drvKscV215.c +SRCS.c += ../drvMsg.c +SRCS.c += ../drvMz8310.c +SRCS.c += ../drvOms.c +SRCS.c += ../drvStc.c +SRCS.c += ../drvTime.c +# SRCS.c += ../drvTranServ.c +SRCS.c += ../drvVmi4100.c +SRCS.c += ../drvXy010.c +SRCS.c += ../drvXy210.c +SRCS.c += ../drvXy220.c +SRCS.c += ../drvXy240.c +SRCS.c += ../drvXy566.c + +TARGETS += module_types.o +# TARGETS += drvAb.o +TARGETS += drvAt5Vxi.o +TARGETS += drvBB232.o +TARGETS += drvBb902.o +TARGETS += drvBb910.o +TARGETS += drvBitBus.o +# TARGETS += drvCaenV265.o +TARGETS += drvComet.o +TARGETS += drvCompuSm.o +TARGETS += drvDvx.o +TARGETS += drvEpvxi.o +TARGETS += drvEpvxiMsg.o +TARGETS += drvFp.o +TARGETS += drvFpm.o +TARGETS += drvGpib.o +TARGETS += drvHp1404a.o +TARGETS += drvHpe1368a.o +TARGETS += drvHpe1445a.o +TARGETS += drvJgvtr1.o +TARGETS += drvKscV215.o +TARGETS += drvMsg.o +TARGETS += drvMz8310.o +TARGETS += drvOms.o +TARGETS += drvStc.o +TARGETS += drvTime.o +# TARGETS += drvTranServ.o +TARGETS += drvVmi4100.o +TARGETS += drvXy010.o +TARGETS += drvXy210.o +TARGETS += drvXy220.o +TARGETS += drvXy240.o +TARGETS += drvXy566.o + +include $(EPICS)/config/RULES.Vx + diff --git a/src/drv/old/VXI_SETUP_README b/src/drv/old/VXI_SETUP_README new file mode 100644 index 000000000..42c6507a2 --- /dev/null +++ b/src/drv/old/VXI_SETUP_README @@ -0,0 +1,30 @@ + +The CPU030 may need to have the nivxi path set correctly: + From the vxWorks shell type "vxitedit" + take option 2 + take option 3 + type list + type modify 0 + type in the correct path when promped + (the path should end in nivxi + and should traverse the niCpu030 + directories shipped with the 030 + ie somethin of the form "???/config/niCPU030/nivxi" + type save + type exit + . + . + . + + +You may may need to setup front panel to backplane trigger +routing: + +To take a TTL input and map it to VXI backplane ECL trigger 0 +type in (to the vxWorks shell): + +epvxiRouteTriggerECL(, 1, 0) + +where specifies the card with the +front panel trigger connection. + diff --git a/src/drv/old/drvAbOLD.c b/src/drv/old/drvAbOLD.c new file mode 100644 index 000000000..84b417df5 --- /dev/null +++ b/src/drv/old/drvAbOLD.c @@ -0,0 +1,2505 @@ +/* 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 +#include /* library for task support */ +#include /* library for semaphore support */ +#include +#include +#include /* library for watchdog timer support */ +#include /* library for ring buffer support */ +#include +#include + +#include +#include +#include +#include +#include + + + /* 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 +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; inwords; 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; istatus != 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; + idata[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; iout_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; iurange & 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; iurange & 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; + iurange & 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; adaptermail); + 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; adapter0) 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; iosw = 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; linksys_fail_set2 = 0xa0a0; + pab_region->sys_fail_set1 = 0x0080; + printf("Card %d Reset\n",0); + } + + ab_reset_wait = 0; + for(link=0; linkmail.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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char SccsId[] = "@(#)drvAt5Vxi.c 1.19\t8/27/93"; + +typedef long at5VxiStatus; + +LOCAL void at5vxi_int_service( + int addr +); + +LOCAL void at5vxi_init_card( + unsigned addr +); + +LOCAL int at5vxi_shutdown(void); + +LOCAL void at5vxi_shutdown_card( + unsigned la +); + +LOCAL at5VxiStatus at5vxi_report_timing( + unsigned card, + unsigned channel +); + +LOCAL void at5vxi_stat( + unsigned card, + int level +); + +/* + * these should be in a header file + */ +LOCAL at5VxiStatus at5vxi_init( + void +); + +at5VxiStatus at5vxi_one_shot( + int preset, /* TRUE or COMPLEMENT logic */ + double edge0_delay, /* sec */ + double edge1_delay, /* set */ + unsigned card, /* 0 through ... */ + unsigned channel, /* 0 through channels on a card */ + int int_source, /* (FALSE)External/(TRUE)Internal source */ + void *event_rtn, /* subroutine to run on events */ + int event_rtn_param /* parameter to pass to above routine */ +); + +at5VxiStatus at5vxi_one_shot_read( + int *preset, /* TRUE or COMPLEMENT logic */ + double *edge0_delay, /* sec */ + double *edge1_delay, /* sec */ + unsigned card, /* 0 through ... */ + unsigned channel, /* 0 through channels on a card */ + int *int_source /* (FALSE)External/(TRUE)Internal src */ +); + +at5VxiStatus at5vxi_ai_driver( + unsigned card, + unsigned chan, + unsigned short *prval +); + +at5VxiStatus at5vxi_ao_driver( + unsigned card, + unsigned chan, + unsigned short *prval, + unsigned short *prbval +); + +at5VxiStatus at5vxi_ao_read( + unsigned card, + unsigned chan, + unsigned short *pval +); + +at5VxiStatus at5vxi_bi_driver( + unsigned card, + unsigned long mask, + unsigned long *prval +); + +at5VxiStatus at5vxi_bo_driver( + unsigned card, + unsigned long val, + unsigned long mask +); + +at5VxiStatus at5vxi_getioscanpvt( + unsigned card, + IOSCANPVT *scanpvt +); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvAt5Vxi={ + 2, + NULL, /* VXI io report takes care of this */ + at5vxi_init}; + +#define EXT_TICKS 5.0e06 /* GTA std speed of SRC1 in Hz */ + +/* + * Set this flag if you wish for the driver to + * to disable the busy period and operate + * without periodic interrupts + * +#define CONTINUOUS_OPERATION + * + */ + +/* + * all AT5VXI cards will use this VME interrupt level + */ +#ifdef CONTINUOUS_OPERATION +# define AT5VXI_INT_LEVEL 0 +# define AT5VXI_INT_ENABLE FALSE +# define AT5VXI_BUSY_ENABLE FALSE +#else +# define AT5VXI_INT_LEVEL 5 +# define AT5VXI_INT_ENABLE TRUE +# define AT5VXI_BUSY_ENABLE TRUE +#endif + +#define abort(A) taskSuspend(0) + +/* + * bit fields allocated starting with the ms bit + */ +struct at5vxi_status{ + unsigned pad0:1; + unsigned modid_cmpl:1; + unsigned dev255:1; + unsigned busy:1; + unsigned pad1:2; + unsigned timer_bank:1; + unsigned ipend:1; + unsigned ienable:1; + unsigned ilevel:3; + unsigned ready:1; + unsigned passed:1; + unsigned sfinh:1; + unsigned sreset:1; +}; + + +#define PSTATUS(PCSR)\ +(&((struct vxi_csr *)PCSR)->dir.r.status) + +struct at5vxi_control{ + unsigned pad0:3; + unsigned busy_enable:1; + unsigned pad1:2; + unsigned timer_bank:1; + unsigned softint:1; + unsigned ienable:1; + unsigned ilevel:3; + unsigned pad2:2; + unsigned sfinh:1; + unsigned sreset:1; +}; + +/* + * Insert or extract a bit field using the standard + * masks and shifts defined below + */ +#ifdef __STDC__ +# define INSERT(FIELD,VALUE)\ + (((VALUE)&(FD_ ## FIELD ## _M))<<(FD_ ## FIELD ## _S)) +# define EXTRACT(FIELD,VALUE)\ + ( ((VALUE)>>(FD_ ## FIELD ## _S)) &(FD_ ## FIELD ## _M)) +#else +# define INSERT(FIELD,VALUE)\ + (((VALUE)&(FD_/* */FIELD/* */_M))<<(FD_/* */FIELD/* */_S)) +# define EXTRACT(FIELD,VALUE)\ + ( ((VALUE)>>(FD_/* */FIELD/* */_S)) &(FD_/* */FIELD/* */_M)) +#endif + +/* + * in the constants below _M is a right justified mask + * and _S is a shift required to right justify the field + */ +#define FD_INT_ENABLE_M (0x1) +#define FD_INT_ENABLE_S (7) +#define FD_BUSY_ENABLE_M (0x1) +#define FD_BUSY_ENABLE_S (12) +#define FD_BUSY_STATUS_M (0x1) +#define FD_BUSY_STATUS_S (12) +#define FD_INT_LEVEL_M (0x7) +#define FD_INT_LEVEL_S (4) +#define FD_TIMER_BANK_M (0x1) +#define FD_TIMER_BANK_S (9) + +#define BUSY(PCSR)\ +(((struct vxi_csr *)PCSR)->dir.r.status & INSERT(BUSY_STATUS,1)) + +/* + * Some constants for the CSR. + * + */ +#ifndef CONTINUOUS_OPERATION +# define INTDISABLE\ + ( \ + INSERT(BUSY_ENABLE, AT5VXI_BUSY_ENABLE) | \ + INSERT(INT_ENABLE, FALSE) | \ + INSERT(INT_LEVEL, AT5VXI_INT_LEVEL) \ + ) +#endif + +/* + * Used to initialize the control register. + * (enables interrupts) + */ +#define CSRINIT\ + ( \ + INSERT(BUSY_ENABLE, AT5VXI_BUSY_ENABLE) | \ + INSERT(INT_ENABLE, AT5VXI_INT_ENABLE) | \ + INSERT(INT_LEVEL, AT5VXI_INT_LEVEL) \ + ) + +/* + * Use bank zero of the timing. + */ +#define BANK0\ + ( \ + INSERT(TIMER_BANK, 0) | \ + INSERT(BUSY_ENABLE, AT5VXI_BUSY_ENABLE) | \ + INSERT(INT_LEVEL, AT5VXI_INT_LEVEL) \ + ) +/* + * Use bank one of the timing. + */ +#define BANK1\ + ( \ + INSERT(TIMER_BANK, 1) | \ + INSERT(BUSY_ENABLE, AT5VXI_BUSY_ENABLE) | \ + INSERT(INT_LEVEL, AT5VXI_INT_LEVEL) \ + ) + +/* + * Some constants for the CSR. + * set at initialization for better readability + */ +#define PCONTROL(PCSR)\ +(&((struct vxi_csr *)PCSR)->dir.w.control) + + + + +#define AT5VXI_TIMER_BANKS_PER_MODULE 2 +#define AT5VXI_CHANNELS_PER_TIMER_BANK 5 +#define AT5VXI_NTIMER_CHANNELS\ + (AT5VXI_TIMER_BANKS_PER_MODULE*AT5VXI_CHANNELS_PER_TIMER_BANK) + + +struct at5vxi_dd{ + vxi16_t bio[2]; + vxi16_t tdata; + vxi8_t pad; + vxi8_t tcmd; + vxi16_t ai[8]; + vxi16_t ao[16]; +}; + + +struct at5vxi_setup{ +# define UNITY 0 +# define TIMES2 1 + unsigned gainA:1; + unsigned gainB:1; + unsigned gainC:1; + unsigned gainD:1; +# define UNIPOLAR 0 +# define BIPOLAR 1 + unsigned modeA:1; + unsigned modeB:1; + unsigned modeC:1; + unsigned modeD:1; + unsigned ch2enbl:1; + unsigned ch2select:3; + unsigned ch1enbl:1; + unsigned ch1select:3; +}; + + +#define AT5VXI_BUSY_PERIOD 2 + +struct bo_val { + volatile int32_t val; + volatile int32_t mask; +}; + +struct ao_val { + volatile int16_t mdt; + volatile int16_t val; +}; + +struct time_val { + volatile unsigned preset; + volatile int16_t iedge0_delay; + volatile int16_t iedge1_delay; + volatile char mdt; + volatile char valid; +}; + +struct at5vxi_config{ + FAST_LOCK lock; /* mutual exclusion */ + struct bo_val bv; /* binary out values */ + struct ao_val av[16]; /* analog out values */ + struct time_val tv[10]; /* delayed pulse values */ + volatile char mdt; /* modified data tag */ + struct vxi_csr *pcsr; /* vxi device hdr ptr */ + struct at5vxi_dd *pdd; /* at5 device dep ptr */ + IOSCANPVT ioscanpvt; +}; + +LOCAL unsigned long at5vxiDriverID; + +#define AT5VXI_PCONFIG(CARD, PTR) \ +epvxiFetchPConfig(CARD, at5vxiDriverID, PTR) + +#define AT5VXI_CORRECT_MAKE(PCSR) (VXIMAKE(PCSR)==VXI_MAKE_AT5) + +struct at5vxi_model{ + char *name; /* AT5 VXI module name */ + char *drawing; /* AT5 VXI assembly drawing number */ +}; + +#define AT5VXI_INDEX_FROM_MODEL(MODEL) ((unsigned)((MODEL)&0xff)) +#define AT5VXI_MODEL_FROM_INDEX(INDEX) ((unsigned)((INDEX)|0xf00)) + +/* + * NOTE: The macro AT5VXI_INDEX_FROM_MODEL(MODEL) defined above + * should return an index into the correct data given the + * VXI device's model code. + */ +struct at5vxi_model at5vxi_models[] = { + {"INTERFACE SIMULATOR", "112Y-280158"}, + {"I CONTROLLER", "112Y-280176"}, + {"CONTROL PREDISTORTER", "112Y-280172"}, + {"VECTOR DETECTOR", "112Y-280230"}, + {"VECTOR MODULATOR", "112Y-280177"}, + {"425MHz ENVELOPE DETECTOR", "112Y-280169"}, + {"425MHz DOWNCONVERTER", "112Y-280165"}, + {"POLAR DETECTOR", "112Y-280567"}, + {"UPCONVERTER", "112Y-280225"}, + {"MONITOR TRANSMITTER", "112Y-280187"}, + {"TIMING DISTRIBUTION", "112Y-280582"}, + {"LINE CONDITIONER", "112Y-280305"}, + {"BEAM FEEDFORWARD", "112Y-280564"}, + {"TIMING RECEIVER", "112Y-280243"}, + {"FAST PROTECTION", "112Y-280246"}, + {"ADAPTIVE FEEDFORWARD", "112Y-280563"}, + {"CABLE CONTROLLER", "112Y-280307"}, + {"Q CONTROLLER", "112Y-280180"}, + {"ENVELOPE DETECTOR", "112Y-280249"}, + {"DOWNCONVERTER", "112Y-280456"}, + {"COAX MONITOR TRANSMITTER", "112Y-280587"}, + {"CAVITY SIMULATOR", "112Y-280232"}, + {"CABLE CONTROLLER (2 CHANNEL)","112Y-280539"}, + {"BREADBOARD", "112Y-280358"}, + {"I/O INTERFACE", "112Y-280359"}, + {"DIAGNOSTIC - BPM", "112Y-280422-1"}, + {"FAST ENVELOPE DETECTOR", "112Y-280421"}, + {"DIAGNOSTIC - CM", "112Y-280422-2"}, + {"DIAGNOSTIC - MISC", "112Y-280422-3"}, + {"FAST VECTOR DETECTOR", "112Y-280651"}, + {"SINGLE-WIDE VECTOR DETECTOR", "112Y-280672"}, + {"FM / AM", "112Y-280xxx"} +}; + +#define AT5VXI_VALID_MODEL(MODEL) \ +(AT5VXI_INDEX_FROM_MODEL(MODEL)dir.w.control = INTDISABLE; +#endif +} + + + +/* + * AT5VXI_INIT_CARD + * + * initialize single at5vxi card + * + */ +LOCAL +void at5vxi_init_card( + unsigned addr +) +{ + at5VxiStatus r0; + struct at5vxi_config *pc; + struct time_val *ptv; + unsigned chan; + int i; + int model; + + r0 = epvxiOpen( + addr, + at5vxiDriverID, + (unsigned long) sizeof(*pc), + at5vxi_stat); + if(r0){ + errPrintf( + r0, + __FILE__, + __LINE__, + "AT5VXI: device open failed %d\n", addr); + return; + } + + r0 = AT5VXI_PCONFIG(addr, pc); + if(r0){ + errMessage(r0, NULL); + epvxiClose(addr, at5vxiDriverID); + return; + } + + pc->pcsr = VXIBASE(addr); + pc->pdd = (struct at5vxi_dd *) &pc->pcsr->dir.r.dd; + + FASTLOCKINIT(&pc->lock); + + scanIoInit(&pc->ioscanpvt); + + + /* + * revert to power up control + * (temporarily disable the busy period) + */ + pc->pcsr->dir.w.control = 0; + + +#ifndef CONTINUOUS_OPERATION + /* + * wait 5 sec for the end of current busy cycle as required + * (busy period is temporarily disabled + * + */ + for(i=0; i<5 && BUSY(pc->pcsr); i++) + taskDelay(sysClkRateGet()); + if(BUSY(pc->pcsr)){ + epvxiClose(addr, at5vxiDriverID); + return; + } +#endif + +#if defined(INIT_BINARY_OUTS) + /* + * Set AD 664 default + */ + { + struct at5vxi_setup su; + + su.gainA = TIMES2; + su.gainB = TIMES2; + su.gainC = TIMES2; + su.gainD = TIMES2; + + su.modeA = BIPOLAR; + su.modeB = BIPOLAR; + su.modeC = BIPOLAR; + su.modeD = BIPOLAR; + + su.ch2enbl = FALSE; + su.ch1enbl = FALSE; + + (* (struct at5vxi_setup *) &pc->pdd->bio[1]) = su; + } +#endif + + /* + * Init AMD 9513 for + * + * binary division + * data ptr seq enbl + * 16 bit bus + * FOUT on + * FOUT divide by 16 + * FOUT source (F1) + * Time of day disabled + */ +# define MASTER_MODE ((uint16_t)0x2000) + *PCONTROL(pc->pcsr) = BANK0; + r0 = stc_init( &pc->pdd->tcmd, + &pc->pdd->tdata, + MASTER_MODE); + if(r0!=STC_SUCCESS){ + epvxiClose(addr, at5vxiDriverID); + return; + } + + *PCONTROL(pc->pcsr) = BANK1; + r0 = stc_init( + &pc->pdd->tcmd, + &pc->pdd->tdata, + MASTER_MODE); + if(r0!=STC_SUCCESS){ + epvxiClose(addr, at5vxiDriverID); + return; + } + + for(chan=0, ptv = pc->tv; chantv); chan++, ptv++){ + unsigned int_source; + + if(chan/AT5VXI_CHANNELS_PER_TIMER_BANK){ + *PCONTROL(pc->pcsr) = BANK1; + } + else{ + *PCONTROL(pc->pcsr) = BANK0; + } + + /* + * casting below discards volatile + * (ok in this case) + */ + r0 = stc_one_shot_read( + (unsigned *)&ptv->preset, + (uint16_t *)&ptv->iedge0_delay, + (uint16_t *)&ptv->iedge1_delay, + &pc->pdd->tcmd, + &pc->pdd->tdata, + chan, + &int_source); + if(r0 == STC_SUCCESS && int_source == FALSE) + ptv->valid = TRUE; + else + ptv->valid = FALSE; + } + +# ifndef CONTINUOUS_OPERATION + r0 = intConnect( + INUM_TO_IVEC(addr), + at5vxi_int_service, + addr); + if(r0 == ERROR) + return; + + sysIntEnable(AT5VXI_INT_LEVEL); +# endif + + /* + * init the csr + * (see at5vxi_init() for field definitions) + * interrupts enabled if not compiled for continuous operation + */ + *PCONTROL(pc->pcsr) = CSRINIT; + + model = VXIMODEL(pc->pcsr); + if(AT5VXI_VALID_MODEL(model)){ + r0 = epvxiRegisterModelName( + VXIMAKE(pc->pcsr), + model, + at5vxi_models[AT5VXI_INDEX_FROM_MODEL(model)].name); + if(r0){ + errMessage(r0,NULL); + } + + r0 = epvxiRegisterMakeName(VXI_MAKE_AT5, "LANL AT5"); + if(r0){ + errMessage(r0,NULL); + } + } + + return; +} + + +/* + * + * AT5VXI_INT_SERVICE + * + * update card busy writes and notify the IO interrupt scanner + */ +void at5vxi_int_service( + int addr +) +{ + struct at5vxi_config *pconfig; + at5VxiStatus r0; + + r0 = AT5VXI_PCONFIG(addr, pconfig); + if(r0){ + logMsg( "AT5VXI: int before init\n", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + return; + } + + /* + * wake up the I/O event scanner + */ + scanIoRequest(pconfig->ioscanpvt); + + /* + * Update outputs while it is safe to do so + */ + if(pconfig->mdt){ + struct at5vxi_dd *pdd; + struct vxi_csr *pcsr; + unsigned chan; + + pcsr = pconfig->pcsr; + pdd = pconfig->pdd; + + for(chan=0; chantv); chan++){ + unsigned chip_chan; + + if(!pconfig->tv[chan].mdt) + continue; + + + if(chan/AT5VXI_CHANNELS_PER_TIMER_BANK){ + *PCONTROL(pcsr) = BANK1; + } + else{ + *PCONTROL(pcsr) = BANK0; + } + + chip_chan = chan% + AT5VXI_CHANNELS_PER_TIMER_BANK; + + r0 = stc_one_shot( + pconfig->tv[chan].preset, + pconfig->tv[chan].iedge0_delay, + pconfig->tv[chan].iedge1_delay, + &pdd->tcmd, + &pdd->tdata, + chip_chan, + FALSE); + if(r0 != STC_SUCCESS){ + logMsg( "AT5 VXI- AMD9513 load fail\n", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + } + else{ + pconfig->tv[chan].valid = TRUE; + } + + /* + * reenable interrupts + */ + *PCONTROL(pcsr) = CSRINIT; + + pconfig->tv[chan].mdt = FALSE; + } + + for(chan=0; chanav); chan++){ + if(!pconfig->av[chan].mdt) + continue; + pdd->ao[chan] = pconfig->av[chan].val; + pconfig->av[chan].mdt = FALSE; + } + + if(pconfig->bv.mask){ + uint32_t work; + + work = ((pdd->bio[1]<<(NBBY*sizeof(uint16_t))) | + pdd->bio[0]); + + /* alter specified bits */ + work = (work & ~pconfig->bv.mask) | + (pconfig->bv.val & pconfig->bv.mask); + + pdd->bio[0] = work; + pdd->bio[1] = work>>(NBBY*sizeof(uint16_t)); + + pconfig->bv.mask = 0; + } + + if(BUSY(pcsr)) + logMsg( "AT5 VXI INT- finished with card busy\n", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + + pconfig->mdt = FALSE; + } + +} + + + +/* + * + * AT5VXI_ONE_SHOT + * + * setup AMD 9513 STC for a repeated two edge timing signal + */ +at5VxiStatus at5vxi_one_shot( + int preset, /* TRUE or COMPLEMENT logic */ + double edge0_delay, /* sec */ + double edge1_delay, /* set */ + unsigned card, /* 0 through ... */ + unsigned channel, /* 0 through channels on a card */ + int int_source, /* (FALSE)External/(TRUE)Internal source */ + void *event_rtn, /* subroutine to run on events */ + int event_rtn_param /* parameter to pass to above routine */ +) +{ + at5VxiStatus status; + register struct vxi_csr *pcsr; + register struct at5vxi_config *pconfig; + + status = AT5VXI_PCONFIG(card, pconfig); + if(status){ + return status; + } + + pcsr = pconfig->pcsr; + +/* AT5VXI does not support internal source for now +*/ if(int_source){ + status = S_dev_badRequest; + errMessage( + status, + "AT5VXI does not support internal trigger source"); + return status; + } + +/* AT5VXI does not support interrupts on timing channels for now +*/ if(event_rtn){ + status = S_dev_badRequest; + errMessage(status, "AT5VXI does not support interrupts on timing channels"); + return status; + } + + if(channel>=AT5VXI_NTIMER_CHANNELS) + return S_dev_badSignalNumber; + +/* dont overflow unsigned short in STC +*/ if(edge0_delay >= devCreateMask(NBBY*sizeof(uint16_t))/EXT_TICKS) + return S_dev_highValue; + if(edge1_delay >= devCreateMask(NBBY*sizeof(uint16_t))/EXT_TICKS) + return S_dev_highValue; + if(edge0_delay < 0.0) + return S_dev_lowValue; + if(edge1_delay < 0.0) + return S_dev_lowValue; + + FASTLOCK(&pconfig->lock); + +# ifdef CONTINUOUS_OPERATION + { + struct at5vxi_dd *pdd; + + pdd = pconfig->pdd; + + if(channel/AT5VXI_CHANNELS_PER_TIMER_BANK){ + *PCONTROL(pcsr) = BANK1; + }else{ + *PCONTROL(pcsr) = BANK0; + } + channel = channel%AT5VXI_CHANNELS_PER_TIMER_BANK; + + status = stc_one_shot( + preset, + (uint16_t) (edge0_delay * EXT_TICKS), + (uint16_t) (edge1_delay * EXT_TICKS), + &pdd->tcmd, + &pdd->tdata, + channel, + FALSE); + + /* + * not required for now but safe + * against future mods + */ + *PCONTROL(pcsr) = CSRINIT; + } +# else + *PCONTROL(pcsr) = INTDISABLE; + pconfig->tv[channel].preset = preset; + pconfig->tv[channel].iedge0_delay = + (edge0_delay * EXT_TICKS); + pconfig->tv[channel].iedge1_delay = + (edge1_delay * EXT_TICKS); + pconfig->tv[channel].mdt = TRUE; + pconfig->mdt = TRUE; + *PCONTROL(pcsr) = CSRINIT; + + status = STC_SUCCESS; +# endif + + FASTUNLOCK(&pconfig->lock); + + if(status!=STC_SUCCESS){ + return status; + } + + return VXI_SUCCESS; +} + + +/* + * + * AT5VXI_ONE_SHOT_READ + * + * read back two edge timing from an AMD 9513 STC + */ +at5VxiStatus at5vxi_one_shot_read( + int *preset, /* TRUE or COMPLEMENT logic */ + double *edge0_delay, /* sec */ + double *edge1_delay, /* sec */ + unsigned card, /* 0 through ... */ + unsigned channel, /* 0 through channels on a card */ + int *int_source /* (FALSE)External/(TRUE)Internal src */ +) +{ +#ifdef CONTINUOUS_OPERATION + uint16_t iedge0; + uint16_t iedge1; +#endif + at5VxiStatus status; + register struct vxi_csr *pcsr; + register struct at5vxi_config *pconfig; + + status = AT5VXI_PCONFIG(card, pconfig); + if(status) + return status; + + pcsr = pconfig->pcsr; + + if(channel>=AT5VXI_NTIMER_CHANNELS) + return S_dev_badSignalNumber; + + +# ifdef CONTINUOUS_OPERATION + { + struct at5vxi_dd *pdd; + + pdd = pconfig->pdd; + + FASTLOCK(&pconfig->lock); + + if(channel/AT5VXI_CHANNELS_PER_TIMER_BANK){ + *PCONTROL(pcsr) = BANK1; + }else{ + *PCONTROL(pcsr) = BANK0; + } + channel = channel%AT5VXI_CHANNELS_PER_TIMER_BANK; + status = stc_one_shot_read( + preset, + &iedge0, + &iedge1, + &pdd->tcmd, + &pdd->tdata, + channel, + int_source); + /* + * not required for noe but safe + * against future mods + */ + *PCONTROL(pcsr) = CSRINIT; + FASTUNLOCK(&pconfig->lock); + + if(status==STC_SUCCESS){ + /* + * AT5VXI does not support external + * source for now + */ + if(int_source) + return S_dev_badRequest; + + *edge0_delay = iedge0 / EXT_TICKS; + *edge1_delay = iedge1 / EXT_TICKS; + } + + + return status; + } +# else + if(!pconfig->tv[channel].valid) + return S_dev_badRequest; + + FASTLOCK(&pconfig->lock); + + *preset = pconfig->tv[channel].preset; + *edge0_delay = pconfig->tv[channel].iedge0_delay + /EXT_TICKS; + *edge1_delay = pconfig->tv[channel].iedge1_delay + /EXT_TICKS; + *int_source = FALSE; + + FASTUNLOCK(&pconfig->lock); + + return VXI_SUCCESS; +# endif +} + + + +/* + * + * AT5VXI_STAT + * + * print status for a single at5 vxi card + * + * + */ +void at5vxi_stat( + unsigned card, + int level +) +{ + struct vxi_csr *pcsr; + register struct at5vxi_dd *pdd; + struct at5vxi_status status; + unsigned channel; + at5VxiStatus r0; + struct at5vxi_config *pconfig; + + static char *busy_status[] = {"","busy"}; + static char *modid_status[] = {"modid-on",""}; + static char *ipend_status[] = {"","int-pending"}; + static char *ienable_status[] = {"int-disabled","int-enabled"}; + static char *ext_st_status[] = {"extended-self-testing",""}; + static char *st_status[] = {"self-testing",""}; + static char *sfinh_status[] = {"","sys-fail-inhibit"}; + static char *sreset_status[] = {"","sys-reset"}; + static char *addr_mode_status[] = {"SC","DC"}; + + if(level==0) + return; + + r0 = AT5VXI_PCONFIG(card, pconfig); + if(r0){ + errMessage(r0,NULL); + return; + } + + pcsr = VXIBASE(card); + pdd = pconfig->pdd; + + r0 = vxMemProbe( (char *)&pcsr->dir.r.status, + READ, + sizeof(status), + (char *)&status); + if(r0 != OK) + return; + + if(VXIMAKE(pcsr) != VXI_MAKE_AT5) + return; + + if(AT5VXI_VALID_MODEL(VXIMODEL(pcsr))){ + printf( "\tDrawing: %s\n", + at5vxi_models[AT5VXI_INDEX_FROM_MODEL(VXIMODEL(pcsr))].drawing); + } + + printf( + "\tcard=%d address-mode=%s %s %s %s %s ilevel=%d %s %s %s %s\n", + card, + addr_mode_status[status.dev255], + busy_status[ status.busy ], + modid_status[ status.modid_cmpl ], + ipend_status[ status.ipend ], + ienable_status[ status.ienable ], + status.ilevel, + ext_st_status[ status.ready ], + st_status[ status.passed ], + sfinh_status[ status.sfinh ], + sreset_status[ status.sreset ]); + + if(pconfig){ + if(pconfig->mdt){ + printf("\toutput update is pending for interrupt\n"); + } + } + + if(level <= 1) + return; + + for(channel=0; channelai); channel++){ + printf( + "\tAI: channel %d value %x\n", + channel, + pdd->ai[channel]); + } + + for(channel=0; channelao); channel++){ + printf( + "\tAO: channel %d value %x\n", + channel, + pdd->ao[channel]); + } + + { + uint32_t work; + + work = ((uint32_t)pdd->bio[1]) << (sizeof(uint16_t)*NBBY); + work |= pdd->bio[0]; + printf("\tBIO: value %x\n", work); + } + + for(channel=0; channeltv); channel++){ + at5vxi_report_timing(card, channel); + } + + return; +} + + +/* + * + * + * AT5VXI_REPORT_TIMING + * + * diagnostic + */ +LOCAL +at5VxiStatus at5vxi_report_timing( + unsigned card, + unsigned channel +) +{ + int preset; + double edge0_delay; + double edge1_delay; + int int_source; + at5VxiStatus status; + char *clk_src[] = {"external-clk", "internal-clk"}; + + status = + at5vxi_one_shot_read( + &preset, + &edge0_delay, + &edge1_delay, + card, + channel, + &int_source); + if(status == VXI_SUCCESS) + printf( + "\tTI: channel %d preset %d delay %f width %f %s\n", + channel, + preset, + edge0_delay, + edge1_delay, + clk_src[int_source?1:0]); + + return status; +} + + +/* + * + * + * AT5VXI_AI_DRIVER + * + * analog input driver + */ +at5VxiStatus at5vxi_ai_driver( + unsigned card, + unsigned chan, + unsigned short *prval +) +{ + at5VxiStatus s; + register struct at5vxi_dd *pdd; + register struct vxi_csr *pcsr; + register struct at5vxi_config *pconfig; + + s = AT5VXI_PCONFIG(card, pconfig); + if(s) + return s; + + pcsr = pconfig->pcsr; + pdd = pconfig->pdd; + + if(chan >= NELEMENTS(pdd->ai)) + return S_dev_badSignalNumber; + + *prval = pdd->ai[chan]; + + return VXI_SUCCESS; +} + + +/* + * + * + * AT5VXI_AO_DRIVER + * + * analog output driver + */ +at5VxiStatus at5vxi_ao_driver( + unsigned card, + unsigned chan, + unsigned short *prval, + unsigned short *prbval +) +{ + struct at5vxi_dd *pdd; + register struct vxi_csr *pcsr; + register struct at5vxi_config *pconfig; + at5VxiStatus s; + + s = AT5VXI_PCONFIG(card, pconfig); + if(s) + return s; + + pcsr = pconfig->pcsr; + pdd = pconfig->pdd; + + if(chan >= NELEMENTS(pdd->ao)) + return S_dev_badSignalNumber; + +#ifdef CONTINUOUS_OPERATION + pdd->ao[chan] = *prval; + *prbval = pdd->ao[chan]; +#else + *PCONTROL(pcsr) = INTDISABLE; + pconfig->av[chan].val = *prval; + pconfig->av[chan].mdt = TRUE; + pconfig->mdt = TRUE; + *PCONTROL(pcsr) = CSRINIT; + + *prbval = *prval; +#endif + return VXI_SUCCESS; +} + + +/* + * + * + * AT5VXI_AO_READ + * + * analog output read back + */ +at5VxiStatus at5vxi_ao_read( + unsigned card, + unsigned chan, + unsigned short *pval +) +{ + register struct at5vxi_dd *pdd; + register struct vxi_csr *pcsr; + register struct at5vxi_config *pconfig; + at5VxiStatus s; + + s = AT5VXI_PCONFIG(card, pconfig); + if(s){ + return s; + } + + pcsr = pconfig->pcsr; + pdd = pconfig->pdd; + + if(chan >= NELEMENTS(pdd->ao)) + return S_dev_badSignalNumber; + + *pval = pdd->ao[chan]; + + return VXI_SUCCESS; +} + + +/* + * + * + * AT5VXI_BI_DRIVER + * + * binary input driver + */ +at5VxiStatus at5vxi_bi_driver( + unsigned card, + unsigned long mask, + unsigned long *prval +) +{ + register uint32_t work; + register struct at5vxi_dd *pdd; + register struct vxi_csr *pcsr; + register struct at5vxi_config *pconfig; + at5VxiStatus s; + + s = AT5VXI_PCONFIG(card, pconfig); + if(s) + return s; + + pcsr = pconfig->pcsr; + pdd = pconfig->pdd; + + FASTLOCK(&pconfig->lock); + work = ((pdd->bio[1]<<(NBBY*sizeof(uint16_t))) | pdd->bio[0]); + *prval = mask & work; + FASTUNLOCK(&pconfig->lock); + + return VXI_SUCCESS; +} + + +/* + * + * + * AT5VXI_BO_DRIVER + * + * binary output driver + */ +at5VxiStatus at5vxi_bo_driver( + unsigned card, + unsigned long val, + unsigned long mask +) +{ +#ifdef CONTINUOUS_OPERATION + register uint32_t work; +#endif + register struct vxi_csr *pcsr; + register struct at5vxi_config *pconfig; + at5VxiStatus s; + + s = AT5VXI_PCONFIG(card, pconfig); + if(s) + return s; + + pcsr = pconfig->pcsr; + + FASTLOCK(&pconfig->lock); + +#ifdef CONTINUOUS_OPERATION + { + struct at5vxi_dd *pdd; + + pdd = pconfig->pdd; + + work = ((pdd->bio[1]<<(NBBY*sizeof(uint16_t))) | pdd->bio[0]); + + /* alter specified bits */ + work = (work & ~mask) | (val & mask); + + pdd->bio[0] = work; + pdd->bio[1] = work>>(NBBY*sizeof(uint16_t)); + } +#else + *PCONTROL(pcsr) = INTDISABLE; + pconfig->bv.val = (pconfig->bv.val & ~mask) | (val & mask); + pconfig->bv.mask |= mask; + pconfig->mdt = TRUE; + *PCONTROL(pcsr) = CSRINIT; +#endif + + FASTUNLOCK(&pconfig->lock); + + return VXI_SUCCESS; +} + + +/* + * + * at5vxi_getioscanpvt() + * + * + */ +at5VxiStatus at5vxi_getioscanpvt( +unsigned card, +IOSCANPVT *scanpvt +) +{ + struct at5vxi_config *pconfig; + at5VxiStatus s; + + s = AT5VXI_PCONFIG(card, pconfig); + if(s == VXI_SUCCESS){ + *scanpvt = pconfig->ioscanpvt; + } + return s; +} + diff --git a/src/drv/old/drvAt5Vxi.h b/src/drv/old/drvAt5Vxi.h new file mode 100644 index 000000000..97e4a23b3 --- /dev/null +++ b/src/drv/old/drvAt5Vxi.h @@ -0,0 +1,93 @@ +/* base/src/drv $Id$ */ + +/* + * + * driver for at5 designed VXI modules + * + * Author: Jeff Hill + * Date: 11-89 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + */ + +typedef long at5VxiStatus; + +at5VxiStatus at5vxi_one_shot( + int preset, /* TRUE or COMPLEMENT logic */ + double edge0_delay, /* sec */ + double edge1_delay, /* set */ + unsigned card, /* 0 through ... */ + unsigned channel, /* 0 through channels on a card */ + int int_source, /* (FALSE)External/(TRUE)Internal source + */ + void *event_rtn, /* subroutine to run on events */ + int event_rtn_param /* parameter to pass to above routine */ +); + +at5VxiStatus at5vxi_one_shot_read( + int *preset, /* TRUE or COMPLEMENT logic */ + double *edge0_delay, /* sec */ + double *edge1_delay, /* sec */ + unsigned card, /* 0 through ... */ + unsigned channel, /* 0 through channels on a card */ + int *int_source /* (FALSE)External/(TRUE)Internal src */ +); + +at5VxiStatus at5vxi_ai_driver( + unsigned card, + unsigned chan, + unsigned short *prval +); + +at5VxiStatus at5vxi_ao_driver( + unsigned card, + unsigned chan, + unsigned short *prval, + unsigned short *prbval +); + +at5VxiStatus at5vxi_ao_read( + unsigned card, + unsigned chan, + unsigned short *pval +); + +at5VxiStatus at5vxi_bi_driver( + unsigned card, + unsigned long mask, + unsigned long *prval +); + +at5VxiStatus at5vxi_bo_driver( + unsigned card, + unsigned long val, + unsigned long mask +); + +at5VxiStatus at5vxi_getioscanpvt( + unsigned card, + IOSCANPVT *scanpvt +); + diff --git a/src/drv/old/drvBB232.c b/src/drv/old/drvBB232.c new file mode 100644 index 000000000..ef119a955 --- /dev/null +++ b/src/drv/old/drvBB232.c @@ -0,0 +1,598 @@ +/* base/src/drv $Id$ */ +/* + * Author: John Winans + * Date: 05-21-92 + * EPICS BITBUS -> R/S-232 driver + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 09-30-91 jrw created + * + */ + +#define DRVBB232_C + +#include +#include +#if 0 /* COMMENTED OUT SOME INCLUDES */ +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* COMMENTED OUT SOME INCLUDES */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +int drvBB232Debug = 0; +int softBB232 = 0; + +extern struct drvBitBusEt drvBitBus; + +/****************************************************************************** + * + ******************************************************************************/ +#define BB232LINK_PRI 50 +#define BB232LINK_OPT VX_FP_TASK|VX_STDIO +#define BB232LINK_STACK 5000 + + +/****************************************************************************** + * + ******************************************************************************/ +static long +report() +{ + printf("Report for BITBUS -> RS-232 driver\n"); + return(OK); +} + +/****************************************************************************** + * + ******************************************************************************/ +static long +init(pparms) +msgDrvIniParm *pparms; +{ + if (drvBB232Debug) + printf("Init for BITBUS -> RS-232 driver\n"); + + return(OK); +} + +/****************************************************************************** + * + * This function is called to allocate any structures needed to hold + * device-specific data that is added to the msgXact structure. + * + * It is also responsible for filling in the msgXact.phwpvt pointer. + * + ******************************************************************************/ +static long +genXact(p) +msgDrvGenXParm *p; +{ + long stat = -1; + char message[100]; + drvBB232Link *pdrvBB232Link; + struct dpvtBitBusHead *pdpvtBitBusHead; + + if (p->plink->type != BITBUS_IO) + { + p->pmsgXact->prec->pact = TRUE; + sprintf("%s: invalid device type in devSup.ascii (%d)\n", p->pmsgXact->prec->name, p->plink->type); + errMessage(S_db_badField, message); + return(ERROR); + } + + if (drvBB232Debug) + printf("BB232 genXact entered for link %d, bug %d, port %d, parm %s\n", p->plink->value.bitbusio.link, p->plink->value.bitbusio.node, p->plink->value.bitbusio.port, p->plink->value.bitbusio.parm); + + p->pmsgXact->phwpvt = p->pmsgXact->pparmBlock->pmsgHwpvt; + + /* try to find the hwpvt structure for the required link, bug, and port */ + while ((p->pmsgXact->phwpvt != NULL) && (stat == -1)) + { + pdrvBB232Link = (drvBB232Link *)(p->pmsgXact->phwpvt->pmsgLink->p); + + if ((pdrvBB232Link->link == p->plink->value.bitbusio.link) + && (pdrvBB232Link->node == p->plink->value.bitbusio.node) + && (pdrvBB232Link->port == p->plink->value.bitbusio.port)) + { + if (pdrvBB232Link->pparmBlock != p->pmsgXact->pparmBlock) + { + sprintf(message, "%s: Two different devices on same BB232 port\n", p->pmsgXact->prec->name); + errMessage(S_db_badField, message); + return(ERROR); + } + else + stat = 0; /* Found the correct hwpvt structure */ + } + else + p->pmsgXact->phwpvt = p->pmsgXact->phwpvt->next; + } + if (stat != 0) + { /* Could not find a msgHwpvt for the right link, create a new one */ + if ((p->pmsgXact->phwpvt = drvMsg_genHwpvt(p->pmsgXact->pparmBlock, p->plink)) == NULL) + return(ERROR); + } + /* Set again in case hwpvt was just allocated (and to make compiler happy) */ + pdrvBB232Link = (drvBB232Link *)(p->pmsgXact->phwpvt->pmsgLink->p); + + p->pmsgXact->callback.callback = NULL; + p->pmsgXact->psyncSem = NULL; + + /* Create the skeleton bitbus driver message */ + pdpvtBitBusHead = (struct dpvtBitBusHead *) malloc(sizeof(struct dpvtBitBusHead)); + (struct dpvtBitBusHead *)(p->pmsgXact->p) = pdpvtBitBusHead; + + pdpvtBitBusHead->finishProc = NULL; + pdpvtBitBusHead->psyncSem = malloc(sizeof(SEM_ID)); + *(pdpvtBitBusHead->psyncSem) = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + pdpvtBitBusHead->link = pdrvBB232Link->link; + + pdpvtBitBusHead->txMsg.route = BB_STANDARD_TX_ROUTE; + pdpvtBitBusHead->txMsg.node = pdrvBB232Link->node; + pdpvtBitBusHead->txMsg.tasks = BB_232_TASK; + pdpvtBitBusHead->txMsg.cmd = 0xff; + + pdpvtBitBusHead->rxMsg.data = (unsigned char *) malloc(BB_MAX_DAT_LEN); + + /* in case I read before write */ + pdpvtBitBusHead->rxMsg.cmd = 0; + pdpvtBitBusHead->rxMsg.length = BB_MSG_HEADER_SIZE; + pdpvtBitBusHead->status = BB_OK; + + pdpvtBitBusHead->rxMaxLen = 0; + pdpvtBitBusHead->ageLimit = 0; + + if (sscanf(p->plink->value.bitbusio.parm,"%d", &(p->pmsgXact->parm)) != 1) + { + p->pmsgXact->prec->pact = TRUE; + sprintf("%s: invalid parameter string >%s<\n", p->pmsgXact->prec->name, p->plink->value.bitbusio.parm); + errMessage(S_db_badField, message); + return(ERROR); + } + + return(OK); +} + +/****************************************************************************** + * + * This function is called to allocate any structures needed to hold + * device-specific data that is added to the msgHwpvt structure. + * + * It is also responsible for filling in the msgHwpvt.pmsgLink pointer. + * + ******************************************************************************/ +static long +genHwpvt(p) +msgDrvGenHParm *p; +{ + int stat = ERROR; + + if (drvBB232Debug) + printf("BB232 genHwpvt entered\n"); + + p->pmsgHwpvt->pmsgLink = drvBB232Block.pmsgLink; + while ((p->pmsgHwpvt->pmsgLink != NULL) && (stat == ERROR)) + { + if ((((drvBB232Link *)(p->pmsgHwpvt->pmsgLink->p))->link == p->plink->value.bitbusio.link) + && (((drvBB232Link *)(p->pmsgHwpvt->pmsgLink->p))->node == p->plink->value.bitbusio.node) + && (((drvBB232Link *)(p->pmsgHwpvt->pmsgLink->p))->port == p->plink->value.bitbusio.port)) + stat = OK; + else + p->pmsgHwpvt->pmsgLink = p->pmsgHwpvt->pmsgLink->next; + } + if (stat != OK) + { + if ((p->pmsgHwpvt->pmsgLink = drvMsg_genLink(p->pparmBlock, p->plink)) == NULL) + return(ERROR); + } + return(OK); +} + +/****************************************************************************** + * + * This function is called to allocate any structures needed to hold + * device-specific data that is added to the msgLink structure. + * + ******************************************************************************/ +static long +genLink(p) +msgDrvGenLParm *p; +{ + char name[20]; + char message[100]; + drvBB232Link *pdrvBB232Link; + + if (drvBB232Debug) + printf("BB232 genLink, link = %d, node = %d\n", p->plink->value.bitbusio.link, p->plink->value.bitbusio.node, p->plink->value.bitbusio.port); + + switch (p->op) { + case MSG_GENLINK_CREATE: + +/* BUG -- verify that the link and node numbers are within range! */ + + if (p->plink->value.bitbusio.port & (~DD_232_PORT)) + { + sprintf(message, "drvBB232: port number %d out of range\n", p->plink->value.bitbusio.port); + errMessage(S_db_badField, message); + return(ERROR); + } + + if ((p->pmsgLink->p = malloc(sizeof(drvBB232Link))) == NULL) + return(ERROR); + + pdrvBB232Link = (drvBB232Link *)(p->pmsgLink->p); + + pdrvBB232Link->link = p->plink->value.bitbusio.link; + pdrvBB232Link->node = p->plink->value.bitbusio.node; + pdrvBB232Link->port = p->plink->value.bitbusio.port; + pdrvBB232Link->pparmBlock = p->pparmBlock; + + + return(OK); + + case MSG_GENLINK_ABORT: + + if (drvBB232Debug) + printf("BB232 genLink: called with abort status\n"); + + pdrvBB232Link = (drvBB232Link *)(p->pmsgLink->p); + /* BUG - free(p->pmsgLink->p); Don't forget to take it out of the list */ + return(OK); + } + return(ERROR); +} + +/****************************************************************************** + * + * This function is called to allow the device to indicate that a device-related + * event has occurred. If an event had occurred, it would have to place the + * address of a valid transaction structure in pxact. This xact structure + * will then be processed by the message link task as indicated by the + * return code of this function. + * + * MSG_EVENT_NONE = no event has occurred, pxact not filled in. + * MSG_EVENT_WRITE = event occurred, process the writeOp assoc'd w/pxact. + * MSG_EVENT_READ = event occurred, process the readOp assoc'd w/pxact. + * + * Note that MSG_EVENT_READ only makes sense when returned with a transaction + * that has deferred readback specified for its readOp function. Because if + * it is NOT a deferred readback, it will be done immediately after the writeOp. + * Even if that writeOp was due to a MSG_EVENT_WRITE from this function. + * + ******************************************************************************/ +static long +checkEvent(p) +msgChkEParm *p; +{ + return(MSG_EVENT_NONE); +} +/****************************************************************************** + * + * This IOCTL function is used to handle non-I/O related operations required + * to operate the RS-232 interface. + * + ******************************************************************************/ +static long +command(p) +ioctlCommand *p; +{ + msgXact *pxact; + struct dpvtBitBusHead *pdpvtBitBusHead; + drvBB232Link *pdrvBB232Link; + + switch (p->cmd) { + case IOCTL232_BREAK: /* send a BREAK signal to the serial port. */ + /* Build a break message to send */ + pxact = (msgXact *) (p->pparm); + pdpvtBitBusHead = (struct dpvtBitBusHead *) pxact->p; + pdrvBB232Link = (drvBB232Link *)(pxact->phwpvt->pmsgLink->p); + pdpvtBitBusHead->status = BB_OK; + pdpvtBitBusHead->rxMaxLen = 0; + pdpvtBitBusHead->txMsg.length = BB_MSG_HEADER_SIZE; + pdpvtBitBusHead->txMsg.cmd = BB_232_BREAK + pdrvBB232Link->port; + pdpvtBitBusHead->rxMsg.cmd = 0; + + if (drvBB232Debug > 7) + { + printf("drvBB232.control (tx L%d N%d P%d)\n", pdrvBB232Link->link, pdrvBB232Link->node, pdrvBB232Link->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); + } + + (*(drvBitBus.qReq))(pdpvtBitBusHead, BB_Q_LOW); + semTake(*(pdpvtBitBusHead->psyncSem), WAIT_FOREVER); + + if ((pdpvtBitBusHead->status == BB_OK) && !(pdpvtBitBusHead->rxMsg.cmd & 1)) + return(OK); + else + return(ERROR); + } + + if (drvBB232Debug) + printf("drvBB232.control: bad command 0x%02.2X\n", p->cmd); + return(ERROR); +} + +/****************************************************************************** + * + * This function is used to write a string of characters out to an RS-232 + * device. + * + * Note that the BUGs reply to the write messages with the first 13 bytes + * of the machine's response string (if there is one.) This response string + * will be left in the pdpvtBitBusHead->rxMsg buffer when the drvRead() + * function is called later. + * + ******************************************************************************/ +static long +drvWrite(pxact, pwrParm) +msgXact *pxact; +msgStrParm *pwrParm; +{ + int len; + drvBB232Link *pdrvBB232Link = (drvBB232Link *)(pxact->phwpvt->pmsgLink->p); + struct dpvtBitBusHead *pdpvtBitBusHead = (struct dpvtBitBusHead *) pxact->p; + unsigned char loopLen; + + /* build a bitbus message and call the bitbus driver */ + + if (pwrParm->len == -1) + len = strlen(pwrParm->buf); + else + len = pwrParm->len; + + pdpvtBitBusHead->txMsg.data = (unsigned char *) pwrParm->buf; + pdpvtBitBusHead->status = BB_OK; + pdpvtBitBusHead->rxMaxLen = BB_MAX_MSG_LENGTH; + + pdpvtBitBusHead->txMsg.cmd = BB_232_CMD + pdrvBB232Link->port; + pdpvtBitBusHead->rxMsg.cmd = 0; + + while (len && (pdpvtBitBusHead->status == BB_OK) && + !(pdpvtBitBusHead->rxMsg.cmd & 1)) + { + if (len > BB_MAX_DAT_LEN) + loopLen = BB_MAX_DAT_LEN; + else + loopLen = len; + + pdpvtBitBusHead->txMsg.length = loopLen + BB_MSG_HEADER_SIZE; + if (softBB232) + { + printf("drvBB232.drvWrite(tx L%d N%d P%d):\n", pdrvBB232Link->link, pdrvBB232Link->node, pdrvBB232Link->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); + } + else + { + (*(drvBitBus.qReq))(pdpvtBitBusHead, BB_Q_LOW); /* do it */ + semTake(*(pdpvtBitBusHead->psyncSem), WAIT_FOREVER); /* wait for completion */ + if (drvBB232Debug > 10) + { + printf("drvBB232.drvWrite (tx L%d N%d P%d)\n", pdrvBB232Link->link, pdrvBB232Link->node, pdrvBB232Link->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); + } + } + + pdpvtBitBusHead->txMsg.data += loopLen; + len -= loopLen; + } + + if ((pdpvtBitBusHead->status != BB_OK) || (pdpvtBitBusHead->rxMsg.cmd & 1)) + { + if (drvBB232Debug) + printf("BB232 write error on link %d, node %d, port %d, driver %02.2X, message %02.2X\n", pdrvBB232Link->link, pdrvBB232Link->node, pdrvBB232Link->port, pdpvtBitBusHead->status, pdpvtBitBusHead->rxMsg.cmd); + + pxact->status = XACT_IOERR; + } + + return(pxact->status); +} + +/****************************************************************************** + * + * This function is used to read a string of characters from an RS-232 device. + * + * It is assumed that pdpvtBitBusHead->rxMsg has already got up to 13 + * bytes of the response data already filled in (left there by the prior + * call to drvWrite().) If a call to this function is made when there is + * garbage in the initial pdpvtBitBusHead->rxMsg buffer, set the length field + * to 0 and the status field to BB_OK. + * + ******************************************************************************/ +static long +drvRead(pxact, prdParm) +msgXact *pxact; +msgStrParm *prdParm; +{ + drvBB232Link *pdrvBB232Link = (drvBB232Link *)(pxact->phwpvt->pmsgLink->p); + struct dpvtBitBusHead *pdpvtBitBusHead = (struct dpvtBitBusHead *) pxact->p; + int len = prdParm->len; + int loopLen = 0; + unsigned char *tmp = pdpvtBitBusHead->rxMsg.data; + + /* check if data already loaded into pdpvtBitBusHead->rxMsg */ + if ((pdpvtBitBusHead->rxMsg.length > BB_MSG_HEADER_SIZE) && (pdpvtBitBusHead->status == BB_OK)) + { + loopLen = pdpvtBitBusHead->rxMsg.length - BB_MSG_HEADER_SIZE; + + if (prdParm->len < loopLen) + loopLen = prdParm->len; + + if (drvBB232Debug > 9) + { + printf("drvBB232.drvRead (rx L%d N%d P%d)\n", pdrvBB232Link->link, pdrvBB232Link->node, pdrvBB232Link->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->rxMsg)); + } + bcopy(pdpvtBitBusHead->rxMsg.data, prdParm->buf, loopLen); + len -= loopLen; + } + + pdpvtBitBusHead->txMsg.length = BB_MSG_HEADER_SIZE; + pdpvtBitBusHead->txMsg.cmd = BB_232_CMD + pdrvBB232Link->port; + + pdpvtBitBusHead->rxMsg.data = (unsigned char *) (&(prdParm->buf[loopLen])); + + if (!(pdpvtBitBusHead->rxMsg.cmd & BB_232_EOI)) + { /* If we did not hit EOI yet, keep reading */ + + while (len && (pdpvtBitBusHead->status == BB_OK)) + { + if (len > BB_MAX_DAT_LEN) + loopLen = BB_MAX_DAT_LEN; + else + loopLen = len; + + pdpvtBitBusHead->rxMaxLen = loopLen + BB_MSG_HEADER_SIZE; + if (softBB232) + { + printf("drvBB232.drvRead(tx L%d N%d P%d)\n", pdrvBB232Link->link, pdrvBB232Link->node, pdrvBB232Link->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); + pdpvtBitBusHead->status = BB_232_EOI; + } + else + { + if (drvBB232Debug > 10) + { + printf("drvBB232.drvRead(tx L%d N%d P%d)\n", pdrvBB232Link->link, pdrvBB232Link->node, pdrvBB232Link->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); + } + + (*(drvBitBus.qReq))(pdpvtBitBusHead, BB_Q_LOW); /* do it */ + semTake(*(pdpvtBitBusHead->psyncSem), WAIT_FOREVER); /* wait for completion */ + + if (drvBB232Debug > 9) + { + printf("drvBB232.drvRead (rx L%d N%d P%d)\n", pdrvBB232Link->link, pdrvBB232Link->node, pdrvBB232Link->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->rxMsg)); + } + } + + if (pdpvtBitBusHead->rxMsg.cmd & (~BB_232_EOI)) + { /* Something is wrong... */ + if (drvBB232Debug > 6) + { + printf("drvBB232.drvRead (rx L%d N%d P%d) Error response from BUG\n", pdrvBB232Link->link, pdrvBB232Link->node, pdrvBB232Link->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->rxMsg)); + } + pxact->status = XACT_IOERR; + break; /* note that we will fall thru to the null-term code */ + } + if (pdpvtBitBusHead->rxMsg.cmd & BB_232_EOI) + { + pdpvtBitBusHead->rxMsg.data += pdpvtBitBusHead->rxMsg.length - BB_MSG_HEADER_SIZE; + len -= pdpvtBitBusHead->rxMsg.length - BB_MSG_HEADER_SIZE; + break; + } + + pdpvtBitBusHead->rxMsg.data += loopLen; + len -= loopLen; + } + } + /* Null-terminate the input string (if there is room) */ + if (len) + *(pdpvtBitBusHead->rxMsg.data) = '\0'; + else + pxact->status = XACT_LENGTH; + + /* Note that the following error check, takes priority over a length error */ + if (pdpvtBitBusHead->status != BB_OK) + { + if (drvBB232Debug) + printf("BB232 read error on link %d, node %d, port %d\n", pdrvBB232Link->link, pdrvBB232Link->node, pdrvBB232Link->port); + pxact->status = XACT_IOERR; + } + +/* BUG -- this can print unterminated strings !! */ + if (drvBB232Debug > 7) + printf("drvRead: got >%s<\n", prdParm->buf); + + pdpvtBitBusHead->rxMsg.length = BB_MSG_HEADER_SIZE; /* in case keep reading */ + return(pxact->status); +} + +/****************************************************************************** + * + * This handles any IOCTL calls made to BB-232 devices. + * + ******************************************************************************/ +static long +drvIoctl(cmd, pparm) +int cmd; +void *pparm; +{ + switch (cmd) { + case MSGIOCTL_REPORT: + return(report()); + case MSGIOCTL_INIT: + return(init(pparm)); + case MSGIOCTL_INITREC: + if (drvBB232Debug) + printf("drvBB232Block.drvIoctl got a MSGIOCTL_INITREC request!\n"); + return(ERROR); + case MSGIOCTL_GENXACT: + return(genXact(pparm)); + case MSGIOCTL_GENHWPVT: + return(genHwpvt(pparm)); + case MSGIOCTL_GENLINK: + return(genLink(pparm)); + case MSGIOCTL_CHECKEVENT: + return(checkEvent(pparm)); + case MSGIOCTL_COMMAND: + return(command(pparm)); + } + return(ERROR); +} + + +msgDrvBlock drvBB232Block = { + "BB232", /* taskName */ + BB232LINK_PRI, /* taskPri */ + BB232LINK_OPT, /* taskOpt */ + BB232LINK_STACK, /* taskStack */ + NULL, /* pmsgLink (linked list header) */ + drvWrite, /* drvWrite */ + drvRead, /* drvRead */ + drvIoctl /* drvIoctl */ +}; diff --git a/src/drv/old/drvBBMsg.c b/src/drv/old/drvBBMsg.c new file mode 100644 index 000000000..a693d71b7 --- /dev/null +++ b/src/drv/old/drvBBMsg.c @@ -0,0 +1,590 @@ +/* base/src/drv $Id$ */ +/* + * Author: John Winans + * Date: 05-21-92 + * EPICS BITBUS driver for message based I/O + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 08-10-92 jrw created + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +int drvBBMsgDebug = 0; + +extern struct drvBitBusEt drvBitBus; + +/****************************************************************************** + * + ******************************************************************************/ +#define BBMSGLINK_PRI 50 +#define BBMSGLINK_OPT VX_FP_TASK|VX_STDIO +#define BBMSGLINK_STACK 5000 + + +/****************************************************************************** + * + ******************************************************************************/ +static long +report() +{ + printf("Report for BITBUS message driver\n"); + return(OK); +} + +/****************************************************************************** + * + ******************************************************************************/ +static long +init(pparms) +msgDrvIniParm *pparms; +{ + if (drvBBMsgDebug) + printf("Init for BITBUS message driver\n"); + + return(OK); +} + +/****************************************************************************** + * + * This function is called to allocate any structures needed to hold + * device-specific data that is added to the msgXact structure. + * + * It is also responsible for filling in the msgXact.phwpvt pointer. + * + ******************************************************************************/ +static long +genXact(p) +msgDrvGenXParm *p; +{ + long stat = -1; + char message[100]; + drvBBMsgLink *pdrvBBMsgLink; + struct dpvtBitBusHead *pdpvtBitBusHead; + + if (p->plink->type != BITBUS_IO) + { + p->pmsgXact->prec->pact = TRUE; + sprintf("%s: invalid device type in devSup.ascii (%d)\n", p->pmsgXact->prec->name, p->plink->type); + errMessage(S_db_badField, message); + return(ERROR); + } + + if (drvBBMsgDebug) + printf("BBMsg genXact entered for link %d, bug %d, port %d, parm %s\n", p->plink->value.bitbusio.link, p->plink->value.bitbusio.node, p->plink->value.bitbusio.port, p->plink->value.bitbusio.parm); + + p->pmsgXact->phwpvt = p->pmsgXact->pparmBlock->pmsgHwpvt; + + /* try to find the hwpvt structure for the required link, bug, and port */ + while ((p->pmsgXact->phwpvt != NULL) && (stat == -1)) + { + pdrvBBMsgLink = (drvBBMsgLink *)(p->pmsgXact->phwpvt->pmsgLink->p); + + if ((pdrvBBMsgLink->link == p->plink->value.bitbusio.link) + && (pdrvBBMsgLink->node == p->plink->value.bitbusio.node) + && (pdrvBBMsgLink->port == p->plink->value.bitbusio.port)) + { + if (pdrvBBMsgLink->pparmBlock != p->pmsgXact->pparmBlock) + { + sprintf(message, "%s: Two different devices on same BBMsg port\n", p->pmsgXact->prec->name); + errMessage(S_db_badField, message); + return(ERROR); + } + else + stat = 0; /* Found the correct hwpvt structure */ + } + else + p->pmsgXact->phwpvt = p->pmsgXact->phwpvt->next; + } + if (stat != 0) + { /* Could not find a msgHwpvt for the right link, create a new one */ + if ((p->pmsgXact->phwpvt = drvMsg_genHwpvt(p->pmsgXact->pparmBlock, p->plink)) == NULL) + return(ERROR); + } + /* Set again in case hwpvt was just allocated (and to make compiler happy) */ + pdrvBBMsgLink = (drvBBMsgLink *)(p->pmsgXact->phwpvt->pmsgLink->p); + + p->pmsgXact->callback.callback = NULL; + p->pmsgXact->psyncSem = NULL; + + /* Create the skeleton bitbus driver message */ + pdpvtBitBusHead = (struct dpvtBitBusHead *) malloc(sizeof(struct dpvtBitBusHead)); + (struct dpvtBitBusHead *)(p->pmsgXact->p) = pdpvtBitBusHead; + + pdpvtBitBusHead->finishProc = NULL; + pdpvtBitBusHead->psyncSem = malloc(sizeof(SEM_ID)); + *(pdpvtBitBusHead->psyncSem) = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + pdpvtBitBusHead->link = pdrvBBMsgLink->link; + + pdpvtBitBusHead->txMsg.route = BB_STANDARD_TX_ROUTE; + pdpvtBitBusHead->txMsg.node = pdrvBBMsgLink->node; + pdpvtBitBusHead->txMsg.tasks = BB_MSG_TASK; + pdpvtBitBusHead->txMsg.cmd = 0xff; + + pdpvtBitBusHead->rxMsg.data = (unsigned char *) malloc(BB_MAX_DAT_LEN); + + /* in case I read before write */ + pdpvtBitBusHead->rxMsg.cmd = 0; + pdpvtBitBusHead->rxMsg.length = BB_MSG_HEADER_SIZE; + pdpvtBitBusHead->status = BB_OK; + + pdpvtBitBusHead->rxMaxLen = 0; + pdpvtBitBusHead->ageLimit = 0; + + if (sscanf(p->plink->value.bitbusio.parm,"%d", &(p->pmsgXact->parm)) != 1) + { + p->pmsgXact->prec->pact = TRUE; + sprintf("%s: invalid parameter string >%s<\n", p->pmsgXact->prec->name, p->plink->value.bitbusio.parm); + errMessage(S_db_badField, message); + return(ERROR); + } + + return(OK); +} + +/****************************************************************************** + * + * This function is called to allocate any structures needed to hold + * device-specific data that is added to the msgHwpvt structure. + * + * It is also responsible for filling in the msgHwpvt.pmsgLink pointer. + * + ******************************************************************************/ +static long +genHwpvt(p) +msgDrvGenHParm *p; +{ + int stat = ERROR; + + if (drvBBMsgDebug) + printf("BBMSG genHwpvt entered\n"); + + p->pmsgHwpvt->pmsgLink = drvBBMSGBlock.pmsgLink; + while ((p->pmsgHwpvt->pmsgLink != NULL) && (stat == ERROR)) + { + if ((((drvBBMsgLink *)(p->pmsgHwpvt->pmsgLink->p))->link == p->plink->value.bitbusio.link) + && (((drvBBMsgLink *)(p->pmsgHwpvt->pmsgLink->p))->node == p->plink->value.bitbusio.node) + && (((drvBBMsgLink *)(p->pmsgHwpvt->pmsgLink->p))->port == p->plink->value.bitbusio.port)) + stat = OK; + else + p->pmsgHwpvt->pmsgLink = p->pmsgHwpvt->pmsgLink->next; + } + if (stat != OK) + { + if ((p->pmsgHwpvt->pmsgLink = drvMsg_genLink(p->pparmBlock, p->plink)) == NULL) + return(ERROR); + } + return(OK); +} + +/****************************************************************************** + * + * This function is called to allocate any structures needed to hold + * device-specific data that is added to the msgLink structure. + * + ******************************************************************************/ +static long +genLink(p) +msgDrvGenLParm *p; +{ + char name[20]; + char message[100]; + drvBBMsgLink *pdrvBBMsgLink; + + if (drvBBMsgDebug) + printf("BBMsg genLink, link = %d, node = %d\n", p->plink->value.bitbusio.link, p->plink->value.bitbusio.node, p->plink->value.bitbusio.port); + + switch (p->op) { + case MSG_GENLINK_CREATE: + +/* BUG -- verify that the link and node numbers are within range! */ + + if (p->plink->value.bitbusio.port & (~DD_MSG_PORT)) + { + sprintf(message, "drvBBMsg: port number %d out of range\n", p->plink->value.bitbusio.port); + errMessage(S_db_badField, message); + return(ERROR); + } + + if ((p->pmsgLink->p = malloc(sizeof(drvBBMsgLink))) == NULL) + return(ERROR); + + pdrvBBMsgLink = (drvBBMsgLink *)(p->pmsgLink->p); + + pdrvBBMsgLink->link = p->plink->value.bitbusio.link; + pdrvBBMsgLink->node = p->plink->value.bitbusio.node; + pdrvBBMsgLink->port = p->plink->value.bitbusio.port; + pdrvBBMsgLink->pparmBlock = p->pparmBlock; + + + return(OK); + + case MSG_GENLINK_ABORT: + + if (drvBBMsgDebug) + printf("BBMsg genLink: called with abort status\n"); + + pdrvBBMsgLink = (drvBBMsgLink *)(p->pmsgLink->p); + /* BUG - free(p->pmsgLink->p); Don't forget to take it out of the list */ + return(OK); + } + return(ERROR); +} + +/****************************************************************************** + * + * This function is called to allow the device to indicate that a device-related + * event has occurred. If an event had occurred, it would have to place the + * address of a valid transaction structure in pxact. This xact structure + * will then be processed by the message link task as indicated by the + * return code of this function. + * + * MSG_EVENT_NONE = no event has occurred, pxact not filled in. + * MSG_EVENT_WRITE = event occurred, process the writeOp assoc'd w/pxact. + * MSG_EVENT_READ = event occurred, process the readOp assoc'd w/pxact. + * + * Note that MSG_EVENT_READ only makes sense when returned with a transaction + * that has deferred readback specified for its readOp function. Because if + * it is NOT a deferred readback, it will be done immediately after the writeOp. + * Even if that writeOp was due to a MSG_EVENT_WRITE from this function. + * + ******************************************************************************/ +static long +checkEvent(p) +msgChkEParm *p; +{ + return(MSG_EVENT_NONE); +} +/****************************************************************************** + * + * This IOCTL function is used to handle non-I/O related operations required + * to operate the RS-MSG interface. + * + ******************************************************************************/ +static long +command(p) +ioctlCommand *p; +{ + msgXact *pxact; + struct dpvtBitBusHead *pdpvtBitBusHead; + drvBBMsgLink *pdrvBBMsgLink; + + switch (p->cmd) { + case IOCTLMSG_BREAK: /* send a BREAK signal to the serial port. */ + /* Build a break message to send */ + pxact = (msgXact *) (p->pparm); + pdpvtBitBusHead = (struct dpvtBitBusHead *) pxact->p; + pdrvBBMsgLink = (drvBBMsgLink *)(pxact->phwpvt->pmsgLink->p); + pdpvtBitBusHead->status = BB_OK; + pdpvtBitBusHead->rxMaxLen = 0; + pdpvtBitBusHead->txMsg.length = BB_MSG_HEADER_SIZE; + pdpvtBitBusHead->txMsg.cmd = BB_MSG_BREAK + pdrvBBMsgLink->port; + pdpvtBitBusHead->rxMsg.cmd = 0; + + if (drvBBMsgDebug > 7) + { + printf("drvBBMsg.control (tx L%d N%d P%d)\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); + } + + (*(drvBitBus.qReq))(pdpvtBitBusHead, BB_Q_LOW); + semTake(*(pdpvtBitBusHead->psyncSem), WAIT_FOREVER); + + if ((pdpvtBitBusHead->status == BB_OK) && !(pdpvtBitBusHead->rxMsg.cmd & 1)) + return(OK); + else + return(ERROR); + } + + if (drvBBMsgDebug) + printf("drvBBMsg.control: bad command 0x%02.2X\n", p->cmd); + return(ERROR); +} + +/****************************************************************************** + * + * This function is used to write a string of characters out to an BB_MSG + * device. + * + * Note that the BUGs reply to the write messages with the first 13 bytes + * of the machine's response string (if there is one.) This response string + * will be left in the pdpvtBitBusHead->rxMsg buffer when the drvRead() + * function is called later. + * + ******************************************************************************/ +static long +drvWrite(pxact, pwrParm) +msgXact *pxact; +msgStrParm *pwrParm; +{ + int len; + drvBBMsgLink *pdrvBBMsgLink = (drvBBMsgLink *)(pxact->phwpvt->pmsgLink->p); + struct dpvtBitBusHead *pdpvtBitBusHead = (struct dpvtBitBusHead *) pxact->p; + unsigned char loopLen; + + /* build a bitbus message and call the bitbus driver */ + + if (pwrParm->len == -1) + len = strlen(pwrParm->buf); + else + len = pwrParm->len; + + pdpvtBitBusHead->txMsg.data = (unsigned char *) pwrParm->buf; + pdpvtBitBusHead->status = BB_OK; + pdpvtBitBusHead->rxMaxLen = BB_MAX_MSG_LENGTH; + + pdpvtBitBusHead->txMsg.cmd = BB_MSG_CMD + pdrvBBMsgLink->port; + pdpvtBitBusHead->rxMsg.cmd = 0; + + while (len && (pdpvtBitBusHead->status == BB_OK) && + !(pdpvtBitBusHead->rxMsg.cmd & 1)) + { + if (len > BB_MAX_DAT_LEN) + loopLen = BB_MAX_DAT_LEN; + else + loopLen = len; + + pdpvtBitBusHead->txMsg.length = loopLen + BB_MSG_HEADER_SIZE; + if (softBBMsg) + { + printf("drvBBMsg.drvWrite(tx L%d N%d P%d):\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); + } + else + { + (*(drvBitBus.qReq))(pdpvtBitBusHead, BB_Q_LOW); /* do it */ + semTake(*(pdpvtBitBusHead->psyncSem), WAIT_FOREVER); /* wait for completion */ + if (drvBBMsgDebug > 10) + { + printf("drvBBMsg.drvWrite (tx L%d N%d P%d)\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); + } + } + + pdpvtBitBusHead->txMsg.data += loopLen; + len -= loopLen; + } + + if ((pdpvtBitBusHead->status != BB_OK) || (pdpvtBitBusHead->rxMsg.cmd & 1)) + { + if (drvBBMsgDebug) + printf("BBMsg write error on link %d, node %d, port %d, driver %02.2X, message %02.2X\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port, pdpvtBitBusHead->status, pdpvtBitBusHead->rxMsg.cmd); + + pxact->status = XACT_IOERR; + } + + return(pxact->status); +} + +/****************************************************************************** + * + * This function is used to read a string of characters from an RS-Msg device. + * + * It is assumed that pdpvtBitBusHead->rxMsg has already got up to 13 + * bytes of the response data already filled in (left there by the prior + * call to drvWrite().) If a call to this function is made when there is + * garbage in the initial pdpvtBitBusHead->rxMsg buffer, set the length field + * to 0 and the status field to BB_OK. + * + ******************************************************************************/ +static long +drvRead(pxact, prdParm) +msgXact *pxact; +msgStrParm *prdParm; +{ + drvBBMsgLink *pdrvBBMsgLink = (drvBBMsgLink *)(pxact->phwpvt->pmsgLink->p); + struct dpvtBitBusHead *pdpvtBitBusHead = (struct dpvtBitBusHead *) pxact->p; + int len = prdParm->len; + int loopLen = 0; + unsigned char *tmp = pdpvtBitBusHead->rxMsg.data; + + /* check if data already loaded into pdpvtBitBusHead->rxMsg */ + if ((pdpvtBitBusHead->rxMsg.length > BB_MSG_HEADER_SIZE) && (pdpvtBitBusHead->status == BB_OK)) + { + loopLen = pdpvtBitBusHead->rxMsg.length - BB_MSG_HEADER_SIZE; + + if (prdParm->len < loopLen) + loopLen = prdParm->len; + + if (drvBBMsgDebug > 9) + { + printf("drvBBMsg.drvRead (rx L%d N%d P%d)\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->rxMsg)); + } + bcopy(pdpvtBitBusHead->rxMsg.data, prdParm->buf, loopLen); + len -= loopLen; + } + + pdpvtBitBusHead->txMsg.length = BB_MSG_HEADER_SIZE; + pdpvtBitBusHead->txMsg.cmd = BB_MSG_CMD + pdrvBBMsgLink->port; + + pdpvtBitBusHead->rxMsg.data = (unsigned char *) (&(prdParm->buf[loopLen])); + + if (!(pdpvtBitBusHead->rxMsg.cmd & BB_MSG_EOI)) + { /* If we did not hit EOI yet, keep reading */ + + while (len && (pdpvtBitBusHead->status == BB_OK)) + { + if (len > BB_MAX_DAT_LEN) + loopLen = BB_MAX_DAT_LEN; + else + loopLen = len; + + pdpvtBitBusHead->rxMaxLen = loopLen + BB_MSG_HEADER_SIZE; + if (softBBMsg) + { + printf("drvBB232.drvRead(tx L%d N%d P%d)\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); + pdpvtBitBusHead->status = BB_232_EOI; + } + else + { + if (drvBBMsgDebug > 10) + { + printf("drvBB232.drvRead(tx L%d N%d P%d)\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); + } + + (*(drvBitBus.qReq))(pdpvtBitBusHead, BB_Q_LOW); /* do it */ + semTake(*(pdpvtBitBusHead->psyncSem), WAIT_FOREVER); /* wait for completion */ + + if (drvBBMsgDebug > 9) + { + printf("drvBB232.drvRead (rx L%d N%d P%d)\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->rxMsg)); + } + } + + if (pdpvtBitBusHead->rxMsg.cmd & (~BB_232_EOI)) + { /* Something is wrong... */ + if (drvBBMsgDebug > 6) + { + printf("drvBB232.drvRead (rx L%d N%d P%d) Error response from BUG\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); + drvBitBusDumpMsg(&(pdpvtBitBusHead->rxMsg)); + } + pxact->status = XACT_IOERR; + break; /* note that we will fall thru to the null-term code */ + } + if (pdpvtBitBusHead->rxMsg.cmd & BB_232_EOI) + { + pdpvtBitBusHead->rxMsg.data += pdpvtBitBusHead->rxMsg.length - BB_MSG_HEADER_SIZE; + len -= pdpvtBitBusHead->rxMsg.length - BB_MSG_HEADER_SIZE; + break; + } + + pdpvtBitBusHead->rxMsg.data += loopLen; + len -= loopLen; + } + } + /* Null-terminate the input string (if there is room) */ + if (len) + *(pdpvtBitBusHead->rxMsg.data) = '\0'; + else + pxact->status = XACT_LENGTH; + + /* Note that the following error check, takes priority over a length error */ + if (pdpvtBitBusHead->status != BB_OK) + { + if (drvBBMsgDebug) + printf("BB232 read error on link %d, node %d, port %d\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); + pxact->status = XACT_IOERR; + } + +/* BUG -- this can print unterminated strings !! */ + if (drvBBMsgDebug > 7) + printf("drvRead: got >%s<\n", prdParm->buf); + + pdpvtBitBusHead->rxMsg.length = BB_MSG_HEADER_SIZE; /* in case keep reading */ + return(pxact->status); +} + +/****************************************************************************** + * + * This handles any IOCTL calls made to BB-MSG devices. + * + ******************************************************************************/ +static long +drvIoctl(cmd, pparm) +int cmd; +void *pparm; +{ + switch (cmd) { + case MSGIOCTL_REPORT: + return(report()); + case MSGIOCTL_INIT: + return(init(pparm)); + case MSGIOCTL_INITREC: + if (drvBBMsgDebug) + printf("drvBBMsgBlock.drvIoctl got a MSGIOCTL_INITREC request!\n"); + return(ERROR); + case MSGIOCTL_GENXACT: + return(genXact(pparm)); + case MSGIOCTL_GENHWPVT: + return(genHwpvt(pparm)); + case MSGIOCTL_GENLINK: + return(genLink(pparm)); + case MSGIOCTL_CHECKEVENT: + return(checkEvent(pparm)); + case MSGIOCTL_COMMAND: + return(command(pparm)); + } + return(ERROR); +} + + +msgDrvBlock drvBBMsgBlock = { + "BBMSG", /* taskName */ + BBMSGLINK_PRI, /* taskPri */ + BBMSGLINK_OPT, /* taskOpt */ + BBMSGLINK_STACK, /* taskStack */ + NULL, /* pmsgLink (linked list header) */ + drvWrite, /* drvWrite */ + drvRead, /* drvRead */ + drvIoctl /* drvIoctl */ +}; diff --git a/src/drv/old/drvBb902.c b/src/drv/old/drvBb902.c new file mode 100644 index 000000000..429f88e3e --- /dev/null +++ b/src/drv/old/drvBb902.c @@ -0,0 +1,202 @@ +/* base/src/drv $Id$ */ +/* + * subroutines that are used to interface to the binary output cards + * + * Author: Bob Dalesio + * Date: 5-26-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 + * + * Modification Log: + * ----------------- + * .01 10-31-91 bg broke bb902 code out of bo_driver.c + * .02 02-20-92 bg Added level to io_report. Added ability + * to read out raw values on card if level + * > 0. + * .03 08-10-92 joh made number of cards runtime configurable + * .04 08-25-92 mrk made masks a macro + * .05 09-14-93 mrk Let report just display one hex value + * + */ + +static char SccsId[] = "@(#)drvBb902.c 1.6 9/14/92 "; + +/* + * Code Portions: + * + * bo_drv_init Finds and initializes all binary output cards present + * bo_driver Interfaces to the binary output cards present + */ + +#include +#include +#include +#include + +static long report(); +static long init(); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvBb902={ + 2, + report, + init}; + +static long report(level) + int level; +{ + int i; + + bb902_io_report(level); + return(0); +} + +static long init() +{ + int status; + + bb902_driver_init(); + return(0); +} + +#define MAX_BB_BO_CARDS (bo_num_cards[BB902]) + +/* Burr-Brown 902 binary output memory structure */ +struct bo_bb902{ + short csr; /* control status register */ + unsigned short low_value; /* low order 16 bits value */ + unsigned short high_value; /* high order 16 bits value */ + char end_pad[0x100-6]; /* pad until next card */ +}; + +static char *bb902_shortaddr; + +/* pointers to the binary output cards */ +struct bo_bb902 **pbo_bb902s; /* Burr-Brown 902s */ + + +/* + * BO_DRIVER_INIT + * + * intialization for the binary output cards + */ +int bb902_driver_init(){ + int bomode; + int status; + short i; + struct bo_bb902 *pbo_bb902; + + pbo_bb902s = (struct bo_bb902 **)calloc(MAX_BB_BO_CARDS, + sizeof(*pbo_bb902s)); + if(!pbo_bb902s){ + return ERROR; + } + + /* intialize the Burr-Brown 902 binary output cards */ + + /* base address of the burr-brown 902 binary output cards */ + + status = sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO,bo_addrs[BB902],&bb902_shortaddr); + if (status != OK){ + printf("Addressing error in bb902 driver\n"); + return (ERROR); + } + pbo_bb902 = (struct bo_bb902 *)bb902_shortaddr; + /* determine which cards are present */ + for (i = 0; i < bo_num_cards[BB902]; i++,pbo_bb902++){ + if (vxMemProbe(pbo_bb902,READ,sizeof(short),&bomode) == OK) + pbo_bb902s[i] = pbo_bb902; + else + pbo_bb902s[i] = 0; + } + return (0); + +} + +/* + * BB902_DRIVER + * + * interface to the Burr-Brown binary outputs + */ + +int bb902_driver(card,val,mask) +unsigned short card; +unsigned long val; +unsigned long mask; +{ + unsigned int work; + + /* verify card exists */ + if (!pbo_bb902s[card]) + return (-1); + /* use structure to handle high and low short swap */ + /* get current output */ + work = (pbo_bb902s[card]->high_value << 16) /* high */ + + pbo_bb902s[card]->low_value; /* low */ + /* alter specified bits */ + work = (work & ~mask) | (val & mask); + + /* write new output */ + pbo_bb902s[card]->high_value = (unsigned short)(work >> 16); + pbo_bb902s[card]->low_value = (unsigned short)work; + return (0); +} + +/* + * bb902_read + * + * read the binary output + */ +int bb902_read(card,mask,pval) +unsigned short card; +unsigned int mask; +unsigned int *pval; +{ + unsigned int work; + + /* verify card exists */ + if (!pbo_bb902s[card]) return (-1); + /* readback */ + *pval = (pbo_bb902s[card]->high_value << 16) /* high */ + + pbo_bb902s[card]->low_value; /* low */ + *pval &= mask; + return(0); +} + +void bb902_io_report(level) + short int level; +{ + unsigned int value; + int card; + + for (card = 0; card < MAX_BB_BO_CARDS; card++){ + if (pbo_bb902s[card]){ + value = (pbo_bb902s[card]->high_value << 16) + + pbo_bb902s[card]->low_value; + printf("BO: BB902: card %d value=0x%08.8x\n",card,value); + } + } + +} diff --git a/src/drv/old/drvBb910.c b/src/drv/old/drvBb910.c new file mode 100644 index 000000000..19db9cbfd --- /dev/null +++ b/src/drv/old/drvBb910.c @@ -0,0 +1,212 @@ +/* bb910_driver.c */ +/* base/src/drv $Id$ */ +/* + * subroutines that are used to interface to the binary input cards + * + * Author: Bob Dalesio + * Date: 6-13-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 + * + * Modification Log: + * ----------------- + * .01 02-09-89 lrd moved I/O addresses to module_types.h + * .02 11-20-89 joh added call to at5vxi driver + * .03 09-11-91 bg added bb910_io_report + * .04 10-31-91 bg broke bb910 code out of bi_driver.c + * .05 02-04-92 bg added the argument level to + * bb910_io_report() and gave it the ability + * to read raw values from card if level > 0 + * .06 08-10-92 joh made the number of cards runtime + * .07 08-25-92 mrk made masks a macro + */ + +/* + * Code Portions: + * + * bi_driver_init Finds and initializes all binary input cards present + * bi_driver Interfaces to the binary input cards present + */ + + +#include +#include +#include +#include + +static long report(); +static long init(); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvBb910={ + 2, + report, + init}; + +static long report(level) + int level; +{ + register int i; + + bb910_io_report(level); + return(0); +} +static long init() +{ + int status; + + bb910_driver_init(); + return(0); +} + +static char SccsId[] = "@(#)drvBb910.c 1.6\t6/3/93"; + +#define MAX_BB_BI_CARDS (bi_num_cards[BB910]) + +/* Burr-Brown 910 binary input memory structure */ +/* Note: the high and low order words are switched from the io card */ +struct bi_bb910{ + unsigned short csr; /* control status register */ + unsigned short low_value; /* low order 16 bits value */ + unsigned short high_value; /* high order 16 bits value */ + char end_pad[0x100-6]; /* pad until next card */ +}; + +/* pointers to the binary input cards */ +struct bi_bb910 **pbi_bb910s; /* Burr-Brown 910s */ + +/* test word for forcing bi_driver */ +int bi_test; + +static char *bb910_shortaddr; + + +/* + * BI_DRIVER_INIT + * + * intialization for the binary input cards + */ + +bb910_driver_init(){ + int bimode; + int status; + register short i; + struct bi_bb910 *pbi_bb910; + + pbi_bb910s = (struct bi_bb910 **) + calloc(MAX_BB_BI_CARDS, sizeof(*pbi_bb910s)); + if(!pbi_bb910s){ + return ERROR; + } + + /* intialize the Burr-Brown 910 binary input cards */ + /* base address of the burr-brown 910 binary input cards */ + + status=sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO,bi_addrs[BB910],&bb910_shortaddr); + if (status != OK){ + printf("Addressing error in bb910 driver\n"); + return ERROR; + } + pbi_bb910 = (struct bi_bb910 *)bb910_shortaddr; + + /* determine which cards are present */ + for (i = 0; i < bi_num_cards[BB910]; i++,pbi_bb910++){ + if (vxMemProbe(pbi_bb910,READ,sizeof(short),&bimode) == OK){ + pbi_bb910s[i] = pbi_bb910; + } + else { + pbi_bb910s[i] = 0; + } + } + + return (0); + +} + + +bb910_driver(card,mask,prval) + + register unsigned short card; + unsigned int mask; + register unsigned int *prval; +{ + register unsigned int work; + + if (!pbi_bb910s[card]) + return (-1); + + /* read */ + + work = (pbi_bb910s[card]->high_value << 16) /* high */ + + pbi_bb910s[card]->low_value; /* low */ + /* apply mask */ + *prval = work & mask; + + return (0); + } + +#define masks(K) ((1< 0){ + num_chans = bi_num_channels[BB910]; + for(j=0,k=1,l=2,m=3;j < num_chans,k < num_chans, l < num_chans,m < num_chans; + j+=IOR_MAX_COLS,k+= IOR_MAX_COLS,l+= IOR_MAX_COLS,m += IOR_MAX_COLS){ + if(j < num_chans){ + bb910_driver(i,masks(j),BB910,&jval); + if (jval != 0) + jval = 1; + printf("Chan %d = %x\t ",j,jval); + } + if(k < num_chans){ + bb910_driver(i,masks(k),BB910,&kval); + if (kval != 0) + kval = 1; + printf("Chan %d = %x\t ",k,kval); + } + if(l < num_chans){ + bb910_driver(i,masks(l),BB910,&lval); + if (lval != 0) + lval = 1; + printf("Chan %d = %x \t",l,lval); + } + if(m < num_chans){ + bb910_driver(i,masks(m),BB910,&mval); + if (mval != 0) + mval = 1; + printf("Chan %d = %x \n",m,mval); + } + } + } + } + } + } diff --git a/src/drv/old/drvBitBus.c b/src/drv/old/drvBitBus.c new file mode 100644 index 000000000..db3c6e074 --- /dev/null +++ b/src/drv/old/drvBitBus.c @@ -0,0 +1,2624 @@ +/* #define XYCOM_DO_RESET_AND_OFFLINE */ +/* #define PEP_DO_RESET_AND_OFFLINE */ + + +/* + changes to UI: + + XYCOM_BB_MAX_OUTSTAND_MSGS -> XycomMaxOutstandMsgs + + + + TODO: + + 1) Synthetic delays after 91s. + 2) Rebuild the link kill stuff so that it does not mess with the WDtask. + 3) Verify race conditions on the slave reset function. + 4) Write a callable bug-syncer that uses the RAC get function ID command. + 5) Merge the watchdog tasks into one. + 6) Try merging the TX and RX tasks. + +*/ + +/* + * Original Author: Ned Arnold + * Author: John Winans + * Date: 09-10-91 + * XVME-402 and PB-bit BitBus driver + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 09-30-91 jrw Completely redesigned and rewritten + * .02 12-02-91 jrw Changed priority info to arrays + * .03 12-16-91 jrw Made the data portion of the message a pointer + * .04 01-21-91 jrw moved the task parameters into task_params.h + * .05 02-12-92 jrw removed IRQ based transmission. + * .06 04-08-92 jrw moved the device configs into module_types.h + * .07 09-28-92 jrw upped the reset delay time to 1/2 second + * .08 08-02-93 mrk Added call to taskwdInsert + * .09 06-28-94 jrw Major work on RAC_RESET and NODE_OFFLINE stuff. + * .10 07-01-94 jrw Merged PEP and Xycom versions together. + * ANSIficated this code + * + * NOTES: + * This driver currently needs work on error message generation. + * + * $Log$ + * Revision 1.37 1995/03/24 21:24:25 winans + * Probable race condition in PEP TX task. Moved the final transmission + * byte assignment into the point where the busy list is locked. This + * is how the Xycom TX task has worked all along. This change fixed + * seems to have fixed an apparant race condition where the receive task + * gets a response to a transmitted message BEFORE the TX task gets + * it on the busy list. + * + * Revision 1.36 1994/12/16 16:11:26 winans + * Added debug flag guards to ALL printing. The default debug level is set + * to 1 -- this provides the same output as the old version. + * + * Revision 1.35 1994/12/12 16:02:57 winans + * Rewrote the init code so that it always returns a zero (don't kill the + * startup.cmd file.) It is possible that this could cause some confusion + * to the database, should it decide to then use a link that did not init + * properly. + * + * Revision 1.34 1994/10/19 18:31:22 winans + * ANSIfied the bitbus driver so that the compiler stopped warning about + * exery third line of code. + * + * Revision 1.33 1994/10/04 18:42:42 winans + * Added an extensive debugging facility. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "drvBitBus.h" + +#define STATIC static + +#define BB_DEFAULT_RETIRE_TIME 5 /* Seconds to wait for a response */ + +STATIC long reportBB(void); +STATIC long initBB(void); +STATIC long qBBReq(struct dpvtBitBusHead *pdpvt, int prio); + +STATIC int xvmeReset(int link); +STATIC int xvmeTmoHandler(int link); +STATIC int xvmeRxTask(int link); +STATIC int xvmeTxTask(int link); +STATIC int xvmeWdTask(int link); +STATIC int xvmeIrqRdav(int link); +STATIC int xvmeIrqRcmd(int link); + +STATIC int pepReset(int link); +STATIC int pepTmoHandler(int link); +STATIC int pepRxTask(int link); +STATIC int pepTxTask(int link); +STATIC int pepWdTask(int link); +STATIC int pepIrqRdav(int link); + +STATIC int bbKill(int link); +STATIC void BBrebootFunc(void); + +STATIC int txStuck(int link); +int BBConfig(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); +int __BBConfig(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); + +#ifdef BB_SUPER_DEBUG +int BBHistDump(int link); +STATIC int BBDumpXactHistory(XactHistStruct *pXact); +#endif + + +/***************************************************************************** + * + * Used to limit the TOTAL number of simultaneous messages that can be + * outstanding on a single Xycom link. (Shell settable.) + * + *****************************************************************************/ +int XycomMaxOutstandMsgs = XYCOM_BB_MAX_OUTSTAND_MSGS; + +/***************************************************************************** + * + * Debugging flags that may be set from the shell. + * + * bbDebug Used to get status information from the driver's internals. + * May be set with a value from 0 to 40. the higher the number + * the more information. 0 will provide no internal debugging. + * + * bbDebugLink Used to get general status information about a single slave + * bbDebugNode node. + * To disable this feature, set one of them to -1. + * + *****************************************************************************/ +int bbDebug = 1; +int bbDebugLink=-1; +int bbDebugNode=-1; + +#ifdef BB_SUPER_DEBUG +static int BBSetHistEvent(int link, struct dpvtBitBusHead *pXact, int State); +#endif + +/****************************************************************************** + * + * This structure contains a list of the outside-callable functions. + * + ******************************************************************************/ +struct drvBitBusEt drvBitBus = { + 3, + reportBB, + initBB, + qBBReq +}; + +#if 0 +/* JRW --> get rid of this crap */ +STATIC char BitbusInitCalled = 0; /* to insure that init is done first */ +STATIC void *short_base; /* base of short address space */ +#endif + + +STATIC BitbusLinkStruct *pBBLink[BB_NUM_LINKS]; /* NULL if link not config'd */ + /*************************************************************************** + * + * User-callable configuration function. + * + * This function is used to configure the type of link as well as to + * initialize it for operation. + * + *****************************************************************************/ +int BBConfig(unsigned long Link, + unsigned long LinkType, + unsigned long BaseAddr, + unsigned long IrqVector, + unsigned long IrqLevel) +{ + __BBConfig(Link, LinkType, BaseAddr, IrqVector, IrqLevel); + return(0); +} + +int __BBConfig(unsigned long Link, + unsigned long LinkType, + unsigned long BaseAddr, + unsigned long IrqVector, + unsigned long IrqLevel) +{ + void *pVoid; + int j; + static int FirstTime = 1; + char nameTemp[30]; + int taskId; + + if (FirstTime) + { + rebootHookAdd(BBrebootFunc); + FirstTime = 0; + } + if (Link >= BB_NUM_LINKS) + { + logMsg("Error: Invalid link (%d) specified in BBConfig()\n", Link); + return(-1); + } + if ((LinkType != BB_CONF_TYPE_XYCOM) && (LinkType != BB_CONF_TYPE_PEP)) + { + logMsg("Error: Invalid link type (%d) specified in BBConfig()\n", LinkType); + return(-1); + } + if (pBBLink[Link] != NULL) + { + logMsg("Error: BBConfig() Attempt to reconfigure link %d!\n", Link); + return(-1); + } + + if ((pBBLink[Link] = (BitbusLinkStruct *) malloc(sizeof(BitbusLinkStruct))) == NULL) + { + logMsg("Error: BBConfig() cannot malloc\n"); + return(-1); + } + + pBBLink[Link]->LinkType = BB_CONF_HOSED; /* Changed if all goes well */ + pBBLink[Link]->BaseAddr = BaseAddr; + pBBLink[Link]->IrqVector = IrqVector; + pBBLink[Link]->IrqLevel = IrqLevel; + +#ifdef BB_SUPER_DEBUG + /* Init the history FIFO to empty */ + pBBLink[Link]->History.sem = semBCreate(SEM_Q_PRIORITY, SEM_FULL); + pBBLink[Link]->History.Next = 0; + pBBLink[Link]->History.Num = 0; +#endif + + pBBLink[Link]->linkEventSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + + if (sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO , BaseAddr, &pVoid) != OK) + { + logMsg("Error: BBConfig() can not translate requested A16 address(0x%8.8X\n", BaseAddr); + pBBLink[Link] = NULL; + return(-1); + } + + /* + * Interface specific I/O mapping and registration + */ + + switch (LinkType) + { + case BB_CONF_TYPE_XYCOM: + pBBLink[Link]->l.XycomLink.rxInt = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + pBBLink[Link]->l.XycomLink.bbRegs = (XycomBBRegsStruct *)pVoid; + if (devRegisterAddress("Xycom Bitbus", atVMEA16, (void*)BaseAddr, sizeof(XycomBBRegsStruct), NULL) != 0) + { + logMsg("Error: BBConfig() can not register address %8.8X\n", pVoid); + pBBLink[Link] = NULL; + return(-1); /* BUG */ + } + break; + case BB_CONF_TYPE_PEP: + pBBLink[Link]->l.PepLink.rxInt = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + pBBLink[Link]->l.PepLink.bbRegs = (PepBBRegsStruct *)pVoid; + if (devRegisterAddress("PEP Bitbus", atVMEA16, (void*)BaseAddr, sizeof(PepBBRegsStruct), NULL) != 0) + { + logMsg("Error: BBConfig() can not register address %8.8X\n", pVoid); + pBBLink[Link] = NULL; + return(-1); /* BUG */ + } + break; + } + + /* + * Common data structure initialization. + */ + + /* Init the prioritized queue lists */ + for (j=0; jqueue[j].head = NULL; + pBBLink[Link]->queue[j].tail = NULL; + pBBLink[Link]->queue[j].sem = semBCreate(SEM_Q_PRIORITY, SEM_FULL); + pBBLink[Link]->queue[j].elements = 0; + } + + /* Init the busy message list */ + pBBLink[Link]->busyList.sem = semBCreate(SEM_Q_PRIORITY, SEM_FULL); + pBBLink[Link]->busyList.head = NULL; + pBBLink[Link]->busyList.tail = NULL; + pBBLink[Link]->busyList.elements = 0; + + for (j=0; jdeviceStatus[j] = BB_IDLE; + pBBLink[Link]->syntheticDelay[j] = 0; + } + pBBLink[Link]->DelayCount = 0; + + pBBLink[Link]->watchDogId = wdCreate(); + pBBLink[Link]->watchDogSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + + /* Clear the link abort status */ + pBBLink[Link]->abortFlag = 0; + pBBLink[Link]->txAbortAck = 0; + pBBLink[Link]->rxAbortAck = 0; + + /* + * Interface type specific initialization of management tasks. + */ + + if (LinkType == BB_CONF_TYPE_XYCOM) + { + /* BOARD SPECIFIC INIT CODE for XYCOM */ + if (xvmeReset(Link) != 0) + { + pBBLink[Link] = NULL; + return(-1); + } + + pBBLink[Link]->LinkType = LinkType; + + /* attach the interrupt handler routines */ + intConnect((BB_IVEC_BASE + 1 + (Link*4)) * 4, xvmeIrqRcmd, Link); + intConnect((BB_IVEC_BASE + 3 + (Link*4)) * 4, xvmeIrqRdav, Link); + + /* Start a task to manage the TX link */ + sprintf(nameTemp, "%s%d-xy", BBTXLINK_NAME, Link); + if ((taskId=taskSpawn(nameTemp, BBTXLINK_PRI, BBTXLINK_OPT, BBTXLINK_STACK, xvmeTxTask, Link)) == ERROR) + { + logMsg("BBConfig(): failed to start TX link task for link %d\n", Link); + } + taskwdInsert(taskId,NULL,NULL); + /* Start a task to manage the RX link */ + sprintf(nameTemp, "%s%d-xy", BBRXLINK_NAME, Link); + if ((taskId=taskSpawn(nameTemp, BBRXLINK_PRI, BBRXLINK_OPT, BBRXLINK_STACK, xvmeRxTask, Link)) == ERROR) + { + logMsg("BBConfig(): failed to start RX link task for link %d\n", Link); + } + taskwdInsert(taskId,NULL,NULL); + /* Start a task to keep an eye on the busy list */ + sprintf(nameTemp, "%s%d-xy", BBWDTASK_NAME, Link); + if ((taskId=taskSpawn(nameTemp, BBWDTASK_PRI, BBWDTASK_OPT, BBWDTASK_STACK, xvmeWdTask, Link)) == ERROR) + { + logMsg("BBConfig(): failed to start watchdog task for link %d\n", Link); + } + taskwdInsert(taskId,NULL,NULL); + } + else if (LinkType == BB_CONF_TYPE_PEP) + { + /* PEP specific init code */ + if (pepReset(Link) != 0) + { + pBBLink[Link] = NULL; + return(-1); + } + + pBBLink[Link]->LinkType = LinkType; + + /* attach the interrupt handler routines */ + intConnect(INUM_TO_IVEC(PEP_BB_IVEC_BASE + (Link*2)), pepIrqRdav, Link); + + /* Start a task to manage the TX link */ + sprintf(nameTemp, "%s%d-pep", BBTXLINK_NAME, Link); + if ((taskId=taskSpawn(nameTemp, BBTXLINK_PRI, BBTXLINK_OPT, BBTXLINK_STACK, pepTxTask, Link)) == ERROR) + { + logMsg("BBConfig(): failed to start TX link task for link %d\n", Link); + } + taskwdInsert(taskId,NULL,NULL); + /* Start a task to manage the RX link */ + sprintf(nameTemp, "%s%d-pep", BBRXLINK_NAME, Link); + if ((taskId=taskSpawn(nameTemp, BBRXLINK_PRI, BBRXLINK_OPT, BBRXLINK_STACK, pepRxTask, Link)) == ERROR) + { + logMsg("BBConfig(): failed to start RX link task for link %d\n", Link); + } + taskwdInsert(taskId,NULL,NULL); + /* Start a task to keep an eye on the busy list */ + sprintf(nameTemp, "%s%d-pep", BBWDTASK_NAME, Link); + if ((taskId=taskSpawn(nameTemp, BBWDTASK_PRI, BBWDTASK_OPT, BBWDTASK_STACK, pepWdTask, Link)) == ERROR) + { + logMsg("BBConfig(): failed to start watchdog task for link %d\n", Link); + } + taskwdInsert(taskId,NULL,NULL); + } + sysIntEnable(IrqLevel); + return(0); +} + /****************************************************************** + * + * The EPICS init routine is not needed. + * + ******************************************************************/ +STATIC long initBB(void) +{ + return(0); +} + +/****************************************************************** + * FUNCTION: reportBB() + * PURPOSE : Prints a message indicating the presence of each + * bitbus module found in system. + * ARGS IN : none + * ARGS OUT: none + * GLOBALS: checks pBBLink[i] for NULL + ******************************************************************/ + +STATIC long reportBB(void) +{ + int i; + + if (bbDebug>1) + printf("Bitbus debugging flag is set to %d\n", bbDebug); + + for (i=0; i< BB_NUM_LINKS; i++) + { + if (pBBLink[i] != NULL) + { + if (pBBLink[i]->LinkType == BB_CONF_TYPE_XYCOM) + printf("Bitbus link %d present at %p IV=0x%2.2X IL=%d (XYCOM)\n", i, pBBLink[i]->l.XycomLink.bbRegs, pBBLink[i]->IrqVector, pBBLink[i]->IrqLevel); + else if (pBBLink[i]->LinkType == BB_CONF_TYPE_PEP) + printf("Bitbus link %d present at %p IV=0x%2.2X IL=%d (PEP)\n", i, pBBLink[i]->l.PepLink.bbRegs, pBBLink[i]->IrqVector, pBBLink[i]->IrqLevel); + } + } + return(OK); +} + /****************************************************************** + * FUNCTION: pepReset() + * PURPOSE : Performs firmware reset of PB-BIT module corresponding + * to link. Attempts to empty any data sitting in receive + * FIFO. Interrupts are disabled and Rx task is unblocked. + * ARGS IN : xvmeRegs ptr to register structure of PB-BIT module + * link link number serviced by PB-BIT module + * ARGS OUT: none + * GLOBALS: twiddles board + ******************************************************************/ +STATIC int +pepReset(int link) +{ + char trash; + int j; + int lockKey; + int probeValue; + + if (bbDebug) + printf("pepReset(%d): Resetting pep module\n", link); + + probeValue = 0; + if (vxMemProbe(&(pBBLink[link]->l.PepLink.bbRegs->int_vec), WRITE, 1, &probeValue) < OK) + { /* no BB board present here */ + logMsg("ERROR: PEP Bitbus link %d not present at 0x%8.8X\n", link, pBBLink[link]->l.PepLink.bbRegs); + return(-1); + } + + /* Write firmware reset package (2 bytes) to board */ + pBBLink[link]->l.PepLink.bbRegs->data = 0x83; + pBBLink[link]->l.PepLink.bbRegs->stat_ctl = 0x01; + + taskDelay(20); /* give the 80152 time to self check */ + + if ((pBBLink[link]->l.PepLink.bbRegs->stat_ctl & 0x10) != 0x0) + { + if (bbDebug) + printf("pepReset(%d): PB-BIT firmware reset failed!\n", link); + return(ERROR); + } + + j = 1026; /* 1K deep receive fifo */ + /* flush receive fifo if junk in it */ + while ((pBBLink[link]->l.PepLink.bbRegs->stat_ctl & PEP_BB_RFNE) && --j) + trash = pBBLink[link]->l.PepLink.bbRegs->data; + + if (!j) + { + if (bbDebug) + printf("pepReset(%d): receive fifo will not clear after reset!\n", link); + return(ERROR); + } + + /* Disable interrupts */ + lockKey = intLock(); + pBBLink[link]->l.PepLink.bbRegs->int_vec = 0; + intUnlock(lockKey); + + /* Tell pepRxTask to Re-enable ints */ + semGive(pBBLink[link]->l.PepLink.rxInt); + + return(OK); +} + /**************************************************************************** + * + * Reset an xvme-402 BitBus card by cycling the reset bit in the fifo + * status register. + * + ****************************************************************************/ +STATIC int xvmeReset(int link) +{ + char trash; + int j; + int lockKey; + int probeValue; + + + if (bbDebug) + printf("xvmeReset(%d): Resetting xvme module\n", link); + + probeValue = XVME_RESET; + if (vxMemProbe(&(pBBLink[link]->l.XycomLink.bbRegs->fifo_stat), WRITE, 1, &probeValue) < OK) + { /* no BB board present here */ + logMsg("ERROR: Xycom Bitbus link %d not present at 0x%8.8X\n", link, pBBLink[link]->l.XycomLink.bbRegs); + return(-1); + } + +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, NULL, XACT_HIST_STATE_RESET); +#endif + + taskDelay(2); + pBBLink[link]->l.XycomLink.bbRegs->fifo_stat = 0; /* clear reset pulse */ + taskDelay(30); /* give the 8044 time to self check */ + + j = 100; /* give up after this */ + while ((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_RCMD) && --j) + trash = pBBLink[link]->l.XycomLink.bbRegs->cmnd; /* flush command buffer if junk in it */ + + if (!j) + { + if (bbDebug) + printf("xvmeReset(%d): Command buffer will not clear after reset!\n", link); + return(ERROR); + } + + j = 100; + while ((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_RFNE) && --j) + trash = pBBLink[link]->l.XycomLink.bbRegs->data; /* flush data buffer if junk in it */ + + if (!j) + { + if (bbDebug) + printf("xvmeReset(%d): Data buffer will not clear after reset!\n", link); + return(ERROR); + } + + if ((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_FSVALID) != XVME_FSIDLE) + { + if (bbDebug) + printf("xvmeReset(%d): XVME board not returning to idle status after reset!\n", link); + return(ERROR); + } + + /* set the interrupt vector */ + lockKey = intLock(); + pBBLink[link]->l.XycomLink.bbRegs->stat_ctl = 0; /* disable all interupts */ + pBBLink[link]->l.XycomLink.bbRegs->int_vec = BB_IVEC_BASE + (link*4);/* set the int vector */ + intUnlock(lockKey); + + semGive(pBBLink[link]->l.XycomLink.rxInt); /* Tell xvmeRxTask to Re-enable interrupts */ + + return(OK); +} + /**************************************************************************** + * + * This function is used to add a node to the HEAD of a list. + * + ****************************************************************************/ +static int +listAddHead(struct bbList *plist, struct dpvtBitBusHead *pnode) +{ + pnode->prev = NULL; + pnode->next = plist->head; + + if (plist->head != NULL) + plist->head->prev = pnode; + + if (plist->tail == NULL) + plist->tail = pnode; + + plist->head = pnode; + + plist->elements++; + + return(0); +} +/****************************************************************************** + * + * This function is used to add a node to the TAIL of a list. + * + ******************************************************************************/ +static int +listAddTail(struct bbList *plist, struct dpvtBitBusHead *pnode) +{ + pnode->next = NULL; /* No next node if this is the TAIL */ + pnode->prev = plist->tail; /* previous node is the 'old' TAIL node */ + + if (plist->tail != NULL) + plist->tail->next = pnode; /* link the 'old' tail to the 'new' tail node */ + + if (plist->head == NULL) + plist->head = pnode; + + plist->tail = pnode; /* this is the 'new' tail node */ + + plist->elements++; + + return(0); +} + /*************************************************************************** + * + * This function is used to delete a node from a list. + * + ***************************************************************************/ +static int +listDel(struct bbList *plist, struct dpvtBitBusHead *pnode) +{ + if (pnode->next != NULL) + pnode->next->prev = pnode->prev; + + if (pnode->prev != NULL) + pnode->prev->next = pnode->next; + + if (plist->head == pnode) + plist->head = pnode->next; + + if (plist->tail == pnode) + plist->tail = pnode->prev; + + plist->elements--; + + return(0); +} + /**************************************************************************** + * + * xycom IRQ handler for receiver data bytes. + * + ****************************************************************************/ +static int +xvmeIrqRdav(int link) +{ + if (bbDebug > 30) + logMsg("bitbus rx IRQ on link %d\n", link); + semGive(pBBLink[link]->l.XycomLink.rxInt); /* deliver the groceries */ + return(0); +} + +/**************************************************************************** + * + * xycom IRQ handler invoked when the BitBus controller has completed the + * transfer of a RECEIVED message. + * + ****************************************************************************/ +static int +xvmeIrqRcmd(int link) +{ + if (bbDebug > 30) + logMsg("bitbus rcmd IRQ on link %d\n", link); + semGive(pBBLink[link]->l.XycomLink.rxInt); /* deliver the groceries */ + return(0); +} + /****************************************************************** + * FUNCTION: pepIrqRdav() + * PURPOSE : Invoked when PB-BIT module has received a complete + * bitbus message (ie. not on a byte-by-byte basis). + * ARGS IN : link link number upon which message has arrived + * ARGS OUT: none + * GLOBALS: unblocks Rx task (if it is not already running) + ******************************************************************/ +STATIC int +pepIrqRdav(int link) +{ + pBBLink[link]->l.PepLink.bbRegs->int_vec = 0; /* disable IRQs */ + + if (bbDebug > 30) + logMsg("PEP bitbus rx IRQ on link %d\n", link); + + semGive(pBBLink[link]->l.PepLink.rxInt); /* unblock it */ + return(0); +} + /****************************************************************** + * FUNCTION: pepTmoHandler() + * PURPOSE : Invoked whenever a watchdog timer times out. Watchdogs + * are running whenever the busyList has any elements on + * it. The idea here is that the watchdog handler scans + * through the busyList, looking for old requests that have + * not been replied to in too long a time. If there are + * any old ones around, they are removed from the list, + * and the associated node is reset and marked offline. + * + * ARGS IN : link + * ARGS OUT: none + * GLOBALS: none + ******************************************************************/ +STATIC int +pepTmoHandler(int link) +{ + if (bbDebug > 25) + logMsg("pepTmoHandler(%d): Watch dog interrupt\n", link); + + semGive(pBBLink[link]->watchDogSem); /* unblock Wd task */ + return(0); +} + +/****************************************************************************** + * + * Watchdogs are running when ever the busy list has any elements in it. + * The idea here is that the watchdog handler scans thru the busy list, + * looking for old requests that have not been replied to in too long + * a time. If there are any old ones around, they are removed from the + * list and marked as un-replied to. + * + ******************************************************************************/ +static int +xvmeTmoHandler(int link) +{ + if (bbDebug > 25) + logMsg("xvmeTmoHandler(%d): Watch dog interrupt\n", link); + + semGive(pBBLink[link]->watchDogSem); + return(0); +} + /**************************************************************************** + * + * Given a link number, make sure it is valid. + * + ****************************************************************************/ +static int +checkLink(int link) +{ + if ((link<0) || (link>BB_NUM_LINKS)) + return(ERROR); /* link number out of range */ + + if (pBBLink[link] == NULL) + return(ERROR); /* link number has no card installed */ + + return(OK); +} + /*************************************************************************** + * + * This function is started as a task during driver init time. It's purpose + * is to read messages that are received from the bitbus link. After a message + * is received, the soliciting message's completion routines are started by + * the use of semaphores and/or a callbackRequest(). + * + ***************************************************************************/ +static int +xvmeRxTask(int link) +{ + int rxState; /* current state of the receiver */ +#define BBRX_HEAD 1 +#define BBRX_DATA 2 +#define BBRX_RCMD 3 +#define BBRX_IGN 4 + + unsigned char rxHead[5]; /* room for header of current rx msg */ + unsigned char *rxMsg; /* where to place next byte (after header) */ + int rxTCount; /* byte counter for data in rxHead */ + unsigned char ch; + struct dpvtBitBusHead *rxDpvtHead; /* for message currently receiving */ + + struct dpvtBitBusHead UselessMsg; /* to hold unsolicited responses */ + unsigned char UselessData[BB_MAX_MSG_LENGTH]; + + int lockKey; /* used for intLock calls */ + + rxMsg = (unsigned char *) NULL; + rxState = BBRX_HEAD; + rxTCount = 0; + rxDpvtHead = (struct dpvtBitBusHead *) NULL; + + /* Dummy up the UselessMsg fields so we can use it */ + UselessMsg.rxMaxLen = BB_MAX_MSG_LENGTH; + UselessMsg.rxMsg.data = UselessData; + UselessMsg.link = link; + UselessMsg.ageLimit = 0; + UselessMsg.retire = 0; + + while (1) + { + /* Wait for RX interrupts, but only if no chars are ready */ + if ((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_RFNE) == 0) + { + /* Enable interrupts and check again because xycom blew it */ + lockKey = intLock(); + pBBLink[link]->l.XycomLink.bbRegs->stat_ctl = XVME_ENABLE_INT | XVME_RX_INT; + intUnlock(lockKey); + + while (((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_RFNE) == 0) && (pBBLink[link]->abortFlag == 0)) + { + /* Re-enable ints here each time in case board got reset */ + lockKey = intLock(); + pBBLink[link]->l.XycomLink.bbRegs->stat_ctl = XVME_ENABLE_INT | XVME_RX_INT; + intUnlock(lockKey); + + /* Wait for groceries */ + semTake(pBBLink[link]->l.XycomLink.rxInt, WAIT_FOREVER); + } + /* Disable RX Interrupts (prevents unnecessary context switching) */ + lockKey = intLock(); + pBBLink[link]->l.XycomLink.bbRegs->stat_ctl = 0; + intUnlock(lockKey); + } + if (pBBLink[link]->abortFlag == 0) + { + /* check to see if we got a data byte or a command byte */ + if ((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_RCMD) == XVME_RCMD) + rxState = BBRX_RCMD; + + switch (rxState) { + case BBRX_HEAD: /* getting the head of a new message */ + rxHead[rxTCount] = pBBLink[link]->l.XycomLink.bbRegs->data; + if (bbDebug>21) + printf("xvmeRxTask(%d): >%2.2X< (Header)\n", link, rxHead[rxTCount]); + + if (++rxTCount == 5) + { /* find the message this is a reply to */ + rxTCount += 2; /* adjust for the link field space */ + + /* Lock the busy list */ + semTake(pBBLink[link]->busyList.sem, WAIT_FOREVER); + + rxDpvtHead = pBBLink[link]->busyList.head; + while (rxDpvtHead != NULL) + { + if (bbDebug>19) + printf("xvmeRxTask(%d): checking reply against %p\n", link, rxDpvtHead); + + /* see if node's match */ + if (rxDpvtHead->txMsg.node == rxHead[2]) + { /* see if the tasks match */ + if (rxDpvtHead->txMsg.tasks == rxHead[3]) + { /* They match, finish putting response into the rxMsg buffer */ + if (bbDebug>4) + printf("xvmeRxTask(%d): reply to %p\n", link, rxDpvtHead); + + /* Delete the node from the list */ + listDel(&(pBBLink[link]->busyList), rxDpvtHead); + + /* If busy list is empty, stop the dog */ + if (pBBLink[link]->busyList.head == NULL) + wdCancel(pBBLink[link]->watchDogId); + + if (rxHead[4] == 0x91) + { /* something bad happened... inject a delay to the */ + /* requested timeout duration. */ + + if (bbDebug) + printf("xvmeRxTask(%d): 0x91 from node %d, invoking synthetic delay\n", link, rxHead[2]); + (pBBLink[link]->syntheticDelay[rxDpvtHead->txMsg.node]) = rxDpvtHead->retire; + pBBLink[link]->DelayCount++; + } + else + { /* decrement the number of outstanding messages to the node */ + (pBBLink[link]->deviceStatus[rxDpvtHead->txMsg.node])--; + } + + /* Wake up Link Task in case was waiting on "this" node */ + semGive(pBBLink[link]->linkEventSem); + +#if 0 + semGive(pXvmeLink[link]->pbbLink->busyList.sem); + + rxDpvtHead->rxMsg.length = rxHead[0]; + rxDpvtHead->rxMsg.route = rxHead[1]; + rxDpvtHead->rxMsg.node = rxHead[2]; + rxDpvtHead->rxMsg.tasks = rxHead[3]; + rxDpvtHead->rxMsg.cmd = rxHead[4]; + rxMsg = rxDpvtHead->rxMsg.data; + + rxDpvtHead->status = BB_OK; /* OK, unless BB_LENGTH */ + rxState = BBRX_DATA; /* finish reading till RCMD */ +#endif + break; /* get out of the while() */ + } + } + rxDpvtHead = rxDpvtHead->next; /* Keep looking */ + } + + semGive(pBBLink[link]->busyList.sem); + + /* Couldn't find a match... fake one so can print the bad message */ + if (rxDpvtHead == NULL) + rxDpvtHead = &UselessMsg; + + rxDpvtHead->rxMsg.length = rxHead[0]; + rxDpvtHead->rxMsg.route = rxHead[1]; + rxDpvtHead->rxMsg.node = rxHead[2]; + rxDpvtHead->rxMsg.tasks = rxHead[3]; + rxDpvtHead->rxMsg.cmd = rxHead[4]; + rxMsg = rxDpvtHead->rxMsg.data; + + rxDpvtHead->status = BB_OK; /* OK, unless BB_LENGTH */ + rxState = BBRX_DATA; /* finish reading till RCMD */ + } + break; + + case BBRX_DATA: /* finish reading data portion of message */ + ch = pBBLink[link]->l.XycomLink.bbRegs->data; + if (rxTCount >= rxDpvtHead->rxMaxLen) + { + rxState = BBRX_IGN; /* toss the rest of the data */ + rxDpvtHead->status = BB_LENGTH; /* set driver status */ + if (bbDebug>22) + printf("xvmeRxTask(%d): %2.2X (Ignored)\n", link, ch); + } + else + { + *rxMsg = ch; + if (bbDebug>22) + printf("xvmeRxTask(%d): %2.2X (Data)\n", link, ch); + rxMsg++; + rxTCount++; + } + break; + + case BBRX_IGN: + ch = pBBLink[link]->l.XycomLink.bbRegs->data; + if (bbDebug>22) + printf("xvmeRxTask(%d): %2.2X (Ignored)\n", link, ch); + break; + + case BBRX_RCMD: + if (rxDpvtHead == NULL) + { + ch = pBBLink[link]->l.XycomLink.bbRegs->cmnd; + if (bbDebug) + printf("xvmeRxTask(%d): got unexpected XVME_RCMD\n", link); +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, NULL, XACT_HIST_STATE_RX_URCMD); +#endif + } + else if (rxDpvtHead == &UselessMsg) + { + rxDpvtHead->status = BB_OK; + rxDpvtHead->rxCmd = pBBLink[link]->l.XycomLink.bbRegs->cmnd; + + if (bbDebug) + { + printf("xvmeRxTask(%d): msg from node %d unsolicited:", link, rxDpvtHead->rxMsg.node); + drvBitBusDumpMsg(&(rxDpvtHead->rxMsg)); + } +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, rxDpvtHead, XACT_HIST_STATE_RX_UNSOLICITED); +#endif + } + else + { + rxDpvtHead->status = BB_OK; + rxDpvtHead->rxCmd = pBBLink[link]->l.XycomLink.bbRegs->cmnd; + + if (bbDebug>24) + printf("xvmeRxTask(%d):RX command byte = %2.2X\n", link, rxDpvtHead->rxCmd); + +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, rxDpvtHead, XACT_HIST_STATE_RX); +#endif + if (rxDpvtHead->finishProc != NULL) + { + if (bbDebug>8) + printf("xvmeRxTask(%d): invoking the callbackRequest\n", link); + callbackRequest(rxDpvtHead); /* schedule completion processing */ + } + else + { + /* If there is a semaphore for synchronous I/O, unlock it */ + if (rxDpvtHead->psyncSem != NULL) + semGive(*(rxDpvtHead->psyncSem)); + } + } + /* Reset the state of the RxTask to expect a new message */ + rxMsg = (unsigned char *) NULL; + rxState = BBRX_HEAD; + rxTCount = 0; + rxDpvtHead = (struct dpvtBitBusHead *) NULL; + + break; + } + } + else + { /* Link abort state is active reset receiver link status now */ + if (rxDpvtHead != NULL) + { /* This xact is not on the busy list, put it back on */ + semTake(pBBLink[link]->busyList.sem, WAIT_FOREVER); + (pBBLink[link]->deviceStatus[rxDpvtHead->txMsg.node])++; + listAddTail(&(pBBLink[link]->busyList), rxDpvtHead); + semGive(pBBLink[link]->busyList.sem); + } + +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, rxDpvtHead, XACT_HIST_STATE_RX_ABORT); +#endif + rxMsg = (unsigned char *) NULL; + rxState = BBRX_HEAD; + rxTCount = 0; + rxDpvtHead = (struct dpvtBitBusHead *) NULL; + + /* Tell the watch dog I am ready for the reset (reset in the dog task) */ + pBBLink[link]->rxAbortAck = 1; + + if (bbDebug) + printf("xvmeRxTask(%d): resetting due to abort status\n", link); + + /* wait for link state to become active again */ + while (pBBLink[link]->abortFlag != 0) + taskDelay(RESET_POLL_TIME); + + if (bbDebug) + printf("xvmeRxTask(%d): restarting after abort\n", link); + } + } +} + +/****************************************************************************** + * + * A user callable link resetter. This sets a flag and releases the dog + * task to reset the link. + * + ******************************************************************************/ +int +bbReset(int link) +{ + if (checkLink(link) != ERROR) + { + pBBLink[link]->nukeEm = 1; + semGive(pBBLink[link]->watchDogSem); + } + else + printf("Link %d not installed.\n", link); + + return(0); +} + +/****************************************************************************** + * + * BUG -- this does not do the nuke=2 reset and kill link operations like + * the PEP does. + * + ******************************************************************************/ +static int +xvmeWdTask(int link) +{ + struct dpvtBitBusHead *pnode; + struct dpvtBitBusHead *npnode; + unsigned long now; + +#ifdef XYCOM_DO_RESET_AND_OFFLINE + SEM_ID syncSem; + + struct dpvtBitBusHead resetNode; + unsigned char resetNodeData; /* 1-byte data field for RAC_OFFLINE */ + + /* init the SEM used when sending the reset message */ + syncSem = semBCreate(SEM_EMPTY, SEM_Q_PRIORITY); + + /* + * Hand-craft a RAC_OFFLINE message to use when a message times out. + * NOTE that having only one copy is OK provided that the dog waits for + * a response before sending it again! + */ + resetNode.finishProc = NULL; /* no callback routine used */ + resetNode.psyncSem = &syncSem;/* do a semGive on this SEM when done sending */ + resetNode.link = link; /* which bitbus link to send message out on */ + resetNode.rxMaxLen = 7; /* Chop off the response... we don't care */ + resetNode.ageLimit = sysClkRateGet()*100; /* make sure this never times out */ + + resetNode.txMsg.length = 8; + resetNode.txMsg.route = BB_STANDARD_TX_ROUTE; + resetNode.txMsg.node = 0xff; + resetNode.txMsg.tasks = 0x0; + resetNode.txMsg.cmd = RAC_OFFLINE; + resetNode.txMsg.data = &resetNodeData; +#endif + + pBBLink[link]->nukeEm = 0; /* Make sure the nuke status is clear */ + while(1) + { + semTake(pBBLink[link]->watchDogSem, WAIT_FOREVER); + now = tickGet(); /* what time is it? */ + + if (pBBLink[link]->nukeEm != 0) + printf("Bitbus manual reset being issued on link %d\n", link); + + if (bbDebug>4) + printf("xvmeWdTask(%d): (Watchdog) checking busy list\n", link); + + pBBLink[link]->rxAbortAck = 0; /* In case we need to use them */ + pBBLink[link]->txAbortAck = 0; + + if (pBBLink[link]->nukeEm != 0) + { /* set abort status and wait for the abort acks */ + pBBLink[link]->abortFlag = 1; + + /* wake up the Tx task so it can observe the abort status */ + semGive(pBBLink[link]->linkEventSem); + + /* wake up the Rx task so it can observe the abort ststus */ + semGive(pBBLink[link]->l.XycomLink.rxInt); + + /* sleep until abort ack from Tx & Rx tasks */ + while ((pBBLink[link]->rxAbortAck == 0) && (pBBLink[link]->txAbortAck == 0)) + taskDelay(RESET_POLL_TIME); + +#ifdef BB_SUPER_DEBUG + if (pBBLink[link]->nukeEm < 2) + { /* Do a history dump since this is not supposed to happen */ + BBHistDump(link); + } +#endif + } + /* + * Run thru entire busy list to see if there are any transactions + * that have been waiting on a response for too long a period. + */ + semTake(pBBLink[link]->busyList.sem, WAIT_FOREVER); + pnode = pBBLink[link]->busyList.head; + + while (pnode != NULL) + { + npnode = pnode->next; /* remember where we were in the list */ + + if ((pBBLink[link]->nukeEm != 0) || (pnode->retire <= now)) + { + /* Get rid of the request and set error status etc... */ + listDel(&(pBBLink[link]->busyList), pnode); + + if (bbDebug) + { +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, pnode, XACT_HIST_STATE_WD_TIMEOUT); +#endif + printf("xvmeWdTask(%d): TIMEOUT on bitbus message:\n", link); + drvBitBusDumpMsg(&pnode->txMsg); + } + + /* BUG -- do this here or defer until RX gets a response? */ + (pBBLink[link]->deviceStatus[pnode->txMsg.node])--; /* fix device status */ + pnode->status = BB_TIMEOUT; + +#ifdef XYCOM_DO_RESET_AND_OFFLINE + /* BUG ----- wait for the response from the last one first? */ + + /* Do now in case we need it later */ + resetNodeData = pnode->txMsg.node; /* mark the node number */ +#endif + + /* Make the callbackRequest if one was spec'd */ + if(pnode->finishProc != NULL) + { + callbackRequest(pnode); /* schedule completion processing */ + } + else + { + /* Release a completion lock if one was spec'd */ + if (pnode->psyncSem != NULL) + semGive(*(pnode->psyncSem)); + } + +#ifdef XYCOM_DO_RESET_AND_OFFLINE + /* If we are not going to reboot the link... */ + if (pBBLink[link]->nukeEm == 0) + { /* Send out a RAC_NODE_OFFLINE to the controller */ + semGive(pBBLink[link]->busyList.sem); + + if (bbDebug) + printf("issuing a node offline for link %d node %d\n", link, resetNodeData); + + semTake(pBBLink[link]->queue[BB_Q_HIGH].sem, WAIT_FOREVER); + listAddHead(&(pBBLink[link]->queue[BB_Q_HIGH]), &resetNode); + semGive(pBBLink[link]->queue[BB_Q_HIGH].sem); + + semGive(pBBLink[link]->linkEventSem); /* Tell TxTask to send the message */ + + if (semTake(syncSem, sysClkRateGet()/4) == ERROR) + { + if (bbDebug) + printf("xvmeWdTask(%d): link dead, trying manual reboot\n", link); + pBBLink[link]->nukeEm = 1; + + pBBLink[link]->abortFlag = 1; /* Start the abort sequence */ + semGive(pBBLink[link]->linkEventSem); /* Let Tx task observe abort status */ + semGive(pBBLink[link]->l.XycomLink.rxInt); /* Let Rx task observe abort ststus */ + + /* sleep until abort ack from Tx & Rx tasks */ + while ((pBBLink[link]->rxAbortAck == 0) && (pBBLink[link]->txAbortAck == 0)) + taskDelay(RESET_POLL_TIME); + } + /* Start over since released the busy list */ + + semTake(pBBLink[link]->busyList.sem, WAIT_FOREVER); + npnode = pBBLink[link]->busyList.head; + } +#else + semGive(pBBLink[link]->linkEventSem); +#endif + } + pnode = npnode; + } + if (pBBLink[link]->busyList.head != NULL) + { /* Restart the dog timer */ + + if (bbDebug>5) + printf("xvmeWdTask(%d): restarting watch dog timer\n", link); + + wdStart(pBBLink[link]->watchDogId, pBBLink[link]->busyList.head->retire - now, xvmeTmoHandler, link); + } + semGive(pBBLink[link]->busyList.sem); + + /* Finish the link reboot if necessary */ + if (pBBLink[link]->nukeEm != 0) + { + xvmeReset(link); + + /* clear the abort_flag */ + pBBLink[link]->abortFlag = 0; + + pBBLink[link]->nukeEm = 0; + } + } +} + +/****************************************************************************** + * + * This function is started as a task during driver init time. It's purpose + * is to keep the link busy. It awaits user-calls to qBbReq() with new work + * as well as wake-up calls from the watchdog timer. + * + * At the time this function is started as its own task, the linked list + * structures will have been created and initialized. + * + ******************************************************************************/ +static int +xvmeTxTask(int link) +{ + struct dpvtBitBusHead *pnode; + int prio; + int working; + int dogStart; + int stuck; + + int txTCount; + int txCCount; + unsigned char *txMsg; + register int x; + unsigned long now; + SEM_ID resetNodeSem; + struct dpvtBitBusHead resetNode; + unsigned char resetNodeData; /* 1-byte data field for RAC_OFFLINE */ + + if (bbDebug) + printf("xvmeTxTask started for link %d\n", link); + + /* hand-craft a RAC_OFFLINE message for use with RAC_RESET_SLAVE commands */ + /* NOTE that having only one copy is OK provided that this message is */ + /* sent immediately following the RAC_RESET_SLAVE message. */ + + resetNodeSem = semBCreate(SEM_Q_PRIORITY, SEM_FULL); + + resetNode.finishProc = NULL; + resetNode.psyncSem = &resetNodeSem; + resetNode.link = link; + resetNode.rxMaxLen = 7; /* Chop it off, we don't care */ + resetNode.ageLimit = sysClkRateGet(); + + resetNode.txMsg.length = 8; + resetNode.txMsg.route = BB_STANDARD_TX_ROUTE; + resetNode.txMsg.node = 0xff; + resetNode.txMsg.tasks = 0x0; + resetNode.txMsg.cmd = RAC_OFFLINE; + resetNode.txMsg.data = &resetNodeData; + + while(1) + { + if (pBBLink[link]->abortFlag != 0) + { + pBBLink[link]->txAbortAck = 1; /* let the dog know we are ready */ +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, NULL, XACT_HIST_STATE_TX_ABORT); +#endif + + if (bbDebug) + printf("xvmeTxTask(%d): resetting due to abort status\n", link); + + while (pBBLink[link]->abortFlag != 0) + taskDelay(RESET_POLL_TIME); /* wait for link to reset */ + + if (bbDebug) + printf("xvmeTxTask(%d): restarting after abort\n", link); + } + else + { + if (pBBLink[link]->DelayCount) + semTake(pBBLink[link]->linkEventSem, 2 * sysClkRateGet()); + else + semTake(pBBLink[link]->linkEventSem, WAIT_FOREVER); + } + + if (bbDebug>5) + printf("xvmeTxTask(%d): got an event\n", link); + + working = 1; + while ((working != 0) && (pBBLink[link]->abortFlag == 0) && (pBBLink[link]->busyList.elements < XycomMaxOutstandMsgs)) + { + working = 0; + + prio = BB_NUM_PRIO-1; + while ((prio >= 0) && (pBBLink[link]->abortFlag == 0)) + { + /* see if the queue has anything in it */ + semTake(pBBLink[link]->queue[prio].sem, WAIT_FOREVER); + + if ((pnode = pBBLink[link]->queue[prio].head) != NULL) + { + now = tickGet(); + while (pBBLink[link]->deviceStatus[pnode->txMsg.node] == BB_BUSY) + { + if ((pBBLink[link]->syntheticDelay[pnode->txMsg.node] != 0) + && (pBBLink[link]->syntheticDelay[pnode->txMsg.node] < now)) + { + if (bbDebug) + printf("xvmeTxTask(%d): terminating synthetic idle on node %d\n", link, pnode->txMsg.node); + (pBBLink[link]->deviceStatus[pnode->txMsg.node])--; + pBBLink[link]->syntheticDelay[pnode->txMsg.node] = 0; + pBBLink[link]-> DelayCount--; + } + else + { + if ((pnode = pnode->next) == NULL) + break; + } + } + } + if (pnode != NULL) + { /* have an xact to start processing */ + working = 1; /* indicate work being done */ + + /* delete the node from the inbound fifo queue */ + listDel(&(pBBLink[link]->queue[prio]), pnode); + + semGive(pBBLink[link]->queue[prio].sem); + +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX); +#endif + if (bbDebug>3) + printf("xvmeTxTask(%d): got xact, pnode=%p\n", link, pnode); + + /* Send the message in polled mode */ + + txTCount = pnode->txMsg.length - 2; + txCCount = 0; + txMsg = &(pnode->txMsg.length); + + while ((txCCount <= txTCount) && (pBBLink[link]->abortFlag == 0)) + { + stuck = 1000; + while (((pBBLink[link]->l.XycomLink.bbRegs->fifo_stat & XVME_TFNF) != XVME_TFNF) && (pBBLink[link]->abortFlag == 0) && --stuck) + for(x=0;x<100;x++); /* wait for TX ready */ + + if (!stuck) + { +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX_STUCK); +#endif + txStuck(link); /* will end up setting abortFlag */ + } + else if (txCCount < txTCount) /* on last time, just wait, no data */ + { + pBBLink[link]->l.XycomLink.bbRegs->data = *txMsg; /* send next byte */ + if (bbDebug>30) + printf("xvmeTxTask(%d): outputting %2.2X\n", link, *txMsg); + + /* On 5th byte, we are dun w/header, set start w/data buffer */ + if (txCCount != 4) + txMsg++; + else + txMsg = pnode->txMsg.data; + } + txCCount++; + } + + if (pBBLink[link]->abortFlag == 0) + { + + /* All data bytes have been sent, put on busy list and release */ + + /* Don't add to busy list if was a RAC_RESET_SLAVE */ + if ((pnode->txMsg.cmd == RAC_RESET_SLAVE) && (pnode->txMsg.tasks == 0)) + { /* Finish the transaction here if was a RAC_RESET_SLAVE */ +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX_RESET); +#endif + if (bbDebug) + printf("xvmeTxTask(%d): RAC_RESET_SLAVE sent, resetting node %d\n", link, pnode->txMsg.node); + + pnode->status = BB_OK; + + if (pnode->finishProc != NULL) + { + if (bbDebug>4) + printf("xvmeTxTask(%d): invoking callbackRequest\n", link); + + callbackRequest(pnode); /* schedule completion processing */ + } + else + { + /* If there is a semaphore for synchronous I/O, unlock it */ + if (pnode->psyncSem != NULL) + semGive(*(pnode->psyncSem)); + } + + /* Wait for last NODE_OFFLINE to finish (if still pending) */ + semTake(resetNodeSem, WAIT_FOREVER); + + /* have to reset the master so it won't wait on a response */ + resetNodeData = pnode->txMsg.node; /* mark the node number */ + semTake(pBBLink[link]->queue[BB_Q_HIGH].sem, WAIT_FOREVER); + listAddHead(&(pBBLink[link]->queue[BB_Q_HIGH]), &resetNode); + semGive(pBBLink[link]->queue[BB_Q_HIGH].sem); + + /* taskDelay(15); */ /* wait while bug is resetting */ + } + else + { + /* Lock the busy list */ + semTake(pBBLink[link]->busyList.sem, WAIT_FOREVER); + + /* set the retire time */ + pnode->retire = tickGet(); + if (pnode->ageLimit) + pnode->retire += pnode->ageLimit; + else + pnode->retire += BB_DEFAULT_RETIRE_TIME * sysClkRateGet(); + + if (pBBLink[link]->busyList.head == NULL) + dogStart = 1; + else + dogStart = 0; + + /* Add pnode to the busy list */ + listAddTail(&(pBBLink[link]->busyList), pnode); + + /* Count the outstanding messages */ + (pBBLink[link]->deviceStatus[pnode->txMsg.node])++; + + semGive(pBBLink[link]->busyList.sem); + + /* If just added something to an empty busy list, start the dog */ + if (dogStart) + { + now = tickGet(); + wdStart(pBBLink[link]->watchDogId, pBBLink[link]->busyList.head->retire - now, xvmeTmoHandler, link); + } + } + + /* Tell the 8044 to fire out the message now */ + pBBLink[link]->l.XycomLink.bbRegs->cmnd = BB_SEND_CMD; /* forward it now */ + } + else + { /* Aborted transmission operation, re-queue the message */ + semTake(pBBLink[link]->queue[BB_Q_HIGH].sem, WAIT_FOREVER); + listAddHead(&(pBBLink[link]->queue[BB_Q_HIGH]), pnode); + semGive(pBBLink[link]->queue[BB_Q_HIGH].sem); +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX_ABORT); +#endif + if (bbDebug) + { + printf("Message in progress when abort issued:\n"); + drvBitBusDumpMsg(&pnode->txMsg); + } + } +/* BUG -- I don't really need this */ + /* break;*/ /* stop checking the fifo queues */ + } + else + { /* we have no xacts that can be processed at this time */ + semGive(pBBLink[link]->queue[prio].sem); + } + prio--; /* look at the next prio queue */ + } + } + } +} + +/****************************************************************************** + * + * This gets called by the transmit task if it gets stuck waiting to send a + * byte to the transmit fifo. + * + ******************************************************************************/ +STATIC int txStuck(int link) +{ + if (bbDebug) + printf("bitbus transmitter task stuck, resetting link %d\n", link); + + bbReset(link); + while (pBBLink[link]->abortFlag == 0) + taskDelay(1); + + return(OK); +} + +/****************************************************************************** + * + * This function is called by user programs to queue an I/O transaction request + * for the BB driver. It is the only user-callable function provided in this + * driver. + * + ******************************************************************************/ +STATIC long qBBReq(struct dpvtBitBusHead *pdpvt, int prio) +{ + static linkErrFlags[BB_NUM_LINKS]; /* Supposedly init'd to zero */ + char message[200]; + + if ((prio < 0) || (prio >= BB_NUM_PRIO)) + { + sprintf(message, "invalid priority requested in call to qbbreq(%p, %d)\n", pdpvt, prio); + errMessage(S_BB_badPrio, message); + return(ERROR); + } + if (checkLink(pdpvt->link) == ERROR) + { + if (pdpvt->link >= BB_NUM_LINKS) + { + sprintf(message, "qbbreq(%p, %d) %d\n", pdpvt, prio, pdpvt->link); + errMessage(S_BB_badlink, message); + } + else if (linkErrFlags[pdpvt->link] == 0) + { /* Anti-message swamping check */ + linkErrFlags[pdpvt->link] = 1; + sprintf(message, "qbbreq(%p, %d) %d... card not present\n", pdpvt, prio, pdpvt->link); + errMessage(S_BB_badlink, message); + } + return(ERROR); + } + +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(pdpvt->link, pdpvt, XACT_HIST_STATE_QUEUE); +#endif + + if (bbDebug>5) + printf("qbbreq(%p, %d): transaction queued\n", pdpvt, prio); + if (bbDebug>6) + drvBitBusDumpMsg(&(pdpvt->txMsg)); + + semTake(pBBLink[pdpvt->link]->queue[prio].sem, WAIT_FOREVER); + + /* Add to the end of the queue of waiting transactions */ + listAddTail(&(pBBLink[pdpvt->link]->queue[prio]), pdpvt); + + semGive(pBBLink[pdpvt->link]->queue[prio].sem); + + semGive(pBBLink[pdpvt->link]->linkEventSem); + + return(OK); +} + + +/****************************************************************** + * + * Reset hook function... takes down ALL the bitbus links and leaves + * them down. + * + ******************************************************************/ +STATIC void BBrebootFunc(void) +{ + int i; + + for (i=0; inukeEm = 2; + semGive(pBBLink[link]->watchDogSem); + } + else + printf("Link %d not installed.\n", link); + + return(0); +} + + + /****************************************************************** + * + * Super debugging facility. + * + ******************************************************************/ +int +drvBitBusDumpMsg(struct bitBusMsg *pbbMsg) +{ + char ascBuf[15]; /* for ascii xlation part of the dump */ + int x; + int y; + int z; + + printf("Link 0x%4.4X, length 0x%2.2X, route 0x%2.2X, node 0x%2.2X, tasks 0x%2.2X, cmd %2.2X\n", pbbMsg->link, pbbMsg->length, pbbMsg->route, pbbMsg->node, pbbMsg->tasks, pbbMsg->cmd); + + x = BB_MSG_HEADER_SIZE; + y = pbbMsg->length; + z = 0; + + while (x < y) + { + printf("%2.2X ", pbbMsg->data[z]); + ascBuf[z] = pbbMsg->data[z]; + + if (!((ascBuf[z] >= 0x20) && (ascBuf[z] <= 0x7e))) + ascBuf[z] = '.'; + + x++; + z++; + } + + while (x < BB_MAX_MSG_LENGTH) + { + printf(" "); + x++; + } + + ascBuf[z] = '\0'; + printf(" *%s*\n", ascBuf); + return(OK); +} + +#ifdef BB_SUPER_DEBUG +/* + * Place an event into the history buffer. + */ +static int BBSetHistEvent(int link, struct dpvtBitBusHead *pXact, int State) +{ + semTake(pBBLink[link]->History.sem, WAIT_FOREVER); + + + pBBLink[link]->History.Num++; + + pBBLink[link]->History.Xact[pBBLink[link]->History.Next].Time = tickGet(); + pBBLink[link]->History.Xact[pBBLink[link]->History.Next].State = State; + if (pXact != NULL) + memcpy (&pBBLink[link]->History.Xact[pBBLink[link]->History.Next].Xact, pXact, sizeof(struct dpvtBitBusHead)); + + if (link==bbDebugLink && pBBLink[link]->History.Xact[pBBLink[link]->History.Next].Xact.txMsg.node==bbDebugNode) + BBDumpXactHistory(&pBBLink[link]->History.Xact[pBBLink[link]->History.Next]); + + if (++pBBLink[link]->History.Next == BB_SUPER_DEBUG_HIST_SIZ) + pBBLink[link]->History.Next = 0; + + semGive(pBBLink[link]->History.sem); + return(0); +} + +int BBHistDump(int link) +{ + int count; + int ix; + + if (checkLink(link) == ERROR) + { + printf(" Link %d is not valid\n", link); + return(-1); + } + printf("Dumping bitbus history for link %d\n", link); + semTake(pBBLink[link]->History.sem, WAIT_FOREVER); + + if (pBBLink[link]->History.Num < BB_SUPER_DEBUG_HIST_SIZ) + { + count = pBBLink[link]->History.Num; + ix = 0; + } + else + { + count = BB_SUPER_DEBUG_HIST_SIZ; + ix = pBBLink[link]->History.Next; + } + + while(count) + { + printf("%8.8d:", pBBLink[link]->History.Xact[ix].Time); + + if (BBDumpXactHistory(&pBBLink[link]->History.Xact[ix]) != 0) + count = 0; + else + --count; + + if (++ix >= BB_SUPER_DEBUG_HIST_SIZ) + ix = 0; + } + + semGive(pBBLink[link]->History.sem); + return(0); +} +STATIC int BBDumpXactHistory(XactHistStruct *pXact) +{ + switch (pXact->State) + { + /* No BB message present */ + case XACT_HIST_STATE_RESET: + printf("Link Reset:\n"); + break; + case XACT_HIST_STATE_RX_ABORT: + printf("Link RX aborted\n"); + break; + case XACT_HIST_STATE_TX_ABORT: + printf("Link TX aborted\n"); + break; + case XACT_HIST_STATE_RX_URCMD: + printf("Link RX got unexpected RCMD\n"); + break; + + /* TX message only */ + case XACT_HIST_STATE_QUEUE: + printf("Message Queued:\n"); + goto do_tx_only; + case XACT_HIST_STATE_TX: + printf("Message Transmitted:\n"); + goto do_tx_only; + case XACT_HIST_STATE_WD_TIMEOUT: + printf("Watchdog Timeout:\n"); + goto do_tx_only; + case XACT_HIST_STATE_TX_STUCK: + printf("Transmitter stuck:\n"); + goto do_tx_only; + case XACT_HIST_STATE_TX_RESET: + printf("TX just sent a RAC RESET:\n"); + + do_tx_only: + drvBitBusDumpMsg(&pXact->Xact.txMsg); + break; + + /* RX message only */ + case XACT_HIST_STATE_RX_UNSOLICITED: + printf("RX unsolicited message:\n"); + drvBitBusDumpMsg(&pXact->Xact.rxMsg); + break; + + /* TX and RX messages present */ + case XACT_HIST_STATE_RX: + printf("RX response... complete transaction:\n"); + drvBitBusDumpMsg(&pXact->Xact.txMsg); + drvBitBusDumpMsg(&pXact->Xact.rxMsg); + break; + + default: + printf("Corrupt bitbus debug state!\n"); + return(-1); + } + return(0); +} +#endif + +/****************************************************************************** + * + * A user callable function that may be used to reset the specified slave + * node. + * + * This routine is NOT reenterant but contains a monitor so that it always + * operates properly. + * + ******************************************************************************/ +int ResetBug(int node, int link) +{ + static int FirstTime = 1; + static SEM_ID monLock; + static SEM_ID syncSem; /* Semaphore to use for completion */ + struct dpvtBitBusHead resetNode; /* actual message to send out */ + unsigned char resetNodeData; /* 1-byte data field for RAC_RESET */ + + if (FirstTime) + { + monLock = semBCreate(SEM_Q_PRIORITY, SEM_FULL); + syncSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + FirstTime = 0; + } + + semTake(monLock, WAIT_FOREVER); + + resetNode.finishProc = NULL; /* no callback routine used */ + resetNode.psyncSem = &syncSem;/* do a semGive on this SEM when done sending */ + resetNode.link = link; /* which bitbus link to send message out on */ + resetNode.rxMaxLen = 7; /* Chop off the response... we don't care */ + resetNode.ageLimit = sysClkRateGet()*2; + + resetNode.txMsg.length = 8; + resetNode.txMsg.route = BB_STANDARD_TX_ROUTE; + resetNode.txMsg.node = node; + resetNode.txMsg.tasks = 0x0; + resetNode.txMsg.cmd = RAC_RESET_SLAVE; + resetNode.txMsg.data = &resetNodeData; + + qBBReq(&resetNode, 0); + semTake(syncSem, WAIT_FOREVER); + semGive(monLock); + + return(0); +} + /****************************************************************** + * FUNCTION: pepRxTask() + * PURPOSE : This function is started as a task during driver init + * time. It's purpose is to read messages off the receive + * FIFO. After a complete message is read, the soliciting + * message's completion routines are started by the use + * of semaphores and/or a callbackRequest(). + * ARGS IN : link + * ARGS OUT: none + * GLOBALS: none + ******************************************************************/ +STATIC int +pepRxTask(int link) +{ + int rxState; /* current state of the receiver */ +#define BBRX_HEAD 1 +#define BBRX_DATA 2 +#define BBRX_RCMD 3 +#define BBRX_IGN 4 + + unsigned char rxHead[7]; /* room for header of current rx msg */ + unsigned char *rxMsg; /* where to place next byte (after header) */ + int rxTCount; /* byte counter for data in rxHead */ + unsigned char ch; + struct dpvtBitBusHead *rxDpvtHead; /* for message currently receiving */ + + struct dpvtBitBusHead UselessMsg; /* to hold unsolicited responses */ + unsigned char UselessData[BB_MAX_MSG_LENGTH]; + + int lockKey; /* used for intLock calls */ + int packageComplete; /* indicates end of package read */ + + rxMsg = (unsigned char *) NULL; + rxState = BBRX_HEAD; + rxTCount = 0; + rxDpvtHead = (struct dpvtBitBusHead *) NULL; + packageComplete = 0; + + /* Dummy up the UselessMsg fields so we can use it */ + UselessMsg.rxMaxLen = BB_MAX_MSG_LENGTH; + UselessMsg.rxMsg.data = UselessData; + UselessMsg.link = link; + UselessMsg.ageLimit = 0; + UselessMsg.retire = 0; + + while (1) + { + /* Wait for RX interrupts, but only if no chars are ready */ + if ((pBBLink[link]->l.PepLink.bbRegs->stat_ctl & PEP_BB_RFNE) == 0) + { + /* Enable interrupts and check again in case PB-BIT blew it */ + lockKey = intLock(); + pBBLink[link]->l.PepLink.bbRegs->int_vec = PEP_BB_IVEC_BASE +(link*2); + intUnlock(lockKey); + + while (((pBBLink[link]->l.PepLink.bbRegs->stat_ctl & PEP_BB_RFNE) == 0) + && (pBBLink[link]->abortFlag == 0)) + { + /* Re-enable ints here each time in case board got reset */ + lockKey = intLock(); + pBBLink[link]->l.PepLink.bbRegs->int_vec = PEP_BB_IVEC_BASE +(link*2); + intUnlock(lockKey); + + if (bbDebug>35) + printf("pepRxTask(%d): waiting on IRQ\n", link); + + /* wait for data bytes */ + semTake(pBBLink[link]->l.PepLink.rxInt, WAIT_FOREVER); + } + /* Disable RX Interrupts (prevents unnecessary context switching) */ + lockKey = intLock(); + pBBLink[link]->l.PepLink.bbRegs->int_vec = 0; + intUnlock(lockKey); + } + + if (pBBLink[link]->abortFlag == 0) + { + /* READ ONE CHAR FROM RECEIVE FIFO */ + ch = pBBLink[link]->l.PepLink.bbRegs->data; + + /* check to see if we got a data byte or a command byte */ + if ((pBBLink[link]->l.PepLink.bbRegs->stat_ctl & PEP_BB_RCMD) == PEP_BB_RCMD) + packageComplete = 1; + + switch (rxState) { + case BBRX_HEAD: /* getting the head of a new message */ + if (rxTCount > 1) /* Toss the 2 PEP specific header bytes */ + rxHead[rxTCount] = ch; + if (bbDebug>21) + printf("pepRxTask(%d): >%2.2X< (Header)\n", link, ch); + + if (++rxTCount == 7) + { + /* find the message this is a reply to */ + /* rxTCount += 2; PEP header bytes already messed up the count (jrw)*/ + + /* Lock the busy list */ + semTake(pBBLink[link]->busyList.sem, WAIT_FOREVER); + + rxDpvtHead = pBBLink[link]->busyList.head; + while (rxDpvtHead != NULL) { + if (bbDebug>19) + printf("pepRxTask(%d): checking reply against %p\n", + link, rxDpvtHead); + + /* see if node's match */ + if (rxDpvtHead->txMsg.node == rxHead[4]) { + /* see if the tasks match */ + /* if (rxDpvtHead->txMsg.tasks == rxHead[5]) */ + { + /* They match, finish putting response into the rxMsg buffer */ + if (bbDebug>4) + printf("pepRxTask(%d): reply to %p\n", + link, rxDpvtHead); + + /* Delete the node from the list */ + listDel(&(pBBLink[link]->busyList), rxDpvtHead); + + /* If busy list is empty, stop the dog */ + if (pBBLink[link]->busyList.head == NULL) + wdCancel(pBBLink[link]->watchDogId); + + if (rxHead[6] == 0x91) + { /* something bad happened... inject a delay to the */ + /* requested timeout duration. */ + if (bbDebug) + printf("pepRxTask(%d): 0x91 from node %d, invoking synthetic delay\n", link, rxHead[4]); + (pBBLink[link]->syntheticDelay[rxDpvtHead->txMsg.node]) = rxDpvtHead->retire; + pBBLink[link]->DelayCount++; + } + else + { /* decrement the number of outstanding messages to the node */ + (pBBLink[link]->deviceStatus[rxDpvtHead->txMsg.node])--; + } + + /* Wake up Link Task in case was waiting on "this" node */ + semGive(pBBLink[link]->linkEventSem); +#if 0 + semGive(pXvmeLink[link]->pbbLink->busyList.sem); + + rxDpvtHead->rxMsg.length = rxHead[2]; + rxDpvtHead->rxMsg.route = rxHead[3]; + rxDpvtHead->rxMsg.node = rxHead[4]; + rxDpvtHead->rxMsg.tasks = rxHead[5]; + rxDpvtHead->rxMsg.cmd = rxHead[6]; + rxMsg = rxDpvtHead->rxMsg.data; + + rxDpvtHead->status = BB_OK; /* OK, unless BB_LENGTH */ + rxState = BBRX_DATA; /* finish reading till RCMD */ +#endif + break; /* get out of the while() */ + } + } + rxDpvtHead = rxDpvtHead->next; /* Keep looking */ + } + + semGive(pBBLink[link]->busyList.sem); + + /* Couldn't find a match... fake one so can print the bad message */ + if (rxDpvtHead == NULL) + rxDpvtHead = &UselessMsg; + + rxDpvtHead->rxMsg.length = rxHead[2]; + rxDpvtHead->rxMsg.route = rxHead[3]; + rxDpvtHead->rxMsg.node = rxHead[4]; + rxDpvtHead->rxMsg.tasks = rxHead[5]; + rxDpvtHead->rxMsg.cmd = rxHead[6]; + rxMsg = rxDpvtHead->rxMsg.data; + + rxDpvtHead->status = BB_OK; /* OK, unless BB_LENGTH */ + rxState = BBRX_DATA; /* finish reading till RCMD */ + } + break; + + case BBRX_DATA: /* finish reading data portion of message */ + if (rxTCount >= rxDpvtHead->rxMaxLen) { + rxState = BBRX_IGN; /* toss the rest of the data */ + rxDpvtHead->status = BB_LENGTH; /* set driver status */ + if (bbDebug>22) + printf("pepRxTask(%d): %2.2X (Ignored)\n", link, ch); + + } + else { + *rxMsg = ch; + if (bbDebug>22) + printf("pepRxTask(%d): %2.2X (Data)\n", link, ch); + rxMsg++; + rxTCount++; + } + break; + + case BBRX_IGN: + if (bbDebug>22) + printf("pepRxTask(%d): %2.2X (Ignored)\n", link, ch); + break; + } + + if (packageComplete) + { + if (rxDpvtHead == NULL) + { + if (bbDebug > 22) + printf("pepRxTask(%d): got unexpected completion status\n", link); +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, NULL, XACT_HIST_STATE_RX_URCMD); +#endif + } + else if (rxDpvtHead == &UselessMsg) + { + rxDpvtHead->status = BB_OK; + if (bbDebug) + { + printf("pepRxTask(%d): msg from node %d unsolicited:\n", link, rxDpvtHead->rxMsg.node); + drvBitBusDumpMsg(&(rxDpvtHead->rxMsg)); + } +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, rxDpvtHead, XACT_HIST_STATE_RX_UNSOLICITED); +#endif + } + else + { + rxDpvtHead->status = BB_OK; + if (bbDebug>24) + printf("pepRxTask(%d): RX command byte = %2.2X\n", link, ch); + +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, rxDpvtHead, XACT_HIST_STATE_RX); +#endif + + if (rxDpvtHead->finishProc != NULL) + { + if (bbDebug>8) + printf("pepRxTask(%d): invoking the callbackRequest\n", link); + callbackRequest(rxDpvtHead); /* schedule completion processing */ + } + else + { + /* If there is a semaphore for synchronous I/O, unlock it */ + if (rxDpvtHead->psyncSem != NULL) + semGive(*(rxDpvtHead->psyncSem)); + } + } + /* Reset the state of the RxTask to expect a new message */ + rxMsg = (unsigned char *) NULL; + rxState = BBRX_HEAD; + rxTCount = 0; + rxDpvtHead = (struct dpvtBitBusHead *) NULL; + packageComplete = 0; + } + } + else + { + /* Link abort state is active reset receiver link status now */ + if (rxDpvtHead != NULL) + { + /* This xact is not on the busy list, put it back on */ + semTake(pBBLink[link]->busyList.sem, WAIT_FOREVER); + (pBBLink[link]->deviceStatus[rxDpvtHead->txMsg.node])++; + listAddTail(&(pBBLink[link]->busyList), rxDpvtHead); + semGive(pBBLink[link]->busyList.sem); + } +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, rxDpvtHead, XACT_HIST_STATE_RX_ABORT); +#endif + + rxMsg = (unsigned char *) NULL; + rxState = BBRX_HEAD; + rxTCount = 0; + rxDpvtHead = (struct dpvtBitBusHead *) NULL; + + /* Tell the watch dog I am ready for the reset (reset in the dog task) */ + pBBLink[link]->rxAbortAck = 1; + + if (bbDebug) + printf("pepRxTask(%d): resetting due to abort status\n", link); + + /* wait for link state to become active again */ + while (pBBLink[link]->abortFlag != 0) + taskDelay(RESET_POLL_TIME); + + if (bbDebug) + printf("pepRxTask(%d): restarting after abort\n", link); + } + } +} + +/****************************************************************** + * FUNCTION: pepWdTask() + * PURPOSE : + * + * ARGS IN : none + * ARGS OUT: none + * GLOBALS: none + ******************************************************************/ +STATIC int pepWdTask(int link) +{ + struct dpvtBitBusHead *pnode; + struct dpvtBitBusHead *npnode; + unsigned long now; + +#ifdef PEP_DO_RESET_AND_OFFLINE + SEM_ID syncSem; + + struct dpvtBitBusHead resetNode; + unsigned char resetNodeData; /* 1-byte data field for RAC_RESET */ + unsigned char responseData; + + struct dpvtBitBusHead offlnNode; + unsigned char offlnNodeData; /* 1-byte data field for RAC_OFFLN */ + unsigned char response1Data; /* 1-byte response data field */ + + /* init the SEM used when sending the RAC_OFFLNE message */ + syncSem = semBCreate(SEM_Q_PRIORITY,SEM_EMPTY); + + /* + * Hand-craft a RAC_RESET and RAC_OFFLINE message to use when + * a message times out. + * NOTE that having only one copy is OK provided that the dog waits for + * a response before sending it again! + */ + resetNode.finishProc = NULL; /* no callback routine used */ + resetNode.psyncSem = NULL; + resetNode.link = link; /* which bitbus link to send message out on */ + resetNode.rxMaxLen = 10; + resetNode.ageLimit = sysClkRateGet()*100; /* make sure this never times out */ + + resetNode.txMsg.length = 8; + resetNode.txMsg.route = BB_STANDARD_TX_ROUTE; + resetNode.txMsg.tasks = 0x0; + resetNode.txMsg.data = &resetNodeData; + resetNode.rxMsg.data = &responseData; + + offlnNode.finishProc = NULL; /* no callback routine used */ + offlnNode.psyncSem = &syncSem;/* do a semGive on this SEM when responded to*/ + offlnNode.link = link; /* which bitbus link to send message out on */ + offlnNode.rxMaxLen = 10; + offlnNode.ageLimit = sysClkRateGet()*100; /* make sure this never times out */ + + offlnNode.txMsg.length = 8; + offlnNode.txMsg.route = BB_STANDARD_TX_ROUTE; + offlnNode.txMsg.tasks = 0x0; + offlnNode.txMsg.data = &offlnNodeData; + offlnNode.rxMsg.data = &response1Data; +#endif + + pBBLink[link]->nukeEm = 0; /* Make sure the nuke status is clear */ + while(1) { + /* SLEEP UNTIL WATCHDOG TIMER ROUTINE GIVES SEMAPHORE */ + semTake(pBBLink[link]->watchDogSem, WAIT_FOREVER); + now = tickGet(); /* what time is it? */ + + if (pBBLink[link]->nukeEm != 0) + printf("Bitbus manual reset being issued on link %d\n", link); + + if (bbDebug>4) + printf("pepWdTask(%d): (Watchdog) checking busy list\n", link); + + pBBLink[link]->rxAbortAck = 0; + pBBLink[link]->txAbortAck = 0; + + if (pBBLink[link]->nukeEm != 0) { + /* set abort status and wait for the abort acks */ + pBBLink[link]->abortFlag = 1; + + /* wake up the Tx task so it can observe the abort status */ + semGive(pBBLink[link]->linkEventSem); + + /* wake up the Rx task so it can observe the abort status */ + semGive(pBBLink[link]->l.PepLink.rxInt); + + /* sleep until abort ack from Tx & Rx tasks */ + while ((pBBLink[link]->rxAbortAck == 0) && + (pBBLink[link]->txAbortAck == 0)) + taskDelay(RESET_POLL_TIME); + +#ifdef BB_SUPER_DEBUG + if (pBBLink[link]->nukeEm < 2) + { /* Do a history dump since this is not supposed to happen */ + BBHistDump(link); + } +#endif + } + /* + * Run thru entire busy list to see if there are any transactions + * that have been waiting on a response for too long a period. + */ + semTake(pBBLink[link]->busyList.sem, WAIT_FOREVER); + pnode = pBBLink[link]->busyList.head; + + while (pnode != NULL) { + npnode = pnode->next; /* remember where we were in the list */ + + if ((pBBLink[link]->nukeEm != 0) || (pnode->retire <= now)) { + /* Get rid of the request and set error status etc... */ + listDel(&(pBBLink[link]->busyList), pnode); + +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, pnode, XACT_HIST_STATE_WD_TIMEOUT); +#endif + if (bbDebug) + { + printf("pepWdTask(%d): TIMEOUT on bitbus message:\n", link); + drvBitBusDumpMsg(&pnode->txMsg); + } + + /* BUG -- do this here or defer until RX gets a response? */ + (pBBLink[link]->deviceStatus[pnode->txMsg.node])--; /* fix device status */ + pnode->status = BB_TIMEOUT; + +#ifdef PEP_DO_RESET_AND_OFFLINE + /* Gotta do this now in case we need the info after the completion */ + resetNode.txMsg.node = pnode->txMsg.node; + offlnNodeData = pnode->txMsg.node; /* mark the node number */ +#endif + /* Make the callbackRequest if one was spec'd */ + if(pnode->finishProc != NULL) + { + if (bbDebug>2) + { + printf("pepWdTask(%d): invoking the callbackRequest %p %d\n", link, pnode->finishProc, pnode->priority); + } + callbackRequest(pnode); /* schedule completion processing */ + } + else + { + /* Release a completion lock if one was spec'd */ + if (pnode->psyncSem != NULL && pBBLink[link]->nukeEm == 0) + semGive(*(pnode->psyncSem)); + } + +#ifdef PEP_DO_RESET_AND_OFFLINE + /* If we are not going to reboot the link... */ + if ( pBBLink[link]->nukeEm == 0 ) { + /* Send out a RAC_RESET/RAC_OFFLINE pair */ + semGive(pBBLink[link]->busyList.sem); + + /* Configure message for a RAC_RESET */ + resetNode.txMsg.cmd = 0x00; + + /* Configure message for a RAC_OFFLINE */ + offlnNode.txMsg.cmd = RAC_OFFLINE; + offlnNode.txMsg.node = 0xff; + + /* Queue the messages (high priority, reset first) */ + + semTake(pBBLink[link]->queue[BB_Q_HIGH].sem, WAIT_FOREVER); + listAddHead(&(pBBLink[link]->queue[BB_Q_HIGH]), &offlnNode); +/* listAddHead(&(pBBLink[link]->queue[BB_Q_HIGH]), &resetNode); */ + semGive(pBBLink[link]->queue[BB_Q_HIGH].sem); + + semGive(pBBLink[link]->linkEventSem); /* Tell TxTask to send the messages */ + + if (semTake(syncSem, (sysClkRateGet()) ) == ERROR) + { + if (bbDebug) + printf("pepWdTask(%d): link dead, trying manual reboot\n", link); + + pBBLink[link]->nukeEm = 1; + pBBLink[link]->abortFlag = 1; + semGive(pBBLink[link]->linkEventSem); + semGive(pBBLink[link]->rxInt); + + /* sleep until abort ack from Tx & Rx tasks */ + while ((pBBLink[link]->rxAbortAck == 0) && + (pBBLink[link]->txAbortAck == 0)) + taskDelay(RESET_POLL_TIME); + } + + /* Start over since released the busy list */ + semTake(pBBLink[link]->busyList.sem, WAIT_FOREVER); + npnode = pBBLink[link]->busyList.head; + } +#endif + + } + pnode = npnode; + } + + /* Finish the link reboot if necessary */ + if (pBBLink[link]->nukeEm != 0) + { + /* shut down the bitbus card. */ + pepReset(link); + + if (pBBLink[link]->nukeEm == 2) + { + /* + * Stop the watchdog task so the link stays dead. + * Since the busy list is locked and the nukeEm flag is still set, + * this link can not do any more work. + */ + abort(); + } + + /* clear the abort_flag */ + pBBLink[link]->abortFlag = 0; + + pBBLink[link]->nukeEm = 0; + } + + if (pBBLink[link]->busyList.head != NULL) { + /* Restart the dog timer */ + if (bbDebug>5) + printf("pepWdTask(%d): restarting watch dog timer\n", link); + + wdStart(pBBLink[link]->watchDogId, pBBLink[link]->busyList.head->retire - now, + pepTmoHandler, link); + } + + semGive(pBBLink[link]->busyList.sem); + + /* In case the TX is waiting on a node that just timed out */ + semGive(pBBLink[link]->linkEventSem); + } +} + +/****************************************************************** + * FUNCTION: pepTxTask() + * PURPOSE : This function is started as a task during driver init + * time. It's purpose is to keep the link busy with + * outgoing messages. It awaits user-calls to qBBReq() + * (new message to send). It also awaits wake up calls + * from the Wd timer (a timed out message to a node, + * once handled, means Tx is free to send again to that + * node). + * + * ARGS IN : link + * ARGS OUT: none + * GLOBALS: none + ******************************************************************/ +STATIC int pepTxTask(int link) +{ + struct dpvtBitBusHead *pnode; + int prio; + int working; + int dogStart; + int stuck; + + int txTCount; + int txCCount; + unsigned char *txMsg; + register int x; + unsigned long now; + + if (bbDebug>1) + printf("pepTxTask started for link %d\n", link); + + while(1) { + if (pBBLink[link]->abortFlag != 0) { + /* let the dog know we are ready */ + pBBLink[link]->txAbortAck = 1; + +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, NULL, XACT_HIST_STATE_TX_ABORT); +#endif + + if (bbDebug) + printf("pepTxTask(%d): resetting due to abort status\n", link); + + while (pBBLink[link]->abortFlag != 0) + taskDelay(RESET_POLL_TIME); /* wait for link to reset */ + + if (bbDebug) + printf("pepTxTask(%d): restarting after abort\n", link); + } + else + { + if (pBBLink[link]->DelayCount) + semTake(pBBLink[link]->linkEventSem, 2 * sysClkRateGet()); + else + semTake(pBBLink[link]->linkEventSem, WAIT_FOREVER); + } + + if (bbDebug>5) + printf("pepTxTask(%d): got an event\n", link); + + working = 1; + while ((working != 0) && (pBBLink[link]->abortFlag == 0)) { + working = 0; + + prio = BB_NUM_PRIO-1; + while ((prio >= 0) && (pBBLink[link]->abortFlag == 0)) { + /* see if the queue has anything in it */ + semTake(pBBLink[link]->queue[prio].sem, WAIT_FOREVER); + + if ((pnode = pBBLink[link]->queue[prio].head) != NULL) + { + now = tickGet(); + while (pBBLink[link]->deviceStatus[pnode->txMsg.node] == BB_BUSY) + { + if ((pBBLink[link]->syntheticDelay[pnode->txMsg.node] != 0) + && (pBBLink[link]->syntheticDelay[pnode->txMsg.node] < now)) + { + if (bbDebug) + printf("pepTxTask(%d): terminating synthetic idle on node %d\n", link, pnode->txMsg.node); + (pBBLink[link]->deviceStatus[pnode->txMsg.node])--; + pBBLink[link]->syntheticDelay[pnode->txMsg.node] = 0; + pBBLink[link]->DelayCount--; + } + else + { + if ((pnode = pnode->next) == NULL) + break; + } + } + } + + if (pnode != NULL) { /* have an xact to start processing */ + working = 1; + + /* delete the node from the inbound fifo queue */ + listDel(&(pBBLink[link]->queue[prio]), pnode); + + semGive(pBBLink[link]->queue[prio].sem); +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX); +#endif + + + if (bbDebug>3) + printf("pepTxTask(%d): got xact, pnode=%p\n", link, pnode); + + /* Send the message in polled mode */ + txTCount = pnode->txMsg.length; + + /* BUG -- would be nice if we verify the length >6 here */ + + txCCount = 0; + txMsg = &(pnode->txMsg.length); + + while ((txCCount < txTCount -1) && (pBBLink[link]->abortFlag == 0)) { + + stuck = 1000; + while (((pBBLink[link]->l.PepLink.bbRegs->stat_ctl & PEP_BB_TFNF) + != PEP_BB_TFNF) && + (pBBLink[link]->abortFlag == 0) && + --stuck) + for(x=0;x<100;x++); /* wait for TX ready */ + + if (!stuck) + { +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX_STUCK); +#endif + txStuck(link); + } + else if (txCCount == 0) { /* first byte is package type */ + if (bbDebug>30) + printf("pepTxTask(%d): outputting %2.2X\n",link,0x81); + pBBLink[link]->l.PepLink.bbRegs->data = 0x81; + } + else if (txCCount == 1) { /* unused 2nd byte of package */ + if (bbDebug>30) + printf("pepTxTask(%d): outputting %2.2X\n",link,0x00); + pBBLink[link]->l.PepLink.bbRegs->data = 0x00; +#if 0 + } else if (txCCount == (txTCount -1)) { /* last byte of package */ + pBBLink[link]->l.PepLink.bbRegs->stat_ctl = *txMsg; + if (bbDebug>30) + printf("pepTxTask(%d): outputting last byte %2.2X\n", + link,*txMsg); +#endif + } else { /* regular ol' message byte */ + pBBLink[link]->l.PepLink.bbRegs->data = *txMsg; + if (bbDebug>30) + printf("pepTxTask(%d): outputting %2.2X\n",link,*txMsg); + if (txCCount != 6) + txMsg++; + else + txMsg = pnode->txMsg.data; + } + txCCount++; + } + + if (pBBLink[link]->abortFlag == 0) { + + /* don't add to busy list if was a RAC_RESET_SLAVE */ + if ((pnode->txMsg.cmd == RAC_RESET_SLAVE) && (pnode->txMsg.tasks == 0)) + { +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX_RESET); +#endif + if (bbDebug) + printf("pepTxTask(%d): RAC_RESET_SLAVE sent\n", link); + + pnode->status = BB_OK; + + if (pnode->finishProc != NULL) { + if (bbDebug>4) + printf("pepTxTask(%d): invoking the callbackRequest\n", + link); + callbackRequest(pnode); /* schedule completion processing */ + } + else + { + /* If there is a semaphore for synchronous I/O, unlock it */ + if (pnode->psyncSem != NULL) + semGive(*(pnode->psyncSem)); + } + taskDelay(15); + } + else + { + /* Lock the busy list */ + semTake(pBBLink[link]->busyList.sem, WAIT_FOREVER); + + pBBLink[link]->l.PepLink.bbRegs->stat_ctl = *txMsg; + if (bbDebug>30) + printf("pepTxTask(%d): outputting last byte %2.2X\n", + link,*txMsg); + + /* set the retire time */ + pnode->retire = tickGet(); + if (pnode->ageLimit) + pnode->retire += pnode->ageLimit; + else + pnode->retire += BB_DEFAULT_RETIRE_TIME * sysClkRateGet(); + + if (pBBLink[link]->busyList.head == NULL) + dogStart = 1; + else + dogStart = 0; + + /* Add pnode to the busy list */ + listAddTail(&(pBBLink[link]->busyList), pnode); + + /* Count the outstanding messages */ + (pBBLink[link]->deviceStatus[pnode->txMsg.node])++; + + semGive(pBBLink[link]->busyList.sem); + + /* If something just added to empty busy list, start the dog */ + if (dogStart) { + now = tickGet(); + wdStart(pBBLink[link]->watchDogId, + pBBLink[link]->busyList.head->retire - now, + pepTmoHandler, link); + } + } + } else { /* if abortFlag != 0 */ + /* Aborted transmission operation, re-queue the message */ + semTake(pBBLink[link]->queue[BB_Q_HIGH].sem, WAIT_FOREVER); + listAddHead(&(pBBLink[link]->queue[BB_Q_HIGH]), pnode); + semGive(pBBLink[link]->queue[BB_Q_HIGH].sem); +#ifdef BB_SUPER_DEBUG + BBSetHistEvent(link, pnode, XACT_HIST_STATE_TX_ABORT); +#endif + } + + } else { /* if pnode == NULL */ + /* we have no xacts that can be processed at this time */ + semGive(pBBLink[link]->queue[prio].sem); + } + prio--; /* look at the next prio queue */ + } + } + } +} + /****************************************************************** + * FUNCTION: dumpStat() + * PURPOSE : Temporary function. Shows contents of status + * register on PB-BIT module corresponding to link. + * ARGS IN : link + * ARGS OUT: none + * GLOBALS: none + ******************************************************************/ +int pepDumpStat(int link) +{ + unsigned char stat_ctl; + + stat_ctl = pBBLink[link]->l.PepLink.bbRegs->stat_ctl; + + printf("stat_ctl reg: %2X\n",stat_ctl); + return(OK); +} diff --git a/src/drv/old/drvBitBus.h b/src/drv/old/drvBitBus.h new file mode 100644 index 000000000..17e0f0667 --- /dev/null +++ b/src/drv/old/drvBitBus.h @@ -0,0 +1,306 @@ +#ifndef EPICS_DRVBITBUS_H +#define EPICS_DRVBITBUS_H + +/* #define BB_SUPER_DEBUG */ + +/* + * Author: John Winans + * Date: 09-10-91 + * BitBus driver + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 09-30-91 jrw Written + * .02 12-10-91 jrw moved some stuff over to drvBitBusInterface.h + * .03 07-01-94 jrw Bigtime hacking... merged PEP and Xycom. + * + * $Log$ + * Revision 1.9 1994/11/17 21:15:26 winans + * Turned off the debugging system (it consumed an additional 30% of CPU) + * + * Revision 1.8 1994/10/04 18:42:44 winans + * Added an extensive debugging facility. + * + * + */ + +/***************************************************************************** + * + * This history stuff below is used to save the recent history of operation. + * This history includes dumps of messages that are queued, transmitted and + * received. This information is saved along with the relative tick time. + * + *****************************************************************************/ +#ifdef BB_SUPER_DEBUG +#define BB_SUPER_DEBUG_HIST_SIZ 50 /* How many messages to save */ + +typedef struct XactHistStruct +{ + struct dpvtBitBusHead Xact; /* BB message operated on */ + unsigned long Time; /* getTick() when operated on */ + int State; /* What was being done to the message */ +} XactHistStruct; + +#define XACT_HIST_STATE_QUEUE 0 /* Some task queued a transaction */ +#define XACT_HIST_STATE_TX 1 /* Transmitter sent a transaction */ +#define XACT_HIST_STATE_RX 2 /* Receiver received a response */ +#define XACT_HIST_STATE_WD_TIMEOUT 3 /* Watchdog times out a transaction */ +#define XACT_HIST_STATE_RESET 4 /* No message... link was reset */ +#define XACT_HIST_STATE_RX_UNSOLICITED 5 /* RX'd message not solicited */ +#define XACT_HIST_STATE_RX_ABORT 6 /* RX task got a link about status */ +#define XACT_HIST_STATE_TX_ABORT 7 /* TX task got a link about status */ +#define XACT_HIST_STATE_RX_URCMD 8 /* RX task got unsolicited RCMD */ +#define XACT_HIST_STATE_TX_RESET 9 /* TX task got a link about status */ +#define XACT_HIST_STATE_TX_STUCK 10 /* TX fifo is stuck on the 8044 */ + +/* One of these is allocated for each bitbus link */ +typedef struct HistoryStruct +{ + SEM_ID sem; /* Use when 'making' history */ + XactHistStruct Xact[BB_SUPER_DEBUG_HIST_SIZ]; + int Next; /* Next history slot to use */ + unsigned long Num; /* Total history messages */ +} HistoryStruct; + +static int BBSetHistEvent(int link, struct dpvtBitBusHead *pXact, int State); +#endif + + +/****************************************************************************** + * + * The BUSY/IDLE notion is used to count the number of outstanding + * messages for a specific node. The idea is that up to BUSY messages + * can be sent to a node before waiting before deciding not to send any more. + * According to the BitBus specs, this value is 7. However, it also + * states that responses can come back out of order. If this is even true + * for messages sent to the SAME TASK ON THE SAME NODE, the received messages + * CAN NOT be routed back to their initiators properly. Because the node# + * and task# is all we have to identify what a response message is for, + * I am limiting the per-node maximum to 1. + * + ******************************************************************************/ + +#define BB_BUSY 1 /* deviceStatus value if device is currently busy */ +#define BB_IDLE 0 /* deviceStatus value if device is currently idle */ + +/****************************************************************************** + * + * This list structure is used in the bitbus driver to represent its queues. + * + ******************************************************************************/ +struct bbList { + struct dpvtBitBusHead *head; /* head of the linked list */ + struct dpvtBitBusHead *tail; /* tail of the linked list */ + int elements; /* holds number of elements on the list */ + SEM_ID sem; /* semaphore for the queue list */ +}; + /***************************************************************************** + * Memory Map of XVME-402 BITBUS CARD + * + * This board is rather stupid in that it wastes a whole Kilo of space + * for its 5 1-byte regs. So the dm* fields in the structure below are + * for those filler locations. + * + *****************************************************************************/ +typedef struct XycomBBRegsStruct { + unsigned char dm0; + unsigned char stat_ctl; + unsigned char dm2; + unsigned char int_vec; + unsigned char dm4; + unsigned char data; + unsigned char dm6; + unsigned char cmnd; + unsigned char dm8; + unsigned char fifo_stat; + unsigned char dmA[1014]; /* Board occupies 1024 bytes in memory*/ +} XycomBBRegsStruct; + +#define XYCOM_BB_MAX_OUTSTAND_MSGS 4 /* per-link max pending messages */ +#define RESET_POLL_TIME 10 /* time to sleep when waiting on a link abort */ + +#define BB_SEND_CMD 0x0 /* command to initiate sending of msg */ + +#define XVME_ENABLE_INT 0x08 /* Allow xvme interupts */ + +#define XVME_TX_INT 0x20 /* int enable TX only */ +#define XVME_TX_PEND 0x10 /* transmit interrupt currently pending */ + +#define XVME_RX_INT 0x80 /* int exable RX only */ +#define XVME_RX_PEND 0x40 /* receive interrupt currently pending */ + +#define XVME_NO_INT 0 /* disable all interrupts */ + +/****************************************************************************** + * + * Fifo status register format for Xycom board + * + * +----+----+----+----+----+----+----+----+ + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Bits + * +----+----+----+----+----+----+----+----+ + * "1" "1" RCMD RFNE "1" "1" "1" TFNF data + * + * RCMD = "1"= command received ( end of message ) + * "0"= No command + * + * RFNE = "1" = Receive Fifo Not Empty + * "0" = Receive Fifo empty + * + * TFNF = "1" = Transmit Fifo Not Full + * "0" = transmit fifo full + * + * NOTE: + * A write to bit 7 of the Fifo status register can be used to reset the + * xvme board. + * + *****************************************************************************/ +#define XVME_RCMD 0x20 /* Command has been received */ +#define XVME_RFNE 0x10 /* Receive Fifo is Not Empty */ +#define XVME_TFNF 0x01 /* Transmit FIFO is Not Full */ +#define XVME_FSVALID 0x31 /* these are the only valid status bits */ +#define XVME_FSIDLE 0x01 /* fifo_stat & FSVALID = 0x1 when it is idle */ +#define XVME_RESET 0x80 /* Write this and you reset the XVME card */ + +/****************************************************************************** + * + * The XycomBBLinkStruct structure holds all the xvme-card specific data + * required for the operation of the link. + * + ******************************************************************************/ +typedef struct XycomBBLinkStruct { + volatile XycomBBRegsStruct *bbRegs;/* pointer to board registers */ + SEM_ID rxInt; /* given when rx interrupts occur */ +} XycomBBLinkStruct; + + /**************************************************************************** + * Memory Map of PEP Modular PB-BIT BITBUS CARD + * + * This board is rather stupid in that it wastes a bunch of bytes + * for its regs. So the dm* fields in the structure below are + * for those filler locations. + * + ***************************************************************************/ +typedef struct PepBBRegsStruct { + unsigned char dm0; + unsigned char data; + unsigned char dm2; + unsigned char stat_ctl; + unsigned char dm[29]; + unsigned char int_vec; +} PepBBRegsStruct; + +/****************************************************************************** + * + * status register format for PEP's PB-BIT board + * + * +----+----+----+----+----+----+----+----+ + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Bits + * +----+----+----+----+----+----+----+----+ + * X X X STF LBP TFF RFNE IRQP + * + * STF = "1"= self-test failed + * "0"= passed + * + * LBP = "1"= last data byte was last of package + * "0"= 1+ bytes left + * + * TFF = "1"= only one more byte may be written to TFIFO + * "0"= 1+ more bytes may be written + * + * RFNE = "1"= Receive Fifo Not Empty + * "0"= Receive Fifo empty + * + * IRQP = "1"= no irq pending + * "0"= irq pending + * + * + *****************************************************************************/ +#define PEP_BB_RCMD 0x08 /* Command has been received */ +#define PEP_BB_RFNE 0x02 /* Receive Fifo is Not Empty */ +#define PEP_BB_TFNF 0x04 /* Transmit FIFO is Not Full */ +#define PEP_BB_FSVALID 0x1f /* these are the only valid status bits */ + +/****************************************************************************** + * + * The PepBBLinkStruct structure holds all the card specific data required for + * the operation of the link. + * + ******************************************************************************/ +typedef struct PepBBLinkStruct { + + volatile PepBBRegsStruct *bbRegs; /* pointer to board registers */ + SEM_ID rxInt; /* given when rx interrupts occur */ + +} PepBBLinkStruct; + /***************************************************************************** + * + * Holds the user-configured board addresses etc. These MUST be set before + * the driver is initialized. And may not be changed after the init phase. + * + * NOTE: + * The setting of these items is intended to be done via records in the future. + * + * The busyList.sem is used to lock the busyList as well as the deviceStatus + * table. + * + *****************************************************************************/ +typedef struct BitbusLinkStruct +{ + unsigned long LinkType; /* Type of link (XYCOM, PEP,...) */ + unsigned long BaseAddr; /* Base address within A16 */ + unsigned long IrqVector; /* Irq vector base */ + unsigned long IrqLevel; /* Irq level */ + + WDOG_ID watchDogId; /* watchdog for timeouts */ + SEM_ID watchDogSem; /* set by the watch dog int handler */ + + unsigned char abortFlag; /* set to 1 if link is being reset by the dog */ + unsigned char txAbortAck; /* set to 1 by txTask to ack abort Sequence */ + unsigned char rxAbortAck; /* set to 1 by rxTask to ack abort Sequence */ + + int nukeEm; /* manual link restart flag */ + SEM_ID linkEventSem; /* given when this link requires service */ + struct bbList queue[BB_NUM_PRIO]; /* prioritized request queues */ + struct bbList busyList; /* messages waiting on a response */ + unsigned char deviceStatus[BB_APERLINK];/* mark a device as idle or busy */ + unsigned long syntheticDelay[BB_APERLINK]; /* holds the wakeup time for 91-delays */ + int DelayCount; /* holds total number of syntheticDelays in progress */ + + union + { + PepBBLinkStruct PepLink; + XycomBBLinkStruct XycomLink; + } l; + +#ifdef BB_SUPER_DEBUG + HistoryStruct History; +#endif + +} BitbusLinkStruct; + +#define BB_CONF_HOSED 0 /* Link is not present */ +#define BB_CONF_TYPE_XYCOM 1 /* Link is a Xycom board */ +#define BB_CONF_TYPE_PEP 2 /* Link is a PEP board */ + +#endif diff --git a/src/drv/old/drvCaenV265.c b/src/drv/old/drvCaenV265.c new file mode 100644 index 000000000..ed42f1f3b --- /dev/null +++ b/src/drv/old/drvCaenV265.c @@ -0,0 +1,807 @@ +/* share/src/drv @(#)drvCaenV265.c 1.1 9/2/94 */ +/* drvCaenV265.c - Driver/Device Support Routines for CAEN V265 + * + * Author: Jeff Hill (johill@lanl.gov) + * Date: 8-11-94 + * + * 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 + * MIT Bates Lab + * + * Modification Log: + * ----------------- + */ + +/* + * ANSI C Includes + */ +#include +#include +#include +#include + +/* + * vxWorks includes + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * EPICS include + */ +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * base address, base interrupt vector, + * number of cards, & interrupt level + */ +#define CAIN_V265_A24_BASE (0x000000) +#define CAIN_V265_INTVEC_BASE (0xA0) +#define CAIN_V265_MAX_CARD_COUNT (8) +#define CAIN_V265_INT_LEVEL (6) + + + + +/* + * all device registers declared + * ANSI C volatile so we dont need to + * use the -fvolatile flag (and dont + * limit the optimizer) + */ +typedef volatile int16_t devReg; + +struct caenV265 { + devReg csr; + devReg clear; + devReg DAC; + devReg gate; + const devReg data; + const devReg pad1[(0xf8-0x8)/2]; + const devReg fixed; + const devReg identifier; + const devReg version; +}; +#define CAENV265ID 0x0812 + +/* + * Insert or extract a bit field using the standard + * masks and shifts defined below + */ +#ifdef __STDC__ +#define INSERT(FIELD,VALUE)\ + (((VALUE)&(FD_ ## FIELD ## _M))<<(FD_ ## FIELD ## _S)) +#define EXTRACT(FIELD,VALUE)\ + ( ((VALUE)>>(FD_ ## FIELD ## _S)) &(FD_ ## FIELD ## _M)) +#else /*__STDC__*/ +#define INSERT(FIELD,VALUE)\ + (((VALUE)&(FD_/* */FIELD/* */_M))<<(FD_/* */FIELD/* */_S)) +#define EXTRACT(FIELD,VALUE)\ + ( ((VALUE)>>(FD_/* */FIELD/* */_S)) &(FD_/* */FIELD/* */_M)) +#endif /*__STDC__*/ + +/* + * in the constants below _M is a right justified mask + * and _S is a shift required to right justify the field + */ + +/* + * csr register + */ +#define FD_FULL_M (0x1) +#define FD_FULL_S (14) +#define FD_READY_M (0x1) +#define FD_READY_S (15) +#define FD_BUSY_FULL_M (0x3) +#define FD_BUSY_FULL_S (14) +#define FD_IVEC_M (0xff) +#define FD_IVEC_S (0) +#define FD_ILEVEL_M (0x7) +#define FD_ILEVEL_S (8) + +/* + * series/version register + */ +#define FD_SERIES_M (0xfff) +#define FD_SERIES_S (0) +#define FD_VERSION_M (0xf) +#define FD_VERSION_S (12) + +/* + * data register + */ +#define FD_CHANNEL_M (0x7) +#define FD_CHANNEL_S (13) +#define FD_RANGE_M (1) +#define FD_RANGE_S (12) +#define FD_DATA_M (0xfff) +#define FD_DATA_S (0) + +struct channel{ + int16_t signal; + char newData; +}; + +enum adc_range {adc_12, adc_15, NUMBER_OF_ADC_RANGES}; +#define NUMBER_OF_SIGNALS 8 +#define NUMBER_OF_FIFO_ENTRIES (16*NUMBER_OF_SIGNALS*NUMBER_OF_ADC_RANGES) +LOCAL struct caenV265Config{ + struct caenV265 *pCaenV265; /* pointer to the card */ + struct channel chan[NUMBER_OF_SIGNALS][NUMBER_OF_ADC_RANGES]; + IOSCANPVT scanpvt; + WDOG_ID wdid; +}caenV265Info[CAIN_V265_MAX_CARD_COUNT]; + +#ifdef __STDC__ +#define SHOW_OFFSET(STRUCT,FIELD) \ +printf( "%s.%s is at 0x%X\n", \ + #STRUCT, \ + #FIELD, \ + offsetof(struct STRUCT, FIELD)) +#endif + +LOCAL void caenV265ISR(unsigned card); +LOCAL int caenV265Shutdown(void); +LOCAL int caenV265IdTest(struct caenV265 *pCaenV265); +int caenV265Test(unsigned card); +LOCAL void caenV265ReadData(struct caenV265Config *pCaenV256Config); +LOCAL int caenV265TestVal(unsigned card, unsigned dacVal); +LOCAL int caenV265IntEnable(unsigned card); + +/* + * device support entry table + */ +LOCAL long caenV265InitRecord(struct aiRecord *pai); +LOCAL long caenV265AiRead(struct aiRecord *pai); +LOCAL long caenV265SpecialLinconv(struct aiRecord *pai, int after); +LOCAL long caenV265GetIoIntInfo(int cmd, struct aiRecord *pai, IOSCANPVT *ppvt); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_ai; + DEVSUPFUN special_linconv; +} devCaenV265 ={ + 6, + NULL, + NULL, + caenV265InitRecord, + caenV265GetIoIntInfo, + caenV265AiRead, + caenV265SpecialLinconv}; + +/* + * driver support entry table + */ +LOCAL long caenV265Init(void); +LOCAL long caenV265IOReport(int level); +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvCaenV265 ={ + 2, + caenV265IOReport, + caenV265Init}; + + + +/* + * verify that register + * offsets match the doc. + */ +#ifdef DEBUG +void offsettest() +{ + SHOW_OFFSET(caenV265, version); + SHOW_OFFSET(caenV265, identifier); + SHOW_OFFSET(caenV265, fixed); + SHOW_OFFSET(caenV265, data); + SHOW_OFFSET(caenV265, gate); + SHOW_OFFSET(caenV265, DAC); + SHOW_OFFSET(caenV265, clear); + SHOW_OFFSET(caenV265, csr); + + return; +} +#endif + + +/* + * caenV265InitRecord() + */ +LOCAL long caenV265InitRecord(struct aiRecord *pai) +{ + struct vmeio *pvmeio; + + /* ai.inp must be an VME_IO */ + switch (pai->inp.type) { + case (VME_IO): + break; + default : + recGblRecordError(S_db_badField,(void *)pai, + "devAiXy566Se (init_record) Illegal INP field"); + return(S_db_badField); + } + + pvmeio = (struct vmeio *)&(pai->inp.value); + + /* + * check for bad signal or card number + */ + if ( pvmeio->signal >= NUMBER_OF_SIGNALS || + pvmeio->card >= CAIN_V265_MAX_CARD_COUNT ) { + + recGblRecordError( + S_db_badField, + (void *)pai, + "devCaenV265 bad card or signal number"); + return -1; + } + + if(!caenV265Info[pvmeio->card].pCaenV265){ + recGblRecordError( + S_db_badField, + (void *)pai, + "devCaenV265 card does not exist"); + return -1; + } + + /* set linear conversion slope*/ + pai->eslo = (pai->eguf-pai->egul)/FD_DATA_M; + + return(0); +} + + +/* + * caenV265AiRead() + */ +LOCAL long caenV265AiRead(struct aiRecord *pai) +{ + int16_t value; + struct vmeio *pvmeio; + + pvmeio = (struct vmeio *)&(pai->inp.value); + + /* + * check for bad signal or card number + */ + if ( pvmeio->signal >= NUMBER_OF_SIGNALS || + pvmeio->card >= CAIN_V265_MAX_CARD_COUNT ) { + + recGblSetSevr(pai, READ_ALARM, INVALID_ALARM); + return -1; + } + + /* + * uninitialized data? + */ + if (!caenV265Info[pvmeio->card].chan[pvmeio->signal][adc_12].newData) { + recGblSetSevr(pai, READ_ALARM, INVALID_ALARM); + return -1; + } + + value = caenV265Info[pvmeio->card].chan[pvmeio->signal][adc_12].signal; + pai->rval = value; + + return 0; +} + + +/* + * caenV265SpecialLinconv() + */ +LOCAL long caenV265SpecialLinconv(struct aiRecord *pai, int after) +{ + if(!after) { + return 0; + } + + /* set linear conversion slope*/ + pai->eslo = (pai->eguf-pai->egul)/FD_DATA_M; + return 0; +} + + +/* + * caenV265GetIoIntInfo() + */ +LOCAL long caenV265GetIoIntInfo( +int cmd, +struct aiRecord *pai, +IOSCANPVT *ppvt) +{ + struct vmeio *pvmeio; + + pvmeio = (struct vmeio *)&(pai->inp.value); + + /* + * check for bad card number + */ + if ( pvmeio->card >= CAIN_V265_MAX_CARD_COUNT ) { + logMsg( + "%s.%d:devCaenV265 bad card number %d %s\n", + (int)__FILE__, + __LINE__, + pvmeio->card, + (int)pai->name,0,0); + recGblRecordError( + S_db_badField, + (void *)pai, + "devCaenV265 bad card number"); + return -1; + } + + *ppvt = &caenV265Info[pvmeio->card].scanpvt; + + return 0; +} + + +/* + * caenV265Init() + */ +LOCAL long caenV265Init(void) +{ + unsigned card; + struct caenV265 *pCaenV265; + int status; + + status = rebootHookAdd(caenV265Shutdown); + if(status){ + errMessage(S_dev_internal,"reboot hook add failed"); + return ERROR; + } + + status = sysBusToLocalAdrs( + VME_AM_STD_SUP_DATA, + CAIN_V265_A24_BASE, + (char **)&pCaenV265); + if(status!=OK){ + errPrintf( + S_dev_badA24, + __FILE__, + __LINE__, + "caenV265Init"); + return ERROR; + } + + for(card=0; cardclear = 0; /* any rw op resets the device */ + pCaenV265->DAC = 0; /* set test-signal "offset" to zero */ + + /* + * attach ISR + */ + vec = CAIN_V265_INTVEC_BASE+card; + status = intConnect( + INUM_TO_IVEC(vec), + caenV265ISR, + card); + assert(status>=0); + + /* + * Enable interrupts + */ + caenV265IntEnable(card); + } + status = sysIntEnable(CAIN_V265_INT_LEVEL); + assert(status>=0); + + return OK; +} + + +/* + * caenV265ISR() + */ +LOCAL void caenV265ISR(unsigned card) +{ + struct caenV265Config *pCaenV256Config = &caenV265Info[card]; + struct caenV265 *pCaenV265 = pCaenV256Config->pCaenV265; + unsigned signal; + int16_t csr; + static unsigned ticks; + unsigned newTicks; + + /* + * If its full then its more efficient + * to read it out without checking + * in between each read + */ + csr = pCaenV265->csr; + if (EXTRACT(FULL,csr)) { + for( signal=0; + signalcsr; + } + + /* + * limit the EPICS scan rate + */ + newTicks = tickGet(); + if(newTicks == ticks){ + /* + * Disable Interrupts + */ + pCaenV265->csr = 0; + + /* + * start a watch dog after one tick + * so that we limit the int rate to + * the system tick rate. + */ + wdStart(pCaenV256Config->wdid, + 1, + caenV265IntEnable, + card); + + return; + } + else{ + ticks = newTicks; + } + + /* + * tell EPICS to scan on int + */ + scanIoRequest(&caenV265Info[card].scanpvt); + + return; +} + + +/* + * caenV265IntEnable + */ +LOCAL int caenV265IntEnable(unsigned card) +{ + struct caenV265Config *pCaenV256Config = &caenV265Info[card]; + struct caenV265 *pCaenV265 = pCaenV256Config->pCaenV265; + unsigned vec; + int16_t newcsr; + + + vec = CAIN_V265_INTVEC_BASE+card; + newcsr = INSERT(IVEC, vec) | INSERT(ILEVEL, CAIN_V265_INT_LEVEL); + pCaenV265->csr = newcsr; + + return OK; +} + + +/* + * caenV265ReadData() + */ +LOCAL void caenV265ReadData(struct caenV265Config *pCaenV256Config) +{ + struct caenV265 *pCaenV265 = pCaenV256Config->pCaenV265; + int16_t val = pCaenV265->data; + int16_t data = EXTRACT(DATA, val); + unsigned range = EXTRACT(RANGE, val); + unsigned signal = EXTRACT(CHANNEL, val); + + if(range>=NUMBER_OF_ADC_RANGES){ + logMsg("caenV265ReadData: bad range number\n",0,0,0,0,0,0); + return; + } + if(signal>=NUMBER_OF_SIGNALS){ + logMsg("caenV265ReadData: bad signal number\n",0,0,0,0,0,0); + return; + } + pCaenV256Config->chan[signal][range].signal=data; + pCaenV256Config->chan[signal][range].newData=TRUE; + + return; +} + + +/* + * caenV265IdTest() + */ +LOCAL int caenV265IdTest(struct caenV265 *pCaenV265) +{ + int status; + int16_t id; + + /* + * Is a card present + */ + status = vxMemProbe( + (char *)&pCaenV265->identifier, + READ, + sizeof(id), + (char *)&id); + if(status!=OK){ + return FALSE; + } + + /* + * Is the correct type of card present + */ + if(id!=CAENV265ID){ + errPrintf( + S_dev_wrongDevice, + __FILE__, + __LINE__, + "caenV265IdTest"); + return FALSE; + } + return TRUE; +} + + +/* + * caenV265Shutdown() + * turns off interrupts so that dont foul up the boot + */ +LOCAL int caenV265Shutdown(void) +{ + struct caenV265 *pCaenV265; + unsigned card; + + for(card=0; cardcsr=0; + } + } + return OK; +} + + +/* + * caenV265Test() + */ +int caenV265Test(unsigned card) +{ + unsigned dacVal; + struct caenV265Config cofigCpy; + unsigned range; + unsigned signal; + + + dacVal=0; + caenV265TestVal(card, dacVal); + while(dacValpCaenV265; + unsigned signal; + + if(!pCaenV265){ + return ERROR; + } + + if(!caenV265IdTest(pCaenV265)){ + return ERROR; + } + + /* + * clear the module + */ + pCaenV265->clear=0; + + /* + * generate a test signal + */ + pCaenV265->DAC=dacVal; + + /* + * generate a test gate + */ + for( signal=0; + signalgate=0; + taskDelay(1); + } + while(!caenV265Info[card].chan[signal][adc_12].newData){ + pCaenV265->gate=0; + taskDelay(1); + } + } + + /* + * turn off test signal + */ + pCaenV265->clear=0; + pCaenV265->DAC=0; + + return OK; +} + + +/* + * caenV265IOReport() + */ +LOCAL long caenV265IOReport(int level) +{ + struct caenV265 *pCaenV265; + unsigned card; + unsigned signal; + unsigned range; + char *pVersion[] = {"NIM","ECL"}; + char *pState[] = { + "FIFO empty", + "FIFO full", + "FIFO partially filled", + "FIFO full"}; + + for (card=0; cardversion)]); + printf("\tseries = %d\n", + EXTRACT(SERIES, pCaenV265->version)); + printf("\tstate = %s\n", + pState[EXTRACT(BUSY_FULL,pCaenV265->csr)]); + printf("\tint level = %d\n", + EXTRACT(ILEVEL,pCaenV265->csr)); + printf("\tint vec = 0x%02X\n", + EXTRACT(IVEC,pCaenV265->csr)); + printf( "\tbase addr= 0x%X on the %s\n", + (unsigned)caenV265Info[card].pCaenV265, + sysModel()); + + for( range=0; + range\n", + signal); + } + } + + } + } + return OK; +} diff --git a/src/drv/old/drvComet.c b/src/drv/old/drvComet.c new file mode 100644 index 000000000..10d650d4a --- /dev/null +++ b/src/drv/old/drvComet.c @@ -0,0 +1,639 @@ +/* comet_driver.c */ +/* base/src/drv $Id$ */ +/* + * Author: Leo R. Dalesio + * Date: 5-92 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 joh 071092 added argument to calloc() + * .02 joh 071092 stripped the hkv2f specific portion off the comet + * std addr base specification and left it at base + * addr zero which is most likely wrong. + * .03 joh 071492 use extended (A32) address space + * instead of standard (A24) address space + * for the base address of the waveform memory + * (changed arg to sysBusToLocalAdrs() + * .04 joh 071592 fixed to use correct size when incrementing + * to the waveform memory of the second card + * .05 joh 071592 modified A16 & A32 base addr to match AT8 + * address standard + * .06 bg 071792 moved addresses to module_types.h + * .07 joh 080592 added io report routines + * .08 ms 080692 added comet_mode routine, modified comet_driver + * and cometDoneTask to allow an external routine + * to control hardware scan mode. Added variable + * scan_control to flag operating mode. + * .09 mrk 082692 added DSET + * .10 joh 082792 fixed uninitialized csr pointer in comet_driver() + * function + * .11 lrd 091692 add signal support + * .12 joh 092992 card number validation now based on module_types.h. + * signal number checking now based on the array element + * count. + * .13 mrk 080293 Added call to taskwdInsert + * .14 mgb 080493 Removed V5/V4 and EPICS_V2 conditionals + */ + +static char *sccsID = "@(#)drvComet.c 1.11\t9/16/92"; + +/* + * Code Portions + * + * comet_init() + * comet_driver(card, pcbroutine, parg) + * cometDoneTask() + * comet_io_report() + * comet_mode(card,mode,arg,val) + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define COMET_NCHAN 4 +#define COMET_CHANNEL_MEM_SIZE 0x20000 /* bytes */ +#define COMET_DATA_MEM_SIZE (COMET_CHANNEL_MEM_SIZE*COMET_NCHAN) +static char *shortaddr; +static short scan_control; /* scan type/rate (if >0 normal, <=0 external control) */ + +/* comet conrtol register map */ +struct comet_cr{ + unsigned char csrh; /* control and status register - high byte */ + unsigned char csrl; /* control and status register - low byte */ + unsigned char lcrh; /* location status register - high byte */ + unsigned char lcrl; /* location status register - low byte */ + unsigned char gdcrh; /* gate duration status register - high byte*/ + unsigned char gdcrl; /* gate duration status register - low byte */ + unsigned char cdr; /* channel delay register */ + unsigned char acr; /* auxiliary control register */ + char pad[0x100-8]; +}; + + +/* defines for the control status register - high byte */ +#define DIGITIZER_ACTIVE 0x80 /* 1- Active */ +#define ARM_DIGITIZER 0x40 /* 1- Arm the digitizer */ +#define CIRC_BUFFER_ENABLED 0x20 /* 0- Stop when memory is full */ +#define WRAP_MODE_ENABLED 0x10 /* 0- Disable wrap around */ +#define AUTO_RESET_LOC_CNT 0x08 /* 1- Reset addr to 0 on trigger */ +#define EXTERNAL_TRIG_ENABLED 0x04 /* 1- use external clk to trigger */ +#define EXTERNAL_GATE_ENABLED 0x02 /* 0- use pulse start conversion */ +#define EXTERNAL_CLK_ENABLED 0x01 /* 0- uses the internal clock */ + + +/* commands for the COMET digitizer */ +#define COMET_INIT_CSRH +#define COMET_INIT_READ + +/* mode commands for the COMET digitizer */ +#define READREG 0 +#define WRITEREG 1 +#define SCANCONTROL 2 +#define SCANSENSE 3 +#define SCANDONE 4 + +/* register selects */ +#define COMET_CSR 0 +#define COMET_LCR 1 +#define COMET_GDCR 2 +#define COMET_CDACR 3 + +/* defines for the control status register - low byte */ +#define SOFTWARE_TRIGGER 0x80 /* 1- generates a software trigger */ +#define UNUSED 0x60 +#define CHAN_DELAY_ENABLE 0x10 /* 0- digitize on trigger */ +#define DIG_RATE_SELECT 0x0f + +/* digitizer rates - not defined but available for 250KHz to 122Hz */ +#define COMET_5MHZ 0x0000 +#define COMET_2MHZ 0x0001 +#define COMET_1MHZ 0x0002 +#define COMET_500KHZ 0x0003 + +/* defines for the auxiliary control register */ +#define ONE_SHOT 0x10 +#define ALL_CHANNEL_MODE 0x80 + + +/* comet configuration data */ +struct comet_config{ + struct comet_cr *pcomet_csr; /* pointer to the control/status register */ + unsigned short *pdata; /* pointer to data area for this COMET card */ + void (*psub)(); /* subroutine to call on end of conversion */ + void *parg[4]; /* argument to return to the arming routine */ + FAST_LOCK lock; /* mutual exclusion lock */ + IOSCANPVT ioscanpvt; + unsigned long nelements; /* number of elements to digitize/read */ + +}; + +/* task ID for the comet done task */ +int cometDoneTaskId; +struct comet_config *pcomet_config; + +static long report(); +static long init(); +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvComet={ + 2, + report, + init}; + + +/* + * cometDoneTask + * + * wait for comet waveform record cycle complete + * and call back to the database with the waveform size and address + * + */ +void +cometDoneTask() +{ + register unsigned card; + register struct comet_config *pconfig; + register long i; + + while(TRUE) + { + + if (scan_control <= 0) + taskDelay(2); + else + { + taskDelay(scan_control); + +/* printf("DoneTask: entering for loop...\n"); */ + + /* check each card for end of conversion */ + for(card=0, pconfig = pcomet_config; card < 2;card++, pconfig++) + { +/* is the card present */ + if (!pconfig->pcomet_csr) + { + if (card == 0) + { +/* + printf("DoneTask: checking card present?...\n"); + printf("DoneTask: pconfig->pcomet_csr %x...\n",pconfig->pcomet_csr); +*/ + } + continue; + } + +/* is the card armed */ + if (!pconfig->psub) + { + if (card == 0) + { +/* printf("DoneTask: checking card armed?...\n"); */ + } + continue; + } + +/* is the digitizer finished conversion */ +/* printf("pconfig->pdata: %x \n", pconfig->pdata); */ + + if (*(pconfig->pdata+pconfig->nelements) == 0xffff) + { + if (card == 0) + { +/* printf("DoneTask: finished conversion?...\n"); */ + } + continue; + } + +/* printf("DoneTask: pcomet_config->pcomet_csr %x...\n",pcomet_config->pcomet_csr); */ +/* printf("DoneTask: DONE\n"); */ + + +#if 0 + /* reset each of the control registers */ + pconfig->pcomet_csr->csrh = pconfig->pcomet_csr->csrl = 0; + pconfig->pcomet_csr->lcrh = pconfig->pcomet_csr->lcrl = 0; + pconfig->pcomet_csr->gdcrh = pconfig->pcomet_csr->gdcrl = 0; + pconfig->pcomet_csr->acr = 0; +#endif + + /* clear the pointer to the subroutine to allow rearming */ +/* pconfig->psub = NULL; */ + +/* post the event */ +/* - is there a bus error for long references to this card?? copy into VME mem? */ + + if(pconfig->parg[0]) + { + (*pconfig->psub)(pconfig->parg[0],pconfig->pdata); + } + if(pconfig->parg[1]) + { + (*pconfig->psub)(pconfig->parg[1],(((char*)pconfig->pdata)+0x20000)); + } + + if(pconfig->parg[2]) + { + (*pconfig->psub)(pconfig->parg[2],(((char*)pconfig->pdata)+0x40000)); + } + + if(pconfig->parg[3]) + { + (*pconfig->psub)(pconfig->parg[3],(((char*)pconfig->pdata)+0x60000)); + } + + + } + } + } +} + + + +/* + * COMET_INIT + * + * intialize the driver for the COMET digitizer from omnibyte + * + */ +comet_init() +{ + register struct comet_config *pconfig; + short readback,got_one,card; + int status; + struct comet_cr *pcomet_cr; + unsigned char *extaddr; + +/* free memory and delete tasks from previous initialization */ + if (cometDoneTaskId) + { + taskwdRemove(cometDoneTaskId); + if ((status = taskDelete(cometDoneTaskId)) < 0) + logMsg("\nCOMET: Failed to delete cometDoneTask: %d",status); + } + else + { + pcomet_config = (struct comet_config *)calloc(wf_num_cards[COMET],sizeof(struct comet_config)); + if (pcomet_config == 0) + { + logMsg("\nCOMET: Couldn't allocate memory for the configuration data"); + return; + } + } + +/* get the standard and short address locations */ + if ((status = sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO,wf_addrs[COMET],&pcomet_cr)) != OK){ + logMsg("\nCOMET: failed to map VME A16 base address\n"); + return; + } + if ((status = sysBusToLocalAdrs(VME_AM_EXT_SUP_DATA,wf_memaddrs[COMET],&extaddr)) != OK){ + logMsg("\nCOMET: failed to map VME A32 base address\n"); + return; + } + +/* determine which cards are present */ + got_one = FALSE; + pconfig = pcomet_config; + + for ( card = 0; + card < 2; + card++, pconfig++, pcomet_cr++, extaddr+= COMET_DATA_MEM_SIZE){ + + /* is the card present */ + if (vxMemProbe(pcomet_cr,READ,sizeof(readback),&readback) != OK) + { + continue; + } + if (vxMemProbe(extaddr,READ,sizeof(readback),&readback) != OK) + { + logMsg("\nCOMET: Found CSR but not data RAM %x\n",extaddr); + continue; + } + + /* initialize the configuration data */ + pconfig->pcomet_csr = pcomet_cr; + pconfig->pdata = (unsigned short *) extaddr; + got_one = TRUE; + + + FASTLOCKINIT(&pcomet_config[card].lock); + + /* initialize the card */ + pcomet_cr->csrh = ARM_DIGITIZER | AUTO_RESET_LOC_CNT; + pcomet_cr->csrl = COMET_1MHZ; + pcomet_cr->lcrh = pcomet_cr->lcrl = 0; + pcomet_cr->gdcrh = 0; + pcomet_cr->gdcrl = 1; + pcomet_cr->cdr = 0; + + /* run it once */ + pcomet_cr->csrl |= SOFTWARE_TRIGGER; + taskDelay(1); + /* reset */ + pcomet_cr->csrl = COMET_5MHZ; + pcomet_cr->acr = ONE_SHOT | ALL_CHANNEL_MODE; + + scanIoInit(&pconfig->ioscanpvt); + + } /*end of for loop*/ + + /* initialization for processing comet digitizers */ + if(got_one) + { + /* start the waveform readback task */ + scan_control = 2; /* scan rate in vxWorks clock ticks */ + cometDoneTaskId = taskSpawn("cometWFTask",WFDONE_PRI,WFDONE_OPT,WFDONE_STACK,(FUNCPTR) cometDoneTask); + taskwdInsert(cometDoneTaskId,NULL,NULL); + } +} + + +static long report(level) + int level; +{ + comet_io_report(level); + return(0); +} + +static long init() +{ + + comet_init(); + return(0); +} + + +/* + * COMET_DRIVER + * + * initiate waveform read + * + */ +comet_driver(card, signal, pcbroutine, parg, nelements) +register short card; +register unsigned short signal; +unsigned int *pcbroutine; +unsigned int *parg; /* pointer to the waveform record */ +unsigned long nelements; +{ + register struct comet_cr *pcomet_csr; + register struct comet_config *pconfig; + register unsigned short *pcomet_data; + register char *dummy; + +/* printf("comet_driver: BEGIN...\n"); */ +/* printf("comet_driver: nelements: %d ...\n",nelements); */ + + /* check for valid card number */ + if(card >= wf_num_cards[COMET]) + return ERROR; + pconfig = (pcomet_config+card); + if(signal >= NELEMENTS(pconfig->parg)) + return ERROR; + pconfig->nelements = nelements * 2; + +/* printf("comet_driver: check for card present...\n"); */ + + /* check for card present */ + if(!pconfig->pcomet_csr) return ERROR; + + /* mutual exclusion area */ + FASTLOCK(&pconfig->lock); + +/* printf("comet_driver: mark the card as armed...\n"); */ + + /* mark the card as armed */ +/* if (pconfig->parg[signal] != 0) */ + pconfig->parg[signal] = parg; +/* if (pconfig->psub) return; */ + pconfig->psub = (void (*)()) pcbroutine; + + /* exit mutual exclusion area */ + FASTUNLOCK(&pconfig->lock); + + pcomet_csr = pconfig->pcomet_csr; + + /* reset each of the control registers */ + pcomet_csr->csrh = pcomet_csr->csrl = 0; + pcomet_csr->lcrh = pcomet_csr->lcrl = 0; + pcomet_csr->gdcrh = pcomet_csr->gdcrl = 0; + pcomet_csr->acr = 0; + + /* arm the card */ + *(pconfig->pdata+pconfig->nelements) = 0xffff; +/* printf("comet_driver: pconfig->pcomet_csr %x...\n",pconfig->pcomet_csr); */ + + if (scan_control > 0) + { +#if 0 /* for debugging purposes */ + pcomet_csr->gdcrh = 0x03; /* # samples per channel */ + pcomet_csr->gdcrl = 0xe8; /* # samples per channel */ +#endif + + pcomet_csr->gdcrh = (pconfig->nelements >> 8) & 0xff; /* # samples per channel */ + pcomet_csr->gdcrl = pconfig->nelements & 0xff; /* # samples per channel */ + pcomet_csr->acr = ONE_SHOT | ALL_CHANNEL_MODE; /* disarm after the trigger */ + pcomet_csr->csrl = COMET_5MHZ; /* sample at 5MhZ */ + + /* arm, reset location counter to 0 on trigger, use external trigger */ + pcomet_csr->csrh = ARM_DIGITIZER | AUTO_RESET_LOC_CNT | EXTERNAL_TRIG_ENABLED; +/* printf("comet_driver: gdcrh: %x gdcrl: %x nelements: %x\n ",pcomet_csr->gdcrh,pcomet_csr->gdcrl, pconfig->nelements); */ + + } + else + pcomet_csr->csrh |= ARM_DIGITIZER; +/* printf("comet_driver: pconfig->pcomet_csr %x...\n",pconfig->pcomet_csr); */ + +/* printf("comet_driver: END...\n"); */ + return OK; +} + + +/* + * COMET_IO_REPORT + * + * print status for all cards in the specified COMET address range + */ +comet_io_report(level) +short int level; +{ + struct comet_config *pconfig; + unsigned card; + unsigned nelements; + int status; + + pconfig = pcomet_config; + for(card=0; card < wf_num_cards[COMET]; card++){ + + if(!pconfig->pcomet_csr) + continue; + + printf( "WF: COMET:\tcard=%d\n", card); + if (level >= 2){ + printf("enter the number of elements to dump:"); + status = scanf("%d",&nelements); + if(status == 1){ + comet_dump(card, nelements); + } + } + pconfig++; + } + return OK; +} + + +/* + * comet_dump + * + */ +int comet_dump(card, n) +unsigned card; +unsigned n; +{ + unsigned short *pdata; + unsigned short *psave; + unsigned short *pbegin; + unsigned short *pend; + + if (card >= wf_num_cards[COMET]) + return ERROR; + + pdata = pcomet_config[card].pdata; + psave = (unsigned short *) malloc(n * sizeof(*psave)); + if(!psave){ + return ERROR; + } + + pbegin = psave; + pend = &psave[n]; + for( pdata = pcomet_config[card].pdata; + psave= wf_num_cards[COMET]) + return ERROR; + if (!pcomet_config[card].pcomet_csr) + return ERROR; + switch (mode) + { + case READREG: + /*cptr = (unsigned char *)pcomet_config[card].pcomet_csr; + for (i = 0; i < 6; i++, cptr++) + printf("%x %x\n",cptr,*cptr);*/ + cptr = (unsigned char *)pcomet_config[card].pcomet_csr; /* point to offset 0 */ + cptr += arg<<1; /* build new offset */ + val = (*cptr++)<<8; /* read value and return */ + val |= *cptr; + return val; + break; + case WRITEREG: + cptr = (unsigned char *)pcomet_config[card].pcomet_csr; + cptr += arg<<1; + *cptr++ = val>>8; + *cptr = val; + break; + case SCANCONTROL: + scan_control = val; + break; + case SCANSENSE: + return scan_control; + break; + case SCANDONE: + if (!pcomet_config[card].psub) + return ERROR; + /*pcomet_config[card].psub = NULL;*/ /* clear the pointer to subroutine to allow rearming */ + (*pcomet_config[card].psub)(pcomet_config[card].parg,0xffff,pcomet_config[card].pdata); + break; + default: + return ERROR; + } + return OK; +} + +/*********************************************/ +cometGetioscanpvt(card,scanpvt) +short card; +IOSCANPVT *scanpvt; +{ + register struct comet_config *pconfig; + + pconfig=pcomet_config; + pconfig+=card; + + if ((card >= wf_num_cards[COMET]) || (card < 0)) /* make sure hardware exists */ + return(0); + +/* +This is present in the mix driver...I don't know if I really need it. + if (!pconfig->present) + return(0); +*/ + + *scanpvt = pconfig->ioscanpvt; + + return(0); +} + diff --git a/src/drv/old/drvCompuSm.c b/src/drv/old/drvCompuSm.c new file mode 100644 index 000000000..a1bd303b3 --- /dev/null +++ b/src/drv/old/drvCompuSm.c @@ -0,0 +1,972 @@ +/* drvCompuSm.c */ +/* base/src/drv $Id$ */ +/* + * subroutines and tasks that are used to interface to the Compumotor 1830 + * stepper motor drivers + * + * Author: Bob Dalesio + * Date: 01-03-90 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 02-07-90 lrd add command to read the motor status + * .02 04-11-90 lrd made velocity mode motor go active + * .03 04-12-90 lrd only allow one connection to a motor + * .04 04-13-90 lrd add externally initiated motor motion monitoring + * .05 09-05-91 joh updated for v5 vxWorks + * .06 10-09-91 lrd monitor for external motion once every 2 seconds + * not at 30 Hz (.04 was not implemented correctly) + * .06 11-31-91 bg added compu_sm_io_report. Added sysBusToLocalAdrs() + * for addressing. + * .07 03-02-92 bg added level and ability to print raw values to + * compu_sm_io_report for level > 0. + * .08 05-04-92 bg added compu_sm_reset and rebootHookAdd so ioc can be + * rebooted with control X. + * .09 06-25-92 bg Combined drvCompuSm.c and compu_sm_driver.c + * .10 06-26-92 bg Added level to compu_sm_io_report in drvCompuSm + * structure + * .11 06-29-92 joh took file ptr arg out of io report + * .12 08-06-92 joh merged compu sm include file + * .13 08-27-92 joh silenced ANSI C function proto warning + * .14 08-27-92 joh fixed no epics init + * .15 08-02-93 mrk Added call to taskwdInsert + * .16 10-29-93 jba Fixed max number of cards to use module_types.c + * Fixed error in calculating card addresses + */ +#include +#include +#include +#include /* library for semaphore support */ +#include +#include /* library for ring buffer support */ + +/* drvCompuSm.c - Driver Support Routines for CompuSm */ + +#include +#include +#include +#include +#include +#include + + +long compu_sm_io_report(); +long compu_driver_init(); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvCompuSm={ + 2, + compu_sm_io_report, + compu_driver_init}; + +/* compumotor vme interface information */ +#define MAX_COMPU_MOTORS 8 + +#define RESP_SZ 16 /* card returns 16 chars - cmd & resp */ +#define RESPBUF_SZ (RESP_SZ+1) /* intr routine also passes motor no. */ + +/* Control Byte bit definitions for the Compumotor 1830 */ + /* bits 0 and 1 are not used */ +#define CBEND 0x04 /* end of command string */ +#define CBLMR 0x08 /* last message byte read */ +#define CBCR 0x10 /* command ready in cm_idb */ +#define CBMA 0x20 /* message accepted from odb */ +#define CBEI 0x40 /* enable interrupts */ +#define CBBR 0x80 /* board reset */ +#define SND_MORE CBCR | CBEI /* more chars to send */ +#define SND_LAST CBEND | SND_MORE /* last char being sent */ +#define RD_MORE CBMA | CBEI /* more chars to read */ +#define RD_LAST CBLMR | RD_MORE /* last byte we need to read */ + +/* Status Byte bit definitions */ +#define SBIRDY 0x10 /* idb is ready */ + +/* Structure used for communication with a Compumotor 1830 +** Motor Controller. The data buffer is repeated 64 times +** on even word addresses (0xXXXXXX1,0xXXXXX5...) and the +** control location is repeated 64 times on odd word +** addresses (0xXXXXX3, 0xXXXXX7...). The registers must +** be read and written as bytes. +*/ +struct compumotor { + char cm_d1; /* not accessable */ + char cm_idb; /* input data buffer */ + char cm_d2; /* not accessable */ + char cm_cb; /* control byte */ + char cm_d3[0x100-4]; /* fill to next standard address */ +}; + + +/* This file includes defines for all compumotor 1830 controller +** commands. */ + +#define SM_NULL 0x0 /* Null command */ +#define SM_INT 0x1 /* Interrupt X */ +#define SM_WRT_STAT 0x8 /* Write X to the user defined status bits */ +#define SM_SET_STAT 0x9 /* Set user defined status bit number X */ +#define SM_CLR_STAT 0xa /* Clear user defined status bit number X */ +#define SM_SET_PROG 0xb /* Set programmable output bit X */ +#define SM_CLR_PROG 0xc /* Clear programmable output bit X */ +#define SM_WRT_PROG 0xd /* Write X to the programmable output bits */ +#define SM_DEF_X_TO_Y 0xf /* Define bit X to indicate state Y */ +#define SM_TOG_JOG 0x10 /* Disable/enable the JOG inputs */ +#define SM_DEF_JOG 0x11 /* Define JOG input functions */ +#define SM_TOG_REM_PWR 0x12 /* Turn off/on remote power shutdown */ +#define SM_TOG_REM_SHUT 0x13 /* Disable/enable the "remote shutdown" bit */ +#define SM_SET_CW_MOTN 0x14 /* Set CW motion equal to +- */ +#define SM_TOG_POS_MTN 0x18 /* Turn off/on post-move position maintenance */ +#define SM_TOG_STOP_STL 0x19 /* Turn off/on termination on stall detect */ +#define SM_REP_X_Y 0x20 /* Repeat the following X commands Y times */ +#define SM_REP_TIL_CONT 0x21 /* Repeat the following X commands + until a CONTINUE is received */ +#define SM_WAIT_CONT 0x28 /* Wait for a CONTINUE */ +#define SM_WAIT_MILLI 0x29 /* Wait X milliseconds */ +#define SM_WAIT_SECOND 0x2a /* Wait X seconds */ +#define SM_WAIT_MINUTE 0x2b /* Wait X minutes */ +#define SM_WAIT_TRIGGER 0x2c /* Wait for trigger X to go active */ +#define SM_DEF_A_OP_POS 0x2e /* Define the abs open-loop position as X */ +#define SM_DEF_A_CL_POS 0x2f /* Define absolute closed-loop position */ +#define SM_DEF_ABS_ZERO 0x30 /* Define the present position as the + absolute zero position */ +#define SM_DEF_VEL_ACC 0x31 /* Define default velocity and acceleration */ +#define SM_MOV_DEFAULT 0x32 /* Perform the default move (trapezoidal + continuous) */ +#define SM_MOV_REL_POS 0x33 /* Go to relative position X at default + velocity and acceleration */ +#define SM_MOV_ABS_POS 0x34 /* Go to absolute position X at default + velocity and acceleration */ +#define SM_MOV_REL_ENC 0x35 /* Go to relative encoder position X */ +#define SM_MOV_ABS_ENC 0x36 /* Go to absolute encoder position X */ +#define SM_DEF_OP_HOME 0x38 /* Define HOME location (open loop */ +#define SM_DEF_CL_HOME 0x38 /* Define HOME location (closed loop) */ +#define SM_MOV_HOME_POS 0x39 /* Go HOME at the default velocity and + acceleration */ +#define SM_MOV_HOME_ENC 0x3a /* Go to encoder HOME at the default + velocity and acceleration */ +#define SM_DO_MOV_X 0x40 /* Perform move number X */ +#define SM_DO_SEQ_X 0x41 /* Perform sequence buffer X */ +#define SM_DO_VEL_STR 0x42 /* Perform the velocity streaming buffer */ +#define SM_CONT 0x48 /* CONTINUE (perform next command) */ +#define SM_OP_LOOP_MODE 0x50 /* Enter open loop indexer mode */ +#define SM_VEL_DIS_MODE 0x51 /* Enter velocity-distance streaming mode */ +#define SM_VEL_TIM_MODE 0x52 /* Enter velocity-time streaming mode */ +#define SM_STOP 0x70 /* STOP motion */ +#define SM_DSC_SEQ 0x71 /* Discontinue the sequence buffer */ +#define SM_SSP_SEQ 0x72 /* Suspend the sequence buffer; + wait for a CONTINUE to resume */ +#define SM_DSC_SNGL 0x73 /* Discontinue any singular command + currently being performed */ +#define SM_STOP_ON_TRG 0x74 /* STOP motion when trigger X goes active */ +#define SM_DSC_SEQ_TRG 0x75 /* Discontinue the sequence buffer when + trigger X goes active */ +#define SM_SSP_SEQ_TRG 0x76 /* Suspend sequence buffer when trigger + X goes active */ +#define SM_DSC_SNGL_TRG 0x77 /* Discontinue any singular command when + trigger X goes active */ +#define SM_KILL 0x78 /* Kill motion */ +#define SM_KILL_SEQ 0x79 /* Kill the sequence buffer */ +#define SM_KILL_SEQ_SNGL 0x7a /* Kill current sequence singular command; + wait for CONTINUE */ +#define SM_KILL_VEL_STR 0x7b /* Kill the velocity streaming buffer */ +#define SM_KILL_ON_TRG 0x7c /* Kill motion when trigger X goes active */ +#define SM_KILL_SEQ_TRG 0x7d /* Kill the sequence buffer when trigger + X goes active */ +#define SM_KL_SQSNG_TRG 0x7e /* Kill current sequence singular command + when trigger X goes active; wait for + a continue */ +#define SM_KL_VLSTR_TRG 0x7f /* Kill the velocity streaming buffer + when trigger X goes active */ +#define SM_GET_B_REL_POS 0x80 /* Request position relative to the + beginning of the current move */ +#define SM_GET_E_REL_POS 0x81 /* Request position relative to the + end of the current move */ +#define SM_GET_H_REL_POS 0x82 /* Request position relative to the home + limit switch */ +#define SM_GET_Z_REL_POS 0x83 /* Request position relative to the + absolute zero position */ +#define SM_GET_CUR_DIR 0x84 /* Request current direction */ +#define SM_GET_VEL 0x85 /* Request current velocity */ +#define SM_GET_ACC 0x86 /* Request current acceleration */ +#define SM_GET_MOV_STAT 0x88 /* Request current move status */ +#define SM_GET_LIM_STAT 0x89 /* Request state of the limit switches */ +#define SM_GET_HOME_STAT 0x8a /* Request state of the HOME switch */ +#define SM_GET_TRV_DIR 0x8b /* Request direction of travel */ +#define SM_GET_MOT_MOV 0x8c /* Request whether motor is moving or not */ +#define SM_GET_MOT_CONST 0x8d /* Request whether motor is at constant, + nonzero velocity or not */ +#define SM_GET_MOT_ACC 0x8e /* Request whether motor is or is not + accelerating */ +#define SM_GET_MOT_DEC 0x8f /* Request whether motor is or is not + decelerating */ +#define SM_GET_MODE 0x90 /* Request present mode */ +#define SM_GET_MV_PARM 0x91 /* Request move parameters for move number X */ +#define SM_GET_SEQ_CMMD 0x92 /* request commands stored in the + sequence buffer */ +#define SM_GET_MVDEF_STAT 0x93 /* Request state of the move definitions */ +#define SM_GET_TRG_STAT 0x94 /* Request state of trigger inputs */ +#define SM_GET_JOG_STAT 0x95 /* Request state of JOG inputs */ +#define SM_GET_Z_STAT 0x96 /* Request state of the Channel Z home input */ +#define SM_GET_OUT_STAT 0x97 /* Request the state of the programmable + output bits */ +#define SM_GET_REL_ENC 0x98 /* Request relative encoder count */ +#define SM_GET_REL_ERR 0x99 /* Request relative error from desired + closed loop position */ +#define SM_GET_ABS_ENC 0x9a /* Request absolute encoder count */ +#define SM_GET_SLIP_STAT 0x9b /* Request slip detect status */ +#define SM_GET_RATIO 0x9c /* Request motor pulse to encoder pulse ratio */ +#define SM_GET_RESOLTN 0x9d /* Request motor resolution */ +#define SM_GET_BACK_SIG 0x9e /* Request backlash sigma (motor steps)*/ +#define SM_GET_ALG 0x9f /* Request position maintenance alg. + const, and max velocity */ +#define SM_INT_NXT_MOV 0xa0 /* Interrupt at start of next move */ +#define SM_INT_ALL_MOV 0xa1 /* Interrupt at the start of every move */ +#define SM_INT_NXT_NZ 0xa2 /* Interrupt at constant nonzero velocity + of next move */ +#define SM_INT_ALL_NZ 0xa3 /* Interrupt at constant nonzero velocity + of every move */ +#define SM_INT_NXT_END 0xa4 /* Interrupt at next end of motion */ +#define SM_INT_ALL_END 0xa5 /* Interrupt at every end of motion */ +#define SM_INT_NXT_STL 0xa6 /* Interrupt on next stall detect */ +#define SM_INT_ALL_STL 0xa7 /* Interrupt on every stall detect */ +#define SM_INT_NXT_PLIM 0xa8 /* Interrupt the next time the motor + hits the positive limit */ +#define SM_INT_ALL_PLIM 0xa9 /* Interrupt on every positive limit */ +#define SM_INT_NXT_NLIM 0xaa /* Interrupt the next time the motor + hits the negative limit */ +#define SM_INT_ALL_NLIM 0xab /* Interrupt on every negative limit */ +#define SM_INT_TRG 0xac /* Interrupt on trigger X active */ +#define SM_INT_INHBT 0xaf /* Inhibit all interrupts */ +#define SM_DEF_RATIO 0xb0 /* Define motor pulse to encoder pulse ratio */ +#define SM_DEF_RESOLTN 0xb1 /* Define motor resolution */ +#define SM_DEF_BACK_SIG 0xb2 /* Define backlash sigma (motor steps)*/ +#define SM_DEF_ALG 0xb3 /* Define position maintenance algorithm + const, and max velocity */ +#define SM_DEF_TEETH 0xb4 /* Define the number of rotor teeth */ +#define SM_DEF_DEADBAND 0xb5 /* Def the deadband region in encoder pulses */ +#define SM_DEF_REL_TRP 0xc8 /* Define move X as a relative, + trapezoidal move */ +#define SM_DEF_ABS_TRP 0xcb /* Define move X as an absolute + trapezoidal move */ +#define SM_DEF_CONT 0xce /* Define move X as a continuous move */ +#define SM_DEF_REL_CL 0xd4 /* Define move X, define it as relative, + closed-loop move */ +#define SM_DEF_ABS_CL 0xd5 /* Def move X as an abs, closed- loop move */ +#define SM_DEF_STSTP_VEL 0xd6 /* Define the start/stop velocity */ +#define SM_DEL_MOV_X 0xd7 /* Delete move X */ +#define SM_END_SEQ_DEF 0xd8 /* End definition of sequence buffer */ +#define SM_BEG_SEQ_DEF 0xd9 /* Begin definition of sequence buffer */ +#define SM_DEL_SEQ_X 0xda /* Delete sequence buffer X */ +#define SM_LD_VD_DATA 0xe0 /* Place data into the velocity-distance buffer */ +#define SM_LD_VT_DATA 0xe1 /* Place data into the velocity-time buffer */ +#define SM_GET_FREE_BYT 0xe2 /* Request number of free bytes in vel- + streaming/sequence buffer */ +#define SM_DEF_VS_CMMD 0xee /* Define command to be executed during + the velocity streaming buffer */ +#define SM_GET_NUM_REV 0xfd /* Request software part number and revision */ +#define SM_TEST_SWITCH 0xff /* Perform the test switch function */ + + +#define VELOCITY_MODE 0 +#define MAX_COMMANDS 256 +#define COMPU_INT_LEVEL 5 + +/* array of pointers to stepper motor driver cards present in system */ +struct compumotor *pcompu_motors[MAX_COMPU_MOTORS]; + +LOCAL SEM_ID compu_wakeup; /* compumotor data request task semaphore */ + +/* response variables */ +LOCAL SEM_ID smRespSem; /* task semaphore */ +LOCAL RING_ID smRespQ; /* ring buffer */ +int smRespId; /* task id */ +#define RESP_Q_SZ (RESPBUF_SZ * 50) /* response ring buffer size */ + +/* interrupt buffers */ +unsigned char sm_responses[MAX_COMPU_MOTORS][RESPBUF_SZ]; +unsigned short counts[MAX_COMPU_MOTORS]; + +/* VME memory Short Address Space is set up in gta_init */ +static int *compu_addr; + +/* motor information */ +struct compu_motor{ +short active; /* flag to tell the oms_task if the motor is moving */ +int callback; /* routine in database library to call with status */ +int callback_arg; /* argument to callback routine */ +short update_count; +short mode; +short motor_resolution; +}; +struct compu_motor compu_motor_array[MAX_COMPU_MOTORS]; + +/* Forward reference. */ +VOID compu_sm_reset(); +VOID compu_sm_stat(); + +/* motor status - returned to the database library routines */ +struct motor_data compu_motor_data_array[MAX_COMPU_MOTORS]; + +/* moving status bit descriptions */ +#define CW_LIMIT 0x01 /* clockwise???? limit */ +#define CCW_LIMIT 0x02 /* counter-clockwise???? limit */ +#define DIRECTION 0x08 /* direction bit */ +#define MOVING 0x10 /* moving status bit */ +#define CONSTANT_VEL 0x20 /* constant velocity */ + +/* directions in driver card-ese */ +#define CLKW 0 /* clockwise direction */ +#define CCLKW 1 /* counter clockwise direction */ + +/* + * Code Portions: + * + * smCmdTask Task which writes commands to the hardware + * smRespTask Task which places reponses from the hardware into resp buffers + * sm_intr Interrupt Handler - collects response data from the hardware + * sm_drv_init Initializes all motors, semaphores, ring buffers and interrupts + * sm_driver Subroutine for outside world to issue commands to motors + * motor_select Subroutine to setting callback arg and verifying no other user + * motor_deselect Subroutine to free the motor for other users + * + * Interaction Chart: + * -------------- ------------------- + * / \ / \ + * | smRespTask | | smCmdTask | + * \ / \ / + * --------------- ------------------- + * ^ ^ | + * TAKES | | GETS | + * | | | + * -------------- --------------- | + * Resp Semaphore Response Queue | + * -------------- --------------- | + * ^ ^ | + * GIVES | | PUTS | + * | | | + * --------------- | + * / \ | + * | sm_intr | | + * \ / | + * --------------- | + * ^ reads responses writes commands | + * | from hardware to hardware V + */ + +/* + * COMPU_RESP_TASK + * + * converts readback from the compumotor 1830 cards into a structure that + * is returned to the database library layer every .1 second while a motor + * is moving + */ +compu_resp_task() +{ + unsigned char resp[RESPBUF_SZ]; + register short i; + register struct motor_data *pmotor_data; + + FOREVER { + /* wait for somebody to wake us up */ + semTake (smRespSem, WAIT_FOREVER); + /* the response buffer contains: */ + /* 0 - motor number */ + /* 1 - the command which solicited this response */ + /* 2 - the first byte of the response */ + + /* process requests in the command ring buffer */ + while (rngBufGet(smRespQ,(char *)resp,RESPBUF_SZ) == RESPBUF_SZ){ + pmotor_data = &compu_motor_data_array[resp[0]]; + + /* convert argument */ + switch(resp[1]){ + + case (SM_GET_VEL): + { + register long *pvelocity = (long *)(&resp[3]); + pmotor_data->velocity = *pvelocity; + + break; + } + case (SM_GET_MOV_STAT): + { + register struct compu_motor *pcompu_motor; + register int (*psmcb_routine)(); + + pcompu_motor = &compu_motor_array[resp[0]]; + pmotor_data->moving = (resp[2] & MOVING)?1:0; + pmotor_data->constant_velocity = (resp[2] & CONSTANT_VEL)?1:0; + pmotor_data->cw_limit = (resp[2] & CW_LIMIT)?1:0; + pmotor_data->ccw_limit = (resp[2] & CCW_LIMIT)?1:0; + pmotor_data->direction = (resp[2] & DIRECTION)?1:0; + + /* post every .1 second or not moving */ + if ((pcompu_motor->update_count-- <= 0) + || (pmotor_data->moving == 0)){ + if (pcompu_motor->callback != 0){ + (int)psmcb_routine = pcompu_motor->callback; + (*psmcb_routine)(pmotor_data,pcompu_motor->callback_arg); + } + if (pmotor_data->moving){ + /* motors are reported at 10 Hz */ + pcompu_motor->update_count = 3; + }else{ + pcompu_motor->active = FALSE; + pcompu_motor->update_count = 0; + } + } + break; + } + case (SM_GET_ABS_ENC): + { + register long *pencoder = (long *)(&resp[2]); + pmotor_data->encoder_position = *pencoder; + break; + } + case (SM_GET_Z_REL_POS): + { + register long *pmotor = (long *)(&resp[4]); + pmotor_data->motor_position = *pmotor; + break; + } + case (SM_GET_CUR_DIR): + pmotor_data->direction = (resp[2] == 0xff)?CLKW:CCLKW; + break; + } + } + } +} + +/* Data request commands for the positional and velocity mode motors */ +char compu_velo_reqs[] = { SM_GET_VEL, SM_GET_MOV_STAT }; +#define NUM_VEL_REQS 2 +char compu_pos_reqs[] = { SM_GET_ABS_ENC, SM_GET_Z_REL_POS, SM_GET_MOV_STAT }; +#define NUM_POS_REQS 3 +/* + * COMPU_TASK + * + * task to solicit currnet status from the compumotor 1830 cards while they + * are active + */ +compu_task() +{ + register short inactive_count; + register short card; + register short i; + register struct compumotor *pmotor; + register char *preqs; + + /* inactive motors get monitored once every 2 seconds in case they are */ + /* being moved manually */ + inactive_count = 60; + while(1){ + /* This task is run 30 times a second */ + taskDelay(2); + for (card = 0; card < sm_num_cards[CM57_83E]; card++){ + pmotor = pcompu_motors[card]; + if (pmotor == 0) continue; + if ((compu_motor_array[card].active) + || (inactive_count <=0)){ + if (compu_motor_array[card].mode == VELOCITY_MODE){ + preqs = &compu_velo_reqs[0]; + /* request status data */ + for (i = 0; i < NUM_VEL_REQS; i++,preqs++) + compu_send_msg(pmotor,preqs,1); + }else{ + preqs = &compu_pos_reqs[0]; + /* request status data */ + for (i = 0; i < NUM_POS_REQS; i++,preqs++) + compu_send_msg(pmotor,preqs,1); + } + } + } + if (--inactive_count < 0) inactive_count = 60; + } +} + +/* + * COMPU_INTR + * + * interrupt vector for the compumotor 1830 card + */ +compu_intr(mdnum) +register int mdnum; +{ + register struct compumotor *pmtr; /* memory port to motor card */ + register int key; + + key = intLock(); + + /* pointer to the compumotor card interface */ + pmtr = pcompu_motors[mdnum]; + + /* place the response byte into the appropriate response buffer */ + sm_responses[mdnum][counts[mdnum]] = pmtr->cm_idb; + counts[mdnum]++; + + /* when the buffer is full pass it onto the repsonse task */ + if (counts[mdnum] == RESPBUF_SZ){ + if (rngBufPut(smRespQ,(char *)sm_responses[mdnum],RESPBUF_SZ) != RESPBUF_SZ) + logMsg("smRespQ %d - Full\n",mdnum); + else + semGive (smRespSem); + + /* the zero-th byte is the motor number */ + counts[mdnum] = 1; /* start with command */ + + /* inform the hardware that the response is complete */ + pmtr->cm_cb = RD_LAST; + }else{ + /* inform the hardware there is more to send */ + pmtr->cm_cb = RD_MORE; + } + + intUnlock(key); +} + +/* + * COMPU_DRIVER_INIT + * + * initialization for the compumotor 1830 card + */ +long +compu_driver_init(){ + register short i; + int status; + struct compumotor *pmtr; /* memory port to motor card */ + int cok = CBBR; /*to reset board */ + short none_found; /* flags a steppermotor is present */ + int taskId; + struct compumotor *pmtrb; + + /* intialize each driver which is present */ + none_found = TRUE; + rebootHookAdd((FUNCPTR)compu_sm_reset); + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + sm_addrs[CM57_83E], + (int **)&compu_addr); + if (status != OK){ + printf("%s: failed to map A16 base\n", __FILE__); + return ERROR; + } + + pmtrb = (struct compumotor *)compu_addr; + for (i = 0; i < sm_num_cards[CM57_83E]; i++) { + pmtr = (struct compumotor *)((int)pmtrb + (i<<8)); + + /* initialize when card is present */ + + if (vxMemProbe(&pmtr->cm_cb,WRITE,1,&cok) != ERROR){ + none_found = FALSE; + pcompu_motors[i] = pmtr; /* ptr to interface */ + intConnect((MD_INT_BASE+i)*4,compu_intr,i); /* interrupt enable */ + sysIntEnable(COMPU_INT_LEVEL); + + /* init interrupt receive buffers */ + sm_responses[i][0] = i; /* motor number */ + counts[i] = 1; /* buffer index */ + }else{ + pcompu_motors[i] = 0; /* flags no board is present */ + } + } + if (none_found) return(0); + + /* initialize the response task ring buffer */ + if ((smRespQ = rngCreate(RESP_Q_SZ)) == (RING_ID)NULL) + panic ("sm_init: cmRespQ\n"); + + /* intialize the semaphores which awakens the sleeping * + * stepper motor command task and the stepper motor response task */ + if(!(smRespSem=semBCreate(SEM_Q_FIFO,SEM_EMPTY))) + errMessage(0,"semBcreate failed in compu_driver_init"); + if(!(compu_wakeup=semBCreate(SEM_Q_FIFO,SEM_EMPTY))) + errMessage(0,"semBcreate failed in compu_driver_init"); + + /* spawn the sleeping motor driver command and response tasks */ + smRespId = + taskSpawn("compu_resp_task",SMRESP_PRI,SMRESP_OPT,SMRESP_STACK,compu_resp_task); + taskwdInsert(smRespId,NULL,NULL); + taskId = taskSpawn("compu_task",SMRESP_PRI,SMRESP_OPT,2000,compu_task); + taskwdInsert(taskId,NULL,NULL); + return(0); +} + +short trigger = 0; +/* + * COMPU_DRIVER + * + * driver interface to the database library layer + */ +compu_driver(card,value_flag,arg1,arg2) +register short card; +short value_flag; +register int arg1; +register int arg2; +{ + register int *pint; + register short *pshort; + short j,i; + char compu_msg[20]; + + /* verify the stepper motor driver card is present */ + if ((card < 0) || (card > sm_num_cards[CM57_83E]) || (!pcompu_motors[card])) + return (-1); + + switch (value_flag){ + case (SM_MODE): + /* set the motor mode */ + compu_motor_array[card].mode = arg1; + break; + + case (SM_VELOCITY): + compu_motor_data_array[card].velocity = arg1; + compu_motor_data_array[card].accel = arg2; + + /* set the velocity */ + compu_msg[0] = SM_DEF_VEL_ACC; + compu_msg[1] = 0; /* time is in seconds */ + compu_msg[2] = 0; + pint = (int *)&compu_msg[3]; /* velocity */ + *pint = arg1; + pint++; /* acceleration */ + *pint = arg2; + compu_send_msg(pcompu_motors[card],compu_msg,11); + + break; + + case (SM_MOVE): + if (compu_motor_array[card].mode == VELOCITY_MODE) + return(0); +i = 0; +switch (trigger){ +case (0): + /* move the motor */ + compu_msg[i++] = SM_MOV_REL_POS; + pint = (int *)&compu_msg[i]; + *pint = arg1; + i += 4; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + break; +case (1): /* test sequnce buffer */ + compu_msg[i++] = 0xda; /* delete sequence buffer */ + compu_msg[i++] = 01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xd9; /* fill sequence buffer */ + compu_msg[i++] = 01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = SM_MOV_REL_POS; + pint = (int *)&compu_msg[i]; + *pint = arg1; + i += 4; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xd8; /* end sequence buffer */ + compu_msg[i++] = 0x41; /* perform sequence buffer */ + compu_msg[i++] = 0x01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + break; +case (2): /* test move buffer */ + compu_msg[i++] = 0xc8; + compu_msg[i++] = 0x12; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x04; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x04; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + pint = (int *)&compu_msg[i]; + *pint = arg1; + i += 4; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0x40; + compu_msg[i++] = 0x12; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + break; +case (3): /* test sequence buffer with move buffer */ + compu_msg[i++] = 0xc8; + compu_msg[i++] = 0x12; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x04; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x04; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + pint = (int *)&compu_msg[i]; + *pint = arg1; + i += 4; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xda; /* delete sequence buffer */ + compu_msg[i++] = 01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xd9; /* fill sequence buffer */ + compu_msg[i++] = 01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0x40; + compu_msg[i++] = 0x12; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xd8; /* end sequence buffer */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0x41; /* perform sequence buffer */ + compu_msg[i++] = 0x01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + break; +case (4): /* test sequence buffer with move buffer and trigger */ + compu_msg[i++] = 0xc8; + compu_msg[i++] = 0x12; + + compu_msg[i++] = 0x00; + pint = (int *)&compu_msg[i]; + *pint = compu_motor_data_array[card].velocity; + i += 4; + pint++; + *pint = compu_motor_data_array[card].accel; + i += 4; + pint++; + *pint = arg1; + i += 4; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xda; /* delete sequence buffer */ + compu_msg[i++] = 01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xd9; /* fill sequence buffer */ + compu_msg[i++] = 01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0x2c; /* wait for trigger */ + compu_msg[i++] = 1; /*trigger 1 */ + compu_msg[i++] = 1; /* don't care about state */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0x40; + compu_msg[i++] = 0x12; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xd8; /* end sequence buffer */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0x41; /* perform sequence buffer */ + compu_msg[i++] = 0x01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + break; +} +for (j = 0; j < i; j++){ +printf("%x ",compu_msg[j]); +} +/* compu_send_msg(pcompu_motors[card],compu_msg,i); +*/ + /* set the motor to active */ + compu_motor_array[card].active = TRUE; + + /* wakeup the compu task */ + semGive(compu_wakeup); + + break; + + case (SM_MOTION): + if (arg1 == 0){ + compu_msg[0] = SM_STOP; + compu_send_msg(pcompu_motors[card],compu_msg,1); + }else if (compu_motor_array[card].mode == VELOCITY_MODE){ + compu_msg[0] = SM_MOV_DEFAULT; + compu_msg[1] = arg2; /* direction */ + compu_send_msg(pcompu_motors[card],compu_msg,2); + compu_motor_array[card].active = TRUE; + } + + /* wakeup the compu task */ + semGive(compu_wakeup); + break; + + case (SM_CALLBACK): + /* put the callback routine and argument into the data array */ + i = 0; + if (compu_motor_array[card].callback != 0) return(-1); + compu_motor_array[card].callback = arg1; + compu_motor_array[card].callback_arg = arg2; + break; + + case (SM_SET_HOME): + if (compu_motor_array[card].mode == VELOCITY_MODE) + return(OK); + + /* set the motor and encoder position to zero */ + compu_msg[0] = SM_DEF_ABS_ZERO; + compu_send_msg(pcompu_motors[card],compu_msg,1); + + break; + + case (SM_ENCODER_RATIO): + compu_motor_array[card].motor_resolution = arg1; + + /* set the encoder ratio */ + compu_msg[0] = SM_DEF_RATIO; + pshort = (short *)&compu_msg[1]; + *pshort = arg1; /* motor resolution */ + pshort++; + *pshort = arg2; /* encoder resolution */ + compu_send_msg(pcompu_motors[card],compu_msg,5); + + /* set the motor resolution */ + compu_msg[0] = SM_DEF_RESOLTN; + pshort = (short *)&compu_msg[1]; + *pshort = 0; + pshort++; + *pshort = arg1; /* motor resolution */ + compu_send_msg(pcompu_motors[card],compu_msg,5); + + break; + case (SM_READ): + /* set the motor to active */ + compu_motor_array[card].active = TRUE; + + /* wakeup the compu task */ + semGive(compu_wakeup); + + break; + + } + return (OK); +} + +/* + * COMPU_SEND_MSG + * + * send a message to the compumotor 1830 + */ +int wait_count; +compu_send_msg(pmotor,pmsg,count) +register struct compumotor *pmotor; +register char *pmsg; +register short count; +{ + /* write out this command one byte at a time */ + while (count){ + + /* wait for the driver to be ready */ + while ((pmotor->cm_cb & SBIRDY) == 0){ + taskDelay(0); + wait_count++; + } + + /* next byte in the input data buffer of compumotor */ + pmotor->cm_idb = *pmsg; + pmsg++; + count--; + + /* tell compumotor more or complete */ + if (count == 0){ + pmotor->cm_cb = SND_LAST; + }else{ + pmotor->cm_cb = SND_MORE; + } + } +} + + +/* + * COMPU_SM_IO_REPORT + * + * send a message to the compumotor 1830 + */ + +long compu_sm_io_report(level) + short int level; + { + register int i; + + for (i = 0; i < sm_num_cards[CM57_83E]; i++){ + if (pcompu_motors[i]){ + + printf("SM: CM1830: card %d\n",i); + if (level > 0) + compu_sm_stat(i); + } + } + + return OK; + } + +VOID compu_sm_stat(compu_num) + short int compu_num; + { + struct motor_data *pmotor_data; + printf("\tCW limit = %d\t,CCW limit = %d\tMoving = %d\tDirection = %d\n", + compu_motor_data_array[compu_num].cw_limit, + compu_motor_data_array[compu_num].ccw_limit, + compu_motor_data_array[compu_num].moving, + compu_motor_data_array[compu_num].direction); + + printf("\tConstant Velocity = %d\t, Velocity = %d\t \n", + compu_motor_data_array[compu_num].constant_velocity, + compu_motor_data_array[compu_num].velocity); + + printf("\tAcceleration = %d\tEncoder Position = %d\tMotor Position = %d\n", + compu_motor_data_array[compu_num].accel, + compu_motor_data_array[compu_num].encoder_position, + compu_motor_data_array[compu_num].motor_position); + } + +/* + * + * Subroutine to be called during a CTL X reboot. Inhibits interrupts. + * + */ + +VOID compu_sm_reset() + { + short int i; + char compu_msg[20]; + + for (i = 0; i < sm_num_cards[CM57_83E]; i++){ + if (pcompu_motors[i]){ + compu_msg[0] = SM_INT_INHBT; + compu_send_msg(pcompu_motors[i],compu_msg,1); + } + } + } + diff --git a/src/drv/old/drvDvx.c b/src/drv/old/drvDvx.c new file mode 100644 index 000000000..8a72e2327 --- /dev/null +++ b/src/drv/old/drvDvx.c @@ -0,0 +1,1408 @@ +/* share/src/drv/drvDvx.c + * base/src/drv $Id$ + * + * subroutines which are used to interface with the analogic 2502 + * A/D scanner cards + * + * Author: Matthew Stettler + * Date: 5-23-90 + * + * AT-8 hardware design + * + * Modules: + * + * dvx_driver_init Finds and initializes all 2502 cards + * dvx_dma_init Initializes Am9516 DMA controller + * dvx_driver Reads data from 2502 + * dvx_int Interrupt service routine + * + * Test Routines: + * + * dvx_dump dumps RAM buffer + * dvx_dread command line interface to dvx_driver + * dvx_fempty clears fifo from command line + * dvx_dma_stat displays DMA channel status + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * MS 6/22/90 Modifications to aid in debugging interface to Mizar timing + * system. Interrupt service routine now counts words when clearing + * fifo, dvx_dump provides fifo status, and dvx_fempty added to + * clear fifo from command line. + * + * MS 7/9/90 Modifications to speed up interrupt service routine. + * + * MS 7/23/90 Added DMA control logic. + * + * MS 8/20/90 Added support for interrupt scanned records. + * + * MS 9/20/90 Changed data conversion to offset binary + * (only test routines affected) + * + * JH 07/25/91 added dvx_reset() and dvx_reset() on control X reboot + * + * JH 11/14/91 changed a sysBCLSet to sysIntEnable so ioc_core + * will load into the nat inst cpu030 + * + * JH 11/14/91 removed sysBCLSet enabling STD non priv and super + * access since this is already enabled if we are + * processor 0 + * JH 11/14/91 changed DVX_INTLEV from a mask to a level + * to support use of sysIntEnable() which + * is architecture independent + * BG 4/22/92 added sysBusToLocalAddr() for both short and standard + * addresses for this module. Moved DVX_ADDR0 to + * ai_addrs[DVX2502]in module_types.h. Also moved DVX_IVECO + * to module_types.h. + * BG 6/23/92 combined dvx_driver.c and drvDvx.c + * BG 06/26/92 added level to dvx_io_report in drvDvx structure. + * JH 06/29/92 moved the rest of the dvx io_report here + * BG 7/2/92 removed the semaphores from dvx_io_report + * JH 08/03/92 Removed hkv2f dependent base addr + * JH 08/03/92 moved interrupt vector base to module_types.h + * JH 08/05/92 dvx driver init is called from drvDvx now + * JH 08/10/92 merged dvx private include file into this source + * JH 09/09/92 ran through UNIX C beautifier and converted to ANSI C + * JH 09/09/92 check to see if A24 is open prior to mapping + * the seq ram. + * JH 09/14/92 Made taskDelays() CPU tick rate independent + * tick rate + * JH 09/15/92 made interrupt vector CPU independent + * MRK 09/16/92 Added support for new I/O event scanning + * JH 09/16/92 dont write wordcnt every time the fifo is unloaded + * JH 09/16/92 Use sysLocalToBusAdrs() to translate from an internal + * to a VME A24 address in case the internal base for A24 + * addressed local memory is not on a 16MB boundary. + * sysLocalToBusAdrs() is called for each malloc'ed + * pointer used by the DVX to verify that each one is + * within the portion of local memory mapped to A24. + * JH 09/17/92 one more sysLocalToBusAdrs() address translation + * needed + * JRW 01/18/92 Replaced init code to allow user to select the interrupt + * level value and to select the ports that are to be read. + * MGB 08/04/93 Removed V5/V4 and EPICS_V2 conditionals + * FRL 11/17/93 Added gain parameter to dvx_program + * + * + * NOTE (JRW 11-18-92): + * In a phone conversation with Analogic, Tech support said that when the start + * signal is de-asserted (when using external start/stop mode), the DMAC is + * told to start transferring more data. This is in case the sampling + * completes with less than 512 bytes in the fifo. + * + * In another phone conversation with Analogic, tech support told me that when + * the start signal is de-asserted (when using external start/stop mode), the + * sequence program pointer is reset to 0 (zero). This is fine and dandy, but + * it can cause the mux stage/pipeline to start improperly. Thus causing the + * first data sample to be corrupt. + * + * BUGS: + * The driver should inspect the VXI make and model codes and use a data type + * for the DMA buffer that is appropriate. + * + * $Log$ + * Revision 1.22 1995/01/06 17:03:43 winans + * Added some ifdef'd out test code to see if the end of the sequence program + * could be updated to NOT cause an extra sample from port zero to be taken + * at the end of the sequence. As it turns out, it causes more harm than + * good. There must be something wrong with my understanding on how it works. + * For now, the misfeature of the extra sample at the end of the sequence will + * have to stay. + * + * + */ + +static char *SccsId = "$Id$"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* general constants */ +#define DVX_ID 0xCFF5 /* analogic ID code */ +#define MAX_DVX_CARDS 5 /* max # of 2502 cards per IOC */ +#define MAX_PORTS 3 /* max # of 2601 cards per 2502 */ +#define DVX_DRATE 0xFFEC /* default scan rate of 184 KHz */ +#define DVX_SRATE 0xF201 /* slow scan used for run away mode */ +#define DVX_RAMSIZE 2048 /* sequence RAM size (words) */ +#define DVX_NBUF 1 /* default # of input buffers */ + +/* modes of operation */ +#define INIT_MODE 0 /* initialization mode */ +#define RUN_MODE 1 /* aquisition mode */ + +/* self test constants */ +#define TST_RATE 0x3ED /* self test scan rate */ +#define TST_THRESH 0xD00 /* mux card test threshold reg value */ + +/* csr command bits */ +#define CSR_RESET 0x01 /* software reset */ +#define CSR_M_START 0x10 /* internal scan start */ +#define CSR_M_ETRIG 0x40 /* external trigger enable */ +#define CSR_M_ESTART 0x20 /* external start enable */ +#define CSR_M_SYSFINH 0x02 /* system fail inhibit */ +#define CSR_M_A24 0x8000 /* enable sequence RAM */ +#define CSR_M_INT 0x80 /* interrupt enable */ +#define CSR_M_MXTST 0x3A /* mux card test bits */ + +/* csr status bits */ +#define S_NEPTY 0x02 /* fifo not empty when = 1 */ + +/* Sequence Program control codes */ +#define GAIN_CHANNEL 0x80 +#define ADVANCE_TRACK 0x40 +#define ADVANCE_HOLD 0xC0 +#define RESTART 0x00 + +/* analogic 2502 memory structure */ +struct dvx_2502 +{ + unsigned short dev_id; /* device id code (CFF5) */ + unsigned short dev_type; /* type code (B100) */ + unsigned short csr; /* control and status register */ + unsigned short seq_offst; /* sequence RAM offset register */ + unsigned short mem_attr; /* memory attribute register */ + unsigned short samp_rate; /* sample rate register */ + unsigned short dma_point; /* DMA pointer register */ + unsigned short dma_data; /* DMA data register */ + unsigned short thresh; /* threshold register */ + unsigned short fifo; /* input fifo */ + unsigned short end_pad[54]; /* pad to 64 byte boundary */ +}; + +/* input buffer */ +struct dvx_inbuf +{ + struct dvx_inbuf *link; /* link to next buffer */ + int wordcnt; /* # of words read to clear fifo */ + short *data; /* data buffer */ +}; + +/* analogic 2502 control structure */ +struct dvx_rec +{ + struct dvx_2502 *pdvx2502; /* pointer to device registers */ + short *sr_ptr; /* pointer to sequence RAM */ + struct dvx_inbuf *inbuf; /* pointer to current buffer */ + short unsigned csr_shadow; /* csr shadow register */ + short mode; /* operation mode (init or run) */ + int int_vector; /* interrupt vector */ + int intcnt; /* interrupt count # */ + int cnum; /* card number */ + + int dmaSize; /* samples to read before IRQ */ + unsigned int numChan; /* total number of ports to read */ + unsigned long pgmMask[8]; /* ports to be read by seq-program */ + unsigned short gain[8]; /* port gains */ + + int RearmMode; /* zero if auto-rearm, else manual */ + + IOSCANPVT *pioscanpvt; +}; + +/* dma chain table size */ +#define DVX_CTBL 34 /* max size of chain table */ + +/* am9516 register select constants. + * The DMA control registers are accessed through the dvx2502 registers + * dma_point and dma_data. The constants below are the addresses which must + * be loaded into the pointer register to access the named register through + * the data register. All dual registers are commented as #2. To access channel + * #1, OR the value M_CH1 with the channel #2 address. + */ +#define DMA_MMR 0x38 /* master mode register */ +#define DMA_CSR 0x2C /* command/status register #2 */ +#define DMA_CARAH 0x18 /* current address reg A high #2 */ +#define DMA_CARAL 0x08 /* current address reg A low #2 */ +#define DMA_CARBH 0x10 /* current address reg B high #2 */ +#define DMA_CARBL 0x00 /* current address reg B low #2 */ +#define DMA_BARAH 0x1C /* base address reg A high #2 */ +#define DMA_BARAL 0x0C /* base address reg A low #2 */ +#define DMA_BARBH 0x14 /* base address reg B high #2 */ +#define DMA_BARBL 0x04 /* base address reg B low #2 */ +#define DMA_COC 0x30 /* current operation count #2 */ +#define DMA_BOC 0x34 /* base operation count #2 */ +#define DMA_ISR 0x28 /* interrupt save register #2 */ +#define DMA_IVR 0x58 /* interrupt vector register #2 */ +#define DMA_CMRH 0x54 /* channel mode register #2 */ +#define DMA_CMRL 0x50 /* channel mode register #2 */ +#define DMA_CARH 0x24 /* chain address reg high #2 */ +#define DMA_CARL 0x20 /* chain address reg low #2 */ +#define M_CH1 0x2 /* mask for chan 1 reg addresses */ + +/* am9516 command constants + * All dual commands are commented as #1. To command channel #2, OR the + * valur M_CH2 with the channel #1 command. + */ +#define MMR_ENABLE 0x0D /* chip enable value */ +#define CMR_RESET 0x0 /* reset all channels */ +#define CMR_START 0xA0 /* start channel #1 */ +#define CMR_SSR 0x42 /* set software request #1 */ +#define CMR_CSR 0x40 /* clear software request #1 */ +#define CMR_SHM 0x82 /* set hardware mask #1 */ +#define CMR_CHM 0x80 /* clear hardware mask #1 */ +#define CMR_SC 0x22 /* set CIE/IP #1 */ +#define CMR_CC 0x20 /* clear CIE/IP #1 */ +#define CMR_SFB 0x62 /* set flip bit #1 */ +#define CMR_CFB 0x60 /* clear flip bit #1 */ +#define M_CIE 0x10 /* int enable bit mask (SC/CC cmd) */ +#define M_IP 0x4 /* int pending bit mask (SC/CC cmd) */ +#define M_CH2 0x1 /* mask for channel #2 commands */ + +/* am9516 chain reload constants */ +#define R_CAR 0x1 /* chain address */ +#define R_CMR 0x2 /* channel mode */ +#define R_IVR 0x4 /* interrupt vector */ +#define R_PMR 0x8 /* pattern and mask */ +#define R_BOC 0x10 /* base operation count */ +#define R_BAB 0x20 /* base address register B */ +#define R_BAA 0x40 /* base address register A */ +#define R_COC 0x80 /* current operation count */ +#define R_CAB 0x100 /* current address register B */ +#define R_CAA 0x200 /* current address register A */ + +/* If any of the following does not exist replace it with #define <> NULL */ +long dvx_io_report(int level); +static long dvx_driver_init(void); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvDvx={ + 2, + dvx_io_report, + dvx_driver_init}; + +static struct dvx_rec dvx[MAX_DVX_CARDS] = { +{ NULL, NULL, NULL, -1, -1, -1, -1, -1, 128, 0, {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff} +,{0, 0, 0, 0, 0, 0, 0, 0}, +0 , NULL +}, +{ NULL, NULL, NULL, -1, -1, -1, -1, -1, 128, 0, {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff} +,{0, 0, 0, 0, 0, 0, 0, 0}, +0, NULL +}, +{ NULL, NULL, NULL, -1, -1, -1, -1, -1, 128, 0, {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff} +,{0, 0, 0, 0, 0, 0, 0, 0}, +0, NULL +}, +{ NULL, NULL, NULL, -1, -1, -1, -1, -1, 128, 0, {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff} +,{0, 0, 0, 0, 0, 0, 0, 0}, +0, NULL +} +}; + +static int DVX_INTLEV=5; /* allow this to be user setable */ +static int dvxOnline = 0; /* 1 after init invoked */ + +#ifdef __STDC__ +int lclToA24(void *pLocal, void **ppA24); +static void dvx_reset(void); +static void dvx_int(struct dvx_rec *dvxptr); +static int muxtst(int card); +static int sramld(int card); +static int dvx_driver( int card, int chan, short *pval); +int dvx_dread(int card,int chan); +int dvx_dump(int card,int firstchan,int lastchan); +int dvx_chan_print(int dvx_card, int firstchan, int lastchan); +static int dvx_fempty(int card); +static int dvx_dma_init(struct dvx_rec *ptr); +static int dvx_dma_reset(struct dvx_rec *ptr); +int dvx_dma_stat(int card, int chan); +#else /* __STDC__ */ + +int lclToA24(); +static void dvx_reset(); +static void dvx_int(); +static int muxtst(); +static int sramld(); +int dvx_driver(); +int dvx_dread(); +int dvx_dump(); +int dvx_chan_print(); +static int dvx_fempty(); +static int dvx_dma_init(); +static int dvx_dma_reset(); +int dvx_dma_stat(); + + +#endif /* __STDC__ */ + + +int dvxDebug = 0; + + +/* + * dvx_int + * + * interrupt service routine + * + */ +LOCAL void +dvx_int(struct dvx_rec *dvxptr) +{ + /* BUG --- why are there STATIC variables in here????? */ + + static short i, junk; + register struct dvx_2502 *cptr; + static unsigned int tick, t, intlev; + + cptr = dvxptr->pdvx2502; + cptr->dma_point = DMA_CSR; + cptr->dma_data = CMR_CC | M_IP | M_CH2; /* clear dma int channel #2 */ + cptr->csr = dvxptr->csr_shadow & 0xff5f; /* disable fifo interrupts */ + switch (dvxptr->mode) + { + /* + * interrupt recieved during initialization + * - empty fifo and throw away data + */ + case INIT_MODE: + if(dvxDebug) + logMsg("dvx_int: INIT_MODE\n"); + dvxptr->intcnt = 0; /* initialize interrupt count */ + for (i = 0; cptr->csr & 0x2; i++) + junk = cptr->fifo; + break; + /* + * interrupt recieved during data aquisition + * - empty fifo into next input buffer, then make it current + */ + case RUN_MODE: + if(dvxDebug) + logMsg("dvx_int: RUN_MODE\n"); + dvxptr->intcnt++; /* incriment interrupt count */ + + dvxptr->inbuf = dvxptr->inbuf->link; /* update current data buffer */ + + for (i = 0; cptr->csr & S_NEPTY; i++, junk = cptr->fifo); + + dvxptr->inbuf->wordcnt = i; /* record # of words to clear fifo */ + + if (dvxptr->RearmMode == 0) + { + /* enable DMA opeations */ + cptr->dma_point = DMA_CSR; + + /* enable int channel #2 */ + cptr->dma_data = CMR_SC | M_CIE | M_CH2; + + /* start channel #2 */ + cptr->dma_data = CMR_START | M_CH2; + } + + scanIoRequest(*(dvxptr->pioscanpvt)); + break; + } + cptr->csr = dvxptr->csr_shadow; +} + +int dvx_RearmModeSet(int card, int mode) +{ + /* make sure hardware exists */ + if ((card >= ai_num_cards[DVX2502]) || (card < 0)) + return(-1); + + dvx[card].RearmMode = mode; + return(0); +} + +int dvx_rearm(int card) +{ + struct dvx_rec *dvxptr; + struct dvx_2502 *cptr; + int i; + short junk; + + /* make sure hardware exists */ + if ((card >= ai_num_cards[DVX2502]) || (card < 0)) + return(-1); + else if(dvx[card].pdvx2502 == NULL) + return(-2); + + dvxptr = &dvx[card]; + cptr = dvxptr->pdvx2502; + + if (dvxptr->RearmMode == 0) + return(-3); + +#if 0 + cptr = dvxptr->pdvx2502; + cptr->dma_point = DMA_CSR; + cptr->dma_data = CMR_CC | M_IP | M_CH2; /* clear dma int channel #2 */ + cptr->csr = dvxptr->csr_shadow & 0xff5f; /* disable fifo interrupts */ +#endif + + /* Drain the fifo of any crud */ + for (i = 0; cptr->csr & S_NEPTY; i++, junk = cptr->fifo); + + if (dvxDebug) + printf("dvx_rearm(%d) fifo residual = %d\n", card, i); + + /* enable DMA opeations */ + cptr->dma_point = DMA_CSR; + + /* enable int channel #2 */ + cptr->dma_data = CMR_SC | M_CIE | M_CH2; + + /* start channel #2 */ + cptr->dma_data = CMR_START | M_CH2; + + return(0); +} + + +/* + * dvx_driver_init + * + * initialization for 2502 cards + * + */ +LOCAL long dvx_driver_init(void) +{ + int i; + int j; + int status; + unsigned short card_id; + short *ramptr; + struct dvx_inbuf *ibptr; + struct dvx_inbuf *ibptra; + int intvec = DVX_IVEC0; + struct dvx_2502 *pDvxA16; + short *pDvxA24; + short *pDvxA24Bus; + + /* + * dont continue DMA while vxWorks is control X + * rebooting (and changing bus arbitration mode) + * joh 072591 + */ + rebootHookAdd(dvx_reset); + + dvxOnline = 1; /* do not allow any more user config modifications */ + + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + ai_addrs[DVX2502], + &pDvxA16); + if (status != OK){ + logMsg( "%s: A16 base addr problems DVX 2502\n", + __FILE__); + return ERROR; + } + + + /* + * search for DVX cards + */ + for ( i = 0, pDvxA24Bus = (short *)ai_memaddrs[DVX2502]; + i < ai_num_cards[DVX2502]; + i++, pDvxA16++, pDvxA24Bus += DVX_RAMSIZE) + { +# ifdef DEBUG + logMsg("Probing for DVX at %x\n", pDvxA16); +# endif + + dvx[i].pdvx2502 = NULL; + status = vxMemProbe ( + &pDvxA16->dev_id, + READ, + sizeof(card_id), + &card_id); + if (status <0){ + continue; + } + if (card_id != DVX_ID){ /* see if detected card is a 2502 */ + logMsg("%s: Card installed at addr=0X%x is not a dvx2502\n", + __FILE__, + pDvxA16); + continue; + } + + /* Card found! Finish the init for it. */ + + dvx[i].cnum = i; /* record card number */ + pDvxA16->csr = CSR_RESET; /* software reset */ + + status = sysBusToLocalAdrs( + VME_AM_STD_SUP_DATA, + pDvxA24Bus, + &pDvxA24); + if (status != OK){ + logMsg( "%s: A24 base addr problems DVX 2502 A24=%x\n", + __FILE__, + pDvxA24Bus); + continue; + } + + /* + * check for incorrectly addressed card in A24 + */ + status = vxMemProbe (pDvxA24, READ, sizeof(card_id), &card_id); + if (status == OK){ + logMsg( "%s: A24 responding where DVX should be addr=%x\n", + __FILE__, + pDvxA24); + logMsg( "%s: DVX card=%d ignored\n", + __FILE__, + i); + continue; + } + + + if ((dvx[i].pioscanpvt = (IOSCANPVT *) malloc(sizeof(IOSCANPVT))) == NULL) + return(-1); + if (dvx[i].dmaSize == 0) + { + logMsg("%s: No channels selected on card %d, init aborted\n", i); + continue; + } + + dvx[i].mode = INIT_MODE; /* initialization mode */ + /* create linked list of input buffers */ + for ( j = 0, ibptra = NULL; + j < DVX_NBUF; + j++, ibptra = ibptr) + { + + ibptr = (struct dvx_inbuf *)malloc(sizeof (struct dvx_inbuf)); + /* + * exit with error if buffer allocation fails + */ + if (ibptr == NULL) + return -1; /* unsuccessfull */ + + /* Needs to come from A24 mappable memory...? */ +#if 0 + if ((ibptr->data = (short *) malloc(dvx[i].dmaSize * sizeof(short))) == NULL) +#else + if ((ibptr->data = (short *) devLibA24Malloc(dvx[i].dmaSize * sizeof(short))) == NULL) +#endif + return(-1); + + if (dvxDebug) + printf("dvx_driver_init(%d) allocated ibp at %p buffer at %p\n", i, ibptr, ibptr->data); + if (j == 0){ + dvx[i].inbuf = ibptr; /* initialize if first */ + } + + ibptr->wordcnt = 0; + ibptr->link = ibptra; /* LINK TO last buffer */ + } + dvx[i].inbuf->link = ibptr; /* close list */ + + /* + * locate sequence RAM in an unassigned portion of VME A24 + * + * Use the A24 bus address since the processor may not have placed + * A24 on a 16 MB boundary + */ + pDvxA16->seq_offst = (int)pDvxA24Bus>>8; + dvx[i].csr_shadow = CSR_M_A24; /* enable sequence RAM (shadow csr) */ + pDvxA16->csr = dvx[i].csr_shadow; /* enable sequence RAM */ + + /* + * record card address and allocate input buffers + */ + dvx[i].pdvx2502 = pDvxA16; /* record card address */ + + /* + * locate and enable sequence RAM + */ + dvx[i].sr_ptr = pDvxA24; /* record seq RAM address */ + + /* + * set up interrupt handler + */ + dvx[i].csr_shadow |= (intvec<<8); /* load int vector (shadow csr) */ + pDvxA16->csr = dvx[i].csr_shadow; /* load int vector */ + dvx[i].int_vector = intvec; /* save interrupt vector # */ + status = intConnect(INUM_TO_IVEC(intvec),dvx_int,&dvx[i]); + if (status != OK) + return -2; /* abort if can't connect */ + sysIntEnable(DVX_INTLEV); /* enable interrupt level */ + + /* make sure the DMA chip is fully disabled */ + dvx_dma_reset(dvx[i].pdvx2502); + + dvx[i].csr_shadow |= CSR_M_INT; /* enable fifo int (shadow csr) */ + pDvxA16->csr = dvx[i].csr_shadow; /* enable fifo interrupts */ + intvec++; /* advance to next vector # */ + + /* + * test mux cards and load sequence RAM + */ + muxtst(i); /* test mux cards */ + sramld(i); /* load scan program */ + + dvx[i].csr_shadow ^= CSR_M_INT; /* disable fifo int (shadow csr) */ + pDvxA16->csr = dvx[i].csr_shadow; /* disable fifo interrupts */ + /* + * initialize DMA + */ + dvx_dma_init(&dvx[i]); /* initialize DMA controller */ + /* + * set scan rate and enable external start + */ + pDvxA16->samp_rate = DVX_DRATE; /* scan rate of 184 KHz */ + dvx[i].csr_shadow |= CSR_M_ESTART; /* enable ext start (shadow csr) */ + pDvxA16->csr = dvx[i].csr_shadow; /* enable external start */ + dvx[i].mode = RUN_MODE; /* ready to aquire data */ + scanIoInit(dvx[i].pioscanpvt); + } + + return 0; /* return 0 to database */ +} + + +/* + * muxtst + * + * test multiplexer cards + * I suspect this test does nothing more than light the pass LED on all + * the 2601 multiplexer cards. + * + */ +LOCAL int muxtst(int card) +{ + int i; + short *ramptr; + + /* + * inhibit sys fail and load test setup parameters + */ + dvx[card].pdvx2502->csr = dvx[card].csr_shadow | CSR_M_SYSFINH; + ramptr = dvx[card].sr_ptr; /* pointer to sequence RAM */ + dvx[card].pdvx2502->thresh = TST_THRESH; /* load test threshold */ + dvx[card].pdvx2502->samp_rate = TST_RATE; /* test sample rate */ + /* + * load test program into sequence RAM + */ + for (i = 0; i < MAX_PORTS; i++) + { + *ramptr++ = GAIN_CHANNEL; /* first sequence RAM value */ + *ramptr++ = ADVANCE_HOLD | i; /* mux card select */ + } + *ramptr++ = RESTART; /* end of scan */ + *ramptr = RESTART; + /* + * run test and restore csr + */ + dvx[card].pdvx2502->csr = dvx[card].csr_shadow | CSR_M_MXTST; + taskDelay(sysClkRateGet()); /* let test run */ + dvx[card].pdvx2502->csr = dvx[card].csr_shadow; + dvx[card].pdvx2502->thresh = 0; /* restore threshold */ +} + +/* + * dvx_program() is used to define what ports on what boards to scan + * when taking a sample. Each time a start pulse is supplied to the DVX + * board, it will read from the ports in the specified by the user. When + * dmaSize samples have been taken (possibly after many start pulses) the DMA + * controller will terminate transferring data and generate a completion event. + * After this event, the whole process starts over again with the next start + * pulse. + * + * The dvx_program() parms are simply a bit mask of what ports to read from + * each board. There may be up to 8 boards on one DVX master. The bit masks + * are 32 bit unsigned numbers that should be assigned during the startup + * script when booting the IOC. + * + * To program a DVX card (card 0) to read all ports from 2 S/H muxes (boards 2 + * and 6) and 1 mux (board 1). You may do the following. + * + * dvx_program(0, 2, 0x0000ffff, 0, 0) -- 16 ports from board 2 gain = 1 + * dvx_program(0, 6, 0x0000ffff, 0, 2) -- 16 ports from board 6 gain = 4 + * dvx_program(0, 1, 0xffffffff, 64, 3) -- 32 ports from board 1 gain = 8 + * + * The 64 on the last line specified that we want to read 64 samples before the + * DMA is considered complete. The last dvx_program() value for the dma size + * is the only one that is used, all previous are discarded. + * + * If so desired, the above example could have used 128 for the DMA size value. + * in this case, the dvx 'system' would not consider the sample complete until + * all the ports were read twice. + * + * NOTE: Each card has its own notion of DMA size. Each card also has its + * own notion of default values. If no programming is done for a + * specific card number, its default values will be used. + * + * The ports are read from the lowest board number and lowest port number first. + */ +int +dvx_program(int card, int board, unsigned long mask, int dmaSize, int gain) +{ + int i; + unsigned long maskCheck; + int numSamp; + static int firstTime = 1; + + if (dvxOnline) + { + printf("DVX cards are already on line, no modifications allowed\n"); + return(-1); + } + if ((card < 0) || (card > ai_num_cards[DVX2502])) + { + printf("dvx_program(%d, %d, 0x%08.8X): invalid card number specified\n", card, board, mask); + return(1); + } + if ((board < 0) || (board > 7)) + { + printf("dvx_program(%d, %d, 0x%08.8X): invalid board number specified\n", card, board, mask); + return(2); + } + if ((gain < 0) || (gain > 3)) + { + errPrintf(-1, __FILE__, __LINE__, + "dvx_program(%d, %d, 0x%08.8X, %d, %d): invalid gain specified\n", card, board, + mask, dmaSize, gain); + return(2); + } + if (firstTime) + { /* Clear out the default port numbers, this is the first dvx_program call */ + int i; + + firstTime=0; + for (i=0; i<8; i++) + dvx[card].pgmMask[i] = 0; + } + + dvx[card].pgmMask[board] = mask; + dvx[card].dmaSize = dmaSize; + dvx[card].gain[board] = gain; + + return(0); +} + +/* + * Allow the user to specify the interrupt level number + * + * It returns the 'old' IRQ level value. + * + * NOTE that off the shelf DVX2502s are set to use IRQ level 1 + */ + +int dvx_setIrqLevel(int level) +{ + int i; + + if (dvxOnline) + { + printf("DVX card(s) already initialized at level %d, new IRQ level ignored\n", DVX_INTLEV); + return(DVX_INTLEV); + } + i = DVX_INTLEV; + DVX_INTLEV = level; + return(i); +} + +/* + * This can be called by a user program to get information about how + * the dvx card is programmed. + */ +int dvx_getProgram(int card, int *dmaSize, int *numChan) +{ + *dmaSize = dvx[card].dmaSize; + *numChan = dvx[card].numChan; + + if (dvxDebug) + printf("total DMA samples=%d, total number of physical channels=%d\n", *dmaSize, *numChan); + + return(-1); +} + +/* + * This version of the sequence program loader allows the number of channels + * to be programmable. It is assumed that the programmable constants will + * be fetched from the user. + * + */ +LOCAL +int sramld(int card) +{ + short *ramptr; + int i, port, firstPort; + unsigned long mask; + + /* load sequence program */ + ramptr = dvx[card].sr_ptr; /* point to sequence RAM */ + dvx[card].numChan = 0; + + for (i=0; i<8; i++) + { + mask = 1; + port = 0; + firstPort = -1; + + while(port < 32) + { + if (mask & dvx[card].pgmMask[i]) + { /* I need to read a sample from this port */ + if (firstPort == -1) + { /* save this one for prescan */ + firstPort = port; + } + else + { + *ramptr++ = GAIN_CHANNEL | (dvx[card].gain[i] <<3) | ((port >> 3) & 3); + *ramptr++ = ADVANCE_HOLD | ((port & 0x07) << 3) | i; + dvx[card].numChan++; + if (dvxDebug) + printf("board %d, port %d\n", i, port); + } + } + mask <<= 1; + port++; + } + if (firstPort != -1) + { /* Put the first port number to read on each board, last in scan list. */ + *ramptr++ = GAIN_CHANNEL | (dvx[card].gain[i] << 3) | ((firstPort >> 3) & 3); + *ramptr++ = ADVANCE_HOLD | ((firstPort & 0x07) << 3) | i; + dvx[card].numChan++; + if (dvxDebug) + printf("board %d, port %d\n", i, firstPort); + } + } + if (dvxDebug) + printf("Total channels read in %d\n", dvx[card].numChan); + +#if 1 /* This causes an extra sample to be taken at the end of the scan */ + *ramptr++ = 0; /* mark the end of the sequence program */ + *ramptr++ = 0; +#else /* This was supposed to get rid of the extra one, but does not work */ + *(ramptr-1) &= 0xff3f; + *ramptr = *(ramptr-1); +#endif + + /* set scan rate and run it once */ + dvx[card].pdvx2502->samp_rate = DVX_DRATE; + dvx[card].pdvx2502->csr = dvx[card].csr_shadow | CSR_M_START; + taskDelay(sysClkRateGet()); /* let scan run */ + dvx[card].pdvx2502->csr = dvx[card].csr_shadow; /* restore csr */ +} + +/* + * dvx_driver + * + * interface to analog input buffer + * + */ +int dvx_driver( +int card, +int chan, +short *pval +) +{ + short ival; + + if ((card >= ai_num_cards[DVX2502]) || (card < 0)) /* make sure hardware exists */ + return -1; + else if (dvx[card].pdvx2502 == NULL) + return -2; + else if (chan > dvx[card].dmaSize) + return -2; + + *pval = dvx[card].inbuf->data[chan]; + return 0; +} + +/* + * dvxReadWf + * + * Allows a waveform record to read all samples from the dvx buffer as a + * waveform. + */ +int dvxReadWf(int card, int start, int num, short *pwf, unsigned long *numRead) +{ + int dataIndex; + + if(dvxDebug) + printf("dvxReadWf(%d, %d, %d, 0x%08.8X, 0x%08.8X)\n", card, start, num, pwf, numRead); + + *numRead = 0; /* in case we have an error condition */ + + /* make sure hardware exists */ + if ((card >= ai_num_cards[DVX2502]) || (card < 0)) + return(-1); + else if ((dvx[card].pdvx2502 == NULL)||(start > dvx[card].dmaSize)|| + (start < 0)||(num < 1)) + return(-2); + + /* if user asked for too many, chop off the length to that available */ + if (start+num > dvx[card].dmaSize) + num -= (start+num) - dvx[card].dmaSize; + + dataIndex = start+num; + *numRead = num; + + if (dvxDebug) + printf("dvxReadWf(): Actual elements read: %d\n", num); + + while(num) + { + num--; + dataIndex--; + pwf[num] = dvx[card].inbuf->data[dataIndex]; + } + return(0); +} + + +/* + * dvx_dread + * + * stand alone interface to dvx_driver + * + */ +int dvx_dread(int card,int chan) +{ + short stat; + short unsigned data; + float volts; + + stat = dvx_driver(card,chan,(short *)&data); + volts = data * 10./32767. - 10; + printf("channel # %d\tdata = %x\tvolts = %f\n" + ,chan,data,volts); +} + + +/* + * dvx_dump + * + * dump RAM buffer + * + */ +int dvx_dump(int card,int firstchan,int lastchan) +{ + int i, port, ix, printing, tmp; + short unsigned data; + unsigned long mask; + float volts; + + printf("Entering dvx_dump with card = %d,firstchan = %d,lastchan = %d\n",card,firstchan, + lastchan); + if ((card >= ai_num_cards[DVX2502]) || (card < 0)) + return -1; + else if (dvx[card].pdvx2502 == 0) + return -2; + printf("buffer address = %x word count = %d interrupt count = %d\n", + dvx[card].inbuf,dvx[card].inbuf->wordcnt,dvx[card].intcnt); + if (dvx[card].pdvx2502->csr & 0x2) + printf("fifo status = not empty,"); + else + printf("fifo status = empty,"); + printf(" current input channel = %x\n", + (dvx[card].pdvx2502->csr & 0x3fc0)>>6); + + ix = 0; + printing= 1; + while ((ix < dvx[card].dmaSize) && (ix < lastchan) && printing) + { + printing = 0; + for (i=0; i<8; i++) + { + mask = 1; + port = 0; + while(port < 32) + { + if (mask & dvx[card].pgmMask[i]) + { + if (ix >= firstchan) + { + tmp = dvx[card].inbuf->data[ix]; + tmp &= 0x0000ffff; + + volts = tmp * 10./32767.; + printf("signal %2d, board %d, port %2d, data 0x%04.4X, voltage %f\n", ix, i, port, tmp, volts); + } + + ix++; + printing = 1; + } + mask <<= 1; + port++; + } + } + } + + printf("end of list\n"); + + return 0; +} + +dvx_getioscanpvt(int card, IOSCANPVT *scanpvt) +{ + if ((card >= ai_num_cards[DVX2502]) || (card < 0))return(0); + if (dvx[card].pdvx2502 == 0) return(0); + *scanpvt = *(dvx[card].pioscanpvt); + return(0); +} + +/* + * + * dvx_io_report + * + * +*/ +long dvx_io_report(int level) +{ + short int i; + unsigned short card_id; + + for (i = 0; i < ai_num_cards[DVX2502]; i++){ + if (!dvx[i].pdvx2502) + continue; + + /* If detected card is a 2502 print out its number. */ + printf("AI: DVX2505:\tcard %d\n",i); + + if(level > 0 ){ + int firstchan; + int lastchan; + + printf("Enter number of the first channel you wish to read:\n"); + scanf("%d",&firstchan); + printf("First channel is %d\n",firstchan); + printf("Enter number of the last channel you wish to read:\n"); + scanf("%d",&lastchan); + printf("Last channel is %d\n",lastchan); + dvx_dump(i,firstchan,lastchan); + } + } + + return OK; +} + + + +/* + * dvx_fempty + * + * empty fifo + * + */ +LOCAL int dvx_fempty(int card) +{ + int i, junk; + + if ((card>= ai_num_cards[DVX2502]) || (card < 0)) + return -1; + else if (dvx[card].pdvx2502 == 0) + return -2; + for (i = 0; dvx[card].pdvx2502->csr & 0x2; i++) + junk = dvx[card].pdvx2502->fifo; + printf("%d words read from fifo\n",i); + return 0; +} + + +LOCAL dvx_dma_reset(struct dvx_2502 *dev) +{ + dev->dma_point = DMA_CSR; + dev->dma_data = CMR_RESET; /* reset the thing */ + return(0); +} + +/* + * dvx_dma_init + * + * am9516 DMA controller initialization + * + * local to A24 bus addr conversions below are necessary on processors + * that dont place the local base for A24 on an even 16 MB boundary + */ +LOCAL dvx_dma_init(struct dvx_rec *ptr) +{ + int i, j; + int status; + short *cptr, *cptra, *cptr0, *pext; + short *BusPtr; + struct dvx_2502 *dev; + struct dvx_inbuf *bpnt; + + dev = ptr->pdvx2502; /* point to hardware */ + + dvx_dma_reset(dev); /* reset the DMA chip */ + + /* build chain table, needs to come from A24 mappable memory */ +#if 0 + if ((cptr = cptr0 = (short *)malloc(DVX_CTBL)) == NULL) +#else + if ((cptr = cptr0 = (short *)devLibA24Malloc(DVX_CTBL)) == NULL) +#endif + return -1; + dev->dma_point = DMA_MMR; /* enable chip */ + dev->dma_data = MMR_ENABLE; + + /* + * The 2502 uses A24 priv data VME addr mods + * for its DMAchain operations + */ + status = sysLocalToBusAdrs( + VME_AM_STD_SUP_DATA, + cptr, + &BusPtr); + if(status < 0){ + logMsg( "%s:Local chain addr 0x%X does not map to A24 bus addr\n", + __FILE__, + cptr); + return -1; + } + dev->dma_point = DMA_CARH; /* load init chain address */ + dev->dma_data = (int) BusPtr>>8 & 0xff00; + dev->dma_point = DMA_CARL; + dev->dma_data = (int) BusPtr & 0xffff; + + for ( i = 0, bpnt = ptr->inbuf->link; + i < DVX_NBUF; + i++, cptr = cptra, bpnt = bpnt->link) + { /* create chain for each input buffer */ + if ((i + 1) == DVX_NBUF) + cptra = cptr0; /* close list */ + else + /* needs to come from A24 mapped memory */ +#if 0 + if ((cptra = (short *)malloc(DVX_CTBL)) == NULL) +#else + if ((cptra = (short *)devLibA24Malloc(DVX_CTBL)) == NULL) +#endif + return -1; /* allocate next chain */ + + /* Set the reload word */ + *cptr++ = R_CAA | R_CAB | R_COC | R_CMR | R_CAR; /* load mask */ + + /* + * + * source options: + * 1) data (not chain) operation + * 2) hold (dont auto incr or decr) the src address + * + * The src addr here is ignored by the dvx2502 hardware + * and is therefore set to zero. The source is always + * the dvx 2502 fifo. + */ + *cptr++ = 0xd0; + *cptr++ = 0; /* address reg A (src) */ + + /* + * The 2502 uses A24 non-priv data VME addr mods + * for its DMA data transfers + */ + status = sysLocalToBusAdrs( + VME_AM_STD_USR_DATA, + bpnt->data, + &BusPtr); + if(status < 0){ + logMsg( "%s: Local dest addr 0x%X does not map to A24 addr\n", + __FILE__, + bpnt->data); + return -1; + } + *cptr++ = (((int)BusPtr>>8) & 0xff00) | 0xc0; + *cptr++ = (int) BusPtr & 0xffff; /* address reg B (dest) */ + + *cptr++ = ptr->dmaSize; /* operation count */ + *cptr++ = 0x4; + *cptr++ = 0x0252; /* dma mode control */ + + /* + * The 2502 uses A24 priv data VME addr mods + * for its DMA chain operations + */ + status = sysLocalToBusAdrs( + VME_AM_STD_SUP_DATA, + cptra, + &BusPtr); + if(status < 0){ + logMsg( "%s:Local addr 0x%X does not map to A24 addr\n", + __FILE__, + cptra); + return -1; + } + *cptr++ = (int)BusPtr>>8 & 0xff00; + *cptr = (int)BusPtr& 0xffff; /* next chain address */ + } + /* enable DMA opeations */ + dev->dma_point = DMA_CSR; + dev->dma_data = CMR_SC | M_CIE | M_CH2; /* enable int channel #2 */ + dev->dma_data = CMR_START | M_CH2; /* start channel #2 */ + return 0; +} + + +/* + * dvx_dma_stat + * + * reads status of dma channel + * + */ +int dvx_dma_stat(int card, int chan) +{ + struct dvx_2502 *ptr; + short unsigned temp; + + if ((card < 0) || (card > ai_num_cards[DVX2502])) + return -1; + else if (dvx[card].pdvx2502 == 0) + return -2; + else + { + ptr = dvx[card].pdvx2502; + temp = (chan & 0x1)<<1; + ptr->dma_point = DMA_CSR | temp; + printf("dma status = %X\n",ptr->dma_data); + ptr->dma_point = DMA_MMR; + printf("master mode register = %X\n",ptr->dma_data); + ptr->dma_point = DMA_CARH | temp; + printf("chain address = %4.4X",ptr->dma_data); + ptr->dma_point = DMA_CARL | temp; + printf("%4.4X\n",ptr->dma_data); + ptr->dma_point = DMA_CARAH | temp; + printf("current address register A = %4.4X",ptr->dma_data); + ptr->dma_point = DMA_CARAL | temp; + printf("%4.4X\n",ptr->dma_data); + ptr->dma_point = DMA_CARBH | temp; + printf("current address register B = %4.4X",ptr->dma_data); + ptr->dma_point = DMA_CARBL | temp; + printf("%4.4X\n",ptr->dma_data); + ptr->dma_point = DMA_BARAH | temp; + printf("base address register A = %4.4X",ptr->dma_data); + ptr->dma_point = DMA_BARAL | temp; + printf("%4.4X\n",ptr->dma_data); + ptr->dma_point = DMA_BARBH | temp; + printf("base address register B = %4.4X",ptr->dma_data); + ptr->dma_point = DMA_BARBL | temp; + printf("%4.4X\n",ptr->dma_data); + ptr->dma_point = DMA_COC | temp; + printf("current operation count = %4.4X\n",ptr->dma_data); + ptr->dma_point = DMA_BOC | temp; + printf("base operation count = %4.4X\n",ptr->dma_data); + } + return 0; +} + + + +/* + * + * dvx_reset + * joh 072591 + * + */ +#if 0 /* This is hideous... should reset only those we started! */ +LOCAL void +dvx_reset(void) +{ + struct dvx_2502 *pDvxA16; + unsigned short card_id; + int i; + int status; + int card_found = FALSE; + + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + ai_addrs[DVX2502], + &pDvxA16); + if (status != OK){ + logMsg( "%s: A16 base addr problems DVX 2502\n", + __FILE__); + return; + } + + /* + * search for cards + */ + for (i = 0; i < ai_num_cards[DVX2502]; i++, pDvxA16++){ + status = vxMemProbe ( + &pDvxA16->dev_id, + READ, + sizeof(card_id), + &card_id); + if (status != OK) + continue; + /* + * see if detected card is a 2502 + * and reset if so + */ + if (card_id == DVX_ID){ + /* reset the DMA controller */ + dvx_dma_reset(pDvxA16); + + pDvxA16->csr = CSR_RESET; + card_found = TRUE; + } + } + + /* + * wait long enough for the current DMA to end + * + * 1 sec + */ + if(card_found){ + printf("Waiting for DVX 2502 DMA to complete..."); + taskDelay(sysClkRateGet()); + printf("done\n"); + } +} +#else +LOCAL void +dvx_reset(void) +{ + int i; + int CardFound = 0; + + /* + * search for cards + */ + for (i = 0; i < ai_num_cards[DVX2502]; i++) + { + if (dvx[i].pdvx2502 != NULL) + { + dvx_dma_reset(dvx[i].pdvx2502); + dvx[i].pdvx2502->csr = CSR_RESET; + CardFound = 1; + } + } + /* + * wait long enough for the current DMA to end + * + * 1 sec + */ + if(CardFound) + { + printf("Waiting for DVX 2502 DMA to complete..."); + taskDelay(sysClkRateGet()); + printf("done\n"); + } +} +#endif diff --git a/src/drv/old/drvEpvxi.c b/src/drv/old/drvEpvxi.c new file mode 100644 index 000000000..ed7c8d100 --- /dev/null +++ b/src/drv/old/drvEpvxi.c @@ -0,0 +1,4448 @@ +/* + * drvEpvxi.c + * + * base/src/drv $Id$ + * Routines for the VXI device support and resource management. + * + * Author: Jeff Hill + * Date: 11-89 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 joh 02-14-90 formal release + * .02 joh 04-04-90 as requested KLUDGED dynamic address so they + * dont have to specify model number in DCT + * .03 joh 04-04-90 as requested KLUDGED dynamic address so they + * dont have to specify model number in DCT + * .04 joh 07-26-90 changed from ttl trig 7 to ecl trig 0 + * .05 joh 07-27-90 added support for multiple slot0 cards + * .06 joh 08-08-91 delinting + * .07 joh 09-05-91 converted to v5 vxWorks + * .08 joh 12-05-91 split vxi_driver.c into vxi_driver.c and + * vxi_resman.c + * .09 joh 01-29-91 added MXI support & removed KLUDGE + * .10 joh 07-06-92 added A24 & A32 address config + * .11 joh 07-07-92 added routine to return A24 or A32 base + * .12 joh 07-13-92 merged in model hash support written by + * Richard Baker (summer intern) + * .13 joh 07-21-92 Now stores extender info in a hierarchical + * linked list + * .14 joh 07-29-92 vxi record topology needed check for + * device present + * .15 joh 07-29-92 added sccs id + * .16 joh 08-19-92 make name registration + * .17 joh 08-21-92 cleaned up A24/A32 MXI setup + * .18 joh 08-26-92 dont return error if a make or model + * has already been registered + * .19 joh 09-03-92 Use the correct routine in NIVXI + * for CPU030 trigger routing + * .20 joh 09-30-92 split epvxiOpen() into epvxiOpen() and + * epvxiDeviceVerify() + * .21 joh 10-30-92 NI CPU030 trigger routing was failing + * due to no entry for the 030 in the resman + * tables - it cant see itself in A16. + * A work around was installed. + * .22 joh 05-24-93 Fixed over-zealous parameter checks in + * TTL trigger route + * .23 joh 06-03-93 Fixed incorrect MXI BP TTL trigger enable + * .24 joh 07-12-93 Record the task id when opening a device + * .25 joh 07-21-93 Improved DC device allocation in MXI + * environment + * .26 joh 11-10-93 Now configures multiple DC devices per slot. + * Blocked address devices are preallocated + * where possible. Independently addressed + * multiple devices per slot are allocated on + * demand. + * + * RM unfinished items + * ------------------- + * 1. Assigning the cmdr/serv hierarchy from within a DC res man + * needs to be revisited + * 2. Should this module prevent two triggers from driving + * the same front panel connector at once? + * + * + * NOTES + * ----- + * + * + */ + +/* + * Code Portions + * + * local + * vxi_find_slot given a VXI modules addr find its slot + * vxi_init_ignore_list init list of interrupt handlers to ignore + * vxi_vec_inuse check to see if vector is in use + * vxi_configure_hierarchies setup commander servant hierarchies + * vxi_self_test test for dev self test passed + * open_slot0_device open slot zero devices + * nicpu030_init NI CPU030 controller setup + * nivxi_cpu030_set_modid set modid on the NICPU030 + * nivxi_cpu030_clr_all_modid clear all modid lines on the NICPU030 + * set_reg_modid set modid on a reg based slot0 device + * clr_all_reg_modid clr all modid on a reg based slot0 dev + * vxi_find_sc_devices find all SC devices and open them + * vxi_find_dc_devices find all DC devices and open them + * vxi_count_dc_devices determine the number of DC devices in + * this extender + * vxi_init_ignore_list find addresses of default int handlers + * vxi_vec_inuse test for int vector in use + * mxi_map mat the addresses on a MXI bus extender + * vxi_find_mxi_devices search for and open mxi bus repeaters + * map_mxi_inward map from a VXI crate towards the RM + * vxi_address_config setup A24 and A32 offsets + * open_vxi_device log VXI device info + * vxi_record_topology find slots and extenders for each device + * + * for use by IOC core + * epvxiResman entry for a VXI resource manager which + * also sets up the MXI bus + * epvxiIOReport call io device specific report routines + * for all registered devices and print + * information about device's configuration + * epvxiDeviceList print info useful when debugging drivers + * vxi_init backwards compatibility + * vxi_io_report backwards compatibility + * + * for use by vxi drivers + * epvxiLookupLA find LA given search pattern + * epvxiUniqueDriverID obtain a unique id each call + * epvxiOpen register a drivers use of a device + * epvxiClose disconnect from a device + * epvxiPConfig fetch a driver config block given a LA + * epvxiRouteTriggerECL route ECL trig to/from front panel + * epvxiRouteTriggerTTL route TTL trig to/from front panel + * + */ + +static char *sccsId = "$Id$\t$Date$"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if 0 +#include +#endif + +#include +#define SRCepvxiLib /* allocate externals here */ +#include +#include + +#define NICPU030 + +/* + * EPICS driver entry point table + */ +typedef long (*DRVSUPFUN) (); +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvVxi={ + 2, + epvxiIOReport, + epvxiResman}; + + +/* + * so setting ECL triggers does not mess with the + * RM's address windows + */ +#define MXI_CONTROL_CONSTANT 0x4000 +#define INTX_INT_OUT_ENABLE 0x7f00 +#define INTX_INT_IN_ENABLE 0x7f7f + +#define abort(A) taskSuspend(0) + +#define VXIMSGINTLEVEL 1 + +#define BELL 7 + +#define UKN_LA (-1) +#define UKN_SLOT (-1) +#define UKN_CRATE (-1) + +#define DEFAULT_VXI_A24_BASE 0x90000 +#define DEFAULT_VXI_A24_SIZE 0x10000 +#define DEFAULT_VXI_A32_BASE 0x90000000 +#define DEFAULT_VXI_A32_SIZE 0x10000000 + +/* + * laPassLast and laPassFirst must be last/first respectively + * and have no purpose outside of being delimiters + */ +enum laPass { + laPassSC, + laPassAnchoredDC, + laPassFloatingDC, + laPassSetWindows}; + + +LOCAL char niCpu030Initialized; +LOCAL VXIE root_extender; +LOCAL ELLLIST crateList; + +LOCAL char *ignore_list[] = {"_excStub","_excIntStub"}; +LOCAL void *ignore_addr_list[NELEMENTS(ignore_list)]; +LOCAL unsigned char last_la; +LOCAL unsigned char first_la; + +#define SETMODID(PVXISZ, SLOT) \ +(*(PVXISZ)->set_modid)((PVXISZ), SLOT) + +#define CLRMODID(PVXISZ) \ +(*(PVXISZ)->clear_modid)(PVXISZ); + +LOCAL +SYMTAB *epvxiSymbolTable; +LOCAL +char epvxiSymbolTableDeviceIdString[] = "%03x:%03x"; +LOCAL +char epvxiSymbolTableMakeIdString[] = "%03x"; + +/* + * for the VXI symbol table + * just contains model names for now + */ +#define EPVXI_MODEL_NAME_SYMBOL 1 +#define EPVXI_MAKE_NAME_SYMBOL 2 +#define EPVXI_MAX_SYMBOLS_LOG2 8 +#define EPVXI_MAX_SYMBOLS (1<la_mapped){ + break; + } + vxi_find_dc_devices(pvxie); + break; + + + /* + * allocate DC devices + */ + case laPassAnchoredDC: + /* + * wait until laPassFloatingDC if + * nothing is mapped + */ + if(!pvxie->la_mapped){ + break; + } + vxi_find_dc_devices(pvxie); + break; + + default: + break; + } + + /* + * + * find any MXI bus repeaters + * + */ + vxi_find_mxi_devices( + pvxie, + pass); + + return VXI_SUCCESS; +} + + +/* + * + * vxi_unmap_mxi_devices() + * + * close any MXI devices which have open windows but + * have not been encountered by this execution + * of the resource manager. + * + * This makes the MXI/VXI configure correctly after + * a control x (soft) reboot. + */ +LOCAL void vxi_unmap_mxi_devices(void) +{ + struct vxi_csr *pmxi; + EPVXISTAT status; + int16_t id; + unsigned addr; + + for(addr=first_la; addr<=last_la; addr++){ + /* + * only configure devices not seen before + */ + if(epvxiLibDeviceList[addr]){ + continue; + } + + pmxi = VXIBASE(addr); + + status = vxMemProbe( (char *)pmxi, + READ, + sizeof(id), + (char *)&id); + if(status<0){ + continue; + } + + if(!VXIMXI(pmxi)){ + continue; + } + + /* + * force all of these back to the hard reset state + */ + pmxi->dir.w.dd.mxi.la_window = 0; + pmxi->dir.w.dd.mxi.a16_window_low = 0; + pmxi->dir.w.dd.mxi.a16_window_high = 0; + pmxi->dir.w.dd.mxi.a24_window_low = 0; + pmxi->dir.w.dd.mxi.a24_window_high = 0; + pmxi->dir.w.dd.mxi.a32_window_low = 0; + pmxi->dir.w.dd.mxi.a32_window_high = 0; + } +} + + + +/* + * + * vxi_find_mxi_devices() + * + */ +LOCAL void vxi_find_mxi_devices( +VXIE *pvxie, +enum laPass pass +) +{ + struct vxi_csr *pmxi; + unsigned addr; + VXIDI *pvxidi; + VXIE *pnewvxie; + + for( addr=first_la; addr<=last_la; addr++){ + + pvxidi = epvxiLibDeviceList[addr]; + + /* + * only configure devices seen before + */ + if(!pvxidi){ + continue; + } + + /* + * skip MXI devices which are not + * in this extender + */ + if(pvxidi->pvxie != pvxie){ + continue; + } + + pmxi = VXIBASE(addr); + + pnewvxie = open_mxi_device( + addr, + pvxie, + ext_import_mxi_into_vxi); + if(!pnewvxie){ + continue; + } + + /* + * open the LA window outward over the entire LA range + */ + pmxi->dir.w.dd.mxi.control = + MXI_UPPER_LOWER_BOUNDS; + pmxi->dir.w.dd.mxi.la_window = + VXIADDRMASK | (VXIADDRMASK<la_mapped){ + pvxie->la_mapped = TRUE; + pvxie->la_low = min(pvxie->la_low, pnewvxie->la_low); + pvxie->la_high = max(pvxie->la_high, pnewvxie->la_high); + } + + /* + * disable the window until the last pass + */ + if(pass != laPassSetWindows){ + pmxi->dir.w.dd.mxi.la_window = + 0 | (0<la_mapped){ +# ifdef DEBUG + printf( "VXI resman: VXI to MXI(%x) %x-%x\n", + addr, + pnewvxie->la_low, + pnewvxie->la_high); +# endif + pmxi->dir.w.dd.mxi.la_window = + (pnewvxie->la_low<la_high+1); + + /* + * if INTX is installed gate the interrupts off of + * INTX + */ + if(MXIINTX(pmxi)){ + pmxi->dir.w.dd.mxi.INTX_interrupt = + INTX_INT_IN_ENABLE; + } + } + else{ + printf( "VXI resman: VXI to MXI LA=0x%X is empty\n", + addr); + pmxi->dir.w.dd.mxi.la_window = + 0 | (0<pvxieSelf){ + return pvxidi->pvxieSelf; + } + + pnewvxie = (VXIE *) calloc(1, sizeof(*pnewvxie)); + if(!pnewvxie){ + errMessage(S_epvxi_noMemory, "MXI device ignored"); + return NULL; + } + + pnewvxie->type = type; + pnewvxie->la = la; + pnewvxie->la_low = last_la; + pnewvxie->la_high = first_la; + pnewvxie->pParent = pvxie; + + pvxidi->pvxieSelf = pnewvxie; + + /* + * make sure PARENT window includes the MXI + * bus extender + */ + pvxie->la_mapped = TRUE; + pvxie->la_low = min(pvxie->la_low, la); + pvxie->la_high = max(pvxie->la_high, la); + ellAdd(&pvxie->extenders, &pnewvxie->node); + + epvxiRegisterModelName( + VXIMAKE(pmxi), + VXIMODEL(pmxi), + "MXI bus extender"); + + return pnewvxie; +} + + +/* + * MAP_MXI_INWARD + * + * for each MXI found that we have not seen before + * open the la window inward for all devices + * + */ +LOCAL EPVXISTAT map_mxi_inward( +VXIE *pvxie, +enum laPass pass +) +{ + VXIDI *pvxidi; + struct vxi_csr *pmxi_new; + unsigned addr; + EPVXISTAT status; + VXIE *pnewvxie; + + /* + * open all new MXI devices now + * so that we dont confuse them with + * SC devices when a MXI's window is + * completely open. + * + * If we attempt to communicate with a + * MXI device while another MXI device + * at the same level has its window open + * all the way we see VME bus conflicts. + */ + for(addr=first_la; addr<=last_la; addr++){ + + pvxidi = epvxiLibDeviceList[addr]; + if(!pvxidi){ + /* + * if it has not been seen before we know + * its a MXI device + */ + status = open_vxi_device(pvxie, addr); + if(status==VXI_SUCCESS){ + open_mxi_device( + addr, + pvxie, + ext_export_vxi_onto_mxi); + } + } + } + + /* + * now step through and open up all MXI devices found + */ + for(addr=first_la; addr<=last_la; addr++){ + + pvxidi = epvxiLibDeviceList[addr]; + + if(!pvxidi){ + continue; + } + + pnewvxie = pvxidi->pvxieSelf; + + /* + * dont bother with + * devices that are + * not extenders + * here + */ + if(!pnewvxie){ + continue; + } + + /* + * if it is an extender dont + * configure it unless + * it is a child of the + * current parent + */ + if(pvxidi->pvxie != pvxie){ + continue; + } + + pmxi_new = VXIBASE(addr); + + /* + * open the address window inward for all device + */ + pmxi_new->dir.w.dd.mxi.control = + MXI_UPPER_LOWER_BOUNDS; + pmxi_new->dir.w.dd.mxi.la_window = + 1 | (1<la_mapped){ + pvxie->la_mapped = TRUE; + pvxie->la_low = min(pvxie->la_low, pnewvxie->la_low); + pvxie->la_high = max(pvxie->la_high, pnewvxie->la_high); + } + + /* + * temporarily close the windows so that we can discover + * iproperly located SC devices + */ + if(pass != laPassSetWindows){ + pmxi_new->dir.w.dd.mxi.la_window = + 0 | (0<la_mapped){ +# ifdef DEBUG + printf( "VXI resman: MXI to VXI LA=%x %x-%x\n", + addr, + pnewvxie->la_low, + pnewvxie->la_high); +# endif + pmxi_new->dir.w.dd.mxi.la_window = + pnewvxie->la_low | + ((pnewvxie->la_high+1)<dir.w.dd.mxi.INTX_interrupt = + INTX_INT_OUT_ENABLE; + } + } + else{ + + printf( "VXI resman: MXI to VXI LA=0x%X is empty\n", + addr); + pmxi_new->dir.w.dd.mxi.la_window = + 0 | (0<node.next; + if(pvxisz->la == pnewvxie->la || + pvxisz->pvxie == pnewvxie){ + ellDelete(&crateList, &pvxisz->node); + } + pvxisz = next; + } +# endif /*REMOVE_UNUSED_SLOT_ZERO_DEVICES*/ + } + } + return VXI_SUCCESS; +} + + +/* + * + * open_vxi_device + * + * + */ +LOCAL EPVXISTAT open_vxi_device( +VXIE *pvxie, +unsigned la +) +{ + struct vxi_csr *pdevice; + VXIDI *plac; + int16_t id; + EPVXISTAT status; + + /* + * just return if this device is known about + */ + if(epvxiLibDeviceList[la]){ + return VXI_SUCCESS; + } + + pdevice = VXIBASE(la); + + status = vxMemProbe( (char *)pdevice, + READ, + sizeof(id), + (char *)&id); + if(status<0){ + return S_dev_noDevice; + } + + status = verify_valid_window(pvxie, la); + if(status){ + errMessage( + status, + "VXI resman: no access to SC device"); + errMessage( + status, + "VXI resman: without MXI LA window overlap."); + errPrintf( + status, + __FILE__, + __LINE__, + "VXI resman: SC device LA=0X%X", + la); + errPrintf( + status, + __FILE__, + __LINE__, + "VXI resman: extender LA=0X%X", + pvxie->la); + errMessage( + status, + "VXI resman: SC device ignored"); + return status; + } + + plac = (VXIDI *) calloc(1, sizeof(**epvxiLibDeviceList)); + if(!plac){ + errMessage(S_epvxi_noMemory,"... continuing"); + return S_epvxi_noMemory; + } + + plac->make = VXIMAKE(pdevice); + plac->model = VXIMODEL(pdevice); + plac->class = VXICLASS(pdevice); + plac->pvxie = pvxie; + epvxiLibDeviceList[la] = plac; + + pvxie->la_low = min(pvxie->la_low, la); + pvxie->la_high = max(pvxie->la_high, la); + pvxie->la_mapped = TRUE; + + if(vxi_vec_inuse(la)){ + errPrintf( + S_epvxi_badConfig, + __FILE__, + __LINE__, + "SC VXI device at allocated int vec=0x%X", + la); + epvxiSetDeviceOffline(la); + } + else{ + if(VXISLOT0MODELTEST(plac->model)){ + plac->slot0_dev = TRUE; + open_slot0_device(pvxie, la); + } + } + +# ifdef DEBUG + printf("Found LA=0X%X extender LA=0X%X\n", la, pvxie->la); +# endif + + return VXI_SUCCESS; +} + + +/* + * + * verify_valid_window() + * + * determine if this la is within one + * of the other extenders at the same level + * + */ +LOCAL EPVXISTAT verify_valid_window( +VXIE *pvxie, +unsigned la +) +{ + VXIE *pChild; + + /* + * If its the root extender we dont care + */ + if(!pvxie->pParent){ + return VXI_SUCCESS; + } + + for( pChild = (VXIE *) pvxie->pParent->extenders.node.next; + pChild; + pChild = (VXIE *) pChild->node.next){ + + /* + * of course its ok to be in the + * current extender + */ + if(pChild == pvxie){ + continue; + } + + /* + * its not ok to overlap other extenders + * window + */ + if(pChild->la_mapped){ + if(la >= pChild->la_low && + la <= pChild->la_high){ + + return S_epvxi_badConfig; + } + } + } + + /* + * traverse the hierarchy + */ + return verify_valid_window(pvxie->pParent, la); +} + + +/* + * + * + * VXI_FIND_SC_DEVICES + * + */ +LOCAL void vxi_find_sc_devices( +VXIE *pvxie +) +{ + unsigned addr; + + /* + * Locate the slots of all SC devices + */ + for(addr=first_la; addr<=last_la; addr++){ + + /* + * dont configure devices seen before + */ + if(epvxiLibDeviceList[addr]){ + continue; + } + + (void) open_vxi_device(pvxie, addr); + } +} + + +/* + * + * vxi_record_topology() + * + * Record topological information after all MXIs + * and slot zero cards have been located since + * MXIs can appear in the address space prior + * to their slot zero cards ( so their slots + * cant be found initially ). + * + */ +LOCAL void vxi_record_topology(void) +{ + VXIDI **pplac; + struct vxi_csr *pdevice; + VXISZ *pvxisz; + unsigned la; + unsigned slot; + EPVXISTAT status; + + for( la=first_la, pplac = epvxiLibDeviceList; + la<=last_la; + la++, pplac++){ + + if(!*pplac){ + continue; + } + + pdevice = VXIBASE(la); + status = vxi_find_slot(pdevice, &slot, &pvxisz); + if(status==VXI_SUCCESS){ + (*pplac)->slot = slot; + (*pplac)->slot_zero_la = pvxisz->la; + (*pplac)->extender_la = pvxisz->pvxie->la; + + if(VXISLOT0MODEL(pdevice)){ + if((*pplac)->slot!=0){ + errPrintf( + S_epvxi_badConfig, + __FILE__, + __LINE__, + "VXI slot 0 found in slot %d? LA=0x%X", + (*pplac)->slot, + la); + } + } + } + else{ + errPrintf( + status, + __FILE__, + __LINE__, + "LA=0X%X", + la); + (*pplac)->slot = UKN_SLOT; + (*pplac)->slot_zero_la = UKN_LA; + (*pplac)->extender_la = UKN_LA; + } + } +} + + +/* + * + * + * VXI_FIND_DC_DEVICES + * + */ +LOCAL void vxi_find_dc_devices( +VXIE *pvxie +) +{ + int prealloc; + int16_t id; + EPVXISTAT status; + unsigned offset; + struct vxi_csr *pcsr; + VXISZ *pvxisz; + unsigned nDC; + int slot; + + /* + * dont move DC devices if SC device at address 0xff + */ + if(epvxiLibDeviceList[VXIDYNAMICADDR]){ + errPrintf( + S_epvxi_badConfig, + __FILE__, + __LINE__, + "VXI SC device recorded at dynamic address 0x%X", + VXIDYNAMICADDR); + errMessage( + S_epvxi_badConfig, + "VXI DC devices ignored"); + return; + } + + pcsr = VXIBASE(VXIDYNAMICADDR); + status = vxMemProbe( (char *)pcsr, + READ, + sizeof(id), + (char *)&id); + if(status == OK){ + errPrintf( + S_epvxi_badConfig, + __FILE__, + __LINE__, + "VXI SC device at dynamic address 0x%X", + VXIDYNAMICADDR); + errMessage( + S_epvxi_badConfig, + "VXI DC devices ignored"); + return; + } + + /* + * if unanchored force them to all be in one + * contiguous block + */ + prealloc = FALSE; + if(!pvxie->la_mapped){ + status = vxi_count_dc_devices(pvxie, &nDC); + if(status){ + return; + } + if(nDC < 1){ + return; + } + status = vxi_alloc_la( + pvxie, + nDC, + &offset); + if(status){ + errMessage( + status, + "VXI resman: unanchored DC VXI device block doesnt fit"); + errPrintf( + status, + __FILE__, + __LINE__, + "VXI resman: %d DC VXI devices in extender LA=0X%X ignored", + nDC, + pvxie->la); + return; + } + prealloc = TRUE; + } + + /* + * find all DC devices + */ + pvxisz = (VXISZ *) crateList.node.next; + while(pvxisz){ + /* + * We wish to selectively configure DC devices + * accessed through the current extender. + * + * If the slot zero card is the extender then + * the LAs of the extender and the slot zero will + * match. Otherwise the slotzero card was found + * by opening up the window in the extender + * and the slot zeros extender will be the + * current extender. + */ + if(pvxisz->pvxie == pvxie || pvxisz->la == pvxie->la){ + for(slot=0;slotnode.next; + } +} + + +/* + * vxi_assign_dc_addresses() + */ +LOCAL EPVXISTAT vxi_assign_dc_addresses( +VXIE *pvxie, +struct vxi_csr *pcsr, +int prealloc, +unsigned *pOffset, +VXISZ *pvxisz, +int slot +) +{ + unsigned offset; + unsigned count; + EPVXISTAT status; + int16_t id; + + status = vxMemProbe( + (char *)pcsr, + READ, + sizeof(id), + (char *)&id); + if(status<0){ + return S_epvxi_noDevice; + } + + count = slot_la_count(pcsr); + + if(prealloc){ + offset = *pOffset; + } + else{ + status = vxi_alloc_la( + pvxie, + count, + &offset); + if(status){ + errPrintf( + status, + __FILE__, + __LINE__, + "VXI: %d DC VXI device(s) do(es) not fit", + count); + errPrintf( + status, + __FILE__, + __LINE__, + "VXI: DC VXI device(s) at slot %d in extender LA=0X%X ignored", + slot, + pvxie->la); + return S_epvxi_noMemory; + } + } + + /* + * blocked addr devices recv their + * addr assignements in unison + */ + pcsr->dir.w.addr = offset; + + while(count){ + count--; + status = open_vxi_device( + pvxie, + offset); + if(status){ + errPrintf( + status, + __FILE__, + __LINE__, + "VXI resman: DC dev assign to LA=0X%X failed", + offset); + errPrintf( + status, + __FILE__, + __LINE__, + "VXI resman: Slot Zero LA=0X%X", + pvxisz->la); + errPrintf( + status, + __FILE__, + __LINE__, + "VXI resman: DC VXI device ignored"); + } + offset++; + } + + if(prealloc){ + *pOffset = offset; + } + + return status; +} + + +/* + * + * + * VXI_COUNT_DC_DEVICES + * + */ +LOCAL EPVXISTAT vxi_count_dc_devices( +VXIE *pvxie, +unsigned *pCount +) +{ + int16_t id; + EPVXISTAT status; + struct vxi_csr *pcsr; + VXISZ *pvxisz; + int slot; + unsigned nDC; + + /* + * dont count DC devices if SC device at address 0xff + */ + if(epvxiLibDeviceList[VXIDYNAMICADDR]){ + status = S_epvxi_badConfig; + errMessage(status, "SC device at DC address"); + return status; + } + + pcsr = VXIBASE(VXIDYNAMICADDR); + status = vxMemProbe( (char *)pcsr, + READ, + sizeof(id), + (char *)&id); + if(status == OK){ + status = S_epvxi_badConfig; + errMessage(status, "SC device at DC address"); + return status; + } + + /* + * find all dynamic modules + */ + nDC=0; + pvxisz = (VXISZ *) crateList.node.next; + while(pvxisz){ + /* + * We wish to selectively configure DC devices + * accessed through the current extender. + * + * If the slot zero card is the extender then + * the LAs of the extender and the slot zero will + * match. Otherwise the slotzero card was found + * by opening up the window in the extender + * and the slot zeros extender will be the + * current extender. + * + * Counts multiple blocked addr device per slot here. + * Does not try to count multiple independently + * addressed devices per slot here. Space is + * allocated for these DC devices on demand due to + * difficulties counting them ahead of time. + */ + if(pvxisz->pvxie == pvxie || + pvxisz->la == pvxie->la){ + + for(slot=0; slot=0){ + nDC += slot_la_count(pcsr); + } + } + CLRMODID(pvxisz); + } + + pvxisz = (VXISZ *) pvxisz->node.next; + } + *pCount = nDC; + return VXI_SUCCESS; +} + + +/* + * + * slot_la_count() + * + */ +LOCAL unsigned slot_la_count(struct vxi_csr *pcsr) +{ + unsigned blockedAddrCount; + + /* + * Rule F.2.6 + */ + blockedAddrCount = VXINDCDEVICES(pcsr); + if(blockedAddrCount==0 || blockedAddrCount==0xff){ + blockedAddrCount = 1; + } + return blockedAddrCount; +} + + +/* + * + * open slot 0 device + * + * + */ +LOCAL void open_slot0_device( +VXIE *pvxie, +unsigned la +) +{ + struct vxi_csr *pcsr; + EPVXISTAT status; + VXISZ *pvxisz; + + pcsr = VXIBASE(la); + + /* + * MXI's are device class extended + */ + if(VXICLASS(pcsr) != VXI_REGISTER_DEVICE){ + if(!VXIMXI(pcsr)){ + errPrintf( + S_epvxi_badConfig, + __FILE__, + __LINE__, +"Only register based slot 0 devices currently supported LA=0x%X", + la); + return; + } + } + + pvxisz = (VXISZ *) crateList.node.next; + while(pvxisz){ + if(pvxisz->pcsr == pcsr){ + return; + } + pvxisz = (VXISZ *) pvxisz->node.next; + } + + pvxisz = (VXISZ *) calloc(1, sizeof(*pvxisz)); + if(!pvxisz){ + errMessage( + S_epvxi_noMemory, + "continuing..."); + return; + } + pvxisz->reg = TRUE; + pvxisz->pcsr = pcsr; + pvxisz->set_modid = set_reg_modid; + pvxisz->clear_modid = clr_all_reg_modid; + pvxisz->pvxie = pvxie; + pvxisz->la = la; + + ellAdd(&crateList, &pvxisz->node); + + /* + * force the slot zero device into a known state + */ + CLRMODID(pvxisz); + + if(!epvxiLibDeviceList[la]){ + status = open_vxi_device(pvxie, la); + if(status){ + errPrintf( + status, + __FILE__, + __LINE__, + "attempted slot zero device open la=0X%X", + la); + return; + } + } +} + + + + + +/* + * + * NICPU030_INIT() + * check to see if this code is running on a + * national instruments cpu030 installed in + * slot zero. + * + */ +#ifdef NICPU030 +LOCAL EPVXISTAT nicpu030_init( +VXIE *pvxie +) +{ + int i; + EPVXISTAT status = S_epvxi_internal; + int16_t model; + UINT8 type; + UINT8 la; + + /* + * If we are running this code on the NI 030 + * we are the resource manager and the NI 030 will + * be the first slot zero card found. + */ + if(niCpu030Initialized){ + return VXI_SUCCESS; + } + + for(i=0; inicpu030 = TRUE; + pvxisz->set_modid = nivxi_cpu030_set_modid; + pvxisz->clear_modid = nivxi_cpu030_clr_all_modid; + pvxisz->la = la; + pvxisz->pvxie = pvxie; + + ellAdd(&crateList, &pvxisz->node); + } + return VXI_SUCCESS; +} +#endif + + + +/* + * + * vxi_alloc_la() + * + */ +LOCAL EPVXISTAT vxi_alloc_la( +VXIE *pvxie, +unsigned count, +unsigned *poffset +) +{ + EPVXISTAT status; + unsigned hla; + VXIDI *pvxidi; + unsigned peak = 0; + unsigned la; + + if(count<1){ + status = S_epvxi_internal; + errMessage(status,NULL); + return status; + } + + /* + * look inside the range thats mapped first + */ + if(pvxie->la_mapped){ + for(la=pvxie->la_low; la<=last_la; la++){ + + pvxidi = epvxiLibDeviceList[la]; + + /* + * skip all devices seen before + */ + if(pvxidi){ + /* + * LA window cant cross extender boundaries + * so just quit + */ + if(pvxidi->pvxie != pvxie){ + break; + } + peak= 0; + continue; + } + + if(vxi_la_occupied(la)){ + peak= 0; + continue; + } + + peak++; + + if(peak >= count){ + *poffset = ((int)la)-(count-1); + return VXI_SUCCESS; + } + } + + hla = pvxie->la_low; + } + else{ + hla = last_la; + } + + /* + * unachored DC device allocations go from high + * to low so that we avoid mc680xx reserved + * interrupt vectors + * + * stop before unsigned la=0 is decremented + * (shouldnt allocate the resource manager's LA anyways) + */ + peak=0; + for(la=hla; la>=max(VXI_RESMAN_LA+1,first_la); la--){ + + pvxidi = epvxiLibDeviceList[la]; + + /* + * skip all devices seen before + */ + if(pvxidi){ + /* + * LA window cant cross extender boundaries + * (if something is mapped already) so just quit + */ + if(pvxie->la_mapped){ + if(pvxidi->pvxie != pvxie){ + break; + } + } + + peak= 0; + continue; + } + + if(vxi_la_occupied(la)){ + peak= 0; + continue; + } + + peak++; + + if(peak >= count){ + *poffset = la; + return VXI_SUCCESS; + } + } + + return S_epvxi_internal; +} + + +/* + * + * vxi_la_occupied() + * + */ +LOCAL EPVXISTAT vxi_la_occupied(unsigned la) +{ + struct vxi_csr *pcsr; + int16_t *pi16; + int16_t i16; + EPVXISTAT s; + + /* + * dont allocate the resource manager's LA + */ + if(la == VXI_RESMAN_LA){ + return TRUE; + } + + /* + * Check to see if this LA belongs to + * the NI 030 CPU (that does not show + * in A16 when it is a VME BM) + */ + if(niCpu030Initialized){ + if(la == (*pnivxi_func[(unsigned)e_GetMyLA])()){ + return TRUE; + } + } + + /* + * Probe the entire LA space + */ + pcsr = VXIBASE(la); + for( pi16 = (int16_t *) &pcsr->dir.r.make; + pi16 <= (int16_t *) &pcsr->dir.r.dd.reg.ddx2e; + pi16++){ + + s = vxMemProbe( (char *)pi16, + READ, + sizeof(i16), + (char *)&i16); + if(s == OK){ + return TRUE; + } + } + + /* + * dont allow vxi int vec to overlap + * VME vectors in use + */ + if(vxi_vec_inuse(la)){ + return TRUE; + } + + return FALSE; +} + + + +/* + * + * VXI_FIND_SLOT + * given a VXI module's addr find its slot + * + */ +LOCAL EPVXISTAT vxi_find_slot( +struct vxi_csr *pcsr, +unsigned *pslot, +VXISZ **ppvxisz +) +{ + VXISZ *pvxisz; + EPVXISTAT status; + unsigned char slot; + + status = S_epvxi_slotNotFound; + + /* + * RULE C.2.7 + */ + if(VXIMODIDSTATUS(pcsr->dir.r.status)){ + errMessage(status, "device's MODID status is active & no MODID?"); + return status; + } + + pvxisz = (VXISZ *) crateList.node.next; + while(pvxisz){ + + /* + * if it is a slot zero card + * then dont bother searching + */ + if(pvxisz->pcsr == pcsr){ + *pslot = 0; + *ppvxisz = pvxisz; + status = VXI_SUCCESS; + break; + } + + for(slot=0;slotdir.r.status)){ + *pslot = slot; + *ppvxisz = pvxisz; + status = VXI_SUCCESS; + break; + } + } + CLRMODID(pvxisz); + + if(status == VXI_SUCCESS) + break; + + pvxisz = (VXISZ *) pvxisz->node.next; + } + + return status; +} + + +/* + * + * VXI_RESET_DC + * force all dynamic devices back to address 0xff + * (In case this is a ctrl X restart) + * + * not tested with at5vxi modules + */ +#ifdef JUNKYARD +LOCAL EPVXISTAT vxi_reset_dc(void) +{ + register unsigned addr; + unsigned slot; + unsigned crate; + int16_t id; + EPVXISTAT status; + struct vxi_csr_w *pcr; + struct vxi_csr_r *psr; + + for(addr=first_la; addr<=last_la; addr++){ + + psr = (struct vxi_csr_r *) VXIBASE(addr); + pcr = (struct vxi_csr_w *) psr; + + status = vxMemProbe( psr, + READ, + sizeof(id), + &id); + if(status == ERROR) + continue; + + status = vxi_find_slot(psr, &slot, &crate); + if(status){ + return status; + } + + SETMODID(slot, crate); + + + pcr->addr = VXIDYNAMICADDR; + } + + return VXI_SUCCESS; +} +#endif + + +/* + * + * VXI_DC_TEST + * determine if a VXI module in the static address range is dynamic + * + */ +#ifdef JUNKYARD +LOCAL EPVXISTAT vxi_dc_test( +unsigned current_addr +) +{ + register unsigned addr; + unsigned slot; + unsigned crate; + int16_t id; + EPVXISTAT status; + struct vxi_csr_w *pcr; + struct vxi_csr_r *psr; + + static unsigned open_addr; + unsigned dynamic; + + for(addr=first_la; addr<=last_la; addr++){ + + status = vxMemProbe( VXIBASE(addr), + READ, + sizeof(id), + &id); + if(status == ERROR){ + open_addr = addr; + break; + } + } + + psr = (struct vxi_csr_r *) VXIBASE(current_addr); + pcr = (struct vxi_csr_w *) psr; + + status = vxi_find_slot(psr, &slot, &crate); + if(status){ + errMessage(status,NULL); + return status; + } + + SETMODID(slot, crate); + pcr->addr = open_addr; + + psr = (struct vxi_csr_r *) VXIBASE(open_addr); + pcr = (struct vxi_csr_w *) psr; + + status = vxMemProbe( psr, + READ, + sizeof(id), + &id); + + if(status==OK){ + dynamic = TRUE; + pcr->addr = current_addr; + } + else + dynamic = FALSE; + + status = vxMemProbe( VXIBASE(current_addr), + READ, + sizeof(id), + &id); + if(status == ERROR) + return S_epvxi_internal; + + + return dynamic; +} +#endif + + +/* + * + * VXI_CONFIGURE_HIERARCHIES + * + */ +LOCAL void vxi_configure_hierarchies( +unsigned commander_la, +unsigned servant_area +) +{ + EPVXISTAT status; + struct vxi_csr *pcsr; + unsigned long response; + VXIDI **ppvxidi; + VXIDI *pvxidi; + unsigned sla; + unsigned last_sla; + unsigned area; + + last_sla = servant_area+commander_la; + + if(last_sla >= NELEMENTS(epvxiLibDeviceList)){ + errPrintf( + S_epvxi_internal, + __FILE__, + __LINE__, + "VXI resman: Clipping servant area (LA=0x%X)", + commander_la); + last_sla = NELEMENTS(epvxiLibDeviceList)-1; + } + + sla = commander_la+1; + ppvxidi = &epvxiLibDeviceList[sla]; + for( ; + sla<=last_sla; + sla += area+1, ppvxidi += area+1){ + + pvxidi = *ppvxidi; + area = 0; + + if(!pvxidi){ + continue; + } + + pvxidi->commander_la = commander_la; + + if(!pvxidi->st_passed){ + continue; + } + + pcsr = VXIBASE(sla); + + if(VXICLASS(pcsr) != VXI_MESSAGE_DEVICE){ + continue; + } + + if(commander_la != VXI_RESMAN_LA){ + status = epvxiCmdQuery( + commander_la, + (unsigned long)MBC_GRANT_DEVICE | sla, + &response); + if(status){ + errPrintf( + status, + __FILE__, + __LINE__, + "VXI resman: GD failed (LA=0x%X)", + sla); + } + else{ + printf( + "VXI resman: gd resp %x\n", + response); + } + } + if(VXICMDR(pcsr)){ + status = epvxiCmdQuery( + sla, + (unsigned long)MBC_READ_SERVANT_AREA, + &response); + if(status){ + errPrintf( + status, + __FILE__, + __LINE__, + "VXI resman: RSA failed (LA=0x%X)", + sla); + } + else{ + area = response & MBR_READ_SERVANT_AREA_MASK; + + printf( "The servant area was %d (LA=0x%X)\n", + area, + sla); + + vxi_configure_hierarchies( + sla, + area); + } + } + } +} + + +/* + * + * VXI_BEGIN_NORMAL_OPERATION + * + */ +LOCAL void vxi_begin_normal_operation(void) +{ + EPVXISTAT status; + unsigned la; + VXIDI **ppvxidi; + VXIDI *pvxidi; + struct vxi_csr *pcsr; + + for( la=0, ppvxidi = epvxiLibDeviceList; + ppvxidi < epvxiLibDeviceList+NELEMENTS(epvxiLibDeviceList); + ppvxidi++, la++){ + + unsigned cmdr; + unsigned long cmd; + unsigned long resp; + + pvxidi = *ppvxidi; + + if(!pvxidi){ + continue; + } + + pcsr = VXIBASE(la); + + if(!pvxidi->st_passed){ + continue; + } + + if(VXICLASS(pcsr) != VXI_MESSAGE_DEVICE){ + continue; + } + + cmdr = VXICMDR(pcsr); + + cmd = MBC_BEGIN_NORMAL_OPERATION; +/* + * this will send the begin nml op command to servants which + * have a commander + * + * more work needs to be done here if this situation occurs + * see below + */ + if(cmdr){ + cmd |= MBC_TOP_LEVEL_CMDR; + } + status = epvxiCmdQuery(la, cmd, &resp); + if(status){ + errPrintf( + status, + __FILE__, + __LINE__, + "VXI resman: Device rejected BEGIN_NORMAL_OPERATION LA=0x%X (reason=%d)", + la, + status); + } + else if( + MBR_STATUS(resp)!=MBR_STATUS_SUCCESS || + MBR_BNO_STATE(resp)!=MBR_BNO_STATE_NO){ + errPrintf( + S_epvxi_msgDeviceFailure, + __FILE__, + __LINE__, + "VXI resman: Device rejected BEGIN_NORMAL_OPERATION LA=0x%X (status=%x) (state=%x)", + la, + MBR_STATUS(resp), + MBR_BNO_STATE(resp)); + } + else{ + pvxidi->msg_dev_online = TRUE; + } + + /* + * Dont send begin normal operation cmd + * to servants who have a commander + */ +/* + * apparently this is not a good enough test + * for CMDR since some devices are rejecting this cmd + */ +#if 0 + if(cmdr){ + unsigned long sa=0; + + printf("Found a msg based cmdr\n"); + + status = epvxiCmdQuery( + la, + (unsigned long)MBC_READ_SERVANT_AREA, + &sa); + if(status){ + errMessage( + status, + "vxi resman: rsa failed"); + } + else{ + sa = sa & MBR_READ_SERVANT_AREA_MASK; + printf( + "The servant area was %d\n", + sa); + la += sa; + } + } +#endif + } +} + + +/* + * + * VXI_SELF_TEST + * check self test bits and place in safe state if failed + * print message about failed devices + * + */ +LOCAL EPVXISTAT vxi_self_test(void) +{ + unsigned la; + uint16_t wd; + struct vxi_csr *pcsr; + VXIDI **ppvxidi; + + + for( la=0, ppvxidi = epvxiLibDeviceList; + ppvxidi < epvxiLibDeviceList+NELEMENTS(epvxiLibDeviceList); + ppvxidi++, la++){ + + if(!*ppvxidi){ + continue; + } + + pcsr = VXIBASE(la); + + wd = pcsr->dir.r.status; + + if(VXIPASSEDSTATUS(wd)){ + (*ppvxidi)->st_passed = TRUE; + } + else{ + errMessage( + S_epvxi_selfTestFailed, + "VXI resman: device self test failed"); + epvxiSetDeviceOffline(la); + } + } + + return VXI_SUCCESS; +} + + +/* + * + * + * epvxiSetDeviceOffline() + * + */ +LOCAL EPVXISTAT epvxiSetDeviceOffline( +unsigned la +) +{ + struct vxi_csr *pcsr; + + pcsr = VXIBASE(la); + + errPrintf( + S_epvxi_badConfig, + __FILE__, + __LINE__, + "WARNING: VXI device placed off line %c(LA=0x%X)", + BELL, + la); + + pcsr->dir.w.control = VXISAFECONTROL; + + return VXI_SUCCESS; +} + + +/* + * + * VXI_ADDRESS_CONFIG + * + */ +LOCAL void vxi_address_config(void) +{ + char *pBase; + EPVXISTAT status; + + /* + * fetch the EPICS address ranges from the global + * symbol table if they are available + */ + status = symbol_value_fetch( + "_EPICS_VXI_A24_BASE", + &root_extender.A24_base, + sizeof(root_extender.A24_base)); + if(status){ + root_extender.A24_base = DEFAULT_VXI_A24_BASE; + } + status = symbol_value_fetch( + "_EPICS_VXI_A24_SIZE", + &root_extender.A24_size, + sizeof(root_extender.A24_size)); + if(status){ + root_extender.A24_size = DEFAULT_VXI_A24_SIZE; + } + status = symbol_value_fetch( + "_EPICS_VXI_A32_BASE", + &root_extender.A32_base, + sizeof(root_extender.A32_base)); + if(status){ + root_extender.A32_base = DEFAULT_VXI_A32_BASE; + } + status = symbol_value_fetch( + "_EPICS_VXI_A32_SIZE", + &root_extender.A32_size, + sizeof(root_extender.A32_size)); + if(status){ + root_extender.A32_size = DEFAULT_VXI_A32_SIZE; + } + + /* + * find A24 and A32 on this processor + */ + status = sysBusToLocalAdrs( + VME_AM_STD_SUP_DATA, + (char *)root_extender.A24_base, + &pBase); + if(status == OK){ + root_extender.A24_base = (long) pBase; + root_extender.A24_ok = TRUE; + } + else{ + root_extender.A24_ok = FALSE; + errMessage( + S_epvxi_badConfig, + "A24 VXI Base Addr problems"); + } + status = sysBusToLocalAdrs( + VME_AM_EXT_SUP_DATA, + (char *)root_extender.A32_base, + &pBase); + if(status == OK){ + root_extender.A32_base = (long) pBase; + root_extender.A32_ok = TRUE; + } + else{ + root_extender.A32_ok = FALSE; + errMessage( + S_epvxi_badConfig, + "A32 VXI Base Addr problems"); + } + + vxi_allocate_address_block(&root_extender); +} + + +/* + * + * VXI_ALLOCATE_ADDRESS_BLOCK + * + */ +LOCAL void vxi_allocate_address_block( +VXIE *pvxie +) +{ + unsigned la; + struct vxi_csr *pcsr; + VXIDI **ppvxidi; + VXIE *psubvxie; + unsigned long A24_base; + unsigned long A24_size; + unsigned long A32_base; + unsigned long A32_size; + + if(!pvxie->la_mapped){ + return; + } + + switch(pvxie->type){ + case ext_export_vxi_onto_mxi: + case ext_import_mxi_into_vxi: + pvxie->A24_base = MXIA24ALIGN(pvxie->A24_base); + pvxie->A32_base = MXIA32ALIGN(pvxie->A32_base); + pvxie->A24_size &= ~MXIA24MASK; + pvxie->A32_size &= ~MXIA32MASK; + break; + case ext_local_cpu: + default: + break; + } + + A24_base = pvxie->A24_base; + A24_size = pvxie->A24_size; + A32_base = pvxie->A32_base; + A32_size = pvxie->A32_size; + + psubvxie = (VXIE *) &pvxie->extenders.node; + while(psubvxie = (VXIE *) ellNext((ELLNODE *)psubvxie)){ + + psubvxie->A24_base = A24_base; + psubvxie->A24_size = A24_size; + psubvxie->A32_base = A32_base; + psubvxie->A32_size = A32_size; + psubvxie->A24_ok = pvxie->A24_ok; + psubvxie->A32_ok = pvxie->A32_ok; + + vxi_allocate_address_block(psubvxie); + + if(psubvxie->A24_mapped){ + A24_base = psubvxie->A24_base + psubvxie->A24_size; + A24_size -= psubvxie->A24_size; + pvxie->A24_mapped = TRUE; + } + + if(psubvxie->A32_mapped){ + A32_base = psubvxie->A32_base + psubvxie->A32_size; + A32_size -= psubvxie->A32_size; + pvxie->A32_mapped = TRUE; + } + } + + for( la=pvxie->la_low, ppvxidi = &epvxiLibDeviceList[la]; + ppvxidi <= &epvxiLibDeviceList[pvxie->la_high]; + ppvxidi++, la++){ + + unsigned long m; + unsigned long size; + unsigned long mask; + + if(!*ppvxidi){ + continue; + } + + /* + * dont configure devices lower in the hierarchy + */ + if((*ppvxidi)->A24_mapped || (*ppvxidi)->A32_mapped){ + continue; + } + + pcsr = VXIBASE(la); + + m = VXIREQMEM(pcsr); + + switch(VXIADDRSPACE(pcsr)){ + case VXI_ADDR_EXT_A24: + if(!pvxie->A24_ok){ + break; + } + + /* + * perform any needed alignment + */ + size = VXIA24MEMSIZE(m); + if(size>A24_size){ + errPrintf( + S_epvxi_badConfig, + __FILE__, + __LINE__, + "VXI A24 device does not fit Request=%d Avail=%d LA=0X%X", + size, + A24_size, + la); + epvxiSetDeviceOffline(la); + break; + } + mask = size-1; + A24_base = ((A24_base)+mask)&(~mask); + pcsr->dir.w.offset = A24_base>>8; + pcsr->dir.w.control = VXIMEMENBLCONTROL; + (*ppvxidi)->pFatAddrBase = (void *) A24_base; + (*ppvxidi)->A24_mapped = TRUE; + pvxie->A24_mapped = TRUE; + A24_base += size; + A24_size -= size; + break; + + case VXI_ADDR_EXT_A32: + if(!pvxie->A32_ok){ + break; + } + + /* + * perform any needed alignment + */ + size = VXIA32MEMSIZE(m); + if(size>A32_size){ + errPrintf( + S_epvxi_badConfig, + __FILE__, + __LINE__, + "VXI A32 device does not fit Request=%d Avail=%d LA=0X%X", + size, + A32_size, + la); + epvxiSetDeviceOffline(la); + break; + } + mask = size-1; + A32_base = (A32_base+mask)&(~mask); + pcsr->dir.w.offset = A32_base>>16; + pcsr->dir.w.control = VXIMEMENBLCONTROL; + (*ppvxidi)->pFatAddrBase = (void *) A32_base; + (*ppvxidi)->A32_mapped = TRUE; + pvxie->A32_mapped = TRUE; + A32_base += size; + A32_size -= size; + break; + + default: + /* + * do nothing + */ + break; + } + } + + pcsr = VXIBASE(pvxie->la); + + if(pvxie->A24_mapped){ + pvxie->A24_size = pvxie->A24_size - A24_size; + pvxie->A24_size = MXIA24ALIGN(pvxie->A24_size); + } + + if(pvxie->A32_mapped){ + pvxie->A32_size = pvxie->A32_size - A32_size; + pvxie->A32_size = MXIA32ALIGN(pvxie->A32_size); + } + + switch(pvxie->type){ + case ext_export_vxi_onto_mxi: + if(pvxie->A24_mapped){ + /* + * window enables only after the low + * byte is written + */ + pcsr->dir.w.dd.mxi.a24_window_high = + (pvxie->A24_base+pvxie->A24_size) + >> MXIA24MASKSIZE; + pcsr->dir.w.dd.mxi.a24_window_low = + pvxie->A24_base + >> MXIA24MASKSIZE; + } + if(pvxie->A32_mapped){ + /* + * window enables only after the low + * byte is written + */ + pcsr->dir.w.dd.mxi.a32_window_high = + (pvxie->A32_base+pvxie->A32_size) + >> MXIA32MASKSIZE; + pcsr->dir.w.dd.mxi.a32_window_low = + pvxie->A32_base + >> MXIA32MASKSIZE; + } + break; + + case ext_import_mxi_into_vxi: + if(pvxie->A24_mapped){ + /* + * window enables only after the low + * byte is written + */ + pcsr->dir.w.dd.mxi.a24_window_high = + pvxie->A24_base + >> MXIA24MASKSIZE; + pcsr->dir.w.dd.mxi.a24_window_low = + (pvxie->A24_base+pvxie->A24_size) + >> MXIA24MASKSIZE; + } + if(pvxie->A32_mapped){ + /* + * window enables only after the low + * byte is written + */ + pcsr->dir.w.dd.mxi.a32_window_high = + pvxie->A32_base + >> MXIA32MASKSIZE; + pcsr->dir.w.dd.mxi.a32_window_low = + (pvxie->A32_base+pvxie->A32_size) + >> MXIA32MASKSIZE; + } + break; + + case ext_local_cpu: + default: + break; + } +} + + +/* + * + * symbol_value_fetch + * + */ +LOCAL EPVXISTAT symbol_value_fetch( +char *pname, +void *pdest, +unsigned dest_size +) +{ + EPVXISTAT status; + UINT8 type; + char *pvalue; + + status = symFindByName( + sysSymTbl, + pname, + &pvalue, + &type); + if(status == OK){ + bcopy(pvalue, pdest, dest_size); + return VXI_SUCCESS; + } + else{ + return S_epvxi_internal; + } +} + + +/* + * + * VXI_INIT_IGNORE_LIST + * init list of interrupt handlers to ignore + * + */ +LOCAL EPVXISTAT vxi_init_ignore_list(void) +{ + int i; + UINT8 type; + EPVXISTAT status; + + for(i=0; ireg)){ + errMessage( + S_epvxi_internal, + "bad crate for set_reg_modid"); + return; + } + VXI_SET_REG_MODID(pvxisz->pcsr, slot); +} + +/* + * + * CLR_ALL_REG_MODID + * + */ +LOCAL void clr_all_reg_modid( +VXISZ *pvxisz +) +{ + if(!(pvxisz->reg)){ + errMessage( + S_epvxi_internal, + "bad crate for clr_all_reg_modid"); + return; + } + VXI_CLR_ALL_REG_MODID(pvxisz->pcsr); +} + + + +/* + * + * NIVXI_CPU030_SET_MODID + * + */ +#ifdef NICPU030 +LOCAL void nivxi_cpu030_set_modid( +VXISZ *pvxisz, +unsigned slot +) +{ + EPVXISTAT status; + + if(niCpu030Initialized){ + status = (*pnivxi_func[(unsigned)e_SetMODID])(TRUE,1<slot, + pmxidi->pvxisz->la, + pmxidi->class); + printf("\t"); + if(pmxidi->pvxieSelf){ + printf("extender, "); + } + if(pmxidi->msg_dev_online){ + printf("msg online, "); + } + printf("driver ID %d, ", pmxidi->driverID); + if(taskIdVerify(pmxidi->taskID)>=0){ + printf( "opened by task %s, ", + taskName(pmxidi->taskID)); + } + printf("cmdr la=0x%X, ", pmxidi->commander_la); + printf("extdr la=0x%X, ", pmxidi->extender_la); + printf("slot-zero la=0x%X, ", pmxidi->slot_zero_la); + printf("make 0X%X, ", (unsigned) pmxidi->make); + printf("model 0x%X, ", pmxidi->model); + printf( "pio_report_func %x, ", + (unsigned) pmxidi->pio_report_func); + printf("\n"); + } + i++; + ppmxidi++; + } + + return VXI_SUCCESS; +} + + +/* + * + * epvxiCrateList() + * + */ +EPVXISTAT epvxiCrateList(void) +{ + VXISZ *pvxisz; + + printf("VXI crate list\n"); + pvxisz = (VXISZ *) crateList.node.next; + while(pvxisz){ + + printf("LA=0X%X", pvxisz->la); + printf( ", extender LA=0X%X", + pvxisz->pvxie->la); + if(pvxisz->reg){ + printf(", register device"); + } + if(pvxisz->msg){ + printf(", message device"); + } + if(pvxisz->nicpu030){ + printf(", NI030"); + } + printf("\n"); + pvxisz = (VXISZ *) pvxisz->node.next; + } + + return VXI_SUCCESS; +} + + +/* + * + * epvxiUniqueDriverID() + * + * return a non zero unique id for a VXI driver + */ +long epvxiUniqueDriverID(void) +{ + if(epvxiNextDriverIDdriverID == vxiDriverID){ + return S_epvxi_deviceOpen; + } + else if(pvxidi->driverID != NO_DRIVER_ATTACHED_ID){ + return S_epvxi_notOwner; + } + + if(driverConfigSize){ + pconfig = (void *)calloc(1,driverConfigSize); + if(!pconfig){ + return S_epvxi_noMemory; + } + pvxidi->pDriverConfig = pconfig; + } + else{ + pvxidi->pDriverConfig = NULL; + } + + pvxidi->pio_report_func = pio_report_func; + + pvxidi->taskID = taskIdSelf(); + pvxidi->driverID = vxiDriverID; + + return VXI_SUCCESS; +} + + +/* + * + * epvxiDeviceVerify() + * + * + */ +EPVXISTAT epvxiDeviceVerify(unsigned la) +{ + EPVXISTAT status; + VXICSR *pcsr; + VXIDI *pvxidi; + uint16_t device_status; + + if(la > NELEMENTS(epvxiLibDeviceList)){ + return S_epvxi_badLA; + } + + pvxidi = epvxiLibDeviceList[la]; + if(!pvxidi){ + return S_epvxi_uknDevice; + } + + /* + * verify that the device exists + * and check the self test in memory + * since this may run before + * the self tests are verified. + */ + pcsr = VXIBASE(la); + status = vxMemProbe( (char *)&pcsr->dir.r.status, + READ, + sizeof(pcsr->dir.r.status), + (char *)&device_status); + if(status != OK){ + return S_epvxi_uknDevice; + } + if(!VXIPASSEDSTATUS(device_status)){ + return S_epvxi_selfTestFailed; + } + + return VXI_SUCCESS; +} + + +/* + * + * epvxiClose() + * + * 1) Unregister a driver's ownership of a device + * 2) Free driver's configuration block if one is allocated + */ +EPVXISTAT epvxiClose( +unsigned la, +int vxiDriverID +) +{ + VXIDI *pvxidi; + + if(la > NELEMENTS(epvxiLibDeviceList)){ + return S_epvxi_badLA; + } + + pvxidi = epvxiLibDeviceList[la]; + + if(pvxidi){ + if(pvxidi->driverID == vxiDriverID){ + pvxidi->driverID = NO_DRIVER_ATTACHED_ID; + if(pvxidi->pDriverConfig){ + free(pvxidi->pDriverConfig); + pvxidi->pDriverConfig = NULL; + } + return VXI_SUCCESS; + } + return S_epvxi_notOwner; + } + + return S_epvxi_notOwner; +} + + +/* + * + * epvxiLookupLA() + * + */ +EPVXISTAT epvxiLookupLA( +epvxiDeviceSearchPattern *pdsp, +void (*pfunc)(), +void *parg +) +{ + VXIDI *plac; + unsigned i; + + for(i=first_la; i<=last_la; i++){ + long flags; + + flags = pdsp->flags; + plac = epvxiLibDeviceList[i]; + + /* + * skip devices not present + */ + if(!plac){ + continue; + } + + if(flags & VXI_DSP_make){ + if(plac->make != pdsp->make){ + continue; + } + } + + if(flags & VXI_DSP_model){ + if(plac->model != pdsp->model){ + continue; + } + } + + if(flags & VXI_DSP_class){ + if(plac->class != pdsp->class){ + continue; + } + } + + if(flags & VXI_DSP_slot){ + if(plac->slot != pdsp->slot){ + continue; + } + } + + if(flags & VXI_DSP_slot_zero_la){ + if(plac->slot_zero_la != pdsp->slot_zero_la){ + continue; + } + } + + if(flags & VXI_DSP_commander_la){ + if(plac->commander_la != pdsp->commander_la){ + continue; + } + } + + if(flags & VXI_DSP_extender_la){ + if(plac->extender_la != pdsp->extender_la){ + continue; + } + } + + (*pfunc)(i, parg); + + } + + return VXI_SUCCESS; +} + + +/* + * epvxiRouteTriggerECL() + */ +EPVXISTAT epvxiRouteTriggerECL( +unsigned la, /* slot zero device logical address */ +unsigned enable_map, /* bits 0-5 correspond to trig 0-5 */ + /* a 1 enables a trigger */ + /* a 0 disables a trigger */ +unsigned io_map /* bits 0-5 correspond to trig 0-5 */ + /* a 1 sources the front panel */ + /* a 0 sources the back plane */ +) +{ + VXIDI *plac; + struct vxi_csr *pcsr; + char mask; + EPVXISTAT status; + int i; + + mask = (1<> 1, + io_map = io_map >> 1, + i++){ + + int (*pfunc)(); + int src; + int dest; + + if(!(enable_map&1)){ + continue; + } + + if(io_map&1){ + src = TRIG_LINE_FPOUT; + dest = TRIG_LINE_ECL_BASE + i; + } + else{ + src = TRIG_LINE_FPIN; + dest = TRIG_LINE_ECL_BASE + i; + } + + pfunc = pnivxi_func[(unsigned)e_MapTrigToTrig]; + status = (*pfunc)( + la, + src, + dest, + 0); + if(status < 0){ + status = S_epvxi_badTrigIO; + errPrintf( + status, + __FILE__, + __LINE__, + "NI CPU030 ECL trig map fail LA=0X%X", + la); + return status; + } + + } + return VXI_SUCCESS; + } + } + + plac = epvxiLibDeviceList[la]; + if(plac){ + if(!plac->st_passed){ + return S_epvxi_selfTestFailed; + } + } + else{ + return S_epvxi_noDevice; + } + + pcsr = VXIBASE(la); + + if(VXIMXI(pcsr)){ + int ctrl = MXI_CONTROL_CONSTANT; + + if(enable_map & (1<<0)){ + ctrl |= MXI_ECL0_ENABLE; + } + if(io_map & (1<<0)){ + ctrl |= MXI_ECL0_BP_TO_FP; + } + else{ + ctrl |= MXI_ECL0_FP_TO_BP; + } + + + if(enable_map & (1<<1)){ + ctrl |= MXI_ECL1_ENABLE; + } + if(io_map & (1<<1)){ + ctrl |= MXI_ECL1_BP_TO_FP; + } + else{ + ctrl |= MXI_ECL1_FP_TO_BP; + } + + pcsr->dir.w.dd.mxi.control = ctrl; + + return VXI_SUCCESS; + } + + /* + * HP MODEL E1404 trigger routing + */ + if(VXIMAKE(pcsr)==VXI_MAKE_HP){ + if( VXIMODEL(pcsr)==VXI_HP_MODEL_E1404_REG || + VXIMODEL(pcsr)==VXI_HP_MODEL_E1404_REG_SLOT0){ + return hpE1404RouteTriggerECL( + la, + enable_map, + io_map); + } + } + + status = S_epvxi_uknDevice; + errPrintf( + status, + __FILE__, + __LINE__, + "failed to map ECL trigger for (la=0x%X)", + la); + return status; +} + + +/* + * epvxiRouteTriggerTTL() + * + */ +EPVXISTAT epvxiRouteTriggerTTL( +unsigned la, /* slot zero device logical address */ +unsigned enable_map, /* bits 0-5 correspond to trig 0-5 */ + /* a 1 enables a trigger */ + /* a 0 disables a trigger */ +unsigned io_map /* bits 0-5 correspond to trig 0-5 */ + /* a 1 sources the front panel */ + /* a 0 sources the back plane */ +) +{ + VXIDI *plac; + struct vxi_csr *pcsr; + unsigned mask; + EPVXISTAT status; + int i; + + mask = (1<> 1, + io_map = io_map >> 1, + i++){ + + int (*pfunc)(); + int src; + int dest; + + if(!(enable_map&1)){ + continue; + } + + if(io_map&1){ + src = TRIG_LINE_FPOUT; + dest = TRIG_LINE_TTL_BASE + i; + } + else{ + src = TRIG_LINE_FPIN; + dest = TRIG_LINE_TTL_BASE + i; + } + + pfunc = pnivxi_func[(unsigned)e_MapTrigToTrig]; + status = (*pfunc)( + la, + src, + dest, + 0); + if(status < 0){ + status = S_epvxi_badTrigIO; + errPrintf( + status, + __FILE__, + __LINE__, + "NI030 TTL trig map fail LA=0X%X", + la); + return status; + } + + } + return VXI_SUCCESS; + } + } + + plac = epvxiLibDeviceList[la]; + if(plac){ + if(!plac->st_passed){ + return S_epvxi_selfTestFailed; + } + } + else{ + return S_epvxi_noDevice; + } + + + pcsr = VXIBASE(la); + + if(VXIMXI(pcsr)){ + int16_t tmp; + + tmp = ~io_map & enable_map; + tmp = (enable_map<<8) | tmp; + pcsr->dir.w.dd.mxi.trigger_config = tmp; + + return VXI_SUCCESS; + } + + /* + * HP MODEL E1404 trigger routing + */ + if(VXIMAKE(pcsr)==VXI_MAKE_HP){ + if( VXIMODEL(pcsr)==VXI_HP_MODEL_E1404_REG || + VXIMODEL(pcsr)==VXI_HP_MODEL_E1404_REG_SLOT0){ + return hpE1404RouteTriggerTTL( + la, + enable_map, + io_map); + } + } + + status = S_epvxi_uknDevice; + errPrintf( + status, + __FILE__, + __LINE__, + "Failed to map TTL trigger for (LA=%x%X)", + la); + return status; +} + + +/* + * vxi_io_report() + */ +EPVXISTAT vxi_io_report( +unsigned level +) +{ + return epvxiIOReport(level); +} + + +/* + * + * epvxiIOReport + * + * call io report routines for all registered devices + * + */ +EPVXISTAT epvxiIOReport( +unsigned level +) +{ + unsigned la; + unsigned resmanLA; + EPVXISTAT status; + + /* Get local address from VME address. */ + /* in case the resource manager has not been called */ + if(!epvxi_local_base){ + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + (char *)VXIBASEADDR, + (char **)&epvxi_local_base); + if(status != OK){ + status = S_epvxi_badConfig; + errMessage( + status, + "A16 base map failed"); + return status; + } + } + + /* + * special support for the niCPU030 + * since it does not see itself + */ + nicpu030_init(&root_extender); + + if(niCpu030Initialized){ + if(pnivxi_func[(unsigned)e_GetMyLA]){ + resmanLA = (*pnivxi_func[(unsigned)e_GetMyLA])(); + printf("VXI LA 0x%02X ", resmanLA); + } + } + else{ + printf("VXI LA "); + } + + printf("%s resident resource manager\n", + sysModel()); + + for(la=first_la; la<=last_la; la++){ + report_one_device(la, level); + } + + return VXI_SUCCESS; +} + + +/* + * + * report_one_device() + * + */ +LOCAL EPVXISTAT report_one_device( +unsigned la, +int level +) +{ + VXIDI *plac; + VXISZ *pvxisz; + unsigned slot; + EPVXISTAT status; + int make; + int model; + struct vxi_csr *pcsr; + int16_t id; + + pcsr = VXIBASE(la); + status = vxMemProbe( (char *)pcsr, + READ, + sizeof(id), + (char *)&id); + if(status != OK){ + return S_epvxi_internal; + } + + status = vxi_find_slot(pcsr, &slot, &pvxisz); + if(status){ + pvxisz = NULL; + slot = UKN_SLOT; + } + + + /* + * the logical address + */ + printf("VXI LA 0x%02X ", la); + + /* + * crate and slot + */ + if(pvxisz){ + printf( "slot zero LA=0X%02X slot %2d ", + pvxisz->la, + slot); + } + else{ + printf( "slot zero LA=?? slot=?? ", + UKN_LA, + UKN_LA); + } + + + /* + * make + */ + make = VXIMAKE(pcsr); + { + char buf[32]; + unsigned nactual; + + status = epuxiLookupMakeName( + make, + buf, + sizeof(buf)-1, + &nactual); + if(status==VXI_SUCCESS){ + buf[sizeof(buf)-1] = NULL; + printf("%s ", buf); + } + else{ + printf("make 0x%03X ", make); + } + } + + /* + * model + */ + model = VXIMODEL(pcsr); + { + char model_name[32]; + unsigned int nread; + + status = epuxiLookupModelName( + make, + model, + model_name, + sizeof(model_name)-1, + &nread); + if(status){ + printf( "model 0x%03X ", model); + } + else{ + model_name[sizeof(model_name)]=NULL; + printf( "%s ", model_name); + } + } + + printf("\n"); + + if(!VXIPASSEDSTATUS(pcsr->dir.r.status)){ + printf("\t---- Self Test Failed ----\n"); + return VXI_SUCCESS; + } + + /* + * call their io report routine if they supply one + */ + plac = epvxiLibDeviceList[la]; + if(plac){ + if(plac->pio_report_func){ + (*plac->pio_report_func)(la, level); + } + } + + if(level == 0){ + return VXI_SUCCESS; + } + + /* + * print out physical addresses of the cards + */ + printf("\tA16=0x%X ", (int) VXIBASE(la)); + if(VXIMEMENBL(pcsr)){ + long VMEmod = NULL; + char *VMEaddr = NULL; + char *pname = NULL; + char *pbase; + + switch(VXIADDRSPACE(pcsr)){ + case VXI_ADDR_EXT_A24: + VMEmod = VME_AM_STD_SUP_DATA; + VMEaddr = (char *) (pcsr->dir.w.offset<<8); + pname = "A24"; + break; + case VXI_ADDR_EXT_A32: + VMEmod = VME_AM_EXT_SUP_DATA; + VMEaddr = (char *) (pcsr->dir.w.offset<<16); + pname = "A32"; + break; + } + if(pname){ + status = sysBusToLocalAdrs( + VMEmod, + VMEaddr, + &pbase); + if(status>=0){ + printf( "%s=0x%X", + pname, + (unsigned)pbase); + } + else{ + printf( "failure mapping %s=%x", + pname, + (unsigned)VMEaddr); + } + } + } + printf("\n"); + + if(VXISLOT0MODEL(pcsr)){ + printf("\tSlot Zero Device\n"); + } + + printf( "\t%s device", + vxi_device_class_names[VXICLASS(pcsr)]); + switch(VXICLASS(pcsr)){ + case VXI_MEMORY_DEVICE: + break; + + case VXI_EXTENDED_DEVICE: + if(VXIMXI(pcsr)){ + mxi_io_report(pcsr, level); + } + break; + + case VXI_MESSAGE_DEVICE: + { + unsigned long resp; + + if(VXICMDR(pcsr)){ + printf(", cmdr"); + } + if(VXIFHS(pcsr)){ + printf(", fh"); + } + if(VXISHM(pcsr)){ + printf(", shm"); + } + if(VXIMBINT(pcsr)){ + printf(", interrupter"); + } + if(VXIVMEBM(pcsr)){ + printf(", VME bus master"); + } + if(VXISIGREG(pcsr)){ + printf(", has signal reg"); + } + printf("\n"); + /* + * all message based devices are required to + * implement this command query + */ + status = epvxiCmdQuery( + la, + (unsigned long)MBC_READ_PROTOCOL, + &resp); + if(status==VXI_SUCCESS){ + printf("\tprotocols("); + if(MBR_REV_12(resp)){ + printf("Rev 1.2 device, "); + } + if(MBR_RP_LW(resp)){ + printf("long word serial, "); + } + if(MBR_RP_ELW(resp)){ + printf("extended long word serial, "); + } + if(MBR_RP_I(resp)){ + printf("VXI instr, "); + } + if(MBR_RP_I4(resp)){ + printf("488 instr, "); + } + if(MBR_RP_TRG(resp)){ + printf("sft trig, "); + } + if(MBR_RP_PH(resp)){ + printf("prog int hdlr, "); + } + if(MBR_RP_PI(resp)){ + printf("prog interrupter, "); + } + if(MBR_RP_EG(resp)){ + printf("event gen, "); + } + if(MBR_RP_RG(resp)){ + printf("resp gen, "); + } + printf(")"); + } + break; + } + case VXI_REGISTER_DEVICE: + break; + + } + printf("\n"); + + return VXI_SUCCESS; +} + + +/* + * + * mxi_io_report() + * + * + */ +LOCAL void mxi_io_report( +struct vxi_csr *pmxi, +int level +) +{ + unsigned la; + unsigned ha; + unsigned a; + unsigned b; + char *msg; + + printf(", MXI sub class\n\t"); + + if(pmxi->dir.w.dd.mxi.control & MXI_UPPER_LOWER_BOUNDS){ + la = VXIADDRMASK & + pmxi->dir.w.dd.mxi.la_window; + ha = VXIADDRMASK & + (pmxi->dir.w.dd.mxi.la_window>>NVXIADDRBITS); + if(la 0 since la < ha + */ + ha--; + } + else if(la>ha){ + msg = "MXI LA's seen by this crate"; + /* + * la > 0 since la > ha + */ + la--; + } + else if(la == 0){ + msg = "LA window disabled"; + la = 0; + ha = 0; + } + else if(la >= 0x80){ + msg = "MXI LA's seen by this crate"; + la = 0; + ha = 0xff; + } + else{ + msg = "local VXI LA's seen by MXI"; + la = 0; + ha = 0xff; + } + + printf(", %s 0x%X-0x%X\n\t", + msg, + la, + ha); + + a = pmxi->dir.w.dd.mxi.a24_window_low; + b = pmxi->dir.w.dd.mxi.a24_window_high; + printf(", A24 window 0x%X-0x%X", + a, + b); + + a = pmxi->dir.w.dd.mxi.a32_window_low; + b = pmxi->dir.w.dd.mxi.a32_window_high; + printf(", A32 window 0x%X-0x%X", + a, + b); + } +# ifdef BASE_PLUS_SIZE_MXI_SUPPORT + else{ + la = VXIADDRMASK & + pmxi->dir.w.dd.mxi.la_window; + ha = la + (MXI_LA_WINDOW_SIZE_MASK & + (pmxi->dir.w.dd.mxi.la_window>>NVXIADDRBITS)); + + printf(", LA window 0x%X-0x%X", + la, + ha); + } +# endif +} + + +/* + * + * vxi_allocate_int_lines() + * + * + */ +LOCAL void vxi_allocate_int_lines(void) +{ + EPVXISTAT status; + struct vxi_csr *pcsr; + VXIDI **ppvxidi; + VXIDI *pvxidi; + unsigned la; + unsigned long resp; + unsigned long cmd; + unsigned line_count; + + for( la=0, ppvxidi = epvxiLibDeviceList; + ppvxidi < epvxiLibDeviceList+NELEMENTS(epvxiLibDeviceList); + ppvxidi++, la++){ + + pvxidi = *ppvxidi; + + if(!pvxidi){ + continue; + } + + pcsr = VXIBASE(la); + + if(VXICLASS(pcsr) != VXI_MESSAGE_DEVICE){ + continue; + } + + /* + * find out if this is a programmable interrupter + */ + status = epvxiCmdQuery( + la, + (unsigned long)MBC_READ_PROTOCOL, + &resp); + if(status){ + errPrintf( + status, + __FILE__, + __LINE__, + "Device rejected READ_PROTOCOL (LA=0x%X)", + la); + continue; + } + if(!MBR_RP_PI(resp)){ + continue; + } + + printf("Programming interrupter (LA=0x%X)\n", la); + + cmd = MBC_READ_INTERRUPTERS; + status = epvxiCmdQuery( + la, + cmd, + &resp); + if(status){ + errPrintf( + status, + __FILE__, + __LINE__, + "Device rejected READ_INTERRUPTERS (LA=0x%X)", + la); + continue; + } + line_count = resp&MBR_READ_INTERRUPTERS_MASK; + while(line_count--){ + cmd = MBC_ASSIGN_INTERRUPTER_LINE | + ((line_count+1)<<4) | + VXIMSGINTLEVEL; + sysIntEnable(VXIMSGINTLEVEL); + status = epvxiCmdQuery( + la, + cmd, + &resp); + if(status){ + errPrintf( + status, + __FILE__, + __LINE__, + "Device rejected ASSIGN_INT(LA=0x%X)", + la); + continue; + } + if(MBR_STATUS(resp) != MBR_STATUS_SUCCESS){ + errPrintf( + S_epvxi_msgDeviceFailure, + __FILE__, + __LINE__, + "ASSIGN_INT failed (LA=0x%X)", + la); + continue; + } + } + } +} + + + + +/* + * + * epvxiSymbolTableInit() + * (written by Richard Baker LANL summer intern) + * + */ +LOCAL EPVXISTAT epvxiSymbolTableInit(void) +{ + + epvxiSymbolTable = symTblCreate( + EPVXI_MAX_SYMBOLS_LOG2, + FALSE, + memSysPartId); + if(!epvxiSymbolTable){ + return S_epvxi_noMemory; + } + + return VXI_SUCCESS; +} + + +/* + * + * epuxiRegisterModelName() + * (written by Richard Baker LANL summer intern) + * + * + */ +EPVXISTAT epvxiRegisterModelName( +unsigned int make, +unsigned int model, +char *pmodel_name +) +{ + char name[EPVXI_MAX_SYMBOL_LENGTH]; + char *pcopy; + EPVXISTAT status; + + if(!epvxiSymbolTable){ /* initialize table at 1st call */ + status = epvxiSymbolTableInit(); + if(status){ + return status; + } + } + + sprintf(name, epvxiSymbolTableDeviceIdString, make,model); + pcopy = (char *) malloc(strlen(pmodel_name)+1); + if(pcopy == NULL){ + return S_epvxi_noMemory; + } + + strcpy(pcopy, pmodel_name); + + status = symAdd( + epvxiSymbolTable, + name, + pcopy, + EPVXI_MODEL_NAME_SYMBOL, + NULL); + if(status < 0){ + char *pold_model_name; + UINT8 type; + + free(pcopy); + + status = symFindByNameAndType( + epvxiSymbolTable, + name, + &pold_model_name, + &type, + EPVXI_MODEL_NAME_SYMBOL, + ~0); + if(status<0){ + return S_epvxi_noMemory; + } + else if(strcmp(pmodel_name, pold_model_name)){ + return S_epvxi_nameMismatch; + } + } + + return VXI_SUCCESS; +} + + +/* + * + * epvxiRegisterMakeName() + * + * + */ +EPVXISTAT epvxiRegisterMakeName( +unsigned int make, +char *pmake_name +) +{ + char name[EPVXI_MAX_SYMBOL_LENGTH]; + char *pcopy; + EPVXISTAT status; + + if(!epvxiSymbolTable){ /* initialize table at 1st call */ + status = epvxiSymbolTableInit(); + if(status){ + return status; + } + } + + sprintf(name, epvxiSymbolTableMakeIdString, make); + pcopy = (char *) malloc(strlen(pmake_name)+1); + if(pcopy == NULL){ + return S_epvxi_noMemory; + } + + strcpy(pcopy, pmake_name); + + status = symAdd( + epvxiSymbolTable, + name, + pcopy, + EPVXI_MAKE_NAME_SYMBOL, + NULL); + if(status<0){ + char *pold_make_name; + UINT8 type; + + free(pcopy); + + status = symFindByNameAndType( + epvxiSymbolTable, + name, + &pold_make_name, + &type, + EPVXI_MAKE_NAME_SYMBOL, + ~0); + if(status<0){ + return S_epvxi_noMemory; + } + else if(strcmp(pmake_name, pold_make_name)){ + return S_epvxi_nameMismatch; + } + } + + return VXI_SUCCESS; +} + + +/* + * + * epuxiLookupMakeName() + * (written by Richard Baker LANL summer intern) + * + */ +EPVXISTAT epuxiLookupMakeName( +unsigned int make, /* VXI manuf. */ +char *pbuffer, /* model name return */ +unsigned int bufsize, /* size of supplied buf */ +unsigned int *preadcount) /* n bytes written */ +{ + char name[EPVXI_MAX_SYMBOL_LENGTH]; + char *pmake_name; + UINT8 type; + EPVXISTAT status; + + if(!epvxiSymbolTable){ /* initialize table at 1st call */ + status = epvxiSymbolTableInit(); + if(status){ + return status; + } + } + + sprintf(name, epvxiSymbolTableMakeIdString, make); + status = symFindByNameAndType( + epvxiSymbolTable, + name, + &pmake_name, + &type, + EPVXI_MAKE_NAME_SYMBOL, + ~0); + if(status<0){ + return S_epvxi_noMatch; + } + if(type != EPVXI_MAKE_NAME_SYMBOL){ + abort(0); + } + *preadcount = min(strlen(pmake_name)+1, bufsize); + strncpy(pbuffer, pmake_name, bufsize); + + return VXI_SUCCESS; +} + + +/* + * + * epuxiLookupModelName() + * (written by Richard Baker LANL summer intern) + * + */ +EPVXISTAT epuxiLookupModelName( +unsigned int make, /* VXI manuf. */ +unsigned int model, /* VXI model code */ +char *pbuffer, /* model name return */ +unsigned int bufsize, /* size of supplied buf */ +unsigned int *preadcount) /* n bytes written */ +{ + char name[EPVXI_MAX_SYMBOL_LENGTH]; + char *pmodel_name; + UINT8 type; + EPVXISTAT status; + + if(!epvxiSymbolTable){ /* initialize table at 1st call */ + status = epvxiSymbolTableInit(); + if(status){ + return status; + } + } + + sprintf(name, epvxiSymbolTableDeviceIdString, make, model); + status = symFindByNameAndType( + epvxiSymbolTable, + name, + &pmodel_name, + &type, + EPVXI_MODEL_NAME_SYMBOL, + ~0); + if(status<0){ + return S_epvxi_noMatch; + } + if(type != EPVXI_MODEL_NAME_SYMBOL){ + abort(0); + } + *preadcount = min(strlen(pmodel_name)+1, bufsize); + strncpy(pbuffer, pmodel_name, bufsize); + + return VXI_SUCCESS; +} + + +/* + * + * epvxiExtenderList() + * + * list any bus extenders + */ +EPVXISTAT epvxiExtenderList(void) +{ + epvxiExtenderPrint(&root_extender); + + return VXI_SUCCESS; +} + + + +/* + * + * epvxiExtenderPrint + * + * + */ +LOCAL void epvxiExtenderPrint(VXIE *pvxie) +{ + VXIE *psubvxie; + + + printf( "%s Extender LA=0x%02X\n", + ext_type_name[pvxie->type], + pvxie->la); + + if(pvxie->la_mapped){ + printf("\tLA window 0x%02X - 0x%02X\n", + pvxie->la_low, + pvxie->la_high); + } + + if(pvxie->A24_mapped){ + printf("\tA24 window base=0x%08X size=0x%08X\n", + pvxie->A24_base, + pvxie->A24_size); + } + + if(pvxie->A32_mapped){ + printf("\tA32 window base=0x%08X size=0x%08X\n", + pvxie->A32_base, + pvxie->A32_size); + } + + psubvxie = (VXIE *) &pvxie->extenders.node; + while(psubvxie = (VXIE *) ellNext((ELLNODE *)psubvxie)){ + epvxiExtenderPrint(psubvxie); + } +} + + +/* + * + * register some common manufacturer names + * for consistency + * + */ +LOCAL void epvxiRegisterCommonMakeNames(void) +{ + int i; + EPVXISTAT status; + + for(i=0; i +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +enum msgDeviceSyncType { + syncInt, + syncSignal, + syncPoll +}; + +typedef +struct epvxiMessageDeviceInfo{ + unsigned err:1; /* error pending */ + unsigned trace:1; /* debug trace on */ + unsigned long timeout; /* in ticks */ + enum msgDeviceSyncType syncType; + SEM_ID syncSem; + FAST_LOCK lck; +}VXIMDI; + +#define VXIMSGSYNCDELAY 1 + +#define DEFAULTMSGTMO (sysClkRateGet()*10) /* 10 sec */ +#define MAXIMUMTMO (0xffffff) + +/* + * set to a valid LA when the LA of the + * commander is located + */ +LOCAL +int msgCommanderLA = (-1); + + +#define abort(A) taskSuspend(0) + +#define epvxiPMsgConfig(LA)\ +((VXIMDI *)(epvxiLibDeviceList[LA]?epvxiLibDeviceList[LA]->pMsgConfig:0)) + +/* + * local functions + */ +LOCAL void set_la( + unsigned la, + unsigned *pla +); + +LOCAL void vxiMsgInt( + unsigned la +); + +LOCAL void signalHandler( + int16_t signal +); + +LOCAL EPVXISTAT epvxiReadSlowHandshake( + unsigned la, + char *pbuf, + unsigned long count, + unsigned long *pread_count, + unsigned long option +); + +#ifdef FASTHANDSHAKE +LOCAL EPVXISTAT epvxiReadFastHandshake( + unsigned la, + char *pbuf, + unsigned long count, + unsigned long *pread_count, + unsigned long option +); +#endif + +LOCAL EPVXISTAT vxiMsgClose( + unsigned la +); + +LOCAL EPVXISTAT vxiMsgOpen( + unsigned la +); + +LOCAL void vxiMsgSignalSetup( + void +); + +LOCAL void vxiCPU030MsgSignalSetup( + void +); + +LOCAL void vxiHP1404MsgSignalSetup( + void +); + +LOCAL EPVXISTAT vxiAttemptAsyncModeControl( + unsigned la, + unsigned long cmd +); + +LOCAL EPVXISTAT vxiMsgSync( + unsigned la, + unsigned resp_mask, + unsigned resp_state, + int override_err +); + +LOCAL EPVXISTAT fetch_protocol_error( + unsigned la +); + + +/* + * should be in a header + */ +EPVXISTAT vxi_msg_test( + unsigned la +); + +EPVXISTAT vxi_msg_print_id( + unsigned la +); + +EPVXISTAT vxi_msg_test_protocol_error( + unsigned la +); + + +/* + * + * vxi_msg_test() + * + */ +EPVXISTAT vxi_msg_test( + unsigned la +) +{ + char buf[512]; + unsigned long count; + EPVXISTAT status; + + status = epvxiWrite(la, "*IDN?", 5, &count, epvxiWriteOptNone); + if(status != VXI_SUCCESS){ + return status; + } + status = epvxiRead(la, buf, sizeof(buf)-1, &count, epvxiReadOptNone); + if(status != VXI_SUCCESS){ + return status; + } + + buf[count] = NULL; + printf("%s %d\n", buf,count); + + status = epvxiWrite(la, "*TST?", 5, &count, epvxiWriteOptNone); + if(status != VXI_SUCCESS){ + return status; + } + status = epvxiRead(la, buf, sizeof(buf)-1, &count, epvxiReadOptNone); + if(status != VXI_SUCCESS){ + return status; + } + + buf[count] = NULL; + printf("%s %d\n", buf, count); + + return VXI_SUCCESS; +} + + +/* + * + * vxi_msg_print_id + * + */ +EPVXISTAT vxi_msg_print_id( + unsigned la +) +{ + char buf[32]; + unsigned long count; + char *pcmd = "*IDN?"; + EPVXISTAT status; + + status = epvxiWrite(la, pcmd, strlen(pcmd), &count, epvxiWriteOptNone); + if(status != VXI_SUCCESS){ + return status; + } + status = epvxiRead(la, buf, sizeof(buf)-1, &count, epvxiReadOptNone); + if(status != VXI_SUCCESS){ + return status; + } + + buf[count] = NULL; + printf(" %s ", buf); + + return VXI_SUCCESS; +} + + +/* + * + * vxi_msg_test_protocol_error + * + */ +EPVXISTAT vxi_msg_test_protocol_error( + unsigned la +) +{ + int i; + EPVXISTAT status; + + for(i=0;i<1000;i++){ + status = epvxiCmd(la, MBC_READ_PROTOCOL); + if(status){ + return status; + } + } + return VXI_SUCCESS; +} + + +/* + * epvxiCmd() + * + * deliver a command to a msg based device + * + */ +EPVXISTAT epvxiCmd( +unsigned la, +unsigned long cmd +) +{ + struct vxi_csr *pcsr; + VXIMDI *pvximdi; + EPVXISTAT status; + +# ifdef DEBUG + printf("cmd to be sent %4x (la=%d)\n", cmd, la); +# endif + + while(!(pvximdi = epvxiPMsgConfig(la))){ + status = vxiMsgOpen(la); + if(status != VXI_SUCCESS){ + return status; + } + } + + pcsr = VXIBASE(la); + + FASTLOCK(&pvximdi->lck); + + /* + * RULE C.3.3 + * A commander shall not send any command requiring a servant to + * place data in in its data registers until the commander has read + * (from the data registers) all data generated by previous commands + * (and the read ready bit is set to zero). + */ + status = vxiMsgSync( + la, + VXIWRITEREADYMASK|VXIREADREADYMASK, + VXIWRITEREADYMASK, + cmd == MBC_CLEAR); + if(status>=0){ + pcsr->dir.w.dd.msg.dlow = cmd; + } + else{ + /* + * RULE C.3.2 + */ + if(pcsr->dir.r.dd.msg.response&VXIREADREADYMASK){ + status = S_epvxi_unreadData; + } + } + + FASTUNLOCK(&pvximdi->lck); + + if(status == S_epvxi_protocolError){ + return fetch_protocol_error(la); + } + + if(pvximdi->trace){ + printf( "VXI Trace: (la=0X%X) Cmd -> %x\n", + la, + cmd); + } + + return status; +} + + + +/* + * epvxiQuery() + * + * query the response to a command + * + */ +EPVXISTAT epvxiQuery( +unsigned la, +unsigned long *presp +) +{ + struct vxi_csr *pcsr; + VXIMDI *pvximdi; + EPVXISTAT status; + + while(!(pvximdi = epvxiPMsgConfig(la))){ + status = vxiMsgOpen(la); + if(status != VXI_SUCCESS){ + return status; + } + } + + pcsr = VXIBASE(la); + + FASTLOCK(&pvximdi->lck); + + status = vxiMsgSync( + la, + VXIREADREADYMASK, + VXIREADREADYMASK, + FALSE); + if(status==VXI_SUCCESS){ + *presp = pcsr->dir.r.dd.msg.dlow; + } + + FASTUNLOCK(&pvximdi->lck); + +# ifdef DEBUG + printf("resp returned %4x (la=%d)\n", *presp, la); +# endif + + if(status == S_epvxi_protocolError){ + return fetch_protocol_error(la); + } + + if(pvximdi->trace){ + printf( "VXI Trace: (la=0X%X) Query -> %x\n", + la, + *presp); + } + + return status; +} + + +/* + * epvxiCmdQuery() + */ +EPVXISTAT epvxiCmdQuery( +unsigned la, +unsigned long cmd, +unsigned long *presp +) +{ + EPVXISTAT status; + + status = epvxiCmd(la, cmd); + if(status){ + return status; + } + status = epvxiQuery(la, presp); + return status; +} + + +/* + * epvxiRead() + * + * Read a string using fast handshake mode + * or call a routine to do a slow handshake + * if that is all that is supported. + */ +EPVXISTAT epvxiRead( +unsigned la, +char *pbuf, +unsigned long count, +unsigned long *pread_count, +unsigned long option +) +{ + VXIMDI *pvximdi; + EPVXISTAT status; + + while(!(pvximdi = epvxiPMsgConfig(la))){ + status = vxiMsgOpen(la); + if(status != VXI_SUCCESS){ + return status; + } + } + + /* + * does the device support fast handshake + */ +# ifdef FASTHANDSHAKE + if(VXIFHS(pcsr)){ + status = epvxiReadFastHandshake( + la, + pbuf, + count, + pread_count, + option); + } + else{ +# endif + status = epvxiReadSlowHandshake( + la, + pbuf, + count, + pread_count, + option); +# ifdef FASTHANDSHAKE + } +# endif + + if(pvximdi->trace){ + printf( "VXI Trace: (la=0X%X) Read -> %*s\n", + la, + (int)count, + pbuf); + } + + return status; +} + + +#ifdef FASTHANDSHAKE +@@@@ needs to tell them if their buffer is full + and the EOM bit wasnt se @@@@ +/* + * epvxiReadFastHandshake() + * + * Read a string using fast handshake mode + * or call a routine to do a slow handshake + * if that is all that is supported. + * + * This function will be tested and installed + * if a card with fast handshake s found to exist + * + */ +LOCAL EPVXISTAT epvxiReadFastHandshake( + unsigned la, + char *pbuf, + unsigned long count, + unsigned long *pread_count, + unsigned long option +) +{ + struct vxi_csr *pcsr; + VXIMDI *pvximdi; + short resp; + int fhm; + short cmd; + EPVXISTAT status; + int i; + + while(!(pvximdi = epvxiPMsgConfig(la))){ + status = vxiMsgOpen(la); + if(status != VXI_SUCCESS){ + return status; + } + } + + pcsr = VXIBASE(la); + + FASTLOCK(&pvximdi->lck); + fhm = FALSE; + /* + * always leave room to write a NULL termination + */ + for(i=0; i<(count-1); i++){ + + while(TRUE){ + /* + * wait for fast handshake mode + */ + if(!fhm){ + status = vxiMsgSync( + la, + VXIFHSMMASK, + 0, + FALSE); + if(status){ + *pread_count = i; + goto exit; + } + fhm = TRUE; + } + + cmd = MBC_BR; + status = vxMemProbe( + &pcsr->dir.r.dd.msg.dlow, + WRITE, + sizeof(pcsr->dir.r.dd.msg.dlow), + &cmd); + if(status == OK){ + break; + } + fhm = FALSE; + } + + while(TRUE){ + /* + * wait for fast handshake mode + */ + if(!fhm){ + status = vxiMsgSync( + la, + VXIFHSMMASK, + 0, + FALSE); + if(status){ + *pread_count = i; + goto exit; + } + fhm = TRUE; + } + + status = vxMemProbe( + &pcsr->dir.r.dd.msg.dlow, + READ, + sizeof(pcsr->dir.r.dd.msg.dlow), + &resp); + if(status == OK){ + break; + } + fhm = FALSE; + } + + *pbuf = resp; + pbuf++; + if(resp & MBC_END){ + *pread_count = i+1; + break; + } + } + status = VXI_SUCCESS; +exit: + FASTUNLOCK(&pvximdi->lck); + + if(status == S_epvxi_protocolError){ + return fetch_protocol_error(la); + } + + *pbuf = NULL; + + return status; +} +#endif + + +/* + * epvxiReadSlowHandshake() + */ +LOCAL +EPVXISTAT epvxiReadSlowHandshake( + unsigned la, + char *pbuf, + unsigned long count, + unsigned long *pread_count, + unsigned long option +) +{ + VXIMDI *pvximdi; + struct vxi_csr *pcsr; + short resp; + EPVXISTAT status; + int function_status; + int i; + + while(!(pvximdi = epvxiPMsgConfig(la))){ + status = vxiMsgOpen(la); + if(status != VXI_SUCCESS){ + return status; + } + } + + pcsr = VXIBASE(la); + + /* + * always leave room to write a NULL termination + */ + if(count<1){ + return S_epvxi_bufferFull; + } + + FASTLOCK(&pvximdi->lck); + + /* + * always leave room to write a NULL termination + */ + function_status = S_epvxi_bufferFull; + for(i=0; i<(count-1); i++){ + + /* + * wait for handshake + * + * RULE C.3.3 specifies that there shouldnt be + * any unread data present at this point . + */ + status = vxiMsgSync( + la, + VXIWRITEREADYMASK|VXIDORMASK|VXIREADREADYMASK, + VXIWRITEREADYMASK|VXIDORMASK, + FALSE); + if(status){ + if(pcsr->dir.r.dd.msg.response&VXIREADREADYMASK){ + function_status = S_epvxi_unreadData; + } + else{ + function_status = status; + } + break; + } + + pcsr->dir.w.dd.msg.dlow = MBC_BR; + + /* + * wait for handshake + */ + status = vxiMsgSync( + la, + VXIREADREADYMASK, + VXIREADREADYMASK, + FALSE); + if(status){ + function_status = status; + break; + } + + resp = pcsr->dir.r.dd.msg.dlow; + + *pbuf = resp; + pbuf++; + if(resp & MBC_END){ + + /* + * so the read count will be correct below + */ + i++; + function_status = VXI_SUCCESS; + break; + } + } + FASTUNLOCK(&pvximdi->lck); + + *pread_count = i; + + /* + * append the NULL + */ + *pbuf = NULL; + + if(function_status == S_epvxi_protocolError){ + return fetch_protocol_error(la); + } + + return function_status; +} + + +/* + * epvxiWrite() + * (set the end bit on the last byte sent) + */ +EPVXISTAT epvxiWrite( +unsigned la, +char *pbuf, +unsigned long count, +unsigned long *pwrite_count, +unsigned long option +) +{ + VXIMDI *pvximdi; + struct vxi_csr *pcsr; + int i; + short cmd; + short extra; + EPVXISTAT status; + char *pstr; + + while(!(pvximdi = epvxiPMsgConfig(la))){ + status = vxiMsgOpen(la); + if(status != VXI_SUCCESS){ + return status; + } + } + + pcsr = VXIBASE(la); + + FASTLOCK(&pvximdi->lck); + pstr = pbuf; + if(option&epvxiWriteOptPartialMsg){ + extra = 0; + } + else{ + extra = MBC_END; + } + for(i=0; idir.r.dd.msg.dlow = cmd; + pstr++; + } + *pwrite_count = i; + status = VXI_SUCCESS; +exit: + FASTUNLOCK(&pvximdi->lck); + + if(status == S_epvxi_protocolError){ + return fetch_protocol_error(la); + } + + if(pvximdi->trace){ + printf( "VXI Trace: (la=0X%X) Write -> %*s\n", + la, + (int)count, + pbuf); + } + + return status; +} + + +/* + * + * epvxiSetTimeout() + * + * change the message based transfer timeout + * (timeout is in milli sec) + * + */ +EPVXISTAT epvxiSetTimeout( +unsigned la, +unsigned long timeout +) +{ + VXIMDI *pvximdi; + EPVXISTAT status; + + while(!(pvximdi = epvxiPMsgConfig(la))){ + status = vxiMsgOpen(la); + if(status != VXI_SUCCESS){ + return status; + } + } + + /* + * order of operations significant here + */ + if(timeout > MAXIMUMTMO){ + return S_epvxi_timeoutToLarge; + } + + pvximdi->timeout = (timeout * sysClkRateGet())/1000; + + return VXI_SUCCESS; +} + + +/* + * + * epvxiSetTraceEnable() + * + * turn trace mode on or off + * + */ +EPVXISTAT epvxiSetTraceEnable(la, enable) +unsigned la; +int enable; +{ + VXIMDI *pvximdi; + EPVXISTAT status; + + while(!(pvximdi = epvxiPMsgConfig(la))){ + status = vxiMsgOpen(la); + if(status != VXI_SUCCESS){ + return status; + } + } + + pvximdi->trace = enable?TRUE:FALSE; + + return VXI_SUCCESS; +} + + +/* + * + * vxiMsgClose() + * + * + */ +LOCAL +EPVXISTAT vxiMsgClose( +unsigned la +) +{ + EPVXISTAT status; + VXIMDI *pvximdi; + + pvximdi = epvxiPMsgConfig(la); + if(!pvximdi){ + return S_epvxi_notOpen; + } + + status = semDelete(pvximdi->syncSem); + if(status){ + errMessage( + S_epvxi_internal, + "vxiMsgClose(): bad sem id"); + } + FASTLOCKFREE(&pvximdi->lck); + return VXI_SUCCESS; +} + + +/* + * + * vxiMsgOpen() + * + * + */ +LOCAL +EPVXISTAT vxiMsgOpen( + unsigned la +) +{ + EPVXISTAT status; + VXIDI *pvxidi; + VXIMDI *pvximdi; + unsigned long resp; + unsigned long read_proto_resp; + unsigned long cmd; + struct vxi_csr *pcsr; + int signalSync = FALSE; + int intSync = FALSE; + + + /* + * return quickly if we have been here before + */ + pvxidi = epvxiLibDeviceList[la]; + if(pvxidi){ + if(pvxidi->pMsgConfig){ + return VXI_SUCCESS; + } + } + + /* + * standard verification of unknown LA + */ + status = epvxiDeviceVerify(la); + if(status){ + return status; + } + + pcsr = VXIBASE(la); + if(VXICLASS(pcsr) != VXI_MESSAGE_DEVICE){ + return S_epvxi_notMsgDevice; + } + + pvximdi = (VXIMDI *) calloc(1, sizeof(*pvximdi)); + if(!pvximdi){ + return S_epvxi_noMemory; + } + + pvxidi->pMsgConfig = (void *) pvximdi; + + vxiMsgSignalSetup(); + + pvximdi->syncSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + if(!pvximdi->syncSem){ + return S_epvxi_noMemory; + } + + /* + * + * assume the worst for the transfers below + * + */ + pvximdi->timeout = DEFAULTMSGTMO; + pvximdi->syncType = syncPoll; + FASTLOCKINIT(&pvximdi->lck); + + /* + * if it is not an interrupter or a signal + * generator then we poll + */ + if(!VXIMBINT(pcsr) && !VXIVMEBM(pcsr)){ + return VXI_SUCCESS; + } + + /* + * if it is not a response generator then we poll + */ + status = epvxiCmdQuery( + la, + (unsigned long) MBC_READ_PROTOCOL, + &read_proto_resp); + if(status){ + /* + * All devices are required by the VXI standard + * to accept this command while in the + * configure state or in the normal operation + * state. Some dont. + * + */ + errPrintf( + status, + __FILE__, + __LINE__, + "Device rejected MBC_READ_PROTOCOL (la=0X%X)", + la); + return VXI_SUCCESS; + } + +return VXI_SUCCESS; + + if(!MBR_RP_RG(read_proto_resp)){ + return VXI_SUCCESS; + } + +printf("mb device has response gen\n"); + + /* + * try to setup interrupt synchronization first + * (this works even if we dont have a signal register) + */ + if(VXIMBINT(pcsr)){ + cmd = MBC_ASYNC_MODE_CONTROL | + MBC_AMC_RESP_ENABLE | + MBC_AMC_RESP_INT_ENABLE; + status = vxiAttemptAsyncModeControl(la, cmd); + if(status>=0){ + printf( "%s: mb device has int sync!\n", + __FILE__); + intSync = TRUE; + } + } + + /* + * hopefully a signal register is available if we get to here + */ + if(VXIVMEBM(pcsr) && !intSync && msgCommanderLA>=0){ + cmd = MBC_ASYNC_MODE_CONTROL | + MBC_AMC_RESP_ENABLE | + MBC_AMC_EVENT_ENABLE | + MBC_AMC_RESP_SIGNAL_ENABLE | + MBC_AMC_EVENT_SIGNAL_ENABLE; + status = vxiAttemptAsyncModeControl(la, cmd); + if(status>=0){ + printf( "%s: mb device has signal sync!\n", + __FILE__); + signalSync = TRUE; + } + } + + if(!intSync && !signalSync){ + errMessage( + S_epvxi_msgDeviceFailure, + "mb responder failed to configure"); + return VXI_SUCCESS; + } + + cmd = MBC_CONTROL_RESPONSE; + status = epvxiCmdQuery( + la, + cmd, + &resp); + if(status){ + errMessage( + status, + "Control response rejected by responder"); + vxiMsgClose(la); + return status; + } + if( MBR_STATUS(resp) != MBR_STATUS_SUCCESS || + (resp^cmd)&MBR_CR_CONFIRM_MASK){ + errPrintf( + S_epvxi_msgDeviceFailure, + __FILE__, + __LINE__, + "Control Response Failed %x", + resp); + return VXI_SUCCESS; + } +printf("sent ctrl resp (la=%d) (cmd=%x)\n", la, cmd); + +printf("synchronized msg based device is ready!\n"); + + if(intSync){ + pvximdi->syncType = syncInt; + } + if(signalSync){ + pvximdi->syncType = syncSignal; + } + + return VXI_SUCCESS; +} + + +/* + * + * vxiMsgSignalSetup + * + * + */ +LOCAL +void vxiMsgSignalSetup( + void +) +{ + static char vxiMsgSignalInit; + + if(vxiMsgSignalInit){ + return; + } + + vxiMsgSignalInit = TRUE; + + vxiHP1404MsgSignalSetup(); + + if(msgCommanderLA<0){ + vxiCPU030MsgSignalSetup(); + } + + if(msgCommanderLA<0){ + errMessage( + S_epvxi_noCmdr, + NULL); + } +} + + +/* + * + * vxiCPU030MsgSignalSetup + * + * + */ +LOCAL void vxiCPU030MsgSignalSetup( + void +) +{ + int niMsgLA; + EPVXISTAT status; + + if( !pnivxi_func[(unsigned)e_EnableSignalInt] || + !pnivxi_func[(unsigned)e_SetSignalHandler] || + !pnivxi_func[(unsigned)e_RouteSignal] || + !pnivxi_func[(unsigned)e_GetMyLA]){ + return; + } + + niMsgLA = (*pnivxi_func[(unsigned)e_GetMyLA])(); + +# define ANY_DEVICE (-1) +# define MSG_RESP_ENABLE (0x3f) + status = (*pnivxi_func[(unsigned)e_RouteSignal])( + ANY_DEVICE, + ~0); /* enable every thing */ + if(status){ + return; + } + +# define UKN_DEVICE (-2) + status = (*pnivxi_func[(unsigned)e_SetSignalHandler])( + UKN_DEVICE, + signalHandler); + if(status){ + return; + } + + status = (*pnivxi_func[(unsigned)e_EnableSignalInt])(); + if(status){ + return; + } + + msgCommanderLA = niMsgLA; + + return; +} + + +/* + * + * vxiHP1404MsgSignalSetup + * + * + */ +LOCAL +void vxiHP1404MsgSignalSetup( + void +) +{ + epvxiDeviceSearchPattern dsp; + int hpMsgLA = -1; + int hpRegLA = -1; + EPVXISTAT status; + + dsp.flags = VXI_DSP_make | VXI_DSP_model; + dsp.make = VXI_MAKE_HP; + dsp.model = VXI_HP_MODEL_E1404_MSG; + status = epvxiLookupLA(&dsp, set_la, (void *)&hpMsgLA); + if(status){ + return; + } + if(hpMsgLA<0){ + return; + } + dsp.flags = VXI_DSP_make | VXI_DSP_slot; + dsp.make = VXI_MAKE_HP; + dsp.slot = epvxiLibDeviceList[hpMsgLA]->slot; + status = epvxiLookupLA(&dsp, set_la, (void *)&hpRegLA); + if(status){ + return; + } + + if(hpRegLA<0){ + return; + } + + msgCommanderLA = hpMsgLA; + status = hpE1404SignalConnect(hpRegLA, signalHandler); + if(status){ + errMessage(status, NULL); + } + + return; +} + + +/* + * + * set_la + * + * + */ +LOCAL void set_la( +unsigned la, +unsigned *pla +) +{ + *pla = la; +} + + +/* + * + * vxiAttemptAsyncModeControl + * + * + */ +LOCAL EPVXISTAT vxiAttemptAsyncModeControl( + unsigned la, + unsigned long cmd +) +{ + EPVXISTAT status; + unsigned long resp; + unsigned long tmpcmd; + + if(msgCommanderLA<0 && cmd&MBC_AMC_RESP_SIGNAL_ENABLE){ + return S_epvxi_badConfig; + } + + /* + * this step tells the device what la to signal at + */ + if(cmd & MBC_AMC_RESP_SIGNAL_ENABLE){ + tmpcmd = MBC_IDENTIFY_COMMANDER | msgCommanderLA; + status = epvxiCmd( + la, + tmpcmd); + if(status){ + errPrintf( + status, + __FILE__, + __LINE__, + "IDENTIFY_COMMANDER rejected (la=0X%X)", + la); + return status; + } +printf("sent id cmdr (la=0X%X) (cmd=%x)\n", la, tmpcmd); + } + + status = epvxiCmdQuery( + la, + cmd, + &resp); + if(status){ + errPrintf( + status, + __FILE__, + __LINE__, + "Async mode control rejected (la=0X%X)", + la); + return status; + } + if( MBR_STATUS(resp) != MBR_STATUS_SUCCESS || + (resp^cmd)&MBR_AMC_CONFIRM_MASK){ + status = S_epvxi_msgDeviceFailure; + errPrintf( + status, + __FILE__, + __LINE__, + "async mode ctrl failure (la=0X%X,cmd=%x,resp=%x)", + la, + cmd, + resp); + return status; + } +printf("sent asynch mode control (la=%d) (cmd=%x)\n",la,cmd); + + + if(cmd & MBC_AMC_RESP_INT_ENABLE){ + intConnect( + INUM_TO_IVEC(la), + vxiMsgInt, + la); +printf("connected to interrupt (la=%d)\n", la); + } + + return VXI_SUCCESS; +} + + +/* + * + * vxiMsgSync() + * + * + */ +LOCAL EPVXISTAT vxiMsgSync( + unsigned la, + unsigned resp_mask, + unsigned resp_state, + int override_err +) +{ + VXIMDI *pvximdi; + struct vxi_csr *pcsr; + EPVXISTAT status; + long timeout; + unsigned short resp; + int pollcnt = 100; + + + while(!(pvximdi = epvxiPMsgConfig(la))){ + status = vxiMsgOpen(la); + if(status != VXI_SUCCESS){ + return status; + } + } + + pcsr = VXIBASE(la); + +# ifdef DEBUG + printf( "Syncing to resp mask %4x, request %4x (la=%d)\n", + resp_mask, + resp_state, + la); +# endif + + timeout = pvximdi->timeout; + do{ + int sync; + + resp = pcsr->dir.r.dd.msg.response; + + sync = !((resp^resp_state)&resp_mask); + + if(!(resp & VXIERRNOTMASK)){ + if(!override_err && !pvximdi->err){ + pvximdi->err = TRUE; + return S_epvxi_protocolError; + } + } + + if(sync){ + return VXI_SUCCESS; + } + + /* + * this improves VXI throughput at the + * expense of sucking CPU + */ + if(pollcnt>0){ + pollcnt--; + } + else{ + status = semTake( + pvximdi->syncSem, + VXIMSGSYNCDELAY); + if(status){ + timeout -= VXIMSGSYNCDELAY; + } + } + } + while(timeout>0); + + /* + * sync timed out if we got here + */ + status = S_epvxi_deviceTMO; + errPrintf( + status, + __FILE__, + __LINE__, + "msg dev timed out after %d sec", + (pvximdi->timeout-timeout) / sysClkRateGet()); + errPrintf( + status, + __FILE__, + __LINE__, + "resp mask %4x, request %4x, actual %4x", + resp_mask, + resp_state, + resp); + return status; +} + + +/* + * + * fetch_protocol_error + * + */ +LOCAL EPVXISTAT fetch_protocol_error( + unsigned la +) +{ + VXIMDI *pvximdi; + unsigned long error; + struct vxi_csr *pcsr; + unsigned short resp; + EPVXISTAT status; + + pvximdi = epvxiPMsgConfig(la); + if(!pvximdi){ + return S_epvxi_errFetchFailed; + } + + status = epvxiCmdQuery( + la, + (unsigned long)MBC_READ_PROTOCOL_ERROR, + &error); + if(status){ + errMessage(status, "serial protocol error fetch"); + return S_epvxi_errFetchFailed; + } + + pcsr = VXIBASE(la); + resp = pcsr->dir.r.dd.msg.response; + + if(resp & VXIERRNOTMASK){ + pvximdi->err = FALSE; + } + else{ + errPrintf( + S_epvxi_msgDeviceFailure, + __FILE__, + __LINE__, + "Device failed to clear its ERR bit (la=0X%X)", + la); + } + + switch(error){ + case MBE_MULTIPLE_QUERIES: + status = S_epvxi_multipleQueries; + break; + case MBE_UNSUPPORTED_CMD: + status = S_epvxi_unsupportedCmd; + break; + case MBE_DIR_VIOLATION: + status = S_epvxi_dirViolation; + break; + case MBE_DOR_VIOLATION: + status = S_epvxi_dorViolation; + break; + case MBE_RR_VIOLATION: + status = S_epvxi_rrViolation; + break; + case MBE_WR_VIOLATION: + status = S_epvxi_wrViolation; + break; + case MBE_NO_ERROR: + default: + status = S_epvxi_errFetchFailed; + break; + } + + errMessage(status, "serial protocol error"); + return status; +} + + +/* + * + * vxiMsgInt + * + * + */ +LOCAL +void vxiMsgInt( + unsigned la +) +{ + VXIMDI *pvximdi; + + /* + * verify that this device is open for business + */ + pvximdi = epvxiPMsgConfig(la); + if(pvximdi){ + + /* + * + * wakeup pending tasks + * + */ + semGive(pvximdi->syncSem); + } + else{ + logMsg( + "%s: vxiMsgInt(): msg int to ukn or closed dev\n", + (int)__FILE__, + NULL, + NULL, + NULL, + NULL, + NULL); + } +} + + + +/* + * signalHandler + */ +LOCAL +void signalHandler( +int16_t signal +) +{ + unsigned signal_la; + + signal_la = signal & VXIADDRMASK; + + if(MBE_EVENT_TEST(signal)){ + logMsg( "%s: VXI event was ignored %x\n", + (int)__FILE__, + signal, + NULL, + NULL, + NULL, + NULL); + } + else{ + vxiMsgInt(signal_la); + } +} diff --git a/src/drv/old/drvExampleVxi.c b/src/drv/old/drvExampleVxi.c new file mode 100644 index 000000000..c88cd9900 --- /dev/null +++ b/src/drv/old/drvExampleVxi.c @@ -0,0 +1,237 @@ +/* base/src/drv $Id$ */ +/* + * Author: John Winans + * Date: 09-09-92 + * + * Skeleton VXI driver module. + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 09-09-92 jrw written + * .02 08-24-93 joh updated for EPICS format return codes + * .03 08-24-93 joh converted to ANSI C + * .04 08-24-93 joh ran through -Wall + * + */ +#include +#include +#include + +#include +#include +#include + +typedef long exVxiStat; + +exVxiStat vti(int make, int model); + +LOCAL exVxiStat example_init(void); +LOCAL void example_stat(unsigned card, int level); +LOCAL void example_init_card(unsigned addr); +LOCAL int example_shutdown(void); +LOCAL void example_shutdown_card(unsigned la); + + +struct drvet drvExample={ + 2, + NULL, /* VXI I/O report takes care of this */ + example_init +}; + +static unsigned long exampleDriverID; /* ID used to identify this driver */ + +struct examplePrivate { + int j; + +/* + * Define all per-card private variables here. + */ +}; +#define PRIVATE_SIZE sizeof(struct examplePrivate) + +int vxi_make_example = 0x100; /* Set to proper make of the card */ +int vxi_model_example = 0x100; /* Set to proper model of the card */ + +/* + * This is a test entry point that allows a user to do a pseudo-init of + * a make and model of VXI cards. + */ +exVxiStat vti(int make, int model) +{ + vxi_make_example = make; + vxi_model_example = model; + + example_init(); + printf("Driver ID is 0x%08X\n", exampleDriverID); + return(VXI_SUCCESS); +} + +/****************************************************************************** + * + * Initialize all cards controlled by the example driver. + * + ******************************************************************************/ +LOCAL exVxiStat example_init(void) +{ + exVxiStat s; + epvxiDeviceSearchPattern dsp; + + /* + * do nothing on crates without VXI + */ + if(!epvxiResourceMangerOK) + return VXI_SUCCESS; + + if (rebootHookAdd(example_shutdown) < 0){ + s = S_dev_internal; + errMessage(s, "rebootHookAdd failed"); + return(s); + } + + exampleDriverID = epvxiUniqueDriverID(); + + dsp.flags = VXI_DSP_make; + dsp.make = vxi_make_example; + + s = epvxiLookupLA(&dsp, example_init_card, (void *)NULL); + return s; + +} + +/****************************************************************************** + * + * initialize single example card + * + ******************************************************************************/ +LOCAL void example_init_card(unsigned addr) +{ + exVxiStat s; + struct examplePrivate *ep; /* Per-card private variable area */ + struct exampleCard *ec; /* Physical address of the card */ + + /* Tell the VXI sub-system that this driver is in charge of this card */ + s = epvxiOpen(addr, exampleDriverID, PRIVATE_SIZE, example_stat); + if (s) + { + errPrintf(s, __FILE__, __LINE__, "LA=0X%X", addr); + return; + } + + printf("example_init_card entered for card at LA 0x%02X, make 0x%02X, model 0x%02X\n", addr, vxi_make_example, vxi_model_example); + + /* Allocate a private variable area for the card */ + s = epvxiFetchPConfig(addr, exampleDriverID, ep); + if(s){ + errMessage(s, NULL); + epvxiClose(addr, exampleDriverID); + return; + } + + /* Get physical base address of the card */ + ec = (struct exampleCard *) VXIBASE(addr); + + + /*********************************************** + * + * Perform card-specific initialization in here. + * + ***********************************************/ + + /* Register the card's model and make names for reporting purposes */ + s = epvxiRegisterModelName( + vxi_make_example, + vxi_model_example, + "Example Model Name"); + if(s){ + errMessage(s, NULL); + } + s = epvxiRegisterMakeName( + vxi_make_example, + "Example Make Name"); + if(s){ + errMessage(s, NULL); + } + + return; +} + + +/****************************************************************************** + * + * Shut the cards down beacuse a soft-boot will be taking place soon. + * + ******************************************************************************/ +LOCAL int example_shutdown(void) +{ + exVxiStat s; + epvxiDeviceSearchPattern dsp; + + dsp.flags = VXI_DSP_make; + dsp.make = vxi_make_example; + s = epvxiLookupLA(&dsp, example_shutdown_card, (void *)NULL); + if(s){ + errMessage(s, NULL); + } + + return OK; +} + +LOCAL void example_shutdown_card(unsigned la) +{ + + /* + * Perform proper operations here to disable the VXI card from + * generating interrupts and/or other backplane bus activity that + * could cause the CPUs and controllers problems during a soft-boot. + */ + + return; +} + +/****************************************************************************** + * + * Print status for a single card. + * + ******************************************************************************/ +LOCAL void example_stat( +unsigned card, +int level +) +{ + + /* + * Perform proper operations to evaluate the current operating mode + * of the card and print a summary. + */ + + + return; +} + + +/****************************************************************************** + * + * Place any user-required functions here. + * + ******************************************************************************/ diff --git a/src/drv/old/drvFp.c b/src/drv/old/drvFp.c new file mode 100644 index 000000000..bb3215754 --- /dev/null +++ b/src/drv/old/drvFp.c @@ -0,0 +1,526 @@ +/* drvFp.c */ +/* base/src/drv $Id$ + * routines which are used to test and interface with the + * FP10S fast protect module + * + * Author: Matthew Stettler + * Date: 6-92 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 joh 070992 integrated into GTACS & added std header + * .02 joh 070992 merged in include file fp.h + * .03 joh 070992 converted some symbols to LOCAL so they stay out + * of the vxWorks global symbol table + * .04 joh 070992 took out sysSetBCL() and substituted + * sysIntEnable() so this will not be hkv2f + * specific. + * .05 joh 070992 added INUM_TO_IVEC so this will be less + * 68k dependence (added include of iv.h) + * .06 joh 070992 FP_ILEV passed to call sysIntEnable() so that the + * interrupt level can be easily changed + * .07 joh 070992 changed some printf() calls to logMsg() + * so that driver diagnostics will show up in + * the log + * .08 joh 071092 now fetches base addr from module_types.h + * .09 joh 071092 added io_report routine + * .10 joh 071092 added scan task wakeup from ISR + * .11 joh 071092 moved ivec allocation to module_types.h + * .12 joh 072792 added soft reboot int disable + * .13 joh 082792 converted to V5 vxorks + * .14 mrk 090192 support epics I/O event scan, and added DRVET + * .15 mrk 080293 Add call to taskwdInsert + * .16 mgb 080493 Removed V5/V4 and EPICS_V2 conditionals + */ + + + +/* + * + * Routines: + * + * fp_init Finds and initializes fast protect cards + * fp_driver System interface to FP10S modules + * fp_int Interrupt service routine + * fp_en Enables/disables interrupts (toggles) + * fp_mode Sets interrupt reporting mode + * fp_reboot Clean up for soft reboots + * + * Diagnostic Routines: + * + * fp_srd Reads current local inputs and enables + * fp_frd Reads last failure register + * fp_csrd Reads control/status register + * fp_read Command line interface to fp_driver + * fp_dump Prints all fast protect status to console + * fp_monitor Monitor all cards and print failure data to + * console + * + * Routines Return: + * + * -1 No card present + * -2 Interrupt connection error + * -3 Semaphore creation error + * -4 addressing error + * -5 no memory + * 0-8 successfull completion, or # of cards found + * + */ + +static char *sccsId = "@(#)drvFp.c 1.12\t6/4/93"; + +#include "vxWorks.h" +#include "vme.h" +#include "taskLib.h" +#include /* in h/68k if this is compiling for a 68xxx */ + +#include "module_types.h" +#include +#include +#include +#include + +static long report(); +static long init(); +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvFp={ + 2, + report, + init}; + +static long report() +{ + fp_io_report(); +} + +static long init() +{ + fp_init(0); + return(0); +} + +/* general constants */ +#define FP_INTLEV 5 /* interrupt level */ +#define FP_BUFSIZ 8 /* input buffer size */ + +/* csr bit definitions */ +#define CSR_RST 0x1 /* reset status */ +#define CSR_CMD1 0x2 /* force local fail */ +#define CSR_IEN 0x4 /* interrupt enable */ +#define CSR_UDEF0 0x8 /* undefined */ +#define CSR_I0 0x10 /* interrupt level bit #1 */ +#define CSR_I1 0x20 /* interrupt level bit #2 */ +#define CSR_I2 0x40 /* interrupt level bit #3 */ +#define CSR_UDEF1 0x80 /* undefined */ +#define CSR_CARM0_L 0x100 /* latched carrier monitor #0 (one shot) */ +#define CSR_CARM1_L 0x200 /* latched carrier monitor #1 (freq mon) */ +#define CSR_OPTIC 0x400 /* optical carrier input enabled */ +#define CSR_CARM 0x800 /* carrier OK */ +#define CSR_LFAIL0 0x1000 /* local fail #0 (pal monitor) */ +#define CSR_LFAIL1 0x2000 /* local fail #1 (fpga monitor) */ +#define CSR_CMON 0x4000 /* clock fail (one shot) */ +#define CSR_CHNG 0x8000 /* enable switch configuration change */ + +/* csr mask definitions */ +#define CSR_STM 0xff00 /* status mask */ +#define CSR_IM 0x70 /* interrupt level mask */ + +/* driver status */ +#define DRV_MOM 0x010000 /* momentary fault */ +#define DRV_LOC 0x020000 /* local fault */ +#define DRV_REM 0x040000 /* remote fault */ +#define DRV_CLR 0x080000 /* fault cleared */ +#define DRV_HWF 0x800000 /* hardware fault */ + +/* operating modes */ +#define FP_NMSG 0 /* no messages to console */ +#define FP_TMSG 1 /* terse messages to console */ +#define FP_FMSG 2 /* full messages to console */ +#define FP_RUN 3 /* normal operating mode */ + +/* register address map for FP10s */ +struct fp1 + { + unsigned short csr; /* control and status register */ + unsigned short srd; /* current status */ + unsigned short frd; /* latched status */ + unsigned short ivec; /* interrupt vector */ + char end_pad[0xff-0x8]; /* pad to 256 byte boundary */ + }; + +/* fast protect control structure */ +struct fp_rec + { + struct fp1 *fptr; /* pointer to device registers */ + unsigned int drvstat; /* fast protect physical inputs */ + unsigned short lastswitch; /* previous enable switch data */ + short type; /* device type */ + short num; /* device number */ + short fp_vector; /* interrupt vector */ + short mode; /* operating mode */ + unsigned int int_num; /* interrupt number */ + IOSCANPVT ioscanpvt; + }; + +static struct fp_rec *fp; /* fast protect control structure */ +static int fp_num; /* # of fast protect cards found -1 */ +static SEM_ID fp_semid; /* semaphore for monitor task */ + +static void fp_reboot(); + +/* + * fp_int + * + * interrupt service routine + * + */ +fp_int(card) +unsigned card; +{ + register struct fp_rec *ptr = &fp[card]; + register struct fp1 *regptr; + unsigned short temp0, temp1, temp2; + + regptr = ptr->fptr; + temp0 = regptr->csr; + temp1 = regptr->frd; + temp2 = regptr->srd; + switch (ptr->mode) + { + case FP_TMSG: + logMsg("fast protect interrupt!\n"); + logMsg("csr status = %x\n",temp0); + break; + case FP_FMSG: + logMsg("fast protect #%d fault! fault input = %x enable switches = %x\n", + ptr->num,temp1 & 0xff,temp2>>8); + logMsg("csr status = %x\n",temp0); + break; + case FP_RUN: + ptr->drvstat = temp2; /* save last switch data */ + ptr->drvstat |= temp1<<16; /* save fault data */ + ptr->drvstat |= (temp0 & 0xff00)<<16; /* csr status bits */ + if ((temp1 ^ (temp2>>8)) || (temp0 & CSR_CHNG)) /* fault or enable change */ + semGive(fp_semid); /* wake up monitor */ + + /* + * wakeup the interrupt driven scanner + */ + scanIoRequest(fp[card].ioscanpvt); + break; + } + ptr->int_num++; /* log interrupt */ + regptr->csr |= CSR_RST; /* clear status and rearm */ + regptr->csr ^= CSR_RST; +} + + +/* + * fp_init + * + * initialization routine for FP10s fast protect modules + * + * + */ +fp_init(addr) +unsigned int addr; +{ + int i; + short junk; + short intvec = AT8FP_IVEC_BASE; + struct fp1 *ptr; + int status; + + fp = (struct fp_rec *) calloc(bi_num_cards[AT8_FP10S_BI], sizeof(*fp)); + if(!fp){ + return -5; + } + + if(!addr){ + addr = bi_addrs[AT8_FP10S_BI]; + } + + status = sysBusToLocalAdrs( VME_AM_SUP_SHORT_IO, addr, &ptr); + if(status<0){ + logMsg("VME shrt IO addr err in the slave fast protect driver\n"); + return(-4); + } + + status = rebootHookAdd(fp_reboot); + if(status<0){ + logMsg("%s: reboot hook add failed\n", __FILE__); + } + + for (i = 0; + (i < bi_num_cards[AT8_FP10S_BI]) && (vxMemProbe(ptr,READ,2,&junk) == OK); + i++,ptr++) { + + /* register initialization */ + ptr->csr = 0x0000; /* disable interface */ + fp[i].fptr = ptr; /* hardware location */ + fp[i].fp_vector = intvec++; /* interrupt vector */ + ptr->ivec = fp[i].fp_vector; /* load vector */ + fp[i].mode = FP_NMSG; /* set default mode (no messages) */ + fp[i].int_num = 0; /* initialize interrupt number */ + fp[i].type = 10; /* board type */ + fp[i].num = i; /* board number */ + + /* initialize input buffer */ + fp[i].drvstat = ptr->srd; /* initialize enable switch data */ + fp[i].drvstat |= ptr->frd<<16; /* initialize fault data */ + fp[i].drvstat |= (ptr->csr & 0xff00)<<16; /* csr status bits */ + + /* set up interrupt handler */ + ptr->csr |= FP_INTLEV<<4; /* level 5 interrupt */ + if (intConnect(INUM_TO_IVEC(fp[i].fp_vector),fp_int,i) != OK) + return(-2); /* abort if can't connect */ + sysIntEnable(FP_INTLEV); + ptr->csr |= 0x0001; + ptr->csr ^= 0x0001; /* clear status bits */ + if (ptr->csr & CSR_OPTIC) + logMsg("fast protect #%d optically coupled\n",i); + else + logMsg("fast protect #%d elecrically coupled\n",i); + + /* start up module */ + fp[i].fptr->csr |= CSR_IEN; /* enable interrupts */ + fp[i].mode = FP_RUN; /* normal run mode */ + scanIoInit(&fp[i].ioscanpvt); + } + fp_num = i - 1; /* record max card # */ + + /* create the semaphore */ + fp_semid = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + if ((int)fp_semid == 0) /* abort if can't create semaphore */ + return(-3); + + return(i); /* return # found */ +} + + +/* + * + * fp_reboot() + * + * turn off interrupts to avoid ctrl X reboot problems + */ +LOCAL +void fp_reboot() +{ + int i; + + if(!fp){ + return; + } + + for (i = 0; i < bi_num_cards[AT8_FP10S_BI]; i++){ + + if(!fp[i].fptr){ + continue; + } + + fp[i].fptr->csr &= ~CSR_IEN; + } +} + + + +/* + * fp_en + * + * interrupt enable/disable + * (toggles the interrupt enable - joh) + * + */ +fp_en(card) +short card; +{ + unsigned short temp; + + if (card < 0 || (card > fp_num)) + return -1; + fp[card].fptr->csr = fp[card].fptr->csr ^ CSR_IEN; + if (fp[card].fptr->csr & CSR_IEN) + printf("fast protect interrupts enabled\n"); + else + printf("fast protect interrupts disabled\n"); + return 0; +} +/* + * fp_mode + * + * set interrupt reporting mode + * + */ +fp_mode(card,mode) + short card, mode; +{ + if (card < 0 || (card > fp_num)) + return -1; + fp[card].mode = mode; + return 0; +} +/* + * fp_srd + * + * read current local inputs and enable switches + * + */ +fp_srd(card,option) + short card; + short option; +{ + if (card > fp_num) return -1; + if (!option) + printf("local inputs = %x enable switches = %x\n",fp[card].fptr->srd & 0xff, + fp[card].fptr->srd>>8); + return fp[card].fptr->srd; +} +/* + * fp_frd + * + * read latched local inputs + * + */ +fp_frd(card) + short card; +{ + if (card < 0 || (card > fp_num)) + return -1; + return fp[card].fptr->frd & 0xff; +} +/* + * fp_csrd + * + * read csr contents + * + */ +fp_csrd(card) + short card; +{ + if (card < 0 || (card > fp_num)) + return -1; + return fp[card].fptr->csr & 0xff77; +} +/* + * fp_driver + * + * epics interface to fast protect + * + */ +fp_driver(card,mask,prval) + register unsigned short card; + unsigned int mask; + register unsigned int *prval; +{ + register unsigned int temp; + + if (card > fp_num) return -1; + temp = fp[card].drvstat & 0xffff0000; /* latched status info */ + temp |= fp[card].fptr->srd; /* current switches & inputs */ + *prval = temp & mask; + return 0; +} +/* + * fp_read + * + * command line interface to fp_driver + * + */ +fp_read(card) + short card; +{ + unsigned int fpval,ret; + + if ((ret = fp_driver(card,0xffffffff,&fpval)) != 0) + return ret; + printf("Card #%d enable switches = %x inputs = %x\n",card,(fpval & 0x0000ff00)>>8, + fpval & 0x000000ff); + printf("csr status = %x last fault = %x\n",fpval>>24,(fpval & 0x00ff0000)>>16); + printf("raw readback = %x\n",fpval); + return 0; +} +/* + * fp_dump + * + * dump fast protect status to console + * + */ +fp_dump() +{ + int i; + + printf("Fast protect status (fault and CSR are latched):\n"); + printf("Card#\tenables\tinputs\tfault\tCSR status\n"); + for(i = 0; i < (fp_num + 1); i++) + printf("%d\t%x\t%x\t%x\t%x\n",i,fp[i].fptr->srd>>8,fp[i].fptr->srd & 0xff, + (fp[i].drvstat & 0x00ff0000)>>16,fp[i].drvstat>>24); + return i; +} +/* + * fp_monitor + * + * monitor fast protect cards and report failures to console + * + */ +fp_mon() +{ + for(semTake(fp_semid,WAIT_FOREVER);fp_dump() != 0;semTake(fp_semid,WAIT_FOREVER)); +} +fp_monitor() +{ + static char *name = "fpmon"; + int tid; + + if ((tid = taskNameToId(name)) != ERROR) { + taskwdRemove(tid); + taskDelete(tid); + } + if((tid = taskSpawn(name,25,VX_SUPERVISOR_MODE|VX_STDIO, + 1000,fp_mon)) == ERROR) return -1; + taskwdInsert(tid,NULL,NULL); + return 0; +} + +fp_io_report(level) +int level; +{ + int i; + + for(i=0; i<=fp_num; i++){ + printf("BI: AT8-FP-S: card %d\n", i); + } +} + +fp_getioscanpvt(card,scanpvt) +short card; +IOSCANPVT *scanpvt; +{ + if ((card >= bi_num_cards[AT8_FP10S_BI])) return(0); + *scanpvt = fp[card].ioscanpvt; + return(0); +} diff --git a/src/drv/old/drvFpm.c b/src/drv/old/drvFpm.c new file mode 100644 index 000000000..0f4105756 --- /dev/null +++ b/src/drv/old/drvFpm.c @@ -0,0 +1,432 @@ +/* drvFpm.c */ +/* base/src/drv $Id$ */ + +/* + * control routines for use with the FP10M fast protect master modules + * + * routines which are used to test and interface with the + * FP10S fast protect module + * + * Author: Matthew Stettler + * Date: 6-92 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 joh 070992 integrated into GTACS & added std header + * .02 joh 070992 merged in include file fpm.h + * .03 joh 070992 converted some symbols to LOCAL so they stay out + * of the vxWorks global symbol table + * .04 joh 070992 took out sysSetBCL() and substituted + * sysIntEnable() so this will not be hkv2f + * specific. + * .05 joh 070992 added INUM_TO_IVEC so this will be less + * 68k dependence (added include of iv.h) + * .06 joh 070992 FP_ILEV passed to call sysIntEnable() so that the + * interrupt level can be easily changed + * .07 joh 071092 now fetches base addr from module_types.h + * .08 joh 071092 added io report routine + * .09 joh 071092 allocate config structure at run time so that + * the users can adjust the number of cards without + * recompilation + * .10 joh 071092 moved ivec allocation to module_types.h + * .11 joh 072792 added soft reboot int disable + * .12 mrk 090292 added DRVET + * .13 mgb 080493 Removed V5/V4 and EPICS_V2 conditionals + * + * + * Routines: + * + * fpm_init Finds and initializes FP10M cards + * fpm_driver System interface to FP10M modules + * fpm_read Carrier control readback + * fpm_reboot clean up before soft reboots + * + * Daignostic Routines + * fpm_en Enables/disables interrupts (diagnostic enable) + * fpm_mode Sets interrupt reporting mode (logs mode + * changes to console) + * fpm_cdis Disables carrier from console + * fpm_fail Sets carrier failure mode + * fpm_srd Reads current carrier status + * fpm_write Command line interface to fpm_driver + * + * Routines return: + * + * -1 Nonexistent card + * -2 Interrupt connection error + * -3 no memory + * -4 VME short IO bus nonexistent + * 0-2 Successfull completion, or # cards found + * + */ + +static char *sccsId = "@(#)drvFpm.c 1.12\t8/4/93"; + +#include "vxWorks.h" +#include "vme.h" +#include /* in h/68k if this is compiling for a 68xxx */ +#include "module_types.h" +#include +#include + +static long report(); +static long init(); +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvFpm={ + 2, + report, + init}; + +static long report() +{ + fpm_io_report(); +} + +static long init() +{ + fpm_init(0); + return(0); +} + +/* general constants */ +#define FPM_INTLEV 5 /* interrupt level */ + +/* control register bit definitions */ +#define CR_CDIS 0x1 /* software carrier disable */ +#define CR_FS0 0x2 /* fail select 0 */ +#define CR_FS1 0x4 /* fail select 1 */ +#define CR_FS2 0x8 /* fail select 2 */ +#define CR_I0 0x10 /* interrupt level bit 0 */ +#define CR_I1 0x20 /* interrupt level bit 1 */ +#define CR_I2 0x40 /* interrupt level bit 2 */ +#define CR_IEN 0x80 /* interrupt enable */ + +/* control register mask definitions */ +#define CR_IM 0x70 /* interrupt level mask */ + +/* status register bit definitions */ +#define SR_S0 0x1 /* error sequencer state bit 0 */ +#define SR_S1 0x2 /* error sequencer state bit 1 */ +#define SR_S2 0x3 /* error sequencer state bit 2 */ + +/* status register mask definitions */ +#define SR_EM 0x7 /* error state mask */ + +/* operating modes */ +#define FPM_NMSG 0 /* no messages to console */ +#define FPM_TMSG 1 /* terse messages to console */ +#define FPM_FMSG 2 /* full messages to console */ + +/* register address map for FP10M */ +struct fp10m + { + unsigned short cr; /* control register */ + unsigned short sr; /* status register */ + unsigned short ivec; /* interrupt vector */ + char end_pad[0xff-0x6]; /* pad to 256 byte boundary */ + }; + +/* control structure */ +struct fpm_rec + { + struct fp10m *fmptr; /* pointer to device registers */ + short type; /* device type */ + short num; /* board number */ + short vector; /* interrupt vector */ + short mode; /* operating mode */ + unsigned int int_num; /* interrupt number */ + }; + +static struct fpm_rec *fpm; /* fast protect control structure */ + +static int fpm_num; /* # cards found - 1 */ + +static void fpm_reboot(); + +/* + * fpm_int + * + * interrupt service routine + * + */ +fpm_int(ptr) + register struct fpm_rec *ptr; +{ + register struct fp10m *regptr; + + regptr = ptr->fmptr; + switch (ptr->mode) + { + case FPM_TMSG: + logMsg("fast protect master interrupt!\n"); + break; + case FPM_FMSG: + logMsg("fast protect master interrupt!\n"); + logMsg("cr = %x sr = %x\n",regptr->cr,regptr->sr & 0x7); + break; + } + ptr->int_num++; +} +/* + * fpm_init + * + * initialization for fp10m fast protect master modules + * + */ +fpm_init(addr) + unsigned int addr; +{ + int i; + short junk; + short intvec = AT8FPM_IVEC_BASE; + struct fp10m *ptr; + int status; + + fpm = (struct fpm_rec *) calloc( + bo_num_cards[AT8_FP10M_BO], + sizeof(*fpm)); + if(!fpm){ + return -3; + } + + if(!addr){ + addr = bo_addrs[AT8_FP10M_BO]; + } + + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + addr, + &ptr); + if(status<0){ + logMsg("VME shrt IO addr err in the master fast protect driver\n"); + return -4; + } + + status = rebootHookAdd(fpm_reboot); + if(status<0){ + logMsg("%s: reboot hook add failed\n", __FILE__); + } + + for (i = 0; (i < bo_num_cards[AT8_FP10M_BO]) && (vxMemProbe(ptr,READ,2,&junk) == OK); + i++,ptr++) + { + /* + register initialization + */ + ptr->cr = 0x00; /* disable interface */ + fpm[i].fmptr = ptr; /* hardware location */ + fpm[i].vector = intvec++; /* interrupt vector */ + ptr->ivec = fpm[i].vector; /* load vector */ + fpm[i].mode = FPM_NMSG; /* set default mode (no messages) */ + fpm[i].int_num = 0; /* initialize interrupt number */ + fpm[i].type = 2; /* board type */ + fpm[i].num = i; /* board number */ + /* + set up interrupt handler + */ + ptr->cr |= FPM_INTLEV<<4; /* set up board for level 5 interrupt */ + if (intConnect(INUM_TO_IVEC(fpm[i].vector),fpm_int,&fpm[i]) != OK) + return -2; /* abort if can't connect */ + sysIntEnable(FPM_INTLEV); + } + fpm_num = i - 1; /* record last card # */ + return i; /* return # cards found */ +} + + +/* + * + * fpm_reboot() + * + * turn off interrupts to avoid ctrl X reboot problems + */ +LOCAL +void fpm_reboot() +{ + int i; + + if(!fpm){ + return; + } + + for (i = 0; i < bo_num_cards[AT8_FP10M_BO]; i++){ + + if(!fpm[i].fmptr){ + continue; + } + + fpm[i].fmptr->cr &= ~CR_IEN; + } +} + +/* + * fpm_en + * + * interrupt enable/disable + * (toggles the int enable state - joh) + * + */ +fpm_en(card) + short card; +{ + if (card < 0 || (card > fpm_num)) + return -1; + fpm[card].fmptr->cr ^= CR_IEN; + if (fpm[card].fmptr->cr & CR_IEN) + printf("fast protect master interrupts enabled\n"); + else + printf("fast protect master interrupts disabled\n"); + return 0; +} +/* + * fpm_mode + * + * set interrupt reporting mode + * + */ +fpm_mode(card,mode) + short card, mode; +{ + if (card < 0 || (card > fpm_num)) + return -1; + fpm[card].mode = mode; + return 0; +} +/* + * fpm_cdis + * + * carrier disable (1), enable (0) + * + */ +fpm_cdis(card,disable) + short card, disable; +{ + unsigned short temp; + + if (card < 0 || (card > fpm_num)) + return -1; + temp = fpm[card].fmptr->cr; + temp &= 0xfe; + temp |= (disable & 0x01); + fpm[card].fmptr->cr = temp; + return 0; +} +/* + * fpm_fail + * + * set failure mode + * + */ +fpm_fail(card,mode) + short card, mode; +{ + unsigned short temp; + + if (card < 0 || (card > fpm_num)) + return -1; + temp = fpm[card].fmptr->cr; + temp &= 0xf1; + temp |= (mode & 0x7)<<1; + fpm[card].fmptr->cr = temp; + return 0; +} +/* + * fpm_srd + * + * read status bits + * + */ +fpm_srd(card) + short card; +{ + if (card < 0 || ( card > fpm_num)) + return -1; + return fpm[card].fmptr->sr & 0x7; +} +/* + * fpm_driver + * + * epics interface to fast protect master + * + */ +fpm_driver(card,mask,prval) +register unsigned short card; +unsigned int mask; +register unsigned int prval; +{ + register unsigned int temp; + + if (card > fpm_num) + return -1; + temp = fpm[card].fmptr->cr; + fpm[card].fmptr->cr = (temp & (~mask | 0xf0)) | ((prval & mask) & 0xf); + return 0; +} +/* + * fpm_write + * + * command line interface to fpm_driver + * + */ +fpm_write(card,val) + short card; + unsigned int val; +{ + return fpm_driver(card,0xffffffff,val); +} +/* + * fpm_read + * + * read the current control register contents (readback) + * + */ +fpm_read(card,mask,pval) +register unsigned short card; +unsigned int mask; +register unsigned int *pval; +{ + if (card > fpm_num) + return -1; + *pval = fpm[card].fmptr->cr & 0x000f; + return 0; +} + + + +/* + * fpm_io_report() + * + */ +fpm_io_report(level) +int level; +{ + int i; + + for(i=0; i<=fpm_num; i++){ + printf("BO: AT8-FP-M: card %d\n", i); + } +} diff --git a/src/drv/old/drvGpib.c b/src/drv/old/drvGpib.c new file mode 100644 index 000000000..d623e3916 --- /dev/null +++ b/src/drv/old/drvGpib.c @@ -0,0 +1,2598 @@ +/* drvGpib.c */ +/* share/src/drv/drvGpib.c %W% %G% */ + +/****************************************************************************** + * + * TODO: + * - Autodetect the need to use a bounce buffer (saves time on boards that have + * "malloc"-able A24 space. + * + * - Launch campaign against the use of National Instruments hardware. + * + ****************************************************************************** + * + * Author: John Winans + * Date: 09-10-91 + * GPIB driver for the NI-1014 and NI-1014D VME cards. + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 09-13-91 jrw Written on Friday the 13th :-( + * Much of the code in physIo() was stolen from + * a gpib driver that came from Los Alamos. It + * referenced little more than National + * Instruments and Bob Daly (from ANL) in its + * credits. + * .02 12-03-91 jrw changed the setup of the ibLink and niLink structs + * .03 01-21-92 jrw moved task parameters into task_params.h + * .04 01-31-92 jrw added ibSrqLock code + * .05 02-26-92 jrw changed pnode references in the link task's + * busy-list checking, was an endless loop + * .06 04-10-92 jrw moved the device configs into module_types.h + * .07 08-02-92 mrk Added call to taskwdInsert + * + ****************************************************************************** + * + * Notes: + * If 1014D cards are used, make sure that the W7 switch is set to LMR. + * The internals of the 1014D are such that the DMAC can NEVER be hard-reset + * unless the SYSRESET* vme line is asserted. The LMR mode allows the + * initGpib() function to reset the DMAC properly. + * + * + * $Log$ + * Revision 1.28 1995/02/14 22:33:01 winans + * Cleaned up some Hideos hacking and commented out the LANL debug code because + * APS has had some add behaviour from GPIB lately and it is one of few things + * that has changed WRT to it. + * + * Revision 1.27 1994/12/14 22:29:14 winans + * Removed DMAC command chaining structure(s) from the ibLink + * structure so they can be malloc'd seperately. This keeps + * the usage of A24 space restricted to ONLY those structures + * that have to be there. + * + * Revision 1.26 1994/12/12 16:03:00 winans + * Rewrote the init code so that it always returns a zero (don't kill the + * startup.cmd file.) It is possible that this could cause some confusion + * to the database, should it decide to then use a link that did not init + * properly. + * + * Revision 1.25 1994/10/28 19:55:30 winans + * Added VME bus violation prevention code/bug fix from LANL. + * + * Revision 1.24 1994/10/04 18:42:46 winans + * Added an extensive debugging facility. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "drvGpib.h" + +#define STATIC /* static */ + +long reportGpib(void); +/*STATIC*/ long initGpib(void); +STATIC int niIrq(); +STATIC int niIrqError(int link); +STATIC int niTmoHandler(); +STATIC int srqIntEnable(); +STATIC int srqIntDisable(); + +STATIC int qGpibReq(); +STATIC int registerSrqCallback(); +STATIC int writeIb(); +STATIC int readIb(); +STATIC int writeIbCmd(); +STATIC int ioctlIb(); + int srqPollInhibit(); + +STATIC int ibLinkInit(); +STATIC int ibLinkStart(); +STATIC int ibLinkTask(); +struct bbIbLink *findBBLink(); + +int ibDebug = 0; /* Turns on debug messages from this driver */ +int bbibDebug = 0; /* Turns on ONLY bitbus related messages */ +int ibSrqDebug = 0; /* Turns on ONLY srq related debug messages */ +int niIrqOneShot = 0; /* Used for a one shot peek at the DMAC */ +int ibSrqLock = 0; /* set to 1 to stop ALL srq checking & polling */ + +#define STD_ADDRESS_MODE D_SUP|D_S24 /* mode to use when DMAC accesses RAM */ + +/* + * The bounce buffer is where the DMA IO operation(s) take place. If it + * is not large enough, it will be reallocated at that time. However, + * It should be made large enough such this need not happen. + */ +#define DEFAULT_BOUNCE_BUFFER_SIZE 10*1024 + +STATIC int defaultTimeout; /* in 60ths, for GPIB timeouts */ + +static char init_called = 0; /* To insure that init is done first */ +STATIC char *short_base; /* Base of short address space */ +#ifdef USE_OLD_XLATION +STATIC char *ram_base; /* Base of the ram on the CPU board */ +#endif + +STATIC int timeoutSquelch = 0; /* Used to quiet timeout msgs during polling */ + +/* DMA timing bus error problem debugging in niPhysIo */ +int ibDmaDebug = 0; /* Turns on DMA debug messages from this driver */ +int ibDmaTimingError = 0; /* count "bad memProbes"/call of niPhysIo */ +int ibDmaTimingErrorTotal = 0; /* count total "bad memProbes" in niPhysIo */ +int ibDmaMaxError = 0; /* max # bad calls per call of niPhysIo */ +STATIC char testWrite; /* test char to write to 1014 card */ + + +/****************************************************************************** + * + * GPIB driver block. + * + ******************************************************************************/ +struct drvGpibSet drvGpib={ + 9, + reportGpib, + initGpib, + qGpibReq, + registerSrqCallback, + writeIb, + readIb, + writeIbCmd, + ioctlIb, + srqPollInhibit +}; + +/****************************************************************************** + * + * Reference to the bitbus driver block. + * + ******************************************************************************/ +extern struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; + DRVSUPFUN qReq; +} drvBitBus; + +/****************************************************************************** + * + * This structure is used to build array-chained DMA operations. See the + * physIo() function and the National Instruments docs for more info. + * + ******************************************************************************/ +struct cc_ary +{ + void *cc_ccb; + short cc_ONE; + void *cc_n_1addr; + short cc_TWO; +}; + +typedef struct DmaStuffStruct +{ + struct cc_ary cc_array; + char cc_byte; +}DmaStuffStruct; +/****************************************************************************** + * + * This structure is used to hold the hardware-specific information for a + * single GPIB link. There is one for each link constructed in initGpib(). + * + ******************************************************************************/ +struct niLink { + struct ibLink ibLink; + + char tmoFlag; /* timeout has occurred */ + SEM_ID ioSem; /* DMA I/O operation complete or WD timeout */ + WDOG_ID watchDogId; /* watchdog for timeouts */ + struct ibregs *ibregs;/* pointer to board registers */ + + DmaStuffStruct *DmaStuff; + + char r_isr1; + char r_isr2; + int first_read; + + unsigned long cmdSpins; /* total taskDelays while in niGpibCmd() */ + unsigned long maxSpins; /* most taskDelays in one call to niGpibCmd() */ + + char *A24BounceBuffer; /* Where to DMA to */ + unsigned long A24BounceSize; +}; + +STATIC struct niLink *pNiLink[NIGPIB_NUM_LINKS]; /* NULL if link not present */ +STATIC int pollInhibit[NIGPIB_NUM_LINKS][IBAPERLINK]; + /* 0=pollable, 1=user inhibited, 2=no device found */ + +/****************************************************************************** + * + * This structure is used to hold the hardware-specific information for a + * single BitBus GPIB link. They are dynamically allocated (and an ibLinkTask + * started for it) when an IOCTL command requests it. + * + * The IOCTL requests to initiate a BBGPIB_IO link comes from the device support + * init code. When it finds a BBGPIB_IO link it issues an IOCTL for the link & + * bug-node specified in the record. The driver will then initialize the + * required data structures and start a link task for it. It is OK to request + * the initialization of the same link more than 1 time, the driver will ignore + * all but the first request. + * + ******************************************************************************/ +struct bbIbLink { + struct ibLink ibLink; /* associated ibLink structure */ + + SEM_ID syncSem; /* used for syncronous I/O calls */ + struct bbIbLink *next; /* Next BitBus link structure in list */ +}; + +STATIC struct bbIbLink *rootBBLink = NULL; /* Head of bitbus structures */ + +/****************************************************************************** + * + * This function prints a message indicating the status of each possible GPIB + * card found in the system. + * + ******************************************************************************/ +long +reportGpib(void) +{ + int i; + + if (init_called) + { + for (i=0; i< NIGPIB_NUM_LINKS; i++) + { + if (pNiLink[i]) + { + printf("Link %d (address 0x%08.8X) present and initialized.\n", i, pNiLink[i]->ibregs); + printf(" total niGpibCmd() taskDelay() calls = %lu\n", pNiLink[i]->cmdSpins); + printf(" worst case delay in niGpibCmd() = %lu\n", pNiLink[i]->maxSpins); + } + else + { + printf("Link %d not installed.\n", i); + } + } + printf("DMA timing: error = %d, total = %d, max = %d\n", + ibDmaTimingError, ibDmaTimingErrorTotal, ibDmaMaxError); + + } + else + { + printf("Gpib driver has not yet been initialized.\n"); + } + return(OK); +} + +STATIC void rebootFunc(void) +{ + int i; + char probeValue; + char msg[100]; + + for (i=0;iibLink, 0, msg, 1); + probeValue = 0; + vxMemProbe(&(pNiLink[i]->ibregs->ch1.ccr), WRITE, 1, (char *)&probeValue); + vxMemProbe(&(pNiLink[i]->ibregs->ch0.ccr), WRITE, 1, (char *)&probeValue); + taskDelay(1); /* Let it settle */ + + vxMemProbe(&(pNiLink[i]->ibregs->cfg2), WRITE, 1, (char *)&probeValue); + probeValue = D_LMR; + vxMemProbe(&(pNiLink[i]->ibregs->cfg2), WRITE, 1, (char *)&probeValue); + } + } + taskDelay(2); + return; +} +/****************************************************************************** + * + * Called by the iocInit processing. + * initGpib, probes the gpib card addresses and if one is present, it + * is initialized for use. It should only be called one time. + * + * The loops in this function are logically 1 large one. They were seperated + * so that the 1014D cards could be initialized properly. [Both ports must + * have some of their registers set at the same time and then not later + * altered... for example the LMR reset bit.] + * + ******************************************************************************/ +/* BUG -- this should be static */ +/*STATIC*/ long +initGpib(void) +{ + int i; + int j; + int probeValue; + struct ibregs *pibregs; + char s; + + if (init_called) + { + if (ibDebug) + logMsg("initGpib() driver already initialized!\n"); + return(OK); + } + defaultTimeout = sysClkRateGet(); + + /* figure out where the short address space is */ + sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO , 0, &short_base); + +#ifdef USE_OLD_XLATION + /* figure out where the CPU memory is (when viewed from the backplane) */ + sysLocalToBusAdrs(VME_AM_STD_SUP_DATA, &ram_base, &ram_base); + ram_base = (char *)((ram_base - (char *)&ram_base) & 0x00FFFFFF); +#endif + + if (ibDebug) + { + logMsg("Gpib NI1014 driver initializing\n"); + logMsg("short_base 0x%08.8X\n", short_base); +#ifdef USE_OLD_XLATION + logMsg("ram_base 0x%08.8X\n", ram_base); +#endif + logMsg("NIGPIB_SHORT_OFF 0x%08.8X\n", NIGPIB_SHORT_OFF); + logMsg("NIGPIB_NUM_LINKS 0x%08.8X\n", NIGPIB_NUM_LINKS); + } + + /* When probing, send out a reset signal to reset the DMAC and the TLC */ + probeValue = D_LMR | D_SFL; + + rebootHookAdd(rebootFunc); + + pibregs = (struct ibregs *)((unsigned int)short_base + NIGPIB_SHORT_OFF); + /* Gotta do all the probing first because the 1014D's LMRs are shared :-( */ + for (i=0; icfg2), WRITE, 1, (char *)&probeValue) < OK) + { /* no GPIB board present here */ + pNiLink[i] = (struct niLink *) NULL; + + if (ibDebug) + logMsg("Probing of address 0x%08.8X failed\n", pibregs); + + } + else + { /* GPIB board found... reserve space for structures & reset the thing */ + if (ibDebug) + logMsg("GPIB card found at address 0x%08.8X\n", pibregs); + + if ((pNiLink[i] = (struct niLink *)malloc(sizeof(struct niLink))) == NULL) + { /* This better never happen! */ + logMsg("initGpib(): Can't malloc memory for NI-link data structures!\n"); + return(ERROR); + } + + /* Allocate and init the sems and linked lists */ + pNiLink[i]->ibLink.linkType = GPIB_IO; /* spec'd in link.h */ + pNiLink[i]->ibLink.linkId = i; /* link number */ + pNiLink[i]->ibLink.bug = -1; /* this is not a bug link */ + + + /* Clear out the bouncer */ + pNiLink[i]->A24BounceBuffer = NULL; + pNiLink[i]->A24BounceSize = 0; + + taskDelay(1); /* Wait at least 10 msec before continuing */ + + ibLinkInit(&(pNiLink[i]->ibLink)); /* allocate the sems etc... */ + + pibregs->cfg2 = D_SFL; /* can't set all bits at same time */ + pibregs->cfg2 = D_SFL | D_SC; /* put board in operating mode */ + + pNiLink[i]->ibregs = pibregs; + pNiLink[i]->ioSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + pNiLink[i]->watchDogId = wdCreate(); + pNiLink[i]->tmoFlag = 0; + pNiLink[i]->cmdSpins = 0; + pNiLink[i]->maxSpins = 0; + + if ((pNiLink[i]->DmaStuff = (DmaStuffStruct *)devLibA24Malloc(sizeof(DmaStuffStruct))) == NULL) + { /* This better never happen! */ + logMsg("initGpib(): Can't malloc A24 memory for DMAC control structures!\n"); + return(ERROR); + } + pNiLink[i]->DmaStuff->cc_array.cc_ccb = 0; /* DMAC chaining structure */ + pNiLink[i]->DmaStuff->cc_array.cc_ONE = 1; + pNiLink[i]->DmaStuff->cc_array.cc_n_1addr = 0; + pNiLink[i]->DmaStuff->cc_array.cc_TWO = 2; + + pNiLink[i]->first_read = 1; /* used in physIo() */ + } + pibregs++; /* ready for next board window */ + } + + /* Bring up the cards (has to be done last because the 1014D has to have */ + /* both ports reset before either one is initialized. */ + + for (i=0; iibregs->cptr; + pNiLink[i]->r_isr1 = pNiLink[i]->ibregs->isr1; + pNiLink[i]->r_isr2 = pNiLink[i]->ibregs->isr2; + + /* disable all interrupts from the 7210 */ + pNiLink[i]->ibregs->imr1 = 0; /* DMA and ERROR IRQ mask reg */ + pNiLink[i]->ibregs->imr2 = 0; /* SRQ IRQ mask reg */ + pNiLink[i]->ibregs->spmr = 0; /* serial poll mode register */ + + pNiLink[i]->ibregs->adr = 0; /* device address = 0 */ + pNiLink[i]->ibregs->adr = HR_ARS|HR_DT|HR_DL; /* no secondary addressing */ + pNiLink[i]->ibregs->admr = HR_TRM1|HR_TRM0|HR_ADM0; + pNiLink[i]->ibregs->eosr = 0; /* end of string value */ + pNiLink[i]->ibregs->auxmr = ICR|8; /* internal counter = 8 */ + pNiLink[i]->ibregs->auxmr = PPR|HR_PPU; /* paralell poll unconfigure */ + pNiLink[i]->ibregs->auxmr = AUXRA|0; + pNiLink[i]->ibregs->auxmr = AUXRB|0; + pNiLink[i]->ibregs->auxmr = AUXRE|0; + + /* DMAC setup */ + + pNiLink[i]->ibregs->cfg1 = (NIGPIB_IRQ_LEVEL << 5)|D_BRG3|D_DBM; + pNiLink[i]->ibregs->ch1.niv = NIGPIB_IVEC_BASE + i*2; /* normal IRQ vector */ + pNiLink[i]->ibregs->ch1.eiv = NIGPIB_IVEC_BASE+1+i*2; /* error IRQ vector */ + pNiLink[i]->ibregs->ch0.niv = NIGPIB_IVEC_BASE + i*2; /* normal IRQ vector */ + pNiLink[i]->ibregs->ch0.eiv = NIGPIB_IVEC_BASE+1+i*2; /* error IRQ vector */ + pNiLink[i]->ibregs->ch1.ccr = D_EINT; /* stop operation, allow ints */ + pNiLink[i]->ibregs->ch0.ccr = 0; /* stop all channel operation */ + pNiLink[i]->ibregs->ch0.cpr = 3; /* highest priority */ + pNiLink[i]->ibregs->ch1.cpr = 3; /* highest priority */ + pNiLink[i]->ibregs->ch1.dcr = D_CS|D_IACK|D_IPCL; + pNiLink[i]->ibregs->ch0.dcr = D_CS|D_IACK|D_IPCL; + pNiLink[i]->ibregs->ch1.scr = 0; /* no counting during DMA */ + pNiLink[i]->ibregs->ch0.scr = D_MCU; /* count up during DMA cycles */ + pNiLink[i]->ibregs->ch0.mfc = STD_ADDRESS_MODE; + pNiLink[i]->ibregs->ch1.mfc = STD_ADDRESS_MODE; + pNiLink[i]->ibregs->ch1.bfc = STD_ADDRESS_MODE; + + + /* attach the interrupt handler routines */ + intConnect(INUM_TO_IVEC(NIGPIB_IVEC_BASE+i*2), niIrq, i); + intConnect(INUM_TO_IVEC(NIGPIB_IVEC_BASE+(i*2)+1), niIrqError, i); + } + } + + /* should have interrups running before I do any I/O */ + sysIntEnable(NIGPIB_IRQ_LEVEL); + + /* Fire up the TLCs and nudge all the addresses on the GPIB bus */ + /* by doing a serial poll on all of them. If someone did a */ + /* srqPollInhibit() on a specific link, then skip it and continue. */ + + for (i=0; iibregs->auxmr = AUX_PON; /* release pon state */ + + if (ibLinkStart(&(pNiLink[i]->ibLink)) == ERROR) /* start up the link */ + pNiLink[i] = NULL; /* kill the link to prevent flood of problems */ + } + } + + init_called = 1; /* let reportGpib() know init occurred */ + return(OK); +} + +STATIC int +niDumpDmac(int link) +{ + logMsg("ch0: ccr=%02.2X csr=%02.2X cer=%02.2X mtc=%04.4X mar=%08.8X btc=%04.4X bar=%08.8X\n", + pNiLink[link]->ibregs->ch0.ccr & 0xff, + pNiLink[link]->ibregs->ch0.csr & 0xff, + pNiLink[link]->ibregs->ch0.cer & 0xff, + pNiLink[link]->ibregs->ch0.mtc & 0xffff, + niRdLong(&(pNiLink[link]->ibregs->ch0.mar)), + pNiLink[link]->ibregs->ch0.btc & 0xffff, + niRdLong(&(pNiLink[link]->ibregs->ch0.bar))); + + logMsg("ch1: ccr=%02.2X csr=%02.2X cer=%02.2X mtc=%04.4X mar=%08.8X btc=%04.4X bar=%08.8X\n", + pNiLink[link]->ibregs->ch1.ccr & 0xff, + pNiLink[link]->ibregs->ch1.csr & 0xff, + pNiLink[link]->ibregs->ch1.cer & 0xff, + pNiLink[link]->ibregs->ch1.mtc & 0xffff, + niRdLong(&(pNiLink[link]->ibregs->ch1.mar)), + pNiLink[link]->ibregs->ch1.btc & 0xffff, + niRdLong(&(pNiLink[link]->ibregs->ch1.bar))); + + return(OK); +} +/****************************************************************************** + * + * Interrupt handler for all normal DMAC interrupts. + * + * This is invoked at the termination of a DMA operation or if the TLC + * requests an un-masked interrupt (typically SRQ from the GPIB bus.) + * + * Keep in mind that channel0's interrupts are related to the SRQs and that + * the ints from channel1 are related to the DMA operations completing. + * + * Note: + * The isr2 status should always be read first since reading isr1 can reset + * some of the isr2 status. + * + ******************************************************************************/ +STATIC int +niIrq(link) +int link; +{ + if (ibDebug) + logMsg("GPIB interrupt from link %d\n", link); + + if (NIGPIB_IRQ_LEVEL == 4) /* gotta ack ourselves on HK boards */ + sysBusIntAck(NIGPIB_IRQ_LEVEL); + + if (niIrqOneShot) + { + niDumpDmac(link); + niIrqOneShot--; + } + + /* Check the DMA error status bits first */ + if (pNiLink[link]->ibregs->ch0.csr & D_ERR || pNiLink[link]->ibregs->ch1.csr & D_ERR) + { + logMsg("GPIB error during DMA from link %d\n", link); + + /* read the status regs to clear any int status from the TLC */ + pNiLink[link]->r_isr2 |= pNiLink[link]->ibregs->isr2; + pNiLink[link]->r_isr1 |= pNiLink[link]->ibregs->isr1; + + niDumpDmac(link); + + logMsg("r_isr1=%02.2X r_isr2=%02.2X\n", + pNiLink[link]->r_isr1 & 0xff, + pNiLink[link]->r_isr2 & 0xff); + + pNiLink[link]->ibregs->ch0.csr = ~D_PCLT; /* Keep srq int status */ + pNiLink[link]->ibregs->ch1.csr = D_CLEAR; + pNiLink[link]->ibregs->imr1 = 0; + pNiLink[link]->ibregs->imr2 = 0; + + /* No semaphores are given in here because we don't know why we got */ + /* here. It is best to let I/O time out if any was going on. */ + return(ERROR); + } + + /* channel 0 PCL status is the SRQ line for the link */ + + if ((pNiLink[link]->ibregs->ch0.csr) & D_PCLT) + { + pNiLink[link]->ibregs->ch0.csr = D_PCLT; /* Reset srq status */ + pNiLink[link]->ibLink.srqIntFlag = 1; + + if (ibDebug|| ibSrqDebug) + logMsg("GPIB SRQ interrupt on link %d\n", link); + + semGive(pNiLink[link]->ibLink.linkEventSem); + + return(0); + } + +/* BUG -- perhaps set a flag so the WD system knows I proceeded here? */ + + /* if there was a watch-dog timer tie, let the timeout win. */ + if (pNiLink[link]->tmoFlag == FALSE) + { + if (pNiLink[link]->ibregs->ch1.csr & D_PCLT) + { + if (ibDebug) + logMsg("GPIB DMA completion interrupt from link %d\n", link); + /* read the status regs to clear any int status from the TLC */ + /* changed these to = from |= because they never got cleared! */ + pNiLink[link]->r_isr2 = pNiLink[link]->ibregs->isr2; + pNiLink[link]->r_isr1 = pNiLink[link]->ibregs->isr1; + + if (pNiLink[link]->ibregs->ch1.csr & D_COC) + { + /* this should not be set because we ALWAYS ask for 1 too */ + /* many bytes to be transfered. See 1014 docs on ints */ + logMsg("GPIB COC bit set after DMA on channel 1 link %d\n", link); + } + /* DMA complete via sync detect */ + pNiLink[link]->ibregs->imr1 = 0; + pNiLink[link]->ibregs->imr2 = 0; + pNiLink[link]->ibregs->ch1.csr = D_CLEAR; + /* Leave Channel 0's ints alone since it did not generate the interrupt */ + semGive(pNiLink[link]->ioSem); + + return(0); + } + } + else + { + /* The DMAC should get reset by the watch-dog handling code if I get here */ + if (ibDebug) + logMsg("GPIB DMA completion interrupt but wd expired already on link %d\n", link); + } + return(0); +} + +/****************************************************************************** + * + * An interrupt handler that catches the DMAC error interrupts. These should + * never occur. + * + ******************************************************************************/ +STATIC int +niIrqError(int link) +{ + logMsg("GPIB error interrupt generated on link %d\n", link); + + niDumpDmac(link); + + pNiLink[link]->ibregs->ch0.ccr = D_SAB; + pNiLink[link]->ibregs->ch1.ccr = D_SAB; + return(0); +} + +/****************************************************************************** + * + * niGpibCmd() + * + * This function is used to output a command string to the GPIB bus. + * + * The controller is placed in the active state prior to the outputting of + * the first command. + * + * Before calling niGpibCmd() the first time, an niGpibIoctl(IBIFC) call must + * be made to init the bus and enable the interface card. + * + ******************************************************************************/ +#define TOOLONG 100 /* how many times to try to send the same byte */ +#define IDELAY 1000 /* how long to busy wait while sending a byte */ + +STATIC int +niGpibCmd(link, buffer, length) +int link; +char *buffer; +int length; +{ + int iDelay; /* how long to spin before doing a taskWait */ + int tooLong; /* how long should I tolerate waiting */ + int lenCtr; + unsigned spins; /* how many taskDelay() calls made in this function */ + + lenCtr = length; + spins = 0; + + if (ibDebug) + logMsg("niGpibCmd(%d, 0x%08.8X, %d): command string >%s<\n", link, buffer, length, buffer); + + tooLong = TOOLONG; /* limit to wait for ctrlr's command buffer */ + pNiLink[link]->ibregs->auxmr = AUX_TCA; /* take control of the bus */ + + while (lenCtr) + { + pNiLink[link]->r_isr2 &= ~HR_CO; + iDelay = IDELAY; /* wait till the ctlr is ready */ + while (iDelay && (((pNiLink[link]->r_isr2 |= pNiLink[link]->ibregs->isr2) & HR_CO) == 0)) + iDelay--; + + if (iDelay) + { + pNiLink[link]->ibregs->cdor = *buffer++; /* output a byte */ + lenCtr--; + tooLong = TOOLONG; /* reset the limit again */ + } + else + { + if (!(tooLong--)) + { + /* errMsg() */ + logMsg("niGpibCmd(%d, 0x%08.8X, %d): Timeout while writing command >%s<\n", link, buffer, length, buffer); + pNiLink[link]->ibregs->auxmr = AUX_GTS; + if (spins > pNiLink[link]->maxSpins) + pNiLink[link]->maxSpins = spins; + return(ERROR); + } + spins++; + pNiLink[link]->cmdSpins++; + taskDelay(1); /* ctlr is taking too long */ + } + } + tooLong = TOOLONG; + while(tooLong--) + { + pNiLink[link]->r_isr2 &= ~HR_CO; + iDelay = IDELAY; /* wait till the ctlr is ready */ + while (iDelay && (((pNiLink[link]->r_isr2 |= pNiLink[link]->ibregs->isr2) & HR_CO) == 0)) + iDelay--; + + if(iDelay) + { + pNiLink[link]->ibregs->auxmr = AUX_GTS; + if (spins > pNiLink[link]->maxSpins) + pNiLink[link]->maxSpins = spins; + return(length); + } + else + { + spins++; + pNiLink[link]->cmdSpins++; + taskDelay(1); + } + } + /* errMsg() */ + logMsg("niGpibCmd(%d, 0x%08.8X, %d): Timeout after writing command >%s<\n", link, buffer, length, buffer); + pNiLink[link]->ibregs->auxmr = AUX_GTS; + if (spins > pNiLink[link]->maxSpins) + pNiLink[link]->maxSpins = spins; + return(ERROR); +} + +/****************************************************************************** + * + * Read a buffer via Ni-based link. + * + ******************************************************************************/ +STATIC int +niGpibRead(link, buffer, length, time) +int link; +char *buffer; +int length; +int time; +{ + int err; + + if(ibDebug) + logMsg("niGpibRead(%d, 0x%08.8X, %d, %d)\n",link, buffer, length, time); + + if (niCheckLink(link) == ERROR) + { + /* bad link number */ + return(ERROR); + } + + err = niPhysIo(READ, link, buffer, length, time); + pNiLink[link]->r_isr1 &= ~HR_END; + + return(err ? err : length - niGpibResid(link)); +} + + +/****************************************************************************** + * + * Write a buffer out an Ni-based link. + * + ******************************************************************************/ +STATIC int +niGpibWrite(link, buffer, length, time) +int link; +char *buffer; +int length; +int time; +{ + int err; + + if(ibDebug) + logMsg("niGpibWrite(%d, 0x%08.8X, %d, %d)\n",link, buffer, length, time); + + if (niCheckLink(link) == ERROR) + { + /* bad link number */ + return(ERROR); + } + + err = niPhysIo(WRITE, link, buffer, length, time); + + return(err ? err : length - niGpibResid(link)); +} + +/****************************************************************************** + * + * This function is used to figure out the difference in the transfer-length + * requested in a read or write request, and that actually transfered. + * + ******************************************************************************/ +STATIC int +niGpibResid(link) +int link; +{ + register int cnt; + + cnt = pNiLink[link]->ibregs->ch0.mtc; + if (pNiLink[link]->ibregs->ch1.mtc == 2 || cnt) /* add one if carry-cycle */ + cnt++; /* never started */ + + return(cnt); +} + + +/****************************************************************************** + * + * This function is used to validate all non-BitBus -> GPIB link numbers that + * are passed in from user requests. + * + ******************************************************************************/ +STATIC int +niCheckLink(link) +int link; +{ + if (link<0 || link >= NIGPIB_NUM_LINKS) + { + /* link number out of range */ + return(ERROR); + } + if (pNiLink[link] == NULL) + { + /* link number has no card installed */ + return(ERROR); + } + return(OK); +} + +/****************************************************************************** + * + * This function provides access to the GPIB protocol operations on the NI + * interface board. + * + ******************************************************************************/ +STATIC int +niGpibIoctl(link, cmd, v, p) +int link; +int cmd; +int v; +caddr_t p; +{ + int stat = OK; + + if(ibDebug) + logMsg("niGpibIoctl(%d, %d, %d, %08.8X)\n",link, cmd, v, p); + + if (cmd != IBGENLINK && niCheckLink(link) == ERROR) + { + /* bad link number */ + return(ERROR); + } + + switch (cmd) { + case IBTMO: /* set the timeout value for the next transaction */ + /* pNiLink[link]->tmoLimit = v; */ + logMsg("Old NI driver call entered IBTMO ignored\n"); + break; + case IBIFC: /* fire out an Interface Clear pulse */ + pNiLink[link]->ibregs->auxmr = AUX_SIFC; /* assert the line */ + taskDelay(10); /* wait a little while */ + pNiLink[link]->ibregs->auxmr = AUX_CIFC; /* clear the line */ + taskDelay(10); /* wait a little while */ + break; + case IBREN: /* turn on or off the REN line */ + pNiLink[link]->ibregs->auxmr = (v ? AUX_SREN : AUX_CREN); + break; + case IBGTS: /* go to standby (ATN off etc...) */ + pNiLink[link]->ibregs->auxmr = AUX_GTS; + break; + case IBGTA: /* go to active (ATN on etc...) (IBIFC must also be called */ + pNiLink[link]->ibregs->auxmr = AUX_TCA; + break; + case IBNILNK: /* returns the max number of NI links possible */ + stat = NIGPIB_NUM_LINKS; + break; + case IBGENLINK: /* request the creation of a link */ + break; /* this is automatic for NI based links */ + case IBGETLINK: /* return pointer to ibLink structure */ + *(struct ibLink **)p = &(pNiLink[link]->ibLink); + break; + default: + return(ERROR); + } + return(stat); +} + +/****************************************************************************** + * + * This routine does DMA based I/O with the GPIB bus. It sets up the NI board's + * DMA registers, initiates the transfer and waits for it to complete. It uses + * a watchdog timer in case the transfer dies. It returns OK, or ERROR + * depending on if the transfer succeeds or not. + * + ******************************************************************************/ + +STATIC int +niPhysIo(dir, link, buffer, length, time) +int dir; /* direction (READ or WRITE) */ +int link; /* link number to do the I/O with */ +char *buffer; /* data to transfer */ +int length; /* number of bytes to transfer */ +int time; /* time to wait on the DMA operation */ +{ + int status; + short cnt; + register struct ibregs *b; + char w_imr2; + int temp_addr; + int tmoTmp; + + if (pNiLink[link]->A24BounceBuffer == NULL) + { + if ((pNiLink[link]->A24BounceBuffer = devLibA24Malloc(DEFAULT_BOUNCE_BUFFER_SIZE)) == NULL) + { + errMessage(S_IB_A24 ,"niPhysIo ran out of A24 memory!"); + return(ERROR); + } + pNiLink[link]->A24BounceSize = DEFAULT_BOUNCE_BUFFER_SIZE; + if(ibDebug > 5) + logMsg("Got a bouncer at 0x%8.8X\n", pNiLink[link]->A24BounceBuffer); + } + + if (pNiLink[link]->A24BounceSize < length) + { /* Reallocate a larger bounce buffer */ + + devLibA24Free(pNiLink[link]->A24BounceBuffer); /* Loose the old one */ + + if ((pNiLink[link]->A24BounceBuffer = devLibA24Malloc(length)) == NULL) + { + errMessage(S_IB_A24 ,"niPhysIo ran out of A24 memory!"); + pNiLink[link]->A24BounceSize = 0; + pNiLink[link]->A24BounceBuffer = NULL; + return(ERROR); + } + pNiLink[link]->A24BounceSize = length; + if(ibDebug > 5) + logMsg("Got a new bouncer at 0x%8.8X\n", pNiLink[link]->A24BounceBuffer); + } + + b = pNiLink[link]->ibregs; + cnt = length; + + b->auxmr = AUX_GTS; /* go to standby mode */ + b->ch1.ccr = D_SAB; /* halt channel activity */ + b->ch0.ccr = D_SAB; /* halt channel activity */ + + b->ch1.csr = D_CLEAR; + b->ch0.csr = D_CLEAR & ~D_PCLT; + + b->imr2 = 0; /* set these bits last */ + status = OK; + + if (dir == READ) + { + if (pNiLink[link]->first_read == 0) + b->auxmr = AUX_FH; /* finish handshake */ + else + pNiLink[link]->first_read = 0; + + b->auxmr = AUXRA | HR_HLDE; /* hold off on end */ + + if (cnt != 1) + pNiLink[link]->DmaStuff->cc_byte = AUXRA | HR_HLDA; /* (cc) holdoff on all */ + else + pNiLink[link]->DmaStuff->cc_byte = b->auxmr = AUXRA | HR_HLDA; /* last byte, do now */ + b->ch0.ocr = D_DTM | D_XRQ; + /* make sure I only alter the 1014D port-specific fields here! */ + b->cfg1 = D_ECC | D_IN | (NIGPIB_IRQ_LEVEL << 5) | D_BRG3 | D_DBM; + b->ch1.ocr = D_DTM | D_ACH | D_XRQ; + b->ch1.ocr = D_DTM | D_ACH | D_XRQ; + + /* enable interrupts and dma */ + b->imr1 = HR_ENDIE; + w_imr2 = HR_DMAI; + } + else /* (dir == READ) */ + { + /* We will be writing, copy data into the bounce buffer */ + memcpy(pNiLink[link]->A24BounceBuffer, buffer, length); + + if (cnt != 1) + pNiLink[link]->DmaStuff->cc_byte = AUX_SEOI; /* send EOI with last byte */ + else + b->auxmr = AUX_SEOI; /* last byte, do it now */ + + b->ch0.ocr = D_MTD | D_XRQ; + /* make sure I only alter the 1014D port-specific fields here! */ + b->cfg1 = D_ECC | D_OUT | (NIGPIB_IRQ_LEVEL << 5) | D_BRG3 | D_DBM; + b->ch1.ocr = D_MTD | D_ACH | D_XRQ; + + /* enable interrupts and dma */ + b->imr1 = 0; + w_imr2 = HR_DMAO; + } /* dir == READ) */ + + /* setup channel 1 (carry cycle) */ + + if(ibDebug > 5) + logMsg("PhysIO: readying to xlate cc pointers at %8.8X and %8.8X\n", &(pNiLink[link]->DmaStuff->cc_byte), &pNiLink[link]->A24BounceBuffer[cnt - 1]); + +#ifdef USE_OLD_XLATION + pNiLink[link]->DmaStuff->cc_array.cc_ccb = &(pNiLink[link]->DmaStuff->cc_byte) + (long) ram_base; + pNiLink[link]->DmaStuff->cc_array.cc_n_1addr = &(pNiLink[link]->A24BounceBuffer[cnt - 1]) + (long)ram_base; +#else + + if (sysLocalToBusAdrs(VME_AM_STD_SUP_DATA, &(pNiLink[link]->DmaStuff->cc_byte), &(pNiLink[link]->DmaStuff->cc_array.cc_ccb)) == ERROR) + return(ERROR); + + if (sysLocalToBusAdrs(VME_AM_STD_SUP_DATA, &(pNiLink[link]->A24BounceBuffer[cnt - 1]), &(pNiLink[link]->DmaStuff->cc_array.cc_n_1addr)) == ERROR) + return(ERROR); + +#endif + if(ibDebug > 5) + logMsg("PhysIO: &cc_byte=%8.8X, &pNiLink[link]->A24BounceBuffer[cnt-1]=%8.8X, ", pNiLink[link]->DmaStuff->cc_array.cc_ccb, pNiLink[link]->DmaStuff->cc_array.cc_n_1addr); + + cnt--; +#ifdef USE_OLD_XLATION + temp_addr = (long) (&(pNiLink[link]->DmaStuff->cc_array)) + (long)ram_base; +#else + if (sysLocalToBusAdrs(VME_AM_STD_SUP_DATA, &(pNiLink[link]->DmaStuff->cc_array), &temp_addr) == ERROR) + return(ERROR); +#endif + if(ibDebug > 5) + logMsg("&cc_array=%8.8X, ", temp_addr); + + niWrLong(&b->ch1.bar, temp_addr); + b->ch1.btc = 2; + + /* setup channel 0 (main transfer) */ + b->ch0.mtc = cnt ? cnt : 1; +#ifdef USE_OLD_XLATION + temp_addr = (long) (pNiLink[link]->A24BounceBuffer) + (long) ram_base; +#else + if (sysLocalToBusAdrs(VME_AM_STD_SUP_DATA, pNiLink[link]->A24BounceBuffer, &temp_addr) == ERROR) + return(ERROR); +#endif + if(ibDebug > 5) + logMsg("pNiLink[link]->A24BounceBuffer=%8.8X\n", temp_addr); + + niWrLong(&b->ch0.mar, temp_addr); + + /* setup GPIB response timeout handler */ + if (time == 0) + time = defaultTimeout; /* 0 = take the default */ + pNiLink[link]->tmoFlag = FALSE; /* assume no timeout */ + wdStart(pNiLink[link]->watchDogId, time, niTmoHandler, link); + + /* start dma (ch1 first) */ + if (cnt) + b->ch1.ccr = D_EINT | D_SRT; /* enable interrupts */ + else + b->ch1.ccr = D_EINT; + +#ifdef INCLUDE_LANL_DMA_TIMING_CHECKER + /************************************************************************* + * DMAC BUS ERROR CATCH + * The following lines are included because of a possible VME protocol + * violation by the NI1014D gpib board. Occasionally, the board is not + * ready to respond to the "b->ch0.ccr = D_SRT;" line (write to interrupt + * mask register 2) and generates a bus error. Since this problem occurred + * initially with the 68020, faster CPUs may run into this problem more + * often. Thus, while the following set of debugging lines actually provide + * enough of a delay that the problem disappears for the 68020, they are left + * in for possible debugging for the faster CPUs. + **************************************************************************/ + ibDmaTimingError = 0; + while (vxMemProbe(&b->ch0.ccr, WRITE, 1, &testWrite) < 0) + ibDmaTimingError++; + ibDmaTimingErrorTotal += ibDmaTimingError; + if (ibDmaTimingError > ibDmaMaxError) + ibDmaMaxError = ibDmaTimingError; + if (ibDmaDebug) + logMsg("DMA timing: error = %d, total = %d, max = %d\n", + ibDmaTimingError, ibDmaTimingErrorTotal, ibDmaMaxError); + /***************************************************************************/ +#endif + + b->ch0.ccr = D_SRT; + +#ifdef INCLUDE_LANL_DMA_TIMING_CHECKER + /**************************** + * DMAC BUS ERROR CATCH + *****************************/ + ibDmaTimingError = 0; + while (vxMemProbe(&b->imr2, WRITE, 1, &testWrite) < 0) + ibDmaTimingError++; + ibDmaTimingErrorTotal += ibDmaTimingError; + if (ibDmaTimingError > ibDmaMaxError) + ibDmaMaxError = ibDmaTimingError; + if (ibDmaDebug) + logMsg("DMA timing: error = %d, total = %d, max = %d\n", + ibDmaTimingError, ibDmaTimingErrorTotal, ibDmaMaxError); + /***************************************************************************/ +#endif + + b->imr2 = w_imr2; /* this must be done last */ + + /* check for error in DMAC initialization */ + if ((b->ch0.csr & D_ERR) || (b->ch1.csr & D_ERR)) + { + /* errMsg() */ + logMsg("DMAC error initialization on link %d.\n", link); + return (ERROR); + } + if (cnt) + { + if (ibDebug == 1) + logMsg("Link %d waiting for DMA int or WD timeout.\n", link); + semTake(pNiLink[link]->ioSem, WAIT_FOREVER); /* timeout or DMA finish */ + } + else + if (b->ch0.mtc) + { + if (ibDebug == 1) + logMsg("wd cnt =0 wait\n"); + tmoTmp = 0; + while (b->ch0.mtc) + { + taskDelay(1); + if (++tmoTmp == time) + { + pNiLink[link]->tmoFlag = TRUE; + break; + } + } + } + if (pNiLink[link]->tmoFlag == TRUE) + { + status = ERROR; + /* reset */ + pNiLink[link]->r_isr2 |= pNiLink[link]->ibregs->isr2; + pNiLink[link]->r_isr1 |= pNiLink[link]->ibregs->isr1; + pNiLink[link]->ibregs->imr1 = 0; + pNiLink[link]->ibregs->imr2 = 0; + pNiLink[link]->ibregs->ch1.csr = D_CLEAR; + /* errMsg() */ + if (!timeoutSquelch) + logMsg("TIMEOUT GPIB DEVICE on link %d\n", link); + } + else + { + wdCancel(pNiLink[link]->watchDogId); + status = OK; + if (b->ch0.csr & D_ERR) + { + logMsg("DMAC error on link %d, channel 0 = %x\n", link, b->ch0.cer); + status = ERROR; + } + if (b->ch1.csr & D_ERR) + { + logMsg("DMAC error on link %d, channel 1 = %x\n", link, b->ch1.cer); + status = ERROR; + } + } + /* + * DMA transfer complete. Reset as per instructions in GPIB + * 'Programming Considerations' 5-14 + */ + +/* BUG -- Should halt and spin a while before aborting (NI recommendation) */ + b->ch0.ccr = D_SAB; /* halt channel activity */ + b->ch0.csr = D_CLEAR & ~D_PCLT; + b->ch1.ccr = D_SAB; + b->ch1.csr = D_CLEAR; + + b->imr2 = 0; + /* make sure I only alter the 1014D port-specific fields here! */ + b->cfg1 = (NIGPIB_IRQ_LEVEL << 5) | D_BRG3 | D_DBM; + + if (dir == READ) + { /* Copy data from the bounce buffer to the user's buffer */ + memcpy(buffer, pNiLink[link]->A24BounceBuffer, length); + } + + return (status); +} + +/****************************************************************************** + * + * This function is called by the watch-dog timer if it expires while waiting + * for a GPIB transaction to complete. + * + ******************************************************************************/ +STATIC int +niTmoHandler(link) +int link; +{ + pNiLink[link]->tmoFlag = TRUE; /* indicate that timeout occurred */ + semGive(pNiLink[link]->ioSem); /* wake up the phys I/O routine */ + return(0); +} + +/****************************************************************************** + * + * Mark a given device as non-pollable. + * + ******************************************************************************/ +STATIC int +niSrqPollInhibit(link, gpibAddr) +int link; +int gpibAddr; +{ + if (niCheckLink(link) == ERROR) + { + logMsg("drvGpib: niSrqPollInhibit(%d, %d): invalid link number specified\n", link, gpibAddr); + return(ERROR); + } + pollInhibit[link][gpibAddr] = 1; /* mark it as inhibited */ + return(OK); +} + +/****************************************************************************** + * + * Sometimes we have to make sure that regs on the GPIB board are accessed as + * 16-bit values. This function writes out a 32-bit value in 2 16-bit pieces. + * + ******************************************************************************/ +STATIC int +niWrLong(loc, val) +short *loc; +int val; +{ + register short *ptr = loc; + + *ptr = val >> 16; + *(ptr + 1) = val & 0xffff; +} + int +niRdLong(loc) +unsigned short *loc; +{ + register unsigned short *ptr = loc; + int val; + + val = (unsigned long) (*ptr << 16) + (unsigned long) (*(ptr+1) & 0xffff); + return(val); +} + +/****************************************************************************** + * + * This function is used to enable the generation of VME interupts upon the + * detection of an SRQ status on the GPIB bus. + * + ******************************************************************************/ +STATIC int +niSrqIntEnable(link) +int link; +{ + int lockKey; + + if(ibDebug || ibSrqDebug) + logMsg("niSrqIntEnable(%d): ch0.csr = 0x%02.2X, gsr=0x%02.2X\n", link, pNiLink[link]->ibregs->ch0.csr, pNiLink[link]->ibregs->gsr); + + lockKey = intLock(); /* lock out ints because something likes to glitch */ + + if (!((pNiLink[link]->ibregs->ch0.csr) & D_NSRQ)) + { /* SRQ line is CURRENTLY active, just give the event sem and return */ + pNiLink[link]->ibLink.srqIntFlag = 1; + semGive(pNiLink[link]->ibLink.linkEventSem); + + if(ibDebug || ibSrqDebug) + logMsg("niSrqIntEnable(%d): found SRQ active, setting srqIntFlag\n", link); + + /* Clear the PCLT status if is already set to prevent unneeded int later */ + pNiLink[link]->ibregs->ch0.csr = D_PCLT; + } + else + pNiLink[link]->ibregs->ch0.ccr = D_EINT; /* Allow SRQ ints */ + + intUnlock(lockKey); + return(OK); +} + + +/****************************************************************************** + * + * This function is used to disable the generation of VME interupts associated + * with the detection of an SRQ status on the GPIB bus. + * + ******************************************************************************/ +STATIC int +niSrqIntDisable(link) +int link; +{ + int lockKey; + + if(ibDebug || ibSrqDebug) + logMsg("niSrqIntDisable(%d): ch0.csr = 0x%02.2X, gsr=0x%02.2X\n", link, pNiLink[link]->ibregs->ch0.csr, pNiLink[link]->ibregs->gsr); + + lockKey = intLock(); /* lock out ints because something likes to glitch */ + pNiLink[link]->ibregs->ch0.ccr = 0; /* Don't allow SRQ ints */ + intUnlock(lockKey); + + return(OK); +} + +/****************************************************************************** + * + * The following section of GPIB driver is written such that it can operate + * in a device independant fashon. It does this by simply not making + * references to any architecture-specific data areas. + * + * When the architecture-specific information is needed, processing + * is sent to the architecture-specific routines. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Routine used to initialize the values of the fields in an ibLink structure. + * + ******************************************************************************/ +STATIC int +ibLinkInit(plink) +struct ibLink *plink; +{ + int j; + + if(ibDebug || bbibDebug) + logMsg("ibLinkInit(%08.8X): entered, type %d, link %d, bug %d\n", plink, plink->linkType, plink->linkId, plink->bug); + +#ifdef GPIB_SUPER_DEBUG + plink->History.Sem = semBCreate(SEM_Q_PRIORITY, SEM_FULL); + plink->History.Next = 0; + plink->History.Num = 0; +#endif + + plink->srqIntFlag = 0; /* no srq ints set now */ + plink->linkEventSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + + ellInit(&(plink->hiPriList)); /* init the list as empty */ + plink->hiPriSem = semBCreate(SEM_Q_PRIORITY, SEM_FULL); + + ellInit(&(plink->loPriList)); /* init the list as empty */ + plink->loPriSem = semBCreate(SEM_Q_PRIORITY, SEM_FULL); + + plink->srqRing = rngCreate(SRQRINGSIZE * sizeof(struct srqStatus)); + + for (j=0; jsrqHandler[j] = NULL; /* no handler is registered */ + plink->deviceStatus[j] = IDLE; /* assume device is IDLE */ + } + return(OK); +} + +/****************************************************************************** + * + * Init and start an ibLinkTask + * + ******************************************************************************/ +STATIC int +ibLinkStart(plink) +struct ibLink *plink; +{ + int j; + int taskId; + char tName[20]; + + if (ibDebug || bbibDebug) + logMsg("ibLinkStart(%08.8X): entered for linkType %d, link %d\n", plink, plink->linkType, plink->linkId); + + ioctlIb(plink->linkType, plink->linkId, plink->bug, IBIFC, -1, NULL);/* fire out an interface clear */ + ioctlIb(plink->linkType, plink->linkId, plink->bug, IBREN, 1, NULL);/* turn on the REN line */ + +/* BUG -- why not just forget this & only poll registered devices? */ +/* BUG -- the pollinhibit array stuff has to be fixed! */ + + + if ((plink->linkType == GPIB_IO) && (ibSrqLock == 0)) + { + /* poll all available adresses to see if will respond */ + speIb(plink); + for (j=1; j<31; j++) /* poll 1 thru 31 (no 0 or 32) */ + { + if (pollInhibit[plink->linkId][j] != 1);/* if user did not block it out */ + { + if (pollIb(plink, j, 0, POLLTIME) == ERROR) + pollInhibit[plink->linkId][j] = 2; /* address is not pollable */ + } + } + + spdIb(plink); + } + + if (plink->linkType == GPIB_IO) + sprintf(tName, "ib-%2.2d", plink->linkId); + else if (plink->linkType == BBGPIB_IO) + sprintf(tName, "bbib-%2.2d.%2.2d", plink->linkId, plink->bug); + else + strcpy(tName, GPIBLINK_NAME); + + /* Start a task to manage the link */ + if ((taskId = taskSpawn(tName, GPIBLINK_PRI, GPIBLINK_OPT, GPIBLINK_STACK, ibLinkTask, plink)) == ERROR) + { + logMsg("ibLinkStart(): failed to start link task for link %d\n", plink->linkId); + return(ERROR); + } + taskwdInsert(taskId,NULL,NULL); + taskDelay(10); /* give it a chance to start running */ + return(OK); +} + +/***************************************************************************** + * + * At the time this function is started as its own task, the linked list + * structures will have been created and initialized. + * + * This function is spawned as a task for each GPIB bus present in the + * system. That is one for each Ni card port, and one for each Bit Bus + * bug that contains a GPIB port on it. + * + * All global data areas referenced by this task are limited to the non-port + * specific items (no niLink[] references allowed.) so that the same task + * can operate all forms of GPIB busses. + * + *****************************************************************************/ +STATIC int +ibLinkTask(plink) +struct ibLink *plink; /* a reference to the link structures covered */ +{ + struct dpvtGpibHead *pnode; + struct srqStatus ringData; + int pollAddress; + int pollActive; + int working; + + + if (ibDebug) + logMsg("ibLinkTask started for link type %d, link %d\n", plink->linkType, plink->linkId); + + /* send out a UNL and UNT to test-drive the link */ + if (writeIbCmd(plink, "?_", 2) == ERROR) + { + logMsg("ibLinkTask(%08.08X): init failed for link type %d, link %d\n", plink->linkType, plink, plink->linkId); + return(ERROR); + } + + working = 1; /* check queues for work the first time */ + while (1) + { + if (!working) + { + if (ibSrqLock == 0) + { + /* Enable SRQ interrupts while waiting for an event */ + srqIntEnable(plink->linkType, plink->linkId, plink->bug); + } + + /* wait for an event associated with this GPIB link */ + semTake(plink->linkEventSem, WAIT_FOREVER); + + /* Disable SRQ interrupts while processing an event */ + srqIntDisable(plink->linkType, plink->linkId, plink->bug); + + if (ibDebug) + { + logMsg("ibLinkTask(%d, %d): got an event\n", plink->linkType, plink->linkId); + } + } + working = 0; /* Assume will do nothing */ + + /* Check if an SRQ interrupt has occurred recently */ + + /* + * If link is currently doing DMA, this function/task will be performing + * the work. Therfore, it will not be here trying to poll devices, so + * is no need to worry about locking the GPIB link here. + */ + + if ((plink->srqIntFlag) && (ibSrqLock == 0)) + { + if (ibDebug || ibSrqDebug) + logMsg("ibLinkTask(%d, %d): srqIntFlag set.\n", plink->linkType, plink->linkId); + + plink->srqIntFlag = 0; + pollActive = 0; + + pollAddress = 1; /* skip 0 and 31, poll 1-30 */ + while (pollAddress < 31) + { + if (!(pollInhibit[plink->linkId][pollAddress])) /* zero if allowed */ + { + if (!pollActive) + { /* set the serial poll enable mode if not done so yet */ + pollActive = 1; + speIb(plink); + } + if (ibDebug || ibSrqDebug) + logMsg("ibLinkTask(%d, %d): poling device %d\n", plink->linkType, plink->linkId, pollAddress); + if ((ringData.status = pollIb(plink, pollAddress, 1, POLLTIME)) & 0x40) + { + ringData.device = pollAddress; + if (ibDebug || ibSrqDebug) + logMsg("ibLinkTask(%d, %d): device %d srq status = 0x%02.2X\n", plink->linkType, plink->linkId, pollAddress, ringData.status); + if (plink->srqHandler[ringData.device] != NULL) + { /* there is a registered SRQ handler for this device */ + rngBufPut(plink->srqRing, (char *) &ringData, sizeof(ringData)); + } + else + if (ibDebug || ibSrqDebug) + logMsg("ibLinkTask(%d, %d): got an srq from device %d... ignored\n", plink->linkType, plink->linkId, pollAddress); + } + } + pollAddress++; + } + if (pollActive) + { /* unset serial poll mode if it got set above */ + pollActive = 0; + spdIb(plink); + } + else + { + logMsg("ibLinkTask(%d, %d): got an SRQ, but have no pollable devices!\n", plink->linkType, plink->linkId); + } + /* + * If the SRQ link is again/still active, it will be seen on the next + * call to srqIntEnable above. + */ + } + + /* + * See if there is a need to process an SRQ solicited transaction. + * Do all of them before going on to other transactions. + */ + while (rngBufGet(plink->srqRing, (char *)&ringData, sizeof(ringData))) + { + if (ibDebug || ibSrqDebug) + logMsg("ibLinkTask(%d, %d): dispatching srq handler for device %d\n", plink->linkType, plink->linkId, ringData.device); + plink->deviceStatus[ringData.device] = (*(plink->srqHandler)[ringData.device])(plink->srqParm[ringData.device], ringData.status); + working=1; + } + + /* + * see if the Hi priority queue has anything in it + */ + semTake(plink->hiPriSem, WAIT_FOREVER); + + if ((pnode = (struct dpvtGpibHead *)ellFirst(&(plink->hiPriList))) != NULL) + { + while (plink->deviceStatus[pnode->device] == BUSY) + if ((pnode = (struct dpvtGpibHead *)ellNext(pnode)) == NULL) + break; + } + if (pnode != NULL) + ellDelete(&(plink->hiPriList), pnode); + + semGive(plink->hiPriSem); + + if (pnode != NULL) + { + if (ibDebug) + logMsg("ibLinkTask(%d, %d): got Hi Pri xact, pnode= 0x%08.8X\n", plink->linkType, plink->linkId, pnode); + + plink->deviceStatus[pnode->device] = (*(pnode->workStart))(pnode); + working=1; + } + else + { + semTake(plink->loPriSem, WAIT_FOREVER); + if ((pnode = (struct dpvtGpibHead *)ellFirst(&(plink->loPriList))) != NULL) + { + while (plink->deviceStatus[pnode->device] == BUSY) + if ((pnode = (struct dpvtGpibHead *)ellNext(pnode)) == NULL) + break; + } + if (pnode != NULL) + ellDelete(&(plink->loPriList), pnode); + + semGive(plink->loPriSem); + + if (pnode != NULL) + { + if(ibDebug) + logMsg("ibLinkTask(%d, %d): got Lo Pri xact, pnode= 0x%08.8X\n", plink->linkType, plink->linkId, pnode); + plink->deviceStatus[pnode->device] = (*(pnode->workStart))(pnode); + working=1; + } + } + } +} + +/****************************************************************************** + * + * The following are functions used to take care of serial polling. They + * are called from the ibLinkTask. + * + ******************************************************************************/ +/****************************************************************************** + * + * Pollib sends out an SRQ poll and returns the poll response. + * If there is an error during polling (timeout), the value -1 is returned. + * + ******************************************************************************/ +STATIC int +pollIb(plink, gpibAddr, verbose, time) +struct ibLink *plink; +int gpibAddr; +int verbose; /* set to 1 if should log any errors */ +int time; +{ + char pollCmd[4]; + int status; + int tsSave; + unsigned char pollResult[3]; + + + if(verbose && (ibDebug || ibSrqDebug)) + logMsg("pollIb(0x%08.8X, %d, %d, %d)\n", plink, gpibAddr, verbose, time); + + tsSave = timeoutSquelch; + timeoutSquelch = !verbose; /* keep the I/O routines quiet if desired */ + + /* raw-read back the response from the instrument */ + if (readIb(plink, gpibAddr, pollResult, sizeof(pollResult), time) == ERROR) + { + if(verbose) + logMsg("pollIb(%d, %d): data read error\n", plink->linkId, gpibAddr); + status = ERROR; + } + else + { + status = pollResult[0]; + if (ibDebug || ibSrqDebug) + { + logMsg("pollIb(%d, %d): poll status = 0x%02.2X\n", plink->linkId, gpibAddr, status); + } + } + + timeoutSquelch = tsSave; /* return I/O error logging to normal */ + return(status); +} + +/****************************************************************************** + * + * speIb is used to send out a Serial Poll Enable command on the GPIB + * bus. + * + ******************************************************************************/ +STATIC int +speIb(plink) +struct ibLink *plink; +{ + /* write out the Serial Poll Enable command */ + writeIbCmd(plink, "\030", 1); + + return(0); +} + + +/****************************************************************************** + * + * spdIb is used to send out a Serial Poll Disable command on the GPIB + * bus. + * + ******************************************************************************/ +STATIC int +spdIb(plink) +struct ibLink *plink; +{ + /* write out the Serial Poll Disable command */ + writeIbCmd(plink, "\031", 1); + + return(0); +} + +/****************************************************************************** + * + * Functions used to enable and disable SRQ interrupts. These only make + * sense on a Ni based link, so they are ignored in the BitBus case. + * (In the BitBus, SRQ status is passed back via query. So there is no + * asynchronous interupt associated with it.) + * + * The interrupts referred to here are the actual VME bus interrupts that are + * generated by the GPIB interface when it sees the SRQ line go high. + * + ******************************************************************************/ +STATIC int +srqIntEnable(int linkType, int link, int bug) +{ + if (linkType == GPIB_IO) + return(niSrqIntEnable(link)); + + if (linkType == BBGPIB_IO) + return(OK); /* Bit Bus does not use interrupts for SRQ handeling */ + + return(ERROR); /* Invalid link type specified on the call */ +} + +STATIC int +srqIntDisable(int linkType, int link, int bug) +{ + if (linkType == GPIB_IO) + return(niSrqIntDisable(link)); + + if (linkType == BBGPIB_IO) + return(0); /* Bit Bus does not use interrupts for SRQ handeling */ + + return(ERROR); /* Invlaid link type specified on the call */ +} + +/****************************************************************************** + * + * Check the link number and bug number (if is a BBGPIB_IO link) to see if they + * are valid. + * + ******************************************************************************/ +STATIC int +checkLink(linkType, link, bug) +int linkType; +int link; +int bug; +{ + if (linkType == GPIB_IO) + return(niCheckLink(link)); + + if (linkType == BBGPIB_IO) + return(bbCheckLink(link, bug)); + + return(ERROR); /* bad link type specefied */ +} + +/**************************************************************************** + * + * The following routines are the user-callable entry points to the GPIB + * driver. + * + ****************************************************************************/ +/****************************************************************************** + * + * A device support module may call this function to request that the GPIB + * driver NEVER poll a given device. + * + * Devices are polled when an SRQ event is present on the GPIB link. Some + * devices are too dumb to deal with being polled. + * + * This is NOT a static function, because it must be invoked from the startup + * script BEFORE iocInit is called. + * + * BUG -- + * This could change if we decide to poll them during the second call to init() + * when epics 3.3 is available. + * + ******************************************************************************/ +/* STATIC */ int +srqPollInhibit(linkType, link, bug, gpibAddr) +int linkType; /* link type (defined in link.h) */ +int link; /* the link number the handler is related to */ +int bug; /* the bug node address if on a bitbus link */ +int gpibAddr; /* the device address the handler is for */ +{ + if (ibDebug || ibSrqDebug) + logMsg("srqPollInhibit(%d, %d, %d, %d): called\n", linkType, link, bug, gpibAddr); + + if (linkType == GPIB_IO) + { + return(niSrqPollInhibit(link, gpibAddr)); + } + + if (linkType == BBGPIB_IO) + { + return(bbSrqPollInhibit(link, bug, gpibAddr)); + } + + logMsg("drvGpib: srqPollInhibit(%d, %d, %d, %d): invalid link type specified\n", linkType, link, bug, gpibAddr); + return(ERROR); +} + +/****************************************************************************** + * + * This allows a device support module to register an SRQ event handler. + * + * It is used to specify a function to call when an SRQ event is detected + * on the specified link and device. When the SRQ handler is called, it is + * passed the requested parm and the poll-status from the gpib device. + * + ******************************************************************************/ +STATIC int +registerSrqCallback(pibLink, device, handler, parm) +struct ibLink *pibLink; +int device; +int (*handler)(); /* handler function to invoke upon SRQ detection */ +caddr_t parm; /* so caller can have a parm passed back */ +{ + if(ibDebug || ibSrqDebug) + logMsg("registerSrqCallback(%08.8X, %d, 0x%08.8X, %08.8X)\n", pibLink, device, handler, parm); + + pibLink->srqHandler[device] = handler; + pibLink->srqParm[device] = parm; + return(OK); +} + +/****************************************************************************** + * + * Allow users to operate the internal functions of the driver. + * + * This can be fatal to the driver... make sure you know what you are doing! + * + ******************************************************************************/ +STATIC int +ioctlIb(linkType, link, bug, cmd, v, p) +int linkType; /* link type (defined in link.h) */ +int link; /* the link number to use */ +int bug; /* node number if is a bitbus -> gpib link */ +int cmd; +int v; +caddr_t p; +{ + int stat; + + if (linkType == GPIB_IO) + return(niGpibIoctl(link, cmd, v, p));/* link checked in niGpibIoctl */ + + if (linkType == BBGPIB_IO) + return(bbGpibIoctl(link, bug, cmd, v, p));/* link checked in bbGpibIoctl */ + + if (ibDebug || bbibDebug) + logMsg("ioctlIb(%d, %d, %d, %d, %08.8X, %08.8X): invalid link type\n", linkType, link, bug, cmd, v, p); + + return(ERROR); +} + +/****************************************************************************** + * + * This function allows a user program to queue a GPIB work request for + * future execution. It is the ONLY way a user function can initiate + * a GPIB message transaction. + * + * A work request represents a function that the ibLinkTask is to call (when + * ready) to allow the user program access to the readIb, writeIb, and + * writeIbCmd functions. The user programs should never call these functions + * at any other times. + * + * Returns OK, or ERROR. + * + ******************************************************************************/ +STATIC int +qGpibReq(pdpvt, prio) +struct dpvtGpibHead *pdpvt; /* pointer to the device private structure */ +int prio; +{ + + if (pdpvt->pibLink == NULL) + { + logMsg("qGpibReq(%08.8X, %d): dpvt->pibLink == NULL!\n", pdpvt, prio); + return(ERROR); + } + + switch (prio) { + case IB_Q_LOW: /* low priority transaction request */ + semTake(pdpvt->pibLink->loPriSem, WAIT_FOREVER); + ellAdd(&(pdpvt->pibLink->loPriList), pdpvt); + semGive(pdpvt->pibLink->loPriSem); + semGive(pdpvt->pibLink->linkEventSem); + break; + case IB_Q_HIGH: /* high priority transaction request */ + semTake(pdpvt->pibLink->hiPriSem, WAIT_FOREVER); + ellAdd(&(pdpvt->pibLink->hiPriList), pdpvt); + semGive(pdpvt->pibLink->hiPriSem); + semGive(pdpvt->pibLink->linkEventSem); + break; + default: /* invalid priority */ + logMsg("invalid priority requested in call to qgpibreq(%08.8X, %d)\n", pdpvt, prio); + return(ERROR); + } + if (ibDebug) + logMsg("qgpibreq(0x%08.8X, %d): transaction queued\n", pdpvt, prio); + return(OK); +} + +/****************************************************************************** + * + * The following functions are defined for use by device support modules. + * They may ONLY be called by the linkTask. + * + ******************************************************************************/ +/****************************************************************************** + * + * A device support callable entry point used to write data to GPIB devices. + * + * This function returns the number of bytes written out. + * + ******************************************************************************/ +STATIC int +writeIb(pibLink, gpibAddr, data, length, time) +struct ibLink *pibLink; +int gpibAddr; /* the device number to write the data to */ +char *data; /* the data buffer to write out */ +int length; /* number of bytes to write out from the data buffer */ +int time; +{ + char attnCmd[5]; + int stat; + + if(ibDebug || (bbibDebug & (pibLink->linkType == BBGPIB_IO))) + logMsg("writeIb(%08.8X, %d, 0x%08.8X, %d, %d)\n", pibLink, gpibAddr, data, length, time); + + if (pibLink->linkType == GPIB_IO) + { + attnCmd[0] = '?'; /* global unlisten */ + attnCmd[1] = '_'; /* global untalk */ + attnCmd[2] = gpibAddr+LADBASE; /* lad = gpibAddr */ + attnCmd[3] = 0+TADBASE; /* mta = 0 */ + attnCmd[4] = '\0'; /* in case debugging prints it */ + + if (writeIbCmd(pibLink, attnCmd, 4) != 4) + return(ERROR); + stat = niGpibWrite(pibLink->linkId, data, length, time); + + if (writeIbCmd(pibLink, attnCmd, 2) != 2) + return(ERROR); + } + else if (pibLink->linkType == BBGPIB_IO) + { + stat = bbGpibWrite(pibLink, gpibAddr, data, length, time); + } + else + { + return(ERROR); + } + return(stat); +} + +/****************************************************************************** + * + * A device support callable entry point used to read data from GPIB devices. + * + * This function returns the number of bytes read from the device, or ERROR + * if the read operation failed. + * + ******************************************************************************/ +STATIC int +readIb(pibLink, gpibAddr, data, length, time) +struct ibLink *pibLink; +int gpibAddr; /* the device number to read the data from */ +char *data; /* the buffer to place the data into */ +int length; /* max number of bytes to place into the buffer */ +int time; /* max time to allow for read operation */ +{ + char attnCmd[5]; + int stat; + + if(ibDebug || (bbibDebug & (pibLink->linkType == BBGPIB_IO))) + logMsg("readIb(%08.8X, %d, 0x%08.8X, %d)\n", pibLink, gpibAddr, data, length); + + if (pibLink->linkType == GPIB_IO) + { + attnCmd[0] = '_'; /* global untalk */ + attnCmd[1] = '?'; /* global unlisten */ + attnCmd[2] = gpibAddr+TADBASE; /* tad = gpibAddr */ + attnCmd[3] = 0+LADBASE; /* mta = 0 */ + attnCmd[4] = '\0'; + + if (writeIbCmd(pibLink, attnCmd, 4) != 4) + return(ERROR); + + stat = niGpibRead(pibLink->linkId, data, length, time); + + if (writeIbCmd(pibLink, attnCmd, 2) != 2) + return(ERROR); + } + else if (pibLink->linkType == BBGPIB_IO) + { + stat = bbGpibRead(pibLink, gpibAddr, data, length, time); + } + else + { /* incorrect link type specified! */ + return(ERROR); + } + return(stat); +} + +/****************************************************************************** + * + * A device support callable entry point that is used to write commands + * to GPIB devices. (this is the same as a regular write except that the + * ATN line is held high during the write. + * + * This function returns the number of bytes written out. + * + ******************************************************************************/ +STATIC int +writeIbCmd(pibLink, data, length) +struct ibLink *pibLink; +char *data; /* the data buffer to write out */ +int length; /* number of bytes to write out from the data buffer */ +{ + + if(ibDebug || (bbibDebug & (pibLink->linkType == BBGPIB_IO))) + logMsg("writeIbCmd(%08.8X, %08.8X, %d)\n", pibLink, data, length); + + if (pibLink->linkType == GPIB_IO) + { + /* raw-write the data */ + return(niGpibCmd(pibLink->linkId, data, length)); + } + if (pibLink->linkType == BBGPIB_IO) + { + /* raw-write the data */ + return(bbGpibCmd(pibLink, data, length)); + } + return(ERROR); +} + +/****************************************************************************** + * + * These are the BitBus architecture specific functions. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Read a GPIB message via the BitBus driver. + * + ******************************************************************************/ +STATIC int +bbGpibRead(pibLink, device, buffer, length, time) +struct ibLink *pibLink; +int device; +char *buffer; +int length; +int time; +{ + /* The bbIbLink structure starts with the ibLink, so this is OK */ + struct bbIbLink *pbbIbLink = (struct bbIbLink *) pibLink; + + struct dpvtBitBusHead bbdpvt; + int bytesRead; + char msg[150]; + + sprintf(msg, "bbGpibRead(%08.8X, %d, %08.8X, %d, %d): entered", pibLink, device, buffer, length, time); + GpibDebug(pibLink, device, msg, 1); + + bytesRead = 0; + + bbdpvt.finishProc = NULL; /* no callback, synchronous I/O mode */ + bbdpvt.psyncSem = &(pbbIbLink->syncSem); + bbdpvt.link = pibLink->linkId; + + bbdpvt.rxMsg.data = (unsigned char *) buffer; + + bbdpvt.txMsg.route = BB_STANDARD_TX_ROUTE; + bbdpvt.txMsg.node = pibLink->bug; + bbdpvt.txMsg.tasks = BB_GPIB_TASK; + bbdpvt.txMsg.cmd = BB_IBCMD_READ_XACT | device; + bbdpvt.txMsg.length = 7; /* send header only */ + + bbdpvt.rxMsg.cmd = 0; /* init for the while loop */ + bbdpvt.status = BB_OK; + + while (length && (bbdpvt.status == BB_OK) && (!(bbdpvt.rxMsg.cmd & (BB_IBSTAT_EOI|BB_IBSTAT_TMO)))) + { + bbdpvt.rxMaxLen = length > BB_MAX_DAT_LEN ? BB_MAX_DAT_LEN+7 : length+7; + bbdpvt.ageLimit = 0; + if ((*(drvBitBus.qReq))(&bbdpvt, BB_Q_LOW) != OK) + { + bbdpvt.status = BB_NONODE; + return(ERROR); + } + semTake(*(bbdpvt.psyncSem), WAIT_FOREVER); /* wait for response */ + + sprintf(msg, "bbGpibRead(): %02.2X >%.13s< driver status 0x%02.2X", bbdpvt.rxMsg.cmd, bbdpvt.rxMsg.data, bbdpvt.status); + GpibDebug(pibLink, device, msg, 1); + + bbdpvt.txMsg.cmd = BB_IBCMD_READ; /* in case have more reading to do */ + bbdpvt.rxMsg.data += bbdpvt.rxMsg.length - 7; + length -= bbdpvt.rxMsg.length - 7; + bytesRead += bbdpvt.rxMsg.length - 7; + } + if ((bbdpvt.rxMsg.cmd & BB_IBSTAT_TMO) || (bbdpvt.status != BB_OK)) + return(ERROR); + else + return(bytesRead); +} + +/****************************************************************************** + * + * Write a GPIB message by way of the bitbus driver. + * + ******************************************************************************/ +STATIC int +bbGpibWrite(pibLink, device, buffer, length, time) +struct ibLink *pibLink; +int device; +char *buffer; +int length; +int time; +{ + /* The bbIbLink structure starts with the ibLink, so this is OK */ + struct bbIbLink *pbbIbLink = (struct bbIbLink *) pibLink; + + struct dpvtBitBusHead bbdpvt; + unsigned char dbugBuf[BB_MAX_DAT_LEN + 1]; + unsigned char more2GoCommand; + unsigned char lastCommand; + int bytesSent; + char msg[150]; + + sprintf(msg, "bbGpibWrite(%08.8X, %d, %08.8X, %d, %d): entered", pibLink, device, buffer, length, time); + GpibDebug(pibLink, device, msg, 1); + + bytesSent = length; /* we either get an error or send them all */ + + bbdpvt.finishProc = NULL; /* no callback, synchronous I/O mode */ + bbdpvt.psyncSem = &(pbbIbLink->syncSem); + bbdpvt.link = pibLink->linkId; + bbdpvt.rxMaxLen = 7; /* only get the header back */ + + bbdpvt.txMsg.route = BB_STANDARD_TX_ROUTE; + bbdpvt.txMsg.node = pibLink->bug; + bbdpvt.txMsg.tasks = BB_GPIB_TASK; + + bbdpvt.txMsg.data = (unsigned char *) buffer; + + bbdpvt.rxMsg.cmd = 0; /* Init for error checking */ + bbdpvt.status = BB_OK; + + /* if more than BB_MAX_DAT_LEN bytes */ + more2GoCommand = BB_IBCMD_ADDR_WRITE | device; + + /* if less than BB_MAX_DAT_LEN+1 bytes */ + lastCommand = BB_IBCMD_WRITE_XACT | device; + + while (length && (bbdpvt.status == BB_OK) && (!(bbdpvt.rxMsg.cmd & BB_IBSTAT_TMO))) + { + if (length > BB_MAX_DAT_LEN) + { + bbdpvt.txMsg.length = BB_MAX_DAT_LEN+7; + bbdpvt.txMsg.cmd = more2GoCommand; /* Write to device */ + length -= BB_MAX_DAT_LEN; /* Ready for next chunk */ + + more2GoCommand = BB_IBCMD_WRITE; + lastCommand = BB_IBCMD_WRITE_EOI; + } + else + { + bbdpvt.txMsg.length = length+7; + bbdpvt.txMsg.cmd = lastCommand; + length = 0; /* This is the last one */ + } +#if 0 + if (ibDebug || bbibDebug) + { + bcopy(bbdpvt.txMsg.data, dbugBuf, bbdpvt.txMsg.length-7); + dbugBuf[bbdpvt.txMsg.length-7] = '\0'; + logMsg("bbGpibWrite():sending %02.2X >%s<", bbdpvt.txMsg.cmd, dbugBuf); + } +#else + bcopy(bbdpvt.txMsg.data, dbugBuf, bbdpvt.txMsg.length-7); + dbugBuf[bbdpvt.txMsg.length-7] = '\0'; + sprintf(msg, "bbGpibWrite():sending %02.2X >%s<", bbdpvt.txMsg.cmd, dbugBuf); + GpibDebug(pibLink, device, msg, 1); +#endif + + bbdpvt.ageLimit = 0; + if ((*(drvBitBus.qReq))(&bbdpvt, BB_Q_HIGH) != OK) + { + bbdpvt.status = BB_NONODE; + return(ERROR); + } + + semTake(*(bbdpvt.psyncSem), WAIT_FOREVER); /* wait for response */ + + sprintf(msg, " RAC status = 0x%02.2X driver status = 0x%02.2X", bbdpvt.rxMsg.cmd, bbdpvt.status); + GpibDebug(pibLink, device, msg, 1); + + bbdpvt.txMsg.data += BB_MAX_DAT_LEN; /* in case there is more */ + } + + /* All done, check to see if we died due to an error */ + + if ((bbdpvt.rxMsg.cmd & BB_IBSTAT_TMO) || (bbdpvt.status != BB_OK)) + return(ERROR); + else + return(bytesSent); +} + +/******************************************************************************/ +STATIC int +bbGpibCmd(pibLink, buffer, length) +struct ibLink *pibLink; +char *buffer; +int length; +{ + /* The bbIbLink structure starts with the ibLink, so this is OK */ + struct bbIbLink *pbbIbLink = (struct bbIbLink *) pibLink; + + struct dpvtBitBusHead bbdpvt; + int bytesSent; + char msg[150]; + + sprintf(msg, "bbGpibCmd(%08.8X, %08.8X, %d): entered", pibLink, buffer, length); + GpibDebug(pibLink, 0, msg, 1); + + bytesSent = length; + + bbdpvt.finishProc = NULL; /* no callback, synchronous I/O mode */ + bbdpvt.psyncSem = &(pbbIbLink->syncSem); + bbdpvt.link = pibLink->linkId; + bbdpvt.rxMaxLen = 7; /* only get the header back */ + + bbdpvt.status = BB_OK; /* prime these for the while loop */ + bbdpvt.rxMsg.cmd = 0; + + bbdpvt.txMsg.route = BB_STANDARD_TX_ROUTE; + bbdpvt.txMsg.node = pibLink->bug; + bbdpvt.txMsg.tasks = BB_GPIB_TASK; + bbdpvt.txMsg.cmd = BB_IBCMD_WRITE_CMD; + bbdpvt.txMsg.data = (unsigned char *) buffer; + + while ((length > BB_MAX_DAT_LEN) && (bbdpvt.status == BB_OK) && (!(bbdpvt.rxMsg.cmd & BB_IBSTAT_TMO))) + { + bbdpvt.txMsg.length = BB_MAX_DAT_LEN+7; /* send a chunk */ + bbdpvt.ageLimit = 0; + if ((*(drvBitBus.qReq))(&bbdpvt, BB_Q_HIGH) != OK) + { + bbdpvt.status = BB_NONODE; + return(ERROR); + } + semTake(*(bbdpvt.psyncSem), WAIT_FOREVER); /* wait for response */ + + length -= BB_MAX_DAT_LEN; /* ready for next chunk */ + bbdpvt.txMsg.data += BB_MAX_DAT_LEN; + } + if ((bbdpvt.status == BB_OK) && (!(bbdpvt.rxMsg.cmd & BB_IBSTAT_TMO))) + { + if (semTake(*(bbdpvt.psyncSem), 0) == OK) + { + sprintf(msg, "bbGpibCmd() able to take the dang sync sem before queueing!"); + GpibDebug(pibLink, 0, msg, 1); + } + bbdpvt.txMsg.length = length+7; /* send the last chunk */ + bbdpvt.ageLimit = 0; + if ((*(drvBitBus.qReq))(&bbdpvt, BB_Q_HIGH) != OK) + { + bbdpvt.status = BB_NONODE; + return(ERROR); + } + semTake(*(bbdpvt.psyncSem), WAIT_FOREVER); /* wait for response */ +/* BUG -- check bitbus response */ + } + return(bytesSent); +} + +/******************************************************************************/ +STATIC int +bbCheckLink(link, bug) +int link; +int bug; +{ + if (findBBLink(link, bug) != NULL) + return(OK); + else + return(ERROR); +} + +/******************************************************************************/ +STATIC int +bbSrqPollInhibit(link, bug, gpibAddr) +int link; +int bug; +int gpibAddr; +{ + logMsg("bbSrqPollInhibit called for link %d, bug %d, device %d\n", link, bug, gpibAddr); + return(ERROR); +} + +/****************************************************************************** + * + * Initialize all required structures and start an ibLinkTask() for use with + * a BBGPIB_IO based link. + * + ******************************************************************************/ +STATIC int +bbGenLink(link, bug) +int link; +int bug; +{ + struct bbIbLink *bbIbLink; + + if (ibDebug || bbibDebug) + logMsg("bbGenLink(%d, %d): entered\n", link, bug); + + /* First check to see if there is already a link set up */ + bbIbLink = findBBLink(link, bug); + + if (bbIbLink != NULL) + { /* Already have initialized the link for this guy... */ + if (bbibDebug || ibDebug) + logMsg("bbGenLink(%d, %d): link already initialized\n", link, bug); + + return(OK); + } + + /* This link is not started yet, initialize all the required stuff */ + + if ((bbIbLink = (struct bbIbLink *) malloc(sizeof(struct bbIbLink))) == NULL) + { + logMsg("bbGenLink(%d, %d): can't malloc memory for link structure\n", link, bug); + return(ERROR); + } + + bbIbLink->ibLink.linkType = BBGPIB_IO; + bbIbLink->ibLink.linkId = link; + bbIbLink->ibLink.bug = bug; + + bbIbLink->syncSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + + ibLinkInit(&(bbIbLink->ibLink)); + + /* BUG -- should have a lock in the rootBBLink list! */ + bbIbLink->next = rootBBLink; + rootBBLink = bbIbLink; /* link the new one into the list */ + + return(ibLinkStart(&(bbIbLink->ibLink))); +/* BUG -- I should free up the stuff if the init failed for some reason */ +} + +/****************************************************************************** + * + * IOCTL control function for BBGPIB_IO based links. + * + ******************************************************************************/ +STATIC int +bbGpibIoctl(int link, int bug, int cmd, int v, caddr_t p) +{ + int stat = ERROR; + struct bbIbLink *pbbIbLink; + struct dpvtBitBusHead bbDpvt; + unsigned char buf[BB_MAX_DAT_LEN]; + + if (ibDebug || bbibDebug) + logMsg("bbGpibIoctl(%d, %d, %d, %08.8X, %08.8X): called\n", link, bug, cmd, v, p); + +/* No checkLink() is done, because findBBLink() is done when needed */ + + switch (cmd) { + case IBTMO: /* set timeout time for next transaction only */ + /* find the ibLink structure for the requested link & bug */ +#if 1 + if ((pbbIbLink = (struct bbIbLink *)&(findBBLink(link, bug)->ibLink)) != NULL) +#else + if ((pbbIbLink = findBBLink(link, bug)) != NULL) +#endif + { + /* build a TMO message to send to the bug */ + bbDpvt.txMsg.length = 7; + bbDpvt.txMsg.route = BB_STANDARD_TX_ROUTE; + bbDpvt.txMsg.node = bug; + bbDpvt.txMsg.tasks = BB_GPIB_TASK; + bbDpvt.txMsg.cmd = BB_IBCMD_SET_TMO; + bbDpvt.txMsg.data = buf; + + buf[0] = v; + + bbDpvt.rxMsg.route = 0; + bbDpvt.rxMaxLen = 7; /* will only get header back anyway */ + bbDpvt.finishProc = NULL; /* no callback when receive reply */ + bbDpvt.psyncSem = &(pbbIbLink->syncSem); + bbDpvt.link = link; + bbDpvt.ageLimit = 0; + + /* send it to the bug */ + if ((*(drvBitBus.qReq))(&bbDpvt, BB_Q_HIGH) != OK) + { + bbDpvt.status = BB_NONODE; + return(ERROR); + } + semTake(*(bbDpvt.psyncSem), WAIT_FOREVER); /* wait for finish */ + if ((bbDpvt.status == BB_OK) && (!(bbDpvt.rxMsg.cmd & BB_IBSTAT_TMO))) + stat = OK; + else + stat = ERROR; + } + break; + + case IBIFC: /* send an Interface Clear pulse */ + /* find the ibLink structure for the requested link & bug */ +#if 1 + if ((pbbIbLink = (struct bbIbLink *)&(findBBLink(link, bug)->ibLink)) != NULL) +#else + if ((pbbIbLink = findBBLink(link, bug)) != NULL) +#endif + { + /* build an IFC message to send to the bug */ + bbDpvt.txMsg.length = 7; + bbDpvt.txMsg.route = BB_STANDARD_TX_ROUTE; + bbDpvt.txMsg.node = bug; + bbDpvt.txMsg.tasks = BB_GPIB_TASK; + bbDpvt.txMsg.cmd = BB_IBCMD_IFC; + + bbDpvt.rxMsg.route = 0; + bbDpvt.rxMaxLen = 7; /* will only get header back */ + bbDpvt.finishProc = NULL; /* no callback when get reply */ + bbDpvt.psyncSem = &(pbbIbLink->syncSem); + bbDpvt.priority = 0; + bbDpvt.link = link; + bbDpvt.ageLimit = 0; + + /* send it to the bug */ + if ((*(drvBitBus.qReq))(&bbDpvt, BB_Q_HIGH) != OK) + { + bbDpvt.status = BB_NONODE; + return(ERROR); + } + semTake(*(bbDpvt.psyncSem), WAIT_FOREVER); /* wait for finish */ + if ((bbDpvt.status == BB_OK) && (!(bbDpvt.rxMsg.cmd & BB_IBSTAT_TMO))) + stat = OK; + else + stat = ERROR; + } + break; + case IBREN: /* turn the Remote Enable line on or off */ + case IBGTS: /* go to standby (ATN off etc... ) */ + case IBGTA: /* go to active (ATN on etc... ) */ + stat = OK; + break; + case IBGENLINK: /* request the initialization of a link */ + stat = bbGenLink(link, bug); + break; + case IBGETLINK: /* request the address of the ibLink structure */ + *(struct ibLink **)p = &(findBBLink(link, bug)->ibLink); + break; + default: + logMsg("bbGpibIoctl(%d, %d, %d, %08.8X, %08.8X): invalid command requested\n", link, bug, cmd, v, p); + } + return(stat); +} + +/****************************************************************************** + * + * Find a bbIbLink structure given a link number and a bug number. + * + ******************************************************************************/ +struct bbIbLink * +findBBLink(link, bug) +int link; +int bug; +{ + struct bbIbLink *bbIbLink; + + bbIbLink = rootBBLink; + while (bbIbLink != NULL) + { + if ((bbIbLink->ibLink.linkId == link) && (bbIbLink->ibLink.bug == bug)) + break; + else + bbIbLink = bbIbLink->next; + } + if (ibDebug || bbibDebug) + logMsg("findBBLink(%d, %d): returning %08.8X\n", link, bug, bbIbLink); + + return(bbIbLink); +} + /******************************************************************************/ +STATIC int GpibDebug(struct ibLink *pIbLink, int Address, char *Msg, int DBLevel) +{ +#ifdef GPIB_SUPER_DEBUG + semTake(pIbLink->History.Sem, WAIT_FOREVER); + + pIbLink->History.Hist[pIbLink->History.Next].Time = tickGet(); + pIbLink->History.Hist[pIbLink->History.Next].DevAddr = Address; + strncpy(pIbLink->History.Hist[pIbLink->History.Next].Msg, Msg, GPIB_SUPER_DEBUG_HISTORY_STRLEN); + + if (++pIbLink->History.Next == GPIB_SUPER_DEBUG_HISTORY_SIZ) + pIbLink->History.Next = 0; + + if (pIbLink->History.Num < GPIB_SUPER_DEBUG_HISTORY_SIZ) + ++pIbLink->History.Num; + + semGive(pIbLink->History.Sem); +#endif + + if (ibDebug > DBLevel) + { + if (pIbLink->linkType == GPIB_IO) + logMsg("GPIB-L%d-D%d:%s\n", pIbLink->linkId, Address, Msg); + else if (pIbLink->linkType == BBGPIB_IO) + logMsg("BBIB-L%d-B%d-D%d:%s\n", pIbLink->linkId, pIbLink->bug, Address, Msg); + } + + return(0); +} +#ifdef GPIB_SUPER_DEBUG +IBHistDump(int type, int link, int bug) +{ + struct ibLink *pibLink; + int i; + int count; + + if (type == 0) + { /* NI gpib link */ + logMsg("Only bitbus links supported for history dumps\n"); + return(-1); + } + else + { /* Bitbus link */ + if ((pibLink = &(findBBLink(link, bug)->ibLink)) == NULL) + { + logMsg("Invalid link and/or bug specified\n"); + return(-1); + } + } + semTake(pibLink->History.Sem, WAIT_FOREVER); + /* pibLink now represents the link to dump history on */ + if (pibLink->History.Num < GPIB_SUPER_DEBUG_HISTORY_SIZ) + { + i = 0; + count = pibLink->History.Num; + } + else + { + i = pibLink->History.Next; + count = GPIB_SUPER_DEBUG_HISTORY_SIZ; + } + + while(count) + { + if (pibLink->linkType == GPIB_IO) + { + logMsg("%d GPIB-L%d-D%d: %s\n", pibLink->History.Hist[i].Time, + pibLink->linkId, pibLink->History.Hist[i].DevAddr, + pibLink->History.Hist[i].Msg); + } + else if (pibLink->linkType == BBGPIB_IO) + { + logMsg("%d BBIB-l%d-B%d-D%d: %s\n", pibLink->History.Hist[i].Time, + pibLink->linkId, pibLink->bug, pibLink->History.Hist[i].DevAddr, + pibLink->History.Hist[i].Msg); + } + + if (++i == GPIB_SUPER_DEBUG_HISTORY_SIZ) + i = 0; + --count; + } + + semGive(pibLink->History.Sem); + return(0); +} +#endif + +#if 0 +/* A way to stop the CPU when idle... run from shell at prio 250 */ +cpuStopperThingy() +{ + while (1) + asm(" stop #0x3000"); +} +#endif diff --git a/src/drv/old/drvGpib.h b/src/drv/old/drvGpib.h new file mode 100644 index 000000000..cbf4f6290 --- /dev/null +++ b/src/drv/old/drvGpib.h @@ -0,0 +1,315 @@ +/* drvGpib.h */ +/* base/src/drv $Id$ */ + +/* + * Origional Author: Unknown, probably National Instruments + * Author: John Winans + * Date: 10-27-91 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 10-27-91 winans moved into epics area + */ + + +/* GPIB-1014 engineering software package UNIX include file */ + +/* + * The following structure dma_chan defines the memory map of a single + * channel on the Hitachi 68450. + */ + +struct dma_chan { + char csr; /* +0 channel status register */ + char cer; /* +1 channel error register */ + char f0[2]; + char dcr; /* +4 device control register */ + char ocr; /* +5 operation control register */ + char scr; /* +6 sequence control register */ + char ccr; /* +7 channel control register */ + char f1[2]; +unsigned short mtc; /* +10 memory transfer counter */ + long mar; /* +12 memory address register */ + char f2[4]; + long dar; /* +20 device address register */ + char f3[2]; +unsigned short btc; /* +26 base transfer counter */ + long bar; /* +28 base address register */ + char f4[5]; + char niv; /* +37 normal interrupt vector */ + char f5; + char eiv; /* +39 error interrupt vector */ + char f6; + char mfc; /* +41 memory function codes */ + char f7[3]; + char cpr; /* +45 channel priority register */ + char f8[3]; + char dfc; /* +49 device function codes */ + char f9[7]; + char bfc; /* +57 base function codes */ + char fA[6]; +}; + +/* + * The structure ibregs defines the address space of the GPIB-1014. + */ +struct ibregs { + + struct dma_chan ch0; /* +0 dma channel 0 */ + struct dma_chan ch1; /* +64 dma channel 1 */ + struct dma_chan ch2; /* +128 dma channel 2 */ + struct dma_chan ch3; /* +192 dma channel 3 */ +#define gcr ch3.fA[5] /* +255 general control register */ + char fB; + char cfg1; /* +257 config reg 1 */ +#define gsr cfg1 /* +257 GSR */ + char fC[3]; + char cfg2; /* +261 config reg 2 */ + char fD[10]; + char fE, cdor; /* +273 byte out register */ + char fF, imr1; /* +275 interrupt mask register 1 */ + char f10,imr2; /* +277 interrupt mask register 2 */ + char f11,spmr; /* +279 serial poll mode register */ + char f12,admr; /* +281 address mode register */ + char f13,auxmr; /* +283 auxiliary mode register */ + char f14,adr; /* +285 address register 0/1 */ + char f15,eosr; /* +287 end of string register */ + char f16[512-289]; /* +289 filler to rest of window */ +}; +/* +* The structure srqTable defines the srq status of each of 31 possible devices +*/ +struct srqTable { + short active; + char lad; + char tad; + unsigned char status; + SEM_ID pSem; +} ; + +/* 7210 readable registers */ +#define dir cdor +#define isr1 imr1 +#define isr2 imr2 +#define spsr spmr +#define adsr admr +#define cptr auxmr +#define adr0 adr +#define adr1 eosr + +/* GPIB status register */ +#define GSR_EOI 0x80 +#define GSR_ATN 0x40 +#define GSR_SRQ 0x20 +#define GSR_REN 0x10 +#define GSR_IFC 0x08 +#define GSR_NRFD 0x04 +#define GSR_NDAC 0x02 +#define GSR_DAV 0x01 + +/* 68450 DMAC register definitions */ + +/* Device Control Register (dcr) bits */ +#define D_CS 0x80 /* cycle steal mode */ +#define D_CSM 0x80 /* cycle steal mode,with bus Monitor*/ +#define D_CSH 0xC0 /* cycle steal with hold */ +#define D_CSHM 0xC0 /* cycle steal with hold,with bus Monitor */ +#define D_IACK 0x20 /* device with DMAACK, implicitly addressed */ +#define D_P16 0x08 /* 16 bit device port size */ +#define D_IPCL 0x01 /* PCL set to status input with interrupt */ + +/* Operation Control Register (ocr) bits */ +#define D_MTD 0x00 /* direction is from memory to device */ +#define D_DTM 0x80 /* direction is from device to memory */ +#define D_TW 0x10 /* transfer size is word */ +#define D_TL 0x30 /* transfer size is long word */ +#define D_ACH 0x08 /* array chaining */ +#define D_LACH 0x0C /* linked array chaining */ +#define D_ARQ 0x03 /* auto request first transfer, then external*/ +#define D_XRQ 0x02 /* external request mode */ +#define D_ARM 0x01 /* auto request at maximum rate */ + + +/* Sequence Control Register (scr) bits */ +#define D_MCD 0x08 /* memory address counts down */ +#define D_MCU 0x04 /* memory address counts up */ +#define D_DCD 0x02 /* device address counts down */ +#define D_DCU 0x01 /* device address counts up */ + +/* Channel Control Register (ccr) bits */ +#define D_SRT 0x80 /* start channel operation */ +#define D_CON 0x40 /* continue */ +#define D_HLT 0x20 /* halt channel operation */ +#define D_SAB 0x10 /* software abort */ +#define D_EINT 0x08 /* enable channel interrupts */ + +/* Channel Status Register (csr) bits */ +#define D_CLEAR 0xFF /* clear all bits */ +#define D_COC 0x80 /* channel operation complete */ +#define D_BTC 0x40 /* block transfer complete */ +#define D_NDT 0x20 /* normal device termination */ +#define D_ERR 0x10 /* error as coded in cer */ +#define D_ACT 0x08 /* channel active */ +#define D_PCLT 0x02 /* PCL transition occurred */ +#define D_PCLH 0x01 /* PCL line is high */ +#define D_NSRQ 0x01 /* Not SRQ (gpib line) */ + +/* Channel Error Register (cer) bits */ +#define D_ECF 0x01 /* configuration error */ +#define D_ETM 0x02 /* operation timing error */ +#define D_EMA 0x05 /* memory address error */ +#define D_EDA 0x06 /* device address error */ +#define D_EBA 0x07 /* base address error */ +#define D_EBUS 0x08 /* bus error */ +#define D_ECT 0x0C /* transfer count error */ +#define D_EEAB 0x01 /* external abort */ +#define D_ESAB 0x11 /* software abort */ + +/* Channel Priority Register (cpr) bits */ +#define D_PR1 0x01 /* priority 1 */ +#define D_PR2 0x02 /* priority 2 */ +#define D_PR3 0x03 /* priority 3 */ + +/* Function Code Register (fcr) bits */ +#define D_SUP 0x04 /* supervisor access */ +#define D_S24 0x02 /* standard 24 bit addressing */ +#define D_PSA 0x01 /* program space access */ + +/* Configuration Register 1 (cfg1) bits */ +#define D_OUT 0 /* direction memory to GPIB */ +#define D_IN (1<<0) /* direction GPIB to memory */ +#define D_DBM (1<<1) /* disarm Bus Monitor mode */ +#define D_ECC (1<<2) /* arm automatic carry cycle feature */ +#define D_BRG0 (00<<3) /* select bus request/grant line 1 */ +#define D_BRG1 (01<<3) /* select bus request/grant line 1 */ +#define D_BRG2 (02<<3) /* select bus request/grant line 2 */ +#define D_BRG3 (03<<3) /* select bus request/grant line 3 */ + + +/* Configuration Register 2 (cfg2) bits */ +#define D_SC (1<<0) /* set system controller (SC) bit */ +#define D_LMR (1<<1) /* set local master reset bit */ +#define D_SPAC (1<<2) /* set supervisor only access to board */ +#define D_SFL (1<<3) /* clear SYSFAIL line */ + + +/* Control masks for hidden registers (auxmr) */ + +#define ICR 0040 +#define PPR 0140 +#define AUXRA 0200 +#define AUXRB 0240 +#define AUXRE 0300 +#define CNT 0340 /* OR of all of above */ + +/* 7210 bits: POSITION 7210 reg */ + +#define HR_DI (1<<0) /* ISR1 */ +#define HR_DO (1<<1) /* , */ +#define HR_ERR (1<<2) /* , */ +#define HR_DEC (1<<3) /* , */ +#define HR_END (1<<4) /* , */ +#define HR_DET (1<<5) /* , */ +#define HR_APT (1<<6) /* , */ +#define HR_CPT (1<<7) /* , */ +#define HR_DIIE (1<<0) /* IMR1 */ +#define HR_DOIE (1<<1) /* , */ +#define HR_ERRIE (1<<2) /* , */ +#define HR_DECIE (1<<3) /* , */ +#define HR_ENDIE (1<<4) /* , */ +#define HR_DETIE (1<<5) /* , */ +#define HR_ADSC (1<<0) /* ISR2 */ +#define HR_REMC (1<<1) /* , */ +#define HR_LOKC (1<<2) /* , */ +#define HR_CO (1<<3) /* , */ +#define HR_REM (1<<4) /* , */ +#define HR_LOK (1<<5) /* , */ +#define HR_SRQI (1<<6) /* , */ +#define HR_INT (1<<7) /* , */ +#define HR_ACIE (1<<0) /* IMR2 */ +#define HR_REMIE (1<<1) /* , */ +#define HR_LOKIE (1<<2) /* , */ +#define HR_COIE (1<<3) /* , */ +#define HR_DMAI (1<<4) /* , */ +#define HR_DMAO (1<<5) /* , */ +#define HR_SRQIE (1<<6) /* , */ +#define HR_PEND (1<<6) /* SPSR */ +#define HR_RSV (1<<6) /* SPMR */ +#define HR_MJMN (1<<0) /* ADSR */ +#define HR_TA (1<<1) /* , */ +#define HR_LA (1<<2) /* , */ +#define HR_TPAS (1<<3) /* , */ +#define HR_LPAS (1<<4) /* , */ +#define HR_SPMS (1<<5) /* , */ +#define HR_NATN (1<<6) /* , */ +#define HR_CIC (1<<7) /* , */ +#define HR_ADM0 (1<<0) /* ADMR */ +#define HR_ADM1 (1<<1) /* , */ +#define HR_TRM0 (1<<4) /* , */ +#define HR_TRM1 (1<<5) /* , */ +#define HR_LON (1<<6) /* , */ +#define HR_TON (1<<7) /* , */ +#define HR_DL (1<<5) /* ADR */ +#define HR_DT (1<<6) /* , */ +#define HR_ARS (1<<7) /* , */ + +#define HR_HLDA (1<<0) /* auxra */ +#define HR_HLDE (1<<1) /* , */ +#define HR_REOS (1<<2) /* , */ +#define HR_XEOS (1<<3) /* , */ +#define HR_BIN (1<<4) /* , */ +#define HR_CPTE (1<<0) /* auxrb */ +#define HR_SPEOI (1<<1) /* , */ +#define HR_TRI (1<<2) /* , */ +#define HR_INV (1<<3) /* , */ +#define HR_ISS (1<<4) /* , */ +#define HR_PPS (1<<3) /* ppr */ +#define HR_PPU (1<<4) /* , */ + +/* 7210 Auxiliary Commands */ + +#define AUX_PON 000 /* Immediate Execute pon */ +#define AUX_CR 002 /* Chip Reset */ +#define AUX_FH 003 /* Finish Handshake */ +#define AUX_TRIG 004 /* Trigger */ +#define AUX_RTL 005 /* Return to local */ +#define AUX_SEOI 006 /* Send EOI */ +#define AUX_NVAL 007 /* Non-Valid Secondary Command or Address */ +#define AUX_VAL 017 /* Valid Secondary Command or Address */ +#define AUX_CPPF 001 /* Clear Parallel Poll Flag */ +#define AUX_SPPF 011 /* Set Parallel Poll Flag */ +#define AUX_TCA 021 /* Take Control Asynchronously */ +#define AUX_TCS 022 /* Take Control Synchronously */ +#define AUX_TCSE 032 /* Take Control Synchronously on End */ +#define AUX_GTS 020 /* Go To Standby */ +#define AUX_LTN 023 /* Listen */ +#define AUX_LTNC 033 /* Listen in Continuous Mode */ +#define AUX_LUN 034 /* Local Unlisten */ +#define AUX_EPP 035 /* Execute Parallel Poll */ +#define AUX_SIFC 036 /* Set IFC */ +#define AUX_CIFC 026 /* Clear IFC */ +#define AUX_SREN 037 /* Set REN */ +#define AUX_CREN 027 /* Clear REN */ +#define AUX_DSC 024 /* Disable System Control */ + diff --git a/src/drv/old/drvHp1404a.c b/src/drv/old/drvHp1404a.c new file mode 100644 index 000000000..06c51d071 --- /dev/null +++ b/src/drv/old/drvHp1404a.c @@ -0,0 +1,408 @@ +/* base/src/drv $Id$ */ +/* + * + * HP E1404A VXI bus slot zero translator + * device dependent routines + * + * share/src/drv/@(#)drvHp1404a.c 1.7 8/27/93 + * + * Author Jeffrey O. Hill + * Date 030692 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 joh 073092 Added msg device support & interrupt shutdown for + * soft reboots + * .02 joh 082792 converted to ANSI C + * .03 mgb 080493 Removed V5/V4 and EPICS_V2 conditionals + * + * + * + */ + +static char *sccsId = "@(#)drvHp1404a.c 1.7\t8/27/93"; + +#include +#include +#include +#include + +#include +#include +#include + +LOCAL unsigned long hpE1404DriverID; + +struct hpE1404_config{ + void (*pSignalCallback)(int16_t signal); +}; + +#define TLTRIG(N) (1<<(N)) +#define ECLTRIG(N) (1<<((N)+8)) + +/* + * enable int when signal register is written + */ +#define HP1404A_INT_ENABLE 0x0008 +#define HP1404A_INT_DISABLE 0x0000 + +/* + * + * tag the device dependent registers + */ +#define IRQ_enable dir.w.dd.reg.ddx1a +#define MSG_status dir.w.dd.reg.ddx1e +#define fp_trig_drive dir.w.dd.reg.ddx2a +#define bp_trig_drive dir.w.dd.reg.ddx22 +#define signal_read dir.r.dd.reg.ddx10 + +#define hpE1404PConfig(LA, PC) \ + epvxiFetchPConfig((LA), hpE1404DriverID, (PC)) + +LOCAL void hpE1404InitLA( + unsigned la +); + +LOCAL int hpE1404ShutDown( + void +); + +LOCAL void hpE1404ShutDownLA( + unsigned la +); + +LOCAL void hpE1404IOReport( +unsigned la, +unsigned level +); + +LOCAL void hpE1404Int( +unsigned la +); + + + +/* + * + * hpE1404Init + * + */ +hpE1404Stat hpE1404Init(void) +{ + hpE1404Stat status; + + status = rebootHookAdd(hpE1404ShutDown); + if(status<0){ + status = S_dev_internal; + errMessage(status, "rebootHookAdd() failed"); + return status; + } + + hpE1404DriverID = epvxiUniqueDriverID(); + + status = epvxiRegisterMakeName( + VXI_MAKE_HP, + "Hewlett-Packard"); + if(status){ + errMessage(status, NULL); + } + status = epvxiRegisterModelName( + VXI_MAKE_HP, + VXI_HP_MODEL_E1404_REG_SLOT0, + "Slot Zero Translator (reg)"); + if(status){ + errMessage(status, NULL); + } + status = epvxiRegisterModelName( + VXI_MAKE_HP, + VXI_HP_MODEL_E1404_REG, + "Translator (reg)"); + if(status){ + errMessage(status, NULL); + } + status = epvxiRegisterModelName( + VXI_MAKE_HP, + VXI_HP_MODEL_E1404_MSG, + "Translator (msg)"); + if(status){ + errMessage(status, NULL); + } + + { + epvxiDeviceSearchPattern dsp; + + dsp.flags = VXI_DSP_make | VXI_DSP_model; + dsp.make = VXI_MAKE_HP; + dsp.model = VXI_HP_MODEL_E1404_REG_SLOT0; + status = epvxiLookupLA(&dsp, hpE1404InitLA, (void *)NULL); + if(status){ + errMessage(status, NULL); + return status; + } + + dsp.model = VXI_HP_MODEL_E1404_REG; + status = epvxiLookupLA(&dsp, hpE1404InitLA, (void *)NULL); + if(status){ + errMessage(status, NULL); + return status; + } + } + + return VXI_SUCCESS; +} + + +/* + * + * hpE1404ShutDown() + * + * + */ +LOCAL int hpE1404ShutDown(void) +{ + hpE1404Stat status; + epvxiDeviceSearchPattern dsp; + + dsp.flags = VXI_DSP_make | VXI_DSP_model; + dsp.make = VXI_MAKE_HP; + dsp.model = VXI_HP_MODEL_E1404_REG_SLOT0; + status = epvxiLookupLA(&dsp, hpE1404ShutDownLA, (void *)NULL); + if(status){ + errMessage(status, NULL); + return ERROR; + } + + dsp.model = VXI_HP_MODEL_E1404_REG; + status = epvxiLookupLA(&dsp, hpE1404ShutDownLA, (void *)NULL); + if(status){ + errMessage(status, NULL); + return ERROR; + } + return OK; +} + + +/* + * + * hpE1404ShutDownLA() + * + * + */ +LOCAL +void hpE1404ShutDownLA( + unsigned la +) +{ + struct vxi_csr *pcsr; + + pcsr = VXIBASE(la); + + pcsr->IRQ_enable = HP1404A_INT_DISABLE; +} + + +/* + * + * hpE1404InitLA() + * + */ +LOCAL +void hpE1404InitLA( + unsigned la +) +{ + struct hpE1404_config *pc; + struct vxi_csr *pcsr; + hpE1404Stat status; + + status = epvxiOpen( + la, + hpE1404DriverID, + sizeof(*pc), + hpE1404IOReport); + if(status){ + errMessage(status, NULL); + return; + } + + pcsr = VXIBASE(la); + + status = hpE1404PConfig(la, pc); + if(status){ + errMessage(status, NULL); + epvxiClose(la, hpE1404DriverID); + return; + } + + /* + * set the self test status to passed for + * the message based device + */ + pcsr->MSG_status = VXIPASS<<2; + + intConnect( + INUM_TO_IVEC(la), + hpE1404Int, + la); + + /* + * enable int when signal register is written + */ + pcsr->IRQ_enable = HP1404A_INT_ENABLE; + + return; +} + + +/* + * + * hpE1404SignalConnect() + * + */ +hpE1404Stat hpE1404SignalConnect( +unsigned la, +void (*pSignalCallback)(int16_t signal) +) +{ + hpE1404Stat s; + struct hpE1404_config *pc; + + s = hpE1404PConfig(la, pc); + if(s){ + return s; + } + + pc->pSignalCallback = pSignalCallback; + + return VXI_SUCCESS; +} + + +/* + * + * hpE1404Int() + * + */ +LOCAL +void hpE1404Int( + unsigned la +) +{ + hpE1404Stat s; + struct vxi_csr *pcsr; + unsigned short signal; + struct hpE1404_config *pc; + + s = hpE1404PConfig(la, pc); + if(s){ + errMessage(s, NULL); + return; + } + + /* + * vector is only D8 so we cant check the cause of the int + * (signal cause is assumed since that was all that was enabled) + */ + + pcsr = VXIBASE(la); + + signal = pcsr->signal_read; + + if(pc->pSignalCallback){ + (*pc->pSignalCallback)(signal); + } +} + + +/* + * + * hpE1404RouteTriggerECL + * + */ +hpE1404Stat hpE1404RouteTriggerECL( +unsigned la, /* slot zero device logical address */ +unsigned enable_map, /* bits 0-5 correspond to trig 0-5 */ + /* a 1 enables a trigger */ + /* a 0 disables a trigger */ +unsigned io_map /* bits 0-5 correspond to trig 0-5 */ + /* a 1 sources the front panel */ + /* a 0 sources the back plane */ +) +{ + struct vxi_csr *pcsr; + + pcsr = VXIBASE(la); + + pcsr->fp_trig_drive = (io_map&enable_map)<<8; + pcsr->bp_trig_drive = ((~io_map)&enable_map)<<8; + + return VXI_SUCCESS; +} + + +/* + * + * + * hpE1404RouteTriggerTTL + * + * + */ +hpE1404Stat hpE1404RouteTriggerTTL( +unsigned la, /* slot zero device logical address */ +unsigned enable_map, /* bits 0-5 correspond to trig 0-5 */ + /* a 1 enables a trigger */ + /* a 0 disables a trigger */ +unsigned io_map /* bits 0-5 correspond to trig 0-5 */ + /* a 1 sources the front panel */ + /* a 0 sources the back plane */ +) +{ + struct vxi_csr *pcsr; + + pcsr = VXIBASE(la); + + pcsr->fp_trig_drive = io_map&enable_map; + pcsr->bp_trig_drive = (~io_map)&enable_map; + + return VXI_SUCCESS; +} + + +/* + * + * hpE1404IOReport() + * + * + */ +LOCAL +void hpE1404IOReport( + unsigned la, + unsigned level +) +{ + + + + +} diff --git a/src/drv/old/drvHp1404a.h b/src/drv/old/drvHp1404a.h new file mode 100644 index 000000000..f6f6d2bfb --- /dev/null +++ b/src/drv/old/drvHp1404a.h @@ -0,0 +1,74 @@ +/* base/src/drv $Id$ */ +/* + * drvHp1404a.h + * + * HP E1404A VXI bus slot zero translator + * device dependent routines header file + * + * share/src/drv/@(#)drvHp1404a.h 1.1 8/27/93 + * + * Author Jeffrey O. Hill + * Date 030692 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * + * + * + */ + +typedef long hpE1404Stat; + +hpE1404Stat hpE1404Init(void); + +hpE1404Stat hpE1404SignalConnect( + unsigned la, + void (*pSignalCallback)(int16_t signal) +); + +hpE1404Stat hpE1404RouteTriggerECL( +unsigned la, /* slot zero device logical address */ +unsigned enable_map, /* bits 0-5 correspond to trig 0-5 */ + /* a 1 enables a trigger */ + /* a 0 disables a trigger */ +unsigned io_map /* bits 0-5 correspond to trig 0-5 */ + /* a 1 sources the front panel */ + /* a 0 sources the back plane */ +); + +hpE1404Stat hpE1404RouteTriggerTTL( +unsigned la, /* slot zero device logical address */ +unsigned enable_map, /* bits 0-5 correspond to trig 0-5 */ + /* a 1 enables a trigger */ + /* a 0 disables a trigger */ +unsigned io_map /* bits 0-5 correspond to trig 0-5 */ + /* a 1 sources the front panel */ + /* a 0 sources the back plane */ +); + +#define VXI_HP_MODEL_E1404_REG_SLOT0 0x10 +#define VXI_HP_MODEL_E1404_REG 0x110 +#define VXI_HP_MODEL_E1404_MSG 0x111 + + diff --git a/src/drv/old/drvHpe1368a.c b/src/drv/old/drvHpe1368a.c new file mode 100644 index 000000000..a760b09d1 --- /dev/null +++ b/src/drv/old/drvHpe1368a.c @@ -0,0 +1,359 @@ +/* drvHpe1368a.c*/ +/* base/src/drv $Id$ */ + +/* + * hpe1368a_driver.c + * + * driver for hpe1368a and hpe1369a microwave switch VXI modules + * + * Author: Jeff Hill + * Date: 052192 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 071792 joh Added model name registration + * .02 081992 joh vxiUniqueDriverID -> epvxiUniqueDriverID + * .03 082692 mrk Added support for new I/O event scanning and DRVET + * .04 080493 mgb Removed V5/V4 and EPICS_V2 conditionals + * + */ + +static char *sccsId = "@(#)drvHpe1368a.c 1.14\t9/9/93"; + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#define HPE1368A_PCONFIG(LA, PC) \ +epvxiFetchPConfig((LA), hpe1368aDriverId, (PC)) + +#define ChannelEnable(PCSR) ((PCSR)->dir.w.dd.reg.ddx08) +#define ModuleStatus(PCSR) ((PCSR)->dir.r.status) + +#define ALL_SWITCHES_OPEN 0 + +struct hpe1368a_config{ + FAST_LOCK lock; /* mutual exclusion */ + unsigned short pending; /* switch position pending int */ + unsigned short shadow; /* shadow of actual switch pos */ + int busy; /* relays active */ + IOSCANPVT ioscanpvt; +}; + +#define HPE1368A_INT_LEVEL 1 + +LOCAL int hpe1368aDriverId; + +LOCAL void hpe1368a_int_service(unsigned la); +LOCAL void hpe1368a_init_card(unsigned la); +LOCAL void hpe1368a_stat(unsigned la, int level); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvHpe1368a={ + 2, + NULL, /*VXI io report takes care of this */ + hpe1368a_init}; + + +/* + * hpe1368a_init + * + * initialize all hpe1368a cards + * + */ +hpe1368aStat hpe1368a_init(void) +{ + hpe1368aStat r0; + + /* + * do nothing on crates without VXI + */ + if(!epvxiResourceMangerOK){ + return VXI_SUCCESS; + } + + hpe1368aDriverId = epvxiUniqueDriverID(); + + { + epvxiDeviceSearchPattern dsp; + + dsp.flags = VXI_DSP_make | VXI_DSP_model; + dsp.make = VXI_MAKE_HP; + dsp.model = VXI_MODEL_HPE1368A; + r0 = epvxiLookupLA(&dsp, hpe1368a_init_card, (void *)NULL); + if(r0){ + errMessage(r0, NULL); + return r0; + } + } + + return VXI_SUCCESS; +} + + + +/* + * HPE1368A_INIT_CARD + * + * initialize single at5vxi card + * + */ +LOCAL void hpe1368a_init_card(unsigned la) +{ + hpe1368aStat r0; + struct hpe1368a_config *pc; + struct vxi_csr *pcsr; + int model; + + r0 = epvxiOpen( + la, + hpe1368aDriverId, + (unsigned long) sizeof(*pc), + hpe1368a_stat); + if(r0){ + errMessage(r0,NULL); + return; + } + + r0 = HPE1368A_PCONFIG(la, pc); + if(r0){ + errMessage(r0, NULL); + return; + } + + pcsr = VXIBASE(la); + + /* + * we must reset the device to a known state since + * we cant read back the current state + */ + pc->pending = ALL_SWITCHES_OPEN; + pc->shadow = ALL_SWITCHES_OPEN; + ChannelEnable(pcsr) = ALL_SWITCHES_OPEN; + + FASTLOCKINIT(&pc->lock); + scanIoInit(&pc->ioscanpvt); + + r0 = intConnect( + INUM_TO_IVEC(la), + hpe1368a_int_service, + la); + if(r0 == ERROR){ + errMessage(S_dev_vxWorksVecInstlFail, NULL); + return; + } + + sysIntEnable(HPE1368A_INT_LEVEL); + + model = VXIMODEL(pcsr); + r0 = epvxiRegisterModelName( + VXIMAKE(pcsr), + model, + "E 1368A Microwave Switch\n"); + if(r0){ + errMessage(r0, NULL); + } + r0 = epvxiRegisterMakeName(VXIMAKE(pcsr), "Hewlett-Packard"); + if(r0){ + errMessage(r0,NULL); + } +} + + +/* + * + * hpe1368a_int_service() + * + * + * This device interrupts once the + * switches have settled + * + */ +LOCAL void +hpe1368a_int_service(unsigned la) +{ + hpe1368aStat s; + struct hpe1368a_config *pc; + + s = HPE1368A_PCONFIG(la,pc); + if(s){ + return; + } + + /* + * operation completed so we can update + * the shadow value + */ + pc->shadow = pc->pending; + pc->busy = FALSE; + + /* + * tell them that the switches have settled + */ + scanIoRequest(pc->ioscanpvt); +} + + +/* + * HPE1368A_STAT + * + * initialize single at5vxi card + * + */ +LOCAL void hpe1368a_stat( +unsigned la, +int level +) +{ + hpe1368aStat s; + struct hpe1368a_config *pc; + struct vxi_csr *pcsr; + + s = HPE1368A_PCONFIG(la, pc); + if(s){ + errMessage(s,NULL); + return; + } + pcsr = VXIBASE(la); + + if(level>0){ + printf("\tSwitch states %x\n", pc->shadow); + printf("\tModule status %x\n", pcsr->dir.r.status); + if(pc->busy){ + printf("\tModule is busy.\n"); + } + } +} + + +/* + * hpe1368a_getioscanpvt() + */ +hpe1368aStat hpe1368a_getioscanpvt( +unsigned la, +IOSCANPVT *scanpvt +) +{ + hpe1368aStat s; + struct hpe1368a_config *pc; + + s = HPE1368A_PCONFIG(la, pc); + if(s){ + errMessage(s, NULL); + return s; + } + *scanpvt = pc->ioscanpvt; + return VXI_SUCCESS; +} + + +/* + * HPE1368A_BO_DRIVER + */ +hpe1368aStat hpe1368a_bo_driver( +unsigned la, +unsigned val, +unsigned mask +) +{ + hpe1368aStat s; + struct hpe1368a_config *pc; + struct vxi_csr *pcsr; + unsigned int work; + + s = HPE1368A_PCONFIG(la, pc); + if(s){ + errMessage(s, NULL); + return s; + } + + pcsr = VXIBASE(la); + + FASTLOCK(&pc->lock); + + work = pc->pending; + + /* alter specified bits */ + work = (work & ~mask) | (val & mask); + + pc->pending = work; + + ChannelEnable(pcsr) = work; + + FASTUNLOCK(&pc->lock); + + return VXI_SUCCESS; +} + + + +/* + * + * HPE1368A_BI_DRIVER + * + * + * + */ +hpe1368aStat hpe1368a_bi_driver( +unsigned la, +unsigned mask, +unsigned *pval +) +{ + hpe1368aStat s; + struct hpe1368a_config *pc; + + s = HPE1368A_PCONFIG(la, pc); + if(s){ + errMessage(s, NULL); + return s; + } + + FASTLOCK(&pc->lock); + + *pval = pc->shadow & mask; + + FASTUNLOCK(&pc->lock); + + return VXI_SUCCESS; +} diff --git a/src/drv/old/drvHpe1368a.h b/src/drv/old/drvHpe1368a.h new file mode 100644 index 000000000..6e7688555 --- /dev/null +++ b/src/drv/old/drvHpe1368a.h @@ -0,0 +1,60 @@ +/* drvHpe1368a.h*/ +/* base/src/drv $Id$ */ + +/* + * hpe1368a_driver.h + * + * driver for hpe1368a and hpe1369a microwave switch VXI modules + * + * Author: Jeff Hill + * Date: 052192 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * + */ + +#define VXI_MODEL_HPE1368A (0xf28) + +typedef long hpe1368aStat; + +hpe1368aStat hpe1368a_init(void); + +hpe1368aStat hpe1368a_getioscanpvt( +unsigned la, +IOSCANPVT *scanpvt +); + +hpe1368aStat hpe1368a_bo_driver( +unsigned la, +unsigned val, +unsigned mask +); + +hpe1368aStat hpe1368a_bi_driver( +unsigned la, +unsigned mask, +unsigned *pval +); + diff --git a/src/drv/old/drvHpe1445a.c b/src/drv/old/drvHpe1445a.c new file mode 100644 index 000000000..e26af0598 --- /dev/null +++ b/src/drv/old/drvHpe1445a.c @@ -0,0 +1,1185 @@ +/* base/src/drv $Id$ */ +/* + * driver for hpe1445a arbitrary function generator VXI modules + * + * Author: Jeff Hill + * Date: 082492 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 021192 joh Fixed hpe1445aUnloadWaveformLocked() ANSI C + * function prototype missmatch. Changed + * unsigned short to unsigned. + * + * + */ + +static char *sccsId = "@(#)drvHpe1445a.c 1.5\t8/27/93"; + +#include +#include +#include + +#include +#include +#include +#include + +/* + * comment out this line if you prefer + * to use backplane ttl trigger 0 + */ +#define FRONT_PANEL_TRIGGER + +#define VXI_MODEL_HPE1445A (418) + +LOCAL int hpe1445aDriverId; + +#define HPE1445A_MAX_POINTS 0x40000 +#define HPE1445A_MIN_POINTS 4 + +#define HPE1445_DATA_PORT_OFFSET (0x26) + +struct hpe1445aConfig { + FAST_LOCK lck; + char device_active; + double dacPeakAmplitude; + double dacOffset; + char buf[256]; +}; + +#define HPE1445A_PCONFIG(LA, PC) \ +epvxiFetchPConfig((LA), hpe1445aDriverId, (PC)) + +typedef long hpe1445aStat; + +/* + * + * For External Use + * + */ +hpe1445aStat hpe1445aInit(void); +hpe1445aStat hpe1445aSetupDAC(unsigned la, double dacPeakAmplitude, double dacOffset); +hpe1445aStat hpe1445aSetupFreq(unsigned la, char *pFreqString); +void hpe1445aIoReport(unsigned la, int level); +hpe1445aStat hpe1445aLoadWaveform(unsigned la, char *pWaveformName, + double *pdata, unsigned long npoints); +hpe1445aStat hpe1445aUnloadWaveform(unsigned la, char *pWaveformName); +hpe1445aStat hpe1445aActivateWaveform(unsigned la, char *pWaveformName); +hpe1445aStat hpe1445aTest(unsigned la); +hpe1445aStat hpe1445aWriteWithLineno(unsigned la, char *pmsg, unsigned lineno); +void hpe1445aLogErrorsWithLineno(unsigned la, int lineno); + + +/* + * + * For Driver Internal Use + * + */ +LOCAL void hpe1445aInitCard(unsigned la); +LOCAL hpe1445aStat hpe1445aReset(unsigned la); +LOCAL hpe1445aStat logEntireError(unsigned la, int lineno); +LOCAL hpe1445aStat hpe1445aActivateWaveformLocked(unsigned la, char *pWaveformName, + struct hpe1445aConfig *pc); +LOCAL hpe1445aStat hpe1445aSetupFunction(unsigned la); +LOCAL hpe1445aStat hpe1445aSetupOutput(unsigned la); +LOCAL hpe1445aStat hpe1445aArm(unsigned la); +LOCAL hpe1445aStat hpe1445aUnloadWaveformLocked(unsigned la, char *pWaveformName); +LOCAL hpe1445aStat hpe1445aLoadWaveformLocked(unsigned la, + struct hpe1445aConfig *pc, + char *pWaveformName, double *pdata, + unsigned long npoints); + + +#define logErrors(LA) hpe1445aLogErrorsWithLineno(LA, __LINE__) +#define hpe1445aWrite(LA, PMSG) hpe1445aWriteWithLineno(LA, PMSG, __LINE__) + +#define HPE1445A_MAX_NAME_LENGTH 12 +#define SEGMENT_NAME_PREFIX "e_" +#define SEQUENCE_NAME_PREFIX "e__" +#define PREFIX_MAX_NAME_LENGTH 3 +#define MAX_NAME_LENGTH (HPE1445A_MAX_NAME_LENGTH-PREFIX_MAX_NAME_LENGTH) + +#define TEST_FILE_NAME "hpe1445a.dat" + + +/* + * + * hpe1445aTest() + * + * + */ +hpe1445aStat hpe1445aTest(unsigned la) +{ + hpe1445aStat s; + FILE *pf; + char *pfn = TEST_FILE_NAME; + double *pwaveform; + int nsamples; + int i; + + pf = fopen(pfn, "r"); + if(!pf){ + s = S_dev_internal; + errPrintf( + s, + __FILE__, + __LINE__, + "file access problems %s", + pfn); + fclose(pf); + return s; + } + s = fscanf(pf, "%d", &nsamples); + if(s!=1){ + s = S_dev_internal; + errPrintf( + s, + __FILE__, + __LINE__, + "no element count in the file %s", + pfn); + fclose(pf); + return s; + } + + pwaveform = (double *) calloc(nsamples, sizeof(double)); + if(!pwaveform){ + s = S_dev_noMemory; + errPrintf( + s, + __FILE__, + __LINE__, + "specified sample count to large %s", + pfn); + fclose(pf); + return s; + } + + for(i=0; ibuf, + sizeof(pc->buf), + &read_count, + 0); + if(s!=S_epvxi_bufferFull && s!=VXI_SUCCESS){ + errPrintf( + s, + __FILE__, + lineno, + "error fetch problem at LA=0X%X", + la); + return s; + } + nreads++; + /* + * return of zero indicates no errors + * this will always be a very short message + */ + if(nreads==1){ + int val; + int n; + + n = sscanf(pc->buf,"%d",&val); + if(n==1){ + if(val==0){ + return S_epvxi_internal; + } + } + } + errPrintf( + S_epvxi_msgDeviceStatus, + __FILE__, + lineno, + "LA=0X%X: Error => %s", + la, + pc->buf); + + if(s==VXI_SUCCESS){ + break; + } + } + + return VXI_SUCCESS; +} + + +/* + * + * hpe1445aReset() + * + * + */ +LOCAL +hpe1445aStat hpe1445aReset(unsigned la) +{ + hpe1445aStat s; + + s = hpe1445aWrite(la, "*RST"); + if(s){ + return s; + } + + s = hpe1445aWrite(la, "source:list1:ssequence:delete:all"); + if(s){ + return s; + } + + s = hpe1445aWrite(la, "source:list1:segment:delete:all"); + if(s){ + return s; + } + + return VXI_SUCCESS; +} + + +/* + * + * hpe1445aSetupFreq() + * + * + */ +hpe1445aStat hpe1445aSetupFreq(unsigned la, char *pFreqString) +{ + hpe1445aStat s; + struct hpe1445aConfig *pc; + + s = HPE1445A_PCONFIG(la, pc); + if(s){ + errMessage(s,NULL); + return s; + } + + /* + * + * Set the sample rate + * + */ + sprintf(pc->buf, "source:frequency:fixed %s", pFreqString); + s = hpe1445aWrite(la, pc->buf); + if(s){ + return s; + } + + return VXI_SUCCESS; +} + + +/* + * + * hpe1445aSetupFunction() + * + * + */ +LOCAL +hpe1445aStat hpe1445aSetupFunction(unsigned la) +{ + hpe1445aStat s; + + /* + * + * set the function to arbitrary waveform + * + */ + s = hpe1445aWrite(la, "source:function:shape user"); + if(s){ + return s; + } + + return VXI_SUCCESS; +} + + +/* + * + * hpe1445aSetupOutput() + * + * + */ +LOCAL +hpe1445aStat hpe1445aSetupOutput(unsigned la) +{ + hpe1445aStat s; + + + /* + * set the output impedance + * (50 Ohm coax assumed) + * + */ + s = hpe1445aWrite(la, "output:impedance 50 Ohm"); + if(s){ + return s; + } + + /* + * + * disable output low pass filter + * (freq would need to be set if it were turned on) + * + */ + s = hpe1445aWrite(la, "output:filter:lpass:state off"); + if(s){ + return s; + } + + return VXI_SUCCESS; +} + + +/* + * hpe1445aArm() + * + */ +hpe1445aStat hpe1445aArm(unsigned la) +{ + hpe1445aStat s; + + /* + * + * initiate waveform output off the external trigger input + * + */ +#ifdef FRONT_PANEL_TRIGGER + s = hpe1445aWrite(la, "arm:start:layer2:source external"); +#else + s = hpe1445aWrite(la, "arm:start:layer2:source ttltrg0"); +#endif + if(s){ + return s; + } + + /* + * + * initiate waveform output on the rising edge + * + */ + s = hpe1445aWrite(la, "arm:start:layer2:slope positive"); + if(s){ + return s; + } + + /* + * + * output the waveform once only after receiving + * a trigger + * + */ + s = hpe1445aWrite(la, "arm:start:layer1:count 1"); + if(s){ + return s; + } + + /* + * + * output the waveform after each trigger edge + * forever + * + */ + s = hpe1445aWrite(la, "arm:start:layer2:count infinity"); + if(s){ + return s; + } + + return VXI_SUCCESS; +} + + +/* + * + * + * hpe1445aSetupDAC + * + * + * + */ +hpe1445aStat hpe1445aSetupDAC( +unsigned la, +double dacPeakAmplitude, +double dacOffset) +{ + hpe1445aStat s; + struct hpe1445aConfig *pc; + + s = HPE1445A_PCONFIG(la,pc); + if(s){ + errMessage(s,NULL); + return s; + } + + sprintf(pc->buf, + "source:voltage:level:immediate:amplitude %f V", + dacPeakAmplitude); + s = hpe1445aWrite(la, pc->buf); + if(s){ + return s; + } + pc->dacPeakAmplitude = dacPeakAmplitude; + + sprintf(pc->buf, + "source:voltage:level:immediate:offset %f V", + dacOffset); + s = hpe1445aWrite(la, pc->buf); + if(s){ + return s; + } + pc->dacOffset = dacOffset; + + logErrors(la); + + return VXI_SUCCESS; +} + + + +/* + * + * hpe1445aActivateWaveform() + * + * + * + */ +hpe1445aStat hpe1445aActivateWaveform( +unsigned la, +char *pWaveformName +) +{ + hpe1445aStat s; + struct hpe1445aConfig *pc; + + s = HPE1445A_PCONFIG(la, pc); + if(s){ + errMessage(s,NULL); + return s; + } + + FASTLOCK(&pc->lck); + + s = hpe1445aActivateWaveformLocked(la, pWaveformName, pc); + + FASTUNLOCK(&pc->lck); + + return s; +} + + +/* + * + * hpe1445aActivateWaveformLocked() + * + * + * + */ +LOCAL hpe1445aStat +hpe1445aActivateWaveformLocked( +unsigned la, +char *pWaveformName, +struct hpe1445aConfig *pc +) +{ + int s; + + if(pc->device_active){ + s = hpe1445aWrite(la, "abort"); + if(s){ + return s; + } + pc->device_active = FALSE; + } + + /* + * + * select active sequence + * + */ + sprintf( pc->buf, + "source:function:user %s%s", + SEQUENCE_NAME_PREFIX, + pWaveformName); + s = hpe1445aWrite(la, pc->buf); + if(s){ + return s; + } + + /* + * + * initiate the trigger system + * + */ + s = hpe1445aWrite(la, "initiate:immediate"); + if(s){ + return s; + } + pc->device_active = TRUE; + + logErrors(la); + + return VXI_SUCCESS; +} + + + +/* + * + * hpe1445aUnloadWaveform() + * + * + * + */ +hpe1445aStat hpe1445aUnloadWaveform( +unsigned la, +char *pWaveformName +) +{ + hpe1445aStat s; + struct hpe1445aConfig *pc; + + s = HPE1445A_PCONFIG(la, pc); + if(s){ + errMessage(s, NULL); + return s; + } + + FASTLOCK(&pc->lck); + + s = hpe1445aUnloadWaveformLocked(la, pWaveformName); + + FASTUNLOCK(&pc->lck); + + logErrors(la); + + return s; +} + + +/* + * + * hpe1445aUnloadWaveformLocked() + * + * + * + */ +LOCAL hpe1445aStat hpe1445aUnloadWaveformLocked( +unsigned la, +char *pWaveformName +) +{ + hpe1445aStat s; + struct hpe1445aConfig *pc; + + s = HPE1445A_PCONFIG(la, pc); + if(s){ + errMessage(s, NULL); + return s; + } + + sprintf( + pc->buf, + "source:list:ssequence:select %s%s", + SEQUENCE_NAME_PREFIX, + pWaveformName); + s = hpe1445aWrite(la, pc->buf); + if(s){ + return s; + } + + s = hpe1445aWrite(la, "source:list:ssequence:delete:selected"); + if(s){ + return s; + } + + sprintf( + pc->buf, + "source:list:segment:select %s%s", + SEGMENT_NAME_PREFIX, + pWaveformName); + s = hpe1445aWrite(la, pc->buf); + if(s){ + return s; + } + + s = hpe1445aWrite(la, "source:list:segment:delete:selected"); + if(s){ + return s; + } + + return VXI_SUCCESS; +} + + + +/* + * hpe1445aLoadWaveform() + */ +hpe1445aStat +hpe1445aLoadWaveform( +unsigned la, +char *pWaveformName, +double *pdata, +unsigned long npoints +) +{ + hpe1445aStat s; + struct hpe1445aConfig *pc; + + s = HPE1445A_PCONFIG(la, pc); + if(s){ + errMessage(s, NULL); + return s; + } + + if(strlen(pWaveformName)>MAX_NAME_LENGTH){ + s = S_dev_highValue; + errMessage(s, "waveform element name to long"); + return s; + } + + if(npointslck); + + s = hpe1445aLoadWaveformLocked(la, pc, pWaveformName, pdata, npoints); + + FASTUNLOCK(&pc->lck); + + return s; +} + + +/* + * + * hpe1445aLoadWaveformLocked() + * + * + */ +LOCAL hpe1445aStat hpe1445aLoadWaveformLocked( +unsigned la, +struct hpe1445aConfig *pc, +char *pWaveformName, +double *pdata, +unsigned long npoints +) +{ + unsigned long read_count; + hpe1445aStat s; + + s = hpe1445aWrite(la, "source:list:segment:free?"); + if(s){ + return s; + } + s = epvxiRead( + la, + pc->buf, + sizeof(pc->buf), + &read_count, + 0); + if(s){ + errMessage(s, "\"source:list:segment:free?\" query failed"); + return s; + } + + { + int nfree; + int nused; + + s = sscanf(pc->buf, "%d,%d", &nfree, &nused); + if(s!=2){ + s = S_dev_internal; + errMessage(s, "bad \"source:list:segment:free?\" resp"); + return s; + } + if(nfree < npoints){ + s = S_dev_internal; + errPrintf( + s, + __FILE__, + __LINE__, + "%d waveform elements available", + nfree); + errPrintf( + s, + __FILE__, + __LINE__, + "%d element waveform rejected", + npoints); + return s; + } + } + + + /* + * + * select the segment for subsequent + * commands + * + */ + sprintf( + pc->buf, + "source:list:segment:select %s%s", + SEGMENT_NAME_PREFIX, + pWaveformName); + s = hpe1445aWrite(la, pc->buf); + if(s){ + return s; + } + + + sprintf(pc->buf, "source:list:segment:define %u", npoints); + s = hpe1445aWrite(la, pc->buf); + if(s){ + return s; + } + sprintf( pc->buf, + "source:arbitrary:download VXI,%s%s,%d", + SEGMENT_NAME_PREFIX, + pWaveformName, + npoints); + s = hpe1445aWrite(la, pc->buf); + if(s){ + return s; + } + + /* + * wait for the device to finish with the download + * command prior to the backplane download + */ + s = hpe1445aWrite(la, "*OPC?"); + if(s){ + return s; + } + s = epvxiRead( + la, + pc->buf, + sizeof(pc->buf), + &read_count, + 0); + if(s){ + errMessage(s,"\"*OPC?\" query failed"); + return s; + } + + { + double truncationOffset; + double dacPeakAmplitude; + double dacOffset; + double *pwf; + unsigned short *pdata_port; + + pdata_port = (unsigned short *) epvxiA24Base(la); + pdata_port += (HPE1445_DATA_PORT_OFFSET/sizeof(*pdata_port)); + dacPeakAmplitude = pc->dacPeakAmplitude; + dacOffset = pc->dacOffset; + truncationOffset = dacPeakAmplitude/(4095*2); + for(pwf=pdata; pwf < &pdata[npoints]; pwf++){ + short idata; + double fdata; + + /* + * extra step here preserves precision + */ + fdata = (*pwf-dacOffset)*4095; + fdata = fdata/dacPeakAmplitude; + /* + * This offset causes round up to occur and + * not truncation + */ + fdata += truncationOffset; + idata = (short) fdata; + idata = idata << 3; + + if(pwf == &pdata[npoints-4]){ +# define LAST_POINT 1 + idata |= LAST_POINT; + } + + /* + * load the waveform element into the 1445 + */ + *pdata_port = idata; + } + } + + s = hpe1445aWrite(la, "source:arbitrary:download:complete"); + if(s){ + return s; + } + + /* + * + * install this segment into the sequence + * + */ + sprintf( pc->buf, + "source:list:ssequence:select %s%s", + SEQUENCE_NAME_PREFIX, + pWaveformName); + s = hpe1445aWrite(la, pc->buf); + if(s){ + return s; + } + + /* + * set the number of segments in this sequence + */ + s = hpe1445aWrite(la, "source:list:ssequence:define 1"); + if(s){ + return s; + } + sprintf(pc->buf, + "source:list:ssequence:sequence %s%s", + SEGMENT_NAME_PREFIX, + pWaveformName); + s = hpe1445aWrite(la, pc->buf); + if(s){ + return s; + } + s = hpe1445aWrite(la, "source:list:ssequence:dwell:count 1"); + if(s){ + return s; + } + + return VXI_SUCCESS; +} + + +/* + * + * hpe1445aWriteWithLineno() + * + */ +hpe1445aStat hpe1445aWriteWithLineno( + unsigned la, + char *pmsg, + unsigned lineno +) +{ + unsigned long nactual; + hpe1445aStat s; + + s = epvxiWrite( + la, + pmsg, + strlen(pmsg), + &nactual, + 0); + if(s){ + errPrintf( + s, + __FILE__, + lineno, + "LA=0X%02X MSG=%s", + la, + pmsg); + return s; + } + + return VXI_SUCCESS; +} + + +/* + * + * hpe1445aIoReport() + * + * + */ +void hpe1445aIoReport(unsigned la, int level) +{ + hpe1445aStat s; + struct hpe1445aConfig *pc; + char *pStateName[] = {"in",""}; + + s = HPE1445A_PCONFIG(la, pc); + if(s){ + errMessage(s, NULL); + return; + } + + if(level>0){ + printf("\tdevice %sactive, DAC peak = %f V, DAC offset = %f V\n", + pStateName[(unsigned)pc->device_active], + pc->dacPeakAmplitude, + pc->dacOffset); + } +} diff --git a/src/drv/old/drvJgvtr1.c b/src/drv/old/drvJgvtr1.c new file mode 100644 index 000000000..fbe1eb589 --- /dev/null +++ b/src/drv/old/drvJgvtr1.c @@ -0,0 +1,641 @@ +/* drvJgvtr1.c */ +/* base/src/drv $Id$ */ +/* + * Author: Jeff Hill + * Date: 5-89 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * 110689 joh print mem not full message only once + * 120789 joh temporary removal of memory full check + * 050190 joh clear ptr to callback prior to calling + * it so they can rearm from inside the callback + * 071190 joh check STD address after cycle complete detected + * to avoid erroneous card misaddressed messages + * 071190 joh internal sample rate status is bit reversed on the + * card- I added a lookup table to untwist it. + * 020491 ges Change taskDelay from 20 to 2 in "jgvtr1DoneTask". + * To allow rearm and data reads from succesive + * waveform scans up thru 10Hz rates. + * 031491 lrd move data into a local memory area for each card + * 090591 joh converted to V5 vxWorks + * 110591 lrd initialization of cards other than 0 not + * allocating data buffer correctly + * 013092 bg added sysBusToLocalAdrs. Added levels to io_report + * and the ability to read out the Joerger's raw values + * in io_report if level is > 1. + * 031992 joh Took the vxMemProbe out of each arm and checked + * the card present bit instead. + * 062592 bg Combined drvJgvtr1.c and jgvtr_driver.c + * 062992 joh removed file pointer argument added to io + * report by bg + * 082792 joh added ANSI C function prototypes + * 080293 mrk added call to taskwdInsert + * 080493 mgb Removed V5/V4 and EPICS_V2 conditionals + */ + +static char *sccsID = "@(#)drvJgvtr1.c 1.17\t9/9/93"; + +/* + * Code Portions + * + * jgvtr1_init() + * jgvtr1_driver(card, pcbroutine, parg) + * jgvtr1_int_service() + * jgvtr1DoneTask() + * jgvtr1_io_report() + * jgvtr1_stat(card) + * + */ + +/* drvJgvtr1.c - Driver Support Routines for Jgvtr1 */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +LOCAL jgvtr1Stat jgvtr1_io_report( + unsigned level +); + +LOCAL jgvtr1Stat jgvtr1_init( + void +); + +#ifdef INTERRUPT_HARDWARE_FIXED +LOCAL void jgvtr1_int_service( + void +); +#endif + +LOCAL void jgvtr1DoneTask( + void +); + +LOCAL jgvtr1Stat jgvtr1_dump( + unsigned card, + unsigned n +); + +LOCAL jgvtr1Stat jgvtr1_stat( + unsigned card, + int level +); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvJgvtr1={ + 2, + jgvtr1_io_report, + jgvtr1_init}; + +static volatile char *stdaddr; +static volatile char *shortaddr; + + +#define JGVTR1MAXFREQ 25.0e6 +/* NBBY - the number of bits per byte */ +#define JGVTR1SHORTSIZE (1<<(NBBY*sizeof(uint8_t))) +#define JGVTR1STDSIZE (1<<(NBBY*sizeof(uint16_t))) +#define JGVTR1_INT_LEVEL 5 +#define JGVTR1BASE(CARD)\ +(shortaddr+wf_addrs[JGVTR1]+(CARD)*JGVTR1SHORTSIZE) +#define JGVTR1DATA(CARD)\ +(stdaddr+wf_memaddrs[JGVTR1]+(CARD)*JGVTR1STDSIZE) + + +/* +Joerger fixed hardware bug by switching to an inverting tristate buffer +where these commands are read from the VME bus. As a result these commands +are complemented. +*/ +#define JGVTR1ARM (~1) +#define JGVTR1START (~2) +#define JGVTR1STOP (~4) + + + +/* +!! our compiler allocates bit fields starting from the ms bit !! +*/ +struct jgvtr1_status{ + volatile unsigned pad:8; + volatile unsigned internal_frequency:3; + volatile unsigned internal_clock:1; + volatile unsigned cycle_complete:1; + volatile unsigned interrupt:1; + volatile unsigned active:1; + volatile unsigned memory_full:1; +}; + +struct jgvtr1_config{ + char present; /* card present */ + char std_ok; /* std addr ok on first read */ + void (*psub) /* call back routine */ + (void *pprm, unsigned nbytes, uint16_t *pData); + void *pprm; /* call back parameter */ + FAST_LOCK lock; /* mutual exclusion */ + uint16_t *pdata; /* pointer to the data buffer */ +}; + +/* amount of data to make available from the waveform */ +#define JRG_MEM_SIZE 2048 + +LOCAL +struct jgvtr1_config *pjgvtr1_config; + +LOCAL +int jgvtr1_max_card_count; + +#ifdef INTERRUPT_HARDWARE_FIXED +LOCAL +SEM_ID jgvtr1_interrupt; /* interrupt event */ +#endif + + + +/* + * JGVTR1_INIT + * + * intialize the driver for the joerger vtr1 + * + */ +jgvtr1Stat jgvtr1_init(void) +{ + unsigned card; + unsigned card_count = 0; + struct jgvtr1_config *pconfig; + uint16_t readback; + jgvtr1Stat status; + + + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + 0, + (char **)&shortaddr); + if (status != OK){ + status = S_dev_badA16; + errMessage(status,NULL); + return status; + } + + status = sysBusToLocalAdrs( + VME_AM_STD_SUP_DATA, + 0, + (char **)&stdaddr); + if (status != OK){ + status = S_dev_badA24; + errMessage(status,NULL); + return status; + } + + jgvtr1_max_card_count = wf_num_cards[JGVTR1]; + + if(pjgvtr1_config){ + if(FASTLOCKFREE(&pjgvtr1_config->lock)<0) + return ERROR; + free(pjgvtr1_config); + } + + pjgvtr1_config = + (struct jgvtr1_config *) + calloc(wf_num_cards[JGVTR1], sizeof(*pjgvtr1_config)); + if(!pjgvtr1_config){ + status = S_dev_noMemory; + errMessage(status,NULL); + return status; + } + + for( card=0, pconfig=pjgvtr1_config; + card < wf_num_cards[JGVTR1]; + pconfig++, card++){ + + FASTLOCKINIT(&pconfig->lock); + + status = vxMemProbe( (char *)JGVTR1BASE(card), + READ, + sizeof(readback), + (char *)&readback); + if(status==ERROR) + continue; + + + pconfig->pdata = + (uint16_t *)malloc(JRG_MEM_SIZE); + /* + not easy to test for correct addressing in + standard address space since the module does + not respond if it has not clocked in data + + - so I check this the first time data is ready + */ + pconfig->std_ok = FALSE; /* presumed guilty before tested */ + pconfig->present = TRUE; + card_count++; + } + + if(!card_count) + return OK; + + +# ifdef INTERRUPT_HARDWARE_FIXED + jgvtr1_interrupt = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + if(!jgvtr1_interrupt) + return ERROR; +# endif + + /* start the waveform readback task */ + status = taskSpawn( WFDONE_NAME, + WFDONE_PRI, + WFDONE_OPT, + WFDONE_STACK, + (FUNCPTR) jgvtr1DoneTask, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + if(status < 0){ + status = S_dev_internal; + errMessage(status, "vxWorks taskSpawn failed"); + return status; + } + + taskwdInsert(status, NULL, NULL); + + +# ifdef INTERRUPT_HARDWARE_FIXED + status = intConnect( INUM_TO_IVEC(JGVTR1_INT_VEC), + jgvtr1_int_service, + NULL); + if(status != OK) + return S_dev_internal; + sysIntEnable(JGVTR_INT_LEVEL); +# endif + + return JGVTR1_SUCCESS; +} + + + +/* + * JGVTR1_DRIVER + * + * initiate waveform read + * + */ +jgvtr1Stat jgvtr1_driver( +unsigned card, +void (*pcbroutine)(void *, unsigned, uint16_t *), +void *parg +) +{ + if(card >= jgvtr1_max_card_count) + return S_dev_badSignalNumber; + + if(!pjgvtr1_config[card].present) + return S_dev_noDevice; + + if(pjgvtr1_config[card].psub) + return S_dev_badRequest; + + FASTLOCK(&pjgvtr1_config[card].lock); + + *(volatile uint16_t *)JGVTR1BASE(card) = JGVTR1ARM; + + pjgvtr1_config[card].pprm = parg; + pjgvtr1_config[card].psub = pcbroutine; + + FASTUNLOCK(&pjgvtr1_config[card].lock); + + return JGVTR1_SUCCESS; +} + + +/* + * JGVTR1_INT_SERVICE + * + * signal via the RTK that an interrupt occured from the joerger vtr1 + * + */ +#ifdef INTERRUPT_HARDWARE_FIXED +LOCAL void jgvtr1_int_service(void) +{ + semGive(jgvtr1_interrupt); +} +#endif + + +/* + * JGVTR1DONETASK + * + * wait for joerger vtr1 waveform record cycle complete + * and call back to the database with the waveform size and address + * + */ +LOCAL void jgvtr1DoneTask(void) +{ + unsigned card; + struct jgvtr1_config *pconfig; + struct jgvtr1_status stat; + static char started = FALSE; + volatile uint16_t *pdata; + volatile uint16_t *pjgdata; + long i; + + /* dont allow two of this task */ + if(started) + exit(0); + started = TRUE; + + while(TRUE){ + +# ifdef INTERRUPT_HARDWARE_FIXED + semTake(jgvtr1_interrupt, WAIT_FOREVER); +# else + /* ges: changed from 20 ticks to 2 ticks 2/4/91 */ + taskDelay(2); +# endif + + for( card=0, pconfig = pjgvtr1_config; + card < jgvtr1_max_card_count; + card++, pconfig++){ + + if(!pconfig->present) + continue; + + if(!pconfig->psub) + continue; + + stat = *(struct jgvtr1_status *) JGVTR1BASE(card); + /* + * Wait for the module to finish filling its memory + * or a stop trigger + */ + if(!stat.cycle_complete) + continue; + + /* + clear ptr to function here so they + can rearm in the callback + */ + pconfig->psub = NULL; + /* + check the first time for module + correctly addressed + + card does not respond at STD address + until it has data + */ + if(!pconfig->std_ok){ + uint16_t readback; + int status; + + status = vxMemProbe( + (char *)JGVTR1DATA(card), + READ, + sizeof(readback), + (char *)&readback); + if(status==ERROR){ + errPrintf( + S_dev_badA24, + __FILE__, + __LINE__, + "jgvtr1 card %d incorrectly addressed- use std addr 0X%X", + card, + JGVTR1DATA(card)); + pconfig->present = FALSE; + continue; + } + pconfig->std_ok = TRUE; + } +/* + Test for full memory + ( card designer does not give a sample count register ) + ( instead a bus error returned when on the last sample + to test every location is a lot of overhead so a test + for memory full is used for now ) +*/ + if(!stat.memory_full){ + errMessage(S_dev_internal, + "jgvtr1 driver: proceeding with partial mem"); + errMessage(S_dev_internal, + "jgvtr1 driver: beware of bus errors"); + } + + /* copy the data into a local memory buffer */ + /* this is to avoid any bus errors */ + for(i = 0, + pdata = pconfig->pdata, + pjgdata = (volatile uint16_t *)JGVTR1DATA(card); + i < JRG_MEM_SIZE/sizeof(uint16_t); + i++, pdata++, pjgdata++){ + *pdata = *pjgdata; + } + +/* + Post waveform to the database + perhaps the size must be the size below+1 ? + (Joerger's documentation is not clear here) +*/ + (*pconfig->psub)(pconfig->pprm,JRG_MEM_SIZE,pconfig->pdata); + } + } +} + + + +/* + * + * JGVTR1_IO_REPORT + * + * print status for all cards in the specified joerger + * vtr1 address range + * + * + */ +LOCAL jgvtr1Stat jgvtr1_io_report(unsigned level) +{ + unsigned card; + unsigned nelements; + jgvtr1Stat status; + + for(card=0; card < wf_num_cards[JGVTR1]; card++){ + status = jgvtr1_stat(card,level); + if(status){ + continue; + } + if (level >= 2){ + printf("enter the number of elements to dump:"); + status = scanf("%d",&nelements); + if(status == 1){ + jgvtr1_dump(card, nelements); + } + } + } + return JGVTR1_SUCCESS; +} + + + +/* + * + * JGVTR1_STAT + * + * print status for a single card in the joerger vtr1 address range + * + * + */ +jgvtr1Stat jgvtr1_stat( +unsigned card, +int level +) +{ + struct jgvtr1_status stat; + jgvtr1Stat status; + + /* + internal freq status is bit reversed so I + use a lookup table + */ + + static float sample_rate[8] = { + JGVTR1MAXFREQ/(1<<7), + JGVTR1MAXFREQ/(1<<3), + JGVTR1MAXFREQ/(1<<5), + JGVTR1MAXFREQ/(1<<1), + JGVTR1MAXFREQ/(1<<6), + JGVTR1MAXFREQ/(1<<2), + JGVTR1MAXFREQ/(1<<4), + JGVTR1MAXFREQ/(1<<0)}; + + static char *clock_status[] = + {"ext-clock", "internal-clk"}; + static char *cycle_status[] = + {"cycling", "done"}; + static char *interrupt_status[] = + {"", "int-pending"}; + static char *activity_status[] = + {"", "active"}; + static char *memory_status[] = + {"", "mem-full"}; + + status = vxMemProbe( (char *)JGVTR1BASE(card), + READ, + sizeof(stat), + (char *)&stat); + if(status != OK) + return ERROR; + if (level == 0) + printf("WF: JGVTR1:\tcard=%d \n",card); + else if (level > 0) + printf( "WF: JGVTR1:\tcard=%d Sample rate=%g %s %s %s %s %s \n", + card, + sample_rate[stat.internal_frequency], + clock_status[ stat.internal_clock ], + cycle_status[ stat.cycle_complete ], + interrupt_status[ stat.interrupt ], + activity_status[ stat.active ], + memory_status[ stat.memory_full ]); + + return JGVTR1_SUCCESS; +} + + +/* + * jgvtr1_dump + * + */ +LOCAL jgvtr1Stat jgvtr1_dump( +unsigned card, +unsigned n +) +{ + volatile uint16_t *pjgdata; + uint16_t *pread; + uint16_t *pdata; + unsigned nread; + jgvtr1Stat status; + + /* Print out the data if user requests it. */ + + n = min(JRG_MEM_SIZE,n); + + pdata = (uint16_t *)malloc(n * (sizeof(*pdata))); + if(!pdata){ + return S_dev_noMemory; + } + + pread = pdata; + nread = 0; + pjgdata = (volatile uint16_t *)JGVTR1DATA(card); + while(nread <= (n>>1)){ + status = vxMemProbe( + (char *)pjgdata, + READ, + sizeof(*pread), + (char *)pread); + if(status<0){ + break; + } + nread++; + pread++; + pjgdata++; + } + + for(pread=pdata; pread<&pdata[nread]; pread++){ + if ((pread-pdata)%8 == 0){ + printf("\n\t"); + } + printf( "%02X %02X ", + (unsigned char) ((*pread)>>8), + (unsigned char) *pread); + } + + printf("\n"); + + free(pdata); + + return JGVTR1_SUCCESS; +} diff --git a/src/drv/old/drvJgvtr1.h b/src/drv/old/drvJgvtr1.h new file mode 100644 index 000000000..f4c3f6454 --- /dev/null +++ b/src/drv/old/drvJgvtr1.h @@ -0,0 +1,43 @@ +/* drvJgvtr1.h */ +/* base/src/drv $Id$ */ +/* + * Author: Jeff Hill + * Date: 5-89 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + */ + + + +typedef long jgvtr1Stat; + +#define JGVTR1_SUCCESS 0 + +jgvtr1Stat jgvtr1_driver( +unsigned card, +void (*pcbroutine)(void *, unsigned, uint16_t *), +void *parg +); + diff --git a/src/drv/old/drvKscV215.c b/src/drv/old/drvKscV215.c new file mode 100644 index 000000000..35a245a8a --- /dev/null +++ b/src/drv/old/drvKscV215.c @@ -0,0 +1,484 @@ +/* drvKscV215.c*/ +/* base/src/drv $Id$ */ +/* + * KscV215_driver.c + * + * driver for KscV215 VXI module + * + * Author: Jeff Hill + * Date: 052192 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 071792 joh Added model name registration + * .02 081992 joh vxiUniqueDriverID -> epvxiUniqueDriverID + * .03 082692 mrk Added support for new I/O event scanning and DRVET + * .04 012893 joh include file name change + * .05 080493 mgb Removed V5/V4 and EPICS_V2 conditionals + * + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + +#define MAXTRIES 100 + +#define KSCV215_PCONFIG(LA, PC) \ +epvxiFetchPConfig((LA), KscV215DriverId, PC) + +#define ChannelEnable(PCSR) ((PCSR)->dir.w.dd.reg.ddx08) +#define ModuleStatus(PCSR) ((PCSR)->dir.r.status) + +#define ALL_SWITCHES_OPEN 0 + +struct KscV215_config{ + FAST_LOCK lock; /* mutual exclusion */ + IOSCANPVT ioscanpvt; +}; + +#define KSCV215_INT_LEVEL 1 +#define KscV215Handshake (0x0040) +#define KscV215csrInit (0x9000) + +LOCAL int KscV215DriverId; + + +struct KscV215_A24{ + unsigned short diag; + unsigned short isr; + unsigned short pad1[7]; + unsigned short channels[64]; /* odd access causes a bus error ? */ + unsigned short controlMemoryAddr; + unsigned short pad2; + unsigned short controlMemoryDataWrite; + unsigned short pad3; + unsigned short controlMemoryDataRead; + unsigned short pad4; + unsigned short lastChannel; + unsigned short pad5; + unsigned short singleScan; + unsigned short pad6; + unsigned short stopScan; + unsigned short pad7; + unsigned short clearControlMemoryAddr; + unsigned short pad8; + unsigned short enableContinuousScanning; + unsigned short pad9; + unsigned short disableContinuousScanning; + unsigned short pad10; + unsigned short enableDoneInt; + unsigned short pad11; + unsigned short disbaleDoneInt; + unsigned short pad12; + unsigned short clearDoneInt; + unsigned short pad13; + unsigned short testDoneInt; + unsigned short pad14; +}; + +#ifdef INTERRUPTS +LOCAL void KscV215_int_service(unsigned la); +#endif +LOCAL void KscV215_init_card(unsigned la); + +LOCAL void KscV215_stat( +unsigned la, +int level +); + +LOCAL kscV215Stat KscV215WriteSync( +struct KscV215_A24 *pA24, +unsigned short *preg, +unsigned val +); + + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvKscV215={ + 2, + NULL, /* VXI report takes care of this */ + KscV215Init}; + +/* + * KscV215_init + * + * initialize all KscV215 cards + * + */ +kscV215Stat KscV215Init(void) +{ + kscV215Stat r0; + + /* + * do nothing on crates without VXI + */ + if(!epvxiResourceMangerOK){ + return VXI_SUCCESS; + } + + KscV215DriverId = epvxiUniqueDriverID(); + + { + epvxiDeviceSearchPattern dsp; + + dsp.flags = VXI_DSP_make | VXI_DSP_model; + dsp.make = VXI_MAKE_KSC; + dsp.model = VXI_MODEL_KSCV215; + r0 = epvxiLookupLA(&dsp, KscV215_init_card, (void *)NULL); + if(r0){ + return r0; + } + } + + return VXI_SUCCESS; +} + + + +/* + * KSCV215_INIT_CARD + * + * initialize single at5vxi card + * + */ +LOCAL void KscV215_init_card(unsigned la) +{ + kscV215Stat status; + int i; + struct KscV215_config *pc; + struct KscV215_A24 *pA24; + struct vxi_csr *pcsr; + int model; + + status = epvxiOpen( + la, + KscV215DriverId, + (unsigned long) sizeof(*pc), + KscV215_stat); + if(status){ + errPrintf( + status, + __FILE__, + __LINE__, + "AT5VXI: device open failed %d\n", + la); + + return; + } + + status = KSCV215_PCONFIG(la, pc); + if(status){ + errMessage(status,NULL); + epvxiClose(la, KscV215DriverId); + return; + } + + pA24 = epvxiA24Base(la); + pcsr = VXIBASE(la); + + pcsr->dir.w.control = KscV215csrInit; + + status = KscV215WriteSync(pA24, &pA24->controlMemoryAddr, 0); + if(status){ + epvxiClose(la, KscV215DriverId); + errMessage(status, "KscV215 init failed\n"); + return; + } + for(i=0; i<(NELEMENTS(pA24->channels)/2); i++){ + status = KscV215WriteSync( + pA24, + &pA24->controlMemoryDataWrite, + 0); + if(status){ + epvxiClose(la, KscV215DriverId); + errMessage(status, "KscV215 init failed\n"); + return; + } + } + + /* + * turn on continuous scan mode + */ + status = KscV215WriteSync( + pA24, + &pA24->enableContinuousScanning, + 0); + if(status){ + epvxiClose(la, KscV215DriverId); + errMessage(status, "KscV215 init failed- device left open\n"); + return; + } + + FASTLOCKINIT(&pc->lock); + scanIoInit(&pc->ioscanpvt); + +#ifdef INTERRUPTS + status = intConnect( + (unsigned char) INUM_TO_IVEC(la), + KscV215_int_service, + (void *) la); + if(status == ERROR){ + epvxiClose(la, KscV215DriverId); + errMessage(S_dev_vxWorksVecInstlFail, + "KscV215 init failed- device left open"); + return; + } + sysIntEnable(KSCV215_INT_LEVEL); +#endif + + status = epvxiRegisterMakeName(VXI_MAKE_KSC, "Kinetic Systems"); + if(status){ + errMessage(status, NULL); + } + + model = VXIMODEL(pcsr); + status = epvxiRegisterModelName( + VXIMAKE(pcsr), + model, + "V215 16 bit 32 channel ADC\n"); + if(status){ + errMessage(status, NULL); + } + +} + + +/* + * + * KscV215WriteSync + * + * + */ +LOCAL kscV215Stat KscV215WriteSync( +struct KscV215_A24 *pA24, +unsigned short *preg, +unsigned val +) +{ + kscV215Stat status; + int i; + + for(i=0; idiag & KscV215Handshake){ + return VXI_SUCCESS; + } + taskDelay(1); + } + + status = S_dev_deviceTMO; + errMessage(status, NULL); + return status; +} + + +/* + * + * KscV215_int_service() + * + * + * This device interrupts once the + * switches have settled + * + */ +#ifdef INTERRUPTS +LOCAL void KscV215_int_service(unsigned la) +{ + kscV215Stat s; + struct KscV215_config *pc; + + s = KSCV215_PCONFIG(la, pc); + if(s){ + logMsg( "Int to ukn device %s line=%d\n", + __FILE__, + __LINE__, + NULL, + NULL, + NULL, + NULL); + return; + } + + /* + * tell them that the switches have settled + */ + scanIoRequest(pc->ioscanpvt); +} +#endif + + +/* + * KSCV215_STAT + * + * initialize single at5vxi card + * + */ +LOCAL void KscV215_stat( +unsigned la, +int level +) +{ + kscV215Stat s; + struct KscV215_config *pc; + struct vxi_csr *pcsr; + struct KscV215_A24 *pA24; + int i; + + s = KSCV215_PCONFIG(la, pc); + if(s){ + errMessage(s, NULL); + return; + } + pcsr = VXIBASE(la); + + pA24 = (struct KscV215_A24 *) epvxiA24Base(la); + + if(level>0){ + printf ("KSC V215 32 CHANNEL 16 BIT ADC.\n"); + } + if (level > 1) { + for (i = 0; i < 32; i++) + printf ("Channel %d Value %d\n", + i, + pA24->channels[i*2]); + } + if (level > 2) { + printf ("\nGain Setting (Control Memory Data Register\n"); + pA24->controlMemoryAddr = 0; + for (i = 0; i < 32; i++) { + switch (pA24->controlMemoryAddr) { + case 0: + printf ("+- 10V"); + break; + case 1: + printf ("+- 5V"); + break; + case 3: + printf ("+- 2.5V"); + break; + case 5: + printf ("+- 1.25V"); + break; + case 6: + printf ("+- 625mV"); + break; + default: + printf ("Unknown Gain Setting."); + } + } + printf ("\n"); + } + + + +} + + + +/* + * + * + * AT5VXI_AI_DRIVER + * + * analog input driver + */ +kscV215Stat KscV215_ai_driver( +unsigned la, +unsigned chan, +unsigned short *prval +) +{ + struct KscV215_config *pc; + struct vxi_csr *pcsr; + struct KscV215_A24 *pA24; + long tmp; + int i; + kscV215Stat s; + + s = KSCV215_PCONFIG(la, pc); + if(s){ + return s; + } + pcsr = VXIBASE(la); + + pA24 = epvxiA24Base(la); + + if(chan >= NELEMENTS(pA24->channels)/2){ + return S_dev_badSignalNumber; + } + + for(i=0; ichannels[chan<<1]; + if(pA24->diag & KscV215Handshake){ + tmp = tmp + 0xffff; + tmp = tmp >> 4; + tmp &= 0xfff; + *prval = tmp; + return VXI_SUCCESS; + } + taskDelay(1); + } + + s = S_dev_deviceTMO; + return s; +} + + +/* + * KscV215_getioscanpvt() + */ +kscV215Stat KscV215_getioscanpvt( +unsigned la, +IOSCANPVT *scanpvt +) +{ + kscV215Stat s; + struct KscV215_config *pc; + + s = KSCV215_PCONFIG(la, pc); + if(s){ + errMessage(s, NULL); + return s; + } + *scanpvt = pc->ioscanpvt; + return VXI_SUCCESS; +} diff --git a/src/drv/old/drvKscV215.h b/src/drv/old/drvKscV215.h new file mode 100644 index 000000000..34eaf2c27 --- /dev/null +++ b/src/drv/old/drvKscV215.h @@ -0,0 +1,56 @@ +/* drvKscV215.c*/ +/* base/src/drv $Id$ */ +/* + * KscV215_driver.c + * + * driver for KscV215 VXI module + * + * Author: Jeff Hill + * Date: 052192 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * + */ + +#define VXI_MODEL_KSCV215 (0x215) + +typedef long kscV215Stat; + +kscV215Stat KscV215Init(void); + +kscV215Stat KscV215_ai_driver( +unsigned la, +unsigned chan, +unsigned short *prval +); + +kscV215Stat KscV215_getioscanpvt( +unsigned la, +IOSCANPVT *scanpvt +); + + + + diff --git a/src/drv/old/drvMsg.c b/src/drv/old/drvMsg.c new file mode 100644 index 000000000..cfa92d202 --- /dev/null +++ b/src/drv/old/drvMsg.c @@ -0,0 +1,1437 @@ +/* base/src/drv $Id$ */ +/* + * Author: John Winans + * Date: 04-14-92 + * EPICS Generic message based I/O driver + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 04-14-92 jrw created + * .02 05-26-92 jrw changed enumeration of the record types + * .03 08-02-93 mrk Added call to taskwdInsert + * + */ + +#include +#include +#if 0 /* COMMENTED OUT SOME INCLUDES */ +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* COMMENTED OUT SOME INCLUDES */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int msgDebug = 0; + +#ifndef INVALID_ALARM +#define INVALID_ALARM VALID_ALARM +#endif + +static long drvMsg_write(), drvMsg_AiFmt(); +static long drvMsg_AoFmt(); +long drvMsg_proc(); +static long drvMsg_BiFmt(), drvMsg_BoFmt(), drvMsg_MiFmt(), drvMsg_MoFmt(); +static long drvMsg_LiFmt(), drvMsg_LoFmt(), drvMsg_SiFmt(), drvMsg_SoFmt(); +static long drvMsg_SiRaw(), drvMsg_SoRaw(); +static long drvMsg_CheckAck(); + +static void drvMsg_callbackFunc(); +static int msgTask(); + +static long (*(msgSupFun[]))() = { + NULL, /* MSG_OP_NOP */ + drvMsg_write, /* MSG_OP_WRITE */ + drvMsg_AiFmt, /* MSG_OP_FAI */ + drvMsg_AoFmt, /* MSG_OP_FAO */ + drvMsg_BiFmt, /* MSG_OP_FBI */ + drvMsg_BoFmt, /* MSG_OP_FBO */ + drvMsg_MiFmt, /* MSG_OP_FMI */ + drvMsg_MoFmt, /* MSG_OP_FMO */ + drvMsg_LiFmt, /* MSG_OP_FLI */ + drvMsg_LoFmt, /* MSG_OP_FLO */ + drvMsg_SiFmt, /* MSG_OP_FSI */ + drvMsg_SoFmt, /* MSG_OP_FSO */ + drvMsg_SiRaw, /* MSG_OP_RSI */ + drvMsg_SoRaw, /* MSG_OP_RSO */ + drvMsg_CheckAck /* MSG_OP_ACK */ +}; +#define NUM_VALID_OPS sizeof(msgSupFun)/sizeof(msgSupFun[0]) + +/****************************************************************************** + * + * These are the msgRecEnum structures that are used to define the + * types of records supported by the message based driver system. + * + * This list may be extended by the application developer in the device support + * module if necessary. + * + ******************************************************************************/ +msgRecEnum drvMsgAi = { "Analog In" }; +msgRecEnum drvMsgAo = { "Analog Out" }; +msgRecEnum drvMsgBi = { "Binary In" }; +msgRecEnum drvMsgBo = { "Binary Out" }; +msgRecEnum drvMsgMi = { "Multibit In" }; +msgRecEnum drvMsgMo = { "Multibit Out" }; +msgRecEnum drvMsgLi = { "Long In" }; +msgRecEnum drvMsgLo = { "Long Out" }; +msgRecEnum drvMsgSi = { "String In" }; +msgRecEnum drvMsgSo = { "String Out" }; +msgRecEnum drvMsgWf = { "Waveform" }; + +/****************************************************************************** + * + * Driver entry table for the message based I/O driver + * + ******************************************************************************/ +struct drvet drvMsg = { 2, drvMsg_reportMsg, drvMsg_initMsg }; + +/****************************************************************************** + * + ******************************************************************************/ +long +drvMsg_reportMsg(pdset) +msgDset *pdset; +{ + printf("Message driver report\n"); + + if (pdset->pparmBlock->pdrvBlock->drvIoctl != NULL) + return((*(pdset->pparmBlock->pdrvBlock->drvIoctl))(MSGIOCTL_REPORT, NULL)); + + return(OK); +} + + +/****************************************************************************** + * + ******************************************************************************/ +long +drvMsg_initMsg(parm, pdset) +int parm; +msgDset *pdset; +{ + + msgDrvIniParm initParms; + + initParms.parm = parm; + initParms.pdset = pdset; + + if (msgDebug) + printf("Message init routine entered %d, 0x%08.8X\n", parm, pdset); + + if(pdset->pparmBlock->pdrvBlock->drvIoctl != NULL) + return((*(pdset->pparmBlock->pdrvBlock->drvIoctl))(MSGIOCTL_INIT, &initParms)); + + return(OK); +} + +/****************************************************************************** + * + ******************************************************************************/ +drvMsg_xactListAddHead(plist, pnode) +xactQueue *plist; +msgXact *pnode; +{ + pnode->prev = NULL; + pnode->next = plist->head; + + if (plist->head != NULL) + plist->head->prev = pnode; + + if (plist->tail == NULL) + plist->tail = pnode; + + plist->head = pnode; + + return(0); +} +/****************************************************************************** + * + ******************************************************************************/ +drvMsg_xactListAddTail(plist, pnode) +xactQueue *plist; +msgXact *pnode; +{ + pnode->next = NULL; /* No next node if this is the TAIL */ + pnode->prev = plist->tail; /* previous node is the 'old' TAIL node */ + + if (plist->tail != NULL) + plist->tail->next = pnode; /* link the 'old' tail to the 'new' tail node */ + + if (plist->head == NULL) + plist->head = pnode; + + plist->tail = pnode; /* this is the 'new' tail node */ + + return(0); +} +/****************************************************************************** + * + ******************************************************************************/ +drvMsg_xactListDel(plist, pnode) +xactQueue *plist; +msgXact *pnode; +{ + if (pnode->next != NULL) + pnode->next->prev = pnode->prev; + + if (pnode->prev != NULL) + pnode->prev->next = pnode->next; + + if (plist->head == pnode) + plist->head = pnode->next; + + if (plist->tail == pnode) + plist->tail = pnode->prev; + + return(0); +} + +/****************************************************************************** + * + * Generate a transaction structure and initialize it. + * + ******************************************************************************/ +msgXact * +drvMsg_genXact(pparmBlock, plink, prec) +msgParmBlock *pparmBlock; +struct link *plink; /* I/O link structure from record */ +struct dbCommon *prec; +{ + msgXact *pmsgXact; + msgDrvGenXParm genXactParm; + char message[200]; + + /* allocate and fill in msg specific part */ + if ((pmsgXact = malloc(sizeof (msgXact))) == NULL) + { + sprintf(message, "drvMsg_genXact:%s out of memory\n", prec->name); + errMessage(S_db_badField, message); + return(NULL); + } + pmsgXact->pparmBlock = pparmBlock; + pmsgXact->prec = prec; + + genXactParm.plink = plink; + genXactParm.pmsgXact = pmsgXact; + + /* fill in communication-link specific portion and phwpvt */ + if ((*(pparmBlock->pdrvBlock->drvIoctl))(MSGIOCTL_GENXACT, &genXactParm) == ERROR) + { + /* free-up the xact structure and clean up */ + + /* errMessage() */ + printf("(Message driver): An error occurred while initializing %s\n", prec->name); + return(NULL); + } + /* Verify that the parm number is within range */ + if (pmsgXact->parm >= pparmBlock->numCmds) + { + sprintf(message, "(Message driver) %s parm number %d invalid\n", prec->name, pmsgXact->parm); + errMessage(S_db_badField, message); + return(NULL); + } + + /* Make a simple check to see if the parm entry makes sense */ + if ((pparmBlock->pcmds[pmsgXact->parm].flags & READ_DEFER) && (pparmBlock->pcmds[pmsgXact->parm].readOp.p == NULL)) + { + sprintf(message, "(Message driver) %s parm number %d specifies a deferred read, but no read operation\n", prec->name, pmsgXact->parm); + errMessage(S_db_badField, message); + return(NULL); + } + return(pmsgXact); +} + +/****************************************************************************** + * + * Generate a hardware private structure and initialize it. + * This is called by pparmBlock->pdrvBlock->drvIoctl(MSGIOCTL_GENXACT) when it + * finds that a hardware private structure is not present when a transaction + * structure is being initialized for it. + * + ******************************************************************************/ +msgHwpvt * +drvMsg_genHwpvt(pparmBlock, plink) +msgParmBlock *pparmBlock; +struct link *plink; /* I/O link structure from record */ +{ + msgHwpvt *pmsgHwpvt; + msgDrvGenHParm genHParms; + + if (msgDebug) + printf("In drvMsg_genHwpvt\n"); + + /* allocate and fill in msg specific part */ + if((pmsgHwpvt = malloc(sizeof(msgHwpvt))) == NULL) + return(NULL); + + /* Link it into the msgParmBlock list */ + pmsgHwpvt->next = pparmBlock->pmsgHwpvt; + pparmBlock->pmsgHwpvt = pmsgHwpvt; + + pmsgHwpvt->tmoVal = 0; + pmsgHwpvt->tmoCount = 0; + + genHParms.pparmBlock = pparmBlock; + genHParms.plink = plink; + genHParms.pmsgHwpvt = pmsgHwpvt; + + if ((*(pparmBlock->pdrvBlock->drvIoctl))(MSGIOCTL_GENHWPVT, &genHParms) == ERROR) + { + /* Free up the hardware private structure and clean up */ + /* It is still first on the list, so don't have to look for it */ + return(NULL); + } + + return(pmsgHwpvt); +} + +/****************************************************************************** + * + * Generate a message queue link structure and start a task to manage it. + * This is called by pparmBlock->pdrvBlock->drvIoctl(MSGIOCTL_GENHWPVT) when + * it finds that a specific link structure is not present that is needed when + * initializing a hardware private structure. + * + ******************************************************************************/ +msgLink * +drvMsg_genLink(pparmBlock, plink) +msgParmBlock *pparmBlock; +struct link *plink; /* I/O link structure from record */ +{ + msgDrvGenLParm genlParms; + msgLink *pmsgLink; + char name[20]; + long status; + int j; + int taskId; + + if (msgDebug) + printf("In drvMsg_genLink\n"); + + /* Allocate and fill in the msg specific part */ + if ((pmsgLink = malloc(sizeof(msgLink))) == NULL) + return(NULL); + + /* init all the prioritized transaction queues */ + for(j=0; jqueue[j].head = NULL; + pmsgLink->queue[j].tail = NULL; + FASTLOCKINIT(&(pmsgLink->queue[j].lock)); + } + pmsgLink->linkEventSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + + genlParms.pmsgLink = pmsgLink; + genlParms.plink = plink; + genlParms.op = MSG_GENLINK_CREATE; + genlParms.pparmBlock = pparmBlock; + + /* do the driver-specific init */ + if ((*(pparmBlock->pdrvBlock->drvIoctl))(MSGIOCTL_GENLINK, &genlParms) == ERROR) + { + /* free the pmsgLink structure and clean up */ + return(NULL); + } + sprintf(name, "%s", pparmBlock->pdrvBlock->taskName); + if ((taskId = taskSpawn(name, pparmBlock->pdrvBlock->taskPri, pparmBlock->pdrvBlock->taskOpt, pparmBlock->pdrvBlock->taskStack, msgTask, pparmBlock->pdrvBlock, pmsgLink)) == ERROR) + { + printf("Message driver: Failed to start link task %s\n", name); +/* BUG --delete the FASTLOCK in here */ + status = ERROR; + } else { + taskwdInsert(taskId,NULL,NULL); + status = OK; + } + + if (status == ERROR) + { + genlParms.op = MSG_GENLINK_ABORT; + + (*(pparmBlock->pdrvBlock->drvIoctl))(MSGIOCTL_GENLINK, &genlParms); + free(pmsgLink); + return(NULL); + } + return(pmsgLink); +} + +/****************************************************************************** + * + * Check to verify that a given parameter number is within range for the + * given device type. + * + ******************************************************************************/ +long +drvMsg_checkParm(prec, recTyp) +struct dbCommon *prec; +char *recTyp; +{ + unsigned int parm; + char message[100]; + + if (prec->dpvt != NULL) + { + parm = ((msgXact *)(prec->dpvt))->parm; + if ((((msgXact *)(prec->dpvt))->pparmBlock->pcmds)[parm].recTyp != ((struct msgDset *)(prec->dset))->precEnum) + { + sprintf(message, "Message driver-checkParm: %s parm number %d not valid for %s record type\n", prec->name, parm, recTyp); + errMessage(S_db_badField, message); + prec->pact = TRUE; + return(S_db_badField); + } + return(0); + } + return(S_db_badField); +} + +/****************************************************************************** + * + * Callback function used to complete async-record processing. + * + ******************************************************************************/ +static void +drvMsg_callbackFunc(pcallback) +CALLBACK *pcallback; +{ + dbScanLock(((msgXact *)(pcallback->user))->prec); + (*(struct rset *)(((msgXact *)(pcallback->user))->prec->rset)).process(((msgXact *)(pcallback->user))->prec); + dbScanUnlock(((msgXact *)(pcallback->user))->prec); +} + +/****************************************************************************** + * + * Function used to initialize the callback structure within the transaction + * structure. This sets it up so that it is used to perform the completion + * phase of the async record processing. + * + ******************************************************************************/ +long +drvMsg_initCallback(prec) +struct dbCommon *prec; +{ + ((msgXact *)(prec->dpvt))->callback.callback = drvMsg_callbackFunc; + /* ((msgXact *)(prec->dpvt))->callback.priority = prec->prio; */ + ((msgXact *)(prec->dpvt))->callback.user = (void *)(prec->dpvt); + + return(OK); +} + +/****************************************************************************** + * + * Queue a transaction for the link task. + * + ******************************************************************************/ +long +drvMsg_qXact(xact, prio) +msgXact *xact; +int prio; +{ + msgLink *pmsgLink = xact->phwpvt->pmsgLink; + + /* The link verification is done at record-init time & not needed here. */ + + if ((prio < 0) || (prio >= NUM_CALLBACK_PRIORITIES)) + { + xact->status = XACT_BADPRIO; + return(ERROR); + } + + xact->callback.priority = prio; /* Callback processing priority */ + + FASTLOCK(&(pmsgLink->queue[prio].lock)); + drvMsg_xactListAddTail(&(pmsgLink->queue[prio]), xact); + FASTUNLOCK(&(pmsgLink->queue[prio].lock)); + semGive(pmsgLink->linkEventSem); + + return(OK); +} + +/****************************************************************************** + * + * Message-driver link-task. + * + * This function is spawned as a task during iocInit(). It waits on a semaphore + * that is given when either a message (transaction) is queued for a device + * associated with the link, or an event of some kind was detected from a device + * associated with the link. + * + * When the task wakes up, it checks to see if it was due to an event. If so, + * the support function that identified the event will also specify a + * transaction structure to process. If no event occurred, then the + * transaction request queues are checked. + * + * If a transaction was found, either due to an event or the work queues, it + * is processed. Processing a transaction consists of two optional phases. + * These phases are called the write and read phases, but either of them + * may do either writing, reading, or nothing. In the typical case, the + * first phase will do some writing (either setting a condition or soliciting a + * response) and the second phase will do some reading (reading back a solicited + * response) or nothing (in cases where no response will be generated to some + * write command.) + * + ******************************************************************************/ +static int msgTask(pdrvBlock, pmsgLink) +msgDrvBlock *pdrvBlock; +msgLink *pmsgLink; +{ + int working; + int prio; + msgXact *xact; + int event; + msgCmd *pmsgCmd; + msgChkEParm checkEventParms; + + if (msgDebug) + printf("Message driver link task %s started\n", pdrvBlock->taskName); + + checkEventParms.pdrvBlock = pdrvBlock; + checkEventParms.pmsgLink = pmsgLink; + checkEventParms.pxact = &xact; + + working = 1; /* Force a first time check on events and xact queues */ + + while (1) + { + if (!working) + semTake(pmsgLink->linkEventSem, WAIT_FOREVER); + + working = 0; + xact = NULL; + + /* Check to see if we woke up because of a device event */ + event = (*(pdrvBlock->drvIoctl))(MSGIOCTL_CHECKEVENT, &checkEventParms); + + if (event == MSG_EVENT_NONE) + { /* No device events pending, check the request queues for work */ + prio = 0; + while ((xact == NULL) && prio < NUM_CALLBACK_PRIORITIES) + { + FASTLOCK(&(pmsgLink->queue[prio].lock)); + if ((xact = pmsgLink->queue[prio].head) != NULL) + { + drvMsg_xactListDel(&(pmsgLink->queue[prio]), xact); + FASTUNLOCK(&(pmsgLink->queue[prio].lock)); + } + else + FASTUNLOCK(&(pmsgLink->queue[prio].lock)); + + prio++; + } + } + if (xact != NULL) + { + working = 1; + xact->status = XACT_OK; /* All well, so far */ + pmsgCmd = &(xact->pparmBlock->pcmds[xact->parm]); + + if ((pmsgCmd->writeOp.op != MSG_OP_NOP) && ((event == MSG_EVENT_NONE)||(event & MSG_EVENT_WRITE))) + { /* Perform the write portion of a transaction operation */ + (*(xact->pparmBlock->doOp))(xact, &(pmsgCmd->writeOp)); + } + + if ((xact->status == XACT_OK) && (pmsgCmd->readOp.op != MSG_OP_NOP)) + { /* There is a read opertaion spec'd, check to see if I can do it now */ + if (((pmsgCmd->flags & READ_DEFER) == 0)||(event & MSG_EVENT_READ)) + { /* Not a deferred readback parm -or- it is and is time to read */ + (*(xact->pparmBlock->doOp))(xact, &(pmsgCmd->readOp)); + + if (xact->callback.callback != NULL) + callbackRequest(xact); + + if (xact->psyncSem != NULL) + semGive(*(xact->psyncSem)); + } + /* else -- is a defered readback, must wait for readback event */ + } + else + { /* There is no read operation specified, finish the xact opetation */ + if (xact->callback.callback != NULL) + callbackRequest(xact); + + if (xact->psyncSem != NULL) + semGive(*(xact->psyncSem)); + } + } + } +} + +/****************************************************************************** + * + * These functions encapsulates the calls made to the device-specific read and + * write functions. The idea here is that we can monitor and/or buffer I/O + * operations from here. + * + * At the moment, this is used to do cehcks on the read-cache's time-to-live. + * + ******************************************************************************/ +long +drvMsg_drvWrite(pxact, pparm) +msgXact *pxact; +msgStrParm *pparm; +{ + return(pxact->pparmBlock->pdrvBlock->drvWrite(pxact, pparm)); +} + +/****************************************************************************** + * + ******************************************************************************/ +long +drvMsg_drvRead(pxact, pparm) +msgXact *pxact; +msgStrParm *pparm; +{ + return((*(pxact->pparmBlock->pdrvBlock->drvRead))(pxact, pparm)); +} + +/****************************************************************************** + * + * This function is called to handle the processing of a transaction. + * The idea here is that xact->pparmBlock->doOp could point to this + * function if there are not any custom operations, or to it's own + * function that could check to see if the operation is local/custom and + * then call this if it is not. + * + ******************************************************************************/ +long +drvMsg_xactWork(pxact, pop) +msgXact *pxact; +msgCmdOp *pop; +{ + if ((pop->op > 0) && (pop->op < NUM_VALID_OPS)) + return((*(msgSupFun[pop->op]))(pxact, pop->p)); + + printf("drvMsg_xactWork: Invalid operation code %d encountered\n", pop->op); + pxact->status = XACT_BADCMD; + return(XACT_BADCMD); +} + +/****************************************************************************** + * + * Write a string to the device w/o any formatting. + * + ******************************************************************************/ +static long +drvMsg_write(pxact, pparm) +msgXact *pxact; +msgStrParm *pparm; +{ + return(drvMsg_drvWrite(pxact, pparm)); +} + +/****************************************************************************** + * + * Read a string and see if it contains the substring provided in the + * msgAkParm structure. This is useful to check the ack string from a + * device because it will cause the record to go into a INVALID alarm + * state if the ACK does not match the provided string. + * + * If the provided substring is a zero-length string, it will match + * any possible ACK string. + * + ******************************************************************************/ +static long +drvMsg_CheckAck(pxact, packParm) +msgXact *pxact; +msgAkParm *packParm; +{ + msgStrParm strParm; +/* BUG -- the buffer size will have to be configurable */ + char buf[100]; + char *ach; + char *rch; + + strParm.buf = buf; + strParm.len = packParm->len; + + drvMsg_drvRead(pxact, &strParm); + if (msgDebug > 5) + printf("drvMsg_CheckAck comparing >%s< at %d against >%s<\n", buf, packParm->index, packParm->str); + + if (pxact->status == XACT_OK) + { + ach = &(packParm->str[packParm->index]); + rch = buf; + while (*ach != '\0') + { + if (*ach != *rch) + { + *ach = '\0'; /* stop the while loop */ + pxact->status = XACT_IOERR; + } + else + { + ach++; + rch++; + } + } + } + return(pxact->status); +} + +/****************************************************************************** + * + * Read a string and use the format string to extract the value field + * + ******************************************************************************/ +static long +drvMsg_AiFmt(pxact, pfiParm) +msgXact *pxact; +msgFiParm *pfiParm; +{ + msgStrParm strParm; +/* BUG -- some how the buffer length will have to be made configurable */ + char buf[100]; + + strParm.buf = buf; + strParm.len = pfiParm->len; + + drvMsg_drvRead(pxact, &strParm); + + if (pxact->status == XACT_OK) + { + if (sscanf(buf, &(pfiParm->format[pfiParm->startLoc]), &(((struct aiRecord *)(pxact->prec))->val)) != 1) + pxact->status = XACT_IOERR; + } + return(pxact->status); +} + +/****************************************************************************** + * + * Read a string and use the format string to extract the value field + * + * NOTE: The rval is filled in for the BI record so that conversion may + * take place in record support. + * + ******************************************************************************/ +static long +drvMsg_BiFmt(pxact, pfiParm) +msgXact *pxact; +msgFiParm *pfiParm; +{ + msgStrParm strParm; +/* BUG -- some how the buffer length will have to be made configurable */ + char buf[100]; + + strParm.buf = buf; + strParm.len = pfiParm->len; + + drvMsg_drvRead(pxact, &strParm); + + if (pxact->status == XACT_OK) + { + if (sscanf(buf, &(pfiParm->format[pfiParm->startLoc]), &(((struct biRecord *)(pxact->prec))->rval)) != 1) + pxact->status = XACT_IOERR; + } + return(pxact->status); +} + +/****************************************************************************** + * + * Read a string and use the format string to extract the value field + * + * NOTE: The rval is filled in for the MBBI record so that conversion may + * take place in record support. + * + ******************************************************************************/ +static long +drvMsg_MiFmt(pxact, pfiParm) +msgXact *pxact; +msgFiParm *pfiParm; +{ + msgStrParm strParm; +/* BUG -- some how the buffer length will have to be made configurable */ + char buf[100]; + + strParm.buf = buf; + strParm.len = pfiParm->len; + + drvMsg_drvRead(pxact, &strParm); + + if (pxact->status == XACT_OK) + { + if (sscanf(buf, &(pfiParm->format[pfiParm->startLoc]), &(((struct mbbiRecord *)(pxact->prec))->rval)) != 1) + pxact->status = XACT_IOERR; + } + return(pxact->status); +} + +/****************************************************************************** + * + * Read a string and use the format string to extract the value field + * + ******************************************************************************/ +static long +drvMsg_LiFmt(pxact, pfiParm) +msgXact *pxact; +msgFiParm *pfiParm; +{ + msgStrParm strParm; +/* BUG -- some how the buffer length will have to be made configurable */ + char buf[100]; + + strParm.buf = buf; + strParm.len = pfiParm->len; + + drvMsg_drvRead(pxact, &strParm); + + if (pxact->status == XACT_OK) + { + if (sscanf(buf, &(pfiParm->format[pfiParm->startLoc]), &(((struct longinRecord *)(pxact->prec))->val)) != 1) + pxact->status = XACT_IOERR; + } + return(pxact->status); +} + +/****************************************************************************** + * + * Read a string and use the format string to extract the value field + * + ******************************************************************************/ +static long +drvMsg_SiFmt(pxact, pfiParm) +msgXact *pxact; +msgFiParm *pfiParm; +{ + msgStrParm strParm; +/* BUG -- some how the buffer length will have to be made configurable */ + char buf[100]; + + strParm.buf = buf; + strParm.len = pfiParm->len; + + drvMsg_drvRead(pxact, &strParm); + + if (pxact->status == XACT_OK) + { + if (sscanf(buf, &(pfiParm->format[pfiParm->startLoc]), (((struct stringinRecord *)(pxact->prec))->val)) != 1) + pxact->status = XACT_IOERR; + } + return(pxact->status); +} + +/****************************************************************************** + * + * Read a string + * + ******************************************************************************/ +static long +drvMsg_SiRaw(pxact, parm) +msgXact *pxact; +void *parm; +{ + msgStrParm strParm; + + strParm.buf = ((struct stringinRecord *)(pxact->prec))->val; + strParm.len = sizeof(((struct stringinRecord *)(pxact->prec))->val); + + drvMsg_drvRead(pxact, &strParm); + + return(pxact->status); +} + +/****************************************************************************** + * + * This function is used to write a string that includes the VAL field of an + * analog output record, to an RS-232 device. + * + ******************************************************************************/ +static long +drvMsg_AoFmt(pxact, pfoParm) +msgXact *pxact; +msgFoParm *pfoParm; +{ + msgStrParm wrParm; +/* BUG -- some how the buffer length will have to be made configurable */ + char buf[100]; + + wrParm.buf = buf; + + sprintf(buf, pfoParm->format, ((struct aoRecord *)(pxact->prec))->val); + wrParm.len = -1; /* write until reach NULL character */ + + drvMsg_drvWrite(pxact, &wrParm); + return(pxact->status); +} + +static long +drvMsg_BoFmt(pxact, pfoParm) +msgXact *pxact; +msgFoParm *pfoParm; +{ + msgStrParm wrParm; + char buf[100]; + + wrParm.buf = buf; + + sprintf(buf, pfoParm->format, ((struct boRecord *)(pxact->prec))->val); + wrParm.len = -1; /* write until reach NULL character */ + + drvMsg_drvWrite(pxact, &wrParm); + return(pxact->status); +} + +/****************************************************************************** + * + * NOTE: The formatting of the MBBO value uses the RVAL field so that the + * conversion from VAL to RVAL in the record (the movement of one of the + * onvl, twvl,... fields to the rval field during record processing.) + * + ******************************************************************************/ +static long +drvMsg_MoFmt(pxact, pfoParm) +msgXact *pxact; +msgFoParm *pfoParm; +{ + msgStrParm wrParm; + char buf[100]; + + wrParm.buf = buf; + + sprintf(buf, pfoParm->format, ((struct mbboRecord *)(pxact->prec))->rval); + wrParm.len = -1; /* write until reach NULL character */ + + drvMsg_drvWrite(pxact, &wrParm); + return(pxact->status); +} + +static long +drvMsg_LoFmt(pxact, pfoParm) +msgXact *pxact; +msgFoParm *pfoParm; +{ + msgStrParm wrParm; + char buf[100]; + + wrParm.buf = buf; + + sprintf(buf, pfoParm->format, ((struct longoutRecord *)(pxact->prec))->val); + wrParm.len = -1; /* write until reach NULL character */ + + drvMsg_drvWrite(pxact, &wrParm); + return(pxact->status); +} + +static long +drvMsg_SoFmt(pxact, pfoParm) +msgXact *pxact; +msgFoParm *pfoParm; +{ + msgStrParm wrParm; + char buf[100]; + + wrParm.buf = buf; + + sprintf(buf, pfoParm->format, ((struct stringoutRecord *)(pxact->prec))->val); + wrParm.len = -1; /* write until reach NULL character */ + + drvMsg_drvWrite(pxact, &wrParm); + return(pxact->status); +} + +static long +drvMsg_SoRaw(pxact, parm) +msgXact *pxact; +void *parm; +{ + msgStrParm wrParm; + + wrParm.buf = ((struct stringoutRecord *)(pxact->prec))->val; + wrParm.len = -1; /* write until reach NULL character */ + + drvMsg_drvWrite(pxact, &wrParm); + return(pxact->status); +} + +/****************************************************************************** + * + * The following functions are called from record support. + * They are used to initialize a record's DPVT (xact structure) for + * processing by the message driver later when the record is processed. + * + ******************************************************************************/ +/****************************************************************************** + * + * Init record routine for AI + * + ******************************************************************************/ +long +drvMsg_initAi(pai) +struct aiRecord *pai; +{ + char message[100]; + long status; + + pai->dpvt = drvMsg_genXact(((struct msgDset *)(pai->dset))->pparmBlock, &(pai->inp), pai); + + if (pai->dpvt != NULL) + { + status = drvMsg_checkParm(pai, "AI"); + if (status == 0) + drvMsg_initCallback(pai); /* Init for async record completion callback */ + else + pai->pact = 1; /* mark so is never scanned */ + + return(status); + } + pai->pact = 1; /* mark so is never scanned */ + return(S_db_badField); +} +/****************************************************************************** + * + * Init record routine for AO + * + ******************************************************************************/ +long +drvMsg_initAo(pao) +struct aoRecord *pao; +{ + long status; + + pao->dpvt = drvMsg_genXact(((struct msgDset *)(pao->dset))->pparmBlock, &(pao->out), pao); + + if (pao->dpvt != NULL) + { + status = drvMsg_checkParm(pao, "AO"); + if (status == 0) + { + drvMsg_initCallback(pao); /* Init for async record completion callback */ + return(2); /* Don't convert RVAL to VAL at this time */ + } + else + pao->pact = 1; /* mark so is never scanned */ + + return(status); + } + pao->pact = 1; /* mark so is never scanned */ + return(S_db_badField); +} + +/****************************************************************************** + * + * init record routine for BI + * + ******************************************************************************/ +long +drvMsg_initBi(pbi) +struct biRecord *pbi; +{ + char message[100]; + long status; + + pbi->dpvt = drvMsg_genXact(((struct msgDset *)(pbi->dset))->pparmBlock, &(pbi->inp), pbi); + + if (pbi->dpvt != NULL) + { + status = drvMsg_checkParm(pbi, "BI"); + if (status == 0) + drvMsg_initCallback(pbi); /* Init for async record completion callback */ + else + pbi->pact = 1; /* mark so is never scanned */ + + return(status); + } + pbi->pact = 1; /* mark so is never scanned */ + return(S_db_badField); +} +/****************************************************************************** + * + * Init record routine for BO + * + ******************************************************************************/ +long +drvMsg_initBo(pbo) +struct boRecord *pbo; +{ + long status; + + pbo->dpvt = drvMsg_genXact(((struct msgDset *)(pbo->dset))->pparmBlock, &(pbo->out), pbo); + + if (pbo->dpvt != NULL) + { + status = drvMsg_checkParm(pbo, "BO"); + if (status == 0) + { + drvMsg_initCallback(pbo); /* Init for async record completion callback */ + return(2); /* Don't convert RVAL to VAL at this time */ + } + else + pbo->pact = 1; /* mark so is never scanned */ + + return(status); + } + pbo->pact = 1; /* mark so is never scanned */ + return(S_db_badField); +} + +/****************************************************************************** + * + * init record routine for MI + * + ******************************************************************************/ +long +drvMsg_initMi(pmi) +struct mbbiRecord *pmi; +{ + char message[100]; + long status; + + pmi->dpvt = drvMsg_genXact(((struct msgDset *)(pmi->dset))->pparmBlock, &(pmi->inp), pmi); + + if (pmi->dpvt != NULL) + { + status = drvMsg_checkParm(pmi, "MBBI"); + if (status == 0) + drvMsg_initCallback(pmi); /* Init for async record completion callback */ + else + pmi->pact = 1; /* mark so is never scanned */ + + return(status); + } + pmi->pact = 1; /* mark so is never scanned */ + return(S_db_badField); +} +/****************************************************************************** + * + * Init record routine for MO + * + ******************************************************************************/ +long +drvMsg_initMo(pmo) +struct mbboRecord *pmo; +{ + long status; + + pmo->dpvt = drvMsg_genXact(((struct msgDset *)(pmo->dset))->pparmBlock, &(pmo->out), pmo); + + if (pmo->dpvt != NULL) + { + status = drvMsg_checkParm(pmo, "MBBO"); + if (status == 0) + { + drvMsg_initCallback(pmo); /* Init for async record completion callback */ + return(2); /* Don't convert RVAL to VAL at this time */ + } + else + pmo->pact = 1; /* mark so is never scanned */ + + return(status); + } + pmo->pact = 1; /* mark so is never scanned */ + return(S_db_badField); +} + +/****************************************************************************** + * + * init record routine for LI + * + ******************************************************************************/ +long +drvMsg_initLi(pli) +struct longinRecord *pli; +{ + char message[100]; + long status; + + pli->dpvt = drvMsg_genXact(((struct msgDset *)(pli->dset))->pparmBlock, &(pli->inp), pli); + + if (pli->dpvt != NULL) + { + status = drvMsg_checkParm(pli, "LI"); + if (status == 0) + drvMsg_initCallback(pli); /* Init for async record completion callback */ + else + pli->pact = 1; /* mark so is never scanned */ + + return(status); + } + pli->pact = 1; /* mark so is never scanned */ + return(S_db_badField); +} +/****************************************************************************** + * + * init record routine for LO + * + ******************************************************************************/ +long +drvMsg_initLo(plo) +struct longoutRecord *plo; +{ + char message[100]; + long status; + + plo->dpvt = drvMsg_genXact(((struct msgDset *)(plo->dset))->pparmBlock, &(plo->out), plo); + + if (plo->dpvt != NULL) + { + status = drvMsg_checkParm(plo, "LO"); + if (status == 0) + drvMsg_initCallback(plo); /* Init for async record completion callback */ + else + plo->pact = 1; /* mark so is never scanned */ + + return(status); + } + plo->pact = 1; /* mark so is never scanned */ + return(S_db_badField); +} + +/****************************************************************************** + * + * init record routine for SI + * + ******************************************************************************/ +long +drvMsg_initSi(psi) +struct stringinRecord *psi; +{ + char message[100]; + long status; + + psi->dpvt = drvMsg_genXact(((struct msgDset *)(psi->dset))->pparmBlock, &(psi->inp), psi); + + if (psi->dpvt != NULL) + { + status = drvMsg_checkParm(psi, "SI"); + if (status == 0) + drvMsg_initCallback(psi); /* Init for async record completion callback */ + else + psi->pact = 1; /* mark so is never scanned */ + + return(status); + } + psi->pact = 1; /* mark so is never scanned */ + return(S_db_badField); +} +/****************************************************************************** + * + * init record routine for SO + * + ******************************************************************************/ +long +drvMsg_initSo(pso) +struct stringoutRecord *pso; +{ + char message[100]; + long status; + + pso->dpvt = drvMsg_genXact(((struct msgDset *)(pso->dset))->pparmBlock, &(pso->out), pso); + + if (pso->dpvt != NULL) + { + status = drvMsg_checkParm(pso, "SI"); + if (status == 0) + drvMsg_initCallback(pso); /* Init for async record completion callback */ + else + pso->pact = 1; /* mark so is never scanned */ + + return(status); + } + pso->pact = 1; /* mark so is never scanned */ + return(S_db_badField); +} + +/****************************************************************************** + * + * init record routine for WF + * + ******************************************************************************/ +long +drvMsg_initWf(pwf) +struct waveformRecord *pwf; +{ + char message[100]; + long status; + + pwf->dpvt = drvMsg_genXact(((struct msgDset *)(pwf->dset))->pparmBlock, &(pwf->inp), pwf); + + if (pwf->dpvt != NULL) + { + status = drvMsg_checkParm(pwf, "WAVEFORM"); + if (status == 0) + drvMsg_initCallback(pwf); /* Init for async record completion callback */ + else + pwf->pact = 1; /* mark so is never scanned */ + + return(status); + } + pwf->pact = 1; /* mark so is never scanned */ + return(S_db_badField); +} + +/****************************************************************************** + * + * These functions are called by record support. + * + * Service routines to process a input records. + * + ******************************************************************************/ +long +drvMsg_procAi(pai) +struct aiRecord *pai; +{ + return(drvMsg_proc(pai, 2)); /* no conversion */ +} +long +drvMsg_procBi(pbi) +struct biRecord *pbi; +{ + return(drvMsg_proc(pbi, 0)); /* convert RVAL to VAL */ +} +long +drvMsg_procMi(pmi) +struct mbbiRecord *pmi; +{ + return(drvMsg_proc(pmi, 0)); /* convert RVAL to VAL */ +} +long +drvMsg_procLi(pli) +struct longinRecord *pli; +{ + return(drvMsg_proc(pli, 2)); /* no conversion */ +} +long +drvMsg_procSi(psi) +struct stringinRecord *psi; +{ + return(drvMsg_proc(psi, 2)); /* no conversion */ +} +long +drvMsg_procWf(pwf) +struct waveformRecord *pwf; +{ + return(drvMsg_proc(pwf, 2)); /* no conversion */ +} + +/****************************************************************************** + * + * These functions are called by record support. + * + * Service routine to process output records. + * + * It does not make sense to return a conversion code to record support from + * processing an output record. + * + ******************************************************************************/ +long +drvMsg_procAo(pao) +struct aoRecord *pao; +{ + return(drvMsg_proc(pao, 0)); +} +long +drvMsg_procBo(pbo) +struct boRecord *pbo; +{ + return(drvMsg_proc(pbo, 0)); +} +long +drvMsg_procMo(pmo) +struct mbboRecord *pmo; +{ + return(drvMsg_proc(pmo, 0)); +} +long +drvMsg_procLo(plo) +struct longoutRecord *plo; +{ + return(drvMsg_proc(plo, 0)); +} +long +drvMsg_procSo(pso) +struct stringoutRecord *pso; +{ + return(drvMsg_proc(pso, 0)); +} + +/****************************************************************************** + * + * Generic service routine to process a record. + * + ******************************************************************************/ + +/* + * BUG -- I should probably figure out the return code from the conversion + * routine. Not from a hard-coded value passed in from above. + */ + +long +drvMsg_proc(prec, ret) +struct dbCommon *prec; /* record to process */ +int ret; /* If all goes well, return this value */ +{ + if (prec->pact) /* if already actively processing, finish up */ + { + if (((msgXact *)(prec->dpvt))->status != XACT_OK) + { /* something went wrong during I/O processing */ + if (msgDebug) + printf("Setting an alarm on record %s\n", prec->name); + + recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); + } + else + if (ret == 2) + prec->udf = FALSE; /* Set only if I return 2 (I filled in VAL) */ + + return(ret); + } + /* Not already actively processing, start things going */ + + prec->pact = TRUE; + if (drvMsg_qXact(prec->dpvt, prec->prio) == ERROR) + printf("Error during drvMsg_qXact\n"); + + return(ret); +} diff --git a/src/drv/old/drvMz8310.c b/src/drv/old/drvMz8310.c new file mode 100644 index 000000000..d526c0837 --- /dev/null +++ b/src/drv/old/drvMz8310.c @@ -0,0 +1,628 @@ + +/* drvMz8310.c */ +/* base/src/drv $Id$ */ +/* + * Routines specific to the MZ8310. Low level routines for the AMD STC in + * stc_driver.c + * Author: Jeff Hill + * Date: Feb 1989 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification History + * joh 02-20-89 Init Release + * joh 04-28-89 Added read back + * joh 11-17-89 added readback to io report + * joh 12-10-89 DB defaults the internal/external clock + * parameter to 0 or external clock. This was the opposite + * of what this driver expected. Fix was made here. + * joh 07-06-90 print channel number with channel value in IO report + * joh 02-25-91 made ext/int clk IO report more readable + * joh 09-05-91 converted to v5 vxWorks + * bg 09-15-91 added sysBustoLocalAdrs() for addressing + * bg 03-10-92 added the argument, level, to mz310_io_report(). + * bg 04-27-92 added rebootHookAdd and mz8310_reset so ioc will + * not hang on ctl X reboot. + * joh 04-28-92 added arguments to MACROS which had hidden + * parameters + * bg 06-25-92 combined drvMz8310.c and mz8310_driver.c + * bg 06-26-92 Added level to mz8310_io_report. + * joh 08-05-92 callable interface now conforms with epics standard + * mgb 08-04-93 Removed V5/V4 and EPICS_V2 conditionals + * joh 08-24-93 Include drvStc.h and ANSI C upgrade + * joh 09-29-93 removed superfluous error message + */ + +/* drvMz8310.c - Driver Support Routines for Mz8310 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define MZ8310CHIPSIZE 0x20 +#define MZ8310SIZE 0x00000100 +#define MZ8310BASE(CARD) (shortaddr+tm_addrs[MZ8310]+(CARD)*MZ8310SIZE) + +#define MZ8310DATA 0 +#define MZ8310CMD 3 +#define MZ8310CHANONCHIP 5 +#define MZ8310CHIPCOUNT 2 +#define MZ8310CHANCNT (MZ8310CHANONCHIP*MZ8310CHIPCOUNT) + +/* + NOTE: The mizar draftsman has labeled the chip at the + highest address as one and the chip at the lowest address + 2 so I am reversing the chip number below. +*/ +#define CHIP_REVERSE(CHIP) (MZ8310CHIPCOUNT-1-(CHIP)) +#define CHIP_ADDR(CARD,CHIP) (MZ8310BASE(CARD)+\ + (CHIP_REVERSE(CHIP)*MZ8310CHIPSIZE)) + +#define MZ8310_CMD_ADDR(CARD,CHIP)\ +((volatile unsigned char *) CHIP_ADDR(CARD,CHIP) + MZ8310CMD) +#define MZ8310_DATA_ADDR(CARD,CHIP)\ +((volatile unsigned short *) CHIP_ADDR(CARD,CHIP) + MZ8310DATA) +#if 0 +#define MZ8310VECBASE(CARD,CHIP)\ +((volatile unsigned char *) CHIP_ADDR(CARD,CHIP) + 0x41) +#endif + +#define MZ8310VECSIZE (0x20) +#define MZ8310INTCNT 4 +#define MZ8310FIRSTINTCHAN 0 +#define MZ8310INTVEC(CARD,CHAN)\ +(MZ8310_INT_VEC_BASE + (CARD*MZ8310INTCNT) + mz8310_strap[CHAN].vec_num) + +#define MZ8310_INTERUPTABLE(CHAN) (mz8310_strap[CHAN].vec_addr) + +# define INT_TICKS 4.0e06 /* speed of F1 in Hz */ +# define EXT_TICKS 5.0e06 /* GTA std speed of SRC1 in Hz */ + + +struct mz8310_int_conf{ + void (*user_service)(); + int user_param; + unsigned int cnt; +}; + +struct mz8310_conf{ + char init; + FAST_LOCK lock; + struct mz8310_int_conf icf[MZ8310CHANCNT]; +}; + +struct mz8310_strap_info{ + unsigned char irq; /* the level at which the chan gen ints */ + unsigned char vec_num; /* really a vec offset-see MZ8310INTVEC */ + unsigned char vec_addr;/* offset from card base address */ +}; + +static volatile char *shortaddr; + +LOCAL struct mz8310_conf *mzconf; +LOCAL unsigned int mz8310_card_count; + +/* + only 4 unique interrupts per card but any channel can potentially + generate an interrupt depending on board strapping. + + NOTE: existence of vec addr tells the driver that that channel is + strapped for interrupts since the card can't be polled for this info. + + In the MIZAR 8310 Documentation: + + Designation vector reg offset + IRQA 0x41 + IRQB 0x61 + IRQC 0x81 + IRQD 0xa1 +*/ + +LOCAL struct mz8310_strap_info mz8310_strap[MZ8310CHANCNT] = +{ + { NULL, NULL, NULL }, /* channel 0 */ + { NULL, NULL, NULL }, /* channel 1 */ + { NULL, NULL, NULL }, /* channel 2 */ + { NULL, NULL, NULL }, /* channel 3 */ + { NULL, NULL, NULL }, /* channel 4 */ + { NULL, NULL, NULL }, /* channel 5 */ + { 1, 0, 0x41 }, /* channel 6 */ + { 3, 1, 0x61 }, /* channel 7 */ + { 5, 2, 0x81 }, /* channel 8 */ + { 6, 3, 0xa1 } /* channel 9 */ +}; + +/* forward reference. */ +LOCAL int mz8310_reset(void); +LOCAL mz8310Stat mz8310_io_report_card(unsigned card, int level); +LOCAL mz8310Stat mz8310_init_card(unsigned card); +LOCAL mz8310Stat mz8310_setup_int(unsigned card, unsigned channel); +LOCAL mz8310Stat mz8310_io_report(int level); +LOCAL mz8310Stat mz8310_init(void); +LOCAL mz8310Stat mz8310_read_test(int card, int channel); +LOCAL void mz8310_int_service(struct mz8310_int_conf *icf); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvMz8310={ + 2, + mz8310_io_report, + mz8310_init}; + + +/* + * mz8310_io_report() + */ +LOCAL mz8310Stat mz8310_io_report(int level) +{ + unsigned card; + + for(card=0; card= MZ8310CHANONCHIP * MZ8310CHIPCOUNT) + return S_dev_badSignalNumber; + + if(card>=mz8310_card_count) + return S_dev_badA16; + + if(!mzconf) + return S_dev_noDevice; + + FASTLOCK(&mzconf[card].lock); + + status = + stc_one_shot_read( + preset, + &iedge0, + &iedge1, + MZ8310_CMD_ADDR(card,chip), + MZ8310_DATA_ADDR(card,chip), + channel % MZ8310CHANONCHIP, + int_source); + if(status==STC_SUCCESS){ + ticks = *int_source ? INT_TICKS : EXT_TICKS; + *edge0_delay = iedge0 / ticks; + *edge1_delay = iedge1 / ticks; + } + + FASTUNLOCK(&mzconf[card].lock); + + return status; +} + + +/* + * mz8310_read_test() + */ +LOCAL mz8310Stat mz8310_read_test(int card, int channel) +{ + int preset; + double edge0_delay; + double edge1_delay; + int int_source; + mz8310Stat status; + static char *pclktype[] = {"external-clk", "internal-clk"}; + static char *ppresettype[] = {"preset-FALSE", "preset-TRUE "}; + + status = + mz8310_one_shot_read( + &preset, + &edge0_delay, + &edge1_delay, + card, + channel, + &int_source); + if(status==MZ8310_SUCCESS){ + printf( "\tChannel %d %s delay=%f width=%f %s\n", + channel, + ppresettype[preset&1], + edge0_delay, + edge1_delay, + pclktype[int_source&1]); + if(mzconf[card].icf[channel].cnt) + printf("\tChannel %d Interrupt count=%u\n", + channel, + mzconf[card].icf[channel].cnt); + } + + return status; +} + + +/* + * mz8310_one_shot() + */ +mz8310Stat mz8310_one_shot( +int preset, /* TRUE or COMPLEMENT logic */ +double edge0_delay, /* sec */ +double edge1_delay, /* set */ +int card, /* 0 through ... */ +int channel, /* 0 through channels on a card */ +int int_source, /* (FALSE)External/ (TRUE)Internal source */ +void *event_rtn, /* subroutine to run on events */ +int event_rtn_param /* parameter to pass to above routine */ +) +{ + int chip = channel/MZ8310CHANONCHIP; + double ticks = int_source?INT_TICKS:EXT_TICKS; + mz8310Stat status; + + if(channel >= MZ8310CHANONCHIP * MZ8310CHIPCOUNT) + return S_dev_badSignalNumber; + + if(card>=mz8310_card_count) + return S_dev_badA16; + + if(!mzconf) + return S_dev_noDevice; + + /* dont overflow unsigned short in STC */ + if(edge0_delay >= 0xffff/ticks) + return S_dev_highValue; + if(edge1_delay >= 0xffff/ticks) + return S_dev_highValue; + if(edge0_delay < 0.0) + return S_dev_lowValue; + if(edge1_delay < 0.0) + return S_dev_lowValue; + + FASTLOCK(&mzconf[card].lock); + + /* Enable calling of user routine */ + if(MZ8310_INTERUPTABLE(channel)){ + mzconf[card].icf[channel].user_service = event_rtn; + mzconf[card].icf[channel].user_param = event_rtn_param; + } + + status = + stc_one_shot( + preset, + (unsigned short) (edge0_delay * ticks), + (unsigned short) (edge1_delay * ticks), + MZ8310_CMD_ADDR(card,chip), + MZ8310_DATA_ADDR(card,chip), + channel % MZ8310CHANONCHIP, + int_source); + + FASTUNLOCK(&mzconf[card].lock); + + return status; + +} + + + +/* + * mz8310_int_service() + */ +LOCAL void mz8310_int_service(struct mz8310_int_conf *icf) +{ + icf->cnt++; + + if(icf->user_service) + (*icf->user_service)(icf->user_param); + + return; +} + +/* + * The following are provided for mz8310 access from the shell + */ + + +/* + * mz8310_cmd() + */ +int mz8310_cmd( +unsigned value, +unsigned card, +unsigned chip +) +{ + volatile unsigned char *cmd = MZ8310_CMD_ADDR(card,chip); + + *cmd = value; + + return *cmd; +} + + +/* + * mz8310_rdata() + */ +int mz8310_rdata(int card, int chip) +{ + volatile unsigned short *data = MZ8310_DATA_ADDR(card,chip); + + return *data; +} + + +/* + * mz8310_wdata() + */ +int mz8310_wdata( +unsigned value, +int card, +int chip +) +{ + volatile unsigned short *data = MZ8310_DATA_ADDR(card,chip); + + *data = value; + + return value; + +} + + + +/* + * mz8310_reset + */ +LOCAL int mz8310_reset(void) +{ + short card,channel,chip; + + for (card = 0; card < mz8310_card_count; card++){ + FASTLOCK(&mzconf[card].lock); + for ( channel = 0; channel < tm_num_channels[MZ8310]; channel++){ + if (mzconf[card].icf[channel].cnt){ + chip = channel/MZ8310CHANONCHIP; + + stc_one_shot( + 0, + 10, + 0, + MZ8310_CMD_ADDR(card,chip), + MZ8310_DATA_ADDR(card,chip), + channel % MZ8310CHANONCHIP, + 0); + } + } + FASTUNLOCK(&mzconf[card].lock); + } + + return OK; +} + + + diff --git a/src/drv/old/drvMz8310.h b/src/drv/old/drvMz8310.h new file mode 100644 index 000000000..865d1bb8c --- /dev/null +++ b/src/drv/old/drvMz8310.h @@ -0,0 +1,74 @@ +/* drvMz8310.c */ +/* base/src/drv $Id$ */ +/* + * Routines specific to the MZ8310. Low level routines for the AMD STC in + * stc_driver.c + * Author: Jeff Hill + * Date: Feb 1989 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification History + */ + + + + +#define MZ8310_SUCCESS 0 + +typedef long mz8310Stat; + +mz8310Stat mz8310_one_shot_read( +int *preset, /* TRUE or COMPLEMENT logic */ +double *edge0_delay, /* sec */ +double *edge1_delay, /* sec */ +int card, /* 0 through ... */ +int channel, /* 0 through channels on a card */ +int *int_source /* (FALSE)External/(TRUE)Internal src */ +); + +mz8310Stat mz8310_one_shot( +int preset, /* TRUE or COMPLEMENT logic */ +double edge0_delay, /* sec */ +double edge1_delay, /* set */ +int card, /* 0 through ... */ +int channel, /* 0 through channels on a card */ +int int_source, /* (FALSE)External/ (TRUE)Internal source */ +void *event_rtn, /* subroutine to run on events */ +int event_rtn_param /* parameter to pass to above routine */ +); + +int mz8310_cmd( +unsigned value, +unsigned card, +unsigned chip +); + +int mz8310_rdata(int card, int chip); + +int mz8310_wdata( +unsigned value, +int card, +int chip +); + + diff --git a/src/drv/old/drvOms.c b/src/drv/old/drvOms.c new file mode 100644 index 000000000..cede9ac96 --- /dev/null +++ b/src/drv/old/drvOms.c @@ -0,0 +1,685 @@ +/* drvOms.c */ +/* base/src/drv $Id$ */ +/* + * subroutines and tasks that are used to interface to the + * Oregon Micro Systems six axis stepper motor drivers + * + * Author: Bob Dalesio + * Date: 12-28-89 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log + * ----------------- + * .01 02-07-90 lrd add command to read status + * .02 02-09-90 lrd removed the clamp on the MR command + * .03 03-22-90 lrd added the acceleration + * .04 04-12-90 lrd only allow one record to connect to each motor + * .05 04-28-90 lrd request motor data at 10 Hz. Needed delay to let + * motor start moving + * .06 04-30-90 lrd fix interrupt vectors for more than one motor + * .07 07-31-90 lrd lock the communication to the oms card for + * one user + * .08 08-01-90 lrd fix turn off of auxilary output when move is + * complete + * .09 08-01-90 lrd fix the initialization of the card to only + * enable the buffer full interrupt + * .10 10-23-90 lrd clamp the send value to something the motor + * driver can handle + * .11 11-13-90 lrd add intelligence in looking for an OMS card with + * an encoder + * .12 05-15-91 lrd add initialization of encoder and motor position + * .13 09-05-91 joh updated for v5 vxWorks + * .14 12-10-91 bg added sysBusToLocalAddrs(). Added + * compu_sm_driver.c. + * .15 03-10-92 bg Added level to io_report and gave + * compu_sm_io_report() the ability to print out + * contents of motor_data array if level > 1. + * .16 06-26-92 bg Combined drvOms.c with oms_driver.c + * .17 06-29-92 joh took file pointer arg out of io report + * .18 08-11-92 joh io report format cleanup + * .19 08-02-93 mrk Added call to taskwdInsert + * .20 08-05-93 jbk took out 200000 pulse limit + * .21 02-28-94 mrk Replaced itob by cvtLongToString + * .22 05-05-94 kornke Now supports VMEX-8 and VMEX-44E + * (8 axis s'motors and 4 encoded s'motors) + */ + +/* data requests are made from the oms_task at + * a rate of 10Hz when a motor is active + * post every .1 second or not moving + * requests are sent at 10Hz in oms_task + */ + +/* drvOms.c - Driver Support Routines for Oms */ +#include +#include +#include /* library for task support */ +#include +#include /* library for ring buffer support */ +#include +#include /* library to support sysBusToLocalAdrs. */ + +#include +#include +#include +#include +#include +#include + +#define OMS_INT_LEV 5 + +/* 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; +} drvOms={ + 2, + report, + init}; + +static long report(level) + int level; +{ + oms_io_report(level); +} + + +static long init() +{ + int status; + oms_driver_init(); + return(0); +} + +/* + * a rate of 10Hz when a motor is active + * post every .1 second or not moving + * requests are sent at 10Hz in oms_task + */ + +/* addresses of all motors present */ +struct vmex_motor *oms_motor_present[MAX_OMS_CARDS]; +char encoder_present[MAX_OMS_CARDS]; + +static char *localaddr; /* Local address used to address cards. */ + +/* motor information */ +struct oms_motor oms_motor_array[MAX_OMS_CARDS][MAX_OMS_CHANNELS]; + +/* motor status - returned to the database library routines */ +struct motor_data motor_data_array[MAX_OMS_CARDS][MAX_OMS_CHANNELS]; + +char oms_motor_specifier[MAX_OMS_CHANNELS+1] = {'X','Y','Z','T','U','V'}; + +/* scan task parameters */ +LOCAL SEM_ID oms_wakeup; /* oms_task wakeup semaphore */ +LOCAL SEM_ID oms_send_sem; /* oms_task wakeup semaphore */ + +/* response task variables */ +LOCAL SEM_ID oms_resp_sem; /* wakeup semaphore for the resp task */ +LOCAL RING_ID oms_resp_q; /* queue of responses */ + +/* interrupt routine message buffers */ +int resp_inx[MAX_OMS_CARDS]; +char read_buffer[MAX_OMS_CARDS][34]; + +/* forward reference. */ +VOID oms_reset(); + + +oms_intr(card) +register short card; +{ + register struct vmex_motor *pmotor; + register int key; + register char inp_char; + register int *pinx; + + key = intLock(); + + /* pointer to this motor */ + if ((pmotor = oms_motor_present[card]) == 0){ + intUnlock(key); + return(0); + } + pinx = &resp_inx[card]; + + /* get the next character */ + if (pmotor->status & INPUT_BUFFER_FULL){ + + /* check for end of command */ + inp_char = pmotor->data; + if (((inp_char == 0xa) || (inp_char == 0xd)) && (*pinx > 0)){ + /* check if the encoder command caused an error */ + if (pmotor->status & (OMS_ENCODER | OMS_CMD_ERROR)) + encoder_present[card] = FALSE; + else encoder_present[card] = TRUE; + /* terminate and send the message */ + read_buffer[card][*pinx] = 0; + if (rngBufPut(oms_resp_q,read_buffer[card],OMS_MSG_SZ) + != OMS_MSG_SZ){ + logMsg("oms_resp_q full\n"); + }else{ + semGive (oms_resp_sem); + } + *pinx = 0; /* reset buffer */ + + /* save printable ascii characters */ + }else if ((inp_char >= 0x20) && (inp_char < 0x7f)){ + if (*pinx == 0){ + read_buffer[card][*pinx] = card; + *pinx += 1; + } + if (*pinx < OMS_MSG_SZ){ + read_buffer[card][*pinx] = inp_char; + *pinx += 1; + }else{ + logMsg("oms intr buffer full\n"); + } + } + } + intUnlock(key); + return(0); +} + +/* + * STEPPER MOTOR RESPONSE TASK - REMOVES RESPONSES FROM the INTERRUPT QUEUE + */ +short oms_channel[MAX_OMS_CARDS]; +short oms_state[MAX_OMS_CARDS]; +char off_msg[40]; +int oms_debug = 0; +int oms_compare = 3; +oms_resp_task() +{ + unsigned char resp[OMS_MSG_SZ*4]; + register struct motor_data *pmotor_data_array; + register struct oms_motor *poms_motor_array; + short *pchannel; + int (*psmcb_routine)(); + register short *pstate; + register short card,i; + int temp; + + FOREVER { + /* wait for somebody to wake us up */ + semTake (oms_resp_sem, WAIT_FOREVER); + /* process requests in the command ring buffer */ + while (rngBufGet(oms_resp_q,resp,OMS_MSG_SZ) == OMS_MSG_SZ){ + if (oms_debug) + printf("card: %d msg:%s\n",resp[0],&resp[1]); + /* get the card number and pointers to the state and channel */ + card = resp[0]; + pchannel = &oms_channel[card]; + pstate = &oms_state[card]; + pmotor_data_array = &motor_data_array[card][*pchannel]; + poms_motor_array = &oms_motor_array[card][*pchannel]; + + /* motor selection */ + if (resp[1] == 'A') + { + switch (resp[2]) + { + case 'X': + *pchannel = 0; + break; + case 'Y': + *pchannel = 1; + break; + case 'Z': + *pchannel = 2; + break; + case 'T': + *pchannel = 3; + break; + case 'U': + *pchannel = 4; + break; + case 'V': + *pchannel = 5; + break; + case 'R': + *pchannel = 6; + break; + case 'S': + *pchannel = 7; + break; + } + + *pstate = 0; + /* position readback */ + }else if (resp[1] == 'R'){ + if (resp[2] == 'E') *pstate = 1; + else if (resp[2] == 'P') *pstate = 2; + else if (resp[2] == 'A') *pstate = 3; + else *pstate = 0; + + /* convert encoder position */ + }else if (*pstate == 1){ + sscanf(&resp[1],"%d",&temp); + pmotor_data_array->encoder_position = temp; + *pstate = 0; + /* convert motor position */ + /* use the motor position for detecting end of motion because */ + /* all motors use this, not all motors have encoders */ + }else if (*pstate == 2){ + sscanf(&resp[1],"%d",&temp); + if ((pmotor_data_array->motor_position == temp) + && (poms_motor_array->active == TRUE)){ + poms_motor_array->stop_count++; + if (poms_motor_array->stop_count >= oms_compare) { + poms_motor_array->active = FALSE; + strcpy(off_msg,"AB\nAN\n"); + off_msg[1] = oms_motor_specifier[*pchannel]; + oms_send_msg(oms_motor_present[card],off_msg); + poms_motor_array->stop_count = 0; + } + + }else{ + pmotor_data_array->motor_position = temp; + poms_motor_array->stop_count = 0; + } + *pstate = 0; + + /* convert axis status */ + }else if (*pstate == 3){ + pmotor_data_array->ccw_limit = 0; + pmotor_data_array->cw_limit = 0; + if (resp[1] == 'P'){ + pmotor_data_array->direction = 0; + if (resp[3] == 'L'){ + pmotor_data_array->ccw_limit= 1; + } + }else{ + pmotor_data_array->direction = 1; + if (resp[3] == 'L'){ + pmotor_data_array->cw_limit = 1; + } + } + pmotor_data_array->moving = poms_motor_array->active; + *pstate = 0; + + /* post every .1 second or not moving */ + if ((poms_motor_array->update_count-- <= 0) + || (pmotor_data_array->moving == 0)){ + if (poms_motor_array->callback != 0){ + (int)psmcb_routine = poms_motor_array->callback; + (*psmcb_routine)(pmotor_data_array,poms_motor_array->callback_arg); + } + if (pmotor_data_array->moving){ + poms_motor_array->update_count = 2; + }else{ + poms_motor_array->update_count = 0; + } + } + /* reset state */ + }else{ + *pstate = 0; + } + } + } +} + +oms_task() +{ + register short motor_active; + register short card,channel; + char oms_msg[40]; + register struct vmex_motor *pmotor; + + while(1){ + semTake(oms_wakeup, WAIT_FOREVER); + motor_active = TRUE; + while (motor_active){ + motor_active = FALSE; + taskDelay(2); + for (channel = 0; channel < MAX_OMS_CHANNELS; channel++){ + pmotor = oms_motor_present[0]; + for (card = 0; card < MAX_OMS_CARDS; card++,pmotor++){ + if (pmotor == 0) continue; + if (oms_motor_array[card][channel].active){ + motor_active = TRUE; + + /* request status data */ + if ((channel <= 3) && (encoder_present[card])) + strcpy(oms_msg,"A?\nRE\nRP\nRA\n"); + else + strcpy(oms_msg,"A?\nRP\nRA\n"); + oms_msg[1] = oms_motor_specifier[channel]; + oms_send_msg(oms_motor_present[card],oms_msg); + } + } + } + } + } +} + +/* + * OMS_DRIVER_INIT + * + * initialize all oms drivers present + */ +oms_driver_init(){ + struct vmex_motor *pmotor; + short i,j,got_one; + int status; + short dummy; + char oms_init_msg[20]; + int taskId; + + for (i = 0; i < MAX_OMS_CARDS; i++){ + resp_inx[i] = 0; + } + + /* find all cards present */ + got_one = FALSE; + + status = sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO,sm_addrs[OMS_6AXIS], &localaddr); + if (status != OK){ + logMsg("Addressing error in oms driver\n"); + return(ERROR); + } + rebootHookAdd(oms_reset); + pmotor = (struct vmex_motor *)localaddr; + for (i = 0; i < MAX_OMS_CARDS; i++,pmotor++){ + if (vxMemProbe(pmotor,READ,sizeof(short),&dummy) == OK){ + got_one = TRUE; + + /* intialize the motor */ + pmotor->control = 0; + + /* interrupt vector */ + pmotor->vector = 0x80+i; + intConnect(((0x80+i)*4),oms_intr,i); + sysIntEnable(OMS_INT_LEV); + + /* enable interrupt on input buffer full */ + pmotor->control |= 0xa0; + + /* mark the motor card as present */ + oms_motor_present[i] = pmotor; + encoder_present[i] = TRUE; + }else{ + /* mark the motor card as not present */ + oms_motor_present[i] = 0; + } + for (j = 0; j < MAX_OMS_CHANNELS; j++){ + oms_motor_array[i][j].active = FALSE; + oms_motor_array[i][j].callback = 0; + motor_data_array[i][j].encoder_position = 0; + motor_data_array[i][j].motor_position = 0; + } + } + + if (got_one){ + /* initialize the command task ring buffer */ + if ((oms_resp_q = rngCreate(OMS_RESP_Q_SZ)) == (RING_ID)NULL) + panic ("oms_driver_init: oms_resp_q not created\n"); + + /* initialize the oms response task semaphore */ + if(!(oms_resp_sem=semBCreate(SEM_Q_FIFO,SEM_EMPTY))) + errMessage(0,"semBcreate failed in oms_driver_init"); + /* intialize the data request wakeup semaphore */ + if(!(oms_wakeup=semBCreate(SEM_Q_FIFO,SEM_EMPTY))) + errMessage(0,"semBcreate failed in oms_driver_init"); + /* oms card mutual exclusion semaphore */ + if(!(oms_send_sem=semBCreate(SEM_Q_FIFO,SEM_FULL))) + errMessage(0,"semBcreate failed in oms_driver_init"); + /* spawn the motor data request task */ + taskId = taskSpawn("oms_task",42,VX_FP_TASK,8000,oms_task); + taskwdInsert(taskId,NULL,NULL); + + /* spawn the motor data request task */ + taskId = taskSpawn("oms_resp_task",42,VX_FP_TASK,8000,oms_resp_task); + taskwdInsert(taskId,NULL,NULL); + + /* enable echo on each motor that is present */ + + pmotor = (struct vmex_motor *)localaddr; + + for (i = 0; i < MAX_OMS_CARDS; i++,pmotor++){ + if (oms_motor_present[i]){ + + /* give it the initialization commands */ + oms_send_msg(pmotor,"EN\nWY\n"); + } + } + } + return(0); +} + +/* + * OMS_DRIVER + * + * interface routine called from the database library + */ +#define MOTOR_POS 1 +oms_driver(card,channel,value_flag,arg1,arg2) +register short card; +register short channel; +short value_flag; +register int arg1; +int arg2; +{ + char oms_move_msg[100]; + short i,count; + + if (!oms_motor_present[card]) return(-1); + switch (value_flag){ + case (SM_MODE): /* only supports positional mode */ + break; + case (SM_VELOCITY): + /* set the velocity */ + motor_data_array[card][channel].velocity = arg1; + strcpy(oms_move_msg,"A?\nVL"); + oms_move_msg[MOTOR_POS] = oms_motor_specifier[channel]; + count = cvtLongToString(arg1,&oms_move_msg[5]); + strcat(oms_move_msg,"\n"); + oms_send_msg(oms_motor_present[card],oms_move_msg); + + /* set the acceleration */ + strcpy(oms_move_msg,"A?\nAC"); + oms_move_msg[MOTOR_POS] = oms_motor_specifier[channel]; + count = cvtLongToString(arg2,&oms_move_msg[5]); + strcat(oms_move_msg,"\n"); + oms_send_msg(oms_motor_present[card],oms_move_msg); + + break; + + case (SM_MOVE): + /* move the motor */ + strcpy(oms_move_msg,"A?\nAF\nMR"); + oms_move_msg[1] = oms_motor_specifier[channel]; + count = cvtLongToString(arg1,&oms_move_msg[8]); + strcat(oms_move_msg,"\nGO\n"); + oms_send_msg(oms_motor_present[card],oms_move_msg); + + /* set the motor to active */ + oms_motor_array[card][channel].active = TRUE; + + /* wakeup the oms task */ + semGive(oms_wakeup); + + break; + case (SM_MOTION): + /* stop the motor */ + strcpy(oms_move_msg,"A?ST\n"); + if (arg1 == 0){ + oms_move_msg[1] = oms_motor_specifier[channel]; + }else{ + return(0); + } + oms_send_msg(oms_motor_present[card],oms_move_msg); + + /* wakeup the oms task */ + semGive(oms_wakeup); + break; + + case (SM_CALLBACK): + i = 0; + if (oms_motor_array[card][channel].callback != 0) return(-1); + oms_motor_array[card][channel].callback = arg1; + oms_motor_array[card][channel].callback_arg = arg2; + break; + + /* reset encoder and motor positions to zero */ + case (SM_SET_HOME): + /* load the position to be zero */ + strcpy(oms_move_msg,"A?\nLP0\n"); + oms_move_msg[1] = oms_motor_specifier[channel]; + oms_send_msg(oms_motor_present[card],oms_move_msg); + + /* set the motor to active */ + oms_motor_array[card][channel].active = TRUE; + + /* wakeup the oms task */ + semGive(oms_wakeup); + + break; + + case (SM_ENCODER_RATIO): + /* set the encoder ratio */ + /* The "ER" command changes how far a pulse will move the */ + /* motor. */ + /* As this is not the desired action this command is not */ + /* implemented here. */ + break; + + case (SM_READ): + /* set the motor to active */ + oms_motor_array[card][channel].active = TRUE; + + /* wakeup the oms task */ + semGive(oms_wakeup); + + break; + } + return(0); +} + +char last_msg[80]; +int oms_count,oms_icount,oms_illcmd,oms_sleep,oms_isleep; +/* + * OMS_SEND_MSG + * + * Gives messages to the OMS card + */ +oms_send_msg(pmotor,pmsg) +struct vmex_motor *pmotor; +register char *pmsg; +{ +int i; +i = 0; + /* take the mutual exclusion semaphore */ + semTake(oms_send_sem, WAIT_FOREVER); + while (*pmsg){ + if (pmotor->status & 0x01){ + oms_illcmd++; + while ((pmotor->status & TRANSMIT_BUFFER_EMPTY) == 0){ + oms_icount++; + if ((oms_icount % 5) == 0){ + oms_isleep++; +/* A taskDelay makes a 68040 wait frequently */ + /*taskDelay(1);*/ + } + } + pmotor->data = 0x19; /* reset */ + }else{ + while ((pmotor->status & TRANSMIT_BUFFER_EMPTY) == 0){ + oms_count++; + if ((oms_count % 5) == 0){ + oms_sleep++; +/* A taskDelay makes a 68040 wait frequently */ + /*taskDelay(1);*/ + } + } + pmotor->data = *pmsg; + pmsg++; + } + } + /* release the mutual exclusion semaphore */ + semGive(oms_send_sem); +} + +oms_io_report(level) +short int level; +{ + register short int i,j; + + for (i = 0; i < MAX_OMS_CARDS; i++) { + if (oms_motor_present[i]){ + printf("SM: OMS:\tcard %d\n",i); + for (j = 0; j < MAX_OMS_CHANNELS; j++){ + if (level > 0) + oms_sm_stat(i,j); + } + } + + } + } + +VOID oms_sm_stat(card,channel) + short int card,channel; + { + + printf("SM: OMS: Card = %d,channel = %d\n",card,channel); + printf("\tCW limit = %d\tCCW limit = %d\tMoving = %d\tDirection = %d\n", + motor_data_array[card][channel].cw_limit, + motor_data_array[card][channel].ccw_limit, + motor_data_array[card][channel].moving, + motor_data_array[card][channel].direction); + + printf("\tConstant Velocity = %ld\tVelocity = %ld\t \n", + motor_data_array[card][channel].constant_velocity, + motor_data_array[card][channel].velocity); + + printf("\tAcceleration = %ld\tEncoder Position = %ld\tMotor Position = %ld\n", + motor_data_array[card][channel].accel, + motor_data_array[card][channel] .encoder_position, + motor_data_array[card][channel].motor_position); + } + + +/* + * + * Disables interrupts. Called on CTL X reboot. + * + */ + +VOID oms_reset(){ + short card; + struct vmex_motor *pmotor; + short dummy; + + pmotor = (struct vmex_motor *)localaddr; + for (card = 0; card < MAX_OMS_CARDS; card++,pmotor++){ + if(vxMemProbe(pmotor,READ,sizeof(short),&dummy) == OK){ + pmotor->control &= 0x5f; + } + } + +} diff --git a/src/drv/old/drvOms.h b/src/drv/old/drvOms.h new file mode 100644 index 000000000..065074afa --- /dev/null +++ b/src/drv/old/drvOms.h @@ -0,0 +1,74 @@ +/* oms_sm_driver.h */ +/* base/src/drv $Id$ */ +/* + * headers that are used to interface to the + * Oregon Micro Systems six axis stepper motor drivers + * + * Author: Bob Dalesio + * Date: 02-25-90 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 04-12-90 lrd only allow one connection to each motor + * .02 11-13-90 lrd add status bit definitions for encoder commands + * .03 05-05-94 kornke supports 8 OMS channels now + */ +#define MAX_OMS_CARDS 8 +#define MAX_OMS_CHANNELS 8 + + +/* motor information */ +struct oms_motor{ +short active; /* flag to tell the oms_task if the motor is moving */ +int callback; /* routine in database library to call with status */ +int callback_arg; /* argument to callback routine */ +short update_count; +short stop_count; +}; + +#define MIRQE 0x80 +#define TRANSMIT_BUFFER_EMPTY 0x40 +#define INPUT_BUFFER_FULL 0x20 +#define MDONE 0x10 +#define OMS_ENCODER 0x04 +#define OMS_CMD_ERROR 0x01 + +struct vmex_motor{ + char unused0; + char data; + char unused1; + char done; + char unused2; + char control; + char unused3; + char status; + char unused4; + char vector; + char unused5[6]; +}; + +/* oms message defines */ +#define OMS_MSG_SZ 32 /* response message size */ +#define OMS_RESP_Q_SZ (OMS_MSG_SZ*500) /* response ring buffer size */ + diff --git a/src/drv/old/drvRs232.c b/src/drv/old/drvRs232.c new file mode 100644 index 000000000..41176e8a8 --- /dev/null +++ b/src/drv/old/drvRs232.c @@ -0,0 +1,550 @@ +/* base/src/drv $Id$ */ +/* + * Author: John Winans + * Date: 04-13-92 + * EPICS R/S-232 driver for the VxWorks's tty driver + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 09-30-91 jrw created + * + */ + +#define DRVRS232_C + +#include +#include +#if 0 /* COMMENTED OUT SOME INCLUDES */ +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* COMMENTED OUT SOME INCLUDES */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +int drv232Debug = 0; + +static void callbackAbortSerial(); +static void dogAbortSerial(); + +/****************************************************************************** + * + ******************************************************************************/ +#define RSLINK_PRI 50 +#define RSLINK_OPT VX_FP_TASK|VX_STDIO +#define RSLINK_STACK 5000 + + +/****************************************************************************** + * + ******************************************************************************/ +static long +report() +{ + printf("Report for RS-232 driver\n"); + return(OK); +} + +/****************************************************************************** + * + ******************************************************************************/ +static long +init(pparms) +msgDrvIniParm *pparms; +{ + if (drv232Debug) + printf("Init for RS-232 driver\n"); + + return(OK); +} + +/****************************************************************************** + * + * This function is called to allocate any structures needed to hold + * device-specific data that is added to the msgXact structure. + * + * It is also responsible for filling in the msgXact.phwpvt pointer. + * + ******************************************************************************/ +static long +genXact(p) +msgDrvGenXParm *p; +{ + long stat = -1; + char message[100]; + devTy232Link *pdevTy232Link; + + if (drv232Debug) + printf("RS-232 genXact entered for link %d, addr %d, parm %s\n", p->plink->value.gpibio.link, p->plink->value.gpibio.addr, p->plink->value.gpibio.parm); + + p->pmsgXact->phwpvt = p->pmsgXact->pparmBlock->pmsgHwpvt; + + + while ((p->pmsgXact->phwpvt != NULL) && (stat == -1)) + { + pdevTy232Link = (devTy232Link *)(p->pmsgXact->phwpvt->pmsgLink->p); + + if ((pdevTy232Link->link == p->plink->value.gpibio.link) + && (pdevTy232Link->port == p->plink->value.gpibio.addr)) + { + if (pdevTy232Link->pparmBlock != p->pmsgXact->pparmBlock) + { + sprintf(message, "%s: Two different devices on same RS-232 port\n", p->pmsgXact->prec->name); + errMessage(S_db_badField, message); + return(ERROR); + } + else + stat = 0; /* Found the correct hwpvt structure */ + } + else + p->pmsgXact->phwpvt = p->pmsgXact->phwpvt->next; + } + if (stat != 0) + { /* Could not find a msgHwpvt for the right link, create a new one */ + if ((p->pmsgXact->phwpvt = drvMsg_genHwpvt(p->pmsgXact->pparmBlock, p->plink)) == NULL) + return(ERROR); + } + p->pmsgXact->callback.callback = NULL; + p->pmsgXact->psyncSem = NULL; + if (sscanf(p->plink->value.gpibio.parm,"%d", &(p->pmsgXact->parm)) != 1) + { + p->pmsgXact->prec->pact = TRUE; + sprintf("%s: invalid parameter string >%s<\n", p->pmsgXact->prec->name, p->plink->value.gpibio.parm); + errMessage(S_db_badField, message); + return(ERROR); + } + + return(OK); +} + +/****************************************************************************** + * + * This function is called to allocate any structures needed to hold + * device-specific data that is added to the msgHwpvt structure. + * + * It is also responsible for filling in the msgHwpvt.pmsgLink pointer. + * + ******************************************************************************/ +static long +genHwpvt(p) +msgDrvGenHParm *p; +{ + int stat = ERROR; + + if (drv232Debug) + printf("rs232-genHwpvt entered\n"); + + p->pmsgHwpvt->pmsgLink = drv232Block.pmsgLink; + while ((p->pmsgHwpvt->pmsgLink != NULL) && (stat == ERROR)) + { + if ((((devTy232Link *)(p->pmsgHwpvt->pmsgLink->p))->link == p->plink->value.gpibio.link) + && (((devTy232Link *)(p->pmsgHwpvt->pmsgLink->p))->port == p->plink->value.gpibio.addr)) + stat = OK; + else + p->pmsgHwpvt->pmsgLink = p->pmsgHwpvt->pmsgLink->next; + } + if (stat != OK) + { + if ((p->pmsgHwpvt->pmsgLink = drvMsg_genLink(p->pparmBlock, p->plink)) == NULL) + return(ERROR); + } + return(OK); +} + +/****************************************************************************** + * + * This function is called to allocate any structures needed to hold + * device-specific data that is added to the msgLink structure. + * + ******************************************************************************/ +static long +genLink(p) +msgDrvGenLParm *p; +{ + char name[20]; + char message[100]; + devTy232Link *pdevTy232Link; + + if (drv232Debug) + printf("In RS-232's genLink, link = %d, addr = %d\n", p->plink->value.gpibio.link,p->plink->value.gpibio.addr); + + switch (p->op) { + case MSG_GENLINK_CREATE: + + if ((p->pmsgLink->p = malloc(sizeof(devTy232Link))) == NULL) + return(ERROR); + + pdevTy232Link = (devTy232Link *)(p->pmsgLink->p); + + pdevTy232Link->link = p->plink->value.gpibio.link; + pdevTy232Link->port = p->plink->value.gpibio.addr; + pdevTy232Link->pparmBlock = p->pparmBlock; + + if ((pdevTy232Link->doggie = wdCreate()) == NULL) + { + printf("RS-232 driver can't create a watchdog\n"); + /* errMessage(******, message); */ + return(ERROR); + } + + sprintf(name, "/tyCo/%d", p->plink->value.gpibio.addr); + + if (drv232Debug) + printf ("in genlink opening >%s<, baud %d\n", name, ((devTy232ParmBlock *)(p->pparmBlock->p))->baud); + + if ((((devTy232Link *)(p->pmsgLink->p))->ttyFd = open(name, O_RDWR, 0)) != -1) + { + if (drv232Debug) + printf("Successful open w/fd=%d\n", pdevTy232Link->ttyFd); + + /* set baud rate and unbuffered mode */ + (void) ioctl (pdevTy232Link->ttyFd, FIOBAUDRATE, ((devTy232ParmBlock *)(p->pparmBlock->p))->baud); + (void) ioctl (pdevTy232Link->ttyFd, FIOSETOPTIONS, ((devTy232ParmBlock *)(p->pparmBlock->p))->ttyOptions); + + pdevTy232Link->callback.callback = callbackAbortSerial; + pdevTy232Link->callback.priority = priorityHigh; + pdevTy232Link->callback.user = (void *) pdevTy232Link; + + return(OK); + } + else + { + printf("RS-232 genLink failed to open the tty port\n"); + free(p->pmsgLink->p); + return(ERROR); + } + + case MSG_GENLINK_ABORT: + if (drv232Debug) + printf("RS-232 genLink called with abort status\n"); + + pdevTy232Link = (devTy232Link *)(p->pmsgLink->p); + /* free(p->pmsgLink->p); Don't forget to take it out of the list */ + return(OK); + } + return(ERROR); +} + +/****************************************************************************** + * + * This function is called to allow the device to indicate that a device-related + * event has occurred. If an event had occurred, it would have to place the + * address of a valid transaction structure in pxact. This xact structure + * will then be processed by the message link task as indicated by the + * return code of this function. + * + * MSG_EVENT_NONE = no event has occurred, pxact not filled in. + * MSG_EVENT_WRITE = event occurred, process the writeOp assoc'd w/pxact. + * MSG_EVENT_READ = event occurred, process the readOp assoc'd w/pxact. + * + * Note that MSG_EVENT_READ only makes sense when returned with a transaction + * that has deferred readback specified for its readOp function. Because if + * it is NOT a deferred readback, it will be done immediately after the writeOp. + * Even if that writeOp was due to a MSG_EVENT_WRITE from this function. + * + ******************************************************************************/ +static long +checkEvent(p) +msgChkEParm *p; +{ + return(MSG_EVENT_NONE); +} + + +static void +dogAbortSerial(pdevTy232Link) +devTy232Link *pdevTy232Link; +{ + logMsg("RS-232 driver watch-dog timeout on link %d, port %d, fd=%d\n", pdevTy232Link->link, pdevTy232Link->port, pdevTy232Link->ttyFd); + + /* Annoying vxWorks implementation... can't IOCTL from here */ + callbackRequest(&(pdevTy232Link->callback)); +} + +static void +callbackAbortSerial(p) +CALLBACK *p; +{ + ((devTy232Link *)(p->user))->dogAbort = TRUE; + + ioctl(((devTy232Link *)(p->user))->ttyFd, FIOCANCEL); + /* I am not sure if I should really do this, but... */ + ioctl(((devTy232Link *)(p->user))->ttyFd, FIOFLUSH); +} + +/****************************************************************************** + * + * This function is used to write a string of characters out to an RS-232 + * device. + * + ******************************************************************************/ +static long +drvWrite(pxact, pwrParm) +msgXact *pxact; +msgStrParm *pwrParm; +{ + devTy232Link *pdevTy232Link = (devTy232Link *)(pxact->phwpvt->pmsgLink->p); + int len; + char *out; + char in; + + if (wdStart(pdevTy232Link->doggie, ((devTy232ParmBlock *)(pxact->pparmBlock->p))->dmaTimeout, dogAbortSerial, pdevTy232Link) == ERROR) + { + printf("RS-232 driver can not start watchdog timer\n"); + /* errMessage(***,message); */ + pxact->status = XACT_IOERR; + return(pxact->status); + } + + if (((devTy232ParmBlock *)(pxact->pparmBlock->p))->flags & ECHO) + { /* ping-pong the characters out */ + + /*BUG ??? This will only work if we are sure that the RX buffer is clean */ + ioctl(pdevTy232Link->ttyFd, FIORFLUSH); + + out = pwrParm->buf; + while ((*out != '\0') && (pxact->status == XACT_OK)) + { + if (drv232Debug > 5) + printf("out >%c<\n", *out); + if (write(pdevTy232Link->ttyFd, out, 1) != 1) + { + printf("RS-232 write error on link %d, port %d\n", pdevTy232Link->link, pdevTy232Link->port); + pxact->status = XACT_IOERR; + } + else + { + if (read(pdevTy232Link->ttyFd, &in, 1) != 1) + { +printf("Read problem encountered in echo reading mode of a write operation\n"); + if (pdevTy232Link->dogAbort) + pxact->status = XACT_TIMEOUT; + else + pxact->status = XACT_IOERR; + } + else + { + if (*out != in) + printf("echo response out of sync! sent >%c< got >%c<\n", *out, in); + + if (drv232Debug > 5) + printf("in >%c<\n", in); + + if ((*out == '\r') && (((devTy232ParmBlock *)(pxact->pparmBlock->p))->flags & CRLF)) + { + if (read(pdevTy232Link->ttyFd, &in, 1) != 1) + { +printf("Read problem encountered in echo reading mode of a write operation\n"); + if (pdevTy232Link->dogAbort) + pxact->status = XACT_TIMEOUT; + else + pxact->status = XACT_IOERR; + } + else + { + if (drv232Debug > 5) + printf("in >%c<\n", in); + } + } + } + } + out++; + } + } + else + { + if (pwrParm->len == -1) + len = strlen(pwrParm->buf); + else + len = pwrParm->len; + + if (drv232Debug > 4) + printf("block-writing >%s<\n", pwrParm->buf); + + if (write(pdevTy232Link->ttyFd, pwrParm->buf, len) != len) + { + printf("RS-232 write error on link %d, port %d\n", pdevTy232Link->link, pdevTy232Link->port); + pxact->status = XACT_IOERR; + } + } + if (wdCancel(pdevTy232Link->doggie) == ERROR) + printf("Can not cancel RS-232 watchdog timer\n"); + + return(pxact->status); +} + +/****************************************************************************** + * + * This function is used to read a string of characters from an RS-232 device. + * + ******************************************************************************/ +static long +drvRead(pxact, prdParm) +msgXact *pxact; +msgStrParm *prdParm; +{ + devTy232Link *pdevTy232Link = (devTy232Link *)(pxact->phwpvt->pmsgLink->p); + int len; + int clen; + char *ch; + int flags; + int eoi; + devTy232ParmBlock *pdevTy232ParmBlock = (devTy232ParmBlock *)(pxact->pparmBlock->p); + + pdevTy232Link->dogAbort = FALSE; + if (wdStart(pdevTy232Link->doggie, pdevTy232ParmBlock->dmaTimeout, dogAbortSerial, pdevTy232Link) == ERROR) + { + printf("RS-232 driver can not start watchdog timer\n"); + /* errMessage(***,message); */ + pxact->status = XACT_IOERR; + return(pxact->status); + } + + ch = prdParm->buf; + len = prdParm->len - 1; + + flags = pdevTy232ParmBlock->flags; +/* BUG -- This should have a timeout check specified in here */ + if ((pdevTy232ParmBlock->eoi != -1) || (flags & KILL_CRLF)) + { + eoi = 0; + while ((!eoi) && (len > 0)) + { + if (read(pdevTy232Link->ttyFd, ch, 1) != 1) + { +printf("Read problem encountered in eoi/KILL_CRLF reading mode\n"); + if (pdevTy232Link->dogAbort) + pxact->status = XACT_TIMEOUT; + else + pxact->status = XACT_IOERR; + eoi = 1; + } + else + { + if (drv232Debug > 6) + printf("in >%c<\n", *ch); + + if (*ch == pdevTy232ParmBlock->eoi) + eoi = 1; + + if (!(flags & KILL_CRLF) || ((*ch != '\n') && (*ch != '\r'))) + { + ch++; + len--; /* To count the \n's and \r's or not to count... */ + } + } + } + if (len == 0) + { + printf("buffer length overrun during read operation\n"); + pxact->status = XACT_LENGTH; + } + + *ch = '\0'; + } + else + { /* Read xact->rxLen bytes from the device */ + while(len) + { + clen = read(pdevTy232Link->ttyFd, ch, len); + if (pdevTy232Link->dogAbort) + { +printf("Read problem encountered in raw mode\n"); + pxact->status = XACT_TIMEOUT; + len = 0; + } + else + { + len -= clen; + ch += clen; + } + } + *ch = '\0'; + } + if (drv232Debug) + printf("drvRead: got >%s<\n", prdParm->buf); + + if (wdCancel(pdevTy232Link->doggie) == ERROR) + printf("Can not cancel RS-232 watchdog timer, dogAbort=%d \n", pdevTy232Link->dogAbort); + + return(pxact->status); +} + +static long +drvIoctl(cmd, pparm) +int cmd; +void *pparm; +{ + switch (cmd) { + case MSGIOCTL_REPORT: + return(report()); + case MSGIOCTL_INIT: + return(init(pparm)); + case MSGIOCTL_INITREC: + printf("drv232Block.drvIoctl got a MSGIOCTL_INITREC request!\n"); + return(ERROR); + case MSGIOCTL_GENXACT: + return(genXact(pparm)); + case MSGIOCTL_GENHWPVT: + return(genHwpvt(pparm)); + case MSGIOCTL_GENLINK: + return(genLink(pparm)); + case MSGIOCTL_CHECKEVENT: + return(checkEvent(pparm)); + case MSGIOCTL_COMMAND: /* BUG -- finish this routine! */ + return(ERROR); + } + return(ERROR); +} + + +msgDrvBlock drv232Block = { + "RS232", + RSLINK_PRI, + RSLINK_OPT, + RSLINK_STACK, + NULL, + drvWrite, + drvRead, + drvIoctl +}; diff --git a/src/drv/old/drvStc.c b/src/drv/old/drvStc.c new file mode 100644 index 000000000..0a48ab78d --- /dev/null +++ b/src/drv/old/drvStc.c @@ -0,0 +1,290 @@ +/* drvStc.c */ +/* base/src/drv $Id$ */ +/* + * The following are specific driver routines for the AMD STC + * + * NOTE: if multiple threads use these routines at once you must provide locking + * so command/data sequences are gauranteed. See mz8310_driver.c for examples. + * + * + * Author: Jeff Hill + * Date: Feb 89 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * + * joh 022089 Init Release + * joh 042889 Added read back + * joh 111789 Fixed reset goes to byte mode bug + * joh 121090 Fixed confusion about the polarity of internal/external + * clock between DB and the drivers. + * joh 110791 Prevent the stc from generating tc prior to the trigger + * in delayed pulse mode by forcing edge 0 delays of zero to be + * a delay of one instead. + * joh 010491 force all edge 0 delays less than two to two + * joh 082493 ANSI C and EPICS return codes + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + + + +/* + * stc_io_report() + */ +stcStat stc_io_report( +volatile uint8_t *pcmd, +volatile uint16_t *pdata +) +{ + uint8_t cmd; + uint16_t data; + + if(vxMemProbe((char *)pcmd, READ, sizeof(cmd), (char *)&cmd) != OK) + return S_dev_noDevice; + if(vxMemProbe((char *)pdata, READ, sizeof(data), (char *)&data) != OK) + return S_dev_noDevice; + + /* + * addd AMD STC status here + */ + + return STC_SUCCESS; +} + + + +/* + * stc_init() + */ +stcStat stc_init( +volatile uint8_t *pcmd, +volatile uint16_t *pdata, +unsigned master_mode +) +{ + uint8_t cmd; + uint16_t data; + unsigned channel; + + if(vxMemProbe((char *)pcmd, READ, sizeof(cmd), (char *)&cmd) != OK) + return S_dev_noDevice; + if(vxMemProbe((char *)pdata, READ, sizeof(data), (char *)&data) != OK) + return S_dev_noDevice; + + /* + * go to 16 bit mode in order to test the master mode register + */ + STC_BUS16; + if(master_mode != STC_MASTER_MODE){ + + /* + * start in a known state + */ + STC_RESET; + + /* + * required since the reset puts it in byte mode + */ + STC_BUS16; + STC_SET_MASTER_MODE(master_mode); + for(channel=0; channel=CHANONCHIP) + return S_dev_badSignalNumber; + + STC_CTR_READ(mode, edge0, edge1); + + /* + * Only return values if the counter is in the proper mode + * see stc_one_shot() for info on conversions and functions selected + * by these bit fields + */ + if(mode == 0xc16a){ + *int_source = FALSE; + *preset = TRUE; + *edge0_count = ~edge0; + *edge1_count = ~edge1+1; + } + else if(mode == 0xc162){ + *int_source = FALSE; + *preset = FALSE; + *edge0_count = edge0-1; + *edge1_count = edge1; + } + else if(mode == 0xcb6a){ + *int_source = TRUE; + *preset = TRUE; + *edge0_count = ~edge0; + *edge1_count = ~edge1+1; + } + else if(mode == 0xcb62){ + *int_source = TRUE; + *preset = FALSE; + *edge0_count = edge0-1; + *edge1_count = edge1; + } + else + return S_dev_internal; + + return STC_SUCCESS; +} + + + +/* + * stc_one_shot() + */ +stcStat stc_one_shot( +unsigned preset, +unsigned edge0_count, +unsigned edge1_count, +volatile uint8_t *pcmd, +volatile uint16_t *pdata, +unsigned channel, +unsigned int_source +) +{ + uint8_t cmd; + uint16_t data; + + if(vxMemProbe((char *)pcmd, READ, sizeof(cmd), (char *)&cmd) != OK) + return S_dev_noDevice; + if(vxMemProbe((char *)pdata, READ, sizeof(data), (char *)&data) != OK) + return S_dev_noDevice; + if(channel>=CHANONCHIP) + return S_dev_badSignalNumber; + + /* + * joh 110791 + * Prevent the stc from generating tc prior to the trigger + * in delayed pulse mode by forcing edge 0 delays of zero to be + * a delay of one instead. + * + * 010492 + * Strange extra edges occur when the delay is 0 or 1 + * and the counter is reinitialized to a width of + * zero so I have disabled a delay of one also + * + * These extra edges occur when TC is set + */ + + if(edge0_count < 2) + edge0_count = 2; + + STC_DISARM; + + /* + * active positive going edge (gate input) + * count on the rising edge of source + * ctr source: (F1- internal) (SRC1- external) + * mode L - Hardware triggered delayed pulse one-shot + * binary count + * count down (count up if preset is TRUE) + * TC toggled output + * + * see chapter 7 of the Am9513 STC tech man concerning count + 1 + * + */ + + /* + * NOTE: I must be able to read back the state of the preset later + * so I encode this information in the count down/up bit. + * count up on TRUE preset + * count down on FALSE preset + * + * see stc_one_shot_read() above + */ + if(int_source){ + if(preset) + STC_CTR_INIT(0xcb6a, ~edge0_count, ~edge1_count+1) + else + STC_CTR_INIT(0xcb62, edge0_count+1, edge1_count); + }else{ + if(preset) + STC_CTR_INIT(0xc16a, ~edge0_count, ~edge1_count+1) + else + STC_CTR_INIT(0xc162, edge0_count+1, edge1_count); + } + + STC_LOAD; + /* + *see chapter 7 of the Am9513 STC tech man concerning this step + */ + + STC_STEP; + + STC_SET_TC(preset); + + /* + * Only arm counter if the pulse has a finite duration + */ + if(edge1_count != 0){ + STC_ARM; + } + + return STC_SUCCESS; +} + + + diff --git a/src/drv/old/drvStc.h b/src/drv/old/drvStc.h new file mode 100644 index 000000000..5d4a6203b --- /dev/null +++ b/src/drv/old/drvStc.h @@ -0,0 +1,107 @@ +/* drvStc.h */ +/* base/src/drv $Id$ */ +/* + * The following are specific driver routines for the AMD STC + * + * NOTE: if multiple threads use these routines at once you must provide locking + * so command/data sequences are gauranteed. See mz8310_driver.c for examples. + * + * + * Author: Jeff Hill + * Date: Feb 89 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * + * joh 022089 Init Release + * joh 082493 ANSI C and EPICS return codes + */ + +/* + * AMD STC constants + */ +#define CHANONCHIP 5U +#define CHIPCHAN (channel%CHANONCHIP) +#define CHIPNUM (channel/CHANONCHIP) + +#define STC_RESET *pcmd = 0xffU +#define STC_BUS16 *pcmd = 0xefU +#define STC_SET_MASTER_MODE(D) {*pcmd = 0x17U; *pdata=(D);} +#define STC_MASTER_MODE (*pcmd = 0x17U, *pdata) + +#define STC_CTR_INIT(MODE,LOAD,HOLD)\ +{*pcmd = CHIPCHAN+1; *pdata = (MODE); *pdata = (LOAD); *pdata= (HOLD);} + +#define STC_CTR_READ(MODE,LOAD,HOLD)\ +{*pcmd = CHIPCHAN+1; (MODE) = *pdata; (LOAD) = *pdata; (HOLD) = *pdata;} + +#define STC_SET_TC(D) *pcmd = 0xe0U | ((D)?8:0)|(CHIPCHAN+1U) + +#define STC_LOAD *pcmd = 0x40U | 1<<(CHIPCHAN) +#define STC_STEP *pcmd = 0xf0U | (CHIPCHAN+1U) +#define STC_ARM *pcmd = 0x20U | 1< +#include + +#include + +struct pulse{ +double offset; +double width; +}; + + + +/* + * + * time_driver_read() + * + * + */ +time_driver_read + ( + card, /* 0 through ... */ + channel, /* 0 through chans on card */ + card_type, /* module type as stored in GTA DB */ + int_source, /* (TRUE)External/ (FALSE)Internal source */ + preset, /* TRUE or COMPLEMENT logic */ + pulses, /* ptr to array of structure describing pulses */ + npulses, /* N elements found */ + npulmax /* N elements in the caller's array */ + ) +unsigned int card; +unsigned int channel; +unsigned int card_type; +unsigned int *int_source; +int *preset; +struct pulse *pulses; +unsigned int *npulses; +unsigned int npulmax; +{ + int status; + + *npulses=0; + + switch(card_type){ + case MZ8310: + if(npulmax<1) + return ERROR; + status = mz8310_one_shot_read ( + preset, + &pulses->offset, + &pulses->width, + card, + channel, + int_source + ); + if(status==0) + *npulses=1; + + return status; + case VXI_AT5_TIME: + if(npulmax<1) + return ERROR; + status = at5vxi_one_shot_read ( + preset, + &pulses->offset, + &pulses->width, + card, + channel, + int_source + ); + if(status==0) + *npulses=1; + + return status; + case DG535: + break; + default: + break; + } + logMsg("time_driver: No support for that type of timing card\n"); + return ERROR; +} + + +time_driver ( + card, /* 0 through ... */ + channel, /* 0 through chans on card */ + card_type, /* module type as stored in GTA DB */ + int_source, /* (TRUE)External/ (FALSE)Internal source */ + preset, /* TRUE or COMPLEMENT logic */ + pulses, /* ptr to array of structure describing pulses */ + npulses, /* N elements in this array */ + eventrtn, /* routine to run on events */ + eventrtnarg /* argument to above rtn */ + ) +unsigned int card; +unsigned int channel; +unsigned int card_type; +unsigned int int_source; +int preset; +struct pulse *pulses; +unsigned int npulses; +void (*eventrtn)(); +unsigned int eventrtnarg; +{ + + switch(card_type){ + case MZ8310: + if(npulses != 1) + return ERROR; + return mz8310_one_shot ( + preset, + pulses->offset, + pulses->width, + card, + channel, + int_source, + eventrtn, + eventrtnarg + ); + case VXI_AT5_TIME: + if(npulses != 1) + return ERROR; + return at5vxi_one_shot ( + preset, + pulses->offset, + pulses->width, + card, + channel, + int_source, + eventrtn, + eventrtnarg + ); + case DG535: + break; + default: + break; + } + logMsg("time_driver: No support for that type of timing card\n"); + return ERROR; +} + + + +time_test() +{ + unsigned int card=0; + unsigned int channel=0; + unsigned int card_type=MZ8310; + unsigned int int_source=1; + int preset=1; + static struct + pulse pulses={.00001,.00001}; + unsigned int npulses = 1; + + unsigned int t_int_source; + int t_preset; + struct pulse t_pulses; + unsigned int t_npulses; + + int status; + + status = + time_driver ( + card, /* 0 through ... */ + channel, /* 0 through chans on card */ + card_type, /* module type as stored in GTA DB */ + int_source, /* (TRUE)External/ (FALSE)Internal source */ + preset, /* TRUE or COMPLEMENT logic */ + &pulses, /* ptr to array of structure describing pulses */ + npulses, /* N elements in this array */ + NULL, /* routine to run on events */ + NULL /* argument to above rtn */ + ); + if(status==ERROR) + return ERROR; + + + status = + time_driver_read( + card, /* 0 through ... */ + channel, /* 0 through chans on card */ + card_type, /* module type as stored in GTA DB */ + &t_int_source, /* (TRUE)External/ (FALSE)Internal source */ + &t_preset, /* TRUE or COMPLEMENT logic */ + &t_pulses, /* ptr to array of structure describing pulses */ + &t_npulses, /* N elements found */ + 1 /* max N elements in this array */ + ); + if(status==ERROR) + return ERROR; + + + logMsg( "wrote: preset %x internal-clk %x delay %f width %f \n", + preset, + int_source, + pulses.offset, + pulses.width); + logMsg( "read: preset %x internal-clk %x delay %f width %f count %x\n", + t_preset, + t_int_source, + t_pulses.offset, + t_pulses.width, + t_npulses); + return OK; +} diff --git a/src/drv/old/drvVmi4100.c b/src/drv/old/drvVmi4100.c new file mode 100644 index 000000000..3df1ebbec --- /dev/null +++ b/src/drv/old/drvVmi4100.c @@ -0,0 +1,203 @@ +/* base/src/drv $Id$ */ +/* + * subroutines that are used to interface to the vme analog output cards + * + * Author: Bob Dalesio + * Date: 9-26-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 + * + * Modification Log: + * ----------------- + * .01 11-09-88 lrd add LED light status to Ziomek-085 + * .02 02-08-89 lrd addresses from module_types.h + * use 085 structure + * .03 03-17-89 lrd add ao_read routine + * .04 03-29-89 lrd return correct status on write + * .05 11-20-89 joh added call to the at5 vxi driver + * .06 06-08-90 mrk fixed bug (R Daly found) for VMI4100 + * .07 10-31-91 bg broke vmi4100 driver out of ao_driver.c + * broke vmi4100 code out of io_report and + * created vmi400_io_report() + * .08 01-10-92 bg Added levels to io_report and warning message + * that raw values cannot be read from vmi4100 + * card even it the level is 1. + * .09 08-05-92 joh arguments to init routine now conform with the + * standard + * .10 08-05-92 joh added EPICS driver dispatch table + * .11 08-05-92 joh moved parameters from ao_driver.h to here + * .12 08-11-92 joh num of cards now dyn configurable + * .13 08-24-93 mrk removed use of variable base_addr + */ + +static char *sccsID = "@(#)drvVmi4100.c 1.5\t8/27/93"; + +#include +#include +#include +#include +#include "module_types.h" + +/* VMIVME 4100 defines */ +#define MAX_AO_VMI_CARDS (ao_num_cards[VMI4100]) +#define VMI_MAXCHAN (ao_num_channels[VMI4100]) +#define VMI_ENABLE_OUT 0xc100 /*Fail LED off, enable P3 output.*/ + +/* memory structure of the Xycom 4100 Interface */ + +union aoVMI{ + unsigned short csr; + unsigned short data[16]; +}; + +long vmi4100_io_report(); +long vmi4100_init(); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvVmi4100={ + 2, + vmi4100_io_report, + vmi4100_init}; + +LOCAL +unsigned short **pao_vmi4100; + +LOCAL +int vmi4100_addr; + + +/* + * vmi4100_init + * + * intialize the VMI analog outputs + */ +long vmi4100_init() +{ + register unsigned short **pcards_present; + short shval; + int status; + register union aoVMI *pcard; + register short i; + + pao_vmi4100 = (unsigned short **) + calloc(MAX_AO_VMI_CARDS, sizeof(*pao_vmi4100)); + if(!pao_vmi4100){ + return ERROR; + } + + pcards_present = pao_vmi4100; + + if ((status = sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO,ao_addrs[VMI4100], &vmi4100_addr)) != OK){ + printf("Addressing error in vmi4100 driver\n"); + return ERROR; + } + + pcard = (union aoVMI *)((int)vmi4100_addr); + + + /* mark each card present into the card present array */ + for (i = 0; i < ao_num_cards[VMI4100]; i++, pcard+= VMI_MAXCHAN, pcards_present += 1) { + + if (vxMemProbe(pcard,READ,sizeof(short),&shval) == OK) { + *pcards_present = (unsigned short *)pcard; + pcard->csr = VMI_ENABLE_OUT; + } + else{ + *pcards_present = 0; + + } + } + + return OK; +} + + +/* + * vmi4100_driver + * + * VMI4100 analog output driver + */ +vmi4100_driver(card,chan,prval,prbval) +register unsigned short card; +register unsigned short chan; +unsigned short *prval; +unsigned short *prbval; +{ + register union aoVMI *paoVMI; + + /* check on the card and channel number as kept in module_types.h */ + + if ((paoVMI= (union aoVMI *)pao_vmi4100[card]) == 0) + return(-1); + paoVMI->data[chan] = *prval; + + return (0); +} + + +/* + * vmi4100_read + * + * VME analog output driver + */ +vmi4100_read(card,chan,pval) +register unsigned short card; +register unsigned short chan; +unsigned short *pval; +{ + register unsigned short *pcard; + + /* check on the card and channel number as kept in module_types.h */ + + *pval = 0; /* can't read the VMIC 4100 - good design! */ + return (-1); +} + +/* + * vmi4100_io_report + * + * VME analog output driver + */ + +long vmi4100_io_report(level) + short int level; + { + register int i; + + for (i = 0; i < MAX_AO_VMI_CARDS; i++){ + if (pao_vmi4100[i]){ + printf("AO: VMI4100: card %d ",i); + if(level >0){ + printf("VMI4100 card cannot be read.\n"); + } + else + printf("\n"); + + } + } + + return OK; + } + diff --git a/src/drv/old/drvXy010.c b/src/drv/old/drvXy010.c new file mode 100644 index 000000000..b280ad0d8 --- /dev/null +++ b/src/drv/old/drvXy010.c @@ -0,0 +1,179 @@ +/* + * initialize the Xycom SRM010 bus controller card + */ +/* base/src/drv $Id$ */ +/* Author: Betty Ann Gunther + * Date: 06-30-29 + * Initialize xy010 bus controller + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .00 08-11-92 joh Verify the xy010's id to prevent confusion + * with the mv167's GCSR which with an unmodified + * sysLib.c will show up in the xy010's addr + * space + * .01 08-11-92 joh Moved base addr to module_types.h + * ... + */ + +static char *sccsID = "@(#)drvXy010.c 1.3\t6/3/93"; + +#include +#include + +#include +#include + + +/* + * These will hopefully go away + * as the drivers become more autonomous + */ + +static long xy010_id_check(char *); +static long xy010_io_report(int); +static long xy010_init(void); +static long xy010_map(void); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvXy010={ + 2, + xy010_io_report, + xy010_init}; + + +#define CSR_ADDR 0x81 +#define XY010_ID "VMEIDXYC010" +#define XY_LED 0x3 /* set the Xycom status LEDs to OK */ + +LOCAL char *xy010_addr; + + +/* + * initialize the Xycom SRM010 bus controller card + */ +long +xy010_init() +{ + char *pctlreg; + + if(xy010_map()<0){ + return ERROR; + } + + if(xy010_id_check(xy010_addr)<0){ + return OK; + } + + /* Pointer to status control register. */ + pctlreg = xy010_addr + CSR_ADDR; + *pctlreg = XY_LED; + + return OK; +} + + +/* + * + * xy010_map() + * + * + */ +LOCAL +long xy010_map() +{ + int status; + + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + xy010ScA16Base, + &xy010_addr); + + if (status < 0){ + printf("%s: xy010 A16 base addr map failed\n", __FILE__); + return ERROR; + } + + return OK; +} + + +/* + * + * xy010_id_check() + * + * + */ +LOCAL +long xy010_id_check(pBase) +char *pBase; +{ + char *pID; + char *pCmp; + char ID; + int status; + + pID = pBase; + pCmp = XY010_ID; + while(*pCmp){ + pID++; /* ID chars stored at odd addr */ + status = vxMemProbe(pID, READ, sizeof(ID), &ID); + if(status < 0){ + return ERROR; + } + if(*pCmp != ID){ + return ERROR; + } + pID++; + pCmp++; + } + return OK; +} + + +/* + * + * xy010_io_report() + * + * + */ +long xy010_io_report(int level) +{ + char id; + + if(xy010_map()<0){ + return ERROR; + } + + if (xy010_id_check(xy010_addr) == OK) { + printf("SC: XY010:\tcard 0\n"); + } + + return OK; +} + + diff --git a/src/drv/old/drvXy210.c b/src/drv/old/drvXy210.c new file mode 100644 index 000000000..e02c61ebc --- /dev/null +++ b/src/drv/old/drvXy210.c @@ -0,0 +1,185 @@ +/* xy210_driver.c */ +/* base/src/drv $Id$ */ +/* + * subroutines that are used to interface to the binary input cards + * + * Author: Bob Dalesio + * Date: 6-13-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 + * + * Modification Log: + * ----------------- + * .01 02-09-89 lrd moved I/O addresses to module_types.h + * .02 11-20-89 joh added call to at5vxi driver + * .03 09-11-91 bg added bi_io_report + * .04 03-09-92 bg added levels to xy210_io_report. Gave + * xy210_io_report the ability to read raw + * values from card if level > 1 + * .05 08-10-92 joh merged include file + * .06 08-25-92 mrk made masks a macro + * .07 08-25-92 mrk replaced bi_driver by xy210_driver + * .08 09-15-93 mrk Made report shorter + */ + +/* + * Code Portions: + * + * bi_driver_init Finds and initializes all binary input cards present + * bi_driver Interfaces to the binary input cards present + */ + + +#include +#include +#include +#include + +static long report(); +static long init(); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvXy210={ + 2, + report, + init}; + +static long report(level) + int level; +{ + xy210_io_report(level); + return(0); +} + +static long init() +{ + + xy210_driver_init(); + return(0); +} + + +static char SccsId[] = "@(#)drvXy210.c 1.5\t9/20/93"; + +#define MAX_XY_BI_CARDS (bi_num_cards[XY210]) + +/* Xycom 210 binary input memory structure */ +/* Note: the high and low order words are switched from the io card */ +struct bi_xy210{ + char begin_pad[0xc0]; /* nothing until 0xc0 */ +/* unsigned short csr; */ /* control status register */ +/* char pad[0xc0-0x82]; */ /* get to even 32 bit boundary */ + unsigned short low_value; /* low order 16 bits value */ + unsigned short high_value; /* high order 16 bits value */ + char end_pad[0x400-0xc0-4]; /* pad until next card */ +}; + + +/* pointers to the binary input cards */ +struct bi_xy210 **pbi_xy210s; /* Xycom 210s */ + +/* test word for forcing bi_driver */ +int bi_test; + +static char *xy210_addr; + + +/* + * BI_DRIVER_INIT + * + * intialization for the binary input cards + */ +xy210_driver_init(){ + int bimode; + int status; + register short i; + struct bi_xy210 *pbi_xy210; + + pbi_xy210s = (struct bi_xy210 **) + calloc(MAX_XY_BI_CARDS, sizeof(*pbi_xy210s)); + if(!pbi_xy210s){ + return ERROR; + } + + /* initialize the Xycom 210 binary input cards */ + /* base address of the xycom 210 binary input cards */ + if ((status = sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO,bi_addrs[XY210],&xy210_addr)) != OK){ + printf("Addressing error in xy210 driver\n"); + return ERROR; + } + pbi_xy210 = (struct bi_xy210 *)xy210_addr; + /* determine which cards are present */ + for (i = 0; i high_value << 16) /* high */ + + pbi_xy210s[card]->low_value; /* low */ + + /* apply mask */ + + *prval = work & mask; + + return (0); +} + +void xy210_io_report(level) + short int level; + { + int card; + unsigned int value; + + for (card = 0; card < bi_num_cards[XY210]; card++){ + if (pbi_xy210s[card]){ + value = (pbi_xy210s[card]->high_value << 16) /* high */ + + pbi_xy210s[card]->low_value; /* low */ + printf("BI: XY210: card %d value=0x%08.8x\n",card,value); + } + } +} diff --git a/src/drv/old/drvXy220.c b/src/drv/old/drvXy220.c new file mode 100644 index 000000000..db17fb713 --- /dev/null +++ b/src/drv/old/drvXy220.c @@ -0,0 +1,220 @@ +/* xy220_driver.c */ +/* base/src/drv $Id$ */ +/* + * subroutines that are used to interface to the binary output cards + * + * Author: Bob Dalesio + * Date: 5-26-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 + * + * Modification Log: + * ----------------- + * .01 10-31-91 bg broke xy220 driver out of bo_driver.c + * broke xy220 code out of io_report and + * created xy220_io_report(). + * Added sysBusToLocalAdrs. + * .02 02-03-92 bg Gave xy220_io_report the ability to + * read raw values from card if level > 1. + * .03 08-10-92 joh merged potions of bo_driver.h + * .04 08-25-92 mrk made masks a macro + */ + +static char SccsId[] = "@(#)drvXy220.c 1.6\t9/20/93"; + +/* + * Code Portions: + * + * bo_drv_init Finds and initializes all binary output cards present + * bo_driver Interfaces to the binary output cards present + */ + +#include +#include +#include "module_types.h" +#include + +static long report(); +static long init(); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvXy220={ + 2, + report, + init}; +static long report(level) + int level; +{ + xy220_io_report(level); + return(0); +} + +static long init() +{ + + xy220_driver_init(); + return(0); +} + + +#define XY_LED 0x3 /* set the Xycom status LEDs to OK */ +#define XY_SOFTRESET 0x10 /* reset the IO card */ + +/* maximum number of VME binary output cards per IOC */ +#define MAX_XY220_BO_CARDS bo_num_cards[XY220] + +/* Xycom 220 binary output memory structure */ +struct bo_xy220{ + char begin_pad[0x80]; /* nothing until 0x80 */ + short csr; /* control status register */ + unsigned short low_value; /* low order 16 bits value */ + unsigned short high_value; /* high order 16 bits value */ + char end_pad[0x400-0x80-6]; /* pad until next card */ +}; + + +/* pointers to the binary output cards */ +struct bo_xy220 **pbo_xy220s; /* Xycom 220s */ + + +/* + * XY220_DRIVER_INIT + * + * intialization for the xy220 binary output cards + */ +int xy220_driver_init(){ + int bomode; + int status; + register short i; + struct bo_xy220 *pbo_xy220; + + pbo_xy220s = (struct bo_xy220 **) + calloc(MAX_XY220_BO_CARDS, sizeof(*pbo_xy220s)); + if(!pbo_xy220s){ + return ERROR; + } + + /* initialize the Xycom 220 binary output cards */ + /* base address of the xycom 220 binary output cards */ + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + bo_addrs[XY220], + &pbo_xy220); + if (status != OK){ + printf("%s: xy220 A16 base map failure\n", __FILE__); + return ERROR; + } + + /* determine which cards are present */ + for (i = 0; i < MAX_XY220_BO_CARDS; i++,pbo_xy220++){ + if (vxMemProbe(pbo_xy220,READ,sizeof(short),&bomode) == OK){ + pbo_xy220s[i] = pbo_xy220; + pbo_xy220s[i]->csr = XY_SOFTRESET; + pbo_xy220s[i]->csr = XY_LED; + }else{ + pbo_xy220s[i] = 0; + } + } + return(0); +} + +/* + * XY220_DRIVER + * + * interface to the xy220 binary outputs + */ + +int xy220_driver(card,val,mask) +register unsigned short card; +register unsigned int *val; +unsigned int mask; +{ + register unsigned int work; + + /* verify card exists */ + if (!pbo_xy220s[card]) + return (-1); + + /* use structure to handle high and low short swap */ + /* get current output */ + work = (pbo_xy220s[card]->high_value << 16) + + pbo_xy220s[card]->low_value; + + /* alter specified bits */ + work = (work & ~mask) | ((*val) & mask); + + /* write new output */ + pbo_xy220s[card]->high_value = (unsigned short)(work >> 16); + pbo_xy220s[card]->low_value = (unsigned short)work; + +return (0); +} + +/* + * xy220_read + * + * read the binary output + */ +int xy220_read(card,mask,pval) +register unsigned short card; +unsigned int mask; +register unsigned int *pval; +{ + register unsigned int work; + + /* verify card exists */ + if (!pbo_xy220s[card]) + return (-1); + /* readback */ + *pval = (pbo_xy220s[card]->high_value << 16) + pbo_xy220s[card]->low_value; + + *pval &= mask; + + return(0); + +} + +#define masks(K) ((1<high_value << 16) /* high */ + + pbo_xy220s[card]->low_value; /* low */ + printf("BO: XY220: card %d value=0x%08.8x\n",card,value); + } + } + + } diff --git a/src/drv/old/drvXy240.c b/src/drv/old/drvXy240.c new file mode 100644 index 000000000..c38c5e950 --- /dev/null +++ b/src/drv/old/drvXy240.c @@ -0,0 +1,526 @@ +/* xy240_driver.c */ +/* base/src/drv $Id$ */ +/* + * routines used to test and interface with Xycom240 + * digital i/o module + * + * Author: B. Kornke + * Date: 11/20/91 + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 06-25-92 bg Added driver to code. Added xy240_io_report + * to it. Added copyright disclaimer. + * .02 08-10-92 joh merged xy240_driver.h into this source + * .03 08-11-92 joh fixed use of XY240 where XY240_BI or XY240_BO + * should have been used + * .04 08-11-92 joh now allows for runtime reconfiguration of + * the addr map + * .05 08-25-92 mrk added DSET; made masks a macro + * .06 08-26-92 mrk support epics I/O event scan + * .07 08-26-92 joh task params from task params header + * .08 08-26-92 joh removed STDIO task option + * .09 08-26-92 joh increased stack size for V5 + * .10 08-26-92 joh increased stack size for V5 + * .11 08-27-92 joh fixed no status return from bo driver + * .12 09-03-92 joh fixed wrong index used when testing for card + * present + * .13 09-03-92 joh fixed structural problems in the io + * report routines which caused messages to + * be printed even when no xy240's are present + * .14 09-17-92 joh io report now tabs over detailed info + * .15 09-18-92 joh documentation + * .16 08-02-93 mrk Added call to taskwdInsert + * .17 08-04-93 mgb Removed V5/V4 and EPICS_V2 conditionals + */ + +#include "vxWorks.h" +#include "taskLib.h" +#include "vme.h" +#include "module_types.h" +#include "task_params.h" +#include +#include +#include +#include + +#define XY240_ADDR0 (bi_addrs[XY240_BI]) +#define XY240_MAX_CARDS (bi_num_cards[XY240_BI]) +#define XY240_MAX_CHANS (bi_num_channels[XY240_BI]) + +#define masks(K) ((1<dptr; + +} +#endif + +/* + * + *dio_scan + * + *task to check for change of state + * + */ +dio_scan() + +{ + int i; + int first_scan,first_scan_complete; + + for(;;){ + if(interruptAccept) break; + taskDelay(vxTicksPerSecond/30); + } + first_scan_complete = FALSE; + first_scan = TRUE; + for (;;) + { + for (i = 0; i < XY240_MAX_CARDS; i++) + { + if (dio[i].dptr) + if (((dio[i].dptr->port0_1) ^ (dio[i].sport0_1)) || + ((dio[i].dptr->port2_3) ^ (dio[i].sport2_3)) + || first_scan) + { + /* printf("io_scanner_wakeup for card no %d\n",i); */ + scanIoRequest(dio[i].ioscanpvt); + dio[i].sport0_1 = dio[i].dptr->port0_1; + dio[i].sport2_3 = dio[i].dptr->port2_3; + } + } + if (first_scan){ + first_scan = 0; + first_scan_complete = 1; + } + taskDelay(vxTicksPerSecond/30); + } +} + + + +/*DIO DRIVER INIT + * + *initialize xy240 dig i/o card + */ +xy240_init() +{ + short junk; + register short i; + struct dio_xy240 *pdio_xy240; + int tid; + int status; + int at_least_one_present = FALSE; + + /* + * allow for runtime reconfiguration of the + * addr map + */ + dio = (struct dio_rec *) calloc(XY240_MAX_CARDS, sizeof(*dio)); + if(!dio){ + return ERROR; + } + + status = sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO,XY240_ADDR0,&pdio_xy240); + if (status != OK){ + printf("%s: Unable to map the XY240 A16 base addr\n", __FILE__); + return ERROR; + } + + for (i = 0; i < XY240_MAX_CARDS; i++, pdio_xy240++){ + + if (vxMemProbe(pdio_xy240,READ,2,&junk) != OK){ + dio[i].dptr = 0; + continue; + } + + /* + * register initialization + */ + pdio_xy240->csr = 0x3; + pdio_xy240->iclr_vec = 0x00; /*clear interrupt input register latch*/ + pdio_xy240->flg_dir = 0xf0; /*ports 0-3,input;ports 4-7,output*/ + dio[i].sport2_3 = pdio_xy240->port2_3; /*read and save high values*/ + dio[i].dptr = pdio_xy240; + at_least_one_present = TRUE; + scanIoInit(&dio[i].ioscanpvt); + } + + if (at_least_one_present) + { + if ((tid = taskNameToId(XY240_NAME)) != ERROR){ + taskwdRemove(tid); + taskDelete(tid); + } + + status = taskSpawn( + XY240_NAME, + XY240_PRI, + XY_240_OPT, + XY_240_STACK, + dio_scan); + if (status == ERROR){ + printf("Unable to create XY240 scan task\n"); + } + else taskwdInsert(status,NULL,NULL); + } + + + + return OK; + +} + +xy240_getioscanpvt(card,scanpvt) +short card; +IOSCANPVT *scanpvt; +{ + if ((card >= XY240_MAX_CARDS) || (!dio[card].dptr)) return(0); + *scanpvt = dio[card].ioscanpvt; + return(0); +} + + +/* + * XY240_BI_DRIVER + * + *interface to binary inputs + */ + +xy240_bi_driver(card,mask,prval) +register short card; +unsigned int mask; +register unsigned int *prval; +{ + register unsigned int work; + + if ((card >= XY240_MAX_CARDS) || (!dio[card].dptr)) + return -1; + work = (dio[card].dptr->port0_1 << 16) + + dio[card].dptr->port2_3; + *prval = work & mask; + + return(0); +} + +/* + * + *XY240_BO_READ + * + *interface to binary outputs + */ + +xy240_bo_read(card,mask,prval) +register short card; +unsigned int mask; +register unsigned int *prval; +{ + register unsigned int work; + + if ((card >= XY240_MAX_CARDS) || (!dio[card].dptr)){ + return -1; + } + + /* printf("%d\n",dio[card].num); */ + work = (dio[card].dptr->port4_5 << 16) + + dio[card].dptr->port6_7; + + *prval = work &= mask; + + return(0); + } + +/* XY240_DRIVER + * + *interface to binary outputs + */ + +xy240_bo_driver(card,val,mask) +register short card; +unsigned int mask; +register unsigned int val; +{ + register unsigned int work; + + if ((card >= XY240_MAX_CARDS) || (!dio[card].dptr)) + return ERROR; + + /* use structure to handle high and low short swap */ + /* get current output */ + + work = (dio[card].dptr->port6_7 << 16) + + dio[card].dptr->port4_5; + + work = (work & ~mask) | (val & mask); + + dio[card].dptr->port6_7 = (unsigned short)(work >> 16); + dio[card].dptr->port4_5 = (unsigned short)work; + + return OK; + } + + +/*dio_out + * + *test routine for xy240 output + */ +dio_out(card,port,val) +short card,port,val; +{ + + if ((card > XY240_MAX_CARDS-1)) /*test to see if card# is allowable*/ + { + printf("card # out of range\n"); + return -1; + } + else if (!dio[card].dptr) /*see if card exists*/ + { + printf("can't find card %d\n", card); + return -2; + } + else if ((port >7) || (port <4)) /*make sure they're output ports*/ + { + printf("use ports 4-7\n"); + return -3; + } + else if (port == 4) + { + dio[card].dptr->port4_5 = val<< 8; + return -4; + } + else if (port == 5) + { + dio[card].dptr->port4_5 = val; + return -5; + } + else if (port == 6) + { + dio[card].dptr->port6_7 = val<< 8; + return -6; + } +else if (port == 7) + { + dio[card].dptr->port6_7 = val; + return -7; + } +else{ + printf("use ports 4-7\n"); + return -8; + } +} + +/*XY240_WRITE + * + *command line interface to test bo driver + * + */ +xy240_write(card,val) + short card; + unsigned int val; + { + return xy240_bo_driver(card,val,0xffffffff); + } + + + +long +xy240_io_report(level) +short int level; +{ + int card; + + for (card = 0; card < XY240_MAX_CARDS; card++){ + + if(dio[card].dptr){ + printf("B*: XY240:\tcard %d\n",card); + if (level >= 1){ + xy240_bi_io_report(card); + xy240_bo_io_report(card); + } + } + } + +} + + +void xy240_bi_io_report(int card) +{ + short int num_chans,j,k,l,m,status; + int ival,jval,kval,lval,mval; + unsigned int *prval; + + num_chans = XY240_MAX_CHANS; + + if(!dio[card].dptr){ + return; + } + + printf("\tXY240 BINARY IN CHANNELS:\n"); + for( j=0,k=1,l=2,m=3; + j < num_chans,k < num_chans, l< num_chans, m < num_chans; + j+=IOR_MAX_COLS,k+= IOR_MAX_COLS,l+= IOR_MAX_COLS, m += IOR_MAX_COLS){ + + + if(j < num_chans){ + xy240_bi_driver(card,masks(j),&jval); + if (jval != 0) + jval = 1; + printf("\tChan %d = %x\t ",j,jval); + } + if(k < num_chans){ + xy240_bi_driver(card,masks(k),&kval); + if (kval != 0) + kval = 1; + printf("Chan %d = %x\t ",k,kval); + } + if(l < num_chans){ + xy240_bi_driver(card,masks(l),&lval); + if (lval != 0) + lval = 1; + printf("Chan %d = %x \t",l,lval); + } + if(m < num_chans){ + xy240_bi_driver(card,masks(m),&mval); + if (mval != 0) + mval = 1; + printf("Chan %d = %x \n",m,mval); + } + } +} + + +void xy240_bo_io_report(int card) +{ + short int num_chans,j,k,l,m,status; + int ival,jval,kval,lval,mval; + unsigned int *prval; + + num_chans = XY240_MAX_CHANS; + + if(!dio[card].dptr){ + return; + } + + printf("\tXY240 BINARY OUT CHANNELS:\n"); + + for( j=0,k=1,l=2,m=3; + j < num_chans,k < num_chans, l < num_chans,m < num_chans; + j+=IOR_MAX_COLS,k+= IOR_MAX_COLS,l+= IOR_MAX_COLS, m += IOR_MAX_COLS){ + + if(j < num_chans){ + xy240_bo_read(card,masks(j),&jval); + if (jval != 0) + jval = 1; + printf("\tChan %d = %x\t ",j,jval); + } + if(k < num_chans){ + xy240_bo_read(card,masks(k),&kval); + if (kval != 0) + kval = 1; + printf("Chan %d = %x\t ",k,kval); + } + if(l < num_chans){ + xy240_bo_read(card,masks(l),&lval); + if (lval != 0) + lval = 1; + printf("Chan %d = %x \t",l,lval); + } + if(m < num_chans){ + xy240_bo_read(card,masks(m),&mval); + if (mval != 0) + mval = 1; + printf("Chan %d = %x \n",m,mval); + } + } +} + diff --git a/src/drv/old/drvXy566.c b/src/drv/old/drvXy566.c new file mode 100644 index 000000000..7d2d5a706 --- /dev/null +++ b/src/drv/old/drvXy566.c @@ -0,0 +1,1148 @@ +/* base/src/drv $Id$ */ +/* drvXy566.c - Driver Support Routines for xy566 + * + * Author: Bob Dalesio + * Date: 6-13-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 + * + * Modification Log: + * ----------------- + * .00 09-14-88 lrd check for IGEN card present before initing + * .01 9-27-88 lrd removed test code + * .02 09-27-88 lrd removed call to xy_mem_init + * .03 10-04-88 lrd remove IGEN support + * .04 10-07-88 lrd add support for the Xycom 085 arm mechanism + * added external latched AI and made + * others scan continuously + * .05 11-10-88 lrd change addresses per HW Tech Note #2 + * .06 02-08-89 lrd moved module addresses into a table in + * module_types.h from ai_driver.h + * .07 02-24-89 lrd modify for vxWorks 4.0 + * changed sysSetBCL to sysIntEnable + * .08 04-17-89 lrd add callback mechanism for data take complete + * .09 05-10-89 lrd increased performance for xycom 566 interface + * by keeping the address of the memory buffers + * thus removing the need to calculate on each + * read + * .10 07-27-89 lrd modified to use channel 0 not channel 1 + * .11 11-20-89 joh added call to the at5vxi ai driver + * .12 02-15-90 lrd modified for new 085 card + * 02/04/91 ges Change taskDelay from 6 to 2 in + * "xy566DoneTask". Allows rearm and data reads + * for successive waveform scans up thru + * 10 Hz rates. + * .13 08-27-91 bg broke the 566 driver out of ai_driver.c + * and moved it to this file. Moved io_report code + * to this file. + * added arguments and raw value read out + * for analog in cards. + * .14 10/30/91 bg Combined the xy566 waveform driver with the + * xy566 analog in drivers. Changed addressing to + * use sysBusToLocalAddrs and VME_AM_STD_SUP or + * VME_AM_SUP_SHORT_IO + * .15 11-30-91 bg Added sysBusToLocalAdrs to both ai and + * waveform sections. + * .16 02-05-92 bg Changed io_report so it has an argument + * level and so if the level > 1, the raw + * value from the card will be printed out + * for analog ins only. + * .17 03/20/92 bg Added the argument level to io_report, + * but so far it doesn't do anything. Will + * add ability to report ability to read out + * raw value if there is a demand. + * .18 08-10-92 joh cleaned up the merge of the xy566 wf and ai + * drivers + * .19 08-25-92 mrk replaced call to ai_driver by ai_xy566_driver + * .20 08-26-92 mrk support epics I/O event scan + * .21 08-27-92 joh fixed routines which return with and without + * status + * .22 08-27-92 joh fixed nonexsistant EPICS init + * .23 08-02-93 mrk Added call to taskwdInsert + * .24 08-04-93 mgb Removed V5/V4 and EPICS_V2 conditionals + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +static char SccsId[] = "@(#)drvXy566.c 1.14\t8/4/93 "; + +/* + * Code Portions + * + * xy566DoneTask Task to handle data take completion for the 566 waveform + * xy566_init Initializes the 566 waveform cards + * senb/senw Writes to the 566 where the call provides a req'd delay + */ + +/* If any of the following does not exist replace it with #define <> NULL */ + + +static long report(); +static long init(); + +#if 0 +static long xy566_io_report(); +static long ai_566_init(); +#endif + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvXy566={ + 2, + report, + init}; + +static long init() +{ + ai_566_init(); + xy566_init(); + return(0); +} + +static long report() +{ + ai_xy566_io_report(); + xy566_io_report(); +} + +#define MAX_SE_CARDS (ai_num_cards[XY566SE]) +#define MAX_DI_CARDS (ai_num_cards[XY566DI]) +#define MAX_DIL_CARDS (ai_num_cards[XY566DIL]) + +#define XY566_MEM_INCR 0x10000 /* size of data memory area */ + +/* memory structure of the 566 interface */ +struct ai566 { /* struct XVME 566 */ + char dum[0x80]; /* odd bytes 1 - 3f contain + module identification */ + unsigned short a566_csr; /* control status register */ + unsigned char soft_start; /* software start register */ + unsigned char int_vect; /* interrupt vector register */ + unsigned short dram_ptr; /* pointer to data ram */ + char dum1; /* unused */ + unsigned char sram_ptr; /* sequence ram pointer */ + char dum2[0xc0 - 0x88]; + unsigned short stc_data; /* timing chip data port */ + unsigned short stc_control; /* timing chip control port */ + char dum3[0x101 - 0xc4]; + unsigned char gram_base; /* base of gain ram 101,103 to 13f */ + char dum5[0x201 - 0x102]; + unsigned char sram_base; /* base of sequence ram 210,203 to 3ff */ + char dum6[0x3ff -0x202]; + unsigned char card_number; /* logical card number */ +}; + +/* memory structure of the 566 interface */ +struct wf085 { /* struct XVME 566 */ + char dum[0x80]; /* odd bytes 1 - 3f contain + module identification */ + unsigned short csr; /* control status register */ + unsigned char soft_start; /* software start register */ + unsigned char int_vect; /* interrupt vector register */ + unsigned short dram_ptr; /* pointer to data ram */ + char dum1; /* unused */ + unsigned char sram_ptr; /* sequence ram pointer */ + char dum2[0xc0 - 0x88]; + unsigned short stc_data; /* timing chip data port */ + unsigned short stc_control; /* timing chip control port */ + char dum3[0x101 - 0xc4]; + unsigned char gram_base; /* base of gain ram 101,103,.. + ... to 13f */ + char dum5[0x201 - 0x102]; + unsigned char sram_base; /* base of sequence ram 210,203, + ... to 3ff */ + char dum6[0x400 -0x202]; +}; + +/* arrays which keep track of which cards are found at initialization */ +struct ai566 **pai_xy566se; +struct ai566 **pai_xy566di; +struct ai566 **pai_xy566dil; +unsigned short **pai_xy566se_mem; +unsigned short **pai_xy566di_mem; +unsigned short **pai_xy566dil_mem; +static IOSCANPVT *paioscanpvt; + + +/* reset the counter interrupt 0x8000 */ +/* reset the sequence interrupt 0x2000 */ +/* enable the sequence interrupt 0x1000 */ +/* reset the trigger clock interrupt 0x0800 */ +/* enable the sequence controller 0x0100 */ +/* read values into first 32 words on each read 0x0040 */ +/* read in sequential mode (bit 0x0020 == 0) 0x0000 */ +/* interrupt enable 0x0008 */ +/* leds green-on red-off 0x0003 */ + +#define XY566L_CSR 0xb94b +#define XY566_INT_LEVEL 6 + + +/* max card and channel definitions move to module types.h */ +#define MAX_WF_XY_CARDS (wf_num_cards[XY566WF]) + +/* card interface */ +#define WF_XY566_BASE (wf_addrs[XY566WF]) /* XYCOM 566 waveform */ +#define WF_XY085_BASE (wf_armaddrs[XY566WF]) /* XYCOM 085 arm */ + +/* Data RAM area into which the 566 stores the latched data */ +/* This needs to be different for each type of IO card */ +#define WF_XY566_MEMBASE ((unsigned short *)0x1080000) + +/* figure out what these are */ +#define WF566_MEM_INCR 0x10000 /* 65535 bytes per waveform */ +#define WF566_VNUM 0xf1 /* this is currently incorrect */ + + +#define WF_READ 0x00 /* read a waveform */ +#define WF_ARM 0x01 /* arm a waveform */ +#define WF_LATCH 0x02 /* latch a waveform */ +#define WF_SETUP 0x03 /* set up a waveform */ + +/* xycom 085 encoder pulse mechanism */ +#define XY_ARM 0x10 /* arm the encoder pulse circuitry */ +#define XY_BUSY 0x20 /* indicates the waveform is still being taken */ +#define XY_LED 0x3 /* set the Xycom status LEDs to OK */ +#define XY_SOFTRESET 0x10 /* reset the IO card */ + + +/* arrays which keep track of which cards are found at initialization */ +struct ai566 **pwf_xy566; +struct wf085 **pwf_xy085; +char **pwf_mem; + +/* the routine to call when the data is read and the argument to send */ +unsigned int **pargument; +unsigned int **proutine; + +/* VME memory Short Address Space is set up in gta_init */ + +int wfDoneId; /* waveform done task ID */ + +/* forward references */ +static void senw(); +static VOID xy566_reset(); +static int ai_xy566_init(); +static int ai_xy566l_init(); +static VOID rval_convert(); +static VOID xy566_rval_report(); + + +static acro_intr(ap) +register struct acroregs *ap; +{ +} + +/* The following two subroutines introduce a delay between + * successive writes to the 566. This is necessary for some + * parts of the card (i.e. the AM9513). It also insures + * that a value is actually written, instead of compiler + * generated bset or bclr instructions. + */ +static void senw (addr, val) +unsigned short *addr; +unsigned short val; +{ + *addr = val; +} + +senb (addr, val) +unsigned char *addr; +unsigned char val; +{ + *addr = val; +} + +ai566_intr(i) +short i; +{ + register struct ai566 *ap; + + ap = pai_xy566dil[i]; + + scanIoRequest(paioscanpvt[i]); + + /* reset the CSR - needed to allow next interrupt */ + senw(&ap->a566_csr,XY566L_CSR); + +} + +/* + * ai_566_init () + * + * Initialize all VME analog input cards + */ + +long ai_566_init() +{ + /* intialize the Xycom 566 Unipolar Single Ended Analog Inputs */ + ai_xy566_init(&pai_xy566se,ai_addrs[XY566SE],ai_num_channels[XY566SE], + ai_num_cards[XY566SE],ai_memaddrs[XY566SE],&pai_xy566se_mem); + + /* intialize the Xycom 566 Unipolar Differential Analog Inputs */ + ai_xy566_init(&pai_xy566di,ai_addrs[XY566DI],ai_num_channels[XY566DI], + ai_num_cards[XY566DI],ai_memaddrs[XY566DI],&pai_xy566di_mem); + + + /* intialize the Xycom 566 Unipolar Differential Analog Inputs Latched */ + ai_xy566l_init(&pai_xy566dil,ai_addrs[XY566DIL],ai_num_channels[XY566DIL], + ai_num_cards[XY566DIL],ai_memaddrs[XY566DIL],&pai_xy566dil_mem); + + return (0); +} + + +/* + * ai_XY566_INIT + * + * intialize the xycom 566 analog input card + */ +int ai_xy566_init(pppcards_present,base_addr,num_channels,num_cards,paimem,pppmem_present) +register struct ai566 ***pppcards_present; +register unsigned short *base_addr; +register short num_channels; +short num_cards; +register char *paimem; /* mem loc of I/O buffer */ +register short ***pppmem_present; +{ + short shval; + register short i,n; + struct ai566 *pai566; /* memory location of cards */ + char *pai566io; /* mem loc of I/O buffer */ + short status; + struct ai566 **ppcards_present; + short **ppmem_present; + + *pppcards_present = (struct ai566 **) + calloc(num_cards, sizeof(**pppcards_present)); + if(!*pppcards_present){ + return ERROR; + } + + *pppmem_present = (short **) + calloc(num_cards, sizeof(**pppmem_present)); + if(!*pppmem_present){ + return ERROR; + } + + ppcards_present = *pppcards_present; + ppmem_present = *pppmem_present; + + /* map the io card into the VRTX short address space */ + status = sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO, base_addr, &pai566); + if(status != OK){ + logMsg("%s: failed to map XY566 A16 base addr A16=%x\n", + __FILE__, + base_addr); + return ERROR; + } + + /* map the io card into the standard address space */ + status = sysBusToLocalAdrs(VME_AM_STD_SUP_DATA,paimem, &pai566io); + if(status != OK){ + logMsg( "%s: failed to map XY566 A24 base addr A24=%x\n", + __FILE__, + paimem); + return ERROR; + } + + /* mark each card present into the card present array */ + for (i = 0; i < num_cards; + i++, pai566++, ppcards_present++, pai566io+=XY566_MEM_INCR,ppmem_present++) { + if (vxMemProbe(pai566,READ,sizeof(short),&shval) != OK){ + *ppcards_present = 0; + continue; + } + if (vxMemProbe(pai566io,READ,sizeof(short),&shval) != OK){ + *ppcards_present = 0; + continue; + } + + *ppcards_present = pai566; + *ppmem_present = (short *)pai566io; + + /* reset the Xycom 566 board */ + senw(&pai566->a566_csr,0x00); /* off seq control */ + senw(&pai566->a566_csr,XY_SOFTRESET); /* reset */ + senw(&pai566->a566_csr,XY_LED); /* reset off,red off,green on */ + + /* Am9513 commands */ + /* initialize the Am9513 chip on the XY566 */ + senw(&pai566->stc_control,0xffff); /* master reset */ + senw(&pai566->stc_control,0xff5f); /* disarm all counters */ + senw(&pai566->stc_control,0xffef); /* 16 bit mode */ + + /* master mode register */ + senw(&pai566->stc_control,0xff17); /* select master mode reg */ + senw(&pai566->stc_data,0x2200); /* 16 bit, divide by 4 */ + + /* counter two is used to set the time between sequences */ + senw(&pai566->stc_control,0xff02); /*sel counter 2 */ + senw(&pai566->stc_data,0x0b02); /* TC toggle mode */ + senw(&pai566->stc_control,0xffea); /* TC output high */ + + /* counter four is used as time between sequence elements */ + senw(&pai566->stc_control,0xff04); /* sel counter 4 */ + senw(&pai566->stc_data,0x0b02); /* TC toggle mode */ + senw(&pai566->stc_control,0xffec); /* TC output high */ + + /* counter five is used as an event counter */ + senw(&pai566->stc_control,0xff05); /* sel counter 5 */ + senw(&pai566->stc_data,0x0b02); /* TC toggle mode */ + senw(&pai566->stc_control,0xffed); /* TC output high */ + + /* set time between sequences */ + senw(&pai566->stc_control,0xff04); /* sel counter 4 */ + senw(&pai566->stc_data,0x9525); /* see Am9513A manual */ + senw(&pai566->stc_data,0x0014); /* downcount value */ + senw(&pai566->stc_control,0xff68); /* load & arm cntr 4 */ + + senw(&pai566->stc_control,0xff05); /* sel counter 4 */ + senw(&pai566->stc_data,0x97ad); /* see Am9513A manual */ + senw(&pai566->stc_data,0x0100); /* downcount value */ + senw(&pai566->stc_control,0xff70); /* load & arm cntr 4 */ + /* end of the Am9513 commands */ + + /* for each channel set gain and place into the scan list */ + for (n=0; n < num_channels; n++) { + senb((&pai566->gram_base + n*2),0); /* gain == 1 */ + /* end of sequnce = 0x80 | channel */ + /* stop = 0x40 | channel */ + senb((&pai566->sram_base+n*2),(n==num_channels-1)? n|0x80:n); + } + senw(&pai566->dram_ptr, 0); /* data ram at 0 */ + senb(&pai566->sram_ptr, 0); /* seq ram also at 0 */ + + /* set the Xycom 566 board */ + /* reset the counter interrupt 0x8000 */ + /* reset the sequence interrupt 0x2000 */ + /* reset the trigger clock interrupt 0x0800 */ + /* enable the sequence controller 0x0100 */ + /* read values into first 32 words on each read 0x0040 */ + /* read in sequential mode (bit 0x0020 == 0) 0x0000 */ + /* leds green-on red-off 0x0003 */ + senw(&pai566->a566_csr,0xa943 ); /* init csr */ + + /* latch in the first bunch of data and start continuous scan */ + senb(&pai566->soft_start,0); + } + + return OK; +} + +/* + * AI_XY566L_INIT + * + * intialize the xycom 566 latched analog input card + */ +int ai_xy566l_init(pppcards_present,base_addr,num_channels,num_cards,paimem,pppmem_present) +register struct ai566 ***pppcards_present; +register unsigned short *base_addr; +register short num_channels; +short num_cards; +register char *paimem; /* mem loc of I/O buffer */ +register short ***pppmem_present; +{ + short shval; + register short i,n; + struct ai566 *pai566; /* memory location of cards */ + char *pai566io; /* mem loc of I/O buffer */ + int status; + struct ai566 **ppcards_present; + short **ppmem_present; + + *pppcards_present = (struct ai566 **) + calloc(num_cards, sizeof(**pppcards_present)); + if(!*pppcards_present){ + return ERROR; + } + + paioscanpvt = (IOSCANPVT *)calloc(num_cards, sizeof(*paioscanpvt)); + if(!paioscanpvt) { + return ERROR; + } + { + int i; + for(i=0; icard_number,i); + + /* set up the interrupt vector */ + /* taken from the XYCOM-566 Manual. Figure 4-6 Page 4-19 */ + pai566->int_vect = AI566_VNUM + i; + + intConnect((AI566_VNUM + i) * 4, ai566_intr, i); + sysIntEnable(XY566_INT_LEVEL); + + /* reset the Xycom 566 board */ + senw(&pai566->a566_csr,0x00); /* off seq control */ + senw(&pai566->a566_csr,XY_SOFTRESET); /* reset */ + senw(&pai566->a566_csr,XY_LED); /* reset off,red off,green on */ + + /* Am9513 commands */ + /* initialize the Am9513 chip on the XY566 */ + senw(&pai566->stc_control,0xffff); /* master reset */ + senw(&pai566->stc_control,0xff5f); /* disarm all counters */ + senw(&pai566->stc_control,0xffef); /* 16 bit mode */ + + /* master mode register */ + senw(&pai566->stc_control,0xff17); /* select master mode reg */ + senw(&pai566->stc_data,0x2200); /* 16 bit, divide by 4 */ + + /* counter two is used to set the time between sequences */ + senw(&pai566->stc_control,0xff02); /*sel counter 2 */ + senw(&pai566->stc_data,0x0b02); /* TC toggle mode */ + senw(&pai566->stc_control,0xffea); /* TC output high */ + + /* counter four is used as time between sequence elements */ + senw(&pai566->stc_control,0xff04); /* sel counter 4 */ + senw(&pai566->stc_data,0x0b02); /* TC toggle mode */ + senw(&pai566->stc_control,0xffec); /* TC output high */ + + /* counter five is used as an event counter */ + senw(&pai566->stc_control,0xff05); /* sel counter 5 */ + senw(&pai566->stc_data,0x0b02); /* TC toggle mode */ + senw(&pai566->stc_control,0xffed); /* TC output high */ + + /* set time between sequences */ + senw(&pai566->stc_control,0xff04); /* sel counter 4 */ + senw(&pai566->stc_data,0x9525); /* see Am9513A manual */ + senw(&pai566->stc_data,0x0014); /* downcount value */ + senw(&pai566->stc_control,0xff68); /* load & arm cntr 4 */ + + senw(&pai566->stc_control,0xff05); /* sel counter 4 */ + senw(&pai566->stc_data,0x97ad); /* see Am9513A manual */ + senw(&pai566->stc_data,0x0100); /* downcount value */ + senw(&pai566->stc_control,0xff70); /* load & arm cntr 4 */ + /* end of the Am9513 commands */ + + /* for each channel set gain and place into the scan list */ + for (n=0; n < num_channels; n++) { + senb((&pai566->gram_base + n*2), 0); /* gain == 1 */ + + /* end of sequnce = 0x80 | channel */ + /* stop = 0x40 | channel */ + /* interrupt = 0x20 | channel */ + senb((&pai566->sram_base+n*2),(n==num_channels-1)? n|0xe0:n); + } + senw(&pai566->dram_ptr,0); /* data ram at 0 */ + senb(&pai566->sram_ptr,0); /* seq ram also at 0 */ + + /* initialize the control status register */ + /* reset the sequence interrupt 0x2000 */ + /* enable the sequence interrupt 0x1000 */ + /* reset the trigger clock interrupt 0x0800 */ + /* enable the sequence controller 0x0100 */ + /* read values into first 32 words on each read 0x0040 */ + /* read in sequential mode (bit 0x0020 == 0) 0x0000 */ + /* interrupt enable 0x0008 */ + /* leds green-on red-off 0x0003 */ + senw(&pai566->a566_csr,XY566L_CSR); + + } + + return OK; +} + +ai_xy566_getioscanpvt(card,scanpvt) +unsigned short card; +IOSCANPVT *scanpvt; +{ + if((card<=(unsigned short)MAX_DIL_CARDS) && paioscanpvt[card]) *scanpvt = paioscanpvt[card]; + return(0); +} + +ai_xy566_driver(card,chan,type,prval) +register short card; +short chan; +register unsigned int type; +register unsigned short *prval; +{ + /* the register short i is used to calculate the delay for the */ + /* conversion of the XYCOM 566. Make sure it stays a register */ + register unsigned short *pcard; + register unsigned short i; + + /* check on the card and channel number as kept in module_types.h */ + if (card >= ai_num_cards[type]) return(-1); + if (chan >= ai_num_channels[type]) return(-2); + + switch (type){ + + case (XY566SE): + { + register struct ai566 *pai566; + + /* check card specified exists */ + if (pai_xy566se[card] == 0) return(-1); + + /* read the value from the Xycom data RAM area */ + *prval = pai_xy566se_mem[card][chan]; + + return (0); + } + + case (XY566DI): + { + register struct ai566 *pai566; + + /* check card specified exists */ + if (pai_xy566di[card] == 0) return(-1); + + /* read the value form the Xycom data RAM area */ + + *prval = pai_xy566di_mem[card][chan]; + + rval_convert(prval); + return (0); + } + + case (XY566DIL): + { + register struct ai566 *pai566; + + /* check card specified exists */ + if (pai_xy566dil[card] == 0) return(-1); + + /* read the value form the Xycom data RAM area */ + *prval = pai_xy566dil_mem[card][chan]; + + rval_convert(prval); + return (0); + } + + + } + + return (-3); +} + +/* + * rval_convert + * For 566_card - values for XY566DI and XY566DIL + * come from the card as signed hex numbers( -0x7ff to +0x7ff). + * This subrountine converts them to unsigned hex numbers (0x0 - + * 0x7ff. Then it strips off the sign bit. + * +*/ +VOID rval_convert(rval) + unsigned short *rval; +{ + + *rval = *rval + 0x800; + + /* remove the sign bits. */ + + *rval &= 0xfff; +} + +/* + * + * xy566_reset- Called for ctl X reboot. The idea is to disable + * bits 3 and 12 of the CSR. + * +*/ + +VOID xy566_reset(){ + unsigned short csr_val,shval; + short int i; + struct ai566 *pai566; /* memory location of cards */ + short int status; + + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + ai_addrs[XY566DIL], + &pai566); + if (status != OK){ + logMsg("%s: unable to map A16 XY566 base\n", __FILE__); + return; + } + + for (i=0;ia566_csr = csr_val; + } +} + + + + +/* + * io_report (or dbior) subroutine for 566 card. + * + * + */ +long ai_xy566_io_report(level) + char level; +{ + short i,j; + unsigned short rval; + + + for (i = 0; i < MAX_SE_CARDS; i++){ + if (pai_xy566se[i]){ + printf("AI: XY566-SE:\tcard %d\n",i); + if (level == 1){ + xy566_rval_report(i,XY566SE); + } + + } + } + for (i = 0; i < MAX_DI_CARDS; i++){ + if (pai_xy566di[i]){ + printf("AI: XY566-DI:\tcard %d\n",i); + if (level == 1){ + xy566_rval_report(i,XY566DI); + } + } + + } + + for (i = 0; i < MAX_DIL_CARDS; i++){ + if (pai_xy566dil[i]){ + printf("AI: XY566-DIL:\tcard %d\n",i); + if (level == 1){ + xy566_rval_report(i,XY566DIL); + } + } + } + return OK; +} + +/* + * xy566_rval_report -reports the raw value for every channel on the card + * + * called by ai_xy566_io_report if level is 1 + * + */ +VOID xy566_rval_report(card,type) + short int card,type; + { + short i,j,k,l,m,num_chans; + unsigned short jrval,krval,lrval,mrval; + + printf("\n"); + + num_chans = ai_num_channels[type]; + + for(j=0,k=1,l=2,m=3;j < num_chans,k < num_chans, l < num_chans,m < num_chans; + j+=IOR_MAX_COLS,k+=IOR_MAX_COLS,l+= IOR_MAX_COLS,m +=IOR_MAX_COLS){ + if(j < num_chans){ + if( ai_xy566_driver(card,j,type,&jrval) == 0){ + printf("Chan %d = %x \t",j,jrval); + }else + printf("READ_ALARM\n"); + } + + if(k < num_chans){ + if(ai_xy566_driver(card,k,type,&krval) == 0){ + printf("Chan %d = %x \t",k,krval); + } else + printf("READ_ALARM\n"); + } + if(l < num_chans){ + if( ai_xy566_driver(card,l,type,&lrval) == 0){ + printf("Chan %d = %x \t",l,lrval); + } else + printf("READ_ALARM\n"); + } + if(m < num_chans){ + if( ai_xy566_driver(card,m,type,&mrval) == 0){ + printf("Chan %d = %x \n",m,mrval); + } + else + printf("READ_ALARM\n"); + } + } + } + + + +/* forward references */ + + +/* + * xy566_driver + * + */ +xy566_driver(slot,pcbroutine,parg) +register unsigned short slot; +register unsigned int *pcbroutine; +register unsigned int *parg; /* number of values read */ +{ + register struct ai566 *pwf566; + register struct wf085 *pwf085; + + /* slot range checking occurs in wf_driver.c */ + + /* is the Xycom 566 card present */ + if ((pwf566 = pwf_xy566[slot]) == 0) + return(-1); + if ((pwf085 = pwf_xy085[slot]) == 0) + return(-1); + + /* armed already by someone else */ + if (proutine[slot] != 0) + return(-2); /* by someone else */ + + /* set the Xycom 566 board */ + senw(&pwf566->dram_ptr,0); /* RAM pointer to 0 */ + senw(&pwf566->sram_ptr,0); /* sequence pointer to 0 */ + + /* keep the pointer to the value field */ + proutine[slot] = pcbroutine; + pargument[slot] = parg; + + /* arm the encoder pulse mechanism */ + senw(&pwf085->csr,XY_ARM | XY_LED | 0x20); /* set high */ + senw(&pwf085->csr,XY_LED | 0x20); /* set low */ + + return(0); +} + +/* + * xy566DoneTask + * + * polls the busy bit on the Xycom 566 latched waveform records + * The busy bit is set externally when data collection is completed + */ +xy566DoneTask() +{ + register unsigned int **pproutines; + register unsigned int (*pcbroutine)(); + register short i; + + while(TRUE){ + pproutines = proutine; + for (i=0; icsr & XY_BUSY) == 0){ + /* callback routine when data take completed */ + (unsigned int *)pcbroutine = *pproutines; + (*pcbroutine) + (pargument[i],pwf_xy566[i]->dram_ptr,pwf_mem[i]); + + /* reset for someelse to claim */ + *pproutines = 0; + pargument[i] = 0; + } + } + + /* sleep for .1 second - system degrade will slow this task */ + /* that's OK */ + taskDelay(2); /* ges: changed from 6 ticks to 2 ticks 2/4/91 */ + } +} + +/* + * XY566_INIT + * + * intialize the xycom 566 waveform input card + */ +xy566_init() +{ + register struct ai566 **pcards_present = pwf_xy566; + register struct wf085 **parms_present = pwf_xy085; + register char **pmem_present = pwf_mem; + + short shval,status; + register short i,got_one; + struct ai566 *pwf566; /* VME location of cards */ + struct wf085 *pwf085; /* VME location of arm */ + char *pwfMem; /* VME 566 memory buffer */ + + pwf_xy566 = (struct ai566 **) + calloc(wf_num_cards[XY566WF], sizeof(*pwf_xy566)); + if(!pwf_xy566){ + return ERROR; + } + pwf_xy085 = (struct wf085 **) + calloc(wf_num_cards[XY566WF], sizeof(*pwf_xy085)); + if(!pwf_xy085){ + return ERROR; + } + pwf_mem = (char **) + calloc(wf_num_cards[XY566WF], sizeof(*pwf_mem)); + if(!pwf_mem){ + return ERROR; + } + pargument = (unsigned int **) + calloc(wf_num_cards[XY566WF], sizeof(*pargument)); + if(!pargument){ + return ERROR; + } + proutine = (unsigned int **) + calloc(wf_num_cards[XY566WF], sizeof(*proutine)); + if(!proutine){ + return ERROR; + } + + pcards_present = pwf_xy566; + parms_present = pwf_xy085; + pmem_present = pwf_mem; + + /* map the io card into the VME short address space */ + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + WF_XY566_BASE, + &pwf566); + if(status<0){ + logMsg("%s: unable to map A16 XY566 base\n", __FILE__); + return ERROR; + } + + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + WF_XY085_BASE, + &pwf085); + if(status<0){ + logMsg("%s: unable to map A16 XY085 base\n", __FILE__); + return ERROR; + } + + status = sysBusToLocalAdrs( + VME_AM_STD_SUP_DATA, + wf_memaddrs[XY566WF], + &pwfMem); + if (status != OK){ + logMsg("%s: unable to map A24 XY566 base\n", __FILE__); + return ERROR; + } + + /* mark each card present into the card present array */ + got_one = 0; + for (i = 0; + i < wf_num_cards[XY566WF]; + i++, pwf566++,pwf085++,pwfMem += XY566_MEM_INCR, pcards_present += 1) { + + /* is the Xycom 566 here */ + if (vxMemProbe(pwf566,READ,sizeof(short),&shval) != OK){ + *pcards_present = 0; + continue; + } + /* is the Xycom 566 memory here */ + if (vxMemProbe(pwfMem,READ,sizeof(short),&shval) != OK){ + *pcards_present = 0; + continue; + } + /* is the Xycom 085 used as the arming mechanism present */ + if (vxMemProbe(pwf085,READ,sizeof(short),&shval) != OK){ + *pcards_present = 0; + continue; + } + + got_one = 1; + *pcards_present = pwf566; + *parms_present = pwf085; + *pmem_present = pwfMem; + + /* taken from the XYCOM-566 Manual. Figure 4-6 Page 4-19 */ + /* reset the Xycom 566 board */ + senw (&pwf566->a566_csr,0x00); /* off seq control */ + senw (&pwf566->a566_csr,XY_SOFTRESET); /* reset */ + senw (&pwf566->a566_csr,XY_LED); /* reset off,red off,green on */ + + /* Am9513 commands */ + /* initialize the Am9513 chip on the XY566 */ + senw (&pwf566->stc_control, 0xffff); /* master reset */ + senw(&pwf566->stc_control, 0xff5f); /* disarm all counters */ + senw(&pwf566->stc_control, 0xffef); /* 16 bit mode */ + + /* master mode register */ + senw(&pwf566->stc_control, 0xff17); /* select master mode reg */ + senw(&pwf566->stc_data, 0x2200); /* 16 bit, divide by 4 */ + + /* counter two is used to set the time between sequences */ + senw(&pwf566->stc_control, 0xff02); /*sel counter 2 */ + senw(&pwf566->stc_data, 0x0b02); /* TC toggle mode */ + senw(&pwf566->stc_control, 0xffea); /* TC output high */ + + /* counter four is used as time between sequence elements */ + senw(&pwf566->stc_control, 0xff04); /* sel counter 4 */ + senw(&pwf566->stc_data, 0x0b02); /* TC toggle mode */ + senw(&pwf566->stc_control, 0xffec); /* TC output high */ + + /* counter five is used as an event counter */ + senw(&pwf566->stc_control, 0xff05); /* sel counter 5 */ + senw(&pwf566->stc_data, 0x0b02); /* TC toggle mode */ + senw(&pwf566->stc_control, 0xffed); /* TC output high */ + + /* set counter 4 */ + /* active high level gate N 0x8000 */ + /* count on the falling edge 0x1000 */ + /* SRC 5 0x0500 */ + /* disable special gate 0x0080 = 0 */ + /* reload from load 0x0040 = 0 */ + /* count repetitively 0x0020 = 1 */ + /* binary count 0x0010 = 0 */ + /* count down 0x0008 = 0 */ + /* active low terminal count pulse 0x0007 = 5 */ + senw(&pwf566->stc_control,0xff04); /* sel counter 4 */ + senw(&pwf566->stc_data,0x9525); /* see comment above*/ + senw(&pwf566->stc_data,0x0014); /* downcount value */ + senw(&pwf566->stc_control,0xff68); /* load & arm cntr 4 */ + + /* set counter 5 */ + /* active high level gate N 0x8000 */ + /* count on the falling edge 0x1000 */ + /* GATE 2 0x0700 */ + /* enable special gate 0x0080 = 1 */ + /* reload from load 0x0040 = 0 */ + /* count repetitively 0x0020 = 1 */ + /* binary count 0x0010 = 0 */ + /* count up 0x0008 = 1 */ + /* active low terminal count pulse 0x0007 = 5 */ + senw(&pwf566->stc_control,0xff05); /* sel counter 5 */ + senw(&pwf566->stc_data,0x97ad); /* see comment above */ + senw(&pwf566->stc_data,0x0100); /* count value */ + senw(&pwf566->stc_control,0xff70); /* load & arm cntr 5*/ + /* end of the Am9513 commands */ + + /* Xycom gain RAM */ + senb(&pwf566->gram_base,0); /* set gain to 1 ch0*/ + + /* Xycom sequence RAM */ + senb(&pwf566->sram_base,0xc0); /* read only the 0th channel */ + + /* Xycom data RAM index */ + senw(&pwf566->dram_ptr,0); /* data ram at 0 */ + + /* Xycom sequential RAM index */ + senb(&pwf566->sram_ptr,0); /* seq ram also at 0 */ + + /* set the Xycom 566 board */ + /* reset the counter interrupt 0x8000 */ + /* reset the sequence interrupt 0x2000 */ + /* reset the trigger clock interrupt 0x0800 */ + /* enable the sequence controller 0x0100 */ + /* read values into data RAM contiguously (bit 0x0040 == 0) 0x0000 */ + /* read in sequential mode (bit 0x0020 == 0) 0x0000 */ + /* leds green-on red-off 0x0003 */ + senw(&pwf566->a566_csr,0xa903); /* init csr */ + + /* initialize the xycom 085 used as the arming mechanism */ + /* leds green-on red-off 0x0003 */ + senw(&pwf085->csr,XY_LED | 0x20); /* init csr */ + } + + /* start the 566 waveform readback task */ + if (got_one) + wfDoneId = + taskSpawn( + WFDONE_NAME, + WFDONE_PRI, + WFDONE_OPT, + WFDONE_STACK, + xy566DoneTask); + taskwdInsert(wfDoneId,NULL,NULL); + + return 0; +} + + +/* + * XY566_IO_REPORT + * + * + * + * + */ +long +xy566_io_report(level) +short int level; +{ + int i,xy566_bytes; + + /* report all of the xy566 waveform inputs present */ + for (i = 0; i < wf_num_cards[XY566WF]; i++) + if (pwf_xy566[i]){ + printf("WF: XY566: card %d\n",i); + } + +} + + diff --git a/src/drv/old/module_types.c b/src/drv/old/module_types.c new file mode 100644 index 000000000..09996959b --- /dev/null +++ b/src/drv/old/module_types.c @@ -0,0 +1,241 @@ +/* module_types.c */ +/* base/src/drv $Id$ */ +/* + * Author: Marty Kraimer + * Date: 08-23-93 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 08-23-93 mrk Initial Version + */ + +#include + +module_types() +{ + +ai_num_cards[AB1771IL] = 12; +ai_num_cards[AB1771IFE] = 12; +ai_num_cards[AB1771IXE] = 12; +ai_num_cards[XY566SE] = 2; +ai_num_cards[XY566DI] = 2; +ai_num_cards[XY566DIL] = 2; +ai_num_cards[VXI_AT5_AI] = 32; +ai_num_cards[AB1771IFE_SE] = 12; +ai_num_cards[AB1771IFE_4to20MA] = 12; +ai_num_cards[DVX2502] = 1; +ai_num_cards[AB1771IFE_0to5V] = 12; +ai_num_cards[KSCV215] = 32; + +ai_num_channels[AB1771IL] = 8; +ai_num_channels[AB1771IFE] = 8; +ai_num_channels[AB1771IXE] = 8; +ai_num_channels[XY566SE] = 32; +ai_num_channels[XY566DI] = 16; +ai_num_channels[XY566DIL] = 16; +ai_num_channels[VXI_AT5_AI] = 8; +ai_num_channels[AB1771IFE_SE] = 16; +ai_num_channels[AB1771IFE_4to20MA] = 8; +ai_num_channels[DVX2502] = 127; +ai_num_channels[AB1771IFE_0to5V] = 8; +ai_num_channels[KSCV215] = 32; + +ai_addrs[AB1771IL] = 0; +ai_addrs[AB1771IFE] = 0; +ai_addrs[AB1771IXE] = 0; +ai_addrs[XY566SE] = 0x6000; +ai_addrs[XY566DI] = 0x7000; +ai_addrs[XY566DIL] = 0x7800; +ai_addrs[VXI_AT5_AI] = 0xc014; +ai_addrs[AB1771IFE_SE] = 0; +ai_addrs[AB1771IFE_4to20MA] = 0; +ai_addrs[DVX2502] = 0xff00; +ai_addrs[AB1771IFE_0to5V] = 0; +ai_addrs[KSCV215] = 0; + +ai_memaddrs[AB1771IL] = 0; +ai_memaddrs[AB1771IFE] = 0; +ai_memaddrs[AB1771IXE] = 0; +ai_memaddrs[XY566SE] = 0x000000; +ai_memaddrs[XY566DI] = 0x040000; +ai_memaddrs[XY566DIL] = 0x0c0000; +ai_memaddrs[VXI_AT5_AI] = 0; +ai_memaddrs[AB1771IFE_SE] = 0; +ai_memaddrs[AB1771IFE_4to20MA] = 0; +ai_memaddrs[DVX2502] = 0x100000; +ai_memaddrs[AB1771IFE_0to5V] = 0; +ai_memaddrs[KSCV215] = 0; + +ao_num_cards[AB1771OFE] = 12; +ao_num_cards[VMI4100] = 4; +ao_num_cards[ZIO085] = 1; +ao_num_cards[VXI_AT5_AO] = 32; + +ao_num_channels[AB1771OFE] = 4; +ao_num_channels[VMI4100] = 16; +ao_num_channels[ZIO085] = 32; +ao_num_channels[VXI_AT5_AO] = 16; + +ao_addrs[AB1771OFE] = 0; +ao_addrs[VMI4100] = 0x4100; +ao_addrs[ZIO085] = 0x0800; +ao_addrs[VXI_AT5_AO] = 0xc000; + +bi_num_cards[ABBI_08_BIT] = 12; +bi_num_cards[ABBI_16_BIT] = 12; +bi_num_cards[BB910] = 4; +bi_num_cards[XY210] = 2; +bi_num_cards[VXI_AT5_BI] = 32; +bi_num_cards[HPE1368A_BI] = 32; +bi_num_cards[AT8_FP10S_BI] = 8; +bi_num_cards[XY240_BI] = 2; + +bi_num_channels[ABBI_08_BIT] = 8; +bi_num_channels[ABBI_16_BIT] = 16; +bi_num_channels[BB910] = 32; +bi_num_channels[XY210] = 32; +bi_num_channels[VXI_AT5_BI] = 32; +bi_num_channels[HPE1368A_BI] = 16; +bi_num_channels[AT8_FP10S_BI] = 32; +bi_num_channels[XY240_BI] = 32; + +bi_addrs[ABBI_08_BIT] = 0; +bi_addrs[ABBI_16_BIT] = 0; +bi_addrs[BB910] = 0xb800; +bi_addrs[XY210] = 0xa000; +bi_addrs[VXI_AT5_BI] = 0xc000; +bi_addrs[HPE1368A_BI] = 0xc000; +bi_addrs[AT8_FP10S_BI] = 0x0e00; +bi_addrs[XY240_BI] = 0x3000; + +bo_num_cards[ABBO_08_BIT] = 12; +bo_num_cards[ABBO_16_BIT] = 12; +bo_num_cards[BB902] = 4; +bo_num_cards[XY220] = 1; +bo_num_cards[VXI_AT5_BO] = 32; +bo_num_cards[HPE1368A_BO] = 32; +bo_num_cards[AT8_FP10M_BO] = 2; +bo_num_cards[XY240_BO] = 2; + +bo_num_channels[ABBO_08_BIT] = 8; +bo_num_channels[ABBO_16_BIT] = 16; +bo_num_channels[BB902] = 32; +bo_num_channels[XY220] = 32; +bo_num_channels[VXI_AT5_BO] = 32; +bo_num_channels[HPE1368A_BO] = 16; +bo_num_channels[AT8_FP10M_BO] = 32; +bo_num_channels[XY240_BO] = 32; + +bo_addrs[ABBO_08_BIT] = 0; +bo_addrs[ABBO_16_BIT] = 0; +bo_addrs[BB902] = 0x0400; +bo_addrs[XY220] = 0xa800; +bo_addrs[VXI_AT5_BO] = 0xc000; +bo_addrs[HPE1368A_BO] = 0xc000; +bo_addrs[AT8_FP10M_BO] = 0xc000; +bo_addrs[XY240_BO] = 0x3000; + +sm_num_cards[CM57_83E] = 4; +sm_num_cards[OMS_6AXIS] = 4; + +sm_num_channels[CM57_83E] = 1; +sm_num_channels[OMS_6AXIS] = 6; + +sm_addrs[CM57_83E] = 0x8000; +sm_addrs[OMS_6AXIS] = 0x4000; + +wf_num_cards[XY566WF] = 2; +wf_num_cards[CAMAC_THING] = 4; +wf_num_cards[JGVTR1] = 4; +wf_num_cards[COMET] = 4; + +wf_num_channels[XY566WF] = 1; +wf_num_channels[CAMAC_THING] = 1; +wf_num_channels[JGVTR1] = 1; +wf_num_channels[COMET] = 4; + +wf_addrs[XY566WF] = 0x9000; +wf_addrs[CAMAC_THING] = 0; +wf_addrs[JGVTR1] = 0xB000; +wf_addrs[COMET] = 0xbc00; + +wf_armaddrs[XY566WF] = 0x5400; +wf_armaddrs[CAMAC_THING]= 0; +wf_armaddrs[JGVTR1] = 0; +wf_armaddrs[COMET] = 0; + +wf_memaddrs[XY566WF] = 0x080000; +wf_memaddrs[CAMAC_THING]= 0; +wf_memaddrs[JGVTR1] = 0xb80000; +wf_memaddrs[COMET] = 0xe0000000; + +tm_num_cards[MZ8310] = 4; +tm_num_cards[DG535] = 1; +tm_num_cards[VXI_AT5_TIME] = 32; + +tm_num_channels[MZ8310] = 10; +tm_num_channels[DG535] = 1; +tm_num_channels[VXI_AT5_TIME] = 10; + +tm_addrs[MZ8310] = 0x1000; +tm_addrs[DG535] = 0; +tm_addrs[VXI_AT5_TIME] = 0xc000; + +AT830X_1_addrs = 0x0400; +AT830X_1_num_cards = 2; +AT830X_addrs = 0xaa0000; +AT830X_num_cards = 2; + +xy010ScA16Base = 0x0000; + +EPICS_VXI_LA_COUNT = 32; +EPICS_VXI_A24_BASE = (char *) 0x900000; +EPICS_VXI_A24_SIZE = 0x100000; +EPICS_VXI_A32_BASE = (char *) 0x90000000; +EPICS_VXI_A32_SIZE = 0x10000000; + + +AI566_VNUM = 0xf8; +DVX_IVEC0 = 0xd0; +MD_INT_BASE = 0xf0; +MZ8310_INT_VEC_BASE = 0xe8; +AB_VEC_BASE = 0x60; +JGVTR1_INT_VEC = 0xe0; +AT830X_1_IVEC0 = 0xd4; +AT830X_IVEC0 = 0xd6; +AT8FP_IVEC_BASE = 0xa2; +AT8FPM_IVEC_BASE= 0xaa; + +BB_SHORT_OFF = 0x1800; +BB_IVEC_BASE = 0xa0; +BB_IRQ_LEVEL = 5; +PEP_BB_SHORT_OFF= 0x1c00; +PEP_BB_IVEC_BASE= 0xe8; + +NIGPIB_SHORT_OFF = 0x5000; +NIGPIB_IVEC_BASE = 100; +NIGPIB_IRQ_LEVEL = 5; + +return(0); +} diff --git a/src/drv/old/steppermotor.h b/src/drv/old/steppermotor.h new file mode 100644 index 000000000..f9d827ab3 --- /dev/null +++ b/src/drv/old/steppermotor.h @@ -0,0 +1,64 @@ +/* steppermotor.h */ +/* base/src/drv $Id$ */ +/* + * header file to support database library interface to motor drivers + * + * Author: Bob Dalesio + * Date: 12-11-89 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 mm-dd-yy iii Comment + */ + +/* readback data passed to the database library routine from the motor driver */ +struct motor_data{ +short cw_limit; +short ccw_limit; +short moving; +short direction; +short constant_velocity; +long velocity; +long encoder_position; +long motor_position; +long accel; +}; + +/* + * Sets values for the database library based on the value flag: + * 0 - set the mode of the motor (position/velocity) + * 1 - set the velocity of the motor + * 2 - set the poistion of the motor + * 3 - start motor rotating + * 4 - set the callback routine for a motor + */ +#define SM_MODE 0 +#define SM_VELOCITY 1 +#define SM_MOVE 2 +#define SM_MOTION 3 +#define SM_CALLBACK 4 +#define SM_SET_HOME 5 +#define SM_ENCODER_RATIO 6 +#define SM_MOTOR_RESOLUTION 7 +#define SM_READ 8 diff --git a/src/vxWorks/drv/old/drvComet.c b/src/vxWorks/drv/old/drvComet.c new file mode 100644 index 000000000..10d650d4a --- /dev/null +++ b/src/vxWorks/drv/old/drvComet.c @@ -0,0 +1,639 @@ +/* comet_driver.c */ +/* base/src/drv $Id$ */ +/* + * Author: Leo R. Dalesio + * Date: 5-92 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 joh 071092 added argument to calloc() + * .02 joh 071092 stripped the hkv2f specific portion off the comet + * std addr base specification and left it at base + * addr zero which is most likely wrong. + * .03 joh 071492 use extended (A32) address space + * instead of standard (A24) address space + * for the base address of the waveform memory + * (changed arg to sysBusToLocalAdrs() + * .04 joh 071592 fixed to use correct size when incrementing + * to the waveform memory of the second card + * .05 joh 071592 modified A16 & A32 base addr to match AT8 + * address standard + * .06 bg 071792 moved addresses to module_types.h + * .07 joh 080592 added io report routines + * .08 ms 080692 added comet_mode routine, modified comet_driver + * and cometDoneTask to allow an external routine + * to control hardware scan mode. Added variable + * scan_control to flag operating mode. + * .09 mrk 082692 added DSET + * .10 joh 082792 fixed uninitialized csr pointer in comet_driver() + * function + * .11 lrd 091692 add signal support + * .12 joh 092992 card number validation now based on module_types.h. + * signal number checking now based on the array element + * count. + * .13 mrk 080293 Added call to taskwdInsert + * .14 mgb 080493 Removed V5/V4 and EPICS_V2 conditionals + */ + +static char *sccsID = "@(#)drvComet.c 1.11\t9/16/92"; + +/* + * Code Portions + * + * comet_init() + * comet_driver(card, pcbroutine, parg) + * cometDoneTask() + * comet_io_report() + * comet_mode(card,mode,arg,val) + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define COMET_NCHAN 4 +#define COMET_CHANNEL_MEM_SIZE 0x20000 /* bytes */ +#define COMET_DATA_MEM_SIZE (COMET_CHANNEL_MEM_SIZE*COMET_NCHAN) +static char *shortaddr; +static short scan_control; /* scan type/rate (if >0 normal, <=0 external control) */ + +/* comet conrtol register map */ +struct comet_cr{ + unsigned char csrh; /* control and status register - high byte */ + unsigned char csrl; /* control and status register - low byte */ + unsigned char lcrh; /* location status register - high byte */ + unsigned char lcrl; /* location status register - low byte */ + unsigned char gdcrh; /* gate duration status register - high byte*/ + unsigned char gdcrl; /* gate duration status register - low byte */ + unsigned char cdr; /* channel delay register */ + unsigned char acr; /* auxiliary control register */ + char pad[0x100-8]; +}; + + +/* defines for the control status register - high byte */ +#define DIGITIZER_ACTIVE 0x80 /* 1- Active */ +#define ARM_DIGITIZER 0x40 /* 1- Arm the digitizer */ +#define CIRC_BUFFER_ENABLED 0x20 /* 0- Stop when memory is full */ +#define WRAP_MODE_ENABLED 0x10 /* 0- Disable wrap around */ +#define AUTO_RESET_LOC_CNT 0x08 /* 1- Reset addr to 0 on trigger */ +#define EXTERNAL_TRIG_ENABLED 0x04 /* 1- use external clk to trigger */ +#define EXTERNAL_GATE_ENABLED 0x02 /* 0- use pulse start conversion */ +#define EXTERNAL_CLK_ENABLED 0x01 /* 0- uses the internal clock */ + + +/* commands for the COMET digitizer */ +#define COMET_INIT_CSRH +#define COMET_INIT_READ + +/* mode commands for the COMET digitizer */ +#define READREG 0 +#define WRITEREG 1 +#define SCANCONTROL 2 +#define SCANSENSE 3 +#define SCANDONE 4 + +/* register selects */ +#define COMET_CSR 0 +#define COMET_LCR 1 +#define COMET_GDCR 2 +#define COMET_CDACR 3 + +/* defines for the control status register - low byte */ +#define SOFTWARE_TRIGGER 0x80 /* 1- generates a software trigger */ +#define UNUSED 0x60 +#define CHAN_DELAY_ENABLE 0x10 /* 0- digitize on trigger */ +#define DIG_RATE_SELECT 0x0f + +/* digitizer rates - not defined but available for 250KHz to 122Hz */ +#define COMET_5MHZ 0x0000 +#define COMET_2MHZ 0x0001 +#define COMET_1MHZ 0x0002 +#define COMET_500KHZ 0x0003 + +/* defines for the auxiliary control register */ +#define ONE_SHOT 0x10 +#define ALL_CHANNEL_MODE 0x80 + + +/* comet configuration data */ +struct comet_config{ + struct comet_cr *pcomet_csr; /* pointer to the control/status register */ + unsigned short *pdata; /* pointer to data area for this COMET card */ + void (*psub)(); /* subroutine to call on end of conversion */ + void *parg[4]; /* argument to return to the arming routine */ + FAST_LOCK lock; /* mutual exclusion lock */ + IOSCANPVT ioscanpvt; + unsigned long nelements; /* number of elements to digitize/read */ + +}; + +/* task ID for the comet done task */ +int cometDoneTaskId; +struct comet_config *pcomet_config; + +static long report(); +static long init(); +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvComet={ + 2, + report, + init}; + + +/* + * cometDoneTask + * + * wait for comet waveform record cycle complete + * and call back to the database with the waveform size and address + * + */ +void +cometDoneTask() +{ + register unsigned card; + register struct comet_config *pconfig; + register long i; + + while(TRUE) + { + + if (scan_control <= 0) + taskDelay(2); + else + { + taskDelay(scan_control); + +/* printf("DoneTask: entering for loop...\n"); */ + + /* check each card for end of conversion */ + for(card=0, pconfig = pcomet_config; card < 2;card++, pconfig++) + { +/* is the card present */ + if (!pconfig->pcomet_csr) + { + if (card == 0) + { +/* + printf("DoneTask: checking card present?...\n"); + printf("DoneTask: pconfig->pcomet_csr %x...\n",pconfig->pcomet_csr); +*/ + } + continue; + } + +/* is the card armed */ + if (!pconfig->psub) + { + if (card == 0) + { +/* printf("DoneTask: checking card armed?...\n"); */ + } + continue; + } + +/* is the digitizer finished conversion */ +/* printf("pconfig->pdata: %x \n", pconfig->pdata); */ + + if (*(pconfig->pdata+pconfig->nelements) == 0xffff) + { + if (card == 0) + { +/* printf("DoneTask: finished conversion?...\n"); */ + } + continue; + } + +/* printf("DoneTask: pcomet_config->pcomet_csr %x...\n",pcomet_config->pcomet_csr); */ +/* printf("DoneTask: DONE\n"); */ + + +#if 0 + /* reset each of the control registers */ + pconfig->pcomet_csr->csrh = pconfig->pcomet_csr->csrl = 0; + pconfig->pcomet_csr->lcrh = pconfig->pcomet_csr->lcrl = 0; + pconfig->pcomet_csr->gdcrh = pconfig->pcomet_csr->gdcrl = 0; + pconfig->pcomet_csr->acr = 0; +#endif + + /* clear the pointer to the subroutine to allow rearming */ +/* pconfig->psub = NULL; */ + +/* post the event */ +/* - is there a bus error for long references to this card?? copy into VME mem? */ + + if(pconfig->parg[0]) + { + (*pconfig->psub)(pconfig->parg[0],pconfig->pdata); + } + if(pconfig->parg[1]) + { + (*pconfig->psub)(pconfig->parg[1],(((char*)pconfig->pdata)+0x20000)); + } + + if(pconfig->parg[2]) + { + (*pconfig->psub)(pconfig->parg[2],(((char*)pconfig->pdata)+0x40000)); + } + + if(pconfig->parg[3]) + { + (*pconfig->psub)(pconfig->parg[3],(((char*)pconfig->pdata)+0x60000)); + } + + + } + } + } +} + + + +/* + * COMET_INIT + * + * intialize the driver for the COMET digitizer from omnibyte + * + */ +comet_init() +{ + register struct comet_config *pconfig; + short readback,got_one,card; + int status; + struct comet_cr *pcomet_cr; + unsigned char *extaddr; + +/* free memory and delete tasks from previous initialization */ + if (cometDoneTaskId) + { + taskwdRemove(cometDoneTaskId); + if ((status = taskDelete(cometDoneTaskId)) < 0) + logMsg("\nCOMET: Failed to delete cometDoneTask: %d",status); + } + else + { + pcomet_config = (struct comet_config *)calloc(wf_num_cards[COMET],sizeof(struct comet_config)); + if (pcomet_config == 0) + { + logMsg("\nCOMET: Couldn't allocate memory for the configuration data"); + return; + } + } + +/* get the standard and short address locations */ + if ((status = sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO,wf_addrs[COMET],&pcomet_cr)) != OK){ + logMsg("\nCOMET: failed to map VME A16 base address\n"); + return; + } + if ((status = sysBusToLocalAdrs(VME_AM_EXT_SUP_DATA,wf_memaddrs[COMET],&extaddr)) != OK){ + logMsg("\nCOMET: failed to map VME A32 base address\n"); + return; + } + +/* determine which cards are present */ + got_one = FALSE; + pconfig = pcomet_config; + + for ( card = 0; + card < 2; + card++, pconfig++, pcomet_cr++, extaddr+= COMET_DATA_MEM_SIZE){ + + /* is the card present */ + if (vxMemProbe(pcomet_cr,READ,sizeof(readback),&readback) != OK) + { + continue; + } + if (vxMemProbe(extaddr,READ,sizeof(readback),&readback) != OK) + { + logMsg("\nCOMET: Found CSR but not data RAM %x\n",extaddr); + continue; + } + + /* initialize the configuration data */ + pconfig->pcomet_csr = pcomet_cr; + pconfig->pdata = (unsigned short *) extaddr; + got_one = TRUE; + + + FASTLOCKINIT(&pcomet_config[card].lock); + + /* initialize the card */ + pcomet_cr->csrh = ARM_DIGITIZER | AUTO_RESET_LOC_CNT; + pcomet_cr->csrl = COMET_1MHZ; + pcomet_cr->lcrh = pcomet_cr->lcrl = 0; + pcomet_cr->gdcrh = 0; + pcomet_cr->gdcrl = 1; + pcomet_cr->cdr = 0; + + /* run it once */ + pcomet_cr->csrl |= SOFTWARE_TRIGGER; + taskDelay(1); + /* reset */ + pcomet_cr->csrl = COMET_5MHZ; + pcomet_cr->acr = ONE_SHOT | ALL_CHANNEL_MODE; + + scanIoInit(&pconfig->ioscanpvt); + + } /*end of for loop*/ + + /* initialization for processing comet digitizers */ + if(got_one) + { + /* start the waveform readback task */ + scan_control = 2; /* scan rate in vxWorks clock ticks */ + cometDoneTaskId = taskSpawn("cometWFTask",WFDONE_PRI,WFDONE_OPT,WFDONE_STACK,(FUNCPTR) cometDoneTask); + taskwdInsert(cometDoneTaskId,NULL,NULL); + } +} + + +static long report(level) + int level; +{ + comet_io_report(level); + return(0); +} + +static long init() +{ + + comet_init(); + return(0); +} + + +/* + * COMET_DRIVER + * + * initiate waveform read + * + */ +comet_driver(card, signal, pcbroutine, parg, nelements) +register short card; +register unsigned short signal; +unsigned int *pcbroutine; +unsigned int *parg; /* pointer to the waveform record */ +unsigned long nelements; +{ + register struct comet_cr *pcomet_csr; + register struct comet_config *pconfig; + register unsigned short *pcomet_data; + register char *dummy; + +/* printf("comet_driver: BEGIN...\n"); */ +/* printf("comet_driver: nelements: %d ...\n",nelements); */ + + /* check for valid card number */ + if(card >= wf_num_cards[COMET]) + return ERROR; + pconfig = (pcomet_config+card); + if(signal >= NELEMENTS(pconfig->parg)) + return ERROR; + pconfig->nelements = nelements * 2; + +/* printf("comet_driver: check for card present...\n"); */ + + /* check for card present */ + if(!pconfig->pcomet_csr) return ERROR; + + /* mutual exclusion area */ + FASTLOCK(&pconfig->lock); + +/* printf("comet_driver: mark the card as armed...\n"); */ + + /* mark the card as armed */ +/* if (pconfig->parg[signal] != 0) */ + pconfig->parg[signal] = parg; +/* if (pconfig->psub) return; */ + pconfig->psub = (void (*)()) pcbroutine; + + /* exit mutual exclusion area */ + FASTUNLOCK(&pconfig->lock); + + pcomet_csr = pconfig->pcomet_csr; + + /* reset each of the control registers */ + pcomet_csr->csrh = pcomet_csr->csrl = 0; + pcomet_csr->lcrh = pcomet_csr->lcrl = 0; + pcomet_csr->gdcrh = pcomet_csr->gdcrl = 0; + pcomet_csr->acr = 0; + + /* arm the card */ + *(pconfig->pdata+pconfig->nelements) = 0xffff; +/* printf("comet_driver: pconfig->pcomet_csr %x...\n",pconfig->pcomet_csr); */ + + if (scan_control > 0) + { +#if 0 /* for debugging purposes */ + pcomet_csr->gdcrh = 0x03; /* # samples per channel */ + pcomet_csr->gdcrl = 0xe8; /* # samples per channel */ +#endif + + pcomet_csr->gdcrh = (pconfig->nelements >> 8) & 0xff; /* # samples per channel */ + pcomet_csr->gdcrl = pconfig->nelements & 0xff; /* # samples per channel */ + pcomet_csr->acr = ONE_SHOT | ALL_CHANNEL_MODE; /* disarm after the trigger */ + pcomet_csr->csrl = COMET_5MHZ; /* sample at 5MhZ */ + + /* arm, reset location counter to 0 on trigger, use external trigger */ + pcomet_csr->csrh = ARM_DIGITIZER | AUTO_RESET_LOC_CNT | EXTERNAL_TRIG_ENABLED; +/* printf("comet_driver: gdcrh: %x gdcrl: %x nelements: %x\n ",pcomet_csr->gdcrh,pcomet_csr->gdcrl, pconfig->nelements); */ + + } + else + pcomet_csr->csrh |= ARM_DIGITIZER; +/* printf("comet_driver: pconfig->pcomet_csr %x...\n",pconfig->pcomet_csr); */ + +/* printf("comet_driver: END...\n"); */ + return OK; +} + + +/* + * COMET_IO_REPORT + * + * print status for all cards in the specified COMET address range + */ +comet_io_report(level) +short int level; +{ + struct comet_config *pconfig; + unsigned card; + unsigned nelements; + int status; + + pconfig = pcomet_config; + for(card=0; card < wf_num_cards[COMET]; card++){ + + if(!pconfig->pcomet_csr) + continue; + + printf( "WF: COMET:\tcard=%d\n", card); + if (level >= 2){ + printf("enter the number of elements to dump:"); + status = scanf("%d",&nelements); + if(status == 1){ + comet_dump(card, nelements); + } + } + pconfig++; + } + return OK; +} + + +/* + * comet_dump + * + */ +int comet_dump(card, n) +unsigned card; +unsigned n; +{ + unsigned short *pdata; + unsigned short *psave; + unsigned short *pbegin; + unsigned short *pend; + + if (card >= wf_num_cards[COMET]) + return ERROR; + + pdata = pcomet_config[card].pdata; + psave = (unsigned short *) malloc(n * sizeof(*psave)); + if(!psave){ + return ERROR; + } + + pbegin = psave; + pend = &psave[n]; + for( pdata = pcomet_config[card].pdata; + psave= wf_num_cards[COMET]) + return ERROR; + if (!pcomet_config[card].pcomet_csr) + return ERROR; + switch (mode) + { + case READREG: + /*cptr = (unsigned char *)pcomet_config[card].pcomet_csr; + for (i = 0; i < 6; i++, cptr++) + printf("%x %x\n",cptr,*cptr);*/ + cptr = (unsigned char *)pcomet_config[card].pcomet_csr; /* point to offset 0 */ + cptr += arg<<1; /* build new offset */ + val = (*cptr++)<<8; /* read value and return */ + val |= *cptr; + return val; + break; + case WRITEREG: + cptr = (unsigned char *)pcomet_config[card].pcomet_csr; + cptr += arg<<1; + *cptr++ = val>>8; + *cptr = val; + break; + case SCANCONTROL: + scan_control = val; + break; + case SCANSENSE: + return scan_control; + break; + case SCANDONE: + if (!pcomet_config[card].psub) + return ERROR; + /*pcomet_config[card].psub = NULL;*/ /* clear the pointer to subroutine to allow rearming */ + (*pcomet_config[card].psub)(pcomet_config[card].parg,0xffff,pcomet_config[card].pdata); + break; + default: + return ERROR; + } + return OK; +} + +/*********************************************/ +cometGetioscanpvt(card,scanpvt) +short card; +IOSCANPVT *scanpvt; +{ + register struct comet_config *pconfig; + + pconfig=pcomet_config; + pconfig+=card; + + if ((card >= wf_num_cards[COMET]) || (card < 0)) /* make sure hardware exists */ + return(0); + +/* +This is present in the mix driver...I don't know if I really need it. + if (!pconfig->present) + return(0); +*/ + + *scanpvt = pconfig->ioscanpvt; + + return(0); +} + diff --git a/src/vxWorks/drv/old/drvCompuSm.c b/src/vxWorks/drv/old/drvCompuSm.c new file mode 100644 index 000000000..a1bd303b3 --- /dev/null +++ b/src/vxWorks/drv/old/drvCompuSm.c @@ -0,0 +1,972 @@ +/* drvCompuSm.c */ +/* base/src/drv $Id$ */ +/* + * subroutines and tasks that are used to interface to the Compumotor 1830 + * stepper motor drivers + * + * Author: Bob Dalesio + * Date: 01-03-90 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 02-07-90 lrd add command to read the motor status + * .02 04-11-90 lrd made velocity mode motor go active + * .03 04-12-90 lrd only allow one connection to a motor + * .04 04-13-90 lrd add externally initiated motor motion monitoring + * .05 09-05-91 joh updated for v5 vxWorks + * .06 10-09-91 lrd monitor for external motion once every 2 seconds + * not at 30 Hz (.04 was not implemented correctly) + * .06 11-31-91 bg added compu_sm_io_report. Added sysBusToLocalAdrs() + * for addressing. + * .07 03-02-92 bg added level and ability to print raw values to + * compu_sm_io_report for level > 0. + * .08 05-04-92 bg added compu_sm_reset and rebootHookAdd so ioc can be + * rebooted with control X. + * .09 06-25-92 bg Combined drvCompuSm.c and compu_sm_driver.c + * .10 06-26-92 bg Added level to compu_sm_io_report in drvCompuSm + * structure + * .11 06-29-92 joh took file ptr arg out of io report + * .12 08-06-92 joh merged compu sm include file + * .13 08-27-92 joh silenced ANSI C function proto warning + * .14 08-27-92 joh fixed no epics init + * .15 08-02-93 mrk Added call to taskwdInsert + * .16 10-29-93 jba Fixed max number of cards to use module_types.c + * Fixed error in calculating card addresses + */ +#include +#include +#include +#include /* library for semaphore support */ +#include +#include /* library for ring buffer support */ + +/* drvCompuSm.c - Driver Support Routines for CompuSm */ + +#include +#include +#include +#include +#include +#include + + +long compu_sm_io_report(); +long compu_driver_init(); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvCompuSm={ + 2, + compu_sm_io_report, + compu_driver_init}; + +/* compumotor vme interface information */ +#define MAX_COMPU_MOTORS 8 + +#define RESP_SZ 16 /* card returns 16 chars - cmd & resp */ +#define RESPBUF_SZ (RESP_SZ+1) /* intr routine also passes motor no. */ + +/* Control Byte bit definitions for the Compumotor 1830 */ + /* bits 0 and 1 are not used */ +#define CBEND 0x04 /* end of command string */ +#define CBLMR 0x08 /* last message byte read */ +#define CBCR 0x10 /* command ready in cm_idb */ +#define CBMA 0x20 /* message accepted from odb */ +#define CBEI 0x40 /* enable interrupts */ +#define CBBR 0x80 /* board reset */ +#define SND_MORE CBCR | CBEI /* more chars to send */ +#define SND_LAST CBEND | SND_MORE /* last char being sent */ +#define RD_MORE CBMA | CBEI /* more chars to read */ +#define RD_LAST CBLMR | RD_MORE /* last byte we need to read */ + +/* Status Byte bit definitions */ +#define SBIRDY 0x10 /* idb is ready */ + +/* Structure used for communication with a Compumotor 1830 +** Motor Controller. The data buffer is repeated 64 times +** on even word addresses (0xXXXXXX1,0xXXXXX5...) and the +** control location is repeated 64 times on odd word +** addresses (0xXXXXX3, 0xXXXXX7...). The registers must +** be read and written as bytes. +*/ +struct compumotor { + char cm_d1; /* not accessable */ + char cm_idb; /* input data buffer */ + char cm_d2; /* not accessable */ + char cm_cb; /* control byte */ + char cm_d3[0x100-4]; /* fill to next standard address */ +}; + + +/* This file includes defines for all compumotor 1830 controller +** commands. */ + +#define SM_NULL 0x0 /* Null command */ +#define SM_INT 0x1 /* Interrupt X */ +#define SM_WRT_STAT 0x8 /* Write X to the user defined status bits */ +#define SM_SET_STAT 0x9 /* Set user defined status bit number X */ +#define SM_CLR_STAT 0xa /* Clear user defined status bit number X */ +#define SM_SET_PROG 0xb /* Set programmable output bit X */ +#define SM_CLR_PROG 0xc /* Clear programmable output bit X */ +#define SM_WRT_PROG 0xd /* Write X to the programmable output bits */ +#define SM_DEF_X_TO_Y 0xf /* Define bit X to indicate state Y */ +#define SM_TOG_JOG 0x10 /* Disable/enable the JOG inputs */ +#define SM_DEF_JOG 0x11 /* Define JOG input functions */ +#define SM_TOG_REM_PWR 0x12 /* Turn off/on remote power shutdown */ +#define SM_TOG_REM_SHUT 0x13 /* Disable/enable the "remote shutdown" bit */ +#define SM_SET_CW_MOTN 0x14 /* Set CW motion equal to +- */ +#define SM_TOG_POS_MTN 0x18 /* Turn off/on post-move position maintenance */ +#define SM_TOG_STOP_STL 0x19 /* Turn off/on termination on stall detect */ +#define SM_REP_X_Y 0x20 /* Repeat the following X commands Y times */ +#define SM_REP_TIL_CONT 0x21 /* Repeat the following X commands + until a CONTINUE is received */ +#define SM_WAIT_CONT 0x28 /* Wait for a CONTINUE */ +#define SM_WAIT_MILLI 0x29 /* Wait X milliseconds */ +#define SM_WAIT_SECOND 0x2a /* Wait X seconds */ +#define SM_WAIT_MINUTE 0x2b /* Wait X minutes */ +#define SM_WAIT_TRIGGER 0x2c /* Wait for trigger X to go active */ +#define SM_DEF_A_OP_POS 0x2e /* Define the abs open-loop position as X */ +#define SM_DEF_A_CL_POS 0x2f /* Define absolute closed-loop position */ +#define SM_DEF_ABS_ZERO 0x30 /* Define the present position as the + absolute zero position */ +#define SM_DEF_VEL_ACC 0x31 /* Define default velocity and acceleration */ +#define SM_MOV_DEFAULT 0x32 /* Perform the default move (trapezoidal + continuous) */ +#define SM_MOV_REL_POS 0x33 /* Go to relative position X at default + velocity and acceleration */ +#define SM_MOV_ABS_POS 0x34 /* Go to absolute position X at default + velocity and acceleration */ +#define SM_MOV_REL_ENC 0x35 /* Go to relative encoder position X */ +#define SM_MOV_ABS_ENC 0x36 /* Go to absolute encoder position X */ +#define SM_DEF_OP_HOME 0x38 /* Define HOME location (open loop */ +#define SM_DEF_CL_HOME 0x38 /* Define HOME location (closed loop) */ +#define SM_MOV_HOME_POS 0x39 /* Go HOME at the default velocity and + acceleration */ +#define SM_MOV_HOME_ENC 0x3a /* Go to encoder HOME at the default + velocity and acceleration */ +#define SM_DO_MOV_X 0x40 /* Perform move number X */ +#define SM_DO_SEQ_X 0x41 /* Perform sequence buffer X */ +#define SM_DO_VEL_STR 0x42 /* Perform the velocity streaming buffer */ +#define SM_CONT 0x48 /* CONTINUE (perform next command) */ +#define SM_OP_LOOP_MODE 0x50 /* Enter open loop indexer mode */ +#define SM_VEL_DIS_MODE 0x51 /* Enter velocity-distance streaming mode */ +#define SM_VEL_TIM_MODE 0x52 /* Enter velocity-time streaming mode */ +#define SM_STOP 0x70 /* STOP motion */ +#define SM_DSC_SEQ 0x71 /* Discontinue the sequence buffer */ +#define SM_SSP_SEQ 0x72 /* Suspend the sequence buffer; + wait for a CONTINUE to resume */ +#define SM_DSC_SNGL 0x73 /* Discontinue any singular command + currently being performed */ +#define SM_STOP_ON_TRG 0x74 /* STOP motion when trigger X goes active */ +#define SM_DSC_SEQ_TRG 0x75 /* Discontinue the sequence buffer when + trigger X goes active */ +#define SM_SSP_SEQ_TRG 0x76 /* Suspend sequence buffer when trigger + X goes active */ +#define SM_DSC_SNGL_TRG 0x77 /* Discontinue any singular command when + trigger X goes active */ +#define SM_KILL 0x78 /* Kill motion */ +#define SM_KILL_SEQ 0x79 /* Kill the sequence buffer */ +#define SM_KILL_SEQ_SNGL 0x7a /* Kill current sequence singular command; + wait for CONTINUE */ +#define SM_KILL_VEL_STR 0x7b /* Kill the velocity streaming buffer */ +#define SM_KILL_ON_TRG 0x7c /* Kill motion when trigger X goes active */ +#define SM_KILL_SEQ_TRG 0x7d /* Kill the sequence buffer when trigger + X goes active */ +#define SM_KL_SQSNG_TRG 0x7e /* Kill current sequence singular command + when trigger X goes active; wait for + a continue */ +#define SM_KL_VLSTR_TRG 0x7f /* Kill the velocity streaming buffer + when trigger X goes active */ +#define SM_GET_B_REL_POS 0x80 /* Request position relative to the + beginning of the current move */ +#define SM_GET_E_REL_POS 0x81 /* Request position relative to the + end of the current move */ +#define SM_GET_H_REL_POS 0x82 /* Request position relative to the home + limit switch */ +#define SM_GET_Z_REL_POS 0x83 /* Request position relative to the + absolute zero position */ +#define SM_GET_CUR_DIR 0x84 /* Request current direction */ +#define SM_GET_VEL 0x85 /* Request current velocity */ +#define SM_GET_ACC 0x86 /* Request current acceleration */ +#define SM_GET_MOV_STAT 0x88 /* Request current move status */ +#define SM_GET_LIM_STAT 0x89 /* Request state of the limit switches */ +#define SM_GET_HOME_STAT 0x8a /* Request state of the HOME switch */ +#define SM_GET_TRV_DIR 0x8b /* Request direction of travel */ +#define SM_GET_MOT_MOV 0x8c /* Request whether motor is moving or not */ +#define SM_GET_MOT_CONST 0x8d /* Request whether motor is at constant, + nonzero velocity or not */ +#define SM_GET_MOT_ACC 0x8e /* Request whether motor is or is not + accelerating */ +#define SM_GET_MOT_DEC 0x8f /* Request whether motor is or is not + decelerating */ +#define SM_GET_MODE 0x90 /* Request present mode */ +#define SM_GET_MV_PARM 0x91 /* Request move parameters for move number X */ +#define SM_GET_SEQ_CMMD 0x92 /* request commands stored in the + sequence buffer */ +#define SM_GET_MVDEF_STAT 0x93 /* Request state of the move definitions */ +#define SM_GET_TRG_STAT 0x94 /* Request state of trigger inputs */ +#define SM_GET_JOG_STAT 0x95 /* Request state of JOG inputs */ +#define SM_GET_Z_STAT 0x96 /* Request state of the Channel Z home input */ +#define SM_GET_OUT_STAT 0x97 /* Request the state of the programmable + output bits */ +#define SM_GET_REL_ENC 0x98 /* Request relative encoder count */ +#define SM_GET_REL_ERR 0x99 /* Request relative error from desired + closed loop position */ +#define SM_GET_ABS_ENC 0x9a /* Request absolute encoder count */ +#define SM_GET_SLIP_STAT 0x9b /* Request slip detect status */ +#define SM_GET_RATIO 0x9c /* Request motor pulse to encoder pulse ratio */ +#define SM_GET_RESOLTN 0x9d /* Request motor resolution */ +#define SM_GET_BACK_SIG 0x9e /* Request backlash sigma (motor steps)*/ +#define SM_GET_ALG 0x9f /* Request position maintenance alg. + const, and max velocity */ +#define SM_INT_NXT_MOV 0xa0 /* Interrupt at start of next move */ +#define SM_INT_ALL_MOV 0xa1 /* Interrupt at the start of every move */ +#define SM_INT_NXT_NZ 0xa2 /* Interrupt at constant nonzero velocity + of next move */ +#define SM_INT_ALL_NZ 0xa3 /* Interrupt at constant nonzero velocity + of every move */ +#define SM_INT_NXT_END 0xa4 /* Interrupt at next end of motion */ +#define SM_INT_ALL_END 0xa5 /* Interrupt at every end of motion */ +#define SM_INT_NXT_STL 0xa6 /* Interrupt on next stall detect */ +#define SM_INT_ALL_STL 0xa7 /* Interrupt on every stall detect */ +#define SM_INT_NXT_PLIM 0xa8 /* Interrupt the next time the motor + hits the positive limit */ +#define SM_INT_ALL_PLIM 0xa9 /* Interrupt on every positive limit */ +#define SM_INT_NXT_NLIM 0xaa /* Interrupt the next time the motor + hits the negative limit */ +#define SM_INT_ALL_NLIM 0xab /* Interrupt on every negative limit */ +#define SM_INT_TRG 0xac /* Interrupt on trigger X active */ +#define SM_INT_INHBT 0xaf /* Inhibit all interrupts */ +#define SM_DEF_RATIO 0xb0 /* Define motor pulse to encoder pulse ratio */ +#define SM_DEF_RESOLTN 0xb1 /* Define motor resolution */ +#define SM_DEF_BACK_SIG 0xb2 /* Define backlash sigma (motor steps)*/ +#define SM_DEF_ALG 0xb3 /* Define position maintenance algorithm + const, and max velocity */ +#define SM_DEF_TEETH 0xb4 /* Define the number of rotor teeth */ +#define SM_DEF_DEADBAND 0xb5 /* Def the deadband region in encoder pulses */ +#define SM_DEF_REL_TRP 0xc8 /* Define move X as a relative, + trapezoidal move */ +#define SM_DEF_ABS_TRP 0xcb /* Define move X as an absolute + trapezoidal move */ +#define SM_DEF_CONT 0xce /* Define move X as a continuous move */ +#define SM_DEF_REL_CL 0xd4 /* Define move X, define it as relative, + closed-loop move */ +#define SM_DEF_ABS_CL 0xd5 /* Def move X as an abs, closed- loop move */ +#define SM_DEF_STSTP_VEL 0xd6 /* Define the start/stop velocity */ +#define SM_DEL_MOV_X 0xd7 /* Delete move X */ +#define SM_END_SEQ_DEF 0xd8 /* End definition of sequence buffer */ +#define SM_BEG_SEQ_DEF 0xd9 /* Begin definition of sequence buffer */ +#define SM_DEL_SEQ_X 0xda /* Delete sequence buffer X */ +#define SM_LD_VD_DATA 0xe0 /* Place data into the velocity-distance buffer */ +#define SM_LD_VT_DATA 0xe1 /* Place data into the velocity-time buffer */ +#define SM_GET_FREE_BYT 0xe2 /* Request number of free bytes in vel- + streaming/sequence buffer */ +#define SM_DEF_VS_CMMD 0xee /* Define command to be executed during + the velocity streaming buffer */ +#define SM_GET_NUM_REV 0xfd /* Request software part number and revision */ +#define SM_TEST_SWITCH 0xff /* Perform the test switch function */ + + +#define VELOCITY_MODE 0 +#define MAX_COMMANDS 256 +#define COMPU_INT_LEVEL 5 + +/* array of pointers to stepper motor driver cards present in system */ +struct compumotor *pcompu_motors[MAX_COMPU_MOTORS]; + +LOCAL SEM_ID compu_wakeup; /* compumotor data request task semaphore */ + +/* response variables */ +LOCAL SEM_ID smRespSem; /* task semaphore */ +LOCAL RING_ID smRespQ; /* ring buffer */ +int smRespId; /* task id */ +#define RESP_Q_SZ (RESPBUF_SZ * 50) /* response ring buffer size */ + +/* interrupt buffers */ +unsigned char sm_responses[MAX_COMPU_MOTORS][RESPBUF_SZ]; +unsigned short counts[MAX_COMPU_MOTORS]; + +/* VME memory Short Address Space is set up in gta_init */ +static int *compu_addr; + +/* motor information */ +struct compu_motor{ +short active; /* flag to tell the oms_task if the motor is moving */ +int callback; /* routine in database library to call with status */ +int callback_arg; /* argument to callback routine */ +short update_count; +short mode; +short motor_resolution; +}; +struct compu_motor compu_motor_array[MAX_COMPU_MOTORS]; + +/* Forward reference. */ +VOID compu_sm_reset(); +VOID compu_sm_stat(); + +/* motor status - returned to the database library routines */ +struct motor_data compu_motor_data_array[MAX_COMPU_MOTORS]; + +/* moving status bit descriptions */ +#define CW_LIMIT 0x01 /* clockwise???? limit */ +#define CCW_LIMIT 0x02 /* counter-clockwise???? limit */ +#define DIRECTION 0x08 /* direction bit */ +#define MOVING 0x10 /* moving status bit */ +#define CONSTANT_VEL 0x20 /* constant velocity */ + +/* directions in driver card-ese */ +#define CLKW 0 /* clockwise direction */ +#define CCLKW 1 /* counter clockwise direction */ + +/* + * Code Portions: + * + * smCmdTask Task which writes commands to the hardware + * smRespTask Task which places reponses from the hardware into resp buffers + * sm_intr Interrupt Handler - collects response data from the hardware + * sm_drv_init Initializes all motors, semaphores, ring buffers and interrupts + * sm_driver Subroutine for outside world to issue commands to motors + * motor_select Subroutine to setting callback arg and verifying no other user + * motor_deselect Subroutine to free the motor for other users + * + * Interaction Chart: + * -------------- ------------------- + * / \ / \ + * | smRespTask | | smCmdTask | + * \ / \ / + * --------------- ------------------- + * ^ ^ | + * TAKES | | GETS | + * | | | + * -------------- --------------- | + * Resp Semaphore Response Queue | + * -------------- --------------- | + * ^ ^ | + * GIVES | | PUTS | + * | | | + * --------------- | + * / \ | + * | sm_intr | | + * \ / | + * --------------- | + * ^ reads responses writes commands | + * | from hardware to hardware V + */ + +/* + * COMPU_RESP_TASK + * + * converts readback from the compumotor 1830 cards into a structure that + * is returned to the database library layer every .1 second while a motor + * is moving + */ +compu_resp_task() +{ + unsigned char resp[RESPBUF_SZ]; + register short i; + register struct motor_data *pmotor_data; + + FOREVER { + /* wait for somebody to wake us up */ + semTake (smRespSem, WAIT_FOREVER); + /* the response buffer contains: */ + /* 0 - motor number */ + /* 1 - the command which solicited this response */ + /* 2 - the first byte of the response */ + + /* process requests in the command ring buffer */ + while (rngBufGet(smRespQ,(char *)resp,RESPBUF_SZ) == RESPBUF_SZ){ + pmotor_data = &compu_motor_data_array[resp[0]]; + + /* convert argument */ + switch(resp[1]){ + + case (SM_GET_VEL): + { + register long *pvelocity = (long *)(&resp[3]); + pmotor_data->velocity = *pvelocity; + + break; + } + case (SM_GET_MOV_STAT): + { + register struct compu_motor *pcompu_motor; + register int (*psmcb_routine)(); + + pcompu_motor = &compu_motor_array[resp[0]]; + pmotor_data->moving = (resp[2] & MOVING)?1:0; + pmotor_data->constant_velocity = (resp[2] & CONSTANT_VEL)?1:0; + pmotor_data->cw_limit = (resp[2] & CW_LIMIT)?1:0; + pmotor_data->ccw_limit = (resp[2] & CCW_LIMIT)?1:0; + pmotor_data->direction = (resp[2] & DIRECTION)?1:0; + + /* post every .1 second or not moving */ + if ((pcompu_motor->update_count-- <= 0) + || (pmotor_data->moving == 0)){ + if (pcompu_motor->callback != 0){ + (int)psmcb_routine = pcompu_motor->callback; + (*psmcb_routine)(pmotor_data,pcompu_motor->callback_arg); + } + if (pmotor_data->moving){ + /* motors are reported at 10 Hz */ + pcompu_motor->update_count = 3; + }else{ + pcompu_motor->active = FALSE; + pcompu_motor->update_count = 0; + } + } + break; + } + case (SM_GET_ABS_ENC): + { + register long *pencoder = (long *)(&resp[2]); + pmotor_data->encoder_position = *pencoder; + break; + } + case (SM_GET_Z_REL_POS): + { + register long *pmotor = (long *)(&resp[4]); + pmotor_data->motor_position = *pmotor; + break; + } + case (SM_GET_CUR_DIR): + pmotor_data->direction = (resp[2] == 0xff)?CLKW:CCLKW; + break; + } + } + } +} + +/* Data request commands for the positional and velocity mode motors */ +char compu_velo_reqs[] = { SM_GET_VEL, SM_GET_MOV_STAT }; +#define NUM_VEL_REQS 2 +char compu_pos_reqs[] = { SM_GET_ABS_ENC, SM_GET_Z_REL_POS, SM_GET_MOV_STAT }; +#define NUM_POS_REQS 3 +/* + * COMPU_TASK + * + * task to solicit currnet status from the compumotor 1830 cards while they + * are active + */ +compu_task() +{ + register short inactive_count; + register short card; + register short i; + register struct compumotor *pmotor; + register char *preqs; + + /* inactive motors get monitored once every 2 seconds in case they are */ + /* being moved manually */ + inactive_count = 60; + while(1){ + /* This task is run 30 times a second */ + taskDelay(2); + for (card = 0; card < sm_num_cards[CM57_83E]; card++){ + pmotor = pcompu_motors[card]; + if (pmotor == 0) continue; + if ((compu_motor_array[card].active) + || (inactive_count <=0)){ + if (compu_motor_array[card].mode == VELOCITY_MODE){ + preqs = &compu_velo_reqs[0]; + /* request status data */ + for (i = 0; i < NUM_VEL_REQS; i++,preqs++) + compu_send_msg(pmotor,preqs,1); + }else{ + preqs = &compu_pos_reqs[0]; + /* request status data */ + for (i = 0; i < NUM_POS_REQS; i++,preqs++) + compu_send_msg(pmotor,preqs,1); + } + } + } + if (--inactive_count < 0) inactive_count = 60; + } +} + +/* + * COMPU_INTR + * + * interrupt vector for the compumotor 1830 card + */ +compu_intr(mdnum) +register int mdnum; +{ + register struct compumotor *pmtr; /* memory port to motor card */ + register int key; + + key = intLock(); + + /* pointer to the compumotor card interface */ + pmtr = pcompu_motors[mdnum]; + + /* place the response byte into the appropriate response buffer */ + sm_responses[mdnum][counts[mdnum]] = pmtr->cm_idb; + counts[mdnum]++; + + /* when the buffer is full pass it onto the repsonse task */ + if (counts[mdnum] == RESPBUF_SZ){ + if (rngBufPut(smRespQ,(char *)sm_responses[mdnum],RESPBUF_SZ) != RESPBUF_SZ) + logMsg("smRespQ %d - Full\n",mdnum); + else + semGive (smRespSem); + + /* the zero-th byte is the motor number */ + counts[mdnum] = 1; /* start with command */ + + /* inform the hardware that the response is complete */ + pmtr->cm_cb = RD_LAST; + }else{ + /* inform the hardware there is more to send */ + pmtr->cm_cb = RD_MORE; + } + + intUnlock(key); +} + +/* + * COMPU_DRIVER_INIT + * + * initialization for the compumotor 1830 card + */ +long +compu_driver_init(){ + register short i; + int status; + struct compumotor *pmtr; /* memory port to motor card */ + int cok = CBBR; /*to reset board */ + short none_found; /* flags a steppermotor is present */ + int taskId; + struct compumotor *pmtrb; + + /* intialize each driver which is present */ + none_found = TRUE; + rebootHookAdd((FUNCPTR)compu_sm_reset); + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + sm_addrs[CM57_83E], + (int **)&compu_addr); + if (status != OK){ + printf("%s: failed to map A16 base\n", __FILE__); + return ERROR; + } + + pmtrb = (struct compumotor *)compu_addr; + for (i = 0; i < sm_num_cards[CM57_83E]; i++) { + pmtr = (struct compumotor *)((int)pmtrb + (i<<8)); + + /* initialize when card is present */ + + if (vxMemProbe(&pmtr->cm_cb,WRITE,1,&cok) != ERROR){ + none_found = FALSE; + pcompu_motors[i] = pmtr; /* ptr to interface */ + intConnect((MD_INT_BASE+i)*4,compu_intr,i); /* interrupt enable */ + sysIntEnable(COMPU_INT_LEVEL); + + /* init interrupt receive buffers */ + sm_responses[i][0] = i; /* motor number */ + counts[i] = 1; /* buffer index */ + }else{ + pcompu_motors[i] = 0; /* flags no board is present */ + } + } + if (none_found) return(0); + + /* initialize the response task ring buffer */ + if ((smRespQ = rngCreate(RESP_Q_SZ)) == (RING_ID)NULL) + panic ("sm_init: cmRespQ\n"); + + /* intialize the semaphores which awakens the sleeping * + * stepper motor command task and the stepper motor response task */ + if(!(smRespSem=semBCreate(SEM_Q_FIFO,SEM_EMPTY))) + errMessage(0,"semBcreate failed in compu_driver_init"); + if(!(compu_wakeup=semBCreate(SEM_Q_FIFO,SEM_EMPTY))) + errMessage(0,"semBcreate failed in compu_driver_init"); + + /* spawn the sleeping motor driver command and response tasks */ + smRespId = + taskSpawn("compu_resp_task",SMRESP_PRI,SMRESP_OPT,SMRESP_STACK,compu_resp_task); + taskwdInsert(smRespId,NULL,NULL); + taskId = taskSpawn("compu_task",SMRESP_PRI,SMRESP_OPT,2000,compu_task); + taskwdInsert(taskId,NULL,NULL); + return(0); +} + +short trigger = 0; +/* + * COMPU_DRIVER + * + * driver interface to the database library layer + */ +compu_driver(card,value_flag,arg1,arg2) +register short card; +short value_flag; +register int arg1; +register int arg2; +{ + register int *pint; + register short *pshort; + short j,i; + char compu_msg[20]; + + /* verify the stepper motor driver card is present */ + if ((card < 0) || (card > sm_num_cards[CM57_83E]) || (!pcompu_motors[card])) + return (-1); + + switch (value_flag){ + case (SM_MODE): + /* set the motor mode */ + compu_motor_array[card].mode = arg1; + break; + + case (SM_VELOCITY): + compu_motor_data_array[card].velocity = arg1; + compu_motor_data_array[card].accel = arg2; + + /* set the velocity */ + compu_msg[0] = SM_DEF_VEL_ACC; + compu_msg[1] = 0; /* time is in seconds */ + compu_msg[2] = 0; + pint = (int *)&compu_msg[3]; /* velocity */ + *pint = arg1; + pint++; /* acceleration */ + *pint = arg2; + compu_send_msg(pcompu_motors[card],compu_msg,11); + + break; + + case (SM_MOVE): + if (compu_motor_array[card].mode == VELOCITY_MODE) + return(0); +i = 0; +switch (trigger){ +case (0): + /* move the motor */ + compu_msg[i++] = SM_MOV_REL_POS; + pint = (int *)&compu_msg[i]; + *pint = arg1; + i += 4; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + break; +case (1): /* test sequnce buffer */ + compu_msg[i++] = 0xda; /* delete sequence buffer */ + compu_msg[i++] = 01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xd9; /* fill sequence buffer */ + compu_msg[i++] = 01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = SM_MOV_REL_POS; + pint = (int *)&compu_msg[i]; + *pint = arg1; + i += 4; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xd8; /* end sequence buffer */ + compu_msg[i++] = 0x41; /* perform sequence buffer */ + compu_msg[i++] = 0x01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + break; +case (2): /* test move buffer */ + compu_msg[i++] = 0xc8; + compu_msg[i++] = 0x12; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x04; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x04; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + pint = (int *)&compu_msg[i]; + *pint = arg1; + i += 4; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0x40; + compu_msg[i++] = 0x12; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + break; +case (3): /* test sequence buffer with move buffer */ + compu_msg[i++] = 0xc8; + compu_msg[i++] = 0x12; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x04; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x04; + compu_msg[i++] = 0x00; + compu_msg[i++] = 0x00; + pint = (int *)&compu_msg[i]; + *pint = arg1; + i += 4; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xda; /* delete sequence buffer */ + compu_msg[i++] = 01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xd9; /* fill sequence buffer */ + compu_msg[i++] = 01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0x40; + compu_msg[i++] = 0x12; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xd8; /* end sequence buffer */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0x41; /* perform sequence buffer */ + compu_msg[i++] = 0x01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + break; +case (4): /* test sequence buffer with move buffer and trigger */ + compu_msg[i++] = 0xc8; + compu_msg[i++] = 0x12; + + compu_msg[i++] = 0x00; + pint = (int *)&compu_msg[i]; + *pint = compu_motor_data_array[card].velocity; + i += 4; + pint++; + *pint = compu_motor_data_array[card].accel; + i += 4; + pint++; + *pint = arg1; + i += 4; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xda; /* delete sequence buffer */ + compu_msg[i++] = 01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xd9; /* fill sequence buffer */ + compu_msg[i++] = 01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0x2c; /* wait for trigger */ + compu_msg[i++] = 1; /*trigger 1 */ + compu_msg[i++] = 1; /* don't care about state */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0x40; + compu_msg[i++] = 0x12; + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0xd8; /* end sequence buffer */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + compu_msg[i++] = 0x41; /* perform sequence buffer */ + compu_msg[i++] = 0x01; /* buffer 1 */ + compu_send_msg(pcompu_motors[card],compu_msg,i); + i = 0; + break; +} +for (j = 0; j < i; j++){ +printf("%x ",compu_msg[j]); +} +/* compu_send_msg(pcompu_motors[card],compu_msg,i); +*/ + /* set the motor to active */ + compu_motor_array[card].active = TRUE; + + /* wakeup the compu task */ + semGive(compu_wakeup); + + break; + + case (SM_MOTION): + if (arg1 == 0){ + compu_msg[0] = SM_STOP; + compu_send_msg(pcompu_motors[card],compu_msg,1); + }else if (compu_motor_array[card].mode == VELOCITY_MODE){ + compu_msg[0] = SM_MOV_DEFAULT; + compu_msg[1] = arg2; /* direction */ + compu_send_msg(pcompu_motors[card],compu_msg,2); + compu_motor_array[card].active = TRUE; + } + + /* wakeup the compu task */ + semGive(compu_wakeup); + break; + + case (SM_CALLBACK): + /* put the callback routine and argument into the data array */ + i = 0; + if (compu_motor_array[card].callback != 0) return(-1); + compu_motor_array[card].callback = arg1; + compu_motor_array[card].callback_arg = arg2; + break; + + case (SM_SET_HOME): + if (compu_motor_array[card].mode == VELOCITY_MODE) + return(OK); + + /* set the motor and encoder position to zero */ + compu_msg[0] = SM_DEF_ABS_ZERO; + compu_send_msg(pcompu_motors[card],compu_msg,1); + + break; + + case (SM_ENCODER_RATIO): + compu_motor_array[card].motor_resolution = arg1; + + /* set the encoder ratio */ + compu_msg[0] = SM_DEF_RATIO; + pshort = (short *)&compu_msg[1]; + *pshort = arg1; /* motor resolution */ + pshort++; + *pshort = arg2; /* encoder resolution */ + compu_send_msg(pcompu_motors[card],compu_msg,5); + + /* set the motor resolution */ + compu_msg[0] = SM_DEF_RESOLTN; + pshort = (short *)&compu_msg[1]; + *pshort = 0; + pshort++; + *pshort = arg1; /* motor resolution */ + compu_send_msg(pcompu_motors[card],compu_msg,5); + + break; + case (SM_READ): + /* set the motor to active */ + compu_motor_array[card].active = TRUE; + + /* wakeup the compu task */ + semGive(compu_wakeup); + + break; + + } + return (OK); +} + +/* + * COMPU_SEND_MSG + * + * send a message to the compumotor 1830 + */ +int wait_count; +compu_send_msg(pmotor,pmsg,count) +register struct compumotor *pmotor; +register char *pmsg; +register short count; +{ + /* write out this command one byte at a time */ + while (count){ + + /* wait for the driver to be ready */ + while ((pmotor->cm_cb & SBIRDY) == 0){ + taskDelay(0); + wait_count++; + } + + /* next byte in the input data buffer of compumotor */ + pmotor->cm_idb = *pmsg; + pmsg++; + count--; + + /* tell compumotor more or complete */ + if (count == 0){ + pmotor->cm_cb = SND_LAST; + }else{ + pmotor->cm_cb = SND_MORE; + } + } +} + + +/* + * COMPU_SM_IO_REPORT + * + * send a message to the compumotor 1830 + */ + +long compu_sm_io_report(level) + short int level; + { + register int i; + + for (i = 0; i < sm_num_cards[CM57_83E]; i++){ + if (pcompu_motors[i]){ + + printf("SM: CM1830: card %d\n",i); + if (level > 0) + compu_sm_stat(i); + } + } + + return OK; + } + +VOID compu_sm_stat(compu_num) + short int compu_num; + { + struct motor_data *pmotor_data; + printf("\tCW limit = %d\t,CCW limit = %d\tMoving = %d\tDirection = %d\n", + compu_motor_data_array[compu_num].cw_limit, + compu_motor_data_array[compu_num].ccw_limit, + compu_motor_data_array[compu_num].moving, + compu_motor_data_array[compu_num].direction); + + printf("\tConstant Velocity = %d\t, Velocity = %d\t \n", + compu_motor_data_array[compu_num].constant_velocity, + compu_motor_data_array[compu_num].velocity); + + printf("\tAcceleration = %d\tEncoder Position = %d\tMotor Position = %d\n", + compu_motor_data_array[compu_num].accel, + compu_motor_data_array[compu_num].encoder_position, + compu_motor_data_array[compu_num].motor_position); + } + +/* + * + * Subroutine to be called during a CTL X reboot. Inhibits interrupts. + * + */ + +VOID compu_sm_reset() + { + short int i; + char compu_msg[20]; + + for (i = 0; i < sm_num_cards[CM57_83E]; i++){ + if (pcompu_motors[i]){ + compu_msg[0] = SM_INT_INHBT; + compu_send_msg(pcompu_motors[i],compu_msg,1); + } + } + } + diff --git a/src/vxWorks/drv/old/drvFp.c b/src/vxWorks/drv/old/drvFp.c new file mode 100644 index 000000000..bb3215754 --- /dev/null +++ b/src/vxWorks/drv/old/drvFp.c @@ -0,0 +1,526 @@ +/* drvFp.c */ +/* base/src/drv $Id$ + * routines which are used to test and interface with the + * FP10S fast protect module + * + * Author: Matthew Stettler + * Date: 6-92 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 joh 070992 integrated into GTACS & added std header + * .02 joh 070992 merged in include file fp.h + * .03 joh 070992 converted some symbols to LOCAL so they stay out + * of the vxWorks global symbol table + * .04 joh 070992 took out sysSetBCL() and substituted + * sysIntEnable() so this will not be hkv2f + * specific. + * .05 joh 070992 added INUM_TO_IVEC so this will be less + * 68k dependence (added include of iv.h) + * .06 joh 070992 FP_ILEV passed to call sysIntEnable() so that the + * interrupt level can be easily changed + * .07 joh 070992 changed some printf() calls to logMsg() + * so that driver diagnostics will show up in + * the log + * .08 joh 071092 now fetches base addr from module_types.h + * .09 joh 071092 added io_report routine + * .10 joh 071092 added scan task wakeup from ISR + * .11 joh 071092 moved ivec allocation to module_types.h + * .12 joh 072792 added soft reboot int disable + * .13 joh 082792 converted to V5 vxorks + * .14 mrk 090192 support epics I/O event scan, and added DRVET + * .15 mrk 080293 Add call to taskwdInsert + * .16 mgb 080493 Removed V5/V4 and EPICS_V2 conditionals + */ + + + +/* + * + * Routines: + * + * fp_init Finds and initializes fast protect cards + * fp_driver System interface to FP10S modules + * fp_int Interrupt service routine + * fp_en Enables/disables interrupts (toggles) + * fp_mode Sets interrupt reporting mode + * fp_reboot Clean up for soft reboots + * + * Diagnostic Routines: + * + * fp_srd Reads current local inputs and enables + * fp_frd Reads last failure register + * fp_csrd Reads control/status register + * fp_read Command line interface to fp_driver + * fp_dump Prints all fast protect status to console + * fp_monitor Monitor all cards and print failure data to + * console + * + * Routines Return: + * + * -1 No card present + * -2 Interrupt connection error + * -3 Semaphore creation error + * -4 addressing error + * -5 no memory + * 0-8 successfull completion, or # of cards found + * + */ + +static char *sccsId = "@(#)drvFp.c 1.12\t6/4/93"; + +#include "vxWorks.h" +#include "vme.h" +#include "taskLib.h" +#include /* in h/68k if this is compiling for a 68xxx */ + +#include "module_types.h" +#include +#include +#include +#include + +static long report(); +static long init(); +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvFp={ + 2, + report, + init}; + +static long report() +{ + fp_io_report(); +} + +static long init() +{ + fp_init(0); + return(0); +} + +/* general constants */ +#define FP_INTLEV 5 /* interrupt level */ +#define FP_BUFSIZ 8 /* input buffer size */ + +/* csr bit definitions */ +#define CSR_RST 0x1 /* reset status */ +#define CSR_CMD1 0x2 /* force local fail */ +#define CSR_IEN 0x4 /* interrupt enable */ +#define CSR_UDEF0 0x8 /* undefined */ +#define CSR_I0 0x10 /* interrupt level bit #1 */ +#define CSR_I1 0x20 /* interrupt level bit #2 */ +#define CSR_I2 0x40 /* interrupt level bit #3 */ +#define CSR_UDEF1 0x80 /* undefined */ +#define CSR_CARM0_L 0x100 /* latched carrier monitor #0 (one shot) */ +#define CSR_CARM1_L 0x200 /* latched carrier monitor #1 (freq mon) */ +#define CSR_OPTIC 0x400 /* optical carrier input enabled */ +#define CSR_CARM 0x800 /* carrier OK */ +#define CSR_LFAIL0 0x1000 /* local fail #0 (pal monitor) */ +#define CSR_LFAIL1 0x2000 /* local fail #1 (fpga monitor) */ +#define CSR_CMON 0x4000 /* clock fail (one shot) */ +#define CSR_CHNG 0x8000 /* enable switch configuration change */ + +/* csr mask definitions */ +#define CSR_STM 0xff00 /* status mask */ +#define CSR_IM 0x70 /* interrupt level mask */ + +/* driver status */ +#define DRV_MOM 0x010000 /* momentary fault */ +#define DRV_LOC 0x020000 /* local fault */ +#define DRV_REM 0x040000 /* remote fault */ +#define DRV_CLR 0x080000 /* fault cleared */ +#define DRV_HWF 0x800000 /* hardware fault */ + +/* operating modes */ +#define FP_NMSG 0 /* no messages to console */ +#define FP_TMSG 1 /* terse messages to console */ +#define FP_FMSG 2 /* full messages to console */ +#define FP_RUN 3 /* normal operating mode */ + +/* register address map for FP10s */ +struct fp1 + { + unsigned short csr; /* control and status register */ + unsigned short srd; /* current status */ + unsigned short frd; /* latched status */ + unsigned short ivec; /* interrupt vector */ + char end_pad[0xff-0x8]; /* pad to 256 byte boundary */ + }; + +/* fast protect control structure */ +struct fp_rec + { + struct fp1 *fptr; /* pointer to device registers */ + unsigned int drvstat; /* fast protect physical inputs */ + unsigned short lastswitch; /* previous enable switch data */ + short type; /* device type */ + short num; /* device number */ + short fp_vector; /* interrupt vector */ + short mode; /* operating mode */ + unsigned int int_num; /* interrupt number */ + IOSCANPVT ioscanpvt; + }; + +static struct fp_rec *fp; /* fast protect control structure */ +static int fp_num; /* # of fast protect cards found -1 */ +static SEM_ID fp_semid; /* semaphore for monitor task */ + +static void fp_reboot(); + +/* + * fp_int + * + * interrupt service routine + * + */ +fp_int(card) +unsigned card; +{ + register struct fp_rec *ptr = &fp[card]; + register struct fp1 *regptr; + unsigned short temp0, temp1, temp2; + + regptr = ptr->fptr; + temp0 = regptr->csr; + temp1 = regptr->frd; + temp2 = regptr->srd; + switch (ptr->mode) + { + case FP_TMSG: + logMsg("fast protect interrupt!\n"); + logMsg("csr status = %x\n",temp0); + break; + case FP_FMSG: + logMsg("fast protect #%d fault! fault input = %x enable switches = %x\n", + ptr->num,temp1 & 0xff,temp2>>8); + logMsg("csr status = %x\n",temp0); + break; + case FP_RUN: + ptr->drvstat = temp2; /* save last switch data */ + ptr->drvstat |= temp1<<16; /* save fault data */ + ptr->drvstat |= (temp0 & 0xff00)<<16; /* csr status bits */ + if ((temp1 ^ (temp2>>8)) || (temp0 & CSR_CHNG)) /* fault or enable change */ + semGive(fp_semid); /* wake up monitor */ + + /* + * wakeup the interrupt driven scanner + */ + scanIoRequest(fp[card].ioscanpvt); + break; + } + ptr->int_num++; /* log interrupt */ + regptr->csr |= CSR_RST; /* clear status and rearm */ + regptr->csr ^= CSR_RST; +} + + +/* + * fp_init + * + * initialization routine for FP10s fast protect modules + * + * + */ +fp_init(addr) +unsigned int addr; +{ + int i; + short junk; + short intvec = AT8FP_IVEC_BASE; + struct fp1 *ptr; + int status; + + fp = (struct fp_rec *) calloc(bi_num_cards[AT8_FP10S_BI], sizeof(*fp)); + if(!fp){ + return -5; + } + + if(!addr){ + addr = bi_addrs[AT8_FP10S_BI]; + } + + status = sysBusToLocalAdrs( VME_AM_SUP_SHORT_IO, addr, &ptr); + if(status<0){ + logMsg("VME shrt IO addr err in the slave fast protect driver\n"); + return(-4); + } + + status = rebootHookAdd(fp_reboot); + if(status<0){ + logMsg("%s: reboot hook add failed\n", __FILE__); + } + + for (i = 0; + (i < bi_num_cards[AT8_FP10S_BI]) && (vxMemProbe(ptr,READ,2,&junk) == OK); + i++,ptr++) { + + /* register initialization */ + ptr->csr = 0x0000; /* disable interface */ + fp[i].fptr = ptr; /* hardware location */ + fp[i].fp_vector = intvec++; /* interrupt vector */ + ptr->ivec = fp[i].fp_vector; /* load vector */ + fp[i].mode = FP_NMSG; /* set default mode (no messages) */ + fp[i].int_num = 0; /* initialize interrupt number */ + fp[i].type = 10; /* board type */ + fp[i].num = i; /* board number */ + + /* initialize input buffer */ + fp[i].drvstat = ptr->srd; /* initialize enable switch data */ + fp[i].drvstat |= ptr->frd<<16; /* initialize fault data */ + fp[i].drvstat |= (ptr->csr & 0xff00)<<16; /* csr status bits */ + + /* set up interrupt handler */ + ptr->csr |= FP_INTLEV<<4; /* level 5 interrupt */ + if (intConnect(INUM_TO_IVEC(fp[i].fp_vector),fp_int,i) != OK) + return(-2); /* abort if can't connect */ + sysIntEnable(FP_INTLEV); + ptr->csr |= 0x0001; + ptr->csr ^= 0x0001; /* clear status bits */ + if (ptr->csr & CSR_OPTIC) + logMsg("fast protect #%d optically coupled\n",i); + else + logMsg("fast protect #%d elecrically coupled\n",i); + + /* start up module */ + fp[i].fptr->csr |= CSR_IEN; /* enable interrupts */ + fp[i].mode = FP_RUN; /* normal run mode */ + scanIoInit(&fp[i].ioscanpvt); + } + fp_num = i - 1; /* record max card # */ + + /* create the semaphore */ + fp_semid = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + if ((int)fp_semid == 0) /* abort if can't create semaphore */ + return(-3); + + return(i); /* return # found */ +} + + +/* + * + * fp_reboot() + * + * turn off interrupts to avoid ctrl X reboot problems + */ +LOCAL +void fp_reboot() +{ + int i; + + if(!fp){ + return; + } + + for (i = 0; i < bi_num_cards[AT8_FP10S_BI]; i++){ + + if(!fp[i].fptr){ + continue; + } + + fp[i].fptr->csr &= ~CSR_IEN; + } +} + + + +/* + * fp_en + * + * interrupt enable/disable + * (toggles the interrupt enable - joh) + * + */ +fp_en(card) +short card; +{ + unsigned short temp; + + if (card < 0 || (card > fp_num)) + return -1; + fp[card].fptr->csr = fp[card].fptr->csr ^ CSR_IEN; + if (fp[card].fptr->csr & CSR_IEN) + printf("fast protect interrupts enabled\n"); + else + printf("fast protect interrupts disabled\n"); + return 0; +} +/* + * fp_mode + * + * set interrupt reporting mode + * + */ +fp_mode(card,mode) + short card, mode; +{ + if (card < 0 || (card > fp_num)) + return -1; + fp[card].mode = mode; + return 0; +} +/* + * fp_srd + * + * read current local inputs and enable switches + * + */ +fp_srd(card,option) + short card; + short option; +{ + if (card > fp_num) return -1; + if (!option) + printf("local inputs = %x enable switches = %x\n",fp[card].fptr->srd & 0xff, + fp[card].fptr->srd>>8); + return fp[card].fptr->srd; +} +/* + * fp_frd + * + * read latched local inputs + * + */ +fp_frd(card) + short card; +{ + if (card < 0 || (card > fp_num)) + return -1; + return fp[card].fptr->frd & 0xff; +} +/* + * fp_csrd + * + * read csr contents + * + */ +fp_csrd(card) + short card; +{ + if (card < 0 || (card > fp_num)) + return -1; + return fp[card].fptr->csr & 0xff77; +} +/* + * fp_driver + * + * epics interface to fast protect + * + */ +fp_driver(card,mask,prval) + register unsigned short card; + unsigned int mask; + register unsigned int *prval; +{ + register unsigned int temp; + + if (card > fp_num) return -1; + temp = fp[card].drvstat & 0xffff0000; /* latched status info */ + temp |= fp[card].fptr->srd; /* current switches & inputs */ + *prval = temp & mask; + return 0; +} +/* + * fp_read + * + * command line interface to fp_driver + * + */ +fp_read(card) + short card; +{ + unsigned int fpval,ret; + + if ((ret = fp_driver(card,0xffffffff,&fpval)) != 0) + return ret; + printf("Card #%d enable switches = %x inputs = %x\n",card,(fpval & 0x0000ff00)>>8, + fpval & 0x000000ff); + printf("csr status = %x last fault = %x\n",fpval>>24,(fpval & 0x00ff0000)>>16); + printf("raw readback = %x\n",fpval); + return 0; +} +/* + * fp_dump + * + * dump fast protect status to console + * + */ +fp_dump() +{ + int i; + + printf("Fast protect status (fault and CSR are latched):\n"); + printf("Card#\tenables\tinputs\tfault\tCSR status\n"); + for(i = 0; i < (fp_num + 1); i++) + printf("%d\t%x\t%x\t%x\t%x\n",i,fp[i].fptr->srd>>8,fp[i].fptr->srd & 0xff, + (fp[i].drvstat & 0x00ff0000)>>16,fp[i].drvstat>>24); + return i; +} +/* + * fp_monitor + * + * monitor fast protect cards and report failures to console + * + */ +fp_mon() +{ + for(semTake(fp_semid,WAIT_FOREVER);fp_dump() != 0;semTake(fp_semid,WAIT_FOREVER)); +} +fp_monitor() +{ + static char *name = "fpmon"; + int tid; + + if ((tid = taskNameToId(name)) != ERROR) { + taskwdRemove(tid); + taskDelete(tid); + } + if((tid = taskSpawn(name,25,VX_SUPERVISOR_MODE|VX_STDIO, + 1000,fp_mon)) == ERROR) return -1; + taskwdInsert(tid,NULL,NULL); + return 0; +} + +fp_io_report(level) +int level; +{ + int i; + + for(i=0; i<=fp_num; i++){ + printf("BI: AT8-FP-S: card %d\n", i); + } +} + +fp_getioscanpvt(card,scanpvt) +short card; +IOSCANPVT *scanpvt; +{ + if ((card >= bi_num_cards[AT8_FP10S_BI])) return(0); + *scanpvt = fp[card].ioscanpvt; + return(0); +} diff --git a/src/vxWorks/drv/old/drvFpm.c b/src/vxWorks/drv/old/drvFpm.c new file mode 100644 index 000000000..0f4105756 --- /dev/null +++ b/src/vxWorks/drv/old/drvFpm.c @@ -0,0 +1,432 @@ +/* drvFpm.c */ +/* base/src/drv $Id$ */ + +/* + * control routines for use with the FP10M fast protect master modules + * + * routines which are used to test and interface with the + * FP10S fast protect module + * + * Author: Matthew Stettler + * Date: 6-92 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 joh 070992 integrated into GTACS & added std header + * .02 joh 070992 merged in include file fpm.h + * .03 joh 070992 converted some symbols to LOCAL so they stay out + * of the vxWorks global symbol table + * .04 joh 070992 took out sysSetBCL() and substituted + * sysIntEnable() so this will not be hkv2f + * specific. + * .05 joh 070992 added INUM_TO_IVEC so this will be less + * 68k dependence (added include of iv.h) + * .06 joh 070992 FP_ILEV passed to call sysIntEnable() so that the + * interrupt level can be easily changed + * .07 joh 071092 now fetches base addr from module_types.h + * .08 joh 071092 added io report routine + * .09 joh 071092 allocate config structure at run time so that + * the users can adjust the number of cards without + * recompilation + * .10 joh 071092 moved ivec allocation to module_types.h + * .11 joh 072792 added soft reboot int disable + * .12 mrk 090292 added DRVET + * .13 mgb 080493 Removed V5/V4 and EPICS_V2 conditionals + * + * + * Routines: + * + * fpm_init Finds and initializes FP10M cards + * fpm_driver System interface to FP10M modules + * fpm_read Carrier control readback + * fpm_reboot clean up before soft reboots + * + * Daignostic Routines + * fpm_en Enables/disables interrupts (diagnostic enable) + * fpm_mode Sets interrupt reporting mode (logs mode + * changes to console) + * fpm_cdis Disables carrier from console + * fpm_fail Sets carrier failure mode + * fpm_srd Reads current carrier status + * fpm_write Command line interface to fpm_driver + * + * Routines return: + * + * -1 Nonexistent card + * -2 Interrupt connection error + * -3 no memory + * -4 VME short IO bus nonexistent + * 0-2 Successfull completion, or # cards found + * + */ + +static char *sccsId = "@(#)drvFpm.c 1.12\t8/4/93"; + +#include "vxWorks.h" +#include "vme.h" +#include /* in h/68k if this is compiling for a 68xxx */ +#include "module_types.h" +#include +#include + +static long report(); +static long init(); +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvFpm={ + 2, + report, + init}; + +static long report() +{ + fpm_io_report(); +} + +static long init() +{ + fpm_init(0); + return(0); +} + +/* general constants */ +#define FPM_INTLEV 5 /* interrupt level */ + +/* control register bit definitions */ +#define CR_CDIS 0x1 /* software carrier disable */ +#define CR_FS0 0x2 /* fail select 0 */ +#define CR_FS1 0x4 /* fail select 1 */ +#define CR_FS2 0x8 /* fail select 2 */ +#define CR_I0 0x10 /* interrupt level bit 0 */ +#define CR_I1 0x20 /* interrupt level bit 1 */ +#define CR_I2 0x40 /* interrupt level bit 2 */ +#define CR_IEN 0x80 /* interrupt enable */ + +/* control register mask definitions */ +#define CR_IM 0x70 /* interrupt level mask */ + +/* status register bit definitions */ +#define SR_S0 0x1 /* error sequencer state bit 0 */ +#define SR_S1 0x2 /* error sequencer state bit 1 */ +#define SR_S2 0x3 /* error sequencer state bit 2 */ + +/* status register mask definitions */ +#define SR_EM 0x7 /* error state mask */ + +/* operating modes */ +#define FPM_NMSG 0 /* no messages to console */ +#define FPM_TMSG 1 /* terse messages to console */ +#define FPM_FMSG 2 /* full messages to console */ + +/* register address map for FP10M */ +struct fp10m + { + unsigned short cr; /* control register */ + unsigned short sr; /* status register */ + unsigned short ivec; /* interrupt vector */ + char end_pad[0xff-0x6]; /* pad to 256 byte boundary */ + }; + +/* control structure */ +struct fpm_rec + { + struct fp10m *fmptr; /* pointer to device registers */ + short type; /* device type */ + short num; /* board number */ + short vector; /* interrupt vector */ + short mode; /* operating mode */ + unsigned int int_num; /* interrupt number */ + }; + +static struct fpm_rec *fpm; /* fast protect control structure */ + +static int fpm_num; /* # cards found - 1 */ + +static void fpm_reboot(); + +/* + * fpm_int + * + * interrupt service routine + * + */ +fpm_int(ptr) + register struct fpm_rec *ptr; +{ + register struct fp10m *regptr; + + regptr = ptr->fmptr; + switch (ptr->mode) + { + case FPM_TMSG: + logMsg("fast protect master interrupt!\n"); + break; + case FPM_FMSG: + logMsg("fast protect master interrupt!\n"); + logMsg("cr = %x sr = %x\n",regptr->cr,regptr->sr & 0x7); + break; + } + ptr->int_num++; +} +/* + * fpm_init + * + * initialization for fp10m fast protect master modules + * + */ +fpm_init(addr) + unsigned int addr; +{ + int i; + short junk; + short intvec = AT8FPM_IVEC_BASE; + struct fp10m *ptr; + int status; + + fpm = (struct fpm_rec *) calloc( + bo_num_cards[AT8_FP10M_BO], + sizeof(*fpm)); + if(!fpm){ + return -3; + } + + if(!addr){ + addr = bo_addrs[AT8_FP10M_BO]; + } + + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + addr, + &ptr); + if(status<0){ + logMsg("VME shrt IO addr err in the master fast protect driver\n"); + return -4; + } + + status = rebootHookAdd(fpm_reboot); + if(status<0){ + logMsg("%s: reboot hook add failed\n", __FILE__); + } + + for (i = 0; (i < bo_num_cards[AT8_FP10M_BO]) && (vxMemProbe(ptr,READ,2,&junk) == OK); + i++,ptr++) + { + /* + register initialization + */ + ptr->cr = 0x00; /* disable interface */ + fpm[i].fmptr = ptr; /* hardware location */ + fpm[i].vector = intvec++; /* interrupt vector */ + ptr->ivec = fpm[i].vector; /* load vector */ + fpm[i].mode = FPM_NMSG; /* set default mode (no messages) */ + fpm[i].int_num = 0; /* initialize interrupt number */ + fpm[i].type = 2; /* board type */ + fpm[i].num = i; /* board number */ + /* + set up interrupt handler + */ + ptr->cr |= FPM_INTLEV<<4; /* set up board for level 5 interrupt */ + if (intConnect(INUM_TO_IVEC(fpm[i].vector),fpm_int,&fpm[i]) != OK) + return -2; /* abort if can't connect */ + sysIntEnable(FPM_INTLEV); + } + fpm_num = i - 1; /* record last card # */ + return i; /* return # cards found */ +} + + +/* + * + * fpm_reboot() + * + * turn off interrupts to avoid ctrl X reboot problems + */ +LOCAL +void fpm_reboot() +{ + int i; + + if(!fpm){ + return; + } + + for (i = 0; i < bo_num_cards[AT8_FP10M_BO]; i++){ + + if(!fpm[i].fmptr){ + continue; + } + + fpm[i].fmptr->cr &= ~CR_IEN; + } +} + +/* + * fpm_en + * + * interrupt enable/disable + * (toggles the int enable state - joh) + * + */ +fpm_en(card) + short card; +{ + if (card < 0 || (card > fpm_num)) + return -1; + fpm[card].fmptr->cr ^= CR_IEN; + if (fpm[card].fmptr->cr & CR_IEN) + printf("fast protect master interrupts enabled\n"); + else + printf("fast protect master interrupts disabled\n"); + return 0; +} +/* + * fpm_mode + * + * set interrupt reporting mode + * + */ +fpm_mode(card,mode) + short card, mode; +{ + if (card < 0 || (card > fpm_num)) + return -1; + fpm[card].mode = mode; + return 0; +} +/* + * fpm_cdis + * + * carrier disable (1), enable (0) + * + */ +fpm_cdis(card,disable) + short card, disable; +{ + unsigned short temp; + + if (card < 0 || (card > fpm_num)) + return -1; + temp = fpm[card].fmptr->cr; + temp &= 0xfe; + temp |= (disable & 0x01); + fpm[card].fmptr->cr = temp; + return 0; +} +/* + * fpm_fail + * + * set failure mode + * + */ +fpm_fail(card,mode) + short card, mode; +{ + unsigned short temp; + + if (card < 0 || (card > fpm_num)) + return -1; + temp = fpm[card].fmptr->cr; + temp &= 0xf1; + temp |= (mode & 0x7)<<1; + fpm[card].fmptr->cr = temp; + return 0; +} +/* + * fpm_srd + * + * read status bits + * + */ +fpm_srd(card) + short card; +{ + if (card < 0 || ( card > fpm_num)) + return -1; + return fpm[card].fmptr->sr & 0x7; +} +/* + * fpm_driver + * + * epics interface to fast protect master + * + */ +fpm_driver(card,mask,prval) +register unsigned short card; +unsigned int mask; +register unsigned int prval; +{ + register unsigned int temp; + + if (card > fpm_num) + return -1; + temp = fpm[card].fmptr->cr; + fpm[card].fmptr->cr = (temp & (~mask | 0xf0)) | ((prval & mask) & 0xf); + return 0; +} +/* + * fpm_write + * + * command line interface to fpm_driver + * + */ +fpm_write(card,val) + short card; + unsigned int val; +{ + return fpm_driver(card,0xffffffff,val); +} +/* + * fpm_read + * + * read the current control register contents (readback) + * + */ +fpm_read(card,mask,pval) +register unsigned short card; +unsigned int mask; +register unsigned int *pval; +{ + if (card > fpm_num) + return -1; + *pval = fpm[card].fmptr->cr & 0x000f; + return 0; +} + + + +/* + * fpm_io_report() + * + */ +fpm_io_report(level) +int level; +{ + int i; + + for(i=0; i<=fpm_num; i++){ + printf("BO: AT8-FP-M: card %d\n", i); + } +} diff --git a/src/vxWorks/drv/old/drvJgvtr1.c b/src/vxWorks/drv/old/drvJgvtr1.c new file mode 100644 index 000000000..fbe1eb589 --- /dev/null +++ b/src/vxWorks/drv/old/drvJgvtr1.c @@ -0,0 +1,641 @@ +/* drvJgvtr1.c */ +/* base/src/drv $Id$ */ +/* + * Author: Jeff Hill + * Date: 5-89 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * 110689 joh print mem not full message only once + * 120789 joh temporary removal of memory full check + * 050190 joh clear ptr to callback prior to calling + * it so they can rearm from inside the callback + * 071190 joh check STD address after cycle complete detected + * to avoid erroneous card misaddressed messages + * 071190 joh internal sample rate status is bit reversed on the + * card- I added a lookup table to untwist it. + * 020491 ges Change taskDelay from 20 to 2 in "jgvtr1DoneTask". + * To allow rearm and data reads from succesive + * waveform scans up thru 10Hz rates. + * 031491 lrd move data into a local memory area for each card + * 090591 joh converted to V5 vxWorks + * 110591 lrd initialization of cards other than 0 not + * allocating data buffer correctly + * 013092 bg added sysBusToLocalAdrs. Added levels to io_report + * and the ability to read out the Joerger's raw values + * in io_report if level is > 1. + * 031992 joh Took the vxMemProbe out of each arm and checked + * the card present bit instead. + * 062592 bg Combined drvJgvtr1.c and jgvtr_driver.c + * 062992 joh removed file pointer argument added to io + * report by bg + * 082792 joh added ANSI C function prototypes + * 080293 mrk added call to taskwdInsert + * 080493 mgb Removed V5/V4 and EPICS_V2 conditionals + */ + +static char *sccsID = "@(#)drvJgvtr1.c 1.17\t9/9/93"; + +/* + * Code Portions + * + * jgvtr1_init() + * jgvtr1_driver(card, pcbroutine, parg) + * jgvtr1_int_service() + * jgvtr1DoneTask() + * jgvtr1_io_report() + * jgvtr1_stat(card) + * + */ + +/* drvJgvtr1.c - Driver Support Routines for Jgvtr1 */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +LOCAL jgvtr1Stat jgvtr1_io_report( + unsigned level +); + +LOCAL jgvtr1Stat jgvtr1_init( + void +); + +#ifdef INTERRUPT_HARDWARE_FIXED +LOCAL void jgvtr1_int_service( + void +); +#endif + +LOCAL void jgvtr1DoneTask( + void +); + +LOCAL jgvtr1Stat jgvtr1_dump( + unsigned card, + unsigned n +); + +LOCAL jgvtr1Stat jgvtr1_stat( + unsigned card, + int level +); + +struct { + long number; + DRVSUPFUN report; + DRVSUPFUN init; +} drvJgvtr1={ + 2, + jgvtr1_io_report, + jgvtr1_init}; + +static volatile char *stdaddr; +static volatile char *shortaddr; + + +#define JGVTR1MAXFREQ 25.0e6 +/* NBBY - the number of bits per byte */ +#define JGVTR1SHORTSIZE (1<<(NBBY*sizeof(uint8_t))) +#define JGVTR1STDSIZE (1<<(NBBY*sizeof(uint16_t))) +#define JGVTR1_INT_LEVEL 5 +#define JGVTR1BASE(CARD)\ +(shortaddr+wf_addrs[JGVTR1]+(CARD)*JGVTR1SHORTSIZE) +#define JGVTR1DATA(CARD)\ +(stdaddr+wf_memaddrs[JGVTR1]+(CARD)*JGVTR1STDSIZE) + + +/* +Joerger fixed hardware bug by switching to an inverting tristate buffer +where these commands are read from the VME bus. As a result these commands +are complemented. +*/ +#define JGVTR1ARM (~1) +#define JGVTR1START (~2) +#define JGVTR1STOP (~4) + + + +/* +!! our compiler allocates bit fields starting from the ms bit !! +*/ +struct jgvtr1_status{ + volatile unsigned pad:8; + volatile unsigned internal_frequency:3; + volatile unsigned internal_clock:1; + volatile unsigned cycle_complete:1; + volatile unsigned interrupt:1; + volatile unsigned active:1; + volatile unsigned memory_full:1; +}; + +struct jgvtr1_config{ + char present; /* card present */ + char std_ok; /* std addr ok on first read */ + void (*psub) /* call back routine */ + (void *pprm, unsigned nbytes, uint16_t *pData); + void *pprm; /* call back parameter */ + FAST_LOCK lock; /* mutual exclusion */ + uint16_t *pdata; /* pointer to the data buffer */ +}; + +/* amount of data to make available from the waveform */ +#define JRG_MEM_SIZE 2048 + +LOCAL +struct jgvtr1_config *pjgvtr1_config; + +LOCAL +int jgvtr1_max_card_count; + +#ifdef INTERRUPT_HARDWARE_FIXED +LOCAL +SEM_ID jgvtr1_interrupt; /* interrupt event */ +#endif + + + +/* + * JGVTR1_INIT + * + * intialize the driver for the joerger vtr1 + * + */ +jgvtr1Stat jgvtr1_init(void) +{ + unsigned card; + unsigned card_count = 0; + struct jgvtr1_config *pconfig; + uint16_t readback; + jgvtr1Stat status; + + + status = sysBusToLocalAdrs( + VME_AM_SUP_SHORT_IO, + 0, + (char **)&shortaddr); + if (status != OK){ + status = S_dev_badA16; + errMessage(status,NULL); + return status; + } + + status = sysBusToLocalAdrs( + VME_AM_STD_SUP_DATA, + 0, + (char **)&stdaddr); + if (status != OK){ + status = S_dev_badA24; + errMessage(status,NULL); + return status; + } + + jgvtr1_max_card_count = wf_num_cards[JGVTR1]; + + if(pjgvtr1_config){ + if(FASTLOCKFREE(&pjgvtr1_config->lock)<0) + return ERROR; + free(pjgvtr1_config); + } + + pjgvtr1_config = + (struct jgvtr1_config *) + calloc(wf_num_cards[JGVTR1], sizeof(*pjgvtr1_config)); + if(!pjgvtr1_config){ + status = S_dev_noMemory; + errMessage(status,NULL); + return status; + } + + for( card=0, pconfig=pjgvtr1_config; + card < wf_num_cards[JGVTR1]; + pconfig++, card++){ + + FASTLOCKINIT(&pconfig->lock); + + status = vxMemProbe( (char *)JGVTR1BASE(card), + READ, + sizeof(readback), + (char *)&readback); + if(status==ERROR) + continue; + + + pconfig->pdata = + (uint16_t *)malloc(JRG_MEM_SIZE); + /* + not easy to test for correct addressing in + standard address space since the module does + not respond if it has not clocked in data + + - so I check this the first time data is ready + */ + pconfig->std_ok = FALSE; /* presumed guilty before tested */ + pconfig->present = TRUE; + card_count++; + } + + if(!card_count) + return OK; + + +# ifdef INTERRUPT_HARDWARE_FIXED + jgvtr1_interrupt = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + if(!jgvtr1_interrupt) + return ERROR; +# endif + + /* start the waveform readback task */ + status = taskSpawn( WFDONE_NAME, + WFDONE_PRI, + WFDONE_OPT, + WFDONE_STACK, + (FUNCPTR) jgvtr1DoneTask, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + if(status < 0){ + status = S_dev_internal; + errMessage(status, "vxWorks taskSpawn failed"); + return status; + } + + taskwdInsert(status, NULL, NULL); + + +# ifdef INTERRUPT_HARDWARE_FIXED + status = intConnect( INUM_TO_IVEC(JGVTR1_INT_VEC), + jgvtr1_int_service, + NULL); + if(status != OK) + return S_dev_internal; + sysIntEnable(JGVTR_INT_LEVEL); +# endif + + return JGVTR1_SUCCESS; +} + + + +/* + * JGVTR1_DRIVER + * + * initiate waveform read + * + */ +jgvtr1Stat jgvtr1_driver( +unsigned card, +void (*pcbroutine)(void *, unsigned, uint16_t *), +void *parg +) +{ + if(card >= jgvtr1_max_card_count) + return S_dev_badSignalNumber; + + if(!pjgvtr1_config[card].present) + return S_dev_noDevice; + + if(pjgvtr1_config[card].psub) + return S_dev_badRequest; + + FASTLOCK(&pjgvtr1_config[card].lock); + + *(volatile uint16_t *)JGVTR1BASE(card) = JGVTR1ARM; + + pjgvtr1_config[card].pprm = parg; + pjgvtr1_config[card].psub = pcbroutine; + + FASTUNLOCK(&pjgvtr1_config[card].lock); + + return JGVTR1_SUCCESS; +} + + +/* + * JGVTR1_INT_SERVICE + * + * signal via the RTK that an interrupt occured from the joerger vtr1 + * + */ +#ifdef INTERRUPT_HARDWARE_FIXED +LOCAL void jgvtr1_int_service(void) +{ + semGive(jgvtr1_interrupt); +} +#endif + + +/* + * JGVTR1DONETASK + * + * wait for joerger vtr1 waveform record cycle complete + * and call back to the database with the waveform size and address + * + */ +LOCAL void jgvtr1DoneTask(void) +{ + unsigned card; + struct jgvtr1_config *pconfig; + struct jgvtr1_status stat; + static char started = FALSE; + volatile uint16_t *pdata; + volatile uint16_t *pjgdata; + long i; + + /* dont allow two of this task */ + if(started) + exit(0); + started = TRUE; + + while(TRUE){ + +# ifdef INTERRUPT_HARDWARE_FIXED + semTake(jgvtr1_interrupt, WAIT_FOREVER); +# else + /* ges: changed from 20 ticks to 2 ticks 2/4/91 */ + taskDelay(2); +# endif + + for( card=0, pconfig = pjgvtr1_config; + card < jgvtr1_max_card_count; + card++, pconfig++){ + + if(!pconfig->present) + continue; + + if(!pconfig->psub) + continue; + + stat = *(struct jgvtr1_status *) JGVTR1BASE(card); + /* + * Wait for the module to finish filling its memory + * or a stop trigger + */ + if(!stat.cycle_complete) + continue; + + /* + clear ptr to function here so they + can rearm in the callback + */ + pconfig->psub = NULL; + /* + check the first time for module + correctly addressed + + card does not respond at STD address + until it has data + */ + if(!pconfig->std_ok){ + uint16_t readback; + int status; + + status = vxMemProbe( + (char *)JGVTR1DATA(card), + READ, + sizeof(readback), + (char *)&readback); + if(status==ERROR){ + errPrintf( + S_dev_badA24, + __FILE__, + __LINE__, + "jgvtr1 card %d incorrectly addressed- use std addr 0X%X", + card, + JGVTR1DATA(card)); + pconfig->present = FALSE; + continue; + } + pconfig->std_ok = TRUE; + } +/* + Test for full memory + ( card designer does not give a sample count register ) + ( instead a bus error returned when on the last sample + to test every location is a lot of overhead so a test + for memory full is used for now ) +*/ + if(!stat.memory_full){ + errMessage(S_dev_internal, + "jgvtr1 driver: proceeding with partial mem"); + errMessage(S_dev_internal, + "jgvtr1 driver: beware of bus errors"); + } + + /* copy the data into a local memory buffer */ + /* this is to avoid any bus errors */ + for(i = 0, + pdata = pconfig->pdata, + pjgdata = (volatile uint16_t *)JGVTR1DATA(card); + i < JRG_MEM_SIZE/sizeof(uint16_t); + i++, pdata++, pjgdata++){ + *pdata = *pjgdata; + } + +/* + Post waveform to the database + perhaps the size must be the size below+1 ? + (Joerger's documentation is not clear here) +*/ + (*pconfig->psub)(pconfig->pprm,JRG_MEM_SIZE,pconfig->pdata); + } + } +} + + + +/* + * + * JGVTR1_IO_REPORT + * + * print status for all cards in the specified joerger + * vtr1 address range + * + * + */ +LOCAL jgvtr1Stat jgvtr1_io_report(unsigned level) +{ + unsigned card; + unsigned nelements; + jgvtr1Stat status; + + for(card=0; card < wf_num_cards[JGVTR1]; card++){ + status = jgvtr1_stat(card,level); + if(status){ + continue; + } + if (level >= 2){ + printf("enter the number of elements to dump:"); + status = scanf("%d",&nelements); + if(status == 1){ + jgvtr1_dump(card, nelements); + } + } + } + return JGVTR1_SUCCESS; +} + + + +/* + * + * JGVTR1_STAT + * + * print status for a single card in the joerger vtr1 address range + * + * + */ +jgvtr1Stat jgvtr1_stat( +unsigned card, +int level +) +{ + struct jgvtr1_status stat; + jgvtr1Stat status; + + /* + internal freq status is bit reversed so I + use a lookup table + */ + + static float sample_rate[8] = { + JGVTR1MAXFREQ/(1<<7), + JGVTR1MAXFREQ/(1<<3), + JGVTR1MAXFREQ/(1<<5), + JGVTR1MAXFREQ/(1<<1), + JGVTR1MAXFREQ/(1<<6), + JGVTR1MAXFREQ/(1<<2), + JGVTR1MAXFREQ/(1<<4), + JGVTR1MAXFREQ/(1<<0)}; + + static char *clock_status[] = + {"ext-clock", "internal-clk"}; + static char *cycle_status[] = + {"cycling", "done"}; + static char *interrupt_status[] = + {"", "int-pending"}; + static char *activity_status[] = + {"", "active"}; + static char *memory_status[] = + {"", "mem-full"}; + + status = vxMemProbe( (char *)JGVTR1BASE(card), + READ, + sizeof(stat), + (char *)&stat); + if(status != OK) + return ERROR; + if (level == 0) + printf("WF: JGVTR1:\tcard=%d \n",card); + else if (level > 0) + printf( "WF: JGVTR1:\tcard=%d Sample rate=%g %s %s %s %s %s \n", + card, + sample_rate[stat.internal_frequency], + clock_status[ stat.internal_clock ], + cycle_status[ stat.cycle_complete ], + interrupt_status[ stat.interrupt ], + activity_status[ stat.active ], + memory_status[ stat.memory_full ]); + + return JGVTR1_SUCCESS; +} + + +/* + * jgvtr1_dump + * + */ +LOCAL jgvtr1Stat jgvtr1_dump( +unsigned card, +unsigned n +) +{ + volatile uint16_t *pjgdata; + uint16_t *pread; + uint16_t *pdata; + unsigned nread; + jgvtr1Stat status; + + /* Print out the data if user requests it. */ + + n = min(JRG_MEM_SIZE,n); + + pdata = (uint16_t *)malloc(n * (sizeof(*pdata))); + if(!pdata){ + return S_dev_noMemory; + } + + pread = pdata; + nread = 0; + pjgdata = (volatile uint16_t *)JGVTR1DATA(card); + while(nread <= (n>>1)){ + status = vxMemProbe( + (char *)pjgdata, + READ, + sizeof(*pread), + (char *)pread); + if(status<0){ + break; + } + nread++; + pread++; + pjgdata++; + } + + for(pread=pdata; pread<&pdata[nread]; pread++){ + if ((pread-pdata)%8 == 0){ + printf("\n\t"); + } + printf( "%02X %02X ", + (unsigned char) ((*pread)>>8), + (unsigned char) *pread); + } + + printf("\n"); + + free(pdata); + + return JGVTR1_SUCCESS; +} diff --git a/src/vxWorks/drv/old/drvJgvtr1.h b/src/vxWorks/drv/old/drvJgvtr1.h new file mode 100644 index 000000000..f4c3f6454 --- /dev/null +++ b/src/vxWorks/drv/old/drvJgvtr1.h @@ -0,0 +1,43 @@ +/* drvJgvtr1.h */ +/* base/src/drv $Id$ */ +/* + * Author: Jeff Hill + * Date: 5-89 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + */ + + + +typedef long jgvtr1Stat; + +#define JGVTR1_SUCCESS 0 + +jgvtr1Stat jgvtr1_driver( +unsigned card, +void (*pcbroutine)(void *, unsigned, uint16_t *), +void *parg +); + diff --git a/src/vxWorks/drv/old/module_types.c b/src/vxWorks/drv/old/module_types.c new file mode 100644 index 000000000..09996959b --- /dev/null +++ b/src/vxWorks/drv/old/module_types.c @@ -0,0 +1,241 @@ +/* module_types.c */ +/* base/src/drv $Id$ */ +/* + * Author: Marty Kraimer + * Date: 08-23-93 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 08-23-93 mrk Initial Version + */ + +#include + +module_types() +{ + +ai_num_cards[AB1771IL] = 12; +ai_num_cards[AB1771IFE] = 12; +ai_num_cards[AB1771IXE] = 12; +ai_num_cards[XY566SE] = 2; +ai_num_cards[XY566DI] = 2; +ai_num_cards[XY566DIL] = 2; +ai_num_cards[VXI_AT5_AI] = 32; +ai_num_cards[AB1771IFE_SE] = 12; +ai_num_cards[AB1771IFE_4to20MA] = 12; +ai_num_cards[DVX2502] = 1; +ai_num_cards[AB1771IFE_0to5V] = 12; +ai_num_cards[KSCV215] = 32; + +ai_num_channels[AB1771IL] = 8; +ai_num_channels[AB1771IFE] = 8; +ai_num_channels[AB1771IXE] = 8; +ai_num_channels[XY566SE] = 32; +ai_num_channels[XY566DI] = 16; +ai_num_channels[XY566DIL] = 16; +ai_num_channels[VXI_AT5_AI] = 8; +ai_num_channels[AB1771IFE_SE] = 16; +ai_num_channels[AB1771IFE_4to20MA] = 8; +ai_num_channels[DVX2502] = 127; +ai_num_channels[AB1771IFE_0to5V] = 8; +ai_num_channels[KSCV215] = 32; + +ai_addrs[AB1771IL] = 0; +ai_addrs[AB1771IFE] = 0; +ai_addrs[AB1771IXE] = 0; +ai_addrs[XY566SE] = 0x6000; +ai_addrs[XY566DI] = 0x7000; +ai_addrs[XY566DIL] = 0x7800; +ai_addrs[VXI_AT5_AI] = 0xc014; +ai_addrs[AB1771IFE_SE] = 0; +ai_addrs[AB1771IFE_4to20MA] = 0; +ai_addrs[DVX2502] = 0xff00; +ai_addrs[AB1771IFE_0to5V] = 0; +ai_addrs[KSCV215] = 0; + +ai_memaddrs[AB1771IL] = 0; +ai_memaddrs[AB1771IFE] = 0; +ai_memaddrs[AB1771IXE] = 0; +ai_memaddrs[XY566SE] = 0x000000; +ai_memaddrs[XY566DI] = 0x040000; +ai_memaddrs[XY566DIL] = 0x0c0000; +ai_memaddrs[VXI_AT5_AI] = 0; +ai_memaddrs[AB1771IFE_SE] = 0; +ai_memaddrs[AB1771IFE_4to20MA] = 0; +ai_memaddrs[DVX2502] = 0x100000; +ai_memaddrs[AB1771IFE_0to5V] = 0; +ai_memaddrs[KSCV215] = 0; + +ao_num_cards[AB1771OFE] = 12; +ao_num_cards[VMI4100] = 4; +ao_num_cards[ZIO085] = 1; +ao_num_cards[VXI_AT5_AO] = 32; + +ao_num_channels[AB1771OFE] = 4; +ao_num_channels[VMI4100] = 16; +ao_num_channels[ZIO085] = 32; +ao_num_channels[VXI_AT5_AO] = 16; + +ao_addrs[AB1771OFE] = 0; +ao_addrs[VMI4100] = 0x4100; +ao_addrs[ZIO085] = 0x0800; +ao_addrs[VXI_AT5_AO] = 0xc000; + +bi_num_cards[ABBI_08_BIT] = 12; +bi_num_cards[ABBI_16_BIT] = 12; +bi_num_cards[BB910] = 4; +bi_num_cards[XY210] = 2; +bi_num_cards[VXI_AT5_BI] = 32; +bi_num_cards[HPE1368A_BI] = 32; +bi_num_cards[AT8_FP10S_BI] = 8; +bi_num_cards[XY240_BI] = 2; + +bi_num_channels[ABBI_08_BIT] = 8; +bi_num_channels[ABBI_16_BIT] = 16; +bi_num_channels[BB910] = 32; +bi_num_channels[XY210] = 32; +bi_num_channels[VXI_AT5_BI] = 32; +bi_num_channels[HPE1368A_BI] = 16; +bi_num_channels[AT8_FP10S_BI] = 32; +bi_num_channels[XY240_BI] = 32; + +bi_addrs[ABBI_08_BIT] = 0; +bi_addrs[ABBI_16_BIT] = 0; +bi_addrs[BB910] = 0xb800; +bi_addrs[XY210] = 0xa000; +bi_addrs[VXI_AT5_BI] = 0xc000; +bi_addrs[HPE1368A_BI] = 0xc000; +bi_addrs[AT8_FP10S_BI] = 0x0e00; +bi_addrs[XY240_BI] = 0x3000; + +bo_num_cards[ABBO_08_BIT] = 12; +bo_num_cards[ABBO_16_BIT] = 12; +bo_num_cards[BB902] = 4; +bo_num_cards[XY220] = 1; +bo_num_cards[VXI_AT5_BO] = 32; +bo_num_cards[HPE1368A_BO] = 32; +bo_num_cards[AT8_FP10M_BO] = 2; +bo_num_cards[XY240_BO] = 2; + +bo_num_channels[ABBO_08_BIT] = 8; +bo_num_channels[ABBO_16_BIT] = 16; +bo_num_channels[BB902] = 32; +bo_num_channels[XY220] = 32; +bo_num_channels[VXI_AT5_BO] = 32; +bo_num_channels[HPE1368A_BO] = 16; +bo_num_channels[AT8_FP10M_BO] = 32; +bo_num_channels[XY240_BO] = 32; + +bo_addrs[ABBO_08_BIT] = 0; +bo_addrs[ABBO_16_BIT] = 0; +bo_addrs[BB902] = 0x0400; +bo_addrs[XY220] = 0xa800; +bo_addrs[VXI_AT5_BO] = 0xc000; +bo_addrs[HPE1368A_BO] = 0xc000; +bo_addrs[AT8_FP10M_BO] = 0xc000; +bo_addrs[XY240_BO] = 0x3000; + +sm_num_cards[CM57_83E] = 4; +sm_num_cards[OMS_6AXIS] = 4; + +sm_num_channels[CM57_83E] = 1; +sm_num_channels[OMS_6AXIS] = 6; + +sm_addrs[CM57_83E] = 0x8000; +sm_addrs[OMS_6AXIS] = 0x4000; + +wf_num_cards[XY566WF] = 2; +wf_num_cards[CAMAC_THING] = 4; +wf_num_cards[JGVTR1] = 4; +wf_num_cards[COMET] = 4; + +wf_num_channels[XY566WF] = 1; +wf_num_channels[CAMAC_THING] = 1; +wf_num_channels[JGVTR1] = 1; +wf_num_channels[COMET] = 4; + +wf_addrs[XY566WF] = 0x9000; +wf_addrs[CAMAC_THING] = 0; +wf_addrs[JGVTR1] = 0xB000; +wf_addrs[COMET] = 0xbc00; + +wf_armaddrs[XY566WF] = 0x5400; +wf_armaddrs[CAMAC_THING]= 0; +wf_armaddrs[JGVTR1] = 0; +wf_armaddrs[COMET] = 0; + +wf_memaddrs[XY566WF] = 0x080000; +wf_memaddrs[CAMAC_THING]= 0; +wf_memaddrs[JGVTR1] = 0xb80000; +wf_memaddrs[COMET] = 0xe0000000; + +tm_num_cards[MZ8310] = 4; +tm_num_cards[DG535] = 1; +tm_num_cards[VXI_AT5_TIME] = 32; + +tm_num_channels[MZ8310] = 10; +tm_num_channels[DG535] = 1; +tm_num_channels[VXI_AT5_TIME] = 10; + +tm_addrs[MZ8310] = 0x1000; +tm_addrs[DG535] = 0; +tm_addrs[VXI_AT5_TIME] = 0xc000; + +AT830X_1_addrs = 0x0400; +AT830X_1_num_cards = 2; +AT830X_addrs = 0xaa0000; +AT830X_num_cards = 2; + +xy010ScA16Base = 0x0000; + +EPICS_VXI_LA_COUNT = 32; +EPICS_VXI_A24_BASE = (char *) 0x900000; +EPICS_VXI_A24_SIZE = 0x100000; +EPICS_VXI_A32_BASE = (char *) 0x90000000; +EPICS_VXI_A32_SIZE = 0x10000000; + + +AI566_VNUM = 0xf8; +DVX_IVEC0 = 0xd0; +MD_INT_BASE = 0xf0; +MZ8310_INT_VEC_BASE = 0xe8; +AB_VEC_BASE = 0x60; +JGVTR1_INT_VEC = 0xe0; +AT830X_1_IVEC0 = 0xd4; +AT830X_IVEC0 = 0xd6; +AT8FP_IVEC_BASE = 0xa2; +AT8FPM_IVEC_BASE= 0xaa; + +BB_SHORT_OFF = 0x1800; +BB_IVEC_BASE = 0xa0; +BB_IRQ_LEVEL = 5; +PEP_BB_SHORT_OFF= 0x1c00; +PEP_BB_IVEC_BASE= 0xe8; + +NIGPIB_SHORT_OFF = 0x5000; +NIGPIB_IVEC_BASE = 100; +NIGPIB_IRQ_LEVEL = 5; + +return(0); +}