IOCINFRAApp/src/drvAsynKeithley648x.cpp
changeset 0 77d8eafe2a07
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IOCINFRAApp/src/drvAsynKeithley648x.cpp	Thu Mar 07 09:23:26 2019 +0100
@@ -0,0 +1,1339 @@
+/*
+ Description
+    This module provides support for a multiple device port driver. To
+    initialize the driver, the method drvAsynKeithley6485() is called from the
+    startup script with the following calling sequence.
+
+        drvAsynKeithley6485(myport,ioport,ioaddr)
+
+        Where:
+            myport - Keithley6485 Asyn interface port driver name (i.e. "EP0" )
+            ioport - Communication port driver name (i.e. "S0" )
+            ioaddr - Communication port device addr
+
+    The method dbior can be called from the IOC shell to display the current
+    status of the driver.
+*/
+
+
+/* System related include files */
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* EPICS system related include files */
+#include <iocsh.h>
+#include <epicsStdio.h>
+#include <cantProceed.h>
+#include <epicsString.h>
+#include <epicsExport.h>
+#include <errlog.h>
+
+/* EPICS synApps/Asyn related include files */
+#include <asynDriver.h>
+#include <asynDrvUser.h>
+#include <asynInt32.h>
+#include <asynFloat64.h>
+#include <asynOctet.h>
+#include <asynOctetSyncIO.h>
+#include <asynStandardInterfaces.h>
+
+/* Define symbolic constants */
+#define TIMEOUT         (5.0)
+#define BUFFER_SIZE     (100)
+
+
+typedef enum {Octet=1, Float64=2, Int32=3} Type;
+
+static const char *driver = "drvAsynKeithley648x";      /* String for asynPrint */
+
+
+/* Declare port driver structure */
+struct Port
+{
+  int devtype; // 1:6485, 2:6487
+  char* myport;
+  char* ioport;
+  int ioaddr;
+
+  int init; // really needed??
+
+  char model[BUFFER_SIZE+1], *serial, *dig_rev, *disp_rev, *brd_rev;
+
+  struct
+  {
+    int ioErrors;
+    int writeReads;
+    int writeOnlys;
+  } stats;
+
+  struct
+  {
+    double reading;
+    int timestamp;
+    union 
+    {
+      int raw;
+      struct 
+      {
+        unsigned int overflow             : 1;
+        unsigned int filter_enabled       : 1;
+        unsigned int math_enabled         : 1;
+        unsigned int null_enabled         : 1;
+        unsigned int limit_test           : 1;
+        unsigned int limit_result         : 2;
+        unsigned int overvoltage          : 1;
+        unsigned int padding              : 1;
+        unsigned int zero_check_enabled   : 1;
+        unsigned int zero_correct_enabled : 1;
+      } bits;
+    } status;
+    /* 
+       Bits:
+       0 (OFLO)   — Set to 1 if measurement performed while in over-range 
+                    (overflowed reading).
+       1 (Filter) — Set to 1 when measurement performed with the averaging 
+                    filter enabled.
+       2 (Math)   — Set to 1 when measurement performed with CALC1 enabled.
+       3 (Null)   — Set to 1 if null for CALC2 is enabled.
+       4 (Limits) — Set to 1 if a limit test (CALC2) is enabled.
+       5 & 6 (Limit Results) — Provides limit test results:
+                Bit 6    Bit 5   
+                  0        0      All limit tests passed
+                  0        1      CALC2:LIM1 test failed
+                  1        0      CALC2:LIM2 test failed
+       7 (Overvoltage)  — Set to 1 if measurement performed with an 
+                          overvoltage condition on the input.
+       9 (Zero Check)   — Set to 1 when zero check is enabled.
+      10 (Zero Correct) — Set to 1 when zero correct is enabled.
+    */
+    int eom;
+  } data;
+
+  /* Asyn info */
+  asynUser *pasynUser;
+  asynUser *pasynUserTrace;  /* asynUser for asynTrace on this port */
+  asynStandardInterfaces asynStdInterfaces;
+};
+
+
+struct Command
+{
+  const char *tag;
+  int dev;
+  int type;
+  int id;
+};
+
+
+/* Declare command structure */
+struct GenCommand
+{
+  asynStatus (*readFunc)(int which, Port *pport, void* data, Type Iface, 
+                         size_t *length, int *eom);
+  asynStatus (*writeFunc)(int which, Port *pport, void* data, Type Iface);
+};
+
+struct SimpleCommand
+{
+  int type;
+  const char *cmd_str;
+};
+
+/* Public interface forward references */
+int drvAsynKeithley648x(const char* myport,const char* ioport, int ioaddr);
+
+
+/* Forward references for asynCommon methods */
+static void report(void* ppvt,FILE* fp,int details);
+static asynStatus connect(void* ppvt,asynUser* pasynUser);
+static asynStatus disconnect(void* ppvt,asynUser* pasynUser);
+static asynCommon ifaceCommon = {report,connect,disconnect};
+
+/* Forward references for asynDrvUser methods */
+static asynStatus create(void* ppvt,asynUser* pasynUser,const char* drvInfo,
+                         const char** pptypeName,size_t* psize);
+static asynStatus destroy(void* ppvt,asynUser* pasynUser);
+static asynStatus gettype(void* ppvt,asynUser* pasynUser,
+                          const char** pptypeName,size_t* psize);
+static asynDrvUser ifaceDrvUser = {create,gettype,destroy};
+
+/* Forward references for asynFloat64 methods */
+static asynStatus readFloat64(void* ppvt,asynUser* pasynUser,
+                              epicsFloat64* value);
+static asynStatus writeFloat64(void* ppvt,asynUser* pasynUser,
+                               epicsFloat64 value);
+static asynFloat64 ifaceFloat64 = {writeFloat64, readFloat64};
+
+/* Forward references for asynInt32 methods */
+static asynStatus readInt32(void* ppvt,asynUser* pasynUser,epicsInt32* value);
+static asynStatus writeInt32(void* ppvt,asynUser* pasynUser,epicsInt32 value);
+static asynInt32 ifaceInt32 =  {writeInt32, readInt32};
+
+/* Forward references for asynOctet methods */
+static asynStatus flushOctet( void* ppvt, asynUser* pasynUser);
+static asynStatus writeOctet( void* ppvt, asynUser* pasynUser, const char *data,
+                              size_t numchars, size_t* nbytes);
+static asynStatus readOctet( void* ppvt, asynUser* pasynUser, char* data,
+                             size_t maxchars, size_t *nbytes, int *eom);
+static asynOctet ifaceOctet = { writeOctet, readOctet, flushOctet};
+
+
+/* Forward references for external asynOctet interface */
+static asynStatus writeOnly(Port* pport, const char* outBuf);
+static asynStatus writeRead(Port* pport, const char* outBuf, char* inpBuf,
+                            int inputSize, int *eomReason);
+
+
+static asynStatus readDummy(int which, Port *pport, void *data, Type Iface, 
+                            size_t *length, int *eom);
+static asynStatus writeDummy(int which, Port *pport, void* data, Type Iface);
+
+static asynStatus readSimpleData( int which, Port *pport, void *data, 
+                                  Type Iface, size_t *length, int *eom);
+static asynStatus writeSimpleData( int which, Port *pport, void *data, 
+                                   Type Iface);
+
+static asynStatus readCache(int which, Port *pport, void *data, 
+                            Type Iface, size_t *length, int *eom);
+
+static asynStatus readSensorReading(int which, Port *pport, void* data, 
+                                    Type Iface, size_t *length, int *eom);
+static asynStatus readRange(int which, Port *pport, void* data, 
+                            Type Iface, size_t *length, int *eom);
+static asynStatus writeRange(int which, Port *pport, void* data, Type Iface);
+static asynStatus readRate(int which, Port *pport, void* data, 
+                           Type Iface, size_t *length, int *eom);
+static asynStatus writeRate(int which, Port *pport, void* data, Type Iface);
+static asynStatus readVoltageSettings(int which, Port *pport, void* data, 
+                                            Type Iface, size_t *length, int *eom);
+static asynStatus writeVoltageSettings(int which, Port *pport, void* data, 
+                                              Type Iface);
+static asynStatus readCommon(int which, Port *pport, void* data, 
+                             Type Iface, size_t *length, int *eom);
+static asynStatus writeCommon(int which, Port *pport, void* data, Type Iface);
+
+
+
+
+// General commands that need special attention go here
+enum { VOID_CMD, READ_CMD, RANGE_CMD, RANGE_AUTO_ULIMIT_CMD, 
+       RANGE_AUTO_LLIMIT_CMD, RATE_CMD, DIGITAL_FILTER_CONTROL_CMD,
+       VOLTAGE_RANGE_CMD, VOLTAGE_CURRENT_LIMIT_CMD,      GEN_CMD_NUMBER };
+static GenCommand genCommandTable[GEN_CMD_NUMBER] = 
+  {
+    { readDummy,           writeDummy},     // VOID
+    { readSensorReading,   writeDummy},     // READ
+    { readRange,           writeRange},     // RANGE
+    { readRange,           writeRange},     // RANGE_AUTO_ULIMIT
+    { readRange,           writeRange},     // RANGE_AUTO_LLIMIT
+    { readRate,            writeRate},      // RATE
+    { readCommon,          writeCommon},    // DIGITAL_FILTER_CONTROL
+    { readVoltageSettings, writeVoltageSettings}, // VOLTAGE_RANGE_COMMAND
+    { readVoltageSettings, writeVoltageSettings}, // VOLTAGE_CURRENT_LIMIT_COMMAND
+  };
+
+// commands that are very simple-minded go here
+enum { RESET_CMD, RANGE_AUTO_CMD,
+       ZERO_CHECK_CMD, ZERO_CORRECT_CMD, ZERO_CORRECT_ACQUIRE_CMD, 
+       MEDIAN_FILTER_CMD, MEDIAN_FILTER_RANK_CMD, 
+       DIGITAL_FILTER_CMD, DIGITAL_FILTER_COUNT_CMD, 
+       VOLTAGE_CMD, VOLTAGE_STATE_CMD, VOLTAGE_10V_INTERLOCK_CMD, VOLTAGE_INTERLOCK_STATUS_CMD,
+       SIMPLE_CMD_NUMBER};
+
+enum { SIMPLE_TRIGGER=0, SIMPLE_OCTET=Octet, SIMPLE_FLOAT64=Float64, 
+       SIMPLE_INT32=Int32 };
+static SimpleCommand simpleCommandTable[SIMPLE_CMD_NUMBER] = 
+  {
+    { SIMPLE_TRIGGER, "*RST"},                // RESET DEVICE
+
+    { SIMPLE_INT32,   ":RANGE:AUTO"},         // RANGE_AUTO
+
+    { SIMPLE_INT32,   "SYST:ZCH"},            // ZERO CHECK
+    { SIMPLE_INT32,   "SYST:ZCOR"},           // ZERO CORRECT
+    { SIMPLE_TRIGGER, "SYST:ZCOR:ACQ"},       // ZERO CORRECT ACQUIRE
+
+    { SIMPLE_INT32,   "MED"},                 // MEDIAN FILTER
+    { SIMPLE_INT32,   "MED:RANK"},            // MEDIAN FILTER RANK
+
+    { SIMPLE_INT32,   "AVER"},                // DIGITAL FILTER
+    { SIMPLE_INT32,   "AVER:COUN"},           // DIGITAL FILTER COUNT
+
+    { SIMPLE_FLOAT64, "SOUR:VOLT"},           // VOLTAGE
+    { SIMPLE_INT32,   "SOUR:VOLT:STAT"},      // VOLTAGE STATE
+    { SIMPLE_INT32,   "SOUR:VOLT:INT"},       // VOLTAGE 10V INTERLOCK
+    { SIMPLE_INT32,   "SOUR:VOLT:INT:FAIL"},  // VOLTAGE INTERLOCK STATUS
+
+    // { SIMPLE_INT32,   "AVER:"},      // 
+    // { SIMPLE_INT32,   "AVER:"},      // 
+  };
+
+
+enum { TIMESTAMP_CMD, STATUS_RAW_CMD, STATUS_OVERFLOW_CMD, STATUS_FILTER_CMD, 
+       STATUS_MATH_CMD, STATUS_NULL_CMD, STATUS_LIMITS_CMD, 
+       STATUS_OVERVOLTAGE_CMD, STATUS_ZERO_CHECK_CMD, STATUS_ZERO_CORRECT_CMD,
+       MODEL_CMD, SERIAL_CMD, DIG_REV_CMD, DISP_REV_CMD, BRD_REV_CMD, 
+       CACHE_CMD_NUMBER };
+
+#define COMMAND_NUMBER (GEN_CMD_NUMBER + SIMPLE_CMD_NUMBER + CACHE_CMD_NUMBER)
+
+enum { CMD_GEN, CMD_SIMPLE, CMD_CACHE };
+enum { DEV_ALL, DEV_6485, DEV_6487};
+static Command commandTable[ COMMAND_NUMBER ] = 
+  {
+    { "VOID",                     DEV_ALL,  CMD_GEN,    VOID_CMD                     },
+    { "READ",                     DEV_ALL,  CMD_GEN,    READ_CMD                     },
+    { "RANGE",                    DEV_ALL,  CMD_GEN,    RANGE_CMD                    },
+    { "RANGE_AUTO_ULIMIT",        DEV_ALL,  CMD_GEN,    RANGE_AUTO_ULIMIT_CMD        },
+    { "RANGE_AUTO_LLIMIT",        DEV_ALL,  CMD_GEN,    RANGE_AUTO_LLIMIT_CMD        },
+    { "RATE",                     DEV_ALL,  CMD_GEN,    RATE_CMD                     },
+    { "DIGITAL_FILTER_CONTROL",   DEV_ALL,  CMD_GEN,    DIGITAL_FILTER_CONTROL_CMD   },
+    { "VOLTAGE_RANGE",            DEV_6487, CMD_GEN,    VOLTAGE_RANGE_CMD            },
+    { "VOLTAGE_CURRENT_LIMIT",    DEV_6487, CMD_GEN,    VOLTAGE_CURRENT_LIMIT_CMD    },
+    { "RESET",                    DEV_ALL,  CMD_SIMPLE, RESET_CMD                    },
+    { "RANGE_AUTO",               DEV_ALL,  CMD_SIMPLE, RANGE_AUTO_CMD               },
+    { "ZERO_CHECK",               DEV_ALL,  CMD_SIMPLE, ZERO_CHECK_CMD               },
+    { "ZERO_CORRECT",             DEV_ALL,  CMD_SIMPLE, ZERO_CORRECT_CMD             },
+    { "ZERO_CORRECT_ACQUIRE",     DEV_ALL,  CMD_SIMPLE, ZERO_CORRECT_ACQUIRE_CMD     },
+    { "MEDIAN_FILTER",            DEV_ALL,  CMD_SIMPLE, MEDIAN_FILTER_CMD            },
+    { "MEDIAN_FILTER_RANK",       DEV_ALL,  CMD_SIMPLE, MEDIAN_FILTER_RANK_CMD       },
+    { "DIGITAL_FILTER",           DEV_ALL,  CMD_SIMPLE, DIGITAL_FILTER_CMD           },
+    { "DIGITAL_FILTER_COUNT",     DEV_ALL,  CMD_SIMPLE, DIGITAL_FILTER_COUNT_CMD     },
+    { "VOLTAGE",                  DEV_6487, CMD_SIMPLE, VOLTAGE_CMD                  },
+    { "VOLTAGE_STATE",            DEV_6487, CMD_SIMPLE, VOLTAGE_STATE_CMD            },
+    { "VOLTAGE_TENV_INTERLOCK",   DEV_6487, CMD_SIMPLE, VOLTAGE_10V_INTERLOCK_CMD    },
+    { "VOLTAGE_INTERLOCK_STATUS", DEV_6487, CMD_SIMPLE, VOLTAGE_INTERLOCK_STATUS_CMD },
+    { "MODEL",                    DEV_ALL,  CMD_CACHE,  MODEL_CMD                    },
+    { "SERIAL",                   DEV_ALL,  CMD_CACHE,  SERIAL_CMD                   },
+    { "DIG_REV",                  DEV_ALL,  CMD_CACHE,  DIG_REV_CMD                  },
+    { "DISP_REV",                 DEV_ALL,  CMD_CACHE,  DISP_REV_CMD                 },
+    { "BRD_REV",                  DEV_ALL,  CMD_CACHE,  BRD_REV_CMD                  },
+    { "TIMESTAMP",                DEV_ALL,  CMD_CACHE,  TIMESTAMP_CMD                },
+    { "STATUS_RAW",               DEV_ALL,  CMD_CACHE,  STATUS_RAW_CMD               },
+    { "STATUS_OVERFLOW",          DEV_ALL,  CMD_CACHE,  STATUS_OVERFLOW_CMD          },
+    { "STATUS_FILTER",            DEV_ALL,  CMD_CACHE,  STATUS_FILTER_CMD            },
+    { "STATUS_MATH",              DEV_ALL,  CMD_CACHE,  STATUS_MATH_CMD              },
+    { "STATUS_NULL",              DEV_ALL,  CMD_CACHE,  STATUS_NULL_CMD              },
+    { "STATUS_LIMITS",            DEV_ALL,  CMD_CACHE,  STATUS_LIMITS_CMD            },
+    { "STATUS_OVERVOLTAGE",       DEV_ALL,  CMD_CACHE,  STATUS_OVERVOLTAGE_CMD       },
+    { "STATUS_ZERO_CHECK",        DEV_ALL,  CMD_CACHE,  STATUS_ZERO_CHECK_CMD        },
+    { "STATUS_ZERO_CORRECT",      DEV_ALL,  CMD_CACHE,  STATUS_ZERO_CORRECT_CMD      },
+  };
+
+
+
+/****************************************************************************
+ * Define public interface methods
+ ****************************************************************************/
+int drvAsynKeithley648x(const char *type, const char *myport,
+                        const char *ioport, int ioaddr)
+{
+  int status = asynSuccess;
+  Port* pport;
+  //  int i;
+  asynStandardInterfaces *pInterfaces;
+
+  char inpBuf[BUFFER_SIZE];
+  int eomReason;
+
+  pport = (Port*)callocMustSucceed(1,sizeof(Port),"drvAsynKeithley6485");
+  pport->myport = epicsStrDup(myport);
+  pport->ioport = epicsStrDup(ioport);
+  pport->ioaddr = ioaddr;
+
+  pport->devtype = DEV_ALL;
+  if( !strcmp("6485", type))
+    pport->devtype = DEV_6485;
+  if( !strcmp("6487", type))
+    pport->devtype = DEV_6487;
+  if(pport->devtype == DEV_ALL) // DEV_ALL is not a real device
+    {
+      errlogPrintf("%s::drvAsynKeithley6485 type has to be "
+                   "either \'6485\' or \'6487\'.\n", driver);
+      return asynError;
+    }
+
+
+  status = pasynOctetSyncIO->connect(ioport,ioaddr,&pport->pasynUser,NULL);
+  if (status != asynSuccess)
+    {
+      errlogPrintf("%s::drvAsynKeithley6485 port %s can't connect "
+                   "to asynCommon on Octet server %s address %d.\n",
+                   driver, myport, ioport, ioaddr);
+      return asynError;
+    }
+
+  /* Create asynUser for asynTrace */
+  pport->pasynUserTrace = pasynManager->createAsynUser(0, 0);
+  pport->pasynUserTrace->userPvt = pport;
+
+  status = pasynManager->registerPort(myport,ASYN_CANBLOCK,1,0,0);
+  if( status != asynSuccess) 
+    {
+      errlogPrintf("%s::drvAsynKeithley6485 port %s can't register port\n",
+                   driver, myport);
+      return asynError;
+    }
+
+  pInterfaces = &pport->asynStdInterfaces;
+    
+  /* Initialize interface pointers */
+  pInterfaces->common.pinterface    = (void *)&ifaceCommon;
+  pInterfaces->drvUser.pinterface   = (void *)&ifaceDrvUser;
+  pInterfaces->octet.pinterface     = (void *)&ifaceOctet;
+  pInterfaces->int32.pinterface     = (void *)&ifaceInt32;
+  pInterfaces->float64.pinterface   = (void *)&ifaceFloat64;
+
+  status = pasynStandardInterfacesBase->initialize(myport, pInterfaces,
+                                                   pport->pasynUserTrace, 
+                                                   pport);
+  if (status != asynSuccess) 
+    {
+      errlogPrintf("%s::drvAsynKeithley6485 port %s"
+                   " can't register standard interfaces: %s\n",
+                   driver, myport, pport->pasynUserTrace->errorMessage);
+      return asynError;
+    }
+
+#ifdef vxWorks
+  /* Send a sacrificial clear status to vxworks device (i.e. VME)*/
+  /* This fixes a problem with *IDN? call when starting from a cold boot */
+  /* with the SBS IP-Octal hardware. */
+  if( writeOnly(pport,"") )
+    {
+      errlogPrintf("%s::drvAsynKeithley6485 port %s failed to write\n",
+                   driver, myport);
+      return asynError;
+    }
+#endif
+
+  /* Reset device */
+  if( writeOnly(pport,"*CLS") )
+    {
+      errlogPrintf("%s::drvAsynKeithley6485 port %s failed to clear\n",
+                   driver, myport);
+      return asynError;
+    }
+  
+
+  /* Identification query */
+  if( writeRead(pport,"*IDN?",inpBuf,sizeof(inpBuf),&eomReason) )
+    {
+      errlogPrintf("%s::drvAsynKeithley6485 port %s failed to "
+                   "acquire identification\n", driver, myport);
+      return asynError;
+    }
+  strcpy(pport->model,inpBuf);
+  // char *model, *serial, *dig_rev, *disp_rev, *brd_rev;
+  pport->serial = strchr( pport->model, ',');
+  pport->serial = strchr( pport->serial + 1, ',');
+  *(pport->serial) = '\0';
+  pport->serial++;
+  pport->dig_rev = strchr( pport->serial, ',');
+  *(pport->dig_rev) = '\0';
+  pport->dig_rev++;
+  pport->disp_rev = strchr( pport->dig_rev, '/');
+  *(pport->disp_rev) = '\0';
+  pport->disp_rev++;
+  pport->brd_rev = strchr( pport->disp_rev, '/');
+  *(pport->brd_rev) = '\0';
+  pport->brd_rev++;
+  
+  /* Complete initialization */
+  pport->init=1;
+
+  pport->data.reading = 0.0;
+  pport->data.timestamp = 0;
+  pport->data.status.raw = 0;
+
+  return asynSuccess;
+}
+
+
+
+
+/****************************************************************************
+ * Define private read and write parameter methods
+ ****************************************************************************/
+
+static asynStatus readDummy(int which, Port *pport, void *data, Type Iface, 
+                            size_t *length, int *eom)
+{
+  return asynSuccess;
+}
+
+static asynStatus writeDummy(int which, Port *pport, void *data, Type Iface)
+{
+  return asynSuccess;
+}
+
+///
+
+static asynStatus readSimpleData( int which, Port *pport, void *data, 
+                                  Type Iface, size_t *length, int *eom)
+{
+  asynStatus status;
+  char outBuf[BUFFER_SIZE];
+  char inpBuf[BUFFER_SIZE];
+
+  int len;
+
+  // Trigger will automatically not work
+  if( simpleCommandTable[which].type != Iface)
+    return asynSuccess;
+
+  sprintf( outBuf, "%s?", simpleCommandTable[which].cmd_str);
+    
+  status = writeRead( pport, outBuf, inpBuf, BUFFER_SIZE, &pport->data.eom);
+  if( status != asynSuccess)
+    return status;
+
+  switch( Iface)
+    {
+    case Float64:
+      *((epicsFloat64 *) data) = atof(inpBuf);
+      break;
+    case Int32:
+      *((epicsInt32 *) data) = atoi(inpBuf);
+      break;
+    case Octet:
+      len = strlen( inpBuf);
+      if( len > 39)
+        inpBuf[39] = '\0';
+      strcpy( (char *) data, inpBuf);
+      break;
+    }
+
+  return asynSuccess;
+}
+
+static asynStatus writeSimpleData( int which, Port *pport, void *data, 
+                                    Type Iface)
+{
+  char outBuf[BUFFER_SIZE];
+  
+  if( simpleCommandTable[which].type == SIMPLE_TRIGGER )
+    sprintf( outBuf, "%s", simpleCommandTable[which].cmd_str);
+  else
+    {
+      if( simpleCommandTable[which].type != Iface )
+        return asynSuccess;
+      
+      switch( simpleCommandTable[which].type & Iface)
+        {
+        case Float64:
+          sprintf( outBuf, "%s %g", simpleCommandTable[which].cmd_str, 
+                   *((epicsFloat64*) data) );
+          break;
+        case Int32:
+          sprintf( outBuf, "%s %d", simpleCommandTable[which].cmd_str, 
+                   *((epicsInt32*) data) );
+          break;
+        case Octet:
+          sprintf( outBuf, "%s %s", simpleCommandTable[which].cmd_str, 
+                   ((char *) data) );
+          break;
+        }
+    }
+
+  return writeOnly( pport, outBuf);
+}
+
+////
+
+static asynStatus readCache(int which, Port *pport, void *data, 
+                            Type Iface, size_t *length, int *eom)
+{
+  char *char_cache = NULL;
+  int len;
+
+  switch( Iface)
+    {
+    case Octet:
+      switch(which)
+        {
+        case MODEL_CMD:
+          char_cache = pport->model;
+          break;
+        case SERIAL_CMD:
+          char_cache = pport->serial;
+          break;
+        case DIG_REV_CMD:
+          char_cache = pport->dig_rev;
+          break;
+        case DISP_REV_CMD:
+          char_cache = pport->disp_rev;
+          break;
+        case BRD_REV_CMD:
+          char_cache = pport->brd_rev;
+          break;
+        }
+      len = strlen(char_cache);
+      if( len < 40)
+        strcpy( (char *) data, char_cache);
+      else // just in case string will overflow the EPICS string size of 40
+        {
+          len = 39;
+          memcpy( (char *) data, char_cache, len);
+          ((char *) data)[len] = '\0';
+        }
+      *length = len;
+      *eom = 0;
+      break;
+    case Float64:
+    //   switch( which)
+    //     {
+    //     }
+      break;
+    case Int32:
+      switch( which)
+        {
+        case TIMESTAMP_CMD:
+          *(epicsInt32*)data = pport->data.timestamp;
+          break;  
+        case STATUS_RAW_CMD:
+          *(epicsInt32*) data = pport->data.status.raw;
+          break;
+        case STATUS_OVERFLOW_CMD:
+          *(epicsInt32*) data = pport->data.status.bits.overflow;
+          break;
+        case STATUS_FILTER_CMD:
+          *(epicsInt32*) data = pport->data.status.bits.filter_enabled;
+          break;
+        case STATUS_MATH_CMD:
+          *(epicsInt32*) data = pport->data.status.bits.math_enabled;
+          break;
+        case STATUS_NULL_CMD:
+          *(epicsInt32*) data = pport->data.status.bits.null_enabled;
+          break;
+        case STATUS_LIMITS_CMD:
+          if( pport->data.status.bits.limit_test)
+            *(epicsInt32*) data = pport->data.status.bits.limit_result;
+          else
+            *(epicsInt32*) data = 3;
+          break;
+        case STATUS_OVERVOLTAGE_CMD:
+          *(epicsInt32*) data = pport->data.status.bits.overvoltage;
+          break;
+        case STATUS_ZERO_CHECK_CMD:
+          *(epicsInt32*) data = pport->data.status.bits.zero_check_enabled;
+          break;
+        case STATUS_ZERO_CORRECT_CMD:
+          *(epicsInt32*) data = pport->data.status.bits.zero_correct_enabled;
+          break;
+        }
+      break;
+    }
+
+  return asynSuccess;
+}
+
+
+static asynStatus readSensorReading(int which, Port *pport, void *data, 
+                                    Type Iface, size_t *length, int *eom)
+{
+  asynStatus status;
+  char inpBuf[BUFFER_SIZE];
+
+  char *str, *token[3], *saveptr;
+  int pass;
+
+  status = writeRead( pport, "READ?", inpBuf, BUFFER_SIZE, &pport->data.eom);
+  if( status != asynSuccess)
+    return status;
+
+  str = inpBuf;
+  for( pass = 0; pass < 3; pass++, str = NULL)
+    {
+      token[pass] = epicsStrtok_r(str, ",", &saveptr);
+      if (token == NULL)
+        break;
+    }
+  if( pass != 3)
+    return asynError;
+
+  pport->data.reading = atof( token[0]);
+  pport->data.timestamp = (int) atof( token[1]);
+  pport->data.status.raw = (int) atof( token[2]);
+
+  switch( Iface )
+    {
+    case Octet:
+      // only print current value
+      *length = sprintf( (char *) data, "%s", token[0]);
+      *eom = pport->data.eom;
+      break;
+    case Float64:
+      *(epicsFloat64*)data = pport->data.reading;
+      break;
+    case Int32:
+      break;
+    }
+
+  return asynSuccess;
+}
+
+static asynStatus readRange(int which, Port *pport, void *data, 
+                            Type Iface, size_t *length, int *eom)
+{
+  asynStatus status;
+  char inpBuf[BUFFER_SIZE];
+
+  if( Iface == Octet)
+    return asynSuccess;
+
+  switch( which)
+    {
+    case RANGE_CMD:
+      status = writeRead( pport, ":RANGE?", inpBuf, BUFFER_SIZE, 
+                          &pport->data.eom);
+      break;
+    case RANGE_AUTO_ULIMIT_CMD:
+      status = writeRead( pport, ":RANGE:AUTO:ULIM?", inpBuf, BUFFER_SIZE, 
+                          &pport->data.eom);
+      break;
+    case RANGE_AUTO_LLIMIT_CMD:
+      status = writeRead( pport, ":RANGE:AUTO:LLIM?", inpBuf, BUFFER_SIZE, 
+                          &pport->data.eom);
+      break;
+    default:
+      return asynError;
+    }
+  if( status != asynSuccess)
+    return status;
+
+  if( Iface == Float64)
+    {
+      double val;
+
+      val = atof( inpBuf);
+      if( val == 0.0)
+        return asynError;
+
+      *(epicsFloat64*) data = val;
+    }
+  else if( Iface == Int32)
+    {
+      char *p;
+
+      p = strchr( inpBuf, 'E');
+      if(p == NULL)
+        return asynError;
+      p++;
+      *(epicsInt32*) data = 9 + atoi(p) ;
+    }
+
+  return asynSuccess;
+}
+
+
+static asynStatus writeRange( int which, Port *pport, void *data, Type Iface)
+{
+  char outBuf[BUFFER_SIZE];
+  int value;
+
+  if( Iface != Int32)
+    return asynSuccess;
+
+  value = *((epicsInt32*) data);
+  if( (value < 0) || (value > 7) )
+    return asynError;
+    
+  switch( which)
+    {
+    case RANGE_CMD:
+      sprintf( outBuf, ":RANGE 2.0e%d", -9 + value );
+      break;
+    case RANGE_AUTO_ULIMIT_CMD:
+      sprintf( outBuf, ":RANGE:AUTO:ULIM 2.0e%d", -9 + value );
+      break;
+    case RANGE_AUTO_LLIMIT_CMD:
+      sprintf( outBuf, ":RANGE:AUTO:LLIM 2.0e%d", -9 + value );
+      break;
+    default:
+      return asynError;
+    }
+
+  return writeOnly( pport, outBuf);
+}
+
+
+static asynStatus readRate(int which, Port *pport, void *data, 
+                           Type Iface, size_t *length, int *eom)
+{
+  asynStatus status;
+  char inpBuf[BUFFER_SIZE];
+
+  double val;
+  int rate;
+
+  if( Iface != Int32)
+    return asynSuccess;
+
+  status = writeRead( pport, ":NPLC?", inpBuf, BUFFER_SIZE, &pport->data.eom);
+  if( status != asynSuccess)
+    return status;
+
+  val = atof( inpBuf);
+  if( val > 1.0)
+    rate = 0; // SLOW
+  else if( val > 0.1)
+    rate = 1; // MEDIUM
+  else
+    rate = 2; // FAST
+  
+  *((epicsInt32*) data) = rate;
+
+  return asynSuccess;
+}
+
+
+static asynStatus writeRate( int which, Port *pport, void *data, Type Iface)
+{
+  char outBuf[BUFFER_SIZE];
+  int rate;
+  double val;
+
+  if( Iface != Int32)
+    return asynSuccess;
+
+  rate = *((epicsInt32*) data);
+  if( (rate < 0) || (rate > 2) )
+    return asynError;
+
+  switch( rate)
+    {
+    case 0:
+      val = 6.0;
+      break;
+    case 1:
+      val = 1.0;
+      break;
+    case 2:
+      val = 0.1;
+      break;
+    default:
+      val = 1.0;
+      break;
+    }
+
+  sprintf( outBuf, ":NPLC %g", val );
+  return writeOnly( pport, outBuf);
+}
+
+
+static asynStatus readVoltageSettings(int which, Port *pport, void *data, Type Iface, 
+                                      size_t *length, int *eom)
+{
+  asynStatus status;
+  char inpBuf[BUFFER_SIZE];
+
+  double val;
+
+  if(( which != VOLTAGE_RANGE_CMD) && ( which != VOLTAGE_CURRENT_LIMIT_CMD))
+    return asynError;
+  if( Iface == Octet)
+    return asynSuccess;
+
+  if( which == VOLTAGE_RANGE_CMD)
+    status = writeRead( pport, "SOUR:VOLT:RANGE?", inpBuf, BUFFER_SIZE, 
+                        &pport->data.eom);
+  else
+    status = writeRead( pport, "SOUR:VOLT:ILIM?", inpBuf, BUFFER_SIZE, 
+                        &pport->data.eom);
+  if( status != asynSuccess)
+    return status;
+
+  val = atof( inpBuf);
+  if( val == 0.0)
+    return asynError;
+
+  if( Iface == Float64)
+    {
+      *((epicsFloat64*) data) = val;
+    }
+  else if( Iface == Int32)
+    {
+      if( which == VOLTAGE_RANGE_CMD)
+        {
+          if( val == 10.0)
+            *((epicsInt32*) data) = 0;
+          else if( val == 50.0)
+            *((epicsInt32*) data) = 1;
+          else if( val == 500.0)
+            *((epicsInt32*) data) = 2;
+        }
+      else
+        {
+          if( val == 2.5e-5)
+            *((epicsInt32*) data) = 0;
+          else if( val == 2.5e-4)
+            *((epicsInt32*) data) = 1;
+          else if( val == 2.5e-3)
+            *((epicsInt32*) data) = 2;
+          else if( val == 2.5e-2)
+            *((epicsInt32*) data) = 3;
+        }
+    }
+
+  return asynSuccess;
+}
+
+
+static asynStatus writeVoltageSettings( int which, Port *pport, void *data, Type Iface)
+{
+  char outBuf[BUFFER_SIZE];
+  int value;
+
+  if(( which != VOLTAGE_RANGE_CMD) && ( which != VOLTAGE_CURRENT_LIMIT_CMD))
+    return asynError;
+  if( Iface != Int32)
+    return asynSuccess;
+
+  value = *((epicsInt32*) data);
+  if( which == VOLTAGE_RANGE_CMD) 
+    {
+      if( (value < 0) || (value > 2) )
+        return asynError;
+
+      if( value == 0)
+        value = 10;
+      else if( value == 1)
+        value = 50;
+      else 
+        value = 500;
+   
+      sprintf( outBuf, "SOUR:VOLT:RANGE %d", value );
+    }
+  else
+    {
+      if( (value < 0) || (value > 3) )
+        return asynError;
+
+      sprintf( outBuf, "SOUR:VOLT:ILIM 2.5e%d", value - 5 );
+    }
+
+  return writeOnly( pport, outBuf);
+}
+
+
+static asynStatus readCommon(int which, Port *pport, void *data, 
+                             Type Iface, size_t *length, int *eom)
+{
+  asynStatus status;
+  char inpBuf[BUFFER_SIZE];
+
+  int val = 0;
+
+  if( Iface != Int32)
+    return asynSuccess;
+
+  switch(which)
+    {
+    case DIGITAL_FILTER_CONTROL_CMD:
+      status = writeRead( pport, "AVER:TCON?", inpBuf, BUFFER_SIZE, 
+                          &pport->data.eom);
+
+      if( status != asynSuccess)
+        return status;
+      if( !strcmp( "MOV", inpBuf) )
+        val = 0;
+      else if( !strcmp( "REP", inpBuf) )
+        val = 1;
+      else
+        return asynError;
+      break;
+    }
+  
+  *((epicsInt32*) data) = val;
+
+  return asynSuccess;
+}
+
+
+static asynStatus writeCommon( int which, Port *pport, void *data, Type Iface)
+{
+  char outBuf[BUFFER_SIZE];
+  int val;
+
+  if( Iface != Int32)
+    return asynSuccess;
+
+  val = *((epicsInt32*) data);
+  switch( which)
+    {
+    case DIGITAL_FILTER_CONTROL_CMD:
+      if( !val)
+        sprintf( outBuf, "AVER:TCON MOV" );
+      else if(val == 1)
+        sprintf( outBuf, "AVER:TCON REP" );
+      else
+        return asynError;
+      break;
+    }
+
+  return writeOnly( pport, outBuf);
+}
+
+
+/****************************************************************************
+ * Define private interface asynCommon methods
+ ****************************************************************************/
+static void report(void* ppvt,FILE* fp,int details)
+{
+  //  int i;
+  Port* pport = (Port*)ppvt;
+
+  fprintf( fp, "Keithley648x port: %s\n", pport->myport);
+  if( details)
+    {
+      fprintf( fp, "    server:     %s\n", pport->ioport);
+      fprintf( fp, "    address:    %d\n", pport->ioaddr);
+      fprintf( fp, "    ioErrors:   %d\n", pport->stats.ioErrors);
+      fprintf( fp, "    writeReads: %d\n", pport->stats.writeReads);
+      fprintf( fp, "    writeOnlys: %d\n", pport->stats.writeOnlys);
+      fprintf( fp, "    support %s initialized\n",(pport->init)?"IS":"IS NOT");
+    }
+
+}
+
+static asynStatus connect(void* ppvt,asynUser* pasynUser)
+{
+  pasynManager->exceptionConnect(pasynUser);
+  return asynSuccess;
+}
+
+static asynStatus disconnect(void* ppvt,asynUser* pasynUser)
+{
+  pasynManager->exceptionDisconnect(pasynUser);
+  return asynSuccess;
+}
+
+
+/****************************************************************************
+ * Define private interface asynDrvUser methods
+ ****************************************************************************/
+static asynStatus create(void* ppvt, asynUser *pasynUser, const char *drvInfo, 
+                         const char **pptypeName, size_t *psize)
+{
+  Port* pport=(Port*)ppvt;
+  
+  int i;
+  
+  for(i = 0; i < COMMAND_NUMBER; i++) 
+    if( !epicsStrCaseCmp( drvInfo, commandTable[i].tag) ) 
+      {
+        if( (commandTable[i].dev != DEV_ALL) && 
+            (commandTable[i].dev != pport->devtype) )
+          {
+            errlogPrintf("%s::create port %s failed as tag %s is for different "
+                         "device\n", driver, pport->myport, drvInfo);
+            pasynUser->reason = 0;
+            return asynError;
+          }
+        pasynUser->reason = i;
+        break;
+      }
+  if( i == COMMAND_NUMBER ) 
+    {
+      errlogPrintf("%s::create port %s failed to find tag %s\n",
+                   driver, pport->myport, drvInfo);
+      pasynUser->reason = 0;
+      return asynError;
+    }
+  
+  return asynSuccess;
+}
+
+static asynStatus gettype(void* ppvt,asynUser* pasynUser,
+                          const char** pptypeName,size_t* psize)
+{
+  if( pptypeName ) 
+    *pptypeName = NULL;
+  if( psize ) 
+    *psize = 0;
+
+  return asynSuccess;
+}
+
+static asynStatus destroy(void* ppvt,asynUser* pasynUser)
+{
+  return asynSuccess;
+}
+
+
+/****************************************************************************
+ * Define private interface asynFloat64 methods
+ ****************************************************************************/
+static asynStatus writeFloat64(void* ppvt,asynUser* pasynUser,
+                               epicsFloat64 value)
+{
+  Port* pport=(Port*)ppvt;
+  int which = pasynUser->reason;
+
+  int id;
+  id = commandTable[which].id;
+
+  if( pport->init == 0) 
+    return asynError;
+
+   switch( commandTable[which].type )
+    {
+    case CMD_GEN:
+      return genCommandTable[id].writeFunc(id, pport, &value, Float64);
+      break;
+    case CMD_SIMPLE:
+      return writeSimpleData( id, pport, &value, Float64);
+      break;
+    }
+
+  return asynSuccess;
+}
+
+static asynStatus readFloat64(void* ppvt,asynUser* pasynUser,
+                              epicsFloat64* value)
+{
+  Port* pport=(Port*)ppvt;
+  int which = pasynUser->reason;
+
+  int id;
+  id = commandTable[which].id;
+
+  if( pport->init == 0) 
+    return asynError;
+
+  switch( commandTable[which].type )
+    {
+    case CMD_GEN:
+      return genCommandTable[id].readFunc(id, pport, value, Float64, 
+                                          NULL, NULL);
+      break;
+    case CMD_SIMPLE:
+      return readSimpleData( id, pport, value, Float64, NULL, NULL);
+      break;
+    case CMD_CACHE:
+      return readCache(id, pport, value, Float64, NULL, NULL);
+      break;
+    }
+
+  return asynSuccess;
+}
+
+
+/****************************************************************************
+ * Define private interface asynInt32 methods
+ ****************************************************************************/
+static asynStatus writeInt32(void *ppvt, asynUser *pasynUser, epicsInt32 value)
+{
+  Port* pport=(Port*)ppvt;
+  int which = pasynUser->reason;
+
+  int id;
+  id = commandTable[which].id;
+
+  if( pport->init == 0) 
+    return asynError;
+
+  switch( commandTable[which].type )
+    {
+    case CMD_GEN:
+      return genCommandTable[id].writeFunc(id, pport, &value, Int32);
+      break;
+    case CMD_SIMPLE:
+      return writeSimpleData( id, pport, (void *) &value, Int32);
+      break;
+    }
+  
+  return asynSuccess;
+}
+
+static asynStatus readInt32(void *ppvt, asynUser *pasynUser, epicsInt32 *value)
+{
+  Port* pport=(Port*)ppvt;
+  int which = pasynUser->reason;
+
+  int id;
+  id = commandTable[which].id;
+
+  if( pport->init == 0) 
+    return asynError;
+
+  switch( commandTable[which].type )
+    {
+    case CMD_GEN:
+      return genCommandTable[id].readFunc(id, pport, value, Int32, 
+                                          NULL, NULL);
+      break;
+    case CMD_SIMPLE:
+      return readSimpleData( id, pport, value, Int32, NULL, NULL);
+      break;
+    case CMD_CACHE:
+      return readCache(id, pport, value, Int32, NULL, NULL);
+      break;
+    }
+
+  return asynSuccess;
+}
+
+
+/****************************************************************************
+ * Define private interface asynOctet methods
+ ****************************************************************************/
+static asynStatus flushOctet(void *ppvt, asynUser* pasynUser)
+{
+  return asynSuccess;
+}
+
+static asynStatus writeOctet(void *ppvt, asynUser *pasynUser, const char *data,
+                             size_t numchars, size_t *nbytes)
+{
+  Port* pport=(Port*)ppvt;
+  int which = pasynUser->reason;
+
+  int id;
+  id = commandTable[which].id;
+
+  if( pport->init == 0) 
+    return asynError;
+
+  switch( commandTable[which].type )
+    {
+    case CMD_GEN:
+      *nbytes=strlen(data);
+      return genCommandTable[id].writeFunc(id, pport, (void *) data, Octet);
+      break;
+    case CMD_SIMPLE:
+      *nbytes=strlen(data);
+      return writeSimpleData( id, pport, &data, Octet);
+      break;
+    }
+  
+  return asynSuccess;
+}
+
+static asynStatus readOctet(void* ppvt, asynUser* pasynUser, char* data,
+                            size_t maxchars,size_t* nbytes,int* eom)
+{
+  Port* pport=(Port*)ppvt;
+  int which = pasynUser->reason;
+
+  int id;
+  id = commandTable[which].id;
+
+  if( pport->init == 0) 
+    return asynError;
+
+  switch( commandTable[which].type )
+    {
+    case CMD_GEN:
+      return genCommandTable[id].readFunc(id, pport, (void *) data, Octet, 
+                                          nbytes, eom);
+      break;
+    case CMD_SIMPLE:
+      return readSimpleData( id, pport, data, Octet, nbytes, eom);
+      break;
+    case CMD_CACHE:
+      return readCache(id, pport, (void *) data, Octet, nbytes, eom);
+      break;
+    }
+
+  return asynSuccess;
+}
+
+
+/****************************************************************************
+ * Define private Keithley648x external interface asynOctet methods
+ ****************************************************************************/
+static asynStatus writeOnly(Port *pport, const char *outBuf)
+{
+  asynStatus status;
+  size_t nActual, nRequested;
+
+  nRequested=strlen(outBuf);
+  status = 
+    pasynOctetSyncIO->write(pport->pasynUser,outBuf,nRequested,TIMEOUT,&nActual);
+  if( nActual!=nRequested ) 
+    status = asynError;
+
+  if( status!=asynSuccess )
+    {
+      pport->stats.ioErrors++;
+      asynPrint(pport->pasynUserTrace,ASYN_TRACE_ERROR, 
+                "%s writeOnly: error %d wrote \"%s\"\n",
+                pport->myport,status,outBuf);
+    }
+  else
+    pport->stats.writeOnlys++;
+
+  asynPrint(pport->pasynUserTrace, ASYN_TRACEIO_FILTER,
+            "%s writeOnly: wrote \"%s\"\n",
+            pport->myport,outBuf);
+
+  return status;
+}
+
+static asynStatus writeRead(Port *pport, const char *outBuf, char *inpBuf,
+                            int inputSize, int *eomReason)
+{
+  asynStatus status;
+  size_t nWrite, nRead, nWriteRequested;
+
+  nWriteRequested=strlen(outBuf);
+  status = pasynOctetSyncIO->writeRead(pport->pasynUser,outBuf,
+                                       nWriteRequested,inpBuf,inputSize-1,
+                                       TIMEOUT,&nWrite,&nRead,eomReason);
+  if( nWrite!=nWriteRequested ) 
+    status = asynError;
+
+  if( status!=asynSuccess )
+    {
+      pport->stats.ioErrors++;
+      asynPrint(pport->pasynUserTrace,ASYN_TRACE_ERROR,
+                "%s writeRead: error %d wrote \"%s\"\n",
+                pport->myport,status,outBuf);
+    }
+  else
+    {
+      inpBuf[nRead]='\0';
+      pport->stats.writeReads++;
+    }
+
+  asynPrint(pport->pasynUserTrace,ASYN_TRACEIO_FILTER,
+            "%s writeRead: wrote \"%s\" read \"%s\"\n",
+            pport->myport,outBuf,inpBuf);
+  
+  return status;
+}
+
+
+/****************************************************************************
+ * Register public methods
+ ****************************************************************************/
+ 
+/* Initialization method definitions */
+static const iocshArg arg0 = {"type",iocshArgString};
+static const iocshArg arg1 = {"myport",iocshArgString};
+static const iocshArg arg2 = {"ioport",iocshArgString};
+static const iocshArg arg3 = {"ioaddr",iocshArgInt};
+static const iocshArg* args[]= {&arg0,&arg1,&arg2,&arg3};
+static const iocshFuncDef drvAsynKeithley648xFuncDef = 
+  {"drvAsynKeithley648x",4,args};
+static void drvAsynKeithley648xCallFunc(const iocshArgBuf* args)
+{
+  drvAsynKeithley648x(args[0].sval,args[1].sval,args[2].sval,args[3].ival);
+}
+
+/* Registration method */
+static void drvAsynKeithley648xRegister(void)
+{
+  static int firstTime = 1;
+
+  if( firstTime )
+    {
+      firstTime = 0;
+      iocshRegister( &drvAsynKeithley648xFuncDef,drvAsynKeithley648xCallFunc );
+    }
+}
+epicsExportRegistrar( drvAsynKeithley648xRegister );