New file for auxilliary I/O on XPS

This commit is contained in:
MarkRivers
2006-05-10 22:13:14 +00:00
parent 353065ccc0
commit 7529b3e776
+642
View File
@@ -0,0 +1,642 @@
/* drvXPSAsynAux.c */
/* This driver implements "auxilliary" commands for the XPS controller, i.e.
* commands beyond those for the motor record. These include support for
* analog and digital I/O. */
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <cantProceed.h> /* !! for callocMustSucceed() */
#include <epicsExport.h>
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <epicsString.h>
#include <errlog.h>
#include <iocsh.h>
#include <asynDriver.h>
#include <asynFloat64.h>
#include <asynUInt32Digital.h>
#include <asynDrvUser.h>
#include <xps_c8_driver.h>
typedef struct {
char *portName;
int socketID;
epicsMutexId lock;
epicsEventId pollerEventId;
double pollerTimeout;
asynInterface common;
asynInterface float64;
void *float64InterruptPvt;
asynInterface uint32D;
void *uint32DInterruptPvt;
asynInterface drvUser;
asynUser *pasynUser;
} drvXPSAsynAuxPvt;
typedef enum {
readAi,
readAo,
writeAo,
readBi,
readBo,
writeBo
} drvXPSAsynAuxCommand;
typedef struct {
drvXPSAsynAuxCommand command;
char *commandString;
} drvXPSAsynAuxCommandStruct;
static drvXPSAsynAuxCommandStruct drvXPSAsynAuxCommands[] = {
{readAi, "READ_AI"},
{readAo, "READ_A0"},
{writeAo, "WRITE_AO"},
{readBi, "READ_BI"},
{readBo, "READ_BO"},
{writeBo, "WRITE_BO"}
};
#define TCP_TIMEOUT 1.0
#define MAX_ANALOG_INPUTS 4
#define MAX_ANALOG_OUTPUTS 4
#define MAX_DIGITAL_INPUTS 4
#define MAX_DIGITAL_OUTPUTS 3
static char *analogInputNames[MAX_ANALOG_INPUTS] = {
"GPIO2.ADC1", /* Analog Input # 1 of the I/O board connector # 2 */
"GPIO2.ADC2", /* Analog Input # 2 of the I/O board connector # 2 */
"GPIO2.ADC3", /* Analog Input # 3 of the I/O board connector # 2 */
"GPIO2.ADC4", /* Analog Input # 4 of the I/O board connector # 2 */
};
static char *analogOutputNames[MAX_ANALOG_OUTPUTS] = {
"GPIO2.DAC1", /* Analog Output # 1 of the I/O board connector # 2 */
"GPIO2.DAC2", /* Analog Output # 2 of the I/O board connector # 2 */
"GPIO2.DAC3", /* Analog Output # 3 of the I/O board connector # 2 */
"GPIO2.DAC4", /* Analog Output # 4 of the I/O board connector # 2 */
};
static char *digitalInputNames[MAX_DIGITAL_INPUTS] = {
"GPIO1.DI", /* Digital Input of the I/O board connector # 1 (8 bits) */
"GPIO2.DI", /* Digital Input of the I/O board connector # 2 (6 bits) */
"GPIO3.DI", /* Digital Input of the I/O board connector # 3 (6 bits) */
"GPIO4.DI", /* Digital Input of the I/O board connector # 4 (16 bits) */
};
static char *digitalOutputNames[MAX_DIGITAL_OUTPUTS] = {
"GPIO1.DO", /* Digital Output of the I/O board connector # 1 (8 bits) */
"GPIO3.DO", /* Digital Output of the I/O board connector # 3 (6 bits) */
"GPIO4.DO", /* Digital Output of the I/O board connector # 4 (16 bits) */
};
/* These functions are used by the interfaces */
static asynStatus readFloat64 (void *drvPvt, asynUser *pasynUser,
epicsFloat64 *value);
static asynStatus writeFloat64 (void *drvPvt, asynUser *pasynUser,
epicsFloat64 value);
static asynStatus readUInt32D (void *drvPvt, asynUser *pasynUser,
epicsUInt32 *value, epicsUInt32 mask);
static asynStatus writeUInt32D (void *drvPvt, asynUser *pasynUser,
epicsUInt32 value, epicsUInt32 mask);
static asynStatus drvUserCreate (void *drvPvt, asynUser *pasynUser,
const char *drvInfo,
const char **pptypeName, size_t *psize);
static asynStatus drvUserGetType (void *drvPvt, asynUser *pasynUser,
const char **pptypeName, size_t *psize);
static asynStatus drvUserDestroy (void *drvPvt, asynUser *pasynUser);
static void report (void *drvPvt, FILE *fp, int details);
static asynStatus connect (void *drvPvt, asynUser *pasynUser);
static asynStatus disconnect (void *drvPvt, asynUser *pasynUser);
static asynCommon drvXPSAsynAuxCommon = {
report,
connect,
disconnect
};
static asynFloat64 drvXPSAsynAuxFloat64 = {
writeFloat64,
readFloat64
};
static asynUInt32Digital drvXPSAsynAuxUInt32D = {
writeUInt32D,
readUInt32D,
NULL,
NULL,
NULL,
NULL,
NULL
};
static asynDrvUser drvXPSAsynAuxDrvUser = {
drvUserCreate,
drvUserGetType,
drvUserDestroy
};
static void XPSAuxPoller(drvXPSAsynAuxPvt *pPvt);
int XPSAuxConfig(const char *portName, /* asyn port name */
const char *ip, /* XPS IP address or IP name */
int port, /* IP port number that XPS is listening on */
int pollPeriod) /* Time to poll (msec) analog and digital inputs */
{
drvXPSAsynAuxPvt *pPvt;
asynStatus status;
epicsThreadId threadId;
pPvt = callocMustSucceed(1, sizeof(*pPvt), "XPSAuxConfig");
pPvt->portName = epicsStrDup(portName);
pPvt->lock = epicsMutexCreate();
pPvt->pollerEventId = epicsEventCreate(epicsEventEmpty);
pPvt->socketID = TCP_ConnectToServer((char *)ip, port, TCP_TIMEOUT);
if (pPvt->socketID < 0) {
printf("drvXPSAsynAuxConfig: error calling TCP_ConnectToServer\n");
return -1;
}
pPvt->pollerTimeout = pollPeriod/1000.;
/* Link with higher level routines */
pPvt->common.interfaceType = asynCommonType;
pPvt->common.pinterface = (void *)&drvXPSAsynAuxCommon;
pPvt->common.drvPvt = pPvt;
pPvt->float64.interfaceType = asynFloat64Type;
pPvt->float64.pinterface = (void *)&drvXPSAsynAuxFloat64;
pPvt->float64.drvPvt = pPvt;
pPvt->uint32D.interfaceType = asynUInt32DigitalType;
pPvt->uint32D.pinterface = (void *)&drvXPSAsynAuxUInt32D;
pPvt->uint32D.drvPvt = pPvt;
pPvt->drvUser.interfaceType = asynDrvUserType;
pPvt->drvUser.pinterface = (void *)&drvXPSAsynAuxDrvUser;
pPvt->drvUser.drvPvt = pPvt;
status = pasynManager->registerPort(portName,
ASYN_MULTIDEVICE, /*is multiDevice*/
1, /* autoconnect */
0, /* medium priority */
0); /* default stack size */
if (status != asynSuccess) {
errlogPrintf("XPSAuxConfig ERROR: Can't register port\n");
return -1;
}
status = pasynManager->registerInterface(portName,&pPvt->common);
if (status != asynSuccess) {
errlogPrintf("XPSAuxConfig ERROR: Can't register common.\n");
return -1;
}
status = pasynFloat64Base->initialize(pPvt->portName,&pPvt->float64);
if (status != asynSuccess) {
errlogPrintf("XPSAuxConfig ERROR: Can't register float64\n");
return -1;
}
status = pasynManager->registerInterruptSource(pPvt->portName, &pPvt->float64,
&pPvt->float64InterruptPvt);
if (status != asynSuccess) {
errlogPrintf("XPSAuxConfig ERROR: Can't register float64 interrupts\n");
return -1;
}
status = pasynUInt32DigitalBase->initialize(pPvt->portName,&pPvt->uint32D);
if (status != asynSuccess) {
errlogPrintf("XPSAuxConfig ERROR: Can't register uint32D\n");
return -1;
}
status = pasynManager->registerInterruptSource(pPvt->portName, &pPvt->uint32D,
&pPvt->uint32DInterruptPvt);
if (status != asynSuccess) {
errlogPrintf("XPSAuxConfig ERROR: Can't register uint32D interrupts\n");
return -1;
}
status = pasynManager->registerInterface(portName,&pPvt->drvUser);
if (status != asynSuccess) {
errlogPrintf("XPSAuxConfig ERROR: Can't register drvUser.\n");
return -1;
}
/* Create asynUser for debugging */
pPvt->pasynUser = pasynManager->createAsynUser(0, 0);
/* Connect to device */
status = pasynManager->connectDevice(pPvt->pasynUser, portName, -1);
if (status != asynSuccess) {
errlogPrintf("XPSAuxConfig, connectDevice failed\n");
return -1;
}
threadId = epicsThreadCreate("XPSAuxPoller", epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
(EPICSTHREADFUNC)XPSAuxPoller,
pPvt);
if (threadId == NULL) {
errlogPrintf("XPSAuxConfig, epicsThreadCreate failed\n");
return(-1);
}
return 0;
}
static asynStatus readFloat64(void *drvPvt, asynUser *pasynUser,
epicsFloat64 *value)
{
drvXPSAsynAuxPvt *pPvt = (drvXPSAsynAuxPvt *)drvPvt;
int channel;
drvXPSAsynAuxCommand command = pasynUser->reason;
char *GPIOName;
int status;
pasynManager->getAddr(pasynUser, &channel);
switch(command) {
case readAi:
if ((channel < 0) || (channel >= MAX_ANALOG_INPUTS)) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::readFloat64 channel out of range=%d",
channel);
return(asynError);
}
GPIOName = analogInputNames[channel];
break;
case readAo:
if ((channel < 0) || (channel >= MAX_ANALOG_OUTPUTS)) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::readFloat64 channel out of range=%d",
channel);
return(asynError);
}
GPIOName = analogOutputNames[channel];
break;
default:
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::readFloat64 invalid command=%d",
command);
return(asynError);
}
status = GPIOAnalogGet(pPvt->socketID, 1, GPIOName, value);
if (status) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::readFloat64 error calling GPIOAnalogGet=%d",
status);
return(asynError);
}
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
"drvXPSAsynAux::readFloat64, value=%f\n", *value);
return(asynSuccess);
}
static asynStatus writeFloat64(void *drvPvt, asynUser *pasynUser,
epicsFloat64 value)
{
drvXPSAsynAuxPvt *pPvt = (drvXPSAsynAuxPvt *)drvPvt;
int channel;
drvXPSAsynAuxCommand command = pasynUser->reason;
char *GPIOName;
int status;
pasynManager->getAddr(pasynUser, &channel);
switch(command) {
case writeAo:
if ((channel < 0) || (channel >= MAX_ANALOG_OUTPUTS)) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::writeFloat64 channel out of range=%d",
channel);
return(asynError);
}
GPIOName = analogOutputNames[channel];
break;
default:
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::writeFloat64 invalid command=%d",
command);
return(asynError);
}
status = GPIOAnalogSet(pPvt->socketID, 1, GPIOName, &value);
if (status) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::writeFloat64 error calling GPIOAnalogSet=%d",
status);
return(asynError);
}
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
"drvXPSAsynAux::writeFloat64, value=%f\n", value);
return(asynSuccess);
}
static asynStatus readUInt32D(void *drvPvt, asynUser *pasynUser,
epicsUInt32 *value, epicsUInt32 mask)
{
drvXPSAsynAuxPvt *pPvt = (drvXPSAsynAuxPvt *)drvPvt;
int channel;
drvXPSAsynAuxCommand command = pasynUser->reason;
char *GPIOName;
int status;
unsigned short rawValue;
pasynManager->getAddr(pasynUser, &channel);
switch(command) {
case readBi:
if ((channel < 0) || (channel >= MAX_DIGITAL_INPUTS)) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::readUInt32D readBi channel out of range=%d",
channel);
return(asynError);
}
GPIOName = digitalInputNames[channel];
break;
case readBo:
if ((channel < 0) || (channel >= MAX_DIGITAL_OUTPUTS)) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::readUInt32D readBo channel out of range=%d",
channel);
return(asynError);
}
GPIOName = digitalOutputNames[channel];
break;
default:
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::readUInt32D invalid command=%d",
command);
return(asynError);
}
status = GPIODigitalGet(pPvt->socketID, GPIOName, &rawValue);
if (status) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::readUInt32D error calling GPIODigitalGet=%d",
status);
return(asynError);
}
*value = rawValue & mask;
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
"drvXPSAsynAux::readUInt32D, value=%x\n", *value);
return(asynSuccess);
}
static asynStatus writeUInt32D(void *drvPvt, asynUser *pasynUser,
epicsUInt32 value, epicsUInt32 mask)
{
drvXPSAsynAuxPvt *pPvt = (drvXPSAsynAuxPvt *)drvPvt;
int channel;
drvXPSAsynAuxCommand command = pasynUser->reason;
char *GPIOName;
int status;
pasynManager->getAddr(pasynUser, &channel);
switch(command) {
case writeBo:
if ((channel < 0) || (channel >= MAX_DIGITAL_OUTPUTS)) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::writeUInt32D channel out of range=%d",
channel);
return(asynError);
}
GPIOName = digitalOutputNames[channel];
break;
default:
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::writeUInt32D invalid command=%d",
command);
return(asynError);
}
status = GPIODigitalSet(pPvt->socketID, GPIOName, mask, value);
if (status) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::writeUInt32D error calling GPIODigitalSet=%d",
status);
return(asynError);
}
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
"drvXPSAsynAux::writeUInt32D, value=%x\n", value);
return(asynSuccess);
}
static void XPSAuxPoller(drvXPSAsynAuxPvt *pPvt)
{
char analogNames[100] = "";
double analogValues[MAX_ANALOG_INPUTS];
unsigned short digitalValues[MAX_DIGITAL_INPUTS];
unsigned short digitalValuesPrev[MAX_DIGITAL_INPUTS];
ELLLIST *pclientList;
interruptNode *pnode;
asynUInt32DigitalInterrupt *pUInt32DigitalInterrupt;
asynFloat64Interrupt *pfloat64Interrupt;
int firstTime = 1;
int i;
int status;
asynUser *pasynUser;
int addr, reason, mask, changedBits;
/* Build strings with the names of the analog and digital inputs */
for (i=0; i<MAX_ANALOG_INPUTS; i++) {
strcat(analogNames, analogInputNames[i]);
strcat(analogNames, ";");
}
while(1) {
status = epicsEventWaitWithTimeout(pPvt->pollerEventId, pPvt->pollerTimeout);
status = GPIOAnalogGet(pPvt->socketID, MAX_ANALOG_INPUTS, analogNames, analogValues);
if (status) {
asynPrint(pPvt->pasynUser, ASYN_TRACE_ERROR,
"drvXPSAsynAux::XPSAuxPoller error calling GPIOAnalogGet=%d\n", status);
}
for (i=0; i<MAX_DIGITAL_INPUTS; i++) {
status = GPIODigitalGet(pPvt->socketID, digitalInputNames[i], &digitalValues[i]);
if (status) {
asynPrint(pPvt->pasynUser, ASYN_TRACE_ERROR,
"drvXPSAsynAux::XPSAuxPoller error calling GPIODigitalGet=%d\n", status);
}
}
/* Call back any clients who have registered for callbacks on changed digital bits */
pasynManager->interruptStart(pPvt->uint32DInterruptPvt, &pclientList);
pnode = (interruptNode *)ellFirst(pclientList);
while (pnode) {
pUInt32DigitalInterrupt = pnode->drvPvt;
pasynUser = pUInt32DigitalInterrupt->pasynUser;
pasynManager->getAddr(pasynUser, &addr);
reason = pasynUser->reason;
mask = pUInt32DigitalInterrupt->mask;
changedBits = digitalValues[addr] ^ digitalValuesPrev[addr];
if (firstTime) changedBits = 0xffff;
if ((mask & changedBits) && (reason == readBi)) {
pUInt32DigitalInterrupt->callback(pUInt32DigitalInterrupt->userPvt, pasynUser,
mask & digitalValues[addr]);
}
pnode = (interruptNode *)ellNext(&pnode->node);
}
pasynManager->interruptEnd(pPvt->uint32DInterruptPvt);
for (i=0; i<MAX_DIGITAL_INPUTS; i++) {
digitalValuesPrev[i] = digitalValues[i];
}
/* Pass float64 interrupts for analog inputs*/
pasynManager->interruptStart(pPvt->float64InterruptPvt, &pclientList);
pnode = (interruptNode *)ellFirst(pclientList);
while (pnode) {
pfloat64Interrupt = pnode->drvPvt;
addr = pfloat64Interrupt->addr;
reason = pfloat64Interrupt->pasynUser->reason;
if (reason == readAi) {
pfloat64Interrupt->callback(pfloat64Interrupt->userPvt,
pfloat64Interrupt->pasynUser,
analogValues[addr]);
}
pnode = (interruptNode *)ellNext(&pnode->node);
}
pasynManager->interruptEnd(pPvt->float64InterruptPvt);
firstTime = 0;
}
}
/* asynDrvUser routines */
static asynStatus drvUserCreate(void *drvPvt, asynUser *pasynUser,
const char *drvInfo,
const char **pptypeName, size_t *psize)
{
int i;
char *pstring;
int ncommands = sizeof(drvXPSAsynAuxCommands)/sizeof(drvXPSAsynAuxCommands[0]);
asynPrint(pasynUser, ASYN_TRACE_FLOW,
"drvXPSAsynAux::drvUserCreate, drvInfo=%s, pptypeName=%p, psize=%p, pasynUser=%p\n",
drvInfo, pptypeName, psize, pasynUser);
for (i=0; i < ncommands; i++) {
pstring = drvXPSAsynAuxCommands[i].commandString;
if (epicsStrCaseCmp(drvInfo, pstring) == 0) {
break;
}
}
if (i < ncommands) {
pasynUser->reason = drvXPSAsynAuxCommands[i].command;
if (pptypeName) {
*pptypeName = epicsStrDup(pstring);
}
if (psize) {
*psize = sizeof(drvXPSAsynAuxCommands[i].command);
}
asynPrint(pasynUser, ASYN_TRACE_FLOW,
"drvXPSAsynAux::drvUserCreate, command=%d string=%s\n",
pasynUser->reason, pstring);
return(asynSuccess);
} else {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"drvXPSAsynAux::drvUserCreate, unknown command=%s", drvInfo);
return(asynError);
}
}
static asynStatus drvUserGetType(void *drvPvt, asynUser *pasynUser,
const char **pptypeName, size_t *psize)
{
drvXPSAsynAuxCommand command = pasynUser->reason;
asynPrint(pasynUser, ASYN_TRACE_FLOW,
"drvXPSAsynAux::drvUserGetType entered");
*pptypeName = NULL;
*psize = 0;
if (pptypeName)
*pptypeName = epicsStrDup(drvXPSAsynAuxCommands[command].commandString);
if (psize) *psize = sizeof(command);
return(asynSuccess);
}
static asynStatus drvUserDestroy(void *drvPvt, asynUser *pasynUser)
{
asynPrint(pasynUser, ASYN_TRACE_FLOW,
"drvXPSAsynAux::drvUserDestroy, drvPvt=%p, pasynUser=%p\n",
drvPvt, pasynUser);
return(asynSuccess);
}
/* asynCommon routines */
/* Report parameters */
static void report(void *drvPvt, FILE *fp, int details)
{
drvXPSAsynAuxPvt *pPvt = (drvXPSAsynAuxPvt *)drvPvt;
interruptNode *pnode;
ELLLIST *pclientList;
fprintf(fp, "Port: %s\n", pPvt->portName);
if (details >= 1) {
/* Report uint32D interrupts */
pasynManager->interruptStart(pPvt->uint32DInterruptPvt, &pclientList);
pnode = (interruptNode *)ellFirst(pclientList);
while (pnode) {
asynUInt32DigitalInterrupt *puint32DInterrupt = pnode->drvPvt;
fprintf(fp, " int32 callback client address=%p, addr=%d, reason=%d\n",
puint32DInterrupt->callback, puint32DInterrupt->addr,
puint32DInterrupt->pasynUser->reason);
pnode = (interruptNode *)ellNext(&pnode->node);
}
pasynManager->interruptEnd(pPvt->uint32DInterruptPvt);
/* Report float64 interrupts */
pasynManager->interruptStart(pPvt->float64InterruptPvt, &pclientList);
pnode = (interruptNode *)ellFirst(pclientList);
while (pnode) {
asynFloat64Interrupt *pfloat64Interrupt = pnode->drvPvt;
fprintf(fp, " float64 callback client address=%p, addr=%d, reason=%d\n",
pfloat64Interrupt->callback, pfloat64Interrupt->addr,
pfloat64Interrupt->pasynUser->reason);
pnode = (interruptNode *)ellNext(&pnode->node);
}
pasynManager->interruptEnd(pPvt->float64InterruptPvt);
}
}
/* Connect */
static asynStatus connect(void *drvPvt, asynUser *pasynUser)
{
pasynManager->exceptionConnect(pasynUser);
asynPrint(pasynUser, ASYN_TRACE_FLOW,
"drvXPSAsynAux::connect, pasynUser=%p\n", pasynUser);
return(asynSuccess);
}
/* Disconnect */
static asynStatus disconnect(void *drvPvt, asynUser *pasynUser)
{
pasynManager->exceptionDisconnect(pasynUser);
return(asynSuccess);
}
static const iocshArg configArg0 = { "portName",iocshArgString};
static const iocshArg configArg1 = { "IP address",iocshArgString};
static const iocshArg configArg2 = { "IP port",iocshArgInt};
static const iocshArg configArg3 = { "polling period",iocshArgInt};
static const iocshArg * const configArgs[4] = {&configArg0,
&configArg1,
&configArg2,
&configArg3};
static const iocshFuncDef configFuncDef = {"XPSAuxConfig",4,configArgs};
static void configCallFunc(const iocshArgBuf *args)
{
XPSAuxConfig(args[0].sval, args[1].sval, args[2].ival, args[3].ival);
}
void drvXPSAsynAuxRegister(void)
{
iocshRegister(&configFuncDef,configCallFunc);
}
epicsExportRegistrar(drvXPSAsynAuxRegister);