From b3af9b389ad563aefc2de3c654e0f75011aaf230 Mon Sep 17 00:00:00 2001 From: Jeff Hill Date: Wed, 26 Apr 1995 03:20:39 +0000 Subject: [PATCH] moved ansi drivers --- src/drv/ansi/VXI_SETUP_README | 30 + src/drv/ansi/drvAt5Vxi.c | 1410 +++++++++ src/drv/ansi/drvAt5Vxi.h | 94 + src/drv/ansi/drvCaenV265.c | 808 +++++ src/drv/ansi/drvEpvxi.c | 4620 ++++++++++++++++++++++++++++ src/drv/ansi/drvEpvxiMsg.c | 1537 +++++++++ src/drv/ansi/drvHp1404a.c | 408 +++ src/drv/ansi/drvHp1404a.h | 74 + src/drv/ansi/drvHpe1368a.c | 359 +++ src/drv/ansi/drvHpe1368a.h | 60 + src/drv/ansi/drvHpe1445a.c | 1185 +++++++ src/drv/ansi/drvKscV215.c | 484 +++ src/drv/ansi/drvKscV215.h | 56 + src/drv/ansi/drvMz8310.c | 627 ++++ src/drv/ansi/drvMz8310.h | 74 + src/drv/ansi/drvStc.c | 290 ++ src/drv/ansi/drvStc.h | 107 + src/drv/ansi/drvTime.c | 250 ++ src/drv/old/Makefile.Vx | 22 - src/vxWorks/drv/ansi/drvAt5Vxi.c | 1410 +++++++++ src/vxWorks/drv/ansi/drvAt5Vxi.h | 94 + src/vxWorks/drv/ansi/drvHp1404a.c | 408 +++ src/vxWorks/drv/ansi/drvHp1404a.h | 74 + src/vxWorks/drv/ansi/drvHpe1368a.c | 359 +++ src/vxWorks/drv/ansi/drvHpe1368a.h | 60 + src/vxWorks/drv/ansi/drvHpe1445a.c | 1185 +++++++ src/vxWorks/drv/ansi/drvStc.c | 290 ++ src/vxWorks/drv/ansi/drvStc.h | 107 + 28 files changed, 16460 insertions(+), 22 deletions(-) create mode 100644 src/drv/ansi/VXI_SETUP_README create mode 100644 src/drv/ansi/drvAt5Vxi.c create mode 100644 src/drv/ansi/drvAt5Vxi.h create mode 100644 src/drv/ansi/drvCaenV265.c create mode 100644 src/drv/ansi/drvEpvxi.c create mode 100644 src/drv/ansi/drvEpvxiMsg.c create mode 100644 src/drv/ansi/drvHp1404a.c create mode 100644 src/drv/ansi/drvHp1404a.h create mode 100644 src/drv/ansi/drvHpe1368a.c create mode 100644 src/drv/ansi/drvHpe1368a.h create mode 100644 src/drv/ansi/drvHpe1445a.c create mode 100644 src/drv/ansi/drvKscV215.c create mode 100644 src/drv/ansi/drvKscV215.h create mode 100644 src/drv/ansi/drvMz8310.c create mode 100644 src/drv/ansi/drvMz8310.h create mode 100644 src/drv/ansi/drvStc.c create mode 100644 src/drv/ansi/drvStc.h create mode 100644 src/drv/ansi/drvTime.c create mode 100644 src/vxWorks/drv/ansi/drvAt5Vxi.c create mode 100644 src/vxWorks/drv/ansi/drvAt5Vxi.h create mode 100644 src/vxWorks/drv/ansi/drvHp1404a.c create mode 100644 src/vxWorks/drv/ansi/drvHp1404a.h create mode 100644 src/vxWorks/drv/ansi/drvHpe1368a.c create mode 100644 src/vxWorks/drv/ansi/drvHpe1368a.h create mode 100644 src/vxWorks/drv/ansi/drvHpe1445a.c create mode 100644 src/vxWorks/drv/ansi/drvStc.c create mode 100644 src/vxWorks/drv/ansi/drvStc.h diff --git a/src/drv/ansi/VXI_SETUP_README b/src/drv/ansi/VXI_SETUP_README new file mode 100644 index 000000000..42c6507a2 --- /dev/null +++ b/src/drv/ansi/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/ansi/drvAt5Vxi.c b/src/drv/ansi/drvAt5Vxi.c new file mode 100644 index 000000000..73001adbd --- /dev/null +++ b/src/drv/ansi/drvAt5Vxi.c @@ -0,0 +1,1410 @@ +/* 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: + * ----------------- + * .01 joh 021490 first release + * .02 joh 040490 took out init of binary outs as requested by AT5 + * .03 joh 040490 KLUDGED so DC is invarient of model number + * .04 joh 072590 fixed case where a missing SC module could + * be accessed before checking to prevent bus error + * .05 joh 102990 slightly improved sync to busy in init + * .06 joh 032191 added code to implement a new register + * interface from AT5 + * .07 joh 080291 disable ints during control X reboot + * .08 joh 080291 fixed ints on before handler installed problem + * introduced by .06 + * .09 joh 080291 synch source with sorce release control version + * .10 joh 080891 delinting + * .11 joh 090591 converted to v5 vxWorks + * .12 joh 120591 reorganized for use with new vxi support and removed + * KLUDGE introduced in .03 + * .13 joh 042492 removed support for (ifdefs for) the old + * style register map + * .14 joh 071792 added model name registration + * .15 joh 072992 print more raw values in io report + * .16 joh 081092 merged at5vxi_models.h into this source + * .17 joh 081992 function name change + * .18 joh 082792 converted to ansi C + * .19 joh 111392 removed shifts on analog IO + * .20 joh 071593 fixed comment + * .21 joh 081193 took out EPICS_V2 compile switches + * + * Notes: + * ------ + * .01 Dont use C bitfields to write the AT5VXI CSR + * directly because some bits have different meanings + * for read than for write (bitfields writes generate RMW + * instructions- performing reads with C bitfields + * from an IO module works fine however). + * + * To avoid this I write all bits in the CSR with + * each write. See the codes defined by at5vxi_init(). + */ + +/* + * Improvements: + * + * Dont allow them to connect an interrupt if another device is + * installed there ! Perhaps intConnectSafe() should be written? + */ + + + +/* + * Code Portions + * + * AT5VXI_INIT initialize all at5vxi cards + * AT5VXI_INIT_CARD initialize single at5vxi card + * AT5VXI_INT_SERVICE update card busy writes, notify IO scanner + * AT5VXI_ONE_SHOT setup AMD9513 STC with a one shot + * AT5VXI_ONE_SHOT_READ read back timing from an AMD 9513 STC + * AT5VXI_STAT print status for a single at5 vxi card + * AT5VXI_READ_TEST diagnostic + * AT5VXI_AI_DRIVER analog input driver + * AT5VXI_AO_DRIVER analog output driver + * AT5VXI_AO_READ analog output read back + * AT5VXI_BI_DRIVER binary input driver + * AT5VXI_BO_DRIVER binary output driver + * + * + */ + +#include +#include +#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"; + +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 +); + +LOCAL at5VxiStatus at5vxi_init( + void +); + +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){ + epicsPrintf("AT5VXI: int before init\n"); + 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){ + epicsPrintf("AT5 VXI- AMD9513 load fail\n"); + } + 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( + unsigned 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 */ + unsigned int_source, /* (FALSE)External/(TRUE)Internal source */ + void (*event_rtn)(void *pParam),/* subroutine to run on events */ + void *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( + unsigned *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 */ + unsigned *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 +) +{ + unsigned preset; + double edge0_delay; + double edge1_delay; + unsigned 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 %u 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/ansi/drvAt5Vxi.h b/src/drv/ansi/drvAt5Vxi.h new file mode 100644 index 000000000..ab3b5f546 --- /dev/null +++ b/src/drv/ansi/drvAt5Vxi.h @@ -0,0 +1,94 @@ +/* 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: + * ----------------- + */ + +#include + +typedef long at5VxiStatus; + +at5VxiStatus at5vxi_one_shot( + unsigned 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 */ + unsigned int_source, /* (FALSE)External/(TRUE)Internal source */ + void (*event_rtn)(void *pParam), /* subroutine to run on events */ + void *event_rtn_param/* parameter to pass to above routine */ +); + +at5VxiStatus at5vxi_one_shot_read( + unsigned *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 */ + unsigned *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/ansi/drvCaenV265.c b/src/drv/ansi/drvCaenV265.c new file mode 100644 index 000000000..051bc5971 --- /dev/null +++ b/src/drv/ansi/drvCaenV265.c @@ -0,0 +1,808 @@ +/* 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 +#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 ) { + epicsPrintf ( + "%s.%d:devCaenV265 bad card number %d %s\n", + (int)__FILE__, + __LINE__, + pvmeio->card, + (int)pai->name); + 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){ + epicsPrintf ("caenV265ReadData: bad range number\n"); + return; + } + if(signal>=NUMBER_OF_SIGNALS){ + epicsPrintf ("caenV265ReadData: bad signal number\n"); + 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/ansi/drvEpvxi.c b/src/drv/ansi/drvEpvxi.c new file mode 100644 index 000000000..3ee7c637e --- /dev/null +++ b/src/drv/ansi/drvEpvxi.c @@ -0,0 +1,4620 @@ +/* + * 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. + * .27 joh 02-06-95 Force natural alignment of blocked address + * DC devices. + * + * 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 +#include + +#include +#define SRCepvxiLib /* allocate externals here */ +#include +#include + +#define NICPU030 + +/* + * required to use private assert() here + * because they compile this gcc -traditional + */ +#define assert(EXP) ((EXP)?1: (errMessage (S_epvxi_internal, NULL) , 0) ) + +/* + * 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 last_la; +LOCAL unsigned 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(*plac)); + 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 +) +{ + unsigned maxInBlockedDC; /* max LA in a blocked addr DC device */ + int prealloc; + int16_t id; + EPVXISTAT status; + unsigned offset; + struct vxi_csr *pcsr; + VXISZ *pvxisz; + unsigned nDC; + + /* + * 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){ + + /* + * find out how many DC devices are in the system + * and there worst case (blocked address) alignment + */ + status = vxi_count_dc_devices( + pvxie, + &nDC, + &maxInBlockedDC); + if(status){ + return; + } + if(nDC < 1){ + return; + } + /* + * allocate with worst case (blocked address) alignment + */ + status = vxi_alloc_la( + pvxie, + nDC, + blocked_la_alignment (maxInBlockedDC), + &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){ + unsigned count; + + /* + * allocate largest blocked address devices first ... + * so that we dont end up with pads for alignment + * purposes + */ + count = 1 << blocked_la_alignment (maxInBlockedDC); + while (count) { + vxi_assign_dc_addresses( + pvxie, + prealloc, + &offset, + pvxisz, + count); + count = count >> 1; + } + CLRMODID(pvxisz); + } + pvxisz = (VXISZ *) pvxisz->node.next; + } +} + + +/* + * vxi_assign_dc_addresses() + */ +LOCAL void vxi_assign_dc_addresses( +VXIE *pvxie, +int prealloc, +unsigned *pOffset, +VXISZ *pvxisz, +unsigned minNumberDC +) +{ + unsigned slot; + unsigned status; + + for(slot=0;slotla); + return S_epvxi_noMemory; + } + } + + /* + * blocked addr devices must recv their + * addr assignements in unison + * at a natural alignement + */ + assert ( (offset&mask)==0 ); + pcsr->dir.w.addr = offset; + nextOffset = offset + mask + 1; + + while (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"); + } + count--; + offset++; + } + + if(prealloc){ + *pOffset = nextOffset; + } + + return status; +} + + +/* + * + * + * VXI_COUNT_DC_DEVICES + * + */ +LOCAL EPVXISTAT vxi_count_dc_devices( +VXIE *pvxie, +unsigned *pCount, +unsigned *pMaxInBlockedDC /* max LA in a blocked addr DC device */ +) +{ + int16_t id; + EPVXISTAT status; + struct vxi_csr *pcsr; + VXISZ *pvxisz; + int slot; + unsigned nDC; + unsigned maxInBlockedDC; + + /* + * 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; + maxInBlockedDC=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){ + unsigned count; + + /* + * Force the count to be the true + * (including alignement overhead) + */ + blksiz = blocked_la_count(pcsr); + count = 1<node.next; + } + *pCount = nDC; + *pMaxInBlockedDC = maxInBlockedDC; + return VXI_SUCCESS; +} + + +/* + * + * blocked_la_alignment() + * + * Rule F.2.7 + * + * determine the required alignment for DC + * blocked address devices + * + */ +LOCAL unsigned blocked_la_alignment(unsigned blockedLACount) +{ + unsigned alignment; + + if (blockedLACount <= 1) { + return 0; + } + + alignment = 0; + blockedLACount = blockedLACount-1; + while (blockedLACount) { + alignment++; + blockedLACount = blockedLACount >> 1; + } + + return alignment; +} + + +/* + * + * blocked_la_count() + * + */ +LOCAL unsigned blocked_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; + SYM_TYPE 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 alignment, /* number of zero ls bits in LA */ +unsigned *poffset +) +{ + EPVXISTAT status; + unsigned hla; + VXIDI *pvxidi; + unsigned base_la; + unsigned peak; + unsigned la; + unsigned mask = (1<la_mapped){ + + /* + * look inside the range thats mapped (or above) first + * (from the bottom up)) + */ + for(base_la=la=pvxie->la_low; la<=last_la; la++){ + + pvxidi = epvxiLibDeviceList[la]; + + /* + * If we are starting out with the alignement + * wrong then just continue + */ + if (peak==0) { + base_la = la; + if (base_la&mask) { + continue; + } + } + + /* + * 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 count is not evenly divisable by + * (mask+1) then extend the block size + * out to natural alignment + */ + if (peak >= count && ((la+1)&mask)==0 ) { + assert ( (base_la&mask)==0 ); + *poffset = base_la; + return VXI_SUCCESS; + } + } + + hla = pvxie->la_low; + } + else{ + /* + * unachored DC device allocations go from high + * to low so that we avoid mc680xx reserved + * interrupt vectors + */ + hla = last_la; + } + + /* + * Now look from the top down + * + * stop before unsigned la=0 is decremented + * (shouldnt allocate the resource manager's LA anyways) + */ + peak=0; + la=hla; + base_la = la - (count-1); + for(; la>=max(VXI_RESMAN_LA+1,first_la); la--){ + + /* + * If we are starting out with the alignement + * wrong then just continue + */ + if (peak==0) { + base_la = la - (count-1); + if (base_la&mask) { + continue; + } + } + + 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 count is not evenly divisable by + * (mask+1) then extend the block size + * out to natural alignment + */ + if (peak >= count && la == base_la) { + assert ( (base_la&mask)==0 ); + *poffset = base_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; + SYM_TYPE type; + char *pvalue; + + status = symFindByName( + sysSymTbl, + pname, + &pvalue, + &type); + if(status == OK){ + memcpy (pdest, pvalue, 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; + SYM_TYPE 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=?? "); + } + + + /* + * 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; + SYM_TYPE 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; + SYM_TYPE 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; + SYM_TYPE 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; + SYM_TYPE 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/ansi/drvHp1404a.c b/src/drv/ansi/drvHp1404a.c new file mode 100644 index 000000000..06c51d071 --- /dev/null +++ b/src/drv/ansi/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/ansi/drvHp1404a.h b/src/drv/ansi/drvHp1404a.h new file mode 100644 index 000000000..f6f6d2bfb --- /dev/null +++ b/src/drv/ansi/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/ansi/drvHpe1368a.c b/src/drv/ansi/drvHpe1368a.c new file mode 100644 index 000000000..a760b09d1 --- /dev/null +++ b/src/drv/ansi/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/ansi/drvHpe1368a.h b/src/drv/ansi/drvHpe1368a.h new file mode 100644 index 000000000..6e7688555 --- /dev/null +++ b/src/drv/ansi/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/ansi/drvHpe1445a.c b/src/drv/ansi/drvHpe1445a.c new file mode 100644 index 000000000..e26af0598 --- /dev/null +++ b/src/drv/ansi/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/ansi/drvKscV215.c b/src/drv/ansi/drvKscV215.c new file mode 100644 index 000000000..71e4e8b77 --- /dev/null +++ b/src/drv/ansi/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{ + volatile uint16_t diag; + volatile uint16_t isr; + volatile uint16_t pad1[7]; + volatile uint16_t channels[64]; /* odd access causes a bus error ? */ + volatile uint16_t controlMemoryAddr; + volatile uint16_t pad2; + volatile uint16_t controlMemoryDataWrite; + volatile uint16_t pad3; + volatile uint16_t controlMemoryDataRead; + volatile uint16_t pad4; + volatile uint16_t lastChannel; + volatile uint16_t pad5; + volatile uint16_t singleScan; + volatile uint16_t pad6; + volatile uint16_t stopScan; + volatile uint16_t pad7; + volatile uint16_t clearControlMemoryAddr; + volatile uint16_t pad8; + volatile uint16_t enableContinuousScanning; + volatile uint16_t pad9; + volatile uint16_t disableContinuousScanning; + volatile uint16_t pad10; + volatile uint16_t enableDoneInt; + volatile uint16_t pad11; + volatile uint16_t disbaleDoneInt; + volatile uint16_t pad12; + volatile uint16_t clearDoneInt; + volatile uint16_t pad13; + volatile uint16_t testDoneInt; + volatile uint16_t 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, +volatile uint16_t *preg, +uint16_t 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, 0U); + 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, + 0U); + if(status){ + epvxiClose(la, KscV215DriverId); + errMessage(status, "KscV215 init failed\n"); + return; + } + } + + /* + * turn on continuous scan mode + */ + status = KscV215WriteSync( + pA24, + &pA24->enableContinuousScanning, + 0U); + 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, +volatile uint16_t *preg, +uint16_t 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/ansi/drvKscV215.h b/src/drv/ansi/drvKscV215.h new file mode 100644 index 000000000..34eaf2c27 --- /dev/null +++ b/src/drv/ansi/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/ansi/drvMz8310.c b/src/drv/ansi/drvMz8310.c new file mode 100644 index 000000000..dea133c1c --- /dev/null +++ b/src/drv/ansi/drvMz8310.c @@ -0,0 +1,627 @@ + +/* 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 uint8_t *) CHIP_ADDR(CARD,CHIP) + MZ8310CMD) +#define MZ8310_DATA_ADDR(CARD,CHIP)\ +((volatile uint16_t *) CHIP_ADDR(CARD,CHIP) + MZ8310DATA) +#if 0 +#define MZ8310VECBASE(CARD,CHIP)\ +((volatile uint8_t *) 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)(void *user_param); + void *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, unsigned level); +LOCAL mz8310Stat mz8310_init_card(unsigned card); +LOCAL mz8310Stat mz8310_setup_int(unsigned card, unsigned channel); +LOCAL mz8310Stat mz8310_io_report(unsigned level); +LOCAL mz8310Stat mz8310_init(void); +LOCAL mz8310Stat mz8310_read_test(unsigned card, unsigned 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(unsigned 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(unsigned card, unsigned channel) +{ + unsigned preset; + double edge0_delay; + double edge1_delay; + unsigned 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 %u %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 %u Interrupt count=%u\n", + channel, + mzconf[card].icf[channel].cnt); + } + + return status; +} + + +/* + * mz8310_one_shot() + */ +mz8310Stat mz8310_one_shot( +unsigned 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 */ +unsigned int_source, /* (FALSE)External/ (TRUE)Internal source */ +void (*event_rtn)(void *pParam), /* subroutine to run on events */ +void *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, +unsigned card, +unsigned chip +) +{ + volatile uint16_t *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/ansi/drvMz8310.h b/src/drv/ansi/drvMz8310.h new file mode 100644 index 000000000..f4023bbe6 --- /dev/null +++ b/src/drv/ansi/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( +unsigned *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 */ +unsigned *int_source /* (FALSE)External/(TRUE)Internal src */ +); + +mz8310Stat mz8310_one_shot( +unsigned 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 */ +unsigned int_source, /* (FALSE)External/ (TRUE)Internal source */ +void (*event_rtn)(void *pParam), /* subroutine to run on events */ +void *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, +unsigned card, +unsigned chip +); + + diff --git a/src/drv/ansi/drvStc.c b/src/drv/ansi/drvStc.c new file mode 100644 index 000000000..0a48ab78d --- /dev/null +++ b/src/drv/ansi/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/ansi/drvStc.h b/src/drv/ansi/drvStc.h new file mode 100644 index 000000000..5d4a6203b --- /dev/null +++ b/src/drv/ansi/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 +#include + +#include +#include + +struct pulse{ +double offset; +double width; +}; + + + +/* + * + * time_driver_read() + * + * + */ +int 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 card; +unsigned channel; +unsigned card_type; +unsigned *int_source; +unsigned *preset; +struct pulse *pulses; +unsigned *npulses; +unsigned 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; + } + epicsPrintf("time_driver: No support for that type of timing card\n"); + return ERROR; +} + + +int 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)(void *pParam); +void *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; + } + epicsPrintf("time_driver: No support for that type of timing card\n"); + return ERROR; +} + + + +int 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; + + + epicsPrintf( "wrote: preset %x internal-clk %x delay %f width %f \n", + preset, + int_source, + pulses.offset, + pulses.width); + epicsPrintf( "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/Makefile.Vx b/src/drv/old/Makefile.Vx index d2db1597c..038098192 100644 --- a/src/drv/old/Makefile.Vx +++ b/src/drv/old/Makefile.Vx @@ -9,30 +9,19 @@ include $(EPICS)/config/CONFIG_BASE USR_CFLAGS = -fshared-data -fvolatile -mnobitfield -traditional SRCS.c += ../module_types.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 @@ -42,30 +31,19 @@ SRCS.c += ../drvXy240.c SRCS.c += ../drvXy566.c TARGETS += module_types.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 diff --git a/src/vxWorks/drv/ansi/drvAt5Vxi.c b/src/vxWorks/drv/ansi/drvAt5Vxi.c new file mode 100644 index 000000000..73001adbd --- /dev/null +++ b/src/vxWorks/drv/ansi/drvAt5Vxi.c @@ -0,0 +1,1410 @@ +/* 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: + * ----------------- + * .01 joh 021490 first release + * .02 joh 040490 took out init of binary outs as requested by AT5 + * .03 joh 040490 KLUDGED so DC is invarient of model number + * .04 joh 072590 fixed case where a missing SC module could + * be accessed before checking to prevent bus error + * .05 joh 102990 slightly improved sync to busy in init + * .06 joh 032191 added code to implement a new register + * interface from AT5 + * .07 joh 080291 disable ints during control X reboot + * .08 joh 080291 fixed ints on before handler installed problem + * introduced by .06 + * .09 joh 080291 synch source with sorce release control version + * .10 joh 080891 delinting + * .11 joh 090591 converted to v5 vxWorks + * .12 joh 120591 reorganized for use with new vxi support and removed + * KLUDGE introduced in .03 + * .13 joh 042492 removed support for (ifdefs for) the old + * style register map + * .14 joh 071792 added model name registration + * .15 joh 072992 print more raw values in io report + * .16 joh 081092 merged at5vxi_models.h into this source + * .17 joh 081992 function name change + * .18 joh 082792 converted to ansi C + * .19 joh 111392 removed shifts on analog IO + * .20 joh 071593 fixed comment + * .21 joh 081193 took out EPICS_V2 compile switches + * + * Notes: + * ------ + * .01 Dont use C bitfields to write the AT5VXI CSR + * directly because some bits have different meanings + * for read than for write (bitfields writes generate RMW + * instructions- performing reads with C bitfields + * from an IO module works fine however). + * + * To avoid this I write all bits in the CSR with + * each write. See the codes defined by at5vxi_init(). + */ + +/* + * Improvements: + * + * Dont allow them to connect an interrupt if another device is + * installed there ! Perhaps intConnectSafe() should be written? + */ + + + +/* + * Code Portions + * + * AT5VXI_INIT initialize all at5vxi cards + * AT5VXI_INIT_CARD initialize single at5vxi card + * AT5VXI_INT_SERVICE update card busy writes, notify IO scanner + * AT5VXI_ONE_SHOT setup AMD9513 STC with a one shot + * AT5VXI_ONE_SHOT_READ read back timing from an AMD 9513 STC + * AT5VXI_STAT print status for a single at5 vxi card + * AT5VXI_READ_TEST diagnostic + * AT5VXI_AI_DRIVER analog input driver + * AT5VXI_AO_DRIVER analog output driver + * AT5VXI_AO_READ analog output read back + * AT5VXI_BI_DRIVER binary input driver + * AT5VXI_BO_DRIVER binary output driver + * + * + */ + +#include +#include +#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"; + +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 +); + +LOCAL at5VxiStatus at5vxi_init( + void +); + +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){ + epicsPrintf("AT5VXI: int before init\n"); + 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){ + epicsPrintf("AT5 VXI- AMD9513 load fail\n"); + } + 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( + unsigned 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 */ + unsigned int_source, /* (FALSE)External/(TRUE)Internal source */ + void (*event_rtn)(void *pParam),/* subroutine to run on events */ + void *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( + unsigned *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 */ + unsigned *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 +) +{ + unsigned preset; + double edge0_delay; + double edge1_delay; + unsigned 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 %u 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/vxWorks/drv/ansi/drvAt5Vxi.h b/src/vxWorks/drv/ansi/drvAt5Vxi.h new file mode 100644 index 000000000..ab3b5f546 --- /dev/null +++ b/src/vxWorks/drv/ansi/drvAt5Vxi.h @@ -0,0 +1,94 @@ +/* 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: + * ----------------- + */ + +#include + +typedef long at5VxiStatus; + +at5VxiStatus at5vxi_one_shot( + unsigned 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 */ + unsigned int_source, /* (FALSE)External/(TRUE)Internal source */ + void (*event_rtn)(void *pParam), /* subroutine to run on events */ + void *event_rtn_param/* parameter to pass to above routine */ +); + +at5VxiStatus at5vxi_one_shot_read( + unsigned *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 */ + unsigned *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/vxWorks/drv/ansi/drvHp1404a.c b/src/vxWorks/drv/ansi/drvHp1404a.c new file mode 100644 index 000000000..06c51d071 --- /dev/null +++ b/src/vxWorks/drv/ansi/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/vxWorks/drv/ansi/drvHp1404a.h b/src/vxWorks/drv/ansi/drvHp1404a.h new file mode 100644 index 000000000..f6f6d2bfb --- /dev/null +++ b/src/vxWorks/drv/ansi/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/vxWorks/drv/ansi/drvHpe1368a.c b/src/vxWorks/drv/ansi/drvHpe1368a.c new file mode 100644 index 000000000..a760b09d1 --- /dev/null +++ b/src/vxWorks/drv/ansi/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/vxWorks/drv/ansi/drvHpe1368a.h b/src/vxWorks/drv/ansi/drvHpe1368a.h new file mode 100644 index 000000000..6e7688555 --- /dev/null +++ b/src/vxWorks/drv/ansi/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/vxWorks/drv/ansi/drvHpe1445a.c b/src/vxWorks/drv/ansi/drvHpe1445a.c new file mode 100644 index 000000000..e26af0598 --- /dev/null +++ b/src/vxWorks/drv/ansi/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/vxWorks/drv/ansi/drvStc.c b/src/vxWorks/drv/ansi/drvStc.c new file mode 100644 index 000000000..0a48ab78d --- /dev/null +++ b/src/vxWorks/drv/ansi/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/vxWorks/drv/ansi/drvStc.h b/src/vxWorks/drv/ansi/drvStc.h new file mode 100644 index 000000000..5d4a6203b --- /dev/null +++ b/src/vxWorks/drv/ansi/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<