From 1f61d4acc2594cf14c4bb11844a3db3eaebfafe9 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 14 Nov 1995 20:18:07 +0000 Subject: [PATCH] Added CANbus driver (TIP810 Industry-Pack). N.B. drvIpMv162 is not tested. --- src/drv/ansi/Makefile.Vx | 6 + src/drv/ansi/canBus.h | 73 +++ src/drv/ansi/drvIpMv162.c | 341 ++++++++++++ src/drv/ansi/drvIpac.c | 520 ++++++++++++++++++ src/drv/ansi/drvIpac.h | 176 ++++++ src/drv/ansi/drvTip810.c | 1096 +++++++++++++++++++++++++++++++++++++ src/drv/ansi/drvTip810.h | 48 ++ src/drv/ansi/drvVipc310.c | 262 +++++++++ src/drv/ansi/drvVipc610.c | 275 ++++++++++ src/drv/ansi/ipModules.h | 33 ++ src/drv/ansi/ipic.h | 89 +++ src/drv/ansi/pca82c200.h | 188 +++++++ 12 files changed, 3107 insertions(+) create mode 100644 src/drv/ansi/canBus.h create mode 100644 src/drv/ansi/drvIpMv162.c create mode 100644 src/drv/ansi/drvIpac.c create mode 100644 src/drv/ansi/drvIpac.h create mode 100644 src/drv/ansi/drvTip810.c create mode 100644 src/drv/ansi/drvTip810.h create mode 100644 src/drv/ansi/drvVipc310.c create mode 100644 src/drv/ansi/drvVipc610.c create mode 100644 src/drv/ansi/ipModules.h create mode 100644 src/drv/ansi/ipic.h create mode 100644 src/drv/ansi/pca82c200.h diff --git a/src/drv/ansi/Makefile.Vx b/src/drv/ansi/Makefile.Vx index 391aca3fd..62d43c836 100644 --- a/src/drv/ansi/Makefile.Vx +++ b/src/drv/ansi/Makefile.Vx @@ -23,6 +23,12 @@ SRCS.c += ../drvStc.c SRCS.c += ../drvTime.c # SRCS.c += ../drvCaenV265.c +# SRCS.c += ../drvVipc310.c +# SRCS.c += ../drvVipc610.c +# SRCS.c += ../drvIpMv162.c +# SRCS.c += ../drvIpac.c +# SRCS.c += ../drvTip810.c + TARGETS = $(SRCS.c:../%.c=%.o) include $(EPICS)/config/RULES.Vx diff --git a/src/drv/ansi/canBus.h b/src/drv/ansi/canBus.h new file mode 100644 index 000000000..8b8829380 --- /dev/null +++ b/src/drv/ansi/canBus.h @@ -0,0 +1,73 @@ +/******************************************************************************* + +Project: + Gemini/UKIRT CAN Bus Driver for EPICS + +File: + canBus.h + +Description: + CANBUS specific constants + +Author: + Andrew Johnson +Created: + 25 July 1995 + +(c) 1995 Royal Greenwich Observatory + +*******************************************************************************/ + + +#ifndef INCcanBusH +#define INCcanBusH + + +#define CAN_IDENTIFIERS 2048 +#define CAN_DATA_SIZE 8 + +#define CAN_BUS_OK 0 +#define CAN_BUS_ERROR 1 +#define CAN_BUS_OFF 2 + + +#ifndef M_can +#define M_can (811<<16) +#endif + +#define S_can_badMessage (M_can| 1) /*illegal CAN message contents*/ +#define S_can_badAddress (M_can| 2) /*CAN address syntax error*/ +#define S_can_noDevice (M_can| 3) /*CAN bus name does not exist*/ + +typedef struct { + ushort_t identifier; /* 0 .. 2047 with holes! */ + enum { + SEND = 0, RTR = 1 + } rtr; /* Remote Transmission Request */ + uchar_t length; /* 0 .. 8 */ + uchar_t data[CAN_DATA_SIZE]; +} canMessage_t; + +typedef struct { + char *busName; + int timeout; + ushort_t identifier; + ushort_t offset; + signed int parameter; + void *canBusID; +} canIo_t; + +typedef void canMsgCallback_t(void *pprivate, canMessage_t *pmessage); +typedef void canSigCallback_t(void *pprivate, int status); + +extern int canOpen(char *busName, void **pcanBusID); +extern int canRead(void *canBusID, canMessage_t *pmessage, int timeout); +extern int canWrite(void *canBusID, canMessage_t *pmessage, int timeout); +extern int canMessage(void *canBusID, ushort_t identifier, + canMsgCallback_t callback, void *pprivate); +extern int canSignal(void *canBusID, canSigCallback_t callback, void *pprivate); +extern int canIoParse(char *canString, canIo_t *pcanIo); + + +#endif /* INCcanBusH */ + diff --git a/src/drv/ansi/drvIpMv162.c b/src/drv/ansi/drvIpMv162.c new file mode 100644 index 000000000..d625a429c --- /dev/null +++ b/src/drv/ansi/drvIpMv162.c @@ -0,0 +1,341 @@ +/******************************************************************************* + +Project: + Gemini/UKIRT CAN Bus Driver for EPICS + +File: + drvIpMv162.c + +Description: + IPAC Carrier Driver for the IndustryPack carriers on the Motorola + MVME162 CPU board, provides the interface between IPAC driver and the + hardware. + +Author: + Andrew Johnson +Created: + 6 July 1995 + +(c) 1995 Royal Greenwich Observatory + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "drvIpac.h" +#include "ipic.h" + + +/* Characteristics of the card */ + +#define SLOTS 4 +#define IO_SPACES 2 /* Address spaces in A16 */ +#define IPAC_IRQS 2 /* Interrupts per module */ +#define IPIC_BASE 0xfffbc000 + + +/* Base Addresses of IO and ID spaces */ + +#define REGS_A 0xfff58000 +#define PROM_A 0xfff58080 +#define REGS_B 0xfff58100 +#define PROM_B 0xfff58180 +#define REGS_C 0xfff58200 +#define PROM_C 0xfff58280 +#define REGS_D 0xfff58300 +#define PROM_D 0xfff58380 +#define REGS_AB 0xfff58400 +#define REGS_CD 0xfff58500 + + +/* IPIC chip */ + +ipic_t *ipic = (ipic_t *) IPIC_BASE; + + +/* IP Recovery Timers */ + +LOCAL const uchar_t recoveryTime[] = { + IPIC_GEN_RT_0, + IPIC_GEN_RT_2, + IPIC_GEN_RT_2, + IPIC_GEN_RT_4, + IPIC_GEN_RT_4, + IPIC_GEN_RT_8, + IPIC_GEN_RT_8, + IPIC_GEN_RT_8, + IPIC_GEN_RT_8 +}; + + +/* Carrier Base Address structure, only one instance can exist! */ + +LOCAL long mBase[IPAC_ADDR_SPACES][SLOTS] = { + PROM_A, PROM_B, PROM_C, PROM_D, + REGS_A, REGS_B, REGS_C, REGS_D, + REGS_AB, NULL, REGS_CD, NULL, + NULL, NULL, NULL, NULL +}; + + +/******************************************************************************* + +Routine: + initialise + +Purpose: + Initialises the MVME162 IPIC chip with settings given in cardParams + +Description: + + +Parameters: + + +Examples: + "A:m=0x80000000,1024 l=4;B:l=2,2;C:m=0x80100000,64" + +Returns: + 0 = OK, + S_IPAC_tooMany = Carrier already registered + S_IPAC_badDriver = IPIC chip not found + S_IPAC_badAddress = Parameter string error, or address not reachable + +*/ + +LOCAL int initialise ( + char *cardParams, + void **pprivate +) { + static int initialised = FALSE; + ushort_t slot; + int count, p1, p2, next; + char dummy, cmd; + + if (initialised) { + return S_IPAC_tooMany; + } + + if (vxMemProbe((void *)&ipic->chipId, READ, 1, &dummy) || + ipic->chipId != IPIC_CHIP_ID) { + return S_IPAC_badDriver; + } + + /* Initialise the IPIC chip */ + for (slot = 0; slot < SLOTS; slot++) { + ipic->intCtrl[slot][0] = IPIC_INT_ICLR; + ipic->intCtrl[slot][1] = IPIC_INT_ICLR; + ipic->genCtrl[slot] = IPIC_GEN_WIDTH_16 | IPIC_GEN_RT_0; + } + + /* Parse the parameter string */ + slot = 0; + while ((cmd = *cardParams++) != '\0') { + switch (cmd) { + case 'A': + case 'B': + case 'C': + case 'D': + slot = cmd - 'A'; + break; + + case 'm': + p1 = p2 = 0; + count = sscanf(cardParams, "=%p,%d%n", + (void **) &p1, &p2, &next); + if (count != 2 || + p1 < (long) sysMemTop() || + p1 & 0xffff != 0 || + p2 < 64 || p2 > 16384 || + p1 + (p2*1024) > 0xfff00000) { + return S_IPAC_badAddress; + } + + ipic->memBase[slot] = p1 >> 16; + ipic->memSize[slot] = (p2 / 64) - 1; + ipic->genCtrl[slot] |= IPIC_GEN_MEN; + mBase[ipac_addrMem][slot] = p1; + cardParams += next; + break; + + case 'l': + p1 = p2 = 0; + count = sscanf(cardParams, "=%d%n,%d%n", &p1, &next, &p2, &next); + if (count < 1 || count > 2 || + p1 < 0 || p1 > 7 || + p2 < 0 || p2 > 7) { + return S_IPAC_badAddress; + } + + ipic->intCtrl[slot][0] = (p1 & IPIC_INT_LEVEL) | + (ipic->intCtrl[slot][0] & ~IPIC_INT_LEVEL); + ipic->intCtrl[slot][1] = (p2 & IPIC_INT_LEVEL) | + (ipic->intCtrl[slot][1] & ~IPIC_INT_LEVEL); + cardParams += next; + break; + + case 'r': + p1 = 0; + count = sscanf(cardParams, "=%d%n", &p1, &next); + if (count != 1 || + p1 < 0 || p1 > 8) { + return S_IPAC_badAddress; + } + + ipic->genCtrl[slot] = (ipic->genCtrl[slot] & ~IPIC_GEN_RT) | + recoveryTime[p1]; + cardParams += next; + break; + + case 'w': + p1 = 0; + count = sscanf(cardParams, "=%d%n", &p1, &next); + if (count != 1) { + return S_IPAC_badAddress; + } + + switch (p1) { + case 8: + ipic->genCtrl[slot] = IPIC_GEN_WIDTH_8 | + (ipic->genCtrl[slot] & ~IPIC_GEN_WIDTH); + break; + case 16: + ipic->genCtrl[slot] = IPIC_GEN_WIDTH_16 | + (ipic->genCtrl[slot] & ~IPIC_GEN_WIDTH); + break; + case 32: + if (slot & 1) { + /* Illegal for odd-numbered slots */ + return S_IPAC_badAddress; + } + ipic->genCtrl[slot] = IPIC_GEN_WIDTH_32 | + (ipic->genCtrl[slot] & ~IPIC_GEN_WIDTH); + ipic->genCtrl[slot+1] &= ~(IPIC_GEN_WIDTH | + IPIC_GEN_MEN); + break; + default: + return S_IPAC_badAddress; + } + } + } + + initialised = TRUE; + return OK; +} + + +/******************************************************************************* + +Routine: + baseAddr + +Purpose: + Returns the base address for the requested slot & address space + +Description: + This routine only has to do a table lookup in the mBase array. + Note that no parameter checking is required - the IPAC driver which + calls this routine handles that. + +Returns: + The requested address, or NULL if the slot has no memory in the + requested address space. + +*/ + +LOCAL void *baseAddr ( + void *private, + ushort_t slot, + ipac_addr_t space +) { + return (void *) mBase[space][slot]; +} + + +/******************************************************************************* + +Routine: + irqCmd + +Purpose: + Handles interrupter commands and status requests + +Description: + The IPIC chip allows a lot of control over the IP interrupters, thus + all commands perform the requested action. + +Returns: + ipac_irqGetLevel returns the current interrupt level, + ipac_irqPoll returns >0 if interrupt line active else 0, + other calls return 0 = OK. + +*/ + +LOCAL int irqCmd ( + void *private, + ushort_t slot, + ushort_t irqNumber, + ipac_irqCmd_t cmd +) { + switch (cmd) { + case ipac_irqLevel0: + case ipac_irqLevel1: + case ipac_irqLevel2: + case ipac_irqLevel3: + case ipac_irqLevel4: + case ipac_irqLevel5: + case ipac_irqLevel6: + case ipac_irqLevel7: + ipic->intCtrl[slot][irqNumber] = (cmd & IPIC_INT_LEVEL) | + (ipic->intCtrl[slot][irqNumber] & ~IPIC_INT_LEVEL); + return OK; + + case ipac_irqGetLevel: + return ipic->intCtrl[slot][irqNumber] & IPIC_INT_LEVEL; + + case ipac_irqEnable: + ipic->intCtrl[slot][irqNumber] |= IPIC_INT_IEN; + return OK; + + case ipac_irqDisable: + ipic->intCtrl[slot][irqNumber] &= ~IPIC_INT_IEN; + return OK; + + case ipac_irqPoll: + return ipic->intCtrl[slot][irqNumber] & IPIC_INT_INT; + + case ipac_irqSetEdge: + ipic->intCtrl[slot][irqNumber] |= IPIC_INT_EDGE; + return OK; + + case ipac_irqSetLevel: + ipic->intCtrl[slot][irqNumber] &= ~IPIC_INT_EDGE; + return OK; + + case ipac_irqClear: + ipic->intCtrl[slot][irqNumber] |= IPIC_INT_ICLR; + return OK; + + default: + return S_IPAC_notImplemented; + } +} + +/******************************************************************************/ + + +/* IPAC Carrier Table */ + +ipac_carrier_t ipmv162 = { + "Motorola MVME162", + SLOTS, + initialise, + NULL, + baseAddr, + irqCmd +}; + diff --git a/src/drv/ansi/drvIpac.c b/src/drv/ansi/drvIpac.c new file mode 100644 index 000000000..e98a5c850 --- /dev/null +++ b/src/drv/ansi/drvIpac.c @@ -0,0 +1,520 @@ +/******************************************************************************* + +Project: + Gemini/UKIRT CAN Bus Driver for EPICS + +File: + drvIpac.c + +Description: + IPAC Driver, provides a standard interface between IPAC Module + drivers and the IPAC Carrier drivers. + +Author: + Andrew Johnson +Created: + 3 July 1995 + +(c) 1995 Royal Greenwich Observatory + +*******************************************************************************/ + + +#ifndef NO_EPICS +# include +#endif + +#include +#include +#include +#include +#include "drvIpac.h" + + +#define IPAC_MAX_CARRIERS 21 + + +/* Private carrier data structures */ +struct carrierInfo { + ipac_carrier_t *driver; + void *cPrivate; +}; + +LOCAL struct { + ushort_t number; + struct carrierInfo *info[IPAC_MAX_CARRIERS]; +} carriers = { + 0 +}; + + +/* Null carrier stuff */ + +LOCAL ipac_carrier_t nullCarrier = { + "Null carrier (place holder)", + 0, /* No slots */ + NULL, NULL, NULL, NULL +}; + +LOCAL struct carrierInfo nullInfo = { + &nullCarrier, + NULL +}; + + +#ifndef NO_EPICS + +/* EPICS Driver Support Entry Table */ + +struct drvet drvIpac = { + 3, + (DRVSUPFUN) ipacReport, + (DRVSUPFUN) ipacInitialise, + NULL +}; + + +#endif + + +/******************************************************************************* + +Routine: + ipacAddCarrier + +Purpose: + Used to register a carrier board & carrier driver with the IPAC driver. + +Description: + Usually called from the vxWorks (EPICS) startup script. Some types of + carrier may need additional initilisation before or after registering, + but the card parameter string should be sufficient for most carriers. + Note that only the carrier initialise routine is called at this stage. + The order in which carriers are registered with this routine specifies + the carrier number which they will be allocated, starting from zero. + + Checks that the carrier descriptor table looks sensible, then calls the + initialise routine with the given card parameters, and saves the carrier + private pointer and carrier table address. The card number allows the + same descriptor to be used for all carriers of the same type. + + It may be necessary to remove a carrier temporarily from a system in + some circumstances without wanting to have to change the carrier number + allocated to higher numbered carriers. To allow this, it is legal to + call this routine with a NULL (zero) carrier table address, which + switches in the null carrier table instead. + +Returns: + 0 = OK, + S_IPAC_tooMany = Carrier Info Table full, + S_IPAC_badTable = Carrier Table invalid. + +Example: + ipacAddCarrier(&vipc310, "0x6000"); + +*/ + +int ipacAddCarrier ( + ipac_carrier_t *pcarrierTable, + char *cardParams +) { + void *cPrivate; + int status; + + if (carriers.number >= IPAC_MAX_CARRIERS) { + return S_IPAC_tooMany; + } + + if (pcarrierTable == NULL) { + carriers.info[carriers.number] = &nullInfo; + carriers.number++; + return OK; + } + + if (pcarrierTable->numberSlots == 0 || + pcarrierTable->initialise == NULL || + pcarrierTable->baseAddr == NULL || + pcarrierTable->irqCmd == NULL) { + return S_IPAC_badTable; + } + + status = pcarrierTable->initialise(cardParams, &cPrivate); + if (status) { + return status; + } + + carriers.info[carriers.number] = malloc(sizeof (struct carrierInfo)); + carriers.info[carriers.number]->driver = pcarrierTable; + carriers.info[carriers.number]->cPrivate = cPrivate; + carriers.number++; + + return OK; +} + + +/******************************************************************************* + +Routine: + ipmCheck + +Function: + Check on presence of an IPAC module at the given carrier & slot number. + +Description: + Does a quick check to make sure the carrier and slot numbers are legal, + probes the IDprom space to ensure an IPAC is installed, and checks that + the IDprom starts with the "IPAC" identifier. + +Returns: + 0 = OK, + S_IPAC_badAddress = Bad carrier or slot number, + S_IPAC_noModule = No module installed, + S_IPAC_noIpacId = "IPAC" identifier not found + +*/ + +int ipmCheck ( + ushort_t carrier, + ushort_t slot +) { + ipac_idProm_t *id; + char dummy; + + if (carrier >= carriers.number || + slot >= carriers.info[carrier]->driver->numberSlots) { + return S_IPAC_badAddress; + } + + id = (ipac_idProm_t *) ipmBaseAddr(carrier, slot, ipac_addrID); + if (id == NULL) { + return S_IPAC_badDriver; + } + + if (vxMemProbe((void *)&id->asciiI, READ, 1, &dummy)) { + return S_IPAC_noModule; + } + + if (id->asciiI != 'I' || + id->asciiP != 'P' || + id->asciiA != 'A' || + id->asciiC != 'C') { + return S_IPAC_noIpacId; /* Not an IPAC */ + } + + return OK; +} + + +/******************************************************************************* + +Routine: + checkCRC + +Function: + Calculate the CRC of the IDprom at the given address. + +Description: + Generates an industry standard CRC of the ID Prom data as described + in the GreenSpring Industry Pack specification. The CRC byte in the + Prom (at address 0x17) is set to zero for the purpose of calculating + the CRC. + +Returns: + The low 8 bits of the calculated CRC value. + +*/ + +LOCAL int checkCRC ( + uchar_t *data, + ushort_t length +) { + uint_t i, crc = 0xffff; + uchar_t mask; + + for (i = 1; i < 2*length; i += 2) { + mask = 0x80; + while (mask) { + if ((data[i] & mask) && (i != 0x17)) { + crc ^= 0x8000; + } + crc += crc; + if (crc > 0xffff) { + crc = (crc & 0xffff) ^ 0x1021; + } + mask >>= 1; + } + } + + return (~crc) & 0xff; +} + + +/******************************************************************************* + +Routine: + ipmValidate + +Function: + Validate a particular IPAC module type at the given carrier & slot number. + +Description: + Uses ipmCheck to ensure the carrier and slot numbers are legal, probe the + IDprom and check that the IDprom looks like an IPAC module. Calculates + the CRC for the ID Prom, and compares the manufacturer and model ID values + in the Prom to the ones given. + +Returns: + 0 = OK, + S_IPAC_badAddress = Bad carrier or slot number, + S_IPAC_noModule = No module installed, + S_IPAC_noIpacId = "IPAC" identifier not found + S_IPAC_badCRC = CRC Check failed, + S_IPAC_badModule = Manufacturer or model IDs wrong + +*/ + +int ipmValidate ( + ushort_t carrier, + ushort_t slot, + uchar_t manufacturerId, + uchar_t modelId +) { + ipac_idProm_t *id; + int status; + + status = ipmCheck(carrier, slot); + if (status) { + return status; + } + + id = (ipac_idProm_t *) ipmBaseAddr(carrier, slot, ipac_addrID); + if (checkCRC((uchar_t *) id, id->bytesUsed) != id->CRC) { + return S_IPAC_badCRC; + } + + if (id->manufacturerId != manufacturerId || + id->modelId != modelId) { + return S_IPAC_badModule; + } + + return OK; +} + + +/******************************************************************************* + +Routine: + ipmReport + +Function: + returns printable string giving status of module at given carrier/slot. + +Description: + Generates a report string describing the given IPAC slot. If a module + is installed, it includes the manufacturer and model ID numbers. If + the report function is supported by the carrier driver this report + string is appended. + +Returns: + Pointer to static, printable string. + +Sample Output: + "C0 S1 : 0xB1/0x01 - M0 L4,5" + +*/ + +char *ipmReport ( + ushort_t carrier, + ushort_t slot +) { + static char report[80]; + int status; + + sprintf(report, "C%hd S%hd : ", carrier, slot); + + status = ipmCheck(carrier, slot); + if (status == S_IPAC_badAddress) { + strcat(report, "No such carrier/slot"); + return report; + } + + if (status == S_IPAC_noModule) { + strcat(report, "No Module"); + } else { + ipac_idProm_t *id; + char module[16]; + + id = (ipac_idProm_t *) ipmBaseAddr(carrier, slot, ipac_addrID); + sprintf(module, "0x%02hX/0x%02hX", (short) id->manufacturerId, + (short) id->modelId); + strcat(report, module); + } + + if (carriers.info[carrier]->driver->report != NULL) { + strcat(report, " - "); + strcat(report, carriers.info[carrier]->driver->report( + carriers.info[carrier]->cPrivate, slot)); + } + + return report; +} + + +/******************************************************************************* + +Routine: + ipmBaseAddr + +Function: + Returns a pointer to the selected IP address space + +Description: + Checks its input parameters, then calls the carrier driver. This will + return a pointer to the location of the address space indicated by the + space parameter. All IP modules must provide an ID prom to indicate + the module type (space = ipac_addrID). Most modules need register I/O + locations, which are in the I/O space (space = ipac_addrIO). Some + types of module also provide memory (space = ipac_addrMem), but if + this is not required the carrier may allow it to be disabled, in which + case the driver should return a NULL for this address space. Some + carriers provide a 32-bit wide I/O space for Dual-slot IP modules; + carriers which do not should return NULL for this space. + +Returns: + Base CPU address of IP address space, or NULL pointer. + +*/ + +void *ipmBaseAddr ( + ushort_t carrier, + ushort_t slot, + ipac_addr_t space +) { + if (carrier >= carriers.number || + slot >= carriers.info[carrier]->driver->numberSlots) { + return NULL; + } + return carriers.info[carrier]->driver->baseAddr( + carriers.info[carrier]->cPrivate, slot, space); +} + + +/******************************************************************************* + +Routine: + ipmIrqCmd + +Function: + Send command to slot interrupt controller. + +Description: + Checks input parameters, then passes the interrupt command request to + the carrier driver routine. The driver is only required to support + the command ipac_irqEnable; for other commands it may return the status + code S_IPAC_notImplemented and do nothing. + +Returns: + 0 = OK, + S_IPAC_badAddress = illegal carrier, slot or irqNumber, + S_IPAC_notImplemented = Driver does not support that command, + other, depending on command. + +*/ + +int ipmIrqCmd ( + ushort_t carrier, + ushort_t slot, + ushort_t irqNumber, + ipac_irqCmd_t cmd +) { + if (irqNumber > 1 || + carrier >= carriers.number || + slot >= carriers.info[carrier]->driver->numberSlots) { + return S_IPAC_badAddress; + } + + return carriers.info[carrier]->driver->irqCmd( + carriers.info[carrier]->cPrivate, slot, irqNumber, cmd); +} + + +/******************************************************************************* + +Routine: + ipacReport + +Function: + Report status of all known IPAC carriers + +Description: + Prints information on each known carrier board and slot according to the + specified interest level. Level 0 lists carriers only, with the number + of slots it supports. Level 1 gives each slot, manufacturer & model ID + of the installed module (if any), and the carrier driver report for that + slot. Level 2 adds the address of each memory space for the slot. + +Returns: + OK. + +*/ + +int ipacReport ( + int interest +) { + ushort_t carrier, slot; + + for (carrier=0; carrier < carriers.number; carrier++) { + printf(" IP Carrier %2d: %s, %d slots\n", carrier, + carriers.info[carrier]->driver->carrierType, + carriers.info[carrier]->driver->numberSlots); + + if (interest > 0) { + long memBase, io32Base; + + for (slot=0; slot < carriers.info[carrier]->driver->numberSlots; + slot++) { + printf(" %s\n", ipmReport(carrier, slot)); + + if (interest > 1) { + printf(" ID = 0x%lx, I/O = 0x%lx", + (long) ipmBaseAddr(carrier, slot, ipac_addrID), + (long) ipmBaseAddr(carrier, slot, ipac_addrIO)); + io32Base = (long) ipmBaseAddr(carrier, slot, ipac_addrIO32); + if (io32Base != NULL) { + printf(", I/O32 = 0x%lx", io32Base); + } + memBase = (long) ipmBaseAddr(carrier, slot, ipac_addrMem); + if (memBase != NULL) { + printf(", Mem = 0x%lx", memBase); + } + printf("\n"); + } + } + } + } + return OK; +} + + +/******************************************************************************* + +Routine: + ipacInitialise + +Function: + Initialise the IPAC driver + +Description: + Null routine. + +Returns: + OK. + +*/ + +int ipacInitialise ( + int after +) { + return OK; +} + diff --git a/src/drv/ansi/drvIpac.h b/src/drv/ansi/drvIpac.h new file mode 100644 index 000000000..eb9679dcf --- /dev/null +++ b/src/drv/ansi/drvIpac.h @@ -0,0 +1,176 @@ +/******************************************************************************* + +Project: + Gemini/UKIRT CAN Bus Driver for EPICS + +File: + drvIpac.h + +Description: + IPAC Driver header file, defines the software interfaces: + 1. Upwards to the IPAC Module driver + 2. Downwards to the IPAC Carrier driver + +Author: + Andrew Johnson +Created: + 1 July 1995 + +(c) 1995 Royal Greenwich Observatory + +*******************************************************************************/ + + +#ifndef INCdrvIpacH +#define INCdrvIpacH + +#include +#include "ipModules.h" + +#ifndef NO_EPICS +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Error numbers */ + +#ifndef OK +#define OK 0 +#endif + +#ifndef M_ipac +#define M_ipac (600 << 16) +#endif + +#define S_IPAC_badTable (M_ipac| 1) /*IPAC Carrier Table invalid*/ +#define S_IPAC_tooMany (M_ipac| 2) /*Too many IPAC carriers, table full*/ +#define S_IPAC_badAddress (M_ipac| 3) /*Bad IPAC carrier or slot number*/ +#define S_IPAC_badDriver (M_ipac| 4) /*Bad value from IPAC carrier driver*/ +#define S_IPAC_noModule (M_ipac| 5) /*No IP module installed*/ +#define S_IPAC_noIpacId (M_ipac| 6) /*IPAC identifier not found*/ +#define S_IPAC_badCRC (M_ipac| 7) /*IPAC CRC Check failed*/ +#define S_IPAC_badModule (M_ipac| 8) /*IPAC Manufacturer or model ID wrong*/ +#define S_IPAC_notImplemented (M_ipac| 9) /*IPAC Driver command not available*/ + + +/* Structure of the IPAC ID Prom, located in the pack ID space. */ + +typedef struct { + uchar_t pad00; + uchar_t asciiI; + uchar_t pad02; + uchar_t asciiP; + uchar_t pad04; + uchar_t asciiA; + uchar_t pad06; + uchar_t asciiC; + uchar_t pad08; + uchar_t manufacturerId; + uchar_t pad0a; + uchar_t modelId; + uchar_t pad0c; + uchar_t revision; + uchar_t pad0e; + uchar_t reserved; + uchar_t pad10; + uchar_t driverIdLow; + uchar_t pad12; + uchar_t driverIdHigh; + uchar_t pad14; + uchar_t bytesUsed; + uchar_t pad16; + uchar_t CRC; + uchar_t pad18; + uchar_t packSpecific[0x3f-0x18]; +} ipac_idProm_t; + + +/* These are the types of address space implemented in the IP + specification. Some IP modules only use the ID and IO spaces. */ + +#define IPAC_ADDR_SPACES 4 + +typedef enum { + ipac_addrID = 0, /* ID Prom space */ + ipac_addrIO = 1, /* Registers etc */ + ipac_addrIO32 = 2, /* Registers for 32-bit dual-slot */ + ipac_addrMem = 3 /* Memory space */ +} ipac_addr_t; + + +/* The following are the possible commands to the carrier driver to + handle interrupts from the IP modules. Some carriers will only be + able to implement a subset of these commands. Note that irqEnable + should call the vxWorks sysBusEnable routine if this is needed to + pass the carrier interrupts through to the CPU. */ + +typedef enum { + ipac_irqLevel0 = 0, /* Disables interrupts */ + ipac_irqLevel1 = 1, /* Lowest priority */ + ipac_irqLevel2 = 2, + ipac_irqLevel3 = 3, + ipac_irqLevel4 = 4, + ipac_irqLevel5 = 5, + ipac_irqLevel6 = 6, /* Highest priority */ + ipac_irqLevel7 = 7, /* Non-maskable, don't use */ + ipac_irqGetLevel, /* Returns level set (or hard-coded) */ + ipac_irqEnable, /* Required to use interrupts */ + ipac_irqDisable, /* Not necessarily supported */ + ipac_irqPoll, /* Returns interrupt state */ + ipac_irqSetEdge, /* Sets edge-triggered interrupts */ + ipac_irqSetLevel, /* Sets level-triggered (default) */ + ipac_irqClear /* Only needed if using edge-triggered */ +} ipac_irqCmd_t; + + +/* This is a table which each IPAC carrier driver provides to allow + it to be queried by the IPAC driver. One table is required for + each type of carrier. The cPrivate pointer is returned by the + carrier driver initialise routine, and passed to all of the other + routines as a means of identification of the carrier board. */ + +typedef struct { + char *carrierType; + /* String containing carrier board type */ + ushort_t numberSlots; + /* Number of IPAC devices this carrier can hold */ + int (*initialise)(char *cardParams, void **cPrivate); + /* Initialise carrier and return *cPrivate */ + char *(*report)(void *cPrivate, ushort_t slot); + /* Return string with giving status of this slot */ + void *(*baseAddr)(void *cPrivate, ushort_t slot, ipac_addr_t space); + /* Return base addresses for this slot */ + int (*irqCmd)(void *cPrivate, ushort_t slot, + ushort_t irqNumber, ipac_irqCmd_t cmd); + /* interrupt manipulation */ +} ipac_carrier_t; + + +/* Functions for startup and interactive use */ + +extern int ipacAddCarrier(ipac_carrier_t *pcarrier, char *cardParams); +extern int ipacReport(int interest); +extern int ipacInitialise(int after); + + +/* Functions for use in IPAC module drivers */ + +extern int ipmCheck(ushort_t carrier, ushort_t slot); +extern int ipmValidate(ushort_t carrier, ushort_t slot, + uchar_t manufacturerId, uchar_t modelId); +extern char *ipmReport(ushort_t carrier, ushort_t slot); +extern void *ipmBaseAddr(ushort_t carrier, ushort_t slot, ipac_addr_t space); +extern int ipmIrqCmd(ushort_t carrier, ushort_t slot, + ushort_t irqNumber, ipac_irqCmd_t cmd); + + +#ifdef __cplusplus +} +#endif + +#endif /* INCipacH */ + diff --git a/src/drv/ansi/drvTip810.c b/src/drv/ansi/drvTip810.c new file mode 100644 index 000000000..303de4b4c --- /dev/null +++ b/src/drv/ansi/drvTip810.c @@ -0,0 +1,1096 @@ +/******************************************************************************* + +Project: + Gemini/UKIRT CAN Bus Driver for EPICS + +File: + drvTip810.c + +Description: + CAN Bus driver for TEWS TIP810 Industry-Pack Module. + +Author: + Andrew Johnson +Created: + 20 July 1995 + +(c) 1995 Royal Greenwich Observatory + +*******************************************************************************/ + +#include "drvTip810.h" +#include "drvIpac.h" +#include "pca82c200.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#define T810_INT_VEC_BASE 0x60 +#define T810_MAGIC_NUMBER 81001 + + +#ifndef NO_EPICS +#include + +/* EPICS Driver Support Entry Table */ + +struct drvet drvTip810 = { + 3, + (DRVSUPFUN) t810Report, + (DRVSUPFUN) t810Initialise, + NULL +}; + +#endif /* NO_EPICS */ + + +typedef void callback_t(void *pprivate, long parameter); + +typedef struct callbackTable_s { + struct callbackTable_s *pnext; /* linked list ... */ + void *pprivate; /* reference for callback routine */ + callback_t *pcallback; /* registered routine */ +} callbackTable_t; + + +typedef struct t810Dev_s { + struct t810Dev_s *pnext; /* To next device. Must be first member */ + int magicNumber; /* device pointer confirmation */ + char *pbusName; /* Bus identification */ + ushort_t card; /* Industry Pack address */ + ushort_t slot; /* " " " */ + uint_t busRate; /* bit rate of bus in Kbits/sec */ + pca82c200_t *pchip; /* controller registers */ + uchar_t *pintVec; /* interrupt vector register */ + SEM_ID txSem; /* Transmit buffer protection */ + uint_t txCount; /* messages transmitted */ + uint_t rxCount; /* messages received */ + uint_t overCount; /* overrun - lost messages */ + uint_t unusedCount; /* messages without callback */ + ushort_t unusedId; /* last ID received without a callback */ + uint_t errorCount; /* Times entered Error state */ + uint_t busOffCount; /* Times entered Bus Off state */ + SEM_ID readSem; /* canRead task Mutex */ + canMessage_t *preadBuffer; /* canRead destination buffer */ + SEM_ID rxSem; /* canRead message arrival signal */ + callbackTable_t *pmsgHandler[CAN_IDENTIFIERS]; /* message callbacks */ + callbackTable_t *psigHandler; /* error signal callbacks */ +} t810Dev_t; + + +t810Dev_t *pt810First = NULL; + + +/******************************************************************************* + +Routine: + t810Report + +Purpose: + Report status of all t810 devices + +Description: + Prints a list of all the t810 devices created, their IP carrier & + slot numbers and the bus name string. For interest > 0 it gives + some message statistics, and for interest > 1 also lists all CAN + IDs for which a callback has been registered. + +Returns: + OK, or + S_t810_badDevice if device list corrupted. + +*/ + +int t810Report ( + int interest +) { + t810Dev_t *pdevice = pt810First; + ushort_t id, printed; + uchar_t status; + + while (pdevice != NULL) { + if (pdevice->magicNumber != T810_MAGIC_NUMBER) { + printf("t810 device list is corrupt\n"); + return S_t810_badDevice; + } + + printf(" '%s' : IP Carrier %hd Slot %hd, Bus rate %d Kbits/sec\n", + pdevice->pbusName, pdevice->card, pdevice->slot, + pdevice->busRate); + + switch (interest) { + case 1: + printf("\tMessages Sent : %5d\n", pdevice->txCount); + printf("\tMessages Received : %5d\n", pdevice->rxCount); + printf("\tMessage Overruns : %5d\n", pdevice->overCount); + printf("\tDiscarded Messages : %5d\n", pdevice->unusedCount); + if (pdevice->unusedCount > 0) { + printf("\tLast Discarded ID : %#5x\n", pdevice->unusedId); + } + printf("\tError Interrupts : %5d\n", pdevice->errorCount); + printf("\tBus Off Events : %5d\n", pdevice->busOffCount); + break; + + case 2: + printed = 0; + printf("\tCallbacks registered: "); + for (id=0; id < CAN_IDENTIFIERS; id++) { + if (pdevice->pmsgHandler[id] != NULL) { + if (printed % 10 == 0) { + printf("\n\t "); + } + printf("0x%-3hx ", id); + printed++; + } + } + if (printed == 0) { + printf("None."); + } + printf("\n\tcanRead Status : %s\n", + pdevice->preadBuffer ? "Active" : "Idle"); + break; + + case 3: + printf(" pca82c200 Chip Status:\n"); + status = pdevice->pchip->status; + + printf("\tBus Status : %s\n", + status & PCA_SR_BS ? "Bus-Off" : "Bus-On"); + printf("\tError Status : %s\n", + status & PCA_SR_ES ? "Error" : "Ok"); + printf("\tData Overrun : %s\n", + status & PCA_SR_DO ? "Overrun" : "Ok"); + printf("\tReceive Status : %s\n", + status & PCA_SR_RS ? "Receiving" : "Idle"); + printf("\tReceive Buffer Status : %s\n", + status & PCA_SR_RBS ? "Full" : "Empty"); + printf("\tTransmit Status : %s\n", + status & PCA_SR_TS ? "Transmitting" : "Idle"); + printf("\tTransmission Complete : %s\n", + status & PCA_SR_TCS ? "Complete" : "Incomplete"); + printf("\tTransmit Buffer Access : %s\n", + status & PCA_SR_TBS ? "Released" : "Locked"); + break; + } + pdevice = pdevice->pnext; + } + return OK; +} + + +/******************************************************************************* + +Routine: + t810Create + +Purpose: + Register a new TIP810 device + +Description: + Checks that the given name and card/slot numbers are unique, then + creates a new device table, initialises it and adds it to the end + of the linked list. + +Returns: + + +Example: + t810Create "CAN1", 0, 0, 500 + +*/ + +int t810Create ( + char *pbusName, /* Unique Identifier for this device */ + ushort_t card, /* Ipac Driver card .. */ + ushort_t slot, /* .. and slot number */ + uint_t busRate /* in Kbits/sec */ +) { + static const struct { + uint_t rate; + uchar_t busTiming0; + uchar_t busTiming1; + } rateTable[] = { + 5, PCA_BTR0_5K, PCA_BTR1_5K, + 10, PCA_BTR0_10K, PCA_BTR1_10K, + 20, PCA_BTR0_20K, PCA_BTR1_20K, + 50, PCA_BTR0_50K, PCA_BTR1_50K, + 100, PCA_BTR0_100K, PCA_BTR1_100K, + 125, PCA_BTR0_125K, PCA_BTR1_125K, + 250, PCA_BTR0_250K, PCA_BTR1_250K, + 500, PCA_BTR0_500K, PCA_BTR1_500K, + 1000, PCA_BTR0_1M0, PCA_BTR1_1M0, + 1600, PCA_BTR0_1M6, PCA_BTR1_1M6, + -125, PCA_KVASER_125K, PCA_BTR1_KVASER, + -250, PCA_KVASER_250K, PCA_BTR1_KVASER, + -500, PCA_KVASER_500K, PCA_BTR1_KVASER, + -1000,PCA_KVASER_1M0, PCA_BTR1_KVASER, + 0 + }; + t810Dev_t *pdevice, *plist = (t810Dev_t *) &pt810First; + int status, rateIndex, id; + + status = ipmValidate(card, slot, IP_MANUFACTURER_TEWS, + IP_MODEL_TEWS_TIP810); + if (status) { + return status; + } + /* Slot contains a real TIP810 module */ + + if (busRate == 0) { + return S_t810_badBusRate; + } + for (rateIndex = 0; rateTable[rateIndex].rate != busRate; rateIndex++) { + if (rateTable[rateIndex].rate == 0) { + return S_t810_badBusRate; + } + } + /* Bus rate is legal and we now know the right chip settings */ + + while (plist->pnext != NULL) { + plist = plist->pnext; + if (strcmp(plist->pbusName, pbusName) == 0 || + (plist->card == card && + plist->slot == slot)) { + return S_t810_duplicateDevice; + } + } + /* plist now points to the last item in the list */ + + pdevice = malloc(sizeof (t810Dev_t)); + if (pdevice == NULL) { + return errno; + } + /* pdevice is our new device table */ + + pdevice->pnext = NULL; + pdevice->magicNumber = T810_MAGIC_NUMBER; + pdevice->pbusName = pbusName; + pdevice->card = card; + pdevice->slot = slot; + pdevice->busRate = busRate; + pdevice->pchip = (pca82c200_t *) ipmBaseAddr(card, slot, ipac_addrIO); + pdevice->pintVec = 0x41 + (uchar_t *) pdevice->pchip; + pdevice->preadBuffer = NULL; + pdevice->psigHandler = NULL; + + for (id=0; idpmsgHandler[id] = NULL; + } + + pdevice->txSem = semBCreate(SEM_Q_PRIORITY, SEM_FULL); + pdevice->rxSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); + pdevice->readSem = semMCreate(SEM_Q_PRIORITY | + SEM_INVERSION_SAFE | + SEM_DELETE_SAFE); + if (pdevice->txSem == NULL || + pdevice->rxSem == NULL || + pdevice->readSem == NULL) { + free(pdevice); /* Ought to free those semaphores, but... */ + return errno; + } + + plist->pnext = pdevice; + /* device table interface stuff filled in and added to list */ + + pdevice->pchip->control = PCA_CR_RR; /* Reset state */ + pdevice->pchip->acceptanceCode = 0; + pdevice->pchip->acceptanceMask = 0xff; + pdevice->pchip->busTiming0 = rateTable[rateIndex].busTiming0; + pdevice->pchip->busTiming1 = rateTable[rateIndex].busTiming1; + pdevice->pchip->outputControl = PCA_OCR_OCM_NORMAL | + PCA_OCR_OCT0_PUSHPULL | + PCA_OCR_OCT1_PUSHPULL; + /* chip now initialised, but held in the Reset state */ + + return OK; +} + + +/******************************************************************************* + +Routine: + t810Shutdown + +Purpose: + Reboot hook routine + +Description: + Stops interrupts and resets the CAN controller chip. + +Returns: + void + +*/ + +int t810Shutdown ( + int startType +) { + t810Dev_t *pdevice = pt810First; + + while (pdevice != NULL) { + if (pdevice->magicNumber != T810_MAGIC_NUMBER) { + /* Whoops! */ + return S_t810_badDevice; + } + + pdevice->pchip->control = PCA_CR_RR; /* Reset, interrupts off */ + pdevice = pdevice->pnext; + } + return OK; +} + + +/******************************************************************************* + +Routine: + getRxMessage + +Purpose: + Copy a received message from chip to memory + +Description: + Reads a message from the chip receive buffer into the message buffer + and flags to chip to release the buffer for further input. + +Returns: + void + +*/ + +LOCAL void getRxMessage ( + pca82c200_t *pchip, + canMessage_t *pmessage +) { + uchar_t desc0, desc1, i; + + desc0 = pchip->rxBuffer.descriptor0; + desc1 = pchip->rxBuffer.descriptor1; + + pmessage->identifier = (desc0 << PCA_MSG_ID0_RSHIFT) | + ((desc1 & PCA_MSG_ID1_MASK) >> PCA_MSG_ID1_LSHIFT); + pmessage->length = desc1 & PCA_MSG_DLC_MASK; + + if (desc1 & PCA_MSG_RTR) { + pmessage->rtr = RTR; + } else { + pmessage->rtr = SEND; + for (i=0; ilength; i++) { + pmessage->data[i] = pchip->rxBuffer.data[i]; + } + } + + pchip->command = PCA_CMR_RRB; /* Finished with chip buffer */ +} + + +/******************************************************************************* + +Routine: + putTxMessage + +Purpose: + Copy a message from memory to the chip + +Description: + Copies a message from the message buffer into the chip receive buffer + and flags to chip to transmit the message. + +Returns: + void + +*/ + +LOCAL void putTxMessage ( + pca82c200_t *pchip, + canMessage_t *pmessage +) { + uchar_t desc0, desc1, i; + + desc0 = pmessage->identifier >> PCA_MSG_ID0_RSHIFT; + desc1 = (pmessage->identifier << PCA_MSG_ID1_LSHIFT) & PCA_MSG_ID1_MASK; + desc1 |= pmessage->length & PCA_MSG_DLC_MASK; + + if (pmessage->rtr == SEND) { + for (i=0; ilength; i++) { + pchip->txBuffer.data[i] = pmessage->data[i]; + } + } else { + desc1 |= PCA_MSG_RTR; + } + + pchip->txBuffer.descriptor0 = desc0; + pchip->txBuffer.descriptor1 = desc1; + + pchip->command = PCA_CMR_TR; +} + + +/******************************************************************************* + +Routine: + doCallbacks + +Purpose: + calls all routines in the given list + +Description: + + +Returns: + void + +*/ + +LOCAL void doCallbacks ( + callbackTable_t *phandler, + long parameter +) { + while (phandler != NULL) { + (*phandler->pcallback)(phandler->pprivate, parameter); + phandler = phandler->pnext; + } +} + + +/******************************************************************************* + +Routine: + t810ISR + +Purpose: + Interrupt Service Routine + +Description: + + +Returns: + void + +*/ + +LOCAL void t810ISR ( + t810Dev_t *pdevice +) { + uchar_t intSource = pdevice->pchip->interrupt; + + if (intSource & PCA_IR_WUI) { /* Wake-up Interrupt */ + logMsg("Wake-up Interrupt from CANbus '%s'\n", + (int) pdevice->pbusName, 0, 0, 0, 0, 0); + } + + if (intSource & PCA_IR_TI) { /* Transmit Interrupt */ + pdevice->txCount++; + semGive(pdevice->txSem); + } + + if (intSource & PCA_IR_RI) { /* Receive Interrupt */ + canMessage_t message; + callbackTable_t *phandler; + + /* Take a local copy of the message */ + getRxMessage(pdevice->pchip, &message); + pdevice->rxCount++; + + /* Look up the message ID and do the message callbacks */ + phandler = pdevice->pmsgHandler[message.identifier]; + if (phandler == NULL) { + pdevice->unusedId = message.identifier; + pdevice->unusedCount++; + } else { + doCallbacks(phandler, (long) &message); + } + + /* If canRead is waiting for this ID, give it the message and kick it */ + if (pdevice->preadBuffer != NULL && + pdevice->preadBuffer->identifier == message.identifier) { + memcpy(pdevice->preadBuffer, &message, sizeof(canMessage_t)); + pdevice->preadBuffer = NULL; + semGive(pdevice->rxSem); + } + } + + if (intSource & PCA_IR_OI) { /* Overrun Interrupt */ + pdevice->overCount++; + pdevice->pchip->command = PCA_CMR_COS; + } + + if (intSource & PCA_IR_EI) { /* Error Interrupt */ + callbackTable_t *phandler = pdevice->psigHandler; + ushort_t status; + + switch (pdevice->pchip->status & (PCA_SR_ES | PCA_SR_BS)) { + case PCA_SR_ES: + status = CAN_BUS_ERROR; + pdevice->errorCount++; + break; + case PCA_SR_BS: + case PCA_SR_BS | PCA_SR_ES: + status = CAN_BUS_OFF; + pdevice->busOffCount++; + pdevice->pchip->control &= ~PCA_CR_RR; /* Clear Reset state */ + break; + default: + status = CAN_BUS_OK; + break; + } + + doCallbacks(phandler, status); + } +} + + +/******************************************************************************* + +Routine: + t810Initialise + +Purpose: + Initialise driver and all registered hardware + +Description: + Under EPICS this routine is called by iocInit, which must occur + after all canCreate calls in the startup script. It completes the + initialisation of the CAN controller chip and interrupt vector + registers for all known TIP810 devices and starts the chip + running. A reboot hook is used to make sure all interrupts are + turned off if the OS is shut down. + +Returns: + + +*/ + +int t810Initialise ( + void +) { + uchar_t intVec = T810_INT_VEC_BASE, intLevel; + t810Dev_t *pdevice = pt810First; + int status = OK; + + rebootHookAdd(t810Shutdown); + + while (pdevice != NULL) { + pdevice->txCount = 0; + pdevice->rxCount = 0; + pdevice->overCount = 0; + pdevice->unusedCount = 0; + pdevice->errorCount = 0; + pdevice->busOffCount = 0; + + if (intConnect(INUM_TO_IVEC(intVec), t810ISR, (int) pdevice)) { + status = errno; + } + *(pdevice->pintVec) = intVec++; + + intLevel = ipmIrqCmd(pdevice->card, pdevice->slot, 0, ipac_irqGetLevel); + sysIntEnable(intLevel); + + pdevice->pchip->control = PCA_CR_OIE | + PCA_CR_EIE | + PCA_CR_TIE | + PCA_CR_RIE; + + pdevice = pdevice->pnext; + } + return status; +} + + +/******************************************************************************* + +Routine: + canOpen + +Purpose: + Return device pointer for given CAN bus name + +Description: + Searches through the linked list of known t810 devices for one + which matches the name given, and returns the device pointer + associated with the relevant device table. + +Returns: + OK, or S_can_noDevice if no match found. + +Example: + void *can1; + status = canOpen("CAN1", &can1); + +*/ + +int canOpen ( + char *pbusName, + void **ppdevice +) { + t810Dev_t *pdevice = pt810First; + + while (pdevice != NULL) { + if (strcmp(pdevice->pbusName, pbusName) == 0) { + *ppdevice = pdevice; + return OK; + } + pdevice = pdevice->pnext; + } + return S_can_noDevice; +} + + +/******************************************************************************* + +Routine: + strdupn + +Purpose: + duplicate n characters of a string and return pointer to new substring + +Description: + Copies n characters from the input string to a newly malloc'ed memory + buffer, and adds a trailing '\0', then returns the new string pointer. + +Returns: + char *newString, or NULL if malloc failed. + +*/ + +LOCAL char* strdupn ( + const char *ct, + size_t n +) { + char *duplicate; + + duplicate = malloc(n+1); + if (duplicate == NULL) { + return NULL; + } + + memcpy(duplicate, ct, n); + duplicate[n] = '\0'; + + return duplicate; +} + + +/******************************************************************************* + +Routine: + canIoParse + +Purpose: + Parse a CAN address string into a canIo_t structure + +Description: + + +Returns: + OK, or + S_can_badAddress for illegal input strings, + S_can_noDevice for an unregistered bus name. + +Example: + canIoParse("CAN1/20:0126.4 0xfff", &myIo); + +*/ + +int canIoParse ( + char *canString, + canIo_t *pcanIo +) { + char separator; + char *name; + + pcanIo->canBusID = NULL; + + if (canString == NULL || + pcanIo == NULL) { + return S_can_badAddress; + } + + /* Get rid of leading whitespace and non-alphanumeric chars */ + while (!isalnum(*canString)) { + if (*canString++ == '\0') { + return S_can_badAddress; + } + } + + /* First part of string is the bus name */ + name = canString; + + /* find the end of the busName */ + canString = strpbrk(canString, "/:"); + if (canString == NULL || + *canString == '\0') { + return S_can_badAddress; + } + + /* now we're at character after the end of the busName */ + pcanIo->busName = strdupn(name, canString - name); + if (pcanIo->busName == NULL) { + return errno; + } + separator = *canString++; + + /* Handle / if present, convert from ms to ticks */ + if (separator == '/') { + pcanIo->timeout = strtol(canString, &canString, 0) * sysClkRateGet(); + pcanIo->timeout = ((pcanIo->timeout + 500) / 1000); + separator = *canString++; + } else { + pcanIo->timeout = WAIT_FOREVER; + } + + /* String must contain : */ + if (separator != ':') { + return S_can_badAddress; + } + pcanIo->identifier = strtoul(canString, &canString, 0); + + /* Handle . if present */ + separator = *canString++; + if (separator == '.') { + pcanIo->offset = strtoul(canString, &canString, 0); + if (pcanIo->offset >= CAN_DATA_SIZE) { + return S_can_badAddress; + } + separator = *canString++; + } else { + pcanIo->offset = 0; + } + + /* Final parameter is separated by whitespace */ + if (separator != ' ' && + separator != '\t') { + return S_can_badAddress; + } + pcanIo->parameter = strtol(canString, &canString, 0); + + /* Ok, finally look up the bus name */ + return canOpen(pcanIo->busName, &pcanIo->canBusID); +} + + +/******************************************************************************* + +Routine: + canWrite + +Purpose: + writes a CAN message to the bus + +Description: + + +Returns: + + +Example: + + +*/ + +int canWrite ( + void *canBusID, + canMessage_t *pmessage, + int timeout +) { + t810Dev_t *pdevice = (t810Dev_t *) canBusID; + int status; + + if (pdevice->magicNumber != T810_MAGIC_NUMBER) { + return S_t810_badDevice; + } + + if (pmessage->identifier >= CAN_IDENTIFIERS || + pmessage->length > CAN_DATA_SIZE || + (pmessage->rtr != SEND && pmessage->rtr != RTR)) { + return S_can_badMessage; + } + + status = semTake(pdevice->txSem, timeout); + if (status) { + return errno; + } + + if (pdevice->pchip->status & PCA_SR_TBS) { + putTxMessage(pdevice->pchip, pmessage); + return OK; + } else { + semGive(pdevice->txSem); + return S_t810_transmitterBusy; + } +} + + +/******************************************************************************* + +Routine: + canMessage + +Purpose: + Register CAN message callback + +Description: + Adds a new callback routine for the given CAN message ID on the + given device. There can be any number of callbacks for the same ID, + and all are called in turn when a message with this ID is + received. As a result, the callback routine must not change the + message at all - it is only permitted to examine it. The callback + is called from vxWorks Interrupt Context, thus there are several + restrictions in what the routine can perform (see vxWorks User + Guide for details of these). The callback routine should be + declared of type canMsgCallback_t + void callback(void *pprivate, can_Message_t *pmessage); + The pprivate value supplied to canMessage is passed to the callback + routine with each message to allow it to identify its context. + +Returns: + OK, + S_can_badMessage for bad identifier or NULL callback routine, + S_can_badDevice for bad device pointer. + +Example: + + +*/ + +int canMessage ( + void *canBusID, + ushort_t identifier, + canMsgCallback_t *pcallback, + void *pprivate +) { + t810Dev_t *pdevice = (t810Dev_t *) canBusID; + callbackTable_t *phandler, *plist; + + if (pdevice->magicNumber != T810_MAGIC_NUMBER) { + return S_t810_badDevice; + } + + if (identifier >= CAN_IDENTIFIERS || + pcallback == NULL) { + return S_can_badMessage; + } + + phandler = malloc(sizeof (callbackTable_t)); + if (phandler == NULL) { + return errno; + } + + phandler->pnext = NULL; + phandler->pprivate = pprivate; + phandler->pcallback = (callback_t *) pcallback; + + plist = (callbackTable_t *) (&pdevice->pmsgHandler[identifier]); + while (plist->pnext != NULL) { + plist = plist->pnext; + } + /* plist now points to the last handler in the list */ + + plist->pnext = phandler; + return OK; +} + + +/******************************************************************************* + +Routine: + canSignal + +Purpose: + Register CAN error signal callback + +Description: + Adds a new callback routine for the CAN error reports. There can be + any number of error callbacks, and all are called in turn when the + controller chip reports an error or bus Off The callback is called + from vxWorks Interrupt Context, thus there are restrictions in what + the routine can perform (see vxWorks User Guide for details of + these). The callback routine should be declared a canSigCallback_t + void callback(void *pprivate, ushort_t status); + The pprivate value supplied to canSignal is passed to the callback + routine with the error status to allow it to identify its context. + Status values will be one of + CAN_BUS_OK, + CAN_BUS_ERROR or + CAN_BUS_OFF. + If the chip goes to the Bus Off state, the driver will attempt to + restart it. + +Returns: + OK, + S_can_badDevice for bad device pointer. + +Example: + + +*/ + +int canSignal ( + void *canBusID, + canSigCallback_t *pcallback, + void *pprivate +) { + t810Dev_t *pdevice = (t810Dev_t *) canBusID; + callbackTable_t *phandler, *plist; + + if (pdevice->magicNumber != T810_MAGIC_NUMBER) { + return S_t810_badDevice; + } + + phandler = malloc(sizeof (callbackTable_t)); + if (phandler == NULL) { + return errno; + } + + phandler->pnext = NULL; + phandler->pprivate = pprivate; + phandler->pcallback = (callback_t *) pcallback; + + plist = (callbackTable_t *) (&pdevice->psigHandler); + while (plist->pnext != NULL) { + plist = plist->pnext; + } + /* plist now points to the last handler in the list */ + + plist->pnext = phandler; + return OK; +} + + +/******************************************************************************* + +Routine: + canRead + +Purpose: + read incoming CAN message, any ID number + +Description: + The simplest way to implement this is have canRead take a message + ID in the buffer, send an RTR and look for the returned value of + this message. This is in keeping with the CAN philosophy and makes + it useful for simple software interfaces. More complex ones ought + to use the canMessage callback functions. + +Returns: + OK, or + S_t810_badDevice for bad bus ID, + S_can_badMessage for bad message Identifier or length, + vxWorks errno for semTake failures (timeout etc). + +Example: + canMessage_t myBuffer = { + 139, // Can ID + 0, // RTR + 4 // Length + }; + int status = canRead(canID, &myBuffer, WAIT_FOREVER); + +*/ + +int canRead ( + void *canBusID, + canMessage_t *pmessage, + int timeout +) { + t810Dev_t *pdevice = (t810Dev_t *) canBusID; + int status; + + if (pdevice->magicNumber != T810_MAGIC_NUMBER) { + return S_t810_badDevice; + } + + if (pmessage->identifier >= CAN_IDENTIFIERS || + pmessage->length > CAN_DATA_SIZE) { + return S_can_badMessage; + } + + /* This semaphore is so only one task canRead simultaneously */ + status = semTake(pdevice->readSem, timeout); + if (status) { + return errno; + } + + pdevice->preadBuffer = pmessage; + + /* All set for the reply, now send the request */ + pmessage->rtr = RTR; + + status = canWrite(canBusID, pmessage, timeout); + if (status == OK) { + /* Wait for the message to be recieved */ + status = semTake(pdevice->rxSem, timeout); + if (status) { + status = errno; + } + } + if (status) { + /* Problem (timeout) sending the RTR or receiving the reply */ + pdevice->preadBuffer = NULL; + semTake(pdevice->rxSem, NO_WAIT); /* Must leave this EMPTY */ + } + semGive(pdevice->readSem); + return status; +} + + +/******************************************************************************* + +Routine: + canTest + +Purpose: + Test routine, sends a single message to the named bus. + +Description: + + +Returns: + + +Example: + + +*/ + +int canTest ( + char *pbusName, + ushort_t identifier, + ushort_t rtr, + uchar_t length, + char *data +) { + void *canBusID; + canMessage_t message; + int status = canOpen(pbusName, &canBusID); + + if (status) { + printf("Error %d opening CAN bus '%s'\n", status, pbusName); + return ERROR; + } + + message.identifier = identifier; + message.rtr = rtr ? RTR : SEND; + message.length = length; + + if (rtr == 0) { + memcpy(&message.data[0], data, length); + } + + status = canWrite(canBusID, &message, 0); + if (status) { + printf("Error %d writing message\n", status); + return ERROR; + } + return OK; +} diff --git a/src/drv/ansi/drvTip810.h b/src/drv/ansi/drvTip810.h new file mode 100644 index 000000000..43cae8e4f --- /dev/null +++ b/src/drv/ansi/drvTip810.h @@ -0,0 +1,48 @@ +/******************************************************************************* + +Project: + Gemini/UKIRT CAN Bus Driver for EPICS + +File: + drvTip810.h + +Description: + Header file for TEWS TIP810 CAN Bus driver. + +Author: + Andrew Johnson +Created: + 20 July 1995 + +(c) 1995 Royal Greenwich Observatory + +*******************************************************************************/ + + +#ifndef INCdrvTip810H +#define INCdrvTip810H + +#include +#include "canBus.h" + + +/* Error Numbers */ + +#ifndef M_t810 +#define M_t810 (810<<16) +#endif + +#define S_t810_duplicateDevice (M_t810| 1) /*duplicate t810 device definition*/ +#define S_t810_badBusRate (M_t810| 2) /*CANbus bit rate not supported*/ +#define S_t810_badDevice (M_t810| 3) /*device pointer is not for t810*/ +#define S_t810_transmitterBusy (M_t810| 4) /*transmit buffer unexpectedly busy*/ + + +extern int t810Report(int page); +extern int t810Create(char *busName, ushort_t card, ushort_t slot, + uint_t busRate); +extern int t810Shutdown(int starttype); +extern int t810Initialise(void); + +#endif /* INCdrvTip810H */ + diff --git a/src/drv/ansi/drvVipc310.c b/src/drv/ansi/drvVipc310.c new file mode 100644 index 000000000..b393033b0 --- /dev/null +++ b/src/drv/ansi/drvVipc310.c @@ -0,0 +1,262 @@ +/******************************************************************************* + +Project: + Gemini/UKIRT CAN Bus Driver for EPICS + +File: + drvVipc310.c + +Description: + IPAC Carrier Driver for the GreenSpring VIPC310 Dual IndustryPack + Carrier VME board, provides the interface between IPAC driver and the + hardware. This carrier is 3U high, and thus cannot support 32-bit + accesses to dual-slot IP modules. + +Author: + Andrew Johnson +Created: + 5 July 1995 + +(c) 1995 Royal Greenwich Observatory + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include "drvIpac.h" + + +/* Characteristics of the card */ + +#define SLOTS 2 +#define IO_SPACES 2 /* Address spaces in A16 */ +#define IPAC_IRQS 2 /* Interrupts per module */ + + +/* Offsets from base address in VME A16 */ + +#define REGS_A 0x0000 +#define PROM_A 0x0080 +#define REGS_B 0x0100 +#define PROM_B 0x0180 + + +/* VME Interrupt levels */ + +#define IRQ_A0 4 +#define IRQ_A1 5 +#define IRQ_B0 2 +#define IRQ_B1 1 + + +/* Carrier Private structure type, one instance per board */ + +typedef void* private_t[IPAC_ADDR_SPACES][SLOTS]; + + +/******************************************************************************* + +Routine: + initialise + +Purpose: + Creates new private table for VIPC310 at addresses given by cardParams + +Description: + Checks the parameter string for the address of the card I/O space and + optional size of the memory space for the modules. If both the I/O and + memory base addresses can be reached from the CPU, a private table is + created for this board. The private table is a 2-D array of pointers + to the base addresses of the various accessible parts of the IP module. + +Parameters: + The parameter string should comprise a hex number (the 0x or 0X at the + start is optional) optionally followed by a comma and a decimal integer. + The first number is the I/O base address of the card in the VME A16 + address space (the factory default is 0x6000). If present the second + number gives the memory space in Kbytes allocated to each IP module. + The memory base address of the VIPC310 card is set using the same jumpers + as the I/O base address and is always 256 times the I/O base address, + but in the VME A24 address space. The factory default fot the memory + base address is thus 0x600000. If the memory size parameter is omitted + or set to zero then neither IP module provides any memory space. Legal + memory size values are 0, 64, 128, 256, 512, 1024 or 2048. The memory + size interacts with the memory base address such that it is possible to + set the existance of memory in either slot independently with suitable + adjustment of the base address. + +Examples: + "0x6000" + This indicates that the carrier has its I/O base set to 0x6000, and + neither slot uses any memory space. + "1000,512" + Here the I/O base is set to 0x1000, and there is 512 Kbytes of + memory on each module, with the IP module A memory at 0x100000 + and module B at 0x180000. + "0xfe00, 128" + The I/O base is at 0xfe00, and hence the carrier memory base is + 0xfe0000. However because the memory size is set to give each module + 128 Kbytes of memory space, module A cannot be selected (128 K = + 0x020000, so the module is decoded at 0xfc0000 but can't be accessed + because this is below the memory base). + +Returns: + 0 = OK, + S_IPAC_badAddress = Parameter string error, or address not reachable + +*/ + +LOCAL int initialise ( + char *cardParams, + void **pprivate +) { + int params, status1 = OK, status2 = OK, mSize = 0; + long ioBase, mOrig, mBase; + ushort_t space, slot; + private_t *private; + static const int offset[IO_SPACES][SLOTS] = { + PROM_A, PROM_B, + REGS_A, REGS_B + }; + + if (cardParams == NULL || + strlen(cardParams) == 0) { + ioBase = 0x6000; + } else { + params = sscanf(cardParams, "%p,%i", (void **) &ioBase, &mSize); + if (params < 1 || params > 2 || + ioBase > 0xfe00 || ioBase & 0x01ff || + mSize < 0 || mSize > 2048 || mSize & 63) { + return S_IPAC_badAddress; + } + } + + mBase = ioBase << 8; /* Fixed by VIPC310 card */ + + status1 = sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO, + (char *) ioBase, (char **) &ioBase); + if (mSize > 0) { + status2 = sysBusToLocalAdrs(VME_AM_STD_SUP_DATA, + (char *) mBase, (char **) &mBase); + } + if (status1 || status2) { + return S_IPAC_badAddress; + } + + mSize = mSize << 10; /* Convert size from K to Bytes */ + mOrig = mBase & ~(mSize * SLOTS - 1); + + private = malloc(sizeof (private_t)); + for (space = 0; space < IO_SPACES; space++) { + for (slot = 0; slot < SLOTS; slot++) { + (*private)[space][slot] = (void *) (ioBase + offset[space][slot]); + } + } + + (*private)[ipac_addrIO32][0] = NULL; + (*private)[ipac_addrIO32][1] = NULL; + + if (mOrig == mBase) { + (*private)[ipac_addrMem][0] = (void *) mBase; + (*private)[ipac_addrMem][1] = (void *) (mBase + mSize); + } else { + (*private)[ipac_addrMem][0] = NULL; + (*private)[ipac_addrMem][1] = (void *) mBase; + } + + *pprivate = private; + return OK; +} + + +/******************************************************************************* + +Routine: + baseAddr + +Purpose: + Returns the base address for the requested slot & address space + +Description: + Because we did all that hard work in the initialise routine, this + routine only has to do a table lookup in the private array. + Note that no parameter checking is required - the IPAC driver which + calls this routine handles that. + +Returns: + The requested address, or NULL if the module has no memory. + +*/ + +LOCAL void *baseAddr ( + void *private, + ushort_t slot, + ipac_addr_t space +) { + return (*(private_t *) private)[space][slot]; +} + + +/******************************************************************************* + +Routine: + irqCmd + +Purpose: + Handles interrupter commands and status requests + +Description: + The GreenSpring board is limited to fixed interrupt levels, and has + no control over interrupts. The only commands thus supported are + a request of the interrupt level associated with a particular slot + and interrupt number, or to enable interrupts by making sure the + VMEbus interrupter is listening on the necessary level. + +Returns: + ipac_irqGetLevel returns the interrupt level (1, 2, 4 or 5), + ipac_irqEnable returns 0 = OK, + other calls return S_IPAC_notImplemented. + +*/ + +LOCAL int irqCmd ( + void *private, + ushort_t slot, + ushort_t irqNumber, + ipac_irqCmd_t cmd +) { + static const int irqLevel[SLOTS][IPAC_IRQS] = { + IRQ_A0, IRQ_A1, + IRQ_B0, IRQ_B1 + }; + + switch (cmd) { + case ipac_irqGetLevel: + return irqLevel[slot][irqNumber]; + + case ipac_irqEnable: + sysIntEnable(irqLevel[slot][irqNumber]); + return OK; + + default: + return S_IPAC_notImplemented; + } +} + +/******************************************************************************/ + + +/* IPAC Carrier Table */ + +ipac_carrier_t vipc310 = { + "GreenSpring VIPC310", + SLOTS, + initialise, + NULL, + baseAddr, + irqCmd +}; + diff --git a/src/drv/ansi/drvVipc610.c b/src/drv/ansi/drvVipc610.c new file mode 100644 index 000000000..e4e7a45f0 --- /dev/null +++ b/src/drv/ansi/drvVipc610.c @@ -0,0 +1,275 @@ +/******************************************************************************* + +Project: + Gemini/UKIRT CAN Bus Driver for EPICS + +File: + drvVipc610.c + +Description: + IPAC Carrier Driver for the GreenSpring VIPC610-01 Quad IndustryPack + Carrier VME board, provides the interface between IPAC driver and the + hardware. This carrier is 6U high, but cannot support 32-bit accesses + to dual-slot IP modules. Note the -01 option fixes the IRQ levels to + be equivalent to two VIPC310 carriers. + +Author: + Andrew Johnson +Created: + 19 July 1995 + +(c) 1995 Royal Greenwich Observatory + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include "drvIpac.h" + + +/* Characteristics of the card */ + +#define SLOTS 4 +#define IO_SPACES 2 /* Address spaces in A16 */ +#define IPAC_IRQS 2 /* Interrupts per module */ + + +/* Offsets from base address in VME A16 */ + +#define REGS_A 0x0000 +#define PROM_A 0x0080 +#define REGS_B 0x0100 +#define PROM_B 0x0180 +#define REGS_C 0x0200 +#define PROM_C 0x0280 +#define REGS_D 0x0300 +#define PROM_D 0x0380 + + +/* VME Interrupt levels for -01 option */ + +#define IRQ_A0 4 +#define IRQ_A1 5 +#define IRQ_B0 2 +#define IRQ_B1 1 +#define IRQ_C0 4 +#define IRQ_C1 5 +#define IRQ_D0 2 +#define IRQ_D1 1 + + +/* Carrier Private structure type, one instance per board */ + +typedef void* private_t[IPAC_ADDR_SPACES][SLOTS]; + + +/******************************************************************************* + +Routine: + initialise + +Purpose: + Creates new private table for VIPC610 at addresses given by cardParams + +Description: + Checks the parameter string for the address of the card I/O space and + optional size of the memory space for the modules. If both the I/O and + memory base addresses can be reached from the CPU, a private table is + created for this board. The private table is a 2-D array of pointers + to the base addresses of the various accessible parts of the IP module. + +Parameters: + The parameter string should comprise a hex number (the 0x or 0X at the + start is optional) optionally followed by a comma and a decimal integer. + The first number is the I/O base address of the card in the VME A16 + address space (the factory default is 0x6000). If present the second + number gives the memory space in Kbytes allocated to each IP module. + The memory base address of the VIPC610 card is set using the same jumpers + as the I/O base address and is always 256 times the I/O base address, + but in the VME A24 address space. The factory default for the memory + base address is thus 0x600000. If the memory size parameter is omitted + or set to zero then none of the IP modules on the carrier provide any + memory space. Legal memory size values are 0, 64?, 128, 256, 512, 1024 + or 2048. The memory size interacts with the memory base address such + that it is possible to exclude memory from the lower slots while still + providing access to memory in the later slots by adjusting the base + address suitably. + +Examples: + "0x6000" + This indicates that the carrier board has its I/O base set to + 0x6000, and none of the slots provide memory space. + "1000,128" + Here the I/O base is set to 0x1000, and there is 128Kbytes of + memory on each module, with the IP module A memory at 0x100000, + module B at 0x120000, module C at 0x140000 and D at 0x160000. + "7000,1024" + The I/O base is at 0x7000, and hence the carrier memory base is + 0x700000. However because the memory size is set to 1024 Kbytes, + modules A, B and C cannot be selected (1024 K = 0x100000, so they + are decoded at 0x400000, 0x500000 and 0x600000 but can't be accessed + because these are below the base address). + +Returns: + 0 = OK, + S_IPAC_badAddress = Parameter string error, or address not reachable + +*/ + +LOCAL int initialise ( + char *cardParams, + void **pprivate +) { + int params, status1 = OK, status2 = OK, mSize = 0; + ulong_t ioBase, mOrig, mBase, addr; + ushort_t space, slot; + private_t *private; + static const int offset[IO_SPACES][SLOTS] = { + PROM_A, PROM_B, PROM_C, PROM_D, + REGS_A, REGS_B, REGS_C, REGS_D + }; + + if (cardParams == NULL || + strlen(cardParams) == 0) { + ioBase = 0x6000; + } else { + params = sscanf(cardParams, "%p,%i", (void **) &ioBase, &mSize); + if (params < 1 || params > 2 || + ioBase > 0xfc00 || ioBase & 0x01ff || + mSize < 0 || mSize > 2048 || mSize & 63) { + return S_IPAC_badAddress; + } + } + + mBase = ioBase << 8; /* Fixed by the VIPC610 card */ + ioBase = ioBase & 0xfc00; /* Clear A09 */ + + status1 = sysBusToLocalAdrs(VME_AM_SUP_SHORT_IO, + (char *) ioBase, (char **) &ioBase); + if (mSize > 0) { + status2 = sysBusToLocalAdrs(VME_AM_STD_SUP_DATA, + (char *) mBase, (char **) &mBase); + } + if (status1 || status2) { + return S_IPAC_badAddress; + } + + mSize = mSize << 10; /* Convert size from K to Bytes */ + mOrig = mBase & ~(mSize * SLOTS - 1); + + private = malloc(sizeof (private_t)); + for (space = 0; space < IO_SPACES; space++) { + for (slot = 0; slot < SLOTS; slot++) { + (*private)[space][slot] = (void *) (ioBase + offset[space][slot]); + } + } + + for (slot = 0; slot < SLOTS; slot++) { + (*private)[ipac_addrIO32][slot] = NULL; + addr = mOrig + (mSize * slot); + if (addr < mBase) { + (*private)[ipac_addrMem][slot] = NULL; + } else { + (*private)[ipac_addrMem][slot] = (void *) addr; + } + } + + *pprivate = private; + + return OK; +} + + +/******************************************************************************* + +Routine: + baseAddr + +Purpose: + Returns the base address for the requested slot & address space + +Description: + Because we did all that hard work in the initialise routine, this + routine only has to do a table lookup in the private array. + Note that no parameter checking is required - the IPAC driver which + calls this routine handles that. + +Returns: + The requested address, or NULL if the module has no memory. + +*/ + +LOCAL void *baseAddr ( + void *private, + ushort_t slot, + ipac_addr_t space +) { + return (*(private_t *) private)[space][slot]; +} + + +/******************************************************************************* + +Routine: + irqCmd + +Purpose: + Handles interrupter commands and status requests + +Description: + The GreenSpring board is limited to fixed interrupt levels, and has + no control over interrupts. The only commands thus supported are + a request of the interrupt level associated with a particular slot + and interrupt number, or to enable interrupts by making sure the + VMEbus interrupter is listening on the necessary level. + +Returns: + ipac_irqGetLevel returns the interrupt level (1, 2, 4 or 5), + ipac_irqEnable returns 0 = OK, + other calls return S_IPAC_notImplemented. + +*/ + +LOCAL int irqCmd ( + void *private, + ushort_t slot, + ushort_t irqNumber, + ipac_irqCmd_t cmd +) { + static const int irqLevel[SLOTS][IPAC_IRQS] = { + IRQ_A0, IRQ_A1, + IRQ_B0, IRQ_B1, + IRQ_C0, IRQ_C1, + IRQ_D0, IRQ_D1 + }; + + switch (cmd) { + case ipac_irqGetLevel: + return irqLevel[slot][irqNumber]; + + case ipac_irqEnable: + sysIntEnable(irqLevel[slot][irqNumber]); + return OK; + + default: + return S_IPAC_notImplemented; + } +} + +/******************************************************************************/ + + +/* IPAC Carrier Table */ + +ipac_carrier_t vipc610 = { + "GreenSpring VIPC610-01", + SLOTS, + initialise, + NULL, + baseAddr, + irqCmd +}; + diff --git a/src/drv/ansi/ipModules.h b/src/drv/ansi/ipModules.h new file mode 100644 index 000000000..37128f24d --- /dev/null +++ b/src/drv/ansi/ipModules.h @@ -0,0 +1,33 @@ + +#ifndef INCipModulesH +#define INCipModulesH + + +#define IP_MANUFACTURER_GREENSPRING 0xf0 + +#define IP_MODEL_GS_PRECISION_ADC 0x15 +#define IP_MODEL_GS_PRECISION_ADC_N "ADC" + +#define IP_MODEL_GS_QUAD_SERIAL 0x37 +#define IP_MODEL_GS_QUAD_SERIAL_N "Quad_Serial" + +#define IP_MODEL_GS_OCTAL_SERIAL 0x22 +#define IP_MODEL_GS_OCTAL_SERIAL_N "Octal_Serial" + +#define IP_MODEL_GS_SERIAL 0x10 +#define IP_MODEL_GS_SERIAL_N "Serial" + +#define IP_MODEL_GS_QUADRATURE 0x41 +#define IP_MODEL_GS_QUADRATURE_N "Quadrature" + +#define IP_MODEL_GS_488 0x14 +#define IP_MODEL_GS_488_N "ip488" + + +#define IP_MANUFACTURER_TEWS 0xb3 + +#define IP_MODEL_TEWS_TIP810 0x01 + + +#endif /* INCipModulesH */ + diff --git a/src/drv/ansi/ipic.h b/src/drv/ansi/ipic.h new file mode 100644 index 000000000..a7025d911 --- /dev/null +++ b/src/drv/ansi/ipic.h @@ -0,0 +1,89 @@ +/******************************************************************************* + +Project: + Gemini/UKIRT CAN Bus Driver for EPICS + +File: + ipic.h + +Description: + IndustryPack Interface Controller ASIC header file, giving the register + layout and programming model for the IPIC chip used on the MVME162. + +Author: + Andrew Johnson +Created: + 6 July 1995 + +(c) 1995 Royal Greenwich Observatory + +*******************************************************************************/ + + +#ifndef INCipicH +#define INCipicH + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Chip Registers */ + +#define IPIC_CHIP_ID 0x23 +#define IPIC_CHIP_REVISION 0x00 + + +/* Interrupt Control Register bits */ + +#define IPIC_INT_LEVEL 0x07 +#define IPIC_INT_ICLR 0x08 +#define IPIC_INT_IEN 0x10 +#define IPIC_INT_INT 0x20 +#define IPIC_INT_EDGE 0x40 +#define IPIC_INT_PLTY 0x80 + + +/* General Control Registers bits */ + +#define IPIC_GEN_MEN 0x01 +#define IPIC_GEN_WIDTH 0x0c +#define IPIC_GEN_WIDTH_8 0x04 +#define IPIC_GEN_WIDTH_16 0x08 +#define IPIC_GEN_WIDTH_32 0x00 +#define IPIC_GEN_RT 0x30 +#define IPIC_GEN_RT_0 0x00 +#define IPIC_GEN_RT_2 0x10 +#define IPIC_GEN_RT_4 0x20 +#define IPIC_GEN_RT_8 0x30 +#define IPIC_GEN_ERR 0x80 + + +/* IP Reset register bits */ + +#define IPIC_IP_RESET 0x01 + + +/* Chip Structure */ + +typedef struct { + uchar_t chipId; + uchar_t chipRevision; + uchar_t reserved1[2]; + ushort_t memBase[4]; + uchar_t memSize[4]; + uchar_t intCtrl[4][2]; + uchar_t genCtrl[4]; + uchar_t reserved2[3]; + uchar_t ipReset; +} ipic_t; + + +#ifdef __cplusplus +} +#endif + +#endif /* INCipicH */ + diff --git a/src/drv/ansi/pca82c200.h b/src/drv/ansi/pca82c200.h new file mode 100644 index 000000000..dd6b00aca --- /dev/null +++ b/src/drv/ansi/pca82c200.h @@ -0,0 +1,188 @@ +/******************************************************************************* + +Project: + Gemini/UKIRT CAN Bus Driver for EPICS + +File: + pca82c200.h + +Description: + Philips Stand-alone CAN-controller chip header file, giving the register + layout and programming model for the chip used on the TIP810 IP module. + +Author: + Andrew Johnson +Created: + 19 July 1995 + +(c) 1995 Royal Greenwich Observatory + +*******************************************************************************/ + + +#ifndef INCpca82c200H +#define INCpca82c200H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/***** Control Segment Bit Patterns *****/ + +/* Control Register */ + +#define PCA_CR_TM 0x80 /* Test Mode */ +#define PCA_CR_S 0x40 /* Synch */ +#define PCA_CR_OIE 0x10 /* Overrun Interrupt Enable */ +#define PCA_CR_EIE 0x08 /* Error Interrupt Enable */ +#define PCA_CR_TIE 0x04 /* Transmit Interrupt Enable */ +#define PCA_CR_RIE 0x02 /* Receive Interrupt Enable */ +#define PCA_CR_RR 0x01 /* Reset Request */ + + +/* Command Register */ + +#define PCA_CMR_GTS 0x10 /* Goto Sleep */ +#define PCA_CMR_COS 0x08 /* Clear Overrun Status */ +#define PCA_CMR_RRB 0x04 /* Release Receive Buffer */ +#define PCA_CMR_AT 0x02 /* Abort Transmission */ +#define PCA_CMR_TR 0x01 /* Transmission Request */ + + +/* Status Register */ + +#define PCA_SR_BS 0x80 /* Bus Status */ +#define PCA_SR_ES 0x40 /* Error Status */ +#define PCA_SR_TS 0x20 /* Transmit Status */ +#define PCA_SR_RS 0x10 /* Receive Status */ +#define PCA_SR_TCS 0x08 /* Transmission Complete Status */ +#define PCA_SR_TBS 0x04 /* Transmit Buffer Status */ +#define PCA_SR_DO 0x02 /* Data Overrun */ +#define PCA_SR_RBS 0x01 /* Receive Buffer Status */ + + +/* Interrupt Register */ + +#define PCA_IR_WUI 0x10 /* Wake-Up Interrupt */ +#define PCA_IR_OI 0x08 /* Overrun Interrupt */ +#define PCA_IR_EI 0x04 /* Error Interrupt */ +#define PCA_IR_TI 0x02 /* Transmit Interrupt */ +#define PCA_IR_RI 0x01 /* Receive Interrupt */ + + +/* Bus Timing Register 0 */ + +#define PCA_BTR0_1M6 0x00 /* 1.6 Mbits/sec, 20 m */ +#define PCA_BTR0_1M0 0x00 /* 1.0 Mbits/sec, 40 m */ +#define PCA_BTR0_500K 0x00 /* 500 Kbits/sec, 130 m */ +#define PCA_BTR0_250K 0x01 /* 250 Kbits/sec, 270 m */ +#define PCA_BTR0_125K 0x03 /* 125 Kbits/sec, 530 m */ +#define PCA_BTR0_100K 0x43 /* 100 Kbits/sec, 620 m */ +#define PCA_BTR0_50K 0x47 /* 50 Kbits/sec, 1.3 km */ +#define PCA_BTR0_20K 0x53 /* 20 Kbits/sec, 3.3 km */ +#define PCA_BTR0_10K 0x67 /* 10 Kbits/sec, 6.7 km */ +#define PCA_BTR0_5K 0x7f /* 5 Kbits/sec, 10 km */ + +#define PCA_KVASER_1M0 0x00 /* 1.0 Mbits/sec, 40 m -- Kvaser standard */ +#define PCA_KVASER_500K 0x01 /* 500 Kbits/sec, 130 m -- Kvaser standard */ +#define PCA_KVASER_250K 0x03 /* 250 Kbits/sec, 270 m -- Kvaser standard */ +#define PCA_KVASER_125K 0x07 /* 125 Kbits/sec, 530 m -- Kvaser standard */ + + +/* Bus Timing Register 1 */ + +#define PCA_BTR1_1M6 0x11 /* 1.6 Mbits/sec, 20 m */ +#define PCA_BTR1_1M0 0x14 /* 1.0 Mbits/sec, 40 m */ +#define PCA_BTR1_500K 0x1c /* 500 Kbits/sec, 130 m */ +#define PCA_BTR1_250K 0x1c /* 250 Kbits/sec, 270 m */ +#define PCA_BTR1_125K 0x1c /* 125 Kbits/sec, 530 m */ +#define PCA_BTR1_100K 0x2f /* 100 Kbits/sec, 620 m */ +#define PCA_BTR1_50K 0x2f /* 50 Kbits/sec, 1.3 km */ +#define PCA_BTR1_20K 0x2f /* 20 Kbits/sec, 3.3 km */ +#define PCA_BTR1_10K 0x2f /* 10 Kbits/sec, 6.7 km */ +#define PCA_BTR1_5K 0x7f /* 5 Kbits/sec, 10 km */ + +#define PCA_BTR1_KVASER 0x23 /* All speeds -- Kvaser standard */ + +/* Output Control Register */ + +#define PCA_OCR_OCM_NORMAL 0x02 +#define PCA_OCR_OCM_CLOCK 0x03 +#define PCA_OCR_OCM_BIPHASE 0x00 +#define PCA_OCR_OCM_TEST 0x01 + +#define PCA_OCR_OCT1_FLOAT 0x00 +#define PCA_OCR_OCT1_PULLDOWN 0x40 +#define PCA_OCR_OCT1_PULLUP 0x80 +#define PCA_OCR_OCT1_PUSHPULL 0xc0 + +#define PCA_OCR_OCT0_FLOAT 0x00 +#define PCA_OCR_OCT0_PULLDOWN 0x08 +#define PCA_OCR_OCT0_PULLUP 0x10 +#define PCA_OCR_OCT0_PUSHPULL 0x18 + +#define PCA_OCR_OCP1_INVERT 0x20 +#define PCA_OCR_OCP0_INVERT 0x04 + + +/* Message Buffers */ + +#define PCA_MSG_ID0_RSHIFT 3 +#define PCA_MSG_ID1_LSHIFT 5 +#define PCA_MSG_ID1_MASK 0xe0 +#define PCA_MSG_RTR 0x10 +#define PCA_MSG_DLC_MASK 0x0f + + +/***** Chip Structure *****/ + +/* Message Buffers */ + +typedef struct { + uchar_t pad0; + uchar_t descriptor0; + uchar_t pad1; + uchar_t descriptor1; + ushort_t data[8]; +} msgBuffer_t; + + +/* Chip Registers */ + +typedef volatile struct { + uchar_t pad00; + uchar_t control; + uchar_t pad01; + uchar_t command; + uchar_t pad02; + uchar_t status; + uchar_t pad03; + uchar_t interrupt; + uchar_t pad04; + uchar_t acceptanceCode; + uchar_t pad05; + uchar_t acceptanceMask; + uchar_t pad06; + uchar_t busTiming0; + uchar_t pad07; + uchar_t busTiming1; + uchar_t pad08; + uchar_t outputControl; + uchar_t pad09; + uchar_t test; + msgBuffer_t txBuffer; + msgBuffer_t rxBuffer; + uchar_t pad31; + uchar_t clockDivider; +} pca82c200_t; + + +#ifdef __cplusplus +} +#endif + +#endif /* INCpca82c200H */ +