From 3dc48762a752a7914155a9bc81b84522d58b5c73 Mon Sep 17 00:00:00 2001 From: Martin Konrad Date: Tue, 19 Nov 2019 09:48:21 -0500 Subject: [PATCH 01/23] New upstream version 4.37 --- Makefile | 4 + asyn/Makefile | 93 +- asyn/asynDriver/asynDriver.h | 16 +- asyn/asynPortDriver/asynParamType.h | 2 + asyn/asynPortDriver/asynPortDriver.cpp | 357 +- asyn/asynPortDriver/asynPortDriver.h | 15 + asyn/asynPortDriver/paramVal.cpp | 45 +- asyn/asynPortDriver/paramVal.h | 4 + .../unittest/asynPortDriverTest.cpp | 3 +- asyn/asynRecord/asynRecord.c | 24 +- asyn/asynRecord/drvAsyn.c | 7 +- asyn/devEpics/devAsynFloat64.c | 25 +- asyn/devEpics/devAsynInt32.c | 27 +- asyn/devEpics/devAsynInt64.c | 602 ++ asyn/devEpics/devAsynInt64.dbd | 2 + asyn/devEpics/devAsynInt64Array.c | 49 + asyn/devEpics/devAsynInt64Array.dbd | 2 + asyn/devEpics/devAsynInt64TimeSeries.c | 49 + asyn/devEpics/devAsynInt64TimeSeries.dbd | 1 + asyn/devEpics/devAsynUInt32Digital.c | 28 +- asyn/devEpics/devAsynXXXArray.h | 22 +- asyn/devEpics/devEpics.dbd | 12 - asyn/drvAsynFTDI/drvAsynFTDIPort.cpp | 656 ++ asyn/drvAsynFTDI/drvAsynFTDIPort.dbd | 1 + asyn/drvAsynFTDI/drvAsynFTDIPort.h | 25 + asyn/drvAsynFTDI/ftdiDriver.cpp | 639 ++ asyn/drvAsynFTDI/ftdiDriver.h | 88 + asyn/drvAsynSerial/drvAsynIPPort.c | 1 + asyn/drvAsynSerial/drvAsynSerialPort.c | 1 + asyn/drvAsynSerial/drvAsynSerialPortWin32.c | 1 + asyn/interfaces/asynInt64.h | 65 + asyn/interfaces/asynInt64Array.h | 59 + asyn/interfaces/asynInt64ArrayBase.c | 27 + asyn/interfaces/asynInt64ArraySyncIO.c | 218 + asyn/interfaces/asynInt64ArraySyncIO.h | 43 + asyn/interfaces/asynInt64Base.c | 161 + asyn/interfaces/asynInt64SyncIO.c | 268 + asyn/interfaces/asynInt64SyncIO.h | 47 + asyn/interfaces/asynStandardInterfaces.h | 10 + asyn/interfaces/asynStandardInterfacesBase.c | 40 + asyn/miscellaneous/asynInterposeEos.c | 2 +- asyn/vxi11/drvVxi11.c | 2 +- configure/CONFIG_SITE | 6 + configure/RELEASE | 7 +- documentation/Doxyfile | 2 +- documentation/RELEASE_NOTES.html | 58 + documentation/asynDriver.html | 306 +- documentation/testErrors.png | Bin 141576 -> 63325 bytes iocBoot/ioctestErrors/st.cmd | 18 +- testApp/src/Makefile | 2 + testErrorsApp/Db/Makefile | 1 + testErrorsApp/Db/testErrorsInt64.db | 60 + testErrorsApp/adl/testErrors.adl | 5664 +++++++++-------- testErrorsApp/src/testErrors.cpp | 71 +- testErrorsApp/src/testErrors.h | 8 + testFtdiApp/Makefile | 8 + testFtdiApp/src/Makefile | 10 + testFtdiApp/src/testFtdiMain.cpp | 127 + testGpibApp/src/Makefile | 6 + testGpibSerialApp/src/Makefile | 2 + testUsbtmcApp/src/Makefile | 1 + 61 files changed, 7295 insertions(+), 2805 deletions(-) create mode 100644 asyn/devEpics/devAsynInt64.c create mode 100644 asyn/devEpics/devAsynInt64.dbd create mode 100644 asyn/devEpics/devAsynInt64Array.c create mode 100644 asyn/devEpics/devAsynInt64Array.dbd create mode 100644 asyn/devEpics/devAsynInt64TimeSeries.c create mode 100644 asyn/devEpics/devAsynInt64TimeSeries.dbd delete mode 100644 asyn/devEpics/devEpics.dbd create mode 100644 asyn/drvAsynFTDI/drvAsynFTDIPort.cpp create mode 100644 asyn/drvAsynFTDI/drvAsynFTDIPort.dbd create mode 100644 asyn/drvAsynFTDI/drvAsynFTDIPort.h create mode 100644 asyn/drvAsynFTDI/ftdiDriver.cpp create mode 100644 asyn/drvAsynFTDI/ftdiDriver.h create mode 100644 asyn/interfaces/asynInt64.h create mode 100644 asyn/interfaces/asynInt64Array.h create mode 100644 asyn/interfaces/asynInt64ArrayBase.c create mode 100644 asyn/interfaces/asynInt64ArraySyncIO.c create mode 100644 asyn/interfaces/asynInt64ArraySyncIO.h create mode 100644 asyn/interfaces/asynInt64Base.c create mode 100644 asyn/interfaces/asynInt64SyncIO.c create mode 100644 asyn/interfaces/asynInt64SyncIO.h create mode 100644 testErrorsApp/Db/testErrorsInt64.db create mode 100644 testFtdiApp/Makefile create mode 100644 testFtdiApp/src/Makefile create mode 100644 testFtdiApp/src/testFtdiMain.cpp diff --git a/Makefile b/Makefile index 1ee8bf4..290c395 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,10 @@ ifneq ($(EPICS_LIBCOM_ONLY),YES) testUsbtmcApp_DEPEND_DIRS = asyn iocBoot_DEPEND_DIRS += testUsbtmcApp DIRS += iocBoot + ifeq ($(DRV_FTDI),YES) + DIRS += testFtdiApp + testFtdiApp_DEPEND_DIRS = asyn + endif endif DIRS += testAsynPortClientApp diff --git a/asyn/Makefile b/asyn/Makefile index 35c7124..f08a9aa 100644 --- a/asyn/Makefile +++ b/asyn/Makefile @@ -12,6 +12,9 @@ include $(TOP)/configure/CONFIG ASYN = $(TOP)/asyn #USR_CFLAGS += -DDEBUG +USR_CFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET -DUSE_TYPED_DRVET +USR_CPPFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET -DUSE_TYPED_DRVET + USR_INCLUDES_cygwin32 += -I/usr/include/tirpc # The following gets rid of the -fno-implicit-templates flag on vxWorks, @@ -64,11 +67,13 @@ INC += drvAsynIPServerPort.h SRC_DIRS += $(ASYN)/interfaces INC += asynInt32.h asynInt32SyncIO.h +INC += asynInt64.h asynInt64SyncIO.h INC += asynUInt32Digital.h asynUInt32DigitalSyncIO.h INC += asynFloat64.h asynFloat64SyncIO.h INC += asynInt8Array.h asynInt8ArraySyncIO.h INC += asynInt16Array.h asynInt16ArraySyncIO.h INC += asynInt32Array.h asynInt32ArraySyncIO.h +INC += asynInt64Array.h asynInt64ArraySyncIO.h INC += asynFloat32Array.h asynFloat32ArraySyncIO.h INC += asynFloat64Array.h asynFloat64ArraySyncIO.h INC += asynOctet.h asynOctetSyncIO.h @@ -79,9 +84,11 @@ INC += asynOption.h asynOptionSyncIO.h INC += asynDrvUser.h INC += asynStandardInterfaces.h asyn_SRCS += asynInt32Base.c asynInt32SyncIO.c +asyn_SRCS += asynInt64Base.c asynInt64SyncIO.c asyn_SRCS += asynInt8ArrayBase.c asynInt8ArraySyncIO.c asyn_SRCS += asynInt16ArrayBase.c asynInt16ArraySyncIO.c asyn_SRCS += asynInt32ArrayBase.c asynInt32ArraySyncIO.c +asyn_SRCS += asynInt64ArrayBase.c asynInt64ArraySyncIO.c asyn_SRCS += asynUInt32DigitalBase.c asynUInt32DigitalSyncIO.c asyn_SRCS += asynFloat64Base.c asynFloat64SyncIO.c asyn_SRCS += asynFloat32ArrayBase.c asynFloat32ArraySyncIO.c @@ -138,19 +145,24 @@ INC += asynPortClient.h asyn_SRCS += asynPortClient.cpp ifneq ($(EPICS_LIBCOM_ONLY),YES) + ifdef CAT + DBDCAT += devEpics.dbd + else + INSTALL_DBDS += $(INSTALL_DBD)/devEpics.dbd + endif SRC_DIRS += $(ASYN)/devEpics - DBD += devAsynOctet.dbd - DBD += devAsynInt32.dbd - DBD += devAsynInt8Array.dbd - DBD += devAsynInt16Array.dbd - DBD += devAsynInt32Array.dbd - DBD += devAsynInt32TimeSeries.dbd - DBD += devAsynUInt32Digital.dbd - DBD += devAsynFloat64.dbd - DBD += devAsynFloat32Array.dbd - DBD += devAsynFloat64Array.dbd - DBD += devAsynFloat64TimeSeries.dbd - DBD += devEpics.dbd + devEpics_DBD += devAsynOctet.dbd + devEpics_DBD += devAsynInt32.dbd + devEpics_DBD += devAsynInt8Array.dbd + devEpics_DBD += devAsynInt16Array.dbd + devEpics_DBD += devAsynInt32Array.dbd + devEpics_DBD += devAsynInt32TimeSeries.dbd + devEpics_DBD += devAsynUInt32Digital.dbd + devEpics_DBD += devAsynFloat64.dbd + devEpics_DBD += devAsynFloat32Array.dbd + devEpics_DBD += devAsynFloat64Array.dbd + devEpics_DBD += devAsynFloat64TimeSeries.dbd + devEpics_DBD += devAsynRecord.dbd DB += asynInt32TimeSeries.db DB += asynFloat64TimeSeries.db INC += asynEpicsUtils.h @@ -167,6 +179,27 @@ ifneq ($(EPICS_LIBCOM_ONLY),YES) asyn_SRCS += devAsynFloat64Array.c asyn_SRCS += devAsynFloat64TimeSeries.c + # These require 64-bit support + ifdef BASE_7_0 + HAVE_DEVINT64=YES + endif + ifdef BASE_3_16 + ifneq ($(EPICS_MODIFICATION),0) + HAVE_DEVINT64=YES + endif + endif + + ifdef HAVE_DEVINT64 + devEpics_DBD += devAsynInt64.dbd + devEpics_DBD += devAsynInt64Array.dbd + devEpics_DBD += devAsynInt64TimeSeries.dbd + asyn_SRCS += devAsynInt64.c + asyn_SRCS += devAsynInt64Array.c + asyn_SRCS += devAsynInt64TimeSeries.c + endif + + DBD += $(devEpics_DBD) + SRC_DIRS += $(ASYN)/asynRecord DBDINC += asynRecord DBD += devAsynRecord.dbd @@ -209,6 +242,20 @@ ifeq ($(DRV_USBTMC),YES) DBD += drvAsynUSBTMC.dbd endif +ifeq ($(DRV_FTDI),YES) + SRC_DIRS += $(ASYN)/drvAsynFTDI + asyn_SRCS += ftdiDriver.cpp drvAsynFTDIPort.cpp + asyn_SYS_LIBS += usb-1.0 + INC += drvAsynFTDIPort.h + ifeq ($(DRV_FTDI_USE_LIBFTDI1),YES) + asyn_SYS_LIBS += ftdi1 + USR_CXXFLAGS += -DHAVE_LIBFTDI1 + else + asyn_SYS_LIBS += ftdi + endif + DBD += drvAsynFTDIPort.dbd +endif + SRC_DIRS += $(ASYN)/ni1014 asyn_SRCS_vxWorks += drvNi1014.c asyn_SRCS_RTEMS += drvNi1014.c @@ -263,7 +310,27 @@ RPCGEN_FLAGS_solaris = -M endif - # Parallel build sometimes fails. # Make dependences on new records explicit. asynRecord$(OBJ): $(COMMON_DIR)/asynRecord.h + +devEpics.dbd$(DEP): $(TOP)/asyn/Makefile + @$(RM) $@ + @echo "$(COMMON_DIR)/devEpics.dbd:" > $@ + +$(COMMON_DIR)/devEpics.dbd: $(TOP)/asyn/Makefile + +ifdef DBDCAT_COMMAND + # Override the default command when generating devEpics.dbd + $(COMMON_DIR)/devEpics.dbd: DBDCAT_COMMAND = \ + $(PERL) $(TOOLS)/makeIncludeDbd.pl $(devEpics_DBD) $(notdir $@) +else + # 3.14 didn't have the DBDCAT rule: + build: $(COMMON_DIR)/devEpics.dbd + $(COMMON_DIR)/devEpics.dbd: $(devEpics_DBD) + $(ECHO) "Creating dbd file $(notdir $@)" + @$(RM) $(notdir $@) + $(PERL) $(TOOLS)/makeIncludeDbd.pl $(devEpics_DBD) $(notdir $@) + @$(MV) $(notdir $@) $@ +endif + diff --git a/asyn/asynDriver/asynDriver.h b/asyn/asynDriver/asynDriver.h index 4d07bc2..76892a8 100644 --- a/asyn/asynDriver/asynDriver.h +++ b/asyn/asynDriver/asynDriver.h @@ -19,13 +19,27 @@ #include #include #include +#include /* Version number names similar to those provide by base * These macros are always numeric */ #define ASYN_VERSION 4 -#define ASYN_REVISION 36 +#define ASYN_REVISION 37 #define ASYN_MODIFICATION 0 +#ifndef EPICS_VERSION_INT +#define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P)) +#define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL) +#endif +#define LT_EPICSBASE(V,R,M,P) (EPICS_VERSION_INT < VERSION_INT((V),(R),(M),(P))) + +#if LT_EPICSBASE(3,15,0,2) + #if __STDC_VERSION__ < 199901L + typedef long long epicsInt64; + typedef unsigned long long epicsUInt64; + #endif +#endif + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ diff --git a/asyn/asynPortDriver/asynParamType.h b/asyn/asynPortDriver/asynParamType.h index c261bc6..e7debf3 100644 --- a/asyn/asynPortDriver/asynParamType.h +++ b/asyn/asynPortDriver/asynParamType.h @@ -12,12 +12,14 @@ typedef enum { asynParamNotDefined, /**< Undefined */ asynParamInt32, + asynParamInt64, asynParamUInt32Digital, asynParamFloat64, asynParamOctet, asynParamInt8Array, asynParamInt16Array, asynParamInt32Array, + asynParamInt64Array, asynParamFloat32Array, asynParamFloat64Array, asynParamGenericPointer diff --git a/asyn/asynPortDriver/asynPortDriver.cpp b/asyn/asynPortDriver/asynPortDriver.cpp index 5a3f36a..fa5f49d 100644 --- a/asyn/asynPortDriver/asynPortDriver.cpp +++ b/asyn/asynPortDriver/asynPortDriver.cpp @@ -54,11 +54,13 @@ class paramList { asynStatus findParam(const char *name, int *index); asynStatus getName(int index, const char **name); asynStatus setInteger(int index, int value); + asynStatus setInteger64(int index, epicsInt64 value); asynStatus setUInt32(int index, epicsUInt32 value, epicsUInt32 valueMask, epicsUInt32 interruptMask); asynStatus setDouble(int index, double value); asynStatus setString(int index, const char *string); asynStatus setString(int index, const std::string& string); asynStatus getInteger(int index, epicsInt32 *value); + asynStatus getInteger64(int index, epicsInt64 *value); asynStatus getUInt32(int index, epicsUInt32 *value, epicsUInt32 mask); asynStatus getDouble(int index, double *value); asynStatus getString(int index, int maxChars, char *value); @@ -79,6 +81,7 @@ class paramList { private: asynStatus setFlag(int index); asynStatus int32Callback(int command, int addr); + asynStatus int64Callback(int command, int addr); asynStatus uint32Callback(int command, int addr, epicsUInt32 interruptMask); asynStatus float64Callback(int command, int addr); asynStatus octetCallback(int command, int addr); @@ -190,7 +193,27 @@ asynStatus paramList::setInteger(int index, int value) return asynSuccess; } -/** Sets the value for an integer in the parameter library. +/** Sets the value for a 64-bit integer in the parameter library. + * \param[in] index The parameter number + * \param[in] value Value to set. + * \return Returns asynParamBadIndex if the index is not valid or asynParamWrongType if the parametertype is not asynParamInt64. */ +asynStatus paramList::setInteger64(int index, epicsInt64 value) +{ + try{ + getParameter(index)->setInteger64(value); + registerParameterChange(getParameter(index), index); + } + catch (ParamValWrongType&) { + return asynParamWrongType; + } + catch (ParamListInvalidIndex&){ + return asynParamBadIndex; + } + + return asynSuccess; +} + +/** Sets the value for a UInt32 in the parameter library. * \param[in] index The parameter number * \param[in] value Value to set. * \param[in] valueMask Mask to use when setting the value. @@ -300,7 +323,34 @@ asynStatus paramList::getInteger(int index, epicsInt32 *value) return status; } -/** Returns the value for an integer from the parameter library. +/** Returns the value for a 64-bit integer from the parameter library. + * \param[in] index The parameter number + * \param[out] value Address of value to get. + * \return Returns asynParamBadIndex if the index is not valid, asynParamWrongType if the parameter type is not asynParamInt64, + * or asynParamUndefined if the value has not been defined. */ +asynStatus paramList::getInteger64(int index, epicsInt64 *value) +{ + asynStatus status; + *value = 0; + + try { + paramVal *pVal = getParameter(index); + *value = pVal->getInteger64(); + status = pVal->getStatus(); + } + catch (ParamValWrongType&){ + return asynParamWrongType; + } + catch (ParamValNotDefined&){ + return asynParamUndefined; + } + catch (ParamListInvalidIndex&) { + return asynParamBadIndex; + } + return status; +} + +/** Returns the value for a UInt32 from the parameter library. * \param[in] index The parameter number * \param[out] value Address of value to get. * \param[in] mask The mask to use when getting the value. @@ -598,6 +648,50 @@ asynStatus paramList::int32Callback(int command, int addr) return asynSuccess; } +/** Calls the registered asyn callback functions for all clients for a 64-bit integer parameter */ +asynStatus paramList::int64Callback(int command, int addr) +{ + ELLLIST *pclientList; + interruptNode *pnode; + asynStandardInterfaces *pInterfaces = this->pasynPortDriver->getAsynStdInterfaces(); + epicsTimeStamp timeStamp; + this->pasynPortDriver->getTimeStamp(&timeStamp); + int address; + epicsInt64 value; + int alarmStatus=0; + int alarmSeverity=0; + asynStatus status=asynSuccess; + + /* Pass int64 interrupts */ + status = getInteger64(command, &value); + getAlarmStatus(command, &alarmStatus); + getAlarmSeverity(command, &alarmSeverity); + if (!pInterfaces->int64InterruptPvt) return asynParamNotFound; + pasynManager->interruptStart(pInterfaces->int64InterruptPvt, &pclientList); + pnode = (interruptNode *)ellFirst(pclientList); + while (pnode) { + asynInt64Interrupt *pInterrupt = (asynInt64Interrupt *) pnode->drvPvt; + this->pasynPortDriver->getAddress(pInterrupt->pasynUser, &address); + /* If this is not a multi-device then address is -1, change to 0 */ + if (address == -1) address = 0; + if ((command == pInterrupt->pasynUser->reason) && + (address == addr)) { + /* Set the status for the callback */ + pInterrupt->pasynUser->auxStatus = status; + pInterrupt->pasynUser->alarmStatus = alarmStatus; + pInterrupt->pasynUser->alarmSeverity = alarmSeverity; + /* Set the timestamp for the callback */ + pInterrupt->pasynUser->timestamp = timeStamp; + pInterrupt->callback(pInterrupt->userPvt, + pInterrupt->pasynUser, + value); + } + pnode = (interruptNode *)ellNext(&pnode->node); + } + pasynManager->interruptEnd(pInterfaces->int64InterruptPvt); + return asynSuccess; +} + /** Calls the registered asyn callback functions for all clients for an UInt32 parameter */ asynStatus paramList::uint32Callback(int command, int addr, epicsUInt32 interruptMask) { @@ -756,6 +850,9 @@ asynStatus paramList::callCallbacks(int addr) case asynParamInt32: status = int32Callback(index, addr); break; + case asynParamInt64: + status = int64Callback(index, addr); + break; case asynParamUInt32Digital: status = uint32Callback(index, addr, this->vals[index]->uInt32CallbackMask); this->vals[index]->uInt32CallbackMask = 0; @@ -1171,6 +1268,30 @@ asynStatus asynPortDriver::setIntegerParam(int list, int index, int value) return status; } +/** Sets the value for a 64-bit integer in the parameter library. + * Calls setInteger64Param(0, index, value) i.e. for parameter list 0. + * \param[in] index The parameter number + * \param[in] value Value to set. */ +asynStatus asynPortDriver::setInteger64Param(int index, epicsInt64 value) +{ + return this->setInteger64Param(0, index, value); +} + +/** Sets the value for a 64-bit integer in the parameter library. + * Calls paramList::setInteger64 (index, value) for the parameter list indexed by list. + * \param[in] list The parameter list number. Must be < maxAddr passed to asynPortDriver::asynPortDriver. + * \param[in] index The parameter number + * \param[in] value Value to set. */ +asynStatus asynPortDriver::setInteger64Param(int list, int index, epicsInt64 value) +{ + asynStatus status; + static const char *functionName = "setInteger64Param"; + + status = this->params[list]->setInteger64(index, value); + if (status) reportSetParamErrors(status, index, list, functionName); + return status; +} + /** Sets the value for a UInt32Digital in the parameter library. * Calls setUIntDigitalParam(0, index, value, valueMask, 0) i.e. for parameter list 0. * \param[in] index The parameter number @@ -1204,7 +1325,7 @@ asynStatus asynPortDriver::setUIntDigitalParam(int index, epicsUInt32 value, epi } /** Sets the value for a UInt32Digital in the parameter library. - * Calls paramList::setInteger (index, value) for the parameter list indexed by list. + * Calls paramList::setUInt32 (index, value) for the parameter list indexed by list. * \param[in] list The parameter list number. Must be < maxAddr passed to asynPortDriver::asynPortDriver. * \param[in] index The parameter number * \param[in] value Value to set @@ -1423,6 +1544,30 @@ asynStatus asynPortDriver::getIntegerParam(int list, int index, epicsInt32 *valu return status; } +/** Returns the value for a 64-bit integer from the parameter library. + * Calls getInteger64Param(0, index, value) i.e. for parameter list 0. + * \param[in] index The parameter number + * \param[out] value Address of value to get. */ +asynStatus asynPortDriver::getInteger64Param(int index, epicsInt64 *value) +{ + return this->getInteger64Param(0, index, value); +} + +/** Returns the value for a 64-bit integer from the parameter library. + * Calls paramList::getInteger64 (index, value) for the parameter list indexed by list. + * \param[in] list The parameter list number. Must be < maxAddr passed to asynPortDriver::asynPortDriver. + * \param[in] index The parameter number + * \param[out] value Address of value to get. */ +asynStatus asynPortDriver::getInteger64Param(int list, int index, epicsInt64 *value) +{ + asynStatus status; + static const char *functionName = "getInteger64Param"; + + status = this->params[list]->getInteger64(index, value); + if (status) reportGetParamErrors(status, index, list, functionName); + return status; +} + /** Returns the value for an UInt32Digital parameter from the parameter library. * Calls getUIntDigitalParam(0, index, value, mask) i.e. for parameter list 0. * \param[in] index The parameter number @@ -1811,6 +1956,133 @@ asynStatus asynPortDriver::getBounds(asynUser *pasynUser, return asynSuccess; } +/* asynInt64 interface methods */ +extern "C" {static asynStatus readInt64(void *drvPvt, asynUser *pasynUser, + epicsInt64 *value) +{ + asynPortDriver *pPvt = (asynPortDriver *)drvPvt; + asynStatus status; + + pPvt->lock(); + status = pPvt->readInt64(pasynUser, value); + pPvt->unlock(); + return status; +}} + +/** Called when asyn clients call pasynInt64->read(). + * The base class implementation simply returns the value from the parameter library. + * Derived classes rarely need to reimplement this function. + * \param[in] pasynUser pasynUser structure that encodes the reason and address. + * \param[out] value Address of the value to read. */ +asynStatus asynPortDriver::readInt64(asynUser *pasynUser, epicsInt64 *value) +{ + int function; + const char *paramName; + int addr; + asynStatus status = asynSuccess; + epicsTimeStamp timeStamp; getTimeStamp(&timeStamp); + static const char *functionName = "readInt64"; + + status = parseAsynUser(pasynUser, &function, &addr, ¶mName); + if (status != asynSuccess) return status; + /* We just read the current value of the parameter from the parameter library. + * Those values are updated whenever anything could cause them to change */ + status = (asynStatus) getInteger64Param(addr, function, value); + /* Set the timestamp */ + pasynUser->timestamp = timeStamp; + getParamAlarmStatus(addr, function, &pasynUser->alarmStatus); + getParamAlarmSeverity(addr, function, &pasynUser->alarmSeverity); + if (status) + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "%s:%s: status=%d, function=%d, name=%s, value=%lld", + driverName, functionName, status, function, paramName, *value); + else + asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, + "%s:%s: function=%d, name=%s, value=%lld\n", + driverName, functionName, function, paramName, *value); + return status; +} + +extern "C" {static asynStatus writeInt64(void *drvPvt, asynUser *pasynUser, + epicsInt64 value) +{ + asynPortDriver *pPvt = (asynPortDriver *)drvPvt; + asynStatus status; + + pPvt->lock(); + status = pPvt->writeInt64(pasynUser, value); + pPvt->unlock(); + return status; +}} + +/** Called when asyn clients call pasynInt64->write(). + * The base class implementation simply sets the value in the parameter library and + * calls any registered callbacks for this pasynUser->reason and address. + * Derived classes will reimplement this function if they need to perform an action when an + * asynInt64 value is written. + * \param[in] pasynUser pasynUser structure that encodes the reason and address. + * \param[in] value Value to write. */ +asynStatus asynPortDriver::writeInt64(asynUser *pasynUser, epicsInt64 value) +{ + int function ; + const char *paramName; + int addr; + asynStatus status = asynSuccess; + const char* functionName = "writeInt64"; + + status = parseAsynUser(pasynUser, &function, &addr, ¶mName); + if (status != asynSuccess) return status; + + /* Set the parameter in the parameter library. */ + status = (asynStatus) setIntegerParam(addr, function, value); + + /* Do callbacks so higher layers see any changes */ + status = (asynStatus) callParamCallbacks(addr, addr); + + if (status) + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "%s:%s: status=%d, function=%d, name=%s, value=%lld", + driverName, functionName, status, function, paramName, value); + else + asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, + "%s:%s: function=%d, name=%s, value=%lld\n", + driverName, functionName, function, paramName, value); + return status; +} + +extern "C" {static asynStatus getBounds64(void *drvPvt, asynUser *pasynUser, + epicsInt64 *low, epicsInt64 *high) +{ + asynPortDriver *pPvt = (asynPortDriver *)drvPvt; + asynStatus status; + + pPvt->lock(); + status = pPvt->getBounds64(pasynUser, low, high); + pPvt->unlock(); + return status; +}} + +/** Called when asyn clients call pasynInt64->getBounds(), returning the bounds on the asynInt64 interface + * for drivers that use raw units. + * Device support uses these values for unit conversion. + * The base class implementation simply returns low=0, high=65535. + * Derived classes can reimplement this function if they support raw units with different limits. + * \param[in] pasynUser pasynUser structure that encodes the reason and address. + * \param[out] low Address of the low limit. + * \param[out] high Address of the high limit. */ +asynStatus asynPortDriver::getBounds64(asynUser *pasynUser, + epicsInt64 *low, epicsInt64 *high) +{ + /* This is only needed for the asynInt64 interface when the device uses raw units. + Our interface is using engineering units. */ + *low = 0; + *high = 65535; + asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, + "%s::getBounds64,low=%lld, high=%lld\n", + driverName, *low, *high); + return asynSuccess; +} + /* asynUInt32Digital interface methods */ extern "C" {static asynStatus readUInt32Digital(void *drvPvt, asynUser *pasynUser, epicsUInt32 *value, epicsUInt32 mask) @@ -2578,6 +2850,69 @@ asynStatus asynPortDriver::doCallbacksInt32Array(epicsInt32 *value, this->asynStdInterfaces.int32ArrayInterruptPvt); } + +/* asynInt64Array interface methods */ +extern "C" {static asynStatus readInt64Array(void *drvPvt, asynUser *pasynUser, epicsInt64 *value, + size_t nElements, size_t *nIn) +{ + asynPortDriver *pPvt = (asynPortDriver *)drvPvt; + asynStatus status; + + pPvt->lock(); + status = pPvt->readInt64Array(pasynUser, value, nElements, nIn); + pPvt->unlock(); + return status; +}} + +/** Called when asyn clients call pasynInt64Array->read(). + * The base class implementation simply prints an error message. + * Derived classes may reimplement this function if required. + * \param[in] pasynUser pasynUser structure that encodes the reason and address. + * \param[in] value Pointer to the array to read. + * \param[in] nElements Number of elements to read. + * \param[out] nIn Number of elements actually read. */ +asynStatus asynPortDriver::readInt64Array(asynUser *pasynUser, epicsInt64 *value, + size_t nElements, size_t *nIn) +{ + return readArray(pasynUser, value, nElements, nIn); +} + +extern "C" {static asynStatus writeInt64Array(void *drvPvt, asynUser *pasynUser, epicsInt64 *value, + size_t nElements) +{ + asynPortDriver *pPvt = (asynPortDriver *)drvPvt; + asynStatus status; + + pPvt->lock(); + status = pPvt->writeInt64Array(pasynUser, value, nElements); + pPvt->unlock(); + return status; +}} + +/** Called when asyn clients call pasynInt64Array->write(). + * The base class implementation simply prints an error message. + * Derived classes may reimplement this function if required. + * \param[in] pasynUser pasynUser structure that encodes the reason and address. + * \param[in] value Pointer to the array to write. + * \param[in] nElements Number of elements to write. */ +asynStatus asynPortDriver::writeInt64Array(asynUser *pasynUser, epicsInt64 *value, + size_t nElements) +{ + return writeArray(pasynUser, value, nElements); +} + +/** Called by driver to do the callbacks to registered clients on the asynInt64Array interface. + * \param[in] value Address of the array. + * \param[in] nElements Number of elements in the array. + * \param[in] reason A client will be called if reason matches pasynUser->reason registered for that client. + * \param[in] addr A client will be called if addr matches the asyn address registered for that client. */ +asynStatus asynPortDriver::doCallbacksInt64Array(epicsInt64 *value, + size_t nElements, int reason, int addr) +{ + return doCallbacksArray(value, nElements, reason, addr, + this->asynStdInterfaces.int64ArrayInterruptPvt); +} + /* asynFloat32Array interface methods */ extern "C" {static asynStatus readFloat32Array(void *drvPvt, asynUser *pasynUser, epicsFloat32 *value, @@ -3083,6 +3418,7 @@ void asynPortDriver::report(FILE *fp, int details) if (details >= 3) { /* Report interrupt clients */ reportInterrupt (fp, pInterfaces->int32InterruptPvt, "int32"); + reportInterrupt (fp, pInterfaces->int64InterruptPvt, "int64"); reportInterrupt (fp, pInterfaces->uInt32DigitalInterruptPvt,"uint32"); reportInterrupt (fp, pInterfaces->float64InterruptPvt, "float64"); reportInterrupt (fp, pInterfaces->octetInterruptPvt, "octet"); @@ -3205,6 +3541,12 @@ static asynInt32 ifaceInt32 = { getBounds }; +static asynInt64 ifaceInt64 = { + writeInt64, + readInt64, + getBounds64 +}; + static asynUInt32Digital ifaceUInt32Digital = { writeUInt32Digital, readUInt32Digital, @@ -3245,6 +3587,11 @@ static asynInt32Array ifaceInt32Array = { readInt32Array }; +static asynInt64Array ifaceInt64Array = { + writeInt64Array, + readInt64Array +}; + static asynFloat32Array ifaceFloat32Array = { writeFloat32Array, readFloat32Array @@ -3390,12 +3737,14 @@ void asynPortDriver::initialize(const char *portNameIn, int maxAddrIn, int inter if (interfaceMask & asynCommonMask) pInterfaces->common.pinterface = (void *)&ifaceCommon; if (interfaceMask & asynDrvUserMask) pInterfaces->drvUser.pinterface = (void *)&ifaceDrvUser; if (interfaceMask & asynInt32Mask) pInterfaces->int32.pinterface = (void *)&ifaceInt32; + if (interfaceMask & asynInt64Mask) pInterfaces->int64.pinterface = (void *)&ifaceInt64; if (interfaceMask & asynUInt32DigitalMask) pInterfaces->uInt32Digital.pinterface = (void *)&ifaceUInt32Digital; if (interfaceMask & asynFloat64Mask) pInterfaces->float64.pinterface = (void *)&ifaceFloat64; if (interfaceMask & asynOctetMask) pInterfaces->octet.pinterface = (void *)&ifaceOctet; if (interfaceMask & asynInt8ArrayMask) pInterfaces->int8Array.pinterface = (void *)&ifaceInt8Array; if (interfaceMask & asynInt16ArrayMask) pInterfaces->int16Array.pinterface = (void *)&ifaceInt16Array; if (interfaceMask & asynInt32ArrayMask) pInterfaces->int32Array.pinterface = (void *)&ifaceInt32Array; + if (interfaceMask & asynInt64ArrayMask) pInterfaces->int64Array.pinterface = (void *)&ifaceInt64Array; if (interfaceMask & asynFloat32ArrayMask) pInterfaces->float32Array.pinterface = (void *)&ifaceFloat32Array; if (interfaceMask & asynFloat64ArrayMask) pInterfaces->float64Array.pinterface = (void *)&ifaceFloat64Array; if (interfaceMask & asynGenericPointerMask) pInterfaces->genericPointer.pinterface= (void *)&ifaceGenericPointer; @@ -3404,12 +3753,14 @@ void asynPortDriver::initialize(const char *portNameIn, int maxAddrIn, int inter /* Define which interfaces can generate interrupts */ if (interruptMask & asynInt32Mask) pInterfaces->int32CanInterrupt = 1; + if (interruptMask & asynInt64Mask) pInterfaces->int64CanInterrupt = 1; if (interruptMask & asynUInt32DigitalMask) pInterfaces->uInt32DigitalCanInterrupt = 1; if (interruptMask & asynFloat64Mask) pInterfaces->float64CanInterrupt = 1; if (interruptMask & asynOctetMask) pInterfaces->octetCanInterrupt = 1; if (interruptMask & asynInt8ArrayMask) pInterfaces->int8ArrayCanInterrupt = 1; if (interruptMask & asynInt16ArrayMask) pInterfaces->int16ArrayCanInterrupt = 1; if (interruptMask & asynInt32ArrayMask) pInterfaces->int32ArrayCanInterrupt = 1; + if (interruptMask & asynInt64ArrayMask) pInterfaces->int64ArrayCanInterrupt = 1; if (interruptMask & asynFloat32ArrayMask) pInterfaces->float32ArrayCanInterrupt = 1; if (interruptMask & asynFloat64ArrayMask) pInterfaces->float64ArrayCanInterrupt = 1; if (interruptMask & asynGenericPointerMask) pInterfaces->genericPointerCanInterrupt = 1; diff --git a/asyn/asynPortDriver/asynPortDriver.h b/asyn/asynPortDriver/asynPortDriver.h index 52bbdc0..9370b2d 100755 --- a/asyn/asynPortDriver/asynPortDriver.h +++ b/asyn/asynPortDriver/asynPortDriver.h @@ -34,6 +34,8 @@ typedef void (*userTimeStampFunction)(void *userPvt, epicsTimeStamp *pTimeStamp) #define asynFloat64ArrayMask 0x00000800 #define asynGenericPointerMask 0x00001000 #define asynEnumMask 0x00002000 +#define asynInt64Mask 0x00004000 +#define asynInt64ArrayMask 0x00008000 class callbackThread; @@ -52,12 +54,15 @@ class epicsShareClass asynPortDriver { virtual asynStatus parseAsynUser(asynUser *pasynUser, int *reason, int *address, const char **paramName); virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value); virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value); + virtual asynStatus readInt64(asynUser *pasynUser, epicsInt64 *value); + virtual asynStatus writeInt64(asynUser *pasynUser, epicsInt64 value); virtual asynStatus readUInt32Digital(asynUser *pasynUser, epicsUInt32 *value, epicsUInt32 mask); virtual asynStatus writeUInt32Digital(asynUser *pasynUser, epicsUInt32 value, epicsUInt32 mask); virtual asynStatus setInterruptUInt32Digital(asynUser *pasynUser, epicsUInt32 mask, interruptReason reason); virtual asynStatus clearInterruptUInt32Digital(asynUser *pasynUser, epicsUInt32 mask); virtual asynStatus getInterruptUInt32Digital(asynUser *pasynUser, epicsUInt32 *mask, interruptReason reason); virtual asynStatus getBounds(asynUser *pasynUser, epicsInt32 *low, epicsInt32 *high); + virtual asynStatus getBounds64(asynUser *pasynUser, epicsInt64 *low, epicsInt64 *high); virtual asynStatus readFloat64(asynUser *pasynUser, epicsFloat64 *value); virtual asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value); virtual asynStatus readOctet(asynUser *pasynUser, char *value, size_t maxChars, @@ -87,6 +92,12 @@ class epicsShareClass asynPortDriver { size_t nElements); virtual asynStatus doCallbacksInt32Array(epicsInt32 *value, size_t nElements, int reason, int addr); + virtual asynStatus readInt64Array(asynUser *pasynUser, epicsInt64 *value, + size_t nElements, size_t *nIn); + virtual asynStatus writeInt64Array(asynUser *pasynUser, epicsInt64 *value, + size_t nElements); + virtual asynStatus doCallbacksInt64Array(epicsInt64 *value, + size_t nElements, int reason, int addr); virtual asynStatus readFloat32Array(asynUser *pasynUser, epicsFloat32 *value, size_t nElements, size_t *nIn); virtual asynStatus writeFloat32Array(asynUser *pasynUser, epicsFloat32 *value, @@ -142,6 +153,8 @@ class epicsShareClass asynPortDriver { virtual void reportGetParamErrors(asynStatus status, int index, int list, const char *functionName); virtual asynStatus setIntegerParam( int index, int value); virtual asynStatus setIntegerParam(int list, int index, int value); + virtual asynStatus setInteger64Param( int index, epicsInt64 value); + virtual asynStatus setInteger64Param(int list, int index, epicsInt64 value); virtual asynStatus setUIntDigitalParam( int index, epicsUInt32 value, epicsUInt32 valueMask); virtual asynStatus setUIntDigitalParam(int list, int index, epicsUInt32 value, epicsUInt32 valueMask); virtual asynStatus setUIntDigitalParam( int index, epicsUInt32 value, epicsUInt32 valueMask, epicsUInt32 interruptMask); @@ -160,6 +173,8 @@ class epicsShareClass asynPortDriver { virtual asynStatus setStringParam(int list, int index, const std::string& value); virtual asynStatus getIntegerParam( int index, epicsInt32 * value); virtual asynStatus getIntegerParam(int list, int index, epicsInt32 * value); + virtual asynStatus getInteger64Param( int index, epicsInt64 * value); + virtual asynStatus getInteger64Param(int list, int index, epicsInt64 * value); virtual asynStatus getUIntDigitalParam( int index, epicsUInt32 *value, epicsUInt32 mask); virtual asynStatus getUIntDigitalParam(int list, int index, epicsUInt32 *value, epicsUInt32 mask); virtual asynStatus getDoubleParam( int index, double * value); diff --git a/asyn/asynPortDriver/paramVal.cpp b/asyn/asynPortDriver/paramVal.cpp index 25312c9..05d22c7 100644 --- a/asyn/asynPortDriver/paramVal.cpp +++ b/asyn/asynPortDriver/paramVal.cpp @@ -16,12 +16,14 @@ const char* paramVal::typeNames[] = { "asynParamTypeUndefined", "asynParamInt32", + "asynParamInt64", "asynParamUInt32Digital", "asynParamFloat64", "asynParamOctet", "asynParamInt8Array", "asynParamInt16Array", "asynParamInt32Array", + "asynParamInt64Array", "asynParamFloat32Array", "asynParamFloat64Array", "asynParamGenericPointer" @@ -151,6 +153,35 @@ epicsInt32 paramVal::getInteger() return data.ival; } +/** Sets the value for a 64-bit integer. + * \param[in] value Value to set. + * \throws ParamValWrongType if type is not asynParamInt64 + */ +void paramVal::setInteger64(epicsInt64 value) +{ + if (type != asynParamInt64) + throw ParamValWrongType("paramVal::setInteger64 can only handle asynParamInt64"); + if (!isDefined() || (data.ival != value)) + { + setDefined(true); + data.i64val = value; + setValueChanged(); + } +} + +/** Gets the value for a 64-bit integer in the parameter library. + * \throws ParamValWrongType if type is not asynParamInt64 + * \throws paramValNotDefined if the value is not defined + */ +epicsInt64 paramVal::getInteger64() +{ + if (type != asynParamInt64) + throw ParamValWrongType("paramVal::getInteger64 can only handle asynParamInt64"); + if (!isDefined()) + throw ParamValNotDefined("paramVal::getInteger64 value not defined"); + return data.i64val; +} + /** Sets the value for a UInt32 in the parameter library. * \param[in] value Value to set. * \param[in] valueMask Mask to use when setting the value. @@ -258,6 +289,12 @@ void paramVal::report(int id, FILE *fp, int details) else fprintf(fp, "Parameter %d type=asynInt32, name=%s, value is undefined\n", id, getName()); break; + case asynParamInt64: + if (isDefined()) + fprintf(fp, "Parameter %d type=asynInt64, name=%s, value=%lld, status=%d\n", id, getName(), getInteger64(), getStatus()); + else + fprintf(fp, "Parameter %d type=asynInt64, name=%s, value is undefined\n", id, getName()); + break; case asynParamUInt32Digital: if (isDefined()) fprintf(fp, "Parameter %d type=asynUInt32Digital, name=%s, value=0x%x, status=%d, risingMask=0x%x, fallingMask=0x%x, callbackMask=0x%x\n", @@ -268,7 +305,7 @@ void paramVal::report(int id, FILE *fp, int details) break; case asynParamFloat64: if (isDefined()) - fprintf(fp, "Parameter %d type=asynFloat64, name=%s, value=%f, status=%d\n", id, getName(), getDouble(), getStatus()); + fprintf(fp, "Parameter %d type=asynFloat64, name=%s, value=%g, status=%d\n", id, getName(), getDouble(), getStatus()); else fprintf(fp, "Parameter %d type=asynFloat64, name=%s, value is undefined\n", id, getName()); break; @@ -296,6 +333,12 @@ void paramVal::report(int id, FILE *fp, int details) else fprintf(fp, "Parameter %d type=asynInt32Array, name=%s, value is undefined\n", id, getName()); break; + case asynParamInt64Array: + if (isDefined()) + fprintf(fp, "Parameter %d type=asynInt64Array, name=%s, value=%p, status=%d\n", id, getName(), data.pi64, getStatus() ); + else + fprintf(fp, "Parameter %d type=asynInt64Array, name=%s, value is undefined\n", id, getName()); + break; case asynParamFloat32Array: if (isDefined()) fprintf(fp, "Parameter %d type=asynFloat32Array, name=%s, value=%p, status=%d\n", id, getName(), data.pf32, getStatus() ); diff --git a/asyn/asynPortDriver/paramVal.h b/asyn/asynPortDriver/paramVal.h index 6ce1725..c7352d0 100644 --- a/asyn/asynPortDriver/paramVal.h +++ b/asyn/asynPortDriver/paramVal.h @@ -30,6 +30,8 @@ class paramVal { bool nameEquals(const char* name); void setInteger(epicsInt32 value); epicsInt32 getInteger(); + void setInteger64(epicsInt64 value); + epicsInt64 getInteger64(); void setUInt32(epicsUInt32 value, epicsUInt32 valueMask, epicsUInt32 interruptMask); epicsUInt32 getUInt32(epicsUInt32 valueMask); void setDouble(epicsFloat64 value); @@ -56,11 +58,13 @@ class paramVal { union { epicsInt32 ival; + epicsInt64 i64val; epicsUInt32 uival; epicsFloat64 dval; epicsInt8 *pi8; epicsInt16 *pi16; epicsInt32 *pi32; + epicsInt64 *pi64; epicsFloat32 *pf32; epicsFloat64 *pf64; void *pgp; diff --git a/asyn/asynPortDriver/unittest/asynPortDriverTest.cpp b/asyn/asynPortDriver/unittest/asynPortDriverTest.cpp index 4744654..b1a2afd 100644 --- a/asyn/asynPortDriver/unittest/asynPortDriverTest.cpp +++ b/asyn/asynPortDriver/unittest/asynPortDriverTest.cpp @@ -81,6 +81,7 @@ void testA() testOk1(portA->createParam(0, "int32", asynParamInt32, &idx2)==asynError); + testOk1(portA->createParam(0, "int64", asynParamInt64, &idx1)==asynSuccess); testOk1(portA->createParam(0, "float64", asynParamFloat64, &idx1)==asynSuccess); testOk1(portA->createParam(0, "uint32", asynParamUInt32Digital, &idx1)==asynSuccess); testOk1(portA->createParam(0, "y", asynParamInt32, &idx1)==asynSuccess); @@ -160,7 +161,7 @@ void testA() MAIN(asynPortDriverTest) { - testPlan(53); + testPlan(54); interruptAccept=1; try { testA(); diff --git a/asyn/asynRecord/asynRecord.c b/asyn/asynRecord/asynRecord.c index a496460..ea7af92 100644 --- a/asyn/asynRecord/asynRecord.c +++ b/asyn/asynRecord/asynRecord.c @@ -72,8 +72,8 @@ static char *drto_choices[NUM_DRTO_CHOICES] = {"Unknown", "N", "Y"}; /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL -static long init_record(asynRecord * pasynRec, int pass); -static long process(asynRecord * pasynRec); +static long init_record(dbCommon * pasynRec, int pass); +static long process(dbCommon * pasynRec); static long special(struct dbAddr * paddr, int after); #define get_value NULL static long cvt_dbaddr(struct dbAddr * paddr); @@ -81,7 +81,7 @@ static long get_array_info(struct dbAddr * paddr, long *no_elements, long *offset); static long put_array_info(struct dbAddr * paddr, long nNew); #define get_units NULL -static long get_precision(struct dbAddr * paddr, long *precision); +static long get_precision(const struct dbAddr * paddr, long *precision); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL @@ -122,7 +122,7 @@ static void getEos(asynUser * pasynUser); static void reportError(asynRecord * pasynRec, asynStatus status, const char *pformat,...); static void resetError(asynRecord * pasynRec); -struct rset asynRSET = { +rset asynRSET = { RSETNUMBER, report, initialize, @@ -267,8 +267,9 @@ asynRecordDset asynRecordDevice = { epicsExportAddress(dset, asynRecordDevice); -static long init_record(asynRecord * pasynRec, int pass) +static long init_record(dbCommon *pRec, int pass) { + asynRecord *pasynRec = (asynRecord *)pRec; asynRecPvt *pasynRecPvt; asynUser *pasynUser; asynStatus status; @@ -317,8 +318,9 @@ static long init_record(asynRecord * pasynRec, int pass) return (0); } -static long process(asynRecord * pasynRec) +static long process(dbCommon *pRec) { + asynRecord *pasynRec = (asynRecord *)pRec; asynRecPvt *pasynRecPvt = pasynRec->dpvt; callbackState state = pasynRecPvt->state; asynStatus status; @@ -560,6 +562,11 @@ static long special(struct dbAddr * paddr, int after) } else { priority = asynQueuePriorityLow; } + if (fieldIndex == asynRecordHOSTINFO) { + /* Enable changing host:port when not connected */ + priority = asynQueuePriorityConnect; + pasynUserSpecial->reason = ASYN_REASON_QUEUE_EVEN_IF_NOT_CONNECTED; + } status = pasynManager->queueRequest(pasynUserSpecial, priority,QUEUE_TIMEOUT); if(status!=asynSuccess) { @@ -986,7 +993,7 @@ static long put_array_info(struct dbAddr * paddr, long nNew) } return (0); } -static long get_precision(struct dbAddr * paddr, long *precision) +static long get_precision(const struct dbAddr * paddr, long *precision) { int fieldIndex = dbGetFieldIndex(paddr); *precision = 0; @@ -1269,10 +1276,11 @@ static asynStatus connectDevice(asynRecord * pasynRec) pasynUserConnect = pasynManager->duplicateAsynUser(pasynUser, asynCallbackSpecial, queueTimeoutCallbackSpecial); pasynUserConnect->userData = pasynManager->memMalloc(sizeof(*pmsg)); + pasynUserConnect->reason = ASYN_REASON_QUEUE_EVEN_IF_NOT_CONNECTED; pmsg = (callbackMessage *)pasynUserConnect->userData; pmsg->callbackType = callbackGetOption; status = pasynManager->queueRequest(pasynUserConnect, - asynQueuePriorityLow,QUEUE_TIMEOUT); + asynQueuePriorityConnect,QUEUE_TIMEOUT); if(status!=asynSuccess) { reportError(pasynRec, asynError, "queueRequest failed\n"); diff --git a/asyn/asynRecord/drvAsyn.c b/asyn/asynRecord/drvAsyn.c index d14ccc9..90e1b6a 100644 --- a/asyn/asynRecord/drvAsyn.c +++ b/asyn/asynRecord/drvAsyn.c @@ -33,11 +33,8 @@ /* add support so that dbior generates asynDriver reports */ static long drvAsynReport(int level); -struct { - long number; - DRVSUPFUN report; - DRVSUPFUN init; -} drvAsyn={ + +drvet drvAsyn={ 2, drvAsynReport, 0 diff --git a/asyn/devEpics/devAsynFloat64.c b/asyn/devEpics/devAsynFloat64.c index 2333ac8..39c3dc1 100644 --- a/asyn/devEpics/devAsynFloat64.c +++ b/asyn/devEpics/devAsynFloat64.c @@ -74,6 +74,7 @@ typedef struct devPvt{ int ringSize; int ringBufferOverflows; ringBufferElement result; + asynStatus lastStatus; epicsFloat64 sum; interruptCallbackFloat64 interruptCallback; int numAverage; @@ -331,12 +332,16 @@ static void processCallbackInput(asynUser *pasynUser) pPvt->result.alarmSeverity = pPvt->pasynUser->alarmSeverity; if (pPvt->result.status == asynSuccess) { asynPrint(pasynUser, ASYN_TRACEIO_DEVICE, - "%s %s::%s process value=%f\n",pr->name, driverName, functionName,pPvt->result.value); + "%s %s::%s process value=%f\n", pr->name, driverName, functionName, + pPvt->result.value); } else { - asynPrint(pasynUser, ASYN_TRACE_ERROR, - "%s %s::%s process read error %s\n", - pr->name, driverName, functionName,pasynUser->errorMessage); + if (pPvt->result.status != pPvt->lastStatus) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s %s::%s process read error %s\n", + pr->name, driverName, functionName, pasynUser->errorMessage); + } } + pPvt->lastStatus = pPvt->result.status; if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr); } @@ -352,12 +357,16 @@ static void processCallbackOutput(asynUser *pasynUser) pPvt->result.alarmSeverity = pPvt->pasynUser->alarmSeverity; if(pPvt->result.status == asynSuccess) { asynPrint(pasynUser, ASYN_TRACEIO_DEVICE, - "%s %s::%s process val %f\n",pr->name, driverName, functionName,pPvt->result.value); + "%s %s::%s process value %f\n", pr->name, driverName, functionName, pPvt->result.value); } else { - asynPrint(pasynUser, ASYN_TRACE_ERROR, - "%s %s::%s pPvt->result.status=%d, process error %s\n", - pr->name, driverName, functionName,pPvt->result.status, pasynUser->errorMessage); + if (pPvt->result.status != pPvt->lastStatus) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s %s::%s process write error %s\n", + pr->name, driverName, functionName, pasynUser->errorMessage); + } } + pPvt->lastStatus = pPvt->result.status; + pPvt->lastStatus = pPvt->result.status; if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr); } diff --git a/asyn/devEpics/devAsynInt32.c b/asyn/devEpics/devAsynInt32.c index 8642948..f0dc3ca 100644 --- a/asyn/devEpics/devAsynInt32.c +++ b/asyn/devEpics/devAsynInt32.c @@ -92,6 +92,7 @@ typedef struct devPvt{ int ringSize; int ringBufferOverflows; ringBufferElement result; + asynStatus lastStatus; interruptCallbackInt32 interruptCallback; double sum; int numAverage; @@ -500,12 +501,16 @@ static void processCallbackInput(asynUser *pasynUser) } if (pPvt->result.status == asynSuccess) { asynPrint(pasynUser, ASYN_TRACEIO_DEVICE, - "%s %s::%s process value=%d\n",pr->name, driverName, functionName,pPvt->result.value); + "%s %s::%s process value=%d\n", pr->name, driverName, functionName, + pPvt->result.value); } else { - asynPrint(pasynUser, ASYN_TRACE_ERROR, - "%s %s::%s process read error %s\n", - pr->name, driverName, functionName, pasynUser->errorMessage); + if (pPvt->result.status != pPvt->lastStatus) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s %s::%s process read error %s\n", + pr->name, driverName, functionName, pasynUser->errorMessage); + } } + pPvt->lastStatus = pPvt->result.status; if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr); } @@ -519,14 +524,18 @@ static void processCallbackOutput(asynUser *pasynUser) pPvt->result.time = pPvt->pasynUser->timestamp; pPvt->result.alarmStatus = pPvt->pasynUser->alarmStatus; pPvt->result.alarmSeverity = pPvt->pasynUser->alarmSeverity; - if(pPvt->result.status == asynSuccess) { + if (pPvt->result.status == asynSuccess) { asynPrint(pasynUser, ASYN_TRACEIO_DEVICE, - "%s %s::%s process value %d\n",pr->name, driverName, functionName,pPvt->result.value); + "%s %s::%s process value %d\n", pr->name, driverName, functionName, + pPvt->result.value); } else { - asynPrint(pasynUser, ASYN_TRACE_ERROR, - "%s %s::%s process error %s\n", - pr->name, driverName, functionName, pasynUser->errorMessage); + if (pPvt->result.status != pPvt->lastStatus) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s %s::%s process write error %s\n", + pr->name, driverName, functionName, pasynUser->errorMessage); + } } + pPvt->lastStatus = pPvt->result.status; if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr); } diff --git a/asyn/devEpics/devAsynInt64.c b/asyn/devEpics/devAsynInt64.c new file mode 100644 index 0000000..4389a25 --- /dev/null +++ b/asyn/devEpics/devAsynInt64.c @@ -0,0 +1,602 @@ +/* devAsynInt64.c */ +/*********************************************************************** +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory, and the Regents of the University of +* California, as Operator of Los Alamos National Laboratory, and +* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY). +* asynDriver is distributed subject to a Software License Agreement +* found in file LICENSE that is included with this distribution. +***********************************************************************/ +/* + Authors: Mark Rivers and Marty Kraimer + 05-Sept-2004 +*/ + +#include +#include +#include +#include + +#include +#include +#include "epicsMath.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "asynDriver.h" +#include "asynDrvUser.h" +#include "asynInt64.h" +#include "asynInt64SyncIO.h" +#include "asynEpicsUtils.h" + +#define INIT_OK 0 +#define INIT_ERROR -1 + +#define DEFAULT_RING_BUFFER_SIZE 10 + +static const char *driverName = "devAsynInt64"; + +typedef struct ringBufferElement { + epicsInt64 value; + epicsTimeStamp time; + asynStatus status; + epicsAlarmCondition alarmStatus; + epicsAlarmSeverity alarmSeverity; +} ringBufferElement; + +typedef struct devPvt{ + dbCommon *pr; + asynUser *pasynUser; + asynUser *pasynUserSync; + asynInt64 *pint64; + void *int64Pvt; + void *registrarPvt; + int canBlock; + epicsInt64 deviceLow; + epicsInt64 deviceHigh; + epicsMutexId devPvtLock; + ringBufferElement *ringBuffer; + int ringHead; + int ringTail; + int ringSize; + int ringBufferOverflows; + ringBufferElement result; + asynStatus lastStatus; + interruptCallbackInt64 interruptCallback; + int asyncProcessingActive; + CALLBACK processCallback; + CALLBACK outputCallback; + int newOutputCallbackValue; + int numDeferredOutputCallbacks; + IOSCANPVT ioScanPvt; + char *portName; + char *userParam; + int addr; + asynStatus previousQueueRequestStatus; +}devPvt; + +static long getIoIntInfo(int cmd, dbCommon *pr, IOSCANPVT *iopvt); +static long createRingBuffer(dbCommon *pr); +static void processCallbackInput(asynUser *pasynUser); +static void processCallbackOutput(asynUser *pasynUser); +static void outputCallbackCallback(CALLBACK *pcb); +static int getCallbackValue(devPvt *pPvt); +static void interruptCallbackInput(void *drvPvt, asynUser *pasynUser, + epicsInt64 value); +static void interruptCallbackOutput(void *drvPvt, asynUser *pasynUser, + epicsInt64 value); + +static long initLLi(int64inRecord *pli); +static long initLLo(int64outRecord *plo); +static long processLLi(int64inRecord *pr); +static long processLLo(int64outRecord *pr); + +typedef struct analogDset { /* analog dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN processCommon;/*(0)=>(success ) */ + DEVSUPFUN special_linconv; +} analogDset; + +analogDset asynInt64In = { + 5,0,0,initLLi, getIoIntInfo, processLLi }; +analogDset asynInt64Out = { + 5,0,0,initLLo, getIoIntInfo, processLLo }; + +epicsExportAddress(dset, asynInt64In); +epicsExportAddress(dset, asynInt64Out); + +static long initCommon(dbCommon *pr, DBLINK *plink, + userCallback processCallback,interruptCallbackInt64 interruptCallback) +{ + devPvt *pPvt; + asynStatus status; + asynUser *pasynUser; + asynInterface *pasynInterface; + static const char *functionName="initCommon"; + + pPvt = callocMustSucceed(1, sizeof(*pPvt), "devAsynInt64::initCommon"); + pr->dpvt = pPvt; + pPvt->pr = pr; + /* Create asynUser */ + pasynUser = pasynManager->createAsynUser(processCallback, 0); + pasynUser->userPvt = pPvt; + pPvt->pasynUser = pasynUser; + pPvt->devPvtLock = epicsMutexCreate(); + + /* Parse the link to get addr and port */ + status = pasynEpicsUtils->parseLink(pasynUser, plink, + &pPvt->portName, &pPvt->addr, &pPvt->userParam); + if (status != asynSuccess) { + printf("%s %s::%s %s\n", + pr->name, driverName, functionName, pasynUser->errorMessage); + goto bad; + } + + /* Connect to device */ + status = pasynManager->connectDevice(pasynUser, pPvt->portName, pPvt->addr); + if (status != asynSuccess) { + printf("%s %s::%s connectDevice failed %s\n", + pr->name, driverName, functionName, pasynUser->errorMessage); + goto bad; + } + status = pasynManager->canBlock(pPvt->pasynUser, &pPvt->canBlock); + if (status != asynSuccess) { + printf("%s %s::%s canBlock failed %s\n", + pr->name, driverName, functionName, pasynUser->errorMessage); + goto bad; + } + /*call drvUserCreate*/ + pasynInterface = pasynManager->findInterface(pasynUser,asynDrvUserType,1); + if(pasynInterface && pPvt->userParam) { + asynDrvUser *pasynDrvUser; + void *drvPvt; + + pasynDrvUser = (asynDrvUser *)pasynInterface->pinterface; + drvPvt = pasynInterface->drvPvt; + status = pasynDrvUser->create(drvPvt,pasynUser,pPvt->userParam,0,0); + if(status!=asynSuccess) { + printf("%s %s::%s drvUserCreate %s\n", + pr->name, driverName, functionName, pasynUser->errorMessage); + goto bad; + } + } + /* Get interface asynInt64 */ + pasynInterface = pasynManager->findInterface(pasynUser, asynInt64Type, 1); + if (!pasynInterface) { + printf("%s %s::%s findInterface asynInt64Type %s\n", + pr->name, driverName, functionName,pasynUser->errorMessage); + goto bad; + } + pPvt->pint64 = pasynInterface->pinterface; + pPvt->int64Pvt = pasynInterface->drvPvt; + scanIoInit(&pPvt->ioScanPvt); + pPvt->interruptCallback = interruptCallback; + /* Initialize synchronous interface */ + status = pasynInt64SyncIO->connect(pPvt->portName, pPvt->addr, + &pPvt->pasynUserSync, pPvt->userParam); + if (status != asynSuccess) { + printf("%s %s::%s Int64SyncIO->connect failed %s\n", + pr->name, driverName, functionName, pPvt->pasynUserSync->errorMessage); + goto bad; + } + /* If the info field "asyn:READBACK" is 1 and interruptCallback is not NULL + * then register for callbacks on output records */ + if (interruptCallback) { + int enableCallbacks=0; + const char *callbackString; + DBENTRY *pdbentry = dbAllocEntry(pdbbase); + status = dbFindRecord(pdbentry, pr->name); + if (status) { + asynPrint(pPvt->pasynUser, ASYN_TRACE_ERROR, + "%s %s::%s error finding record\n", + pr->name, driverName, functionName); + goto bad; + } + callbackString = dbGetInfo(pdbentry, "asyn:READBACK"); + if (callbackString) enableCallbacks = atoi(callbackString); + if (enableCallbacks) { + status = createRingBuffer(pr); + if (status!=asynSuccess) goto bad; + status = pPvt->pint64->registerInterruptUser( + pPvt->int64Pvt,pPvt->pasynUser, + pPvt->interruptCallback,pPvt,&pPvt->registrarPvt); + if (status!=asynSuccess) { + printf("%s %s::%s error calling registerInterruptUser %s\n", + pr->name, driverName, functionName,pPvt->pasynUser->errorMessage); + } + /* Initialize the interrupt callback */ + callbackSetCallback(outputCallbackCallback, &pPvt->outputCallback); + callbackSetPriority(pr->prio, &pPvt->outputCallback); + callbackSetUser(pPvt, &pPvt->outputCallback); + } + } + return INIT_OK; +bad: + recGblSetSevr(pr,LINK_ALARM,INVALID_ALARM); + pr->pact=1; + return INIT_ERROR; +} + +static long createRingBuffer(dbCommon *pr) +{ + devPvt *pPvt = (devPvt *)pr->dpvt; + asynStatus status; + const char *sizeString; + static const char *functionName="createRingBuffer"; + + if (!pPvt->ringBuffer) { + DBENTRY *pdbentry = dbAllocEntry(pdbbase); + pPvt->ringSize = DEFAULT_RING_BUFFER_SIZE; + status = dbFindRecord(pdbentry, pr->name); + if (status) { + asynPrint(pPvt->pasynUser, ASYN_TRACE_ERROR, + "%s %s::%s error finding record\n", + pr->name, driverName, functionName); + return -1; + } + sizeString = dbGetInfo(pdbentry, "asyn:FIFO"); + if (sizeString) pPvt->ringSize = atoi(sizeString); + pPvt->ringBuffer = callocMustSucceed(pPvt->ringSize+1, sizeof *pPvt->ringBuffer, "devAsynInt64::createRingBuffer"); + } + return asynSuccess; +} + +static long getIoIntInfo(int cmd, dbCommon *pr, IOSCANPVT *iopvt) +{ + devPvt *pPvt = (devPvt *)pr->dpvt; + asynStatus status; + static const char *functionName="getIoIntInfo"; + + /* If initCommon failed then pPvt->pint64 is NULL, return error */ + if (!pPvt->pint64) return -1; + + if (cmd == 0) { + /* Add to scan list. Register interrupts */ + asynPrint(pPvt->pasynUser, ASYN_TRACE_FLOW, + "%s %s::%s registering interrupt\n", + pr->name, driverName, functionName); + status = createRingBuffer(pr); + status = pPvt->pint64->registerInterruptUser( + pPvt->int64Pvt,pPvt->pasynUser, + pPvt->interruptCallback,pPvt,&pPvt->registrarPvt); + if(status!=asynSuccess) { + printf("%s %s::%s registerInterruptUser %s\n", + pr->name, driverName, functionName,pPvt->pasynUser->errorMessage); + } + } else { + asynPrint(pPvt->pasynUser, ASYN_TRACE_FLOW, + "%s %s::%s canceling interrupt\n", + pr->name, driverName, functionName); + status = pPvt->pint64->cancelInterruptUser(pPvt->int64Pvt, + pPvt->pasynUser,pPvt->registrarPvt); + if(status!=asynSuccess) { + printf("%s %s::%s cancelInterruptUser %s\n", + pr->name, driverName, functionName,pPvt->pasynUser->errorMessage); + } + } + *iopvt = pPvt->ioScanPvt; + return 0; +} + +static void processCallbackInput(asynUser *pasynUser) +{ + devPvt *pPvt = (devPvt *)pasynUser->userPvt; + dbCommon *pr = (dbCommon *)pPvt->pr; + static const char *functionName="processCallbackInput"; + + pPvt->result.status = pPvt->pint64->read(pPvt->int64Pvt, pPvt->pasynUser, &pPvt->result.value); + pPvt->result.time = pPvt->pasynUser->timestamp; + pPvt->result.alarmStatus = pPvt->pasynUser->alarmStatus; + pPvt->result.alarmSeverity = pPvt->pasynUser->alarmSeverity; + if (pPvt->result.status == asynSuccess) { + asynPrint(pasynUser, ASYN_TRACEIO_DEVICE, + "%s %s::%s process value=%lld\n",pr->name, driverName, functionName,pPvt->result.value); + } else { + if (pPvt->result.status != pPvt->lastStatus) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s %s::%s process read error %s\n", + pr->name, driverName, functionName, pasynUser->errorMessage); + } + } + pPvt->lastStatus = pPvt->result.status; + if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr); +} + +static void processCallbackOutput(asynUser *pasynUser) +{ + devPvt *pPvt = (devPvt *)pasynUser->userPvt; + dbCommon *pr = pPvt->pr; + static const char *functionName="processCallbackOutput"; + + pPvt->result.status = pPvt->pint64->write(pPvt->int64Pvt, pPvt->pasynUser,pPvt->result.value); + pPvt->result.time = pPvt->pasynUser->timestamp; + pPvt->result.alarmStatus = pPvt->pasynUser->alarmStatus; + pPvt->result.alarmSeverity = pPvt->pasynUser->alarmSeverity; + if(pPvt->result.status == asynSuccess) { + asynPrint(pasynUser, ASYN_TRACEIO_DEVICE, + "%s %s::%s process value %lld\n",pr->name, driverName, functionName,pPvt->result.value); + } else { + if (pPvt->result.status != pPvt->lastStatus) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s %s::%s process error %s\n", + pr->name, driverName, functionName, pasynUser->errorMessage); + } + } + pPvt->lastStatus = pPvt->result.status; + if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr); +} + +static void interruptCallbackInput(void *drvPvt, asynUser *pasynUser, + epicsInt64 value) +{ + devPvt *pPvt = (devPvt *)drvPvt; + dbCommon *pr = pPvt->pr; + ringBufferElement *rp; + static const char *functionName="interruptCallbackInput"; + + asynPrint(pPvt->pasynUser, ASYN_TRACEIO_DEVICE, + "%s %s::%s new value=%lld\n", + pr->name, driverName, functionName, value); + /* There is a problem. A driver could be calling us with a value after + * this record has registered for callbacks but before EPICS has set interruptAccept, + * which means that scanIoRequest will return immediately. + * This is very bad, because if we have pushed a value into the ring buffer + * it won't get popped off because the record won't process. The values + * read the next time the record processes would then be stale. + * We previously worked around this problem by waiting here for interruptAccept. + * But that does not work if the callback is coming from the thread that is executing + * iocInit, which can happen with synchronous drivers (ASYN_CANBLOCK=0) that do callbacks + * when a value is written to them, which can happen in initRecord for an output record. + * Instead we just return. There will then be nothing in the ring buffer, so the first + * read will do a read from the driver, which should be OK. */ + if (!interruptAccept) return; + epicsMutexLock(pPvt->devPvtLock); + rp = &pPvt->ringBuffer[pPvt->ringHead]; + rp->value = value; + rp->time = pasynUser->timestamp; + rp->status = pasynUser->auxStatus; + rp->alarmStatus = pasynUser->alarmStatus; + rp->alarmSeverity = pasynUser->alarmSeverity; + pPvt->ringHead = (pPvt->ringHead==pPvt->ringSize) ? 0 : pPvt->ringHead+1; + if (pPvt->ringHead == pPvt->ringTail) { + /* There was no room in the ring buffer. In the past we just threw away + * the new value. However, it is better to remove the oldest value from the + * ring buffer and add the new one. That way the final value the record receives + * is guaranteed to be the most recent value */ + pPvt->ringTail = (pPvt->ringTail==pPvt->ringSize) ? 0 : pPvt->ringTail+1; + pPvt->ringBufferOverflows++; + } else { + /* We only need to request the record to process if we added a new + * element to the ring buffer, not if we just replaced an element. */ + scanIoRequest(pPvt->ioScanPvt); + } + epicsMutexUnlock(pPvt->devPvtLock); +} + +static void interruptCallbackOutput(void *drvPvt, asynUser *pasynUser, + epicsInt64 value) +{ + devPvt *pPvt = (devPvt *)drvPvt; + dbCommon *pr = pPvt->pr; + ringBufferElement *rp; + static const char *functionName="interruptCallbackOutput"; + + asynPrint(pPvt->pasynUser, ASYN_TRACEIO_DEVICE, + "%s %s::%s new value=%lld\n", + pr->name, driverName, functionName, value); + if (!interruptAccept) return; + epicsMutexLock(pPvt->devPvtLock); + rp = &pPvt->ringBuffer[pPvt->ringHead]; + rp->value = value; + rp->time = pasynUser->timestamp; + rp->status = pasynUser->auxStatus; + rp->alarmStatus = pasynUser->alarmStatus; + rp->alarmSeverity = pasynUser->alarmSeverity; + pPvt->ringHead = (pPvt->ringHead==pPvt->ringSize) ? 0 : pPvt->ringHead+1; + if (pPvt->ringHead == pPvt->ringTail) { + pPvt->ringTail = (pPvt->ringTail==pPvt->ringSize) ? 0 : pPvt->ringTail+1; + pPvt->ringBufferOverflows++; + } else { + /* If this callback was received during asynchronous record processing + * we must defer calling callbackRequest until end of record processing */ + if (pPvt->asyncProcessingActive) { + pPvt->numDeferredOutputCallbacks++; + } else { + callbackRequest(&pPvt->outputCallback); + } + } + epicsMutexUnlock(pPvt->devPvtLock); +} + +static void outputCallbackCallback(CALLBACK *pcb) +{ + static const char *functionName="outputCallbackCallback"; + + devPvt *pPvt; + callbackGetUser(pPvt, pcb); + { + dbCommon *pr = pPvt->pr; + dbScanLock(pr); + epicsMutexLock(pPvt->devPvtLock); + pPvt->newOutputCallbackValue = 1; + dbProcess(pr); + if (pPvt->newOutputCallbackValue != 0) { + /* We called dbProcess but the record did not process, perhaps because PACT was 1 + * Need to remove ring buffer element */ + asynPrint(pPvt->pasynUser, ASYN_TRACE_ERROR, + "%s %s::%s warning dbProcess did not process record, PACT=%d\n", + pr->name, driverName, functionName,pr->pact); + getCallbackValue(pPvt); + pPvt->newOutputCallbackValue = 0; + } + epicsMutexUnlock(pPvt->devPvtLock); + dbScanUnlock(pr); + } +} + +static int getCallbackValue(devPvt *pPvt) +{ + int ret = 0; + static const char *functionName="getCallbackValue"; + + epicsMutexLock(pPvt->devPvtLock); + if (pPvt->ringTail != pPvt->ringHead) { + if (pPvt->ringBufferOverflows > 0) { + asynPrint(pPvt->pasynUser, ASYN_TRACE_WARNING, + "%s %s::%s warning, %d ring buffer overflows\n", + pPvt->pr->name, driverName, functionName, pPvt->ringBufferOverflows); + pPvt->ringBufferOverflows = 0; + } + pPvt->result = pPvt->ringBuffer[pPvt->ringTail]; + pPvt->ringTail = (pPvt->ringTail==pPvt->ringSize) ? 0 : pPvt->ringTail+1; + asynPrint(pPvt->pasynUser, ASYN_TRACEIO_DEVICE, + "%s %s::%s from ringBuffer value=%lld\n", + pPvt->pr->name, driverName, functionName,pPvt->result.value); + ret = 1; + } + epicsMutexUnlock(pPvt->devPvtLock); + return ret; +} + +static void reportQueueRequestStatus(devPvt *pPvt, asynStatus status) +{ + static const char *functionName="reportQueueRequestStatus"; + + if (status != asynSuccess) pPvt->result.status = status; + if (pPvt->previousQueueRequestStatus != status) { + pPvt->previousQueueRequestStatus = status; + if (status == asynSuccess) { + asynPrint(pPvt->pasynUser, ASYN_TRACE_ERROR, + "%s %s::%s queueRequest status returned to normal\n", + pPvt->pr->name, driverName, functionName); + } else { + asynPrint(pPvt->pasynUser, ASYN_TRACE_ERROR, + "%s %s::%s queueRequest error %s\n", + pPvt->pr->name, driverName, functionName,pPvt->pasynUser->errorMessage); + } + } +} + + +static long initLLi(int64inRecord *pr) +{ + int status; + + status = initCommon((dbCommon *)pr,&pr->inp, + processCallbackInput,interruptCallbackInput); + + return status; +} + +static long processLLi(int64inRecord *pr) +{ + devPvt *pPvt = (devPvt *)pr->dpvt; + int status; + + if(!getCallbackValue(pPvt) && !pr->pact) { + if(pPvt->canBlock) pr->pact = 1; + status = pasynManager->queueRequest(pPvt->pasynUser, 0, 0); + if((status==asynSuccess) && pPvt->canBlock) return 0; + if(pPvt->canBlock) pr->pact = 0; + reportQueueRequestStatus(pPvt, status); + } + pr->time = pPvt->result.time; + pasynEpicsUtils->asynStatusToEpicsAlarm(pPvt->result.status, + READ_ALARM, &pPvt->result.alarmStatus, + INVALID_ALARM, &pPvt->result.alarmSeverity); + (void)recGblSetSevr(pr, pPvt->result.alarmStatus, pPvt->result.alarmSeverity); + if(pPvt->result.status==asynSuccess) { + pr->val = pPvt->result.value; + pr->udf=0; + return 0; + } + else { + pPvt->result.status = asynSuccess; + return -1; + } +} + +static long initLLo(int64outRecord *pr) +{ + devPvt *pPvt; + int status; + epicsInt64 value; + + status = initCommon((dbCommon *)pr,&pr->out, + processCallbackOutput,interruptCallbackOutput); + if (status != INIT_OK) return status; + pPvt = pr->dpvt; + /* Read the current value from the device */ + status = pasynInt64SyncIO->read(pPvt->pasynUserSync, + &value, pPvt->pasynUser->timeout); + if (status == asynSuccess) { + pr->val = value; + pr->udf = 0; + } + return INIT_OK; +} + +static long processLLo(int64outRecord *pr) +{ + devPvt *pPvt = (devPvt *)pr->dpvt; + int status; + + epicsMutexLock(pPvt->devPvtLock); + if (pPvt->newOutputCallbackValue && getCallbackValue(pPvt)) { + /* We got a callback from the driver */ + if (pPvt->result.status == asynSuccess) { + pr->val = pPvt->result.value; + pr->udf = 0; + } + } else if(pr->pact == 0) { + pPvt->result.value = pr->val; + if(pPvt->canBlock) { + pr->pact = 1; + pPvt->asyncProcessingActive = 1; + } + epicsMutexUnlock(pPvt->devPvtLock); + status = pasynManager->queueRequest(pPvt->pasynUser, 0, 0); + if((status==asynSuccess) && pPvt->canBlock) return 0; + if(pPvt->canBlock) pr->pact = 0; + epicsMutexLock(pPvt->devPvtLock); + reportQueueRequestStatus(pPvt, status); + } + pasynEpicsUtils->asynStatusToEpicsAlarm(pPvt->result.status, + WRITE_ALARM, &pPvt->result.alarmStatus, + INVALID_ALARM, &pPvt->result.alarmSeverity); + (void)recGblSetSevr(pr, pPvt->result.alarmStatus, pPvt->result.alarmSeverity); + if (pPvt->numDeferredOutputCallbacks > 0) { + callbackRequest(&pPvt->outputCallback); + pPvt->numDeferredOutputCallbacks--; + } + pPvt->newOutputCallbackValue = 0; + pPvt->asyncProcessingActive = 0; + epicsMutexUnlock(pPvt->devPvtLock); + if(pPvt->result.status == asynSuccess) { + return 0; + } + else { + pPvt->result.status = asynSuccess; + return -1; + } +} diff --git a/asyn/devEpics/devAsynInt64.dbd b/asyn/devEpics/devAsynInt64.dbd new file mode 100644 index 0000000..88291f4 --- /dev/null +++ b/asyn/devEpics/devAsynInt64.dbd @@ -0,0 +1,2 @@ +device(int64in,INST_IO,asynInt64In,"asynInt64") +device(int64out,INST_IO,asynInt64Out,"asynInt64") diff --git a/asyn/devEpics/devAsynInt64Array.c b/asyn/devEpics/devAsynInt64Array.c new file mode 100644 index 0000000..76b342b --- /dev/null +++ b/asyn/devEpics/devAsynInt64Array.c @@ -0,0 +1,49 @@ +/* devAsynInt64Array.c */ +/*********************************************************************** +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory, and the Regents of the University of +* California, as Operator of Los Alamos National Laboratory, and +* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY). +* asynDriver is distributed subject to a Software License Agreement +* found in file LICENSE that is included with this distribution. +***********************************************************************/ +/* + Mark Rivers + March 26, 2008 +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "asynDriver.h" +#include "asynDrvUser.h" +#include "asynInt64Array.h" +#include "asynEpicsUtils.h" + +#include "devAsynXXXArray.h" + +/* The code for this driver is generated by the macro in the include file with macro substitution */ + +ASYN_XXX_ARRAY_FUNCS("devAsynInt64Array", asynInt64Array, asynInt64ArrayType, + interruptCallbackInt64Array, epicsInt64, asynInt64ArrayWfIn, asynInt64ArrayWfOut, + menuFtypeINT64, menuFtypeUINT64) + diff --git a/asyn/devEpics/devAsynInt64Array.dbd b/asyn/devEpics/devAsynInt64Array.dbd new file mode 100644 index 0000000..b538a2f --- /dev/null +++ b/asyn/devEpics/devAsynInt64Array.dbd @@ -0,0 +1,2 @@ +device(waveform,INST_IO,asynInt64ArrayWfIn,"asynInt64ArrayIn") +device(waveform,INST_IO,asynInt64ArrayWfOut,"asynInt64ArrayOut") diff --git a/asyn/devEpics/devAsynInt64TimeSeries.c b/asyn/devEpics/devAsynInt64TimeSeries.c new file mode 100644 index 0000000..a459e4c --- /dev/null +++ b/asyn/devEpics/devAsynInt64TimeSeries.c @@ -0,0 +1,49 @@ +/* devAsynInt64Array.c */ +/*********************************************************************** +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory, and the Regents of the University of +* California, as Operator of Los Alamos National Laboratory, and +* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY). +* asynDriver is distributed subject to a Software License Agreement +* found in file LICENSE that is included with this distribution. +***********************************************************************/ +/* + Mark Rivers + February 8, 2012 +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "asynDriver.h" +#include "asynDrvUser.h" +#include "asynEpicsUtils.h" +#include "asynInt64.h" +#include "devAsynXXXTimeSeries.h" + +/* The code for this driver is generated by the macro in the include file with macro substitution */ + +ASYN_XXX_TIME_SERIES_FUNCS("devAsynInt64TimeSeries", asynInt64, asynInt64Type, + interruptCallbackInt64, epicsInt64, asynInt64TimeSeries, + menuFtypeINT64, menuFtypeUINT64) + diff --git a/asyn/devEpics/devAsynInt64TimeSeries.dbd b/asyn/devEpics/devAsynInt64TimeSeries.dbd new file mode 100644 index 0000000..3784a7f --- /dev/null +++ b/asyn/devEpics/devAsynInt64TimeSeries.dbd @@ -0,0 +1 @@ +device(waveform,INST_IO,asynInt64TimeSeries,"asynInt64TimeSeries") diff --git a/asyn/devEpics/devAsynUInt32Digital.c b/asyn/devEpics/devAsynUInt32Digital.c index 9465173..1c50e4c 100644 --- a/asyn/devEpics/devAsynUInt32Digital.c +++ b/asyn/devEpics/devAsynUInt32Digital.c @@ -88,6 +88,7 @@ typedef struct devPvt{ int ringSize; int ringBufferOverflows; ringBufferElement result; + asynStatus lastStatus; interruptCallbackUInt32Digital interruptCallback; CALLBACK processCallback; CALLBACK outputCallback; @@ -407,13 +408,16 @@ static void processCallbackInput(asynUser *pasynUser) pPvt->result.alarmSeverity = pPvt->pasynUser->alarmSeverity; if (pPvt->result.status == asynSuccess) { asynPrint(pasynUser, ASYN_TRACEIO_DEVICE, - "%s %s::%s value=%u\n", - pr->name, driverName, functionName,pPvt->result.value); + "%s %s::%s process value=%u\n", pr->name, driverName, functionName, + pPvt->result.value); } else { - asynPrint(pasynUser, ASYN_TRACE_ERROR, - "%s %s::%s read error %s\n", - pr->name, driverName, functionName, pasynUser->errorMessage); + if (pPvt->result.status != pPvt->lastStatus) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s %s::%s process read error %s\n", + pr->name, driverName, functionName, pasynUser->errorMessage); + } } + pPvt->lastStatus = pPvt->result.status; if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr); } @@ -428,14 +432,18 @@ static void processCallbackOutput(asynUser *pasynUser) pPvt->result.time = pPvt->pasynUser->timestamp; pPvt->result.alarmStatus = pPvt->pasynUser->alarmStatus; pPvt->result.alarmSeverity = pPvt->pasynUser->alarmSeverity; - if(pPvt->result.status == asynSuccess) { + if (pPvt->result.status == asynSuccess) { asynPrint(pasynUser, ASYN_TRACEIO_DEVICE, - "%s %s::%s process value %u\n",pr->name, driverName, functionName,pPvt->result.value); + "%s %s::%s process value %u\n", pr->name, driverName, functionName, + pPvt->result.value); } else { - asynPrint(pasynUser, ASYN_TRACE_ERROR, - "%s %s::%s process error %s\n", - pr->name, driverName, functionName, pasynUser->errorMessage); + if (pPvt->result.status != pPvt->lastStatus) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s %s::%s process write error %s\n", + pr->name, driverName, functionName, pasynUser->errorMessage); + } } + pPvt->lastStatus = pPvt->result.status; if(pr->pact) callbackRequestProcessCallback(&pPvt->processCallback,pr->prio,pr); } diff --git a/asyn/devEpics/devAsynXXXArray.h b/asyn/devEpics/devAsynXXXArray.h index b89a142..7ee6b0b 100644 --- a/asyn/devEpics/devAsynXXXArray.h +++ b/asyn/devEpics/devAsynXXXArray.h @@ -42,7 +42,7 @@ typedef struct devAsynWfPvt{ int canBlock; \ CALLBACK callback; \ IOSCANPVT ioScanPvt; \ - asynStatus status; \ + asynStatus lastStatus; \ int isOutput; \ epicsMutexId ringBufferLock; \ ringBufferElement *ringBuffer; \ @@ -367,10 +367,13 @@ static void callbackWfOut(asynUser *pasynUser) asynPrint(pasynUser, ASYN_TRACEIO_DEVICE, \ "%s %s::callbackWfOut OK\n", pwf->name, driverName); \ } else { \ - asynPrint(pasynUser, ASYN_TRACE_ERROR, \ - "%s %s::callbackWfOut write error %s\n", \ - pwf->name, driverName, pasynUser->errorMessage); \ + if (pPvt->result.status != pPvt->lastStatus) { \ + asynPrint(pasynUser, ASYN_TRACE_ERROR, \ + "%s %s::callbackWfOut write error %s\n", \ + pwf->name, driverName, pasynUser->errorMessage); \ + } \ } \ + pPvt->lastStatus = pPvt->result.status; \ if(pwf->pact) callbackRequestProcessCallback(&pPvt->callback,pwf->prio,pwf); \ } \ \ @@ -387,14 +390,17 @@ static void callbackWfIn(asynUser *pasynUser) pPvt->result.time = pPvt->pasynUser->timestamp; \ pPvt->result.alarmStatus = pPvt->pasynUser->alarmStatus; \ pPvt->result.alarmSeverity = pPvt->pasynUser->alarmSeverity; \ - if (pPvt->result.status == asynSuccess) { \ + if (pPvt->result.status == asynSuccess) { \ pwf->udf=0; \ pwf->nord = (epicsUInt32)nread; \ } else { \ - asynPrint(pasynUser, ASYN_TRACE_ERROR, \ - "%s %s::callbackWfIn read error %s\n", \ - pwf->name, driverName, pasynUser->errorMessage); \ + if (pPvt->result.status != pPvt->lastStatus) { \ + asynPrint(pasynUser, ASYN_TRACE_ERROR, \ + "%s %s::callbackWfIn read error %s\n", \ + pwf->name, driverName, pasynUser->errorMessage); \ + } \ } \ + pPvt->lastStatus = pPvt->result.status; \ if(pwf->pact) callbackRequestProcessCallback(&pPvt->callback,pwf->prio,pwf); \ } \ \ diff --git a/asyn/devEpics/devEpics.dbd b/asyn/devEpics/devEpics.dbd deleted file mode 100644 index 179fb3d..0000000 --- a/asyn/devEpics/devEpics.dbd +++ /dev/null @@ -1,12 +0,0 @@ -include "devAsynOctet.dbd" -include "devAsynInt32.dbd" -include "devAsynInt8Array.dbd" -include "devAsynInt16Array.dbd" -include "devAsynInt32Array.dbd" -include "devAsynInt32TimeSeries.dbd" -include "devAsynFloat64.dbd" -include "devAsynFloat32Array.dbd" -include "devAsynFloat64Array.dbd" -include "devAsynFloat64TimeSeries.dbd" -include "devAsynUInt32Digital.dbd" -include "devAsynRecord.dbd" diff --git a/asyn/drvAsynFTDI/drvAsynFTDIPort.cpp b/asyn/drvAsynFTDI/drvAsynFTDIPort.cpp new file mode 100644 index 0000000..3b54fc0 --- /dev/null +++ b/asyn/drvAsynFTDI/drvAsynFTDIPort.cpp @@ -0,0 +1,656 @@ +/*************************************************** +* Asyn device support for FTDI comms library * +****************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "asynDriver.h" +#include "asynOctet.h" +#include "asynOption.h" +#include "asynInterposeCom.h" +#include "asynInterposeEos.h" +#include "drvAsynFTDIPort.h" +#include "ftdiDriver.h" + +/* + * This structure holds the hardware-specific information for a single + * asyn link. There is one for each IP socket. + */ +typedef struct { + asynUser *pasynUser; /* Not currently used */ + int FTDIvendor; + int FTDIproduct; + int FTDIbaudrate; + int FTDIlatency; + char *portName; + FTDIDriver *driver; + unsigned long nRead; + unsigned long nWritten; + int haveAddress; + osiSockAddr farAddr; + asynInterface common; + asynInterface option; + asynInterface octet; +} ftdiController_t; + +/* + * asynOption methods + */ +static asynStatus +getOption(void *drvPvt, asynUser *pasynUser, const char *key, char *val, int valSize) +{ + ftdiController_t *ftdi = (ftdiController_t *)drvPvt; + int l; + + if (epicsStrCaseCmp(key, "baud") == 0) + l = epicsSnprintf(val, valSize, "%d", ftdi->driver->getBaudRate()); + + else if (epicsStrCaseCmp(key, "bits") == 0) { + const char *v; + switch (ftdi->driver->getBits()) { + case BITS_7: v = "7"; break; + case BITS_8: v = "8"; break; + default: v = "?"; + } + + l = epicsSnprintf(val, valSize, "%s", v); + } + + else if (epicsStrCaseCmp(key, "parity") == 0) { + const char *v; + switch(ftdi->driver->getParity()) { + case NONE: v = "none"; break; + case ODD: v = "odd"; break; + case EVEN: v = "even"; break; + case MARK: v = "mark"; break; + case SPACE: v = "space"; break; + default: v = "?"; + } + l = epicsSnprintf(val, valSize, "%s", v); + } + + else if (epicsStrCaseCmp(key, "stop") == 0) { + const char *v; + switch(ftdi->driver->getStopBits()) { + case STOP_BIT_1: v = "1"; break; + case STOP_BIT_15: v = "1.5"; break; + case STOP_BIT_2: v = "2"; break; + default: v = "?"; + } + l = epicsSnprintf(val, valSize, "%s", v); + } + + else if (epicsStrCaseCmp(key, "break") == 0) { + const char *v; + switch(ftdi->driver->getBreak()) { + case BREAK_OFF: v = "off"; break; + case BREAK_ON: v = "on"; break; + default: v = "?"; + } + l = epicsSnprintf(val, valSize, "%s", v); + } + + else if (epicsStrCaseCmp(key, "flow")) { + int flowctrl = ftdi->driver->getFlowControl(); + if (flowctrl == SIO_DISABLE_FLOW_CTRL) + l = epicsSnprintf(val, valSize, "%s", "none"); + else { + char v[256] = {}; + int n = 0; + if (flowctrl & SIO_RTS_CTS_HS) { strcat(v, "rts_cts"); ++n; } + if (n) { strcat(v, "|"); } + if (flowctrl & SIO_DTR_DSR_HS) { strcat(v, "dtr_dsr"); ++n; } + if (n) { strcat(v, "|"); } + if (flowctrl & SIO_XON_XOFF_HS) { strcat(v, "xon_xoff"); } + l = epicsSnprintf(val, valSize, "%s", v); + } + } + + else { + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "Unsupported key \"%s\"", key); + return asynError; + } + + if (l >= valSize) { + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "Value buffer for key '%s' is too small.", key); + return asynError; + } + return asynSuccess; +} + +static asynStatus +setOption(void *drvPvt, asynUser *pasynUser, const char *key, const char *val) +{ + ftdiController_t *ftdi = (ftdiController_t *)drvPvt; + assert(ftdi); + + asynPrint(pasynUser, ASYN_TRACE_FLOW, + "%s setOption key %s val %s\n", ftdi->portName, key, val); + + if (epicsStrCaseCmp(key, "baud") == 0) { + int baud; + if (sscanf(val, "%d", &baud) != 1) { + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, "Bad number"); + return asynError; + } + if (ftdi->driver->setBaudrate(baud)) { + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "Unsupported data rate (%d baud)", baud); + return asynError; + } + } else if (epicsStrCaseCmp(key, "parity") == 0) { + enum ftdi_parity_type parity; + if (epicsStrCaseCmp(val, "none") == 0) parity = NONE; + else if (epicsStrCaseCmp(val, "even") == 0) parity = EVEN; + else if (epicsStrCaseCmp(val, "odd") == 0) parity = ODD; + else if (epicsStrCaseCmp(val, "mark") == 0) parity = MARK; + else if (epicsStrCaseCmp(val, "space") == 0) parity = SPACE; + else { + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "Invalid parity \"%s\".", val); + return asynError; + } + if (ftdi->driver->setParity(parity)) + return asynError; + } + else if (epicsStrCaseCmp(key, "stop") == 0) { + enum ftdi_stopbits_type sbits; + if (epicsStrCaseCmp(val, "1") == 0) sbits = STOP_BIT_1; + else if (epicsStrCaseCmp(val, "1.5") == 0) sbits = STOP_BIT_15; + else if (epicsStrCaseCmp(val, "2") == 0) sbits = STOP_BIT_2; + else { + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "Invalid number of stop bits \"%s\".", val); + return asynError; + } + if (ftdi->driver->setStopBits(sbits)) + return asynError; + } + else if (epicsStrCaseCmp(key, "break") == 0) { + enum ftdi_break_type brk; + if (epicsStrCaseCmp(val, "on") == 0) brk = BREAK_ON; + else if (epicsStrCaseCmp(val, "off") == 0) brk = BREAK_OFF; + else { + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "Invalid break \"%s\".", val); + return asynError; + } + if (ftdi->driver->setBreak(brk)) + return asynError; + } + else if (epicsStrCaseCmp(key, "flow") == 0) { + int flowctrl = SIO_DISABLE_FLOW_CTRL; + if (epicsStrCaseCmp(val, "none") != 0) { + char *str = epicsStrDup(val); + char *token; + char *rest = str; + + while ((token = strtok_r(rest, "|", &rest))) { + if (epicsStrCaseCmp(token, "rts_cts") == 0) flowctrl |= SIO_RTS_CTS_HS; + else if (epicsStrCaseCmp(token, "dtr_dsr") == 0) flowctrl |= SIO_DTR_DSR_HS; + else if (epicsStrCaseCmp(token, "xon_xoff") == 0) flowctrl |= SIO_XON_XOFF_HS; + else { + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "Invalid flow control \"%s\".", val); + free(str); + return asynError; + } + } + free(str); + } + if (ftdi->driver->setFlowControl(flowctrl)) + return asynError; + } + else if (epicsStrCaseCmp(key, "") != 0) { + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "Unsupported key \"%s\"", key); + return asynError; + } + + return asynSuccess; +} +static const struct asynOption asynOptionMethods = { setOption, getOption }; + +/* + * Close a connection + */ +static void +closeConnection(asynUser *pasynUser,ftdiController_t *ftdi,const char *why) +{ + asynPrint(pasynUser, ASYN_TRACE_FLOW, + "Close %d:%d connection (driver %p): %s\n", ftdi->FTDIvendor, ftdi->FTDIproduct, ftdi->driver, why); + if (ftdi->driver){ + ftdi->driver->disconnectFTDI(); + delete(ftdi->driver); + ftdi->driver = 0; + } +} + +/*Beginning of asynCommon methods*/ +/* + * Report link parameters + */ +static void +asynCommonReport(void *drvPvt, FILE *fp, int details) +{ + ftdiController_t *ftdi = (ftdiController_t *)drvPvt; + + assert(ftdi); + if (details >= 1) { + fprintf(fp, " Port %d:%d: %sonnected\n", + ftdi->FTDIvendor, ftdi->FTDIproduct, + ftdi->driver ? "C" : "Disc"); + } + if (details >= 2) { + fprintf(fp, " Characters written: %lu\n", ftdi->nWritten); + fprintf(fp, " Characters read: %lu\n", ftdi->nRead); + } +} + +/* + * Clean up a connection on exit + */ +static void +cleanup (void *arg) +{ + asynStatus status; + ftdiController_t *ftdi = (ftdiController_t *)arg; + + if (!ftdi) return; + status=pasynManager->lockPort(ftdi->pasynUser); + if(status!=asynSuccess) + asynPrint(ftdi->pasynUser, ASYN_TRACE_ERROR, "%s: cleanup locking error\n", ftdi->portName); + + if (ftdi->driver){ + asynPrint(ftdi->pasynUser, ASYN_TRACE_FLOW, "%s: shutdown socket\n", ftdi->portName); + ftdi->driver->disconnectFTDI(); + delete(ftdi->driver); + } + + if(status==asynSuccess) + pasynManager->unlockPort(ftdi->pasynUser); +} + +/* + * Create a link +*/ +static asynStatus +connectIt(void *drvPvt, asynUser *pasynUser) +{ + ftdiController_t *ftdi = (ftdiController_t *)drvPvt; + + /* + * Sanity check + */ + assert(ftdi); + asynPrint(pasynUser, ASYN_TRACE_FLOW, + "Open connection to %d:%d reason:%d \n", ftdi->FTDIvendor, ftdi->FTDIproduct, + pasynUser->reason); + + if (ftdi->driver){ + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "%d:%d: Link already open!", ftdi->FTDIvendor, ftdi->FTDIproduct); + return asynError; + } + + // Create the driver + ftdi->driver = new FTDIDriver(); + + // Set the vendor & product IDs + ftdi->driver->setVPID(ftdi->FTDIvendor, ftdi->FTDIproduct); + + // Set the baud rate + ftdi->driver->setBaudrate(ftdi->FTDIbaudrate); + + // Set the latency + ftdi->driver->setLatency(ftdi->FTDIlatency); + + // Connect to the remote host + if (ftdi->driver->connectFTDI() != FTDIDriverSuccess){ + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "Can't connect to %d:%d", + ftdi->FTDIvendor, ftdi->FTDIproduct); + ftdi->haveAddress = 0; + delete(ftdi->driver); + ftdi->driver = 0; + return asynError; + } + + asynPrint(pasynUser, ASYN_TRACE_FLOW, "Opened connection to %d:%d\n", ftdi->FTDIvendor, ftdi->FTDIproduct); + return asynSuccess; +} + +static asynStatus +asynCommonConnect(void *drvPvt, asynUser *pasynUser) +{ + asynStatus status = asynSuccess; + + status = connectIt(drvPvt, pasynUser); + + if (status == asynSuccess) + pasynManager->exceptionConnect(pasynUser); + return status; +} + +static asynStatus +asynCommonDisconnect(void *drvPvt, asynUser *pasynUser) +{ + ftdiController_t *ftdi = (ftdiController_t *)drvPvt; + + assert(ftdi); + closeConnection(pasynUser,ftdi,"Disconnect request"); + return asynSuccess; +} + +/*Beginning of asynOctet methods*/ +/* + * Write to the USB FTDI port + */ +static asynStatus writeIt(void *drvPvt, asynUser *pasynUser, + const char *data, size_t numchars,size_t *nbytesTransfered) +{ + ftdiController_t *ftdi = (ftdiController_t *)drvPvt; + size_t thisWrite; + asynStatus status = asynSuccess; + + assert(ftdi); + asynPrint(pasynUser, ASYN_TRACE_FLOW, + "%d:%d write.\n", ftdi->FTDIvendor, ftdi->FTDIproduct); + asynPrintIO(pasynUser, ASYN_TRACEIO_DRIVER, data, numchars, + "%d:%d write %lu\n", ftdi->FTDIvendor, ftdi->FTDIproduct, numchars); + *nbytesTransfered = 0; + + // Here we will simply issue the write to the driver + if (!ftdi->driver){ + if ((status = connectIt(drvPvt, pasynUser)) != asynSuccess){ + return status; + } + } + if (numchars == 0){ + return asynSuccess; + } + if (ftdi->driver->write((unsigned char *)data, numchars, &thisWrite, (int)(pasynUser->timeout*1000.0)) != FTDIDriverSuccess){ + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "%d:%d write error", ftdi->FTDIvendor, ftdi->FTDIproduct); + closeConnection(pasynUser,ftdi,"Write error"); + status = asynError; + } + *nbytesTransfered = thisWrite; + asynPrint(pasynUser, ASYN_TRACE_FLOW, + "wrote %lu to %d:%d, return %s.\n", (unsigned long)*nbytesTransfered, + ftdi->FTDIvendor, ftdi->FTDIproduct, + pasynManager->strStatus(status)); + return status; +} + +/* + * Read from the TCP port + */ +static asynStatus readIt(void *drvPvt, asynUser *pasynUser, + char *data, size_t maxchars,size_t *nbytesTransfered,int *gotEom) +{ + ftdiController_t *ftdi = (ftdiController_t *)drvPvt; + size_t thisRead; + int reason = 0; + asynStatus status = asynSuccess; + FTDIDriverStatus driverStatus = FTDIDriverSuccess; + + assert(ftdi); + asynPrint(pasynUser, ASYN_TRACE_FLOW, + "%d:%d read.\n", ftdi->FTDIvendor, ftdi->FTDIproduct); + if (!ftdi->driver){ + if ((status = connectIt(drvPvt, pasynUser)) != asynSuccess){ + return status; + } + } + if (maxchars <= 0) { + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "%d:%d maxchars %d. Why <=0?\n",ftdi->FTDIvendor, ftdi->FTDIproduct,(int)maxchars); + return asynError; + } + + if (gotEom) *gotEom = 0; + driverStatus = ftdi->driver->read((unsigned char *)data, maxchars, &thisRead, (int)(pasynUser->timeout*1000.0)); + if (driverStatus != FTDIDriverSuccess){ + if (driverStatus == FTDIDriverError){ + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "%d:%d read error", ftdi->FTDIvendor, ftdi->FTDIproduct); + closeConnection(pasynUser,ftdi,"Read error"); + status = asynError; + } else if (driverStatus == FTDIDriverTimeout){ + status = asynTimeout; + } + } + + if (thisRead < 0){ + thisRead = 0; + } + *nbytesTransfered = thisRead; + + /* If there is room add a null byte */ + if (thisRead < maxchars) + data[thisRead] = 0; + else + reason |= ASYN_EOM_CNT; + + if (gotEom) *gotEom = reason; + + if (thisRead == 0 && pasynUser->timeout == 0){ + status = asynTimeout; + } + + return status; +} + +/* + * Flush pending input + */ +static asynStatus +flushIt(void *drvPvt,asynUser *pasynUser) +{ + ftdiController_t *ftdi = (ftdiController_t *)drvPvt; + + assert(ftdi); + asynPrint(pasynUser, ASYN_TRACE_FLOW, "%d:%d flush\n", ftdi->FTDIvendor, ftdi->FTDIproduct); + if (ftdi->driver){ + ftdi->driver->flush(); + } + return asynSuccess; +} + +/* + * Clean up a ftdiController + */ +static void +ftdiCleanup(ftdiController_t *ftdi) +{ + if (ftdi) { + if (ftdi->driver) + free(ftdi->portName); +/* free(ftdi->FTDIDeviceName); */ + free(ftdi); + } +} + +/* + * asynCommon methods + */ +static const struct asynCommon drvAsynFTDIPortAsynCommon = { + asynCommonReport, + asynCommonConnect, + asynCommonDisconnect +}; + +/* + * Configure and register a USB port from a hostInfo string + */ +epicsShareFunc int +drvAsynFTDIPortConfigure(const char *portName, + int vendor, + int product, + int baudrate, + int latency, + unsigned int priority, + int noAutoConnect, + int noProcessEos) +{ + ftdiController_t *ftdi; + asynInterface *pasynInterface; + asynStatus status; + int nbytes; + asynOctet *pasynOctet; + + /* Latency must be between 1 and 255 */ + if (latency < 1) + latency = 1; + else if (latency > 255) + latency= 255; + + /* + * Check arguments + * + if (vendor == 0) { + printf("asynFTDI vendor ID missing.\n"); + return -1; + } + if (product == NULL) { + printf("asynFTDI product ID missing.\n"); + return -1; + } + if (baudrate == NULL) { + printf("asynFTDI username missing.\n"); + return -1; + } */ + + + /* + * Create a driver + */ + nbytes = sizeof(*ftdi) + sizeof(asynOctet); + ftdi = (ftdiController_t *)callocMustSucceed(1, nbytes, + "drvAsynFTDIPortConfigure()"); + pasynOctet = (asynOctet *)(ftdi+1); + ftdi->driver = NULL; + ftdi->FTDIvendor = vendor; + ftdi->FTDIproduct = product; + ftdi->FTDIbaudrate = baudrate; + ftdi->FTDIlatency = latency; + ftdi->portName = epicsStrDup(portName); + + /* + * Link with higher level routines + */ + pasynInterface = (asynInterface *)callocMustSucceed(2, sizeof *pasynInterface, "drvAsynFTDIPortConfigure"); + ftdi->common.interfaceType = asynCommonType; + ftdi->common.pinterface = (void *)&drvAsynFTDIPortAsynCommon; + ftdi->common.drvPvt = ftdi; + ftdi->option.interfaceType = asynOptionType; + ftdi->option.pinterface = (void*)&asynOptionMethods; + ftdi->option.drvPvt = ftdi; + if (pasynManager->registerPort(ftdi->portName, + ASYN_CANBLOCK, + !noAutoConnect, + priority, + 500000) != asynSuccess) { + printf("drvAsynFTDIPortConfigure: Can't register myself.\n"); + ftdiCleanup(ftdi); + return -1; + } + status = pasynManager->registerInterface(ftdi->portName,&ftdi->common); + if(status != asynSuccess) { + printf("drvAsynFTDIPortConfigure: Can't register common.\n"); + ftdiCleanup(ftdi); + return -1; + } + status = pasynManager->registerInterface(ftdi->portName,&ftdi->option); + if(status != asynSuccess) { + printf("drvAsynFTDIPortConfigure: Can't register option.\n"); + ftdiCleanup(ftdi); + return -1; + } + pasynOctet->read = readIt; + pasynOctet->write = writeIt; + pasynOctet->flush = flushIt; + ftdi->octet.interfaceType = asynOctetType; + ftdi->octet.pinterface = pasynOctet; + ftdi->octet.drvPvt = ftdi; + status = pasynOctetBase->initialize(ftdi->portName,&ftdi->octet, 0, 0, 1); + if(status != asynSuccess) { + printf("drvAsynFTDIPortConfigure: pasynOctetBase->initialize failed.\n"); + ftdiCleanup(ftdi); + return -1; + } + if (!noProcessEos) + asynInterposeEosConfig(ftdi->portName, -1, 1, 1); + ftdi->pasynUser = pasynManager->createAsynUser(0,0); + status = pasynManager->connectDevice(ftdi->pasynUser,ftdi->portName,-1); + if(status != asynSuccess) { + printf("connectDevice failed %s\n",ftdi->pasynUser->errorMessage); + ftdiCleanup(ftdi); + return -1; + } + + /* + * Register for socket cleanup + */ + epicsAtExit(cleanup, ftdi); + return 0; +} + +/* + * IOC shell command registration + */ +static const iocshArg drvAsynFTDIPortConfigureArg0 = { "port name",iocshArgString}; +static const iocshArg drvAsynFTDIPortConfigureArg1 = { "vendor ID",iocshArgInt}; +static const iocshArg drvAsynFTDIPortConfigureArg2 = { "product ID",iocshArgInt}; +static const iocshArg drvAsynFTDIPortConfigureArg3 = { "baudrate",iocshArgInt}; +static const iocshArg drvAsynFTDIPortConfigureArg4 = { "latency",iocshArgInt}; +static const iocshArg drvAsynFTDIPortConfigureArg5 = { "priority",iocshArgInt}; +static const iocshArg drvAsynFTDIPortConfigureArg6 = { "disable auto-connect",iocshArgInt}; +static const iocshArg drvAsynFTDIPortConfigureArg7 = { "noProcessEos",iocshArgInt}; +static const iocshArg *drvAsynFTDIPortConfigureArgs[] = { + &drvAsynFTDIPortConfigureArg0, &drvAsynFTDIPortConfigureArg1, + &drvAsynFTDIPortConfigureArg2, &drvAsynFTDIPortConfigureArg3, + &drvAsynFTDIPortConfigureArg4, &drvAsynFTDIPortConfigureArg5, + &drvAsynFTDIPortConfigureArg6}; +static const iocshFuncDef drvAsynFTDIPortConfigureFuncDef = + {"drvAsynFTDIPortConfigure",7,drvAsynFTDIPortConfigureArgs}; +static void drvAsynFTDIPortConfigureCallFunc(const iocshArgBuf *args) +{ + drvAsynFTDIPortConfigure(args[0].sval, args[1].ival, args[2].ival, args[3].ival, args[4].ival, args[5].ival, args[6].ival, args[7].ival); +} + +/* + * This routine is called before multitasking has started, so there's + * no race condition in the test/set of firstTime. + */ +static void +drvAsynFTDIPortRegisterCommands(void) +{ + static int firstTime = 1; + if (firstTime) { + iocshRegister(&drvAsynFTDIPortConfigureFuncDef,drvAsynFTDIPortConfigureCallFunc); + firstTime = 0; + } +} +epicsExportRegistrar(drvAsynFTDIPortRegisterCommands); + diff --git a/asyn/drvAsynFTDI/drvAsynFTDIPort.dbd b/asyn/drvAsynFTDI/drvAsynFTDIPort.dbd new file mode 100644 index 0000000..a5bb517 --- /dev/null +++ b/asyn/drvAsynFTDI/drvAsynFTDIPort.dbd @@ -0,0 +1 @@ +registrar(drvAsynFTDIPortRegisterCommands) diff --git a/asyn/drvAsynFTDI/drvAsynFTDIPort.h b/asyn/drvAsynFTDI/drvAsynFTDIPort.h new file mode 100644 index 0000000..69e55f0 --- /dev/null +++ b/asyn/drvAsynFTDI/drvAsynFTDIPort.h @@ -0,0 +1,25 @@ +/********************************************************************** +* Asyn device support using FTDI communications library * +**********************************************************************/ +#ifndef DRVASYNFTDIPORT_H +#define DRVASYNFTDIPORT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc int drvAsynFTDIPortConfigure(const char *portname, + const int vendor, + const int product, + const int baudrate, + const int latency, + unsigned int priority, + int noAutoConnect, + int noProcessEos); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DRVASYNFTDIPORT_H */ diff --git a/asyn/drvAsynFTDI/ftdiDriver.cpp b/asyn/drvAsynFTDI/ftdiDriver.cpp new file mode 100644 index 0000000..f8d572a --- /dev/null +++ b/asyn/drvAsynFTDI/ftdiDriver.cpp @@ -0,0 +1,639 @@ +/******************************************** + * ftdiDriver.cpp + * + * Wrapper class for the libftdi1 library + * This class provides standard read/write + * and flush methods for an FTDI chip USB connection. + * + * Philip Taylor + * 8 Jul 2013 + * + ********************************************/ + +#include "ftdiDriver.h" +#include "epicsThread.h" +#ifndef _MINGW +#ifdef _WIN32 +int gettimeofday(struct timeval *tv, struct timezone *tz); +void usleep(__int64 usec); +#else +#include +#endif +#endif + +/* + * Uncomment the DEBUG define and recompile for lots of + * driver debug messages. + */ + +/* +#define DEBUG 1 +*/ + +#ifdef DEBUG +#define debugPrint printf +#else +void debugPrint(...){} +#endif + + +/** + * Constructor for the FTDI driver. Accepts a Vendor ID and + * Product ID. Initializes internal variables. + * + * @param vendor - Vendor ID + * @param product - Product ID + */ +FTDIDriver::FTDIDriver() +{ + static const char *functionName = "FTDIDriver::FTDIDriver"; + debugPrint("%s : Method called\n", functionName); + + // Initialize internal FTDI parameters + connected_ = 0; + // Vendor and Product ID set to FTDI FT2232H default values + vendor_ = 0x0403; + product_ = 0x6010; + // Baudrate and latency set to 12 Mb and 2 msecs. + baudrate_ = 12000000; + latency_ = 2; + // Reasonable defaults for line properties and flow control + bits_ = BITS_8; + sbits_ = STOP_BIT_1; + parity_ = NONE; + break_ = BREAK_OFF; + flowctrl_ = SIO_DISABLE_FLOW_CTRL; +} + +/** + * Setup the Vendor ID and Product ID for the USB device. + * + * @param vendor - Vendor ID + * @param product - Product ID + * @return - Success or failure. + */ +FTDIDriverStatus FTDIDriver::setVPID(const int vendor, const int product) +{ + static const char *functionName = "FTDIDriver::setVPID"; + debugPrint("%s : Method called\n", functionName); + + // Store the Vendor ID + vendor_ = vendor; + + // Store the Product ID + product_ = product; + + return FTDIDriverSuccess; +} + +/** + * Setup the baudrate. + * + * @param baudrate - Baud rate + * @return - Success or failure. + */ +FTDIDriverStatus FTDIDriver::setBaudrate(const int baudrate) +{ + static const char *functionName = "FTDIDriver::setBaudrate"; + debugPrint("%s : Method called\n", functionName); + + int f; + if (ftdi_ && (f = ftdi_set_baudrate(ftdi_, baudrate))) { + debugPrint("Failed to set FTDI baudrate: %d (%s)\n", f, ftdi_get_error_string(ftdi_)); + return FTDIDriverError; + } + + baudrate_ = baudrate; + return FTDIDriverSuccess; +} + +/** + * Setup the bits. + * + * @param bits - Bits + * @return - Success or failure. + */ +FTDIDriverStatus FTDIDriver::setBits(enum ftdi_bits_type bits) +{ + static const char *functionName = "FTDIDriver::setBits"; + debugPrint("%s : Method called\n", functionName); + return setLineProperties(bits, sbits_, parity_, break_); +} + +/** + * Setup the stop bits. + * + * @param sbits - Stop bits + * @return - Success or failure. + */ +FTDIDriverStatus FTDIDriver::setStopBits(enum ftdi_stopbits_type sbits) +{ + static const char *functionName = "FTDIDriver::setStopBits"; + debugPrint("%s : Method called\n", functionName); + return setLineProperties(bits_, sbits, parity_, break_); +} + +/** + * Setup the parity. + * + * @param parity - Parity + * @return - Success or failure. + */ +FTDIDriverStatus FTDIDriver::setParity(enum ftdi_parity_type parity) +{ + static const char *functionName = "FTDIDriver::setParity"; + debugPrint("%s : Method called\n", functionName); + return setLineProperties(bits_, sbits_, parity, break_); +} + +/** + * Setup the break. + * + * @param brk - Break + * @return - Success or failure. + */ +FTDIDriverStatus FTDIDriver::setBreak(enum ftdi_break_type brk) +{ + static const char *functionName = "FTDIDriver::setBreak"; + debugPrint("%s : Method called\n", functionName); + return setLineProperties(bits_, sbits_, parity_, brk); +} + +FTDIDriverStatus FTDIDriver::setLineProperties(enum ftdi_bits_type bits, + enum ftdi_stopbits_type sbits, enum ftdi_parity_type parity, enum ftdi_break_type brk) +{ + static const char *functionName = "FTDIDriver::setLineProperties"; + debugPrint("%s : Method called\n", functionName); + + int f; + if (ftdi_ && (f = ftdi_set_line_property2(ftdi_, bits, sbits, parity, brk))) + { + debugPrint("Failed to set FTDI line parameters: %d (%s)\n", f, ftdi_get_error_string(ftdi_)); + return FTDIDriverError; + } + + bits_ = bits; + sbits_ = sbits; + parity_ = parity; + break_ = brk; + + return FTDIDriverSuccess; +} + +/** + * Setup the flow control. + * + * @param flowctrl - Flow control. Either SIO_DISABLE_FLOW_CTRL or + * a OR combination of SIO_RTS_CTS_HS, SIO_DTR_DSR_HS + * and SIO_XON_XOFF_HS. + * @return - Success or failure. + */ +FTDIDriverStatus FTDIDriver::setFlowControl(int flowctrl) +{ + static const char *functionName = "FTDIDriver::setFlowControl"; + debugPrint("%s : Method called\n", functionName); + + int f; + if (ftdi_ && (f = ftdi_setflowctrl(ftdi_, flowctrl))) + { + debugPrint("Failed to set FTDI flow control: %d (%s)\n", f, ftdi_get_error_string(ftdi_)); + return FTDIDriverError; + } + flowctrl_ = flowctrl; + return FTDIDriverSuccess; +} + +/** + * Setup the latency. + * + * @param latency - FTDI chip latency time. The latency timer counts from the + * last time data was sent back to the host. If the latency timer expires, + * the device will send what data it has available to the host regardless + * of how many bytes it is waiting on. The latency timer will then reset + * and begin counting again. The default value for the latency timer is 16ms. + * + * @return - Success or failure. + */ +FTDIDriverStatus FTDIDriver::setLatency(const int latency) +{ + static const char *functionName = "FTDIDriver::setLatency"; + debugPrint("%s : Method called\n", functionName); + + int f; + if (ftdi_ && (f = ftdi_set_latency_timer (ftdi_, latency))) { + debugPrint("Failed to set FTDI latency: %d (%s)\n", f, ftdi_get_error_string(ftdi_)); + return FTDIDriverError; + } + + latency_ = latency; + return FTDIDriverSuccess; +} + +/** + * Get the Baud rate. + * + * @return - The Baud rate. + */ +int FTDIDriver::getBaudRate(void) +{ + return ftdi_->baudrate; +} + +/** + * Get the bits. + * + * @return - The bits. + */ +enum ftdi_bits_type FTDIDriver::getBits(void) +{ + return bits_; +} + +/** + * Get the stop bits. + * + * @return - The stop bits. + */ +enum ftdi_stopbits_type FTDIDriver::getStopBits(void) +{ + return sbits_; +} + +/** + * Get the parity. + * + * @return - The parity. + */ +enum ftdi_parity_type FTDIDriver::getParity(void) +{ + return parity_; +} + +/** + * Get the break. + * + * @return - The break. + */ +enum ftdi_break_type FTDIDriver::getBreak(void) +{ + return break_; +} + +/** + * Get the flow control. + * + * @return - The flow control. + */ +int FTDIDriver::getFlowControl(void) +{ + return flowctrl_; +} + +/** + * Attempt to create a connection Once the connection has + * been established. + * + * @return - Success or failure. + */ +FTDIDriverStatus FTDIDriver::connectFTDI() +{ + int f; + unsigned int chipid; + static const char *functionName = "FTDIDriver::connect"; + debugPrint("%s : Method called\n", functionName); + +#ifdef HAVE_LIBFTDI1 + struct ftdi_version_info version = ftdi_get_library_version(); + printf("Initialized libftdi %s (major: %d, minor: %d, micro: %d, snapshot ver: %s)\n", + version.version_str, version.major, version.minor, version.micro, version.snapshot_str); +#else + printf("Initialized libftdi\n"); +#endif + + // Allocate and initialize a new ftdi context + if ((ftdi_ = ftdi_new()) == NULL) + { + debugPrint("ftdi_new failed: %s\n", ftdi_get_error_string(ftdi_)); + return FTDIDriverError; + } + + // Select interface. In this case just use "ANY" + if ((f = ftdi_set_interface(ftdi_, INTERFACE_ANY)) != 0) + { + debugPrint("ftdi_set_interface failed: %d (%s)\n", f, ftdi_get_error_string(ftdi_)); + return FTDIDriverError; + } + + // Open the FTDI USB device + if ((f = ftdi_usb_open(ftdi_, vendor_, product_)) != 0) + { + debugPrint("Failed to open FTDI device: %d (%s)\n", f, ftdi_get_error_string(ftdi_)); + return FTDIDriverError; + } + + // Read out FTDI chip ID + debugPrint("ftdi->type = %d. ", ftdi_->type); + ftdi_read_chipid(ftdi_, &chipid); + printf("FTDI chipid: %X\n", chipid); + + // Set baudrate + if (setBaudrate(baudrate_)) + return FTDIDriverError; + + // Set line parameters + if (setLineProperties(bits_, sbits_, parity_, break_)) + return FTDIDriverError; + + // Set FTDI chip latency in millisecs. + if (setLatency(latency_)) + return FTDIDriverError; + + // Set FTDI flow control + if (setFlowControl(flowctrl_)) + return FTDIDriverError; + + // Reset device after setting the latency, otherwise the initial communication will result in an error + if ((f = ftdi_usb_reset (ftdi_)) != 0) + { + debugPrint("Failed to reset FTDI: %d (%s)\n", f, ftdi_get_error_string(ftdi_)); + return FTDIDriverError; + } + + // Purge the send and receive buffers + if ((f = ftdi_usb_purge_tx_buffer (ftdi_)) != 0) + { + debugPrint("Failed to purge FTDI tx buffer: %d (%s)\n", f, ftdi_get_error_string(ftdi_)); + return FTDIDriverError; + } + + if ((f = ftdi_usb_purge_rx_buffer (ftdi_)) != 0) + { + debugPrint("Failed to purge FTDI rx buffer: %d (%s)\n", f, ftdi_get_error_string(ftdi_)); + return FTDIDriverError; + } + + // Wait 60 ms. for purge to complete + epicsThreadSleep(0.060); + + if ((f = ftdi_read_data_set_chunksize(ftdi_, 8192)) != 0) + { + debugPrint("Failed to set FTDI read chunk size: %d (%s)\n", f, ftdi_get_error_string(ftdi_)); + return FTDIDriverError; + } + + if ((f = ftdi_write_data_set_chunksize(ftdi_, 8192)) != 0) + { + debugPrint("Failed to set FTDI write chunk size: %d (%s)\n", f, ftdi_get_error_string(ftdi_)); + return FTDIDriverError; + } + + debugPrint("%s : FTDI Connection ready...\n", functionName); + connected_ = 1; + + return FTDIDriverSuccess; +} + +/** + * Flush the FTDI connection as best as possible. + * + * @return - Success or failure. + */ +FTDIDriverStatus FTDIDriver::flush() +{ + int f; + static const char *functionName = "FTDIDriver::flush"; + debugPrint("%s : Method called\n", functionName); + + if (connected_ == 0){ + debugPrint("%s : FTDI not connected\n", functionName); + return FTDIDriverError; + } + + // Clears read & write buffers on the chip and the internal read buffer + f = ftdi_usb_purge_buffers(ftdi_); + if (f < 0){ + debugPrint("Failed to purge FTDI buffers: %d (%s)\n", f, ftdi_get_error_string(ftdi_)); + return FTDIDriverError; + } + return FTDIDriverSuccess; +} + +/** + * Write data to the connected channel. A timeout should be + * specified in milliseconds. + * + * @param buffer - The string buffer to be written. + * @param bufferSize - The number of bytes to write. + * @param bytesWritten - The number of bytes that were written. + * @param timeout - A timeout in ms for the write. + * @return - Success or failure. + */ +FTDIDriverStatus FTDIDriver::write(const unsigned char *buffer, int bufferSize, size_t *bytesWritten, int timeout) +{ + size_t rc; + static const char *functionName = "FTDIDriver::write"; + debugPrint("%s : Method called\n", functionName); + *bytesWritten = 0; + + if (connected_ == 0){ + debugPrint("%s : FTDI not connected\n", functionName); + return FTDIDriverError; + } + + debugPrint("%s : Writing\n", functionName); + + timeval stime; + timeval ctime; + gettimeofday(&stime, NULL); + long mtimeout = (stime.tv_usec / 1000) + timeout; + long tnow = 0; + + // Set timeout value + ftdi_->usb_write_timeout = timeout; + +#ifdef HAS_LIBFTDI1 + struct ftdi_transfer_control *tc = ftdi_write_data_submit ( + ftdi_, (unsigned char *) buffer, (int) bufferSize + ); + rc = ftdi_transfer_data_done (tc); +#else + rc = ftdi_write_data(ftdi_, (unsigned char *) buffer, (int) bufferSize); +#endif + + gettimeofday(&ctime, NULL); + tnow = ((ctime.tv_sec - stime.tv_sec) * 1000) + (ctime.tv_usec / 1000); + debugPrint("%s : Time taken for write => %ld ms\n", functionName, (tnow-(mtimeout-timeout))); + if (rc > 0){ + debugPrint("%s : %d bytes written\n", functionName, rc); + *bytesWritten = (size_t)rc; + } else { + debugPrint("FTDI write error: %d (%s)\n", rc, ftdi_get_error_string(ftdi_)); + *bytesWritten = 0; + return FTDIDriverError; + } + + return FTDIDriverSuccess; +} + +/** + * Read data from the connected channel. A timeout should be + * specified in milliseconds. The read method will continue to + * read data from the channel until either data arrives or the + * timeout is reached. + * + * @param buffer - A string buffer to hold the read data. + * @param bufferSize - The maximum number of bytes to read. + * @param bytesWritten - The number of bytes that have been read. + * @param timeout - A timeout in ms for the read. + * @return - Success or failure. + */ +FTDIDriverStatus FTDIDriver::read(unsigned char *buffer, size_t bufferSize, size_t *bytesRead, int timeout) +{ + static const char *functionName = "FTDIDriver::read"; + debugPrint("%s : Method called\n", functionName); + + if (connected_ == 0){ + debugPrint("%s : Not connected\n", functionName); + return FTDIDriverError; + } + + debugPrint("%s : Reading\n", functionName); + + timeval stime; + timeval ctime; + gettimeofday(&stime, NULL); + long mtimeout = (stime.tv_usec / 1000) + timeout; + long tnow = 0; + int rc = 0; + + *bytesRead = 0; + while ((*bytesRead == 0) && (tnow < mtimeout)){ + rc = ftdi_read_data (ftdi_, &buffer[*bytesRead], (bufferSize-*bytesRead)); + if (rc > 0){ + *bytesRead+=rc; + } + if (*bytesRead == 0){ + usleep(50); + gettimeofday(&ctime, NULL); + tnow = ((ctime.tv_sec - stime.tv_sec) * 1000) + (ctime.tv_usec / 1000); + } + } + + if (*bytesRead < bufferSize) + buffer[*bytesRead] = '\0'; + + debugPrint("%s %d Bytes =>\n", functionName, (int)*bytesRead); + for (int j = 0; j < (int)*bytesRead; j++){ + debugPrint("[%0X] ", buffer[j]); + } + debugPrint("\n"); + + gettimeofday(&ctime, NULL); + tnow = ((ctime.tv_sec - stime.tv_sec) * 1000) + (ctime.tv_usec / 1000); + debugPrint("%s : Time taken for read => %ld ms\n", functionName, (tnow-(mtimeout-timeout))); + + if ((timeout > 0) && (tnow >= mtimeout)){ + return FTDIDriverTimeout; + } + + return FTDIDriverSuccess; +} + + +/** + * Close the connection. + * + * @return - Success or failure. + */ +FTDIDriverStatus FTDIDriver::disconnectFTDI() +{ + static const char *functionName = "FTDIDriver::disconnect"; + debugPrint("%s : Method called\n", functionName); + + if (connected_ == 1){ + ftdi_usb_close(ftdi_); + ftdi_deinit(ftdi_); + connected_ = 0; + } + else { + debugPrint("%s : Connection was not established\n", functionName); + } + return FTDIDriverSuccess; +} + +/** + * Destructor, cleanup. + */ +FTDIDriver::~FTDIDriver() +{ + static const char *functionName = "FTDIDriver::~FTDIDriver"; + debugPrint("%s : Method called\n", functionName); +} + +#ifndef _MINGW +#ifdef _WIN32 +/* Windows implemenation of gettimeofday and usleep */ +#include < time.h > +#include < windows.h > + +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 +#else + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif + +struct timezone +{ + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag = 0; + + if (NULL != tv) + { + GetSystemTimeAsFileTime(&ft); + + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + tmpres /= 10; /*convert into microseconds*/ + /*converting file time to unix epoch*/ + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + + if (NULL != tz) + { + if (!tzflag) + { + _tzset(); + tzflag++; + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; +} + +void usleep(__int64 usec) +{ + HANDLE timer; + LARGE_INTEGER ft; + + ft.QuadPart = -(10*usec); // Convert to 100 nanosecond interval, negative value indicates relative time + + timer = CreateWaitableTimer(NULL, TRUE, NULL); + SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); +} +#endif +#endif \ No newline at end of file diff --git a/asyn/drvAsynFTDI/ftdiDriver.h b/asyn/drvAsynFTDI/ftdiDriver.h new file mode 100644 index 0000000..8ec1bdb --- /dev/null +++ b/asyn/drvAsynFTDI/ftdiDriver.h @@ -0,0 +1,88 @@ + +#ifndef ftdiDriver_H +#define ftdiDriver_H + +#ifdef HAVE_LIBFTDI1 +#include +#else +#include +#endif + +#include + +# ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include +#include +#include +#include +#include + +typedef enum e_FTDIDriverStatus +{ + FTDIDriverSuccess, + FTDIDriverTimeout, + FTDIDriverError +} FTDIDriverStatus; + +/** + * The FTDIDriver class provides a wrapper around the libftdi1 library. + * It simplifies creating FTDI connections and provides a simple read/write/flush + * interface. Setting up an FTDI chip USB connection can be configured with a + * Vendor ID, Product ID, baudrate & latency + * + * @author Philip Taylor (pbt@observatorysciences.co.uk) + */ +class FTDIDriver { + + public: + FTDIDriver(); + FTDIDriverStatus setVPID(const int vendor, const int product); + FTDIDriverStatus setBaudrate(const int baudrate); + FTDIDriverStatus setBits(enum ftdi_bits_type bits); + FTDIDriverStatus setStopBits(enum ftdi_stopbits_type sbits); + FTDIDriverStatus setParity(enum ftdi_parity_type parity); + FTDIDriverStatus setBreak(enum ftdi_break_type brk); + FTDIDriverStatus setFlowControl(int flowctrl); + FTDIDriverStatus setLatency(const int latency); + FTDIDriverStatus setLineProperties(enum ftdi_bits_type bits, + enum ftdi_stopbits_type sbits, enum ftdi_parity_type parity, + enum ftdi_break_type brk); + int getBaudRate(void); + enum ftdi_bits_type getBits(void); + enum ftdi_stopbits_type getStopBits(void); + enum ftdi_parity_type getParity(void); + enum ftdi_break_type getBreak(void); + int getFlowControl(void); + FTDIDriverStatus connectFTDI(); + FTDIDriverStatus flush(); + FTDIDriverStatus write(const unsigned char *buffer, int bufferSize, size_t *bytesWritten, int timeout); + FTDIDriverStatus read(unsigned char *buffer, size_t bufferSize, size_t *bytesRead, int timeout); + FTDIDriverStatus disconnectFTDI(); + virtual ~FTDIDriver(); + + private: + struct ftdi_context *ftdi_; + int connected_; + int vendor_; + int product_; + int baudrate_; + enum ftdi_bits_type bits_; + enum ftdi_stopbits_type sbits_; + enum ftdi_parity_type parity_; + enum ftdi_break_type break_; + int flowctrl_; + int latency_; + off_t got_; + +}; + + +#endif + + diff --git a/asyn/drvAsynSerial/drvAsynIPPort.c b/asyn/drvAsynSerial/drvAsynIPPort.c index 35da4cf..95173bb 100644 --- a/asyn/drvAsynSerial/drvAsynIPPort.c +++ b/asyn/drvAsynSerial/drvAsynIPPort.c @@ -852,6 +852,7 @@ getOption(void *drvPvt, asynUser *pasynUser, ttyController_t *tty = (ttyController_t *)drvPvt; int l; + val[0] = '\0'; assert(tty); if (epicsStrCaseCmp(key, "disconnectOnReadTimeout") == 0) { l = epicsSnprintf(val, valSize, "%c", tty->disconnectOnReadTimeout ? 'Y' : 'N'); diff --git a/asyn/drvAsynSerial/drvAsynSerialPort.c b/asyn/drvAsynSerial/drvAsynSerialPort.c index 0b0caf5..79ef542 100644 --- a/asyn/drvAsynSerial/drvAsynSerialPort.c +++ b/asyn/drvAsynSerial/drvAsynSerialPort.c @@ -139,6 +139,7 @@ getOption(void *drvPvt, asynUser *pasynUser, ttyController_t *tty = (ttyController_t *)drvPvt; int l; + val[0] = '\0'; if (epicsStrCaseCmp(key, "baud") == 0) { l = epicsSnprintf(val, valSize, "%d", tty->baud); } diff --git a/asyn/drvAsynSerial/drvAsynSerialPortWin32.c b/asyn/drvAsynSerial/drvAsynSerialPortWin32.c index 0bbea87..3ab98c2 100644 --- a/asyn/drvAsynSerial/drvAsynSerialPortWin32.c +++ b/asyn/drvAsynSerial/drvAsynSerialPortWin32.c @@ -91,6 +91,7 @@ getOption(void *drvPvt, asynUser *pasynUser, DWORD error; int l; + val[0] = '\0'; assert(tty); if (tty->commHandle == INVALID_HANDLE_VALUE) { epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, diff --git a/asyn/interfaces/asynInt64.h b/asyn/interfaces/asynInt64.h new file mode 100644 index 0000000..0a1f82c --- /dev/null +++ b/asyn/interfaces/asynInt64.h @@ -0,0 +1,65 @@ +/* asynInt64.h */ +/*********************************************************************** +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory, and the Regents of the University of +* California, as Operator of Los Alamos National Laboratory, and +* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY). +* asynDriver is distributed subject to a Software License Agreement +* found in file LICENSE that is included with this distribution. +***********************************************************************/ + +/* 28-June-2004 Mark Rivers +*/ + +#ifndef asynInt64H +#define asynInt64H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef void (*interruptCallbackInt64)(void *userPvt, asynUser *pasynUser, + epicsInt64 data); +typedef struct asynInt64Interrupt { + int addr; + asynUser *pasynUser; + interruptCallbackInt64 callback; + void *userPvt; +} asynInt64Interrupt; +#define asynInt64Type "asynInt64" +typedef struct asynInt64 { + asynStatus (*write)(void *drvPvt, asynUser *pasynUser, epicsInt64 value); + asynStatus (*read)(void *drvPvt, asynUser *pasynUser, epicsInt64 *value); + asynStatus (*getBounds)(void *drvPvt, asynUser *pasynUser, + epicsInt64 *low, epicsInt64 *high); + asynStatus (*registerInterruptUser)(void *drvPvt,asynUser *pasynUser, + interruptCallbackInt64 callback, void *userPvt, + void **registrarPvt); + asynStatus (*cancelInterruptUser)(void *drvPvt, asynUser *pasynUser, + void *registrarPvt); +} asynInt64; + +/* asynInt64Base does the following: + calls registerInterface for asynInt64. + Implements registerInterruptUser and cancelInterruptUser + Provides default implementations of all methods. + registerInterruptUser and cancelInterruptUser can be called + directly rather than via queueRequest. +*/ + +#define asynInt64BaseType "asynInt64Base" +typedef struct asynInt64Base { + asynStatus (*initialize)(const char *portName, + asynInterface *pint64Interface); +} asynInt64Base; +epicsShareExtern asynInt64Base *pasynInt64Base; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* asynInt64H */ diff --git a/asyn/interfaces/asynInt64Array.h b/asyn/interfaces/asynInt64Array.h new file mode 100644 index 0000000..c889ea8 --- /dev/null +++ b/asyn/interfaces/asynInt64Array.h @@ -0,0 +1,59 @@ +/* asynInt64Array.h */ +/*********************************************************************** +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory, and the Regents of the University of +* California, as Operator of Los Alamos National Laboratory, and +* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY). +* asynDriver is distributed subject to a Software License Agreement +* found in file LICENSE that is included with this distribution. +***********************************************************************/ + +/* 28-June-2004 Mark Rivers + +*/ + +#ifndef asynInt64ArrayH +#define asynInt64ArrayH + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef void (*interruptCallbackInt64Array)( + void *userPvt, asynUser *pasynUser, + epicsInt64 *value, size_t nelements); +typedef struct asynInt64ArrayInterrupt { + asynUser *pasynUser; + int addr; + interruptCallbackInt64Array callback; + void *userPvt; +} asynInt64ArrayInterrupt; +#define asynInt64ArrayType "asynInt64Array" +typedef struct asynInt64Array { + asynStatus (*write)(void *drvPvt, asynUser *pasynUser, + epicsInt64 *value, size_t nelements); + asynStatus (*read)(void *drvPvt, asynUser *pasynUser, + epicsInt64 *value, size_t nelements, size_t *nIn); + asynStatus (*registerInterruptUser)(void *drvPvt, asynUser *pasynUser, + interruptCallbackInt64Array callback, void *userPvt, + void **registrarPvt); + asynStatus (*cancelInterruptUser)(void *drvPvt, asynUser *pasynUser, + void *registrarPvt); +} asynInt64Array; + +#define asynInt64ArrayBaseType "asynInt64ArrayBase" +typedef struct asynInt64ArrayBase { + asynStatus (*initialize)(const char *portName, + asynInterface *pint64ArrayInterface); +} asynInt64ArrayBase; +epicsShareExtern asynInt64ArrayBase *pasynInt64ArrayBase; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* asynInt64ArrayH */ diff --git a/asyn/interfaces/asynInt64ArrayBase.c b/asyn/interfaces/asynInt64ArrayBase.c new file mode 100644 index 0000000..2120809 --- /dev/null +++ b/asyn/interfaces/asynInt64ArrayBase.c @@ -0,0 +1,27 @@ +/* asynInt64ArrayBase.c */ +/*********************************************************************** +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory, and the Regents of the University of +* California, as Operator of Los Alamos National Laboratory, and +* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY). +* asynDriver is distributed subject to a Software License Agreement +* found in file LICENSE that is included with this distribution. +***********************************************************************/ + +/* 11-OCT-2004 Marty Kraimer + * 26-MAR-2008 Mark Rivers, converted to use macro +*/ + +#include +#include + +#define epicsExportSharedSymbols +#include +#include "asynDriver.h" +#include "asynInt64Array.h" + +#include "asynXXXArrayBase.h" + +ASYN_XXX_ARRAY_BASE_FUNCS(asynInt64Array, asynInt64ArrayType, asynInt64ArrayBase, pasynInt64ArrayBase, + asynInt64ArrayInterrupt, interruptCallbackInt64Array, epicsInt64) + diff --git a/asyn/interfaces/asynInt64ArraySyncIO.c b/asyn/interfaces/asynInt64ArraySyncIO.c new file mode 100644 index 0000000..00d6af8 --- /dev/null +++ b/asyn/interfaces/asynInt64ArraySyncIO.c @@ -0,0 +1,218 @@ +/*asynInt64ArraySyncIO.c*/ +/*********************************************************************** +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory, and the Regents of the University of +* California, as Operator of Los Alamos National Laboratory, and +* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY). +* asynDriver is distributed subject to a Software License Agreement +* found in file LICENSE that is included with this distribution. +***********************************************************************/ +/* + * This package provide a simple, synchronous interface to asynInt64Array + * Author: Mark Rivers + * Created: 13APR2008 + */ + +#include +#include +#include + +#include + +#define epicsExportSharedSymbols +#include +#include "asynDriver.h" +#include "asynInt64Array.h" +#include "asynDrvUser.h" +#include "asynInt64ArraySyncIO.h" + +typedef struct ioPvt{ + asynCommon *pasynCommon; + void *pcommonPvt; + asynInt64Array *pasynInt64Array; + void *int64ArrayPvt; + asynDrvUser *pasynDrvUser; + void *drvUserPvt; +}ioPvt; + +/*asynInt64ArraySyncIO methods*/ +static asynStatus connect(const char *port, int addr, + asynUser **ppasynUser, const char *drvInfo); +static asynStatus disconnect(asynUser *pasynUser); +static asynStatus writeOp(asynUser *pasynUser,epicsInt64 *pvalue,size_t nelem,double timeout); +static asynStatus readOp(asynUser *pasynUser,epicsInt64 *pvalue,size_t nelem,size_t *nIn,double timeout); +static asynStatus writeOpOnce(const char *port, int addr, + epicsInt64 *pvalue, size_t nelem, double timeout, const char *drvInfo); +static asynStatus readOpOnce(const char *port, int addr, + epicsInt64 *pvalue,size_t nelem, size_t *nIn, double timeout, const char *drvInfo); +static asynInt64ArraySyncIO interface = { + connect, + disconnect, + writeOp, + readOp, + writeOpOnce, + readOpOnce +}; +epicsShareDef asynInt64ArraySyncIO *pasynInt64ArraySyncIO = &interface; + +static asynStatus connect(const char *port, int addr, + asynUser **ppasynUser, const char *drvInfo) +{ + ioPvt *pioPvt; + asynUser *pasynUser; + asynStatus status; + asynInterface *pasynInterface; + + pioPvt = (ioPvt *)callocMustSucceed(1, sizeof(ioPvt),"asynInt64ArraySyncIO"); + pasynUser = pasynManager->createAsynUser(0,0); + pasynUser->userPvt = pioPvt; + *ppasynUser = pasynUser; + status = pasynManager->connectDevice(pasynUser, port, addr); + if (status != asynSuccess) { + return status; + } + pasynInterface = pasynManager->findInterface(pasynUser, asynCommonType, 1); + if (!pasynInterface) { + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "interface %s is not supported by port",asynCommonType); + return asynError; + } + pioPvt->pasynCommon = (asynCommon *)pasynInterface->pinterface; + pioPvt->pcommonPvt = pasynInterface->drvPvt; + pasynInterface = pasynManager->findInterface(pasynUser, asynInt64ArrayType, 1); + if (!pasynInterface) { + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "interface %s is not supported by port",asynInt64ArrayType); + return asynError; + } + pioPvt->pasynInt64Array = (asynInt64Array *)pasynInterface->pinterface; + pioPvt->int64ArrayPvt = pasynInterface->drvPvt; + if(drvInfo) { + /* Check for asynDrvUser interface */ + pasynInterface = pasynManager->findInterface(pasynUser,asynDrvUserType,1); + if(pasynInterface) { + asynDrvUser *pasynDrvUser; + void *drvPvt; + pasynDrvUser = (asynDrvUser *)pasynInterface->pinterface; + drvPvt = pasynInterface->drvPvt; + status = pasynDrvUser->create(drvPvt,pasynUser,drvInfo,0,0); + if(status==asynSuccess) { + pioPvt->pasynDrvUser = pasynDrvUser; + pioPvt->drvUserPvt = drvPvt; + } else { + return status; + } + } + } + return asynSuccess ; +} + +static asynStatus disconnect(asynUser *pasynUser) +{ + ioPvt *pioPvt = (ioPvt *)pasynUser->userPvt; + asynStatus status; + + if(pioPvt->pasynDrvUser) { + status = pioPvt->pasynDrvUser->destroy(pioPvt->drvUserPvt,pasynUser); + if(status!=asynSuccess) { + return status; + } + } + status = pasynManager->freeAsynUser(pasynUser); + if(status!=asynSuccess) { + return status; + } + free(pioPvt); + return asynSuccess; +} + +static asynStatus writeOp(asynUser *pasynUser,epicsInt64 *pvalue,size_t nelem,double timeout) +{ + asynStatus status, unlockStatus; + ioPvt *pPvt = (ioPvt *)pasynUser->userPvt; + + pasynUser->timeout = timeout; + status = pasynManager->queueLockPort(pasynUser); + if(status!=asynSuccess) { + return status; + } + status = pPvt->pasynInt64Array->write(pPvt->int64ArrayPvt, pasynUser,pvalue,nelem); + if (status==asynSuccess) { + asynPrint(pasynUser, ASYN_TRACEIO_DEVICE, + "asynInt64ArraySyncIO wrote: %lld\n", + *pvalue); + } + unlockStatus = pasynManager->queueUnlockPort(pasynUser); + if (unlockStatus != asynSuccess) { + return unlockStatus; + } + return status; +} + +static asynStatus readOp(asynUser *pasynUser,epicsInt64 *pvalue,size_t nelem,size_t *nIn,double timeout) +{ + ioPvt *pPvt = (ioPvt *)pasynUser->userPvt; + asynStatus status, unlockStatus; + + pasynUser->timeout = timeout; + status = pasynManager->queueLockPort(pasynUser); + if(status!=asynSuccess) { + return status; + } + status = pPvt->pasynInt64Array->read(pPvt->int64ArrayPvt, pasynUser, pvalue, nelem, nIn); + if (status==asynSuccess) { + asynPrint(pasynUser, ASYN_TRACEIO_DEVICE, + "asynInt64ArraySyncIO read: %lld\n", *pvalue); + } + unlockStatus = pasynManager->queueUnlockPort(pasynUser); + if (unlockStatus != asynSuccess) { + return unlockStatus; + } + return status; +} + +static asynStatus writeOpOnce(const char *port, int addr, + epicsInt64 *pvalue,size_t nelem,double timeout,const char *drvInfo) +{ + asynStatus status; + asynUser *pasynUser; + + status = connect(port,addr,&pasynUser,drvInfo); + if(status!=asynSuccess) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "asynInt64ArraySyncIO connect failed %s\n", + pasynUser->errorMessage); + disconnect(pasynUser); + return status; + } + status = writeOp(pasynUser,pvalue,nelem,timeout); + if(status!=asynSuccess) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "asynInt64ArraySyncIO writeOp failed %s\n",pasynUser->errorMessage); + } + disconnect(pasynUser); + return status; +} + +static asynStatus readOpOnce(const char *port, int addr, + epicsInt64 *pvalue,size_t nelem,size_t *nIn,double timeout,const char *drvInfo) +{ + asynStatus status; + asynUser *pasynUser; + + status = connect(port,addr,&pasynUser,drvInfo); + if(status!=asynSuccess) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "asynInt64ArraySyncIO connect failed %s\n", + pasynUser->errorMessage); + disconnect(pasynUser); + return status; + } + status = readOp(pasynUser,pvalue,nelem,nIn,timeout); + if(status!=asynSuccess) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "asynInt64ArraySyncIO readOp failed %s\n",pasynUser->errorMessage); + } + disconnect(pasynUser); + return status; +} diff --git a/asyn/interfaces/asynInt64ArraySyncIO.h b/asyn/interfaces/asynInt64ArraySyncIO.h new file mode 100644 index 0000000..3f530cb --- /dev/null +++ b/asyn/interfaces/asynInt64ArraySyncIO.h @@ -0,0 +1,43 @@ +/* asynInt64ArraySyncIO.h */ +/*********************************************************************** +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory, and the Regents of the University of +* California, as Operator of Los Alamos National Laboratory, and +* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY). +* asynDriver is distributed subject to a Software License Agreement +* found in file LICENSE that is included with this distribution. +***********************************************************************/ + +/* 28-June-2004 Mark Rivers +*/ + +#ifndef asynInt64ArraySyncIOH +#define asynInt64ArraySyncIOH + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define asynInt64ArraySyncIOType "asynInt64ArraySyncIO" +typedef struct asynInt64ArraySyncIO { + asynStatus (*connect)(const char *port, int addr, + asynUser **ppasynUser, const char *drvInfo); + asynStatus (*disconnect)(asynUser *pasynUser); + asynStatus (*write)(asynUser *pasynUser,epicsInt64 *pvalue,size_t nelem,double timeout); + asynStatus (*read)(asynUser *pasynUser,epicsInt64 *pvalue,size_t nelem,size_t *Nin,double timeout); + asynStatus (*writeOnce)(const char *port, int addr, + epicsInt64 *pvalue,size_t nelem,double timeout,const char *drvInfo); + asynStatus (*readOnce)(const char *port, int addr, + epicsInt64 *pvalue,size_t nelem,size_t *nIn,double timeout,const char *drvInfo); +} asynInt64ArraySyncIO; +epicsShareExtern asynInt64ArraySyncIO *pasynInt64ArraySyncIO; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* asynInt64ArraySyncIOH */ diff --git a/asyn/interfaces/asynInt64Base.c b/asyn/interfaces/asynInt64Base.c new file mode 100644 index 0000000..efd50c7 --- /dev/null +++ b/asyn/interfaces/asynInt64Base.c @@ -0,0 +1,161 @@ +/* asynInt64Base.c */ +/*********************************************************************** +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory, and the Regents of the University of +* California, as Operator of Los Alamos National Laboratory, and +* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY). +* asynDriver is distributed subject to a Software License Agreement +* found in file LICENSE that is included with this distribution. +***********************************************************************/ + +/* 11-OCT-2004 Marty Kraimer +*/ + +#include +#include + +#define epicsExportSharedSymbols +#include +#include "asynDriver.h" +#include "asynInt64.h" + +static asynStatus initialize(const char *portName, asynInterface *pint64Interface); + +static asynInt64Base int64Base = {initialize}; +epicsShareDef asynInt64Base *pasynInt64Base = &int64Base; + +static asynStatus writeDefault(void *drvPvt, asynUser *pasynUser, + epicsInt64 value); +static asynStatus readDefault(void *drvPvt, asynUser *pasynUser, + epicsInt64 *value); +static asynStatus getBounds(void *drvPvt, asynUser *pasynUser, + epicsInt64 *low, epicsInt64 *high); +static asynStatus registerInterruptUser(void *drvPvt,asynUser *pasynUser, + interruptCallbackInt64 callback, void *userPvt, + void **registrarPvt); +static asynStatus cancelInterruptUser(void *drvPvt, asynUser *pasynUser, + void *registrarPvt); + + +asynStatus initialize(const char *portName, asynInterface *pdriver) +{ + asynInt64 *pasynInt64 = (asynInt64 *)pdriver->pinterface; + + if(!pasynInt64->write) pasynInt64->write = writeDefault; + if(!pasynInt64->read) pasynInt64->read = readDefault; + if(!pasynInt64->getBounds) pasynInt64->getBounds = getBounds; + if(!pasynInt64->registerInterruptUser) + pasynInt64->registerInterruptUser = registerInterruptUser; + if(!pasynInt64->cancelInterruptUser) + pasynInt64->cancelInterruptUser = cancelInterruptUser; + return pasynManager->registerInterface(portName,pdriver); +} + +static asynStatus writeDefault(void *drvPvt, asynUser *pasynUser, + epicsInt64 value) +{ + const char *portName; + asynStatus status; + int addr; + + status = pasynManager->getPortName(pasynUser,&portName); + if(status!=asynSuccess) return status; + status = pasynManager->getAddr(pasynUser,&addr); + if(status!=asynSuccess) return status; + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "write is not supported"); + asynPrint(pasynUser,ASYN_TRACE_ERROR, + "%s %d write is not supported\n",portName,addr); + return asynError; +} + +static asynStatus readDefault(void *drvPvt, asynUser *pasynUser, + epicsInt64 *value) +{ + const char *portName; + asynStatus status; + int addr; + + status = pasynManager->getPortName(pasynUser,&portName); + if(status!=asynSuccess) return status; + status = pasynManager->getAddr(pasynUser,&addr); + if(status!=asynSuccess) return status; + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "write is not supported"); + asynPrint(pasynUser,ASYN_TRACE_ERROR, + "%s %d read is not supported\n",portName,addr); + return asynError; +} + +static asynStatus getBounds(void *drvPvt, asynUser *pasynUser, + epicsInt64 *low, epicsInt64 *high) +{ + const char *portName; + asynStatus status; + int addr; + + status = pasynManager->getPortName(pasynUser,&portName); + if(status!=asynSuccess) return status; + status = pasynManager->getAddr(pasynUser,&addr); + if(status!=asynSuccess) return status; + *low = *high = 0; + asynPrint(pasynUser,ASYN_TRACE_FLOW, + "%s %d getBounds setting low=high=0\n",portName,addr); + return asynSuccess; +} + +static asynStatus registerInterruptUser(void *drvPvt,asynUser *pasynUser, + interruptCallbackInt64 callback, void *userPvt, + void **registrarPvt) +{ + const char *portName; + asynStatus status; + int addr; + interruptNode *pinterruptNode; + void *pinterruptPvt; + asynInt64Interrupt *pasynInt64Interrupt; + + status = pasynManager->getPortName(pasynUser,&portName); + if(status!=asynSuccess) return status; + status = pasynManager->getAddr(pasynUser,&addr); + if(status!=asynSuccess) return status; + status = pasynManager->getInterruptPvt(pasynUser, asynInt64Type, + &pinterruptPvt); + if(status!=asynSuccess) return status; + pasynInt64Interrupt = pasynManager->memMalloc(sizeof(asynInt64Interrupt)); + pinterruptNode = pasynManager->createInterruptNode(pinterruptPvt); + pinterruptNode->drvPvt = pasynInt64Interrupt; + pasynInt64Interrupt->pasynUser = + pasynManager->duplicateAsynUser(pasynUser, NULL, NULL); + pasynInt64Interrupt->addr = addr; + pasynInt64Interrupt->callback = callback; + pasynInt64Interrupt->userPvt = userPvt; + *registrarPvt = pinterruptNode; + asynPrint(pasynUser,ASYN_TRACE_FLOW, + "%s %d registerInterruptUser\n",portName,addr); + return pasynManager->addInterruptUser(pasynUser,pinterruptNode); +} + +static asynStatus cancelInterruptUser(void *drvPvt, asynUser *pasynUser, + void *registrarPvt) +{ + interruptNode *pinterruptNode = (interruptNode *)registrarPvt; + asynInt64Interrupt *pasynInt64Interrupt = + (asynInt64Interrupt *)pinterruptNode->drvPvt; + asynStatus status; + const char *portName; + int addr; + + status = pasynManager->getPortName(pasynUser,&portName); + if(status!=asynSuccess) return status; + status = pasynManager->getAddr(pasynUser,&addr); + if(status!=asynSuccess) return status; + asynPrint(pasynUser,ASYN_TRACE_FLOW, + "%s %d cancelInterruptUser\n",portName,addr); + status = pasynManager->removeInterruptUser(pasynUser,pinterruptNode); + if(status==asynSuccess) + pasynManager->freeInterruptNode(pasynUser, pinterruptNode); + pasynManager->freeAsynUser(pasynInt64Interrupt->pasynUser); + pasynManager->memFree(pasynInt64Interrupt, sizeof(asynInt64Interrupt)); + return status; +} diff --git a/asyn/interfaces/asynInt64SyncIO.c b/asyn/interfaces/asynInt64SyncIO.c new file mode 100644 index 0000000..9940352 --- /dev/null +++ b/asyn/interfaces/asynInt64SyncIO.c @@ -0,0 +1,268 @@ +/*asynInt64SyncIO.c*/ +/*********************************************************************** +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory, and the Regents of the University of +* California, as Operator of Los Alamos National Laboratory, and +* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY). +* asynDriver is distributed subject to a Software License Agreement +* found in file LICENSE that is included with this distribution. +***********************************************************************/ +/* + * This package provide a simple, synchronous interface to asynInt64 + * Author: Marty Kraimer + * Created: 12OCT2004 + */ + +#include +#include +#include + +#include + +#define epicsExportSharedSymbols +#include +#include "asynDriver.h" +#include "asynInt64.h" +#include "asynDrvUser.h" +#include "asynInt64SyncIO.h" + +typedef struct ioPvt{ + asynCommon *pasynCommon; + void *pcommonPvt; + asynInt64 *pasynInt64; + void *int64Pvt; + asynDrvUser *pasynDrvUser; + void *drvUserPvt; +}ioPvt; + +/*asynInt64SyncIO methods*/ +static asynStatus connect(const char *port, int addr, + asynUser **ppasynUser, const char *drvInfo); +static asynStatus disconnect(asynUser *pasynUser); +static asynStatus writeOp(asynUser *pasynUser, epicsInt64 value,double timeout); +static asynStatus readOp(asynUser *pasynUser,epicsInt64 *pvalue,double timeout); +static asynStatus getBounds(asynUser *pasynUser, + epicsInt64 *plow, epicsInt64 *phigh); +static asynStatus writeOpOnce(const char *port, int addr, + epicsInt64 value,double timeout,const char *drvInfo); +static asynStatus readOpOnce(const char *port, int addr, + epicsInt64 *pvalue,double timeout,const char *drvInfo); +static asynStatus getBoundsOnce(const char *port, int addr, + epicsInt64 *plow, epicsInt64 *phigh,const char *drvInfo); +static asynInt64SyncIO interface = { + connect, + disconnect, + writeOp, + readOp, + getBounds, + writeOpOnce, + readOpOnce, + getBoundsOnce +}; +epicsShareDef asynInt64SyncIO *pasynInt64SyncIO = &interface; + +static asynStatus connect(const char *port, int addr, + asynUser **ppasynUser, const char *drvInfo) +{ + ioPvt *pioPvt; + asynUser *pasynUser; + asynStatus status; + asynInterface *pasynInterface; + + pioPvt = (ioPvt *)callocMustSucceed(1, sizeof(ioPvt),"asynInt64SyncIO"); + pasynUser = pasynManager->createAsynUser(0,0); + pasynUser->userPvt = pioPvt; + *ppasynUser = pasynUser; + status = pasynManager->connectDevice(pasynUser, port, addr); + if (status != asynSuccess) { + return status; + } + pasynInterface = pasynManager->findInterface(pasynUser, asynCommonType, 1); + if (!pasynInterface) { + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "port does not implement interface %s",asynCommonType); + return asynError; + } + pioPvt->pasynCommon = (asynCommon *)pasynInterface->pinterface; + pioPvt->pcommonPvt = pasynInterface->drvPvt; + pasynInterface = pasynManager->findInterface(pasynUser, asynInt64Type, 1); + if (!pasynInterface) { + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, + "port does not implement interface %s",asynInt64Type); + return asynError; + } + pioPvt->pasynInt64 = (asynInt64 *)pasynInterface->pinterface; + pioPvt->int64Pvt = pasynInterface->drvPvt; + if(drvInfo) { + /* Check for asynDrvUser interface */ + pasynInterface = pasynManager->findInterface(pasynUser,asynDrvUserType,1); + if(pasynInterface) { + asynDrvUser *pasynDrvUser; + void *drvPvt; + pasynDrvUser = (asynDrvUser *)pasynInterface->pinterface; + drvPvt = pasynInterface->drvPvt; + status = pasynDrvUser->create(drvPvt,pasynUser,drvInfo,0,0); + if(status==asynSuccess) { + pioPvt->pasynDrvUser = pasynDrvUser; + pioPvt->drvUserPvt = drvPvt; + } else { + return status; + } + } + } + return asynSuccess ; +} + +static asynStatus disconnect(asynUser *pasynUser) +{ + ioPvt *pioPvt = (ioPvt *)pasynUser->userPvt; + asynStatus status; + + if(pioPvt->pasynDrvUser) { + status = pioPvt->pasynDrvUser->destroy(pioPvt->drvUserPvt,pasynUser); + if(status!=asynSuccess) { + return status; + } + } + status = pasynManager->freeAsynUser(pasynUser); + if(status!=asynSuccess) { + return status; + } + free(pioPvt); + return asynSuccess; +} + + +static asynStatus writeOp(asynUser *pasynUser, epicsInt64 value,double timeout) +{ + asynStatus status, unlockStatus; + ioPvt *pioPvt = (ioPvt *)pasynUser->userPvt; + + pasynUser->timeout = timeout; + status = pasynManager->queueLockPort(pasynUser); + if(status!=asynSuccess) { + return status; + } + status = pioPvt->pasynInt64->write(pioPvt->int64Pvt, pasynUser, value); + if (status==asynSuccess) { + asynPrint(pasynUser, ASYN_TRACEIO_DEVICE, + "asynInt64SyncIO wrote: %lld\n", value); + } + unlockStatus = pasynManager->queueUnlockPort(pasynUser); + if (unlockStatus != asynSuccess) { + return unlockStatus; + } + return(status); +} + +static asynStatus readOp(asynUser *pasynUser, epicsInt64 *pvalue, double timeout) +{ + ioPvt *pioPvt = (ioPvt *)pasynUser->userPvt; + asynStatus status, unlockStatus; + + pasynUser->timeout = timeout; + status = pasynManager->queueLockPort(pasynUser); + if(status!=asynSuccess) { + return status; + } + status = pioPvt->pasynInt64->read(pioPvt->int64Pvt, pasynUser, pvalue); + if (status==asynSuccess) { + asynPrint(pasynUser, ASYN_TRACEIO_DEVICE, + "asynInt64SyncIO read: %lld\n", *pvalue); + } + unlockStatus = pasynManager->queueUnlockPort(pasynUser); + if (unlockStatus != asynSuccess) { + return unlockStatus; + } + return(status); +} + +static asynStatus getBounds(asynUser *pasynUser, + epicsInt64 *plow, epicsInt64 *phigh) +{ + ioPvt *pioPvt = (ioPvt *)pasynUser->userPvt; + asynStatus status, unlockStatus; + + status = pasynManager->queueLockPort(pasynUser); + if(status!=asynSuccess) { + return status; + } + status = pioPvt->pasynInt64->getBounds(pioPvt->int64Pvt,pasynUser,plow,phigh); + asynPrint(pasynUser, ASYN_TRACE_FLOW, + "asynInt64SyncIO getBounds: status=%d low %lld high %lld\n", + status, *plow,*phigh); + unlockStatus = pasynManager->queueUnlockPort(pasynUser); + if (unlockStatus != asynSuccess) { + return unlockStatus; + } + return(status); +} + +static asynStatus writeOpOnce(const char *port, int addr, + epicsInt64 value,double timeout,const char *drvInfo) +{ + asynStatus status; + asynUser *pasynUser; + + status = connect(port,addr,&pasynUser,drvInfo); + if(status!=asynSuccess) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "asynInt64SyncIO connect failed %s\n", + pasynUser->errorMessage); + disconnect(pasynUser); + return status; + } + status = writeOp(pasynUser,value,timeout); + if(status!=asynSuccess) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "asynInt64SyncIO writeOp failed %s\n",pasynUser->errorMessage); + } + disconnect(pasynUser); + return status; +} + +static asynStatus readOpOnce(const char *port, int addr, + epicsInt64 *pvalue,double timeout,const char *drvInfo) +{ + asynStatus status; + asynUser *pasynUser; + + status = connect(port,addr,&pasynUser,drvInfo); + if(status!=asynSuccess) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "asynInt64SyncIO connect failed %s\n", + pasynUser->errorMessage); + disconnect(pasynUser); + return status; + } + status = readOp(pasynUser,pvalue,timeout); + if(status!=asynSuccess) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "asynInt64SyncIO readOp failed %s\n",pasynUser->errorMessage); + } + disconnect(pasynUser); + return status; +} + +static asynStatus getBoundsOnce(const char *port, int addr, + epicsInt64 *plow, epicsInt64 *phigh,const char *drvInfo) +{ + asynStatus status; + asynUser *pasynUser; + + status = connect(port,addr,&pasynUser,drvInfo); + if(status!=asynSuccess) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "asynInt64SyncIO connect failed %s\n", + pasynUser->errorMessage); + disconnect(pasynUser); + return status; + } + status = getBounds(pasynUser,plow,phigh); + if(status!=asynSuccess) { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "asynInt64SyncIO getBounds failed %s\n",pasynUser->errorMessage); + } + disconnect(pasynUser); + return(status); +} diff --git a/asyn/interfaces/asynInt64SyncIO.h b/asyn/interfaces/asynInt64SyncIO.h new file mode 100644 index 0000000..3d028cd --- /dev/null +++ b/asyn/interfaces/asynInt64SyncIO.h @@ -0,0 +1,47 @@ +/* asynInt64SyncIO.h */ +/*********************************************************************** +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory, and the Regents of the University of +* California, as Operator of Los Alamos National Laboratory, and +* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY). +* asynDriver is distributed subject to a Software License Agreement +* found in file LICENSE that is included with this distribution. +***********************************************************************/ + +/* 28-June-2004 Mark Rivers +*/ + +#ifndef asynInt64SyncIOH +#define asynInt64SyncIOH + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define asynInt64SyncIOType "asynInt64SyncIO" +typedef struct asynInt64SyncIO { + asynStatus (*connect)(const char *port, int addr, + asynUser **ppasynUser, const char *drvInfo); + asynStatus (*disconnect)(asynUser *pasynUser); + asynStatus (*write)(asynUser *pasynUser, epicsInt64 value,double timeout); + asynStatus (*read)(asynUser *pasynUser, epicsInt64 *pvalue,double timeout); + asynStatus (*getBounds)(asynUser *pasynUser, + epicsInt64 *plow, epicsInt64 *phigh); + asynStatus (*writeOnce)(const char *port, int addr, + epicsInt64 value,double timeout, const char *drvInfo); + asynStatus (*readOnce)(const char *port, int addr, + epicsInt64 *pvalue,double timeout, const char *drvInfo); + asynStatus (*getBoundsOnce)(const char *port, int addr, + epicsInt64 *plow, epicsInt64 *phigh,const char *drvInfo); +} asynInt64SyncIO; +epicsShareExtern asynInt64SyncIO *pasynInt64SyncIO; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* asynInt64SyncIOH */ diff --git a/asyn/interfaces/asynStandardInterfaces.h b/asyn/interfaces/asynStandardInterfaces.h index be0d72a..db47961 100644 --- a/asyn/interfaces/asynStandardInterfaces.h +++ b/asyn/interfaces/asynStandardInterfaces.h @@ -20,9 +20,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -58,6 +60,10 @@ typedef struct asynStandardInterfaces { int int32CanInterrupt; void *int32InterruptPvt; + asynInterface int64; + int int64CanInterrupt; + void *int64InterruptPvt; + asynInterface float64; int float64CanInterrupt; void *float64InterruptPvt; @@ -74,6 +80,10 @@ typedef struct asynStandardInterfaces { int int32ArrayCanInterrupt; void *int32ArrayInterruptPvt; + asynInterface int64Array; + int int64ArrayCanInterrupt; + void *int64ArrayInterruptPvt; + asynInterface float32Array; int float32ArrayCanInterrupt; void *float32ArrayInterruptPvt; diff --git a/asyn/interfaces/asynStandardInterfacesBase.c b/asyn/interfaces/asynStandardInterfacesBase.c index 8aa8008..91d3a71 100644 --- a/asyn/interfaces/asynStandardInterfacesBase.c +++ b/asyn/interfaces/asynStandardInterfacesBase.c @@ -124,6 +124,26 @@ static asynStatus initialize(const char *portName, asynStandardInterfaces *pInte } } + if (pInterfaces->int64.pinterface) { + pInterfaces->int64.interfaceType = asynInt64Type; + pInterfaces->int64.drvPvt = pPvt; + status = pasynInt64Base->initialize(portName, &pInterfaces->int64); + if (status != asynSuccess) { + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "Can't register int64"); + return(asynError); + } + if (pInterfaces->int64CanInterrupt) { + status = pasynManager->registerInterruptSource(portName, &pInterfaces->int64, + &pInterfaces->int64InterruptPvt); + if (status != asynSuccess) { + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "Can't register int64 interrupt"); + return(asynError); + } + } + } + if (pInterfaces->float64.pinterface) { pInterfaces->float64.interfaceType = asynFloat64Type; pInterfaces->float64.drvPvt = pPvt; @@ -204,6 +224,26 @@ static asynStatus initialize(const char *portName, asynStandardInterfaces *pInte } } + if (pInterfaces->int64Array.pinterface) { + pInterfaces->int64Array.interfaceType = asynInt64ArrayType; + pInterfaces->int64Array.drvPvt = pPvt; + status = pasynInt64ArrayBase->initialize(portName, &pInterfaces->int64Array); + if (status != asynSuccess) { + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "Can't register int64Array"); + return(asynError); + } + if (pInterfaces->int64ArrayCanInterrupt) { + status = pasynManager->registerInterruptSource(portName, &pInterfaces->int64Array, + &pInterfaces->int64ArrayInterruptPvt); + if (status != asynSuccess) { + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "Can't register int64Array interrupt"); + return(asynError); + } + } + } + if (pInterfaces->float32Array.pinterface) { pInterfaces->float32Array.interfaceType = asynFloat32ArrayType; pInterfaces->float32Array.drvPvt = pPvt; diff --git a/asyn/miscellaneous/asynInterposeEos.c b/asyn/miscellaneous/asynInterposeEos.c index 558c0a7..7abe40c 100644 --- a/asyn/miscellaneous/asynInterposeEos.c +++ b/asyn/miscellaneous/asynInterposeEos.c @@ -232,7 +232,7 @@ static asynStatus readIt(void *ppvt,asynUser *pasynUser, pasynUser,peosPvt->inBuf,peosPvt->inBufSize,&thisRead,&eom); if(status==asynSuccess) { asynPrintIO(pasynUser,ASYN_TRACEIO_FILTER,peosPvt->inBuf,thisRead, - "%s read %d bytes eom=%d\n",peosPvt->portName, thisRead, eom); + "%s read %d bytes eom=%d\n",peosPvt->portName, (int)thisRead, eom); /* * Read could have returned with ASYN_EOM_CNT set in eom because * the number of octets available exceeded inBufSize. This is not diff --git a/asyn/vxi11/drvVxi11.c b/asyn/vxi11/drvVxi11.c index 34f0d29..5573793 100644 --- a/asyn/vxi11/drvVxi11.c +++ b/asyn/vxi11/drvVxi11.c @@ -307,7 +307,7 @@ static BOOL vxiCreateDeviceLink(vxiPort * pvxiPort, } else if(pvxiPort->maxRecvSize!=crLinkR.maxRecvSize) { asynPrint(pasynUser,ASYN_TRACE_ERROR, "%s vxiCreateDeviceLink maxRecvSize changed from %lu to %lu\n", - devName,pvxiPort->maxRecvSize,crLinkR.maxRecvSize); + devName,pvxiPort->maxRecvSize,(unsigned long)crLinkR.maxRecvSize); } if(pvxiPort->abortPort==0) { pvxiPort->abortPort = crLinkR.abortPort; diff --git a/configure/CONFIG_SITE b/configure/CONFIG_SITE index ffa637e..d44a95b 100644 --- a/configure/CONFIG_SITE +++ b/configure/CONFIG_SITE @@ -45,6 +45,12 @@ LINUX_GPIB=NO # DRV_USBTMC=YES #endif +# If you have libusb-1.0 and libftdi, and want FTDI support, set DRV_FTDI=YES +DRV_FTDI=NO + +# If your system has libftdi1, set the following to YES. If it has libftdi, set it to NO +DRV_FTDI_USE_LIBFTDI1=NO + # If you want to build asyn so the only dependency on EPICS base is libCom then set the following flag #EPICS_LIBCOM_ONLY=YES diff --git a/configure/RELEASE b/configure/RELEASE index 66cf16d..ae2d207 100644 --- a/configure/RELEASE +++ b/configure/RELEASE @@ -1,7 +1,6 @@ #RELEASE Location of external products SUPPORT=/corvette/home/epics/devel --include $(TOP)/../configure/SUPPORT.$(EPICS_HOST_ARCH) # IPAC is only necessary if support for Greensprings IP488 is required # IPAC release V2-7 or later is required. @@ -12,4 +11,8 @@ SNCSEQ=$(SUPPORT)/seq-2-2-5 # EPICS_BASE 3.14.6 or later is required EPICS_BASE=/corvette/usr/local/epics-devel/base-7.0.3 --include $(TOP)/../configure/EPICS_BASE.$(EPICS_HOST_ARCH) + +-include $(TOP)/../RELEASE.local +-include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local +-include $(TOP)/configure/RELEASE.local + diff --git a/documentation/Doxyfile b/documentation/Doxyfile index 03c516c..b27e58f 100644 --- a/documentation/Doxyfile +++ b/documentation/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = asyn # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4-36 +PROJECT_NUMBER = 4-37 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 72324a2..0b1ad6f 100755 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -9,6 +9,64 @@

asynDriver: Release Notes

+
+
+

+ Release 4-37

+

+ October 18, 2019

+
+
+

+ Added new 64-bit integer support

+
    +
  • asynDriver adds new asynInt64, asynInt64SyncIO, and asynInt64Array interfaces.
  • +
  • asynDriver.h now does the typedef of epicsInt64 and epicsUInt64 when __STDC_VERSION__ + < 199901L on EPICS 3.14. This allows the Int64 interfaces to be built as long + as the compiler supports the long long and unsigned long long + data types.
  • +
  • devEpics.dbd is now constructed at build time rather than being a static file. + This enables Int64 device support to only be included on EPICS 3.16.1 and later.
  • +
  • asynPortDriver adds support for 64-bit integers: +
      +
    • New parameter types asynParamInt64 and asynParamInt64Array.
    • +
    • New methods setInteger64Param(), getInteger64Param(), readInt64(), writeInt64(), + getBounds64(), readInt64Array(), writeInt64Array(), doCallbacksInt64Array().
    • +
    • New masks for constructor asynInt64Mask, asynInt64ArrayMask.
    • +
    +
  • +
+

+ testErrors test application

+
    +
  • Changes to driver, database and medm screens to test the Int64 interfaces. This + uses the 64-bit device support which must be commented of out st.cmd if running + on EPICS versions prior to 3.16.1.
  • +
+

+ New FTDI driver

+
    +
  • This driver allows much greater control over USB ports for serial communication + than the standard Linux /dev/ttyUSBx driver. Thanks to Bruno Martins for this.
  • +
+

+ devEpics

+
    +
  • Fixes to only print an error message when the status of a write or read operation + changes, not each time there is an error. Thanks to Ben Franksen for this.
  • +
+

+ asynRecord

+
    +
  • Fixes to allow changing the HOSTINFO for the asynIPPort driver when the port is + not connected. Thanks to Krisztián Löki for this.
  • +
+

+ Many files

+
    +
  • Changes to avoid compiler warnings.
  • +
+

diff --git a/documentation/asynDriver.html b/documentation/asynDriver.html index 95abebe..bf2c46f 100755 --- a/documentation/asynDriver.html +++ b/documentation/asynDriver.html @@ -14,12 +14,12 @@

asynDriver: EPICS Driver Support

- Release 4-36

+ Release 4-37

Mark Rivers, Eric Norum, and Marty Kraimer

- August 8, 2019

+ October 18, 2019

Other Contributers

@@ -90,13 +90,16 @@

  • addr - What does it mean for register based interfaces?
  • Example Drivers
  • -
  • asynInt32
  • +
  • asynIntXX (XX=32 or 64)
  • asynInt32SyncIO
  • +
  • asynInt64
  • +
  • asynInt64SyncIO
  • asynUInt32Digital
  • asynUInt32DigitalSyncIO
  • asynFloat64
  • asynFloat64SyncIO
  • -
  • asynXXXArray (XXX=Int8, Int16, Int32, Float32 or Float64)
  • +
  • asynXXXArray (XXX=Int8, Int16, Int32, Int64, Float32 or + Float64)
  • asynXXXArraySyncIO
  • asynEnum
  • asynEnumSyncIO
  • @@ -132,10 +135,11 @@

  • Buffering of driver callbacks
  • Time Stamps
  • asynInt32 device support
  • -
  • asynIntXXXArray device support (XXX=8, 16 or 32) -
  • -
  • asynXXXTimeSeries device support (XXX=Int32 or - Float64)
  • +
  • asynInt64 device support
  • +
  • asynIntXXXArray device support (XXX=8, 16, 32, or + 64)
  • +
  • asynXXXTimeSeries device support (XXX=Int32, Int64, + or Float64)
  • asynUInt32Digital device support
  • asynFloat64 device support
  • asynFloatXXXArray device support (XXX=32 or 64) @@ -162,6 +166,7 @@

  • Green Springs IP488
  • National Instruments GPIB-1014D
  • USB TMC (Test and Measurement Class)
  • +
  • FTDI
  • Additonal Drivers
  • @@ -540,6 +545,9 @@

    asynInt32 methods for devices that read/write integer values. Many analog I/O drivers can use this interface.

    +

    + asynInt64 methods for devices that read/write + 64-bit integer values.

    asynInt8Array methods for devices that read/write arrays of 8-bit integer values

    @@ -549,6 +557,9 @@

    asynInt32Array methods for devices that read/write arrays of 32-bit integer values

    +

    + asynInt64Array methods for devices that read/write + arrays of 64-bit integer values

    asynUInt32Digital methods for devices that read/write arrays of digital values. This interface provides a mask to address individual @@ -2778,12 +2789,14 @@

    for:

    • Int32 - registers appear as 32 integers
    • +
    • Int64 - registers appear as 64 integers
    • UInt32Digital - registers appear a 32 bit unsigned integers and masks can be used to address specific bits.
    • Float64 - registers appear as double precision floats.
    • Int8Array - Arrays of 8 bit integers.
    • Int16Array - Arrays of 16 bit integers.
    • Int32Array - Arrays of 32 bit integers.
    • +
    • Int64Array - Arrays of 64 bit integers.
    • Float32Array - Arrays of single precision floats.
    • Float64Array - Arrays of double precision floats.
    • Enum - Arrays of strings, integer values and integer severities.
    • @@ -2828,9 +2841,11 @@

      • Int32 - The driver supports an array of Int32 values. addr selects an array element. For example a 16 channel ADC would support addr 0 through 15.
      • +
      • Int64 - The driver supports an array of Int64 values. addr selects an array element.
      • Int8Array - Each addr is an array of Int8 values.
      • Int16Array - Each addr is an array of Int16 values.
      • Int32Array - Each addr is an array of Int32 values.
      • +
      • Int64Array - Each addr is an array of Int64 values.
      • Float64 - The driver supports an array of Float64 values. addr selects an array element.
      • Float32Array - Each addr is an array of Float32 values.
      • @@ -2863,49 +2878,49 @@

        is a soft example of a driver that implements asynUInt32Digital.

      -

      - asynInt32

      +

      + asynIntXX (XX=32 or 64)

      - asynInt32 describes the methods implemented by drivers that use integers for communicating + asynIntXX describes the methods implemented by drivers that use integers for communicating with a device.

      -
      typedef void (*interruptCallbackInt32)(void *userPvt, asynUser *pasynUser, 
      -                                       epicsInt32 data);
      -typedef struct asynInt32Interrupt {
      +  
      typedef void (*interruptCallbackIntXX)(void *userPvt, asynUser *pasynUser, 
      +                                       epicsIntXX data);
      +typedef struct asynIntXXInterrupt {
           int addr;
           asynUser *pasynUser;
      -    interruptCallbackInt32 callback;
      +    interruptCallbackIntXX callback;
           void *userPvt;
      -} asynInt32Interrupt;
      -#define asynInt32Type "asynInt32"
      -typedef struct asynInt32 {
      -    asynStatus (*write)(void *drvPvt, asynUser *pasynUser, epicsInt32 value);
      -    asynStatus (*read)(void *drvPvt, asynUser *pasynUser, epicsInt32 *value);
      +} asynIntXXInterrupt;
      +#define asynIntXXType "asynIntXX"
      +typedef struct asynIntXX {
      +    asynStatus (*write)(void *drvPvt, asynUser *pasynUser, epicsIntXX value);
      +    asynStatus (*read)(void *drvPvt, asynUser *pasynUser, epicsIntXX *value);
           asynStatus (*getBounds)(void *drvPvt, asynUser *pasynUser,
      -                           epicsInt32 *low, epicsInt32 *high);
      +                           epicsIntXX *low, epicsIntXX *high);
           asynStatus (*registerInterruptUser)(void *drvPvt,asynUser *pasynUser,
      -                           interruptCallbackInt32 callback, void *userPvt,
      +                           interruptCallbackIntXX callback, void *userPvt,
                                  void **registrarPvt);
           asynStatus (*cancelInterruptUser)(void *drvPvt, asynUser *pasynUser,
                           void *registrarPvt);
      -} asynInt32;
      +} asynIntXX;
       
      -/* asynInt32Base does the following:
      -   calls  registerInterface for asynInt32.
      +/* asynIntXXBase does the following:
      +   calls  registerInterface for asynIntXX.
          Implements registerInterruptUser and cancelInterruptUser
          Provides default implementations of all methods.
          registerInterruptUser and cancelInterruptUser can be called
          directly rather than via queueRequest.
       */
       
      -#define asynInt32BaseType "asynInt32Base"
      -typedef struct asynInt32Base {
      +#define asynIntXXBaseType "asynIntXXBase"
      +typedef struct asynIntXXBase {
           asynStatus (*initialize)(const char *portName,
      -                            asynInterface *pint32Interface);
      -} asynInt32Base;
      -epicsShareExtern asynInt32Base *pasynInt32Base;
      + asynInterface *pintXXInterface); +} asynIntXXBase; +epicsShareExtern asynIntXXBase *pasynIntXXBase;
      + asynIntXX]","i"),o=/checked\s*(?:[^=]|=\s*.checked.)/i,bn=/\/(java|ecma)script/i,aO=/^\s*",""],legend:[1,"
      ","
      "],thead:[1,"
      - asynInt32
      @@ -2946,20 +2961,20 @@

      - asynInt32Base is an interface and associated code that is used by drivers that implement - interface asynInt32. asynInt32Base provides code to handle registerInterruptUser/cancelInterruptUser. + asynIntXXBase is an interface and associated code that is used by drivers that implement + interface asynIntXX. asynIntXXBase provides code to handle registerInterruptUser/cancelInterruptUser. The driver must itself call the callbacks via calls to asynManager:interruptStart and asynManager:interruptEnd.

      + asynIntXXBase @@ -2971,7 +2986,7 @@

      The default implementation of each method does the following:

      - asynInt32Base
      initialize After a driver calls registerPort it can call: -
      pasynInt32Base->initialize(...
      +
      pasynIntXXBase->initialize(...
      Any null methods in the asynInterface are replaced by default implementations.
      + asynIntXX
      - asynInt32
      @@ -3005,31 +3020,31 @@

      -

      - asynInt32SyncIO

      +

      + asynIntXXSyncIO (XX=32 or 64)

      - asynInt32SyncIO describes a synchronous interface to asynInt32. The code that calls + asynIntXXSyncIO describes a synchronous interface to asynIntXX. The code that calls it must be willing to block.

      -
      #define asynInt32SyncIOType "asynInt32SyncIO"
      -typedef struct asynInt32SyncIO {
      +  
      #define asynIntXXSyncIOType "asynIntXXSyncIO"
      +typedef struct asynIntXXSyncIO {
           asynStatus (*connect)(const char *port, int addr,
                                 asynUser **ppasynUser, const char *drvInfo);
           asynStatus (*disconnect)(asynUser *pasynUser);
      -    asynStatus (*write)(asynUser *pasynUser, epicsInt32 value,double timeout);
      -    asynStatus (*read)(asynUser *pasynUser, epicsInt32 *pvalue,double timeout);
      +    asynStatus (*write)(asynUser *pasynUser, epicsIntXX value,double timeout);
      +    asynStatus (*read)(asynUser *pasynUser, epicsIntXX *pvalue,double timeout);
           asynStatus (*getBounds)(asynUser *pasynUser,
      -                    epicsInt32 *plow, epicsInt32 *phigh);
      +                    epicsIntXX *plow, epicsIntXX *phigh);
           asynStatus (*writeOnce)(const char *port, int addr,
      -                    epicsInt32 value,double timeout, const char *drvInfo);
      +                    epicsIntXX value,double timeout, const char *drvInfo);
           asynStatus (*readOnce)(const char *port, int addr,
      -                    epicsInt32 *pvalue,double timeout, const char *drvInfo);
      +                    epicsIntXX *pvalue,double timeout, const char *drvInfo);
           asynStatus (*getBoundsOnce)(const char *port, int addr,
      -                    epicsInt32 *plow, epicsInt32 *phigh,const char *drvInfo);
      -} asynInt32SyncIO;
      -epicsShareExtern asynInt32SyncIO *pasynInt32SyncIO;
      + epicsIntXX *plow, epicsIntXX *phigh,const char *drvInfo); +} asynIntXXSyncIO; +epicsShareExtern asynIntXXSyncIO *pasynIntXXSyncIO;
      + asynIntXXSyncIO @@ -3534,9 +3549,9 @@

      - asynInt32SyncIO
      @@ -3047,21 +3062,21 @@

      write - Calls pasynInt32->write and waits for the operation to complete or time out. + Calls pasynIntXX->write and waits for the operation to complete or time out.
      read - Calls pasynInt32->read and waits for the operation to complete or time out. + Calls pasynIntXX->read and waits for the operation to complete or time out.
      getBounds - Calls pasynInt32->getBounds and waits for the operation to complete or time out. + Calls pasynIntXX->getBounds and waits for the operation to complete or time out.

      - asynXXXArray (XXX=Int8, Int16, Int32, Float32 or Float64)

      + asynXXXArray (XXX=Int8, Int16, Int32, Int64, Float32 or Float64)

      - asynXXXArray describes the methods for communicating via 8, 16, or 32-bit integers, + asynXXXArray describes the methods for communicating via 8, 16, 32, or 64-bit integers, or 32 or 64-bit IEEE float values.

      typedef void (*interruptCallbackXXXArray)(
                     void *userPvt, asynUser *pasynUser,
      @@ -4197,6 +4212,10 @@ 

      int int32CanInterrupt; void *int32InterruptPvt; + asynInterface int64; + int int64CanInterrupt; + void *int64InterruptPvt; + asynInterface float64; int float64CanInterrupt; void *float64InterruptPvt; @@ -4213,6 +4232,10 @@

      int int32ArrayCanInterrupt; void *int32ArrayInterruptPvt; + asynInterface int64Array; + int int64ArrayCanInterrupt; + void *int64ArrayInterruptPvt; + asynInterface float32Array; int float32ArrayCanInterrupt; void *float32ArrayInterruptPvt; @@ -4276,6 +4299,12 @@

      getBounds }; +static asynInt64 ifaceInt64 = { + writeInt64, + readInt64, + getBounds +}; + static asynFloat64 ifaceFloat64 = { writeFloat64, readFloat64 @@ -4330,12 +4359,14 @@

      pInterfaces->drvUser.pinterface = (void *)&ifaceDrvUser; pInterfaces->octet.pinterface = (void *)&ifaceOctet; pInterfaces->int32.pinterface = (void *)&ifaceInt32; + pInterfaces->int64.pinterface = (void *)&ifaceInt64; pInterfaces->float64.pinterface = (void *)&ifaceFloat64; pInterfaces->genericPointer.pinterface = (void *)&ifaceGenericPointer; /* Define which interfaces can generate interrupts */ pInterfaces->octetCanInterrupt = 1; pInterfaces->int32CanInterrupt = 1; + pInterfaces->int64CanInterrupt = 1; pInterfaces->float64CanInterrupt = 1; pInterfaces->genericPointerCanInterrupt = 1; @@ -4438,11 +4469,15 @@

      devices other support is required. This release provides the following:

      • devAsynInt32 - support for drivers that implement interface asynInt32
      • +
      • devAsynInt64 - support for drivers that implement interface asynInt64
      • devAsynInt32TimeSeries - waveform record support for drivers that implement callbacks on interface asynInt32
      • +
      • devAsynInt64TimeSeries - waveform record support for drivers that implement callbacks + on interface asynInt64
      • devAsynInt8Array - support for drivers that implement interface asynInt8Array
      • devAsynInt16Array - support for drivers that implement interface asynInt16Array
      • devAsynInt32Array - support for drivers that implement interface asynInt32Array
      • +
      • devAsynInt64Array - support for drivers that implement interface asynInt64Array
      • devAsynUInt32Digital - support for drivers that implement interface asynUInt32Digital
      • devAsynFloat64 - support for drivers that implement interface asynFloat64
      • devAsynFloat64TimeSeries - waveform record support for drivers that implement @@ -4507,7 +4542,8 @@

        or asynFloat64Average device support if SCAN=I/O Intr. This is probably not a significant limitation. Support for SCAN=I/O Intr was added in R4-34.

      • -
      • Input records that are waveform time series, i.e. asynInt32TimeSeries or asynFloat64TimeSeries. +
      • Input records that are waveform time series, i.e. asynInt32TimeSeries, asynInt64TimeSeries + or asynFloat64TimeSeries.

        These records are normally scanned periodically. The registerInterruptUser callback is used to append values to the time series.

        @@ -4770,8 +4806,44 @@

        field(TWST,"twoVal") field(THST,"threeVal") }

      +

      + asynInt64 device support

      +

      + asynInt64 device support is only available in EPICS base 3.16.1 and later. That + is when the int64in and int64out records were introduced, and when the waveform + record added support for FTVL=Int64. The following support is available:

      +
      +device(int64in,INST_IO,Int64Out,"asynInt64")
      +device(int64out,INST_IO,Int64Out,"asynInt64")
      +

      + devAsynInt64.c provides EPICS device support for drivers that implement interface + asynInt64.

      +
        +
      • int64inRecord +

        + A value is given to val. Each time the record is processed a new value is read. + SCAN "I/O Intr" is supported similar to aiRecord.

        +
      • +
      • int64outRecord +

        + val is written.

        +
      • +
      +

      + Int64 Input Example Records

      +
         record(int64in,"Int64In") {
      +        field(SCAN,"I/O Intr")
      +        field(DTYP,"asynInt64")
      +        field(INP,"@asyn($(port),$(addr))")
      +   }
      +

      + Int64 Output Example Record

      +
         record(int64out,"Int64Out") {
      +        field(DTYP,"asynInt64")
      +        field(OUT,"@asyn($(port),$(addr))")
      +   }

      - asynIntXXXArray device support (XXX=8, 16 or 32)

      + asynIntXXXArray device support (XXX=8, 16, 32, or 64)

      The following support is available:

      device(waveform,INST_IO,asynIntXXXArrayWfIn,"asynIntXXXArrayIn")
      @@ -4781,17 +4853,19 @@ 

      asynIntXXXArray. It has support for both reading and writing a waveform. SCAN "I/O Intr" is supported similar to the aiRecord in devAsynInt32 device support.

      - asynXXXTimeSeries device support (XXX=Int32 or Float64)

      + asynXXXTimeSeries device support (XXX=Int32, Int64, or Float64)

      The following support is available:

      device(waveform,INST_IO,asynInt32TimeSeries,"asynInt32TimeSeries")
      -device(waveform,INST_IO,asynFloat64TimeSeries,"asynFloat64TimSeries")
      +device(waveform,INST_IO,asynInt64TimeSeries,"asynInt64TimeSeries") +device(waveform,INST_IO,asynFloat64TimeSeries,"asynFloat64TimeSeries")

      devAsynXXXTimeSeries.c provides EPICS device support to collect a time series of values into a waveform record. It works with drivers that implement callbacks on - the asynInt32 or asynFloat64 interfaces. This permits more rapid and efficient acquisition - of values from a driver than can be obtained using record support and database operations. - The waveform record RARM field is used to control acquisition as follows:

      + the asynInt32, asynInt64, or asynFloat64 interfaces. This permits more rapid and + efficient acquisition of values from a driver than can be obtained using record + support and database operations. The waveform record RARM field is used to control + acquisition as follows:

      ","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]},ac=a(av);ax.optgroup=ax.option;ax.tbody=ax.tfoot=ax.colgroup=ax.caption=ax.thead;ax.th=ax.td;if(!b.support.htmlSerialize){ax._default=[1,"div
      ","
      "]}b.fn.extend({text:function(e){return b.access(this,function(bv){return bv===L?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||av).createTextNode(bv))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e)){return this.each(function(bw){b(this).wrapAll(e.call(this,bw))})}if(this[0]){var bv=b(e,this[0].ownerDocument).eq(0).clone(true);if(this[0].parentNode){bv.insertBefore(this[0])}bv.map(function(){var bw=this;while(bw.firstChild&&bw.firstChild.nodeType===1){bw=bw.firstChild}return bw}).append(this)}return this},wrapInner:function(e){if(b.isFunction(e)){return this.each(function(bv){b(this).wrapInner(e.call(this,bv))})}return this.each(function(){var bv=b(this),bw=bv.contents();if(bw.length){bw.wrapAll(e)}else{bv.append(e)}})},wrap:function(e){var bv=b.isFunction(e);return this.each(function(bw){b(this).wrapAll(bv?e.call(this,bw):e)})},unwrap:function(){return this.parent().each(function(){if(!b.nodeName(this,"body")){b(this).replaceWith(this.childNodes)}}).end()},append:function(){return this.domManip(arguments,true,function(e){if(this.nodeType===1){this.appendChild(e)}})},prepend:function(){return this.domManip(arguments,true,function(e){if(this.nodeType===1){this.insertBefore(e,this.firstChild)}})},before:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,false,function(bv){this.parentNode.insertBefore(bv,this)})}else{if(arguments.length){var e=b.clean(arguments);e.push.apply(e,this.toArray());return this.pushStack(e,"before",arguments)}}},after:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,false,function(bv){this.parentNode.insertBefore(bv,this.nextSibling)})}else{if(arguments.length){var e=this.pushStack(this,"after",arguments);e.push.apply(e,b.clean(arguments));return e}}},remove:function(e,bx){for(var bv=0,bw;(bw=this[bv])!=null;bv++){if(!e||b.filter(e,[bw]).length){if(!bx&&bw.nodeType===1){b.cleanData(bw.getElementsByTagName("*"));b.cleanData([bw])}if(bw.parentNode){bw.parentNode.removeChild(bw)}}}return this},empty:function(){for(var e=0,bv;(bv=this[e])!=null;e++){if(bv.nodeType===1){b.cleanData(bv.getElementsByTagName("*"))}while(bv.firstChild){bv.removeChild(bv.firstChild)}}return this},clone:function(bv,e){bv=bv==null?false:bv;e=e==null?bv:e;return this.map(function(){return b.clone(this,bv,e)})},html:function(e){return b.access(this,function(by){var bx=this[0]||{},bw=0,bv=this.length;if(by===L){return bx.nodeType===1?bx.innerHTML.replace(ah,""):null}if(typeof by==="string"&&!ae.test(by)&&(b.support.leadingWhitespace||!ar.test(by))&&!ax[(d.exec(by)||["",""])[1].toLowerCase()]){by=by.replace(R,"<$1>");try{for(;bw1&&bw0?this.clone(true):this).get();b(bC[bA])[bv](by);bz=bz.concat(by)}return this.pushStack(bz,e,bC.selector)}}});function bh(e){if(typeof e.getElementsByTagName!=="undefined"){return e.getElementsByTagName("*")}else{if(typeof e.querySelectorAll!=="undefined"){return e.querySelectorAll("*")}else{return[]}}}function az(e){if(e.type==="checkbox"||e.type==="radio"){e.defaultChecked=e.checked}}function D(e){var bv=(e.nodeName||"").toLowerCase();if(bv==="input"){az(e)}else{if(bv!=="script"&&typeof e.getElementsByTagName!=="undefined"){b.grep(e.getElementsByTagName("input"),az)}}}function am(e){var bv=av.createElement("div");ac.appendChild(bv);bv.innerHTML=e.outerHTML;return bv.firstChild}b.extend({clone:function(by,bA,bw){var e,bv,bx,bz=b.support.html5Clone||b.isXMLDoc(by)||!ai.test("<"+by.nodeName+">")?by.cloneNode(true):am(by);if((!b.support.noCloneEvent||!b.support.noCloneChecked)&&(by.nodeType===1||by.nodeType===11)&&!b.isXMLDoc(by)){aj(by,bz);e=bh(by);bv=bh(bz);for(bx=0;e[bx];++bx){if(bv[bx]){aj(e[bx],bv[bx])}}}if(bA){s(by,bz);if(bw){e=bh(by);bv=bh(bz);for(bx=0;e[bx];++bx){s(e[bx],bv[bx])}}}e=bv=null;return bz},clean:function(bI,bw,bv,bx){var bA,bH,bD,bJ=[];bw=bw||av;if(typeof bw.createElement==="undefined"){bw=bw.ownerDocument||bw[0]&&bw[0].ownerDocument||av}for(var bE=0,bG;(bG=bI[bE])!=null;bE++){if(typeof bG==="number"){bG+=""}if(!bG){continue}if(typeof bG==="string"){if(!W.test(bG)){bG=bw.createTextNode(bG)}else{bG=bG.replace(R,"<$1>");var bN=(d.exec(bG)||["",""])[1].toLowerCase(),bz=ax[bN]||ax._default,bK=bz[0],bB=bw.createElement("div"),bL=ac.childNodes,bM;if(bw===av){ac.appendChild(bB)}else{a(bw).appendChild(bB)}bB.innerHTML=bz[1]+bG+bz[2];while(bK--){bB=bB.lastChild}if(!b.support.tbody){var by=v.test(bG),e=bN==="table"&&!by?bB.firstChild&&bB.firstChild.childNodes:bz[1]===""&&!by?bB.childNodes:[];for(bD=e.length-1;bD>=0;--bD){if(b.nodeName(e[bD],"tbody")&&!e[bD].childNodes.length){e[bD].parentNode.removeChild(e[bD])}}}if(!b.support.leadingWhitespace&&ar.test(bG)){bB.insertBefore(bw.createTextNode(ar.exec(bG)[0]),bB.firstChild)}bG=bB.childNodes;if(bB){bB.parentNode.removeChild(bB);if(bL.length>0){bM=bL[bL.length-1];if(bM&&bM.parentNode){bM.parentNode.removeChild(bM)}}}}}var bF;if(!b.support.appendChecked){if(bG[0]&&typeof(bF=bG.length)==="number"){for(bD=0;bD1)};b.extend({cssHooks:{opacity:{get:function(bw,bv){if(bv){var e=Z(bw,"opacity");return e===""?"1":e}else{return bw.style.opacity}}}},cssNumber:{fillOpacity:true,fontWeight:true,lineHeight:true,opacity:true,orphans:true,widows:true,zIndex:true,zoom:true},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(bx,bw,bD,by){if(!bx||bx.nodeType===3||bx.nodeType===8||!bx.style){return}var bB,bC,bz=b.camelCase(bw),bv=bx.style,bE=b.cssHooks[bz];bw=b.cssProps[bz]||bz;if(bD!==L){bC=typeof bD;if(bC==="string"&&(bB=I.exec(bD))){bD=(+(bB[1]+1)*+bB[2])+parseFloat(b.css(bx,bw));bC="number"}if(bD==null||bC==="number"&&isNaN(bD)){return}if(bC==="number"&&!b.cssNumber[bz]){bD+="px"}if(!bE||!("set" in bE)||(bD=bE.set(bx,bD))!==L){try{bv[bw]=bD}catch(bA){}}}else{if(bE&&"get" in bE&&(bB=bE.get(bx,false,by))!==L){return bB}return bv[bw]}},css:function(by,bx,bv){var bw,e;bx=b.camelCase(bx);e=b.cssHooks[bx];bx=b.cssProps[bx]||bx;if(bx==="cssFloat"){bx="float"}if(e&&"get" in e&&(bw=e.get(by,true,bv))!==L){return bw}else{if(Z){return Z(by,bx)}}},swap:function(by,bx,bz){var e={},bw,bv;for(bv in bx){e[bv]=by.style[bv];by.style[bv]=bx[bv]}bw=bz.call(by);for(bv in bx){by.style[bv]=e[bv]}return bw}});b.curCSS=b.css;if(av.defaultView&&av.defaultView.getComputedStyle){aJ=function(bA,bw){var bv,bz,e,by,bx=bA.style;bw=bw.replace(y,"-$1").toLowerCase();if((bz=bA.ownerDocument.defaultView)&&(e=bz.getComputedStyle(bA,null))){bv=e.getPropertyValue(bw);if(bv===""&&!b.contains(bA.ownerDocument.documentElement,bA)){bv=b.style(bA,bw)}}if(!b.support.pixelMargin&&e&&aE.test(bw)&&a1.test(bv)){by=bx.width;bx.width=bv;bv=e.width;bx.width=by}return bv}}if(av.documentElement.currentStyle){aY=function(bz,bw){var bA,e,by,bv=bz.currentStyle&&bz.currentStyle[bw],bx=bz.style;if(bv==null&&bx&&(by=bx[bw])){bv=by}if(a1.test(bv)){bA=bx.left;e=bz.runtimeStyle&&bz.runtimeStyle.left;if(e){bz.runtimeStyle.left=bz.currentStyle.left}bx.left=bw==="fontSize"?"1em":bv;bv=bx.pixelLeft+"px";bx.left=bA;if(e){bz.runtimeStyle.left=e}}return bv===""?"auto":bv}}Z=aJ||aY;function af(by,bw,bv){var bz=bw==="width"?by.offsetWidth:by.offsetHeight,bx=bw==="width"?1:0,e=4;if(bz>0){if(bv!=="border"){for(;bx=1&&b.trim(bw.replace(al,""))===""){bx.removeAttribute("filter");if(bv&&!bv.filter){return}}bx.filter=al.test(bw)?bw.replace(al,e):bw+" "+e}}}b(function(){if(!b.support.reliableMarginRight){b.cssHooks.marginRight={get:function(bv,e){return b.swap(bv,{display:"inline-block"},function(){if(e){return Z(bv,"margin-right")}else{return bv.style.marginRight}})}}}});if(b.expr&&b.expr.filters){b.expr.filters.hidden=function(bw){var bv=bw.offsetWidth,e=bw.offsetHeight;return(bv===0&&e===0)||(!b.support.reliableHiddenOffsets&&((bw.style&&bw.style.display)||b.css(bw,"display"))==="none")};b.expr.filters.visible=function(e){return !b.expr.filters.hidden(e)}}b.each({margin:"",padding:"",border:"Width"},function(e,bv){b.cssHooks[e+bv]={expand:function(by){var bx,bz=typeof by==="string"?by.split(" "):[by],bw={};for(bx=0;bx<4;bx++){bw[e+G[bx]+bv]=bz[bx]||bz[bx-2]||bz[0]}return bw}}});var k=/%20/g,ap=/\[\]$/,bs=/\r?\n/g,bq=/#.*$/,aD=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,a0=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,aN=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,aR=/^(?:GET|HEAD)$/,c=/^\/\//,M=/\?/,a7=/)<[^<]*)*<\/script>/gi,p=/^(?:select|textarea)/i,h=/\s+/,br=/([?&])_=[^&]*/,K=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,z=b.fn.load,aa={},q={},aF,r,aW=["*/"]+["*"];try{aF=bm.href}catch(aw){aF=av.createElement("a");aF.href="";aF=aF.href}r=K.exec(aF.toLowerCase())||[];function f(e){return function(by,bA){if(typeof by!=="string"){bA=by;by="*"}if(b.isFunction(bA)){var bx=by.toLowerCase().split(h),bw=0,bz=bx.length,bv,bB,bC;for(;bw=0){var e=bw.slice(by,bw.length);bw=bw.slice(0,by)}var bx="GET";if(bz){if(b.isFunction(bz)){bA=bz;bz=L}else{if(typeof bz==="object"){bz=b.param(bz,b.ajaxSettings.traditional);bx="POST"}}}var bv=this;b.ajax({url:bw,type:bx,dataType:"html",data:bz,complete:function(bC,bB,bD){bD=bC.responseText;if(bC.isResolved()){bC.done(function(bE){bD=bE});bv.html(e?b("
      ").append(bD.replace(a7,"")).find(e):bD)}if(bA){bv.each(bA,[bD,bB,bC])}}});return this},serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?b.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||p.test(this.nodeName)||a0.test(this.type))}).map(function(e,bv){var bw=b(this).val();return bw==null?null:b.isArray(bw)?b.map(bw,function(by,bx){return{name:bv.name,value:by.replace(bs,"\r\n")}}):{name:bv.name,value:bw.replace(bs,"\r\n")}}).get()}});b.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,bv){b.fn[bv]=function(bw){return this.on(bv,bw)}});b.each(["get","post"],function(e,bv){b[bv]=function(bw,by,bz,bx){if(b.isFunction(by)){bx=bx||bz;bz=by;by=L}return b.ajax({type:bv,url:bw,data:by,success:bz,dataType:bx})}});b.extend({getScript:function(e,bv){return b.get(e,L,bv,"script")},getJSON:function(e,bv,bw){return b.get(e,bv,bw,"json")},ajaxSetup:function(bv,e){if(e){an(bv,b.ajaxSettings)}else{e=bv;bv=b.ajaxSettings}an(bv,e);return bv},ajaxSettings:{url:aF,isLocal:aN.test(r[1]),global:true,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:true,async:true,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":aW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":bd.String,"text html":true,"text json":b.parseJSON,"text xml":b.parseXML},flatOptions:{context:true,url:true}},ajaxPrefilter:f(aa),ajaxTransport:f(q),ajax:function(bz,bx){if(typeof bz==="object"){bx=bz;bz=L}bx=bx||{};var bD=b.ajaxSetup({},bx),bS=bD.context||bD,bG=bS!==bD&&(bS.nodeType||bS instanceof b)?b(bS):b.event,bR=b.Deferred(),bN=b.Callbacks("once memory"),bB=bD.statusCode||{},bC,bH={},bO={},bQ,by,bL,bE,bI,bA=0,bw,bK,bJ={readyState:0,setRequestHeader:function(bT,bU){if(!bA){var e=bT.toLowerCase();bT=bO[e]=bO[e]||bT;bH[bT]=bU}return this},getAllResponseHeaders:function(){return bA===2?bQ:null},getResponseHeader:function(bT){var e;if(bA===2){if(!by){by={};while((e=aD.exec(bQ))){by[e[1].toLowerCase()]=e[2]}}e=by[bT.toLowerCase()]}return e===L?null:e},overrideMimeType:function(e){if(!bA){bD.mimeType=e}return this},abort:function(e){e=e||"abort";if(bL){bL.abort(e)}bF(0,e);return this}};function bF(bZ,bU,b0,bW){if(bA===2){return}bA=2;if(bE){clearTimeout(bE)}bL=L;bQ=bW||"";bJ.readyState=bZ>0?4:0;var bT,b4,b3,bX=bU,bY=b0?bk(bD,bJ,b0):L,bV,b2;if(bZ>=200&&bZ<300||bZ===304){if(bD.ifModified){if((bV=bJ.getResponseHeader("Last-Modified"))){b.lastModified[bC]=bV}if((b2=bJ.getResponseHeader("Etag"))){b.etag[bC]=b2}}if(bZ===304){bX="notmodified";bT=true}else{try{b4=F(bD,bY);bX="success";bT=true}catch(b1){bX="parsererror";b3=b1}}}else{b3=bX;if(!bX||bZ){bX="error";if(bZ<0){bZ=0}}}bJ.status=bZ;bJ.statusText=""+(bU||bX);if(bT){bR.resolveWith(bS,[b4,bX,bJ])}else{bR.rejectWith(bS,[bJ,bX,b3])}bJ.statusCode(bB);bB=L;if(bw){bG.trigger("ajax"+(bT?"Success":"Error"),[bJ,bD,bT?b4:b3])}bN.fireWith(bS,[bJ,bX]);if(bw){bG.trigger("ajaxComplete",[bJ,bD]);if(!(--b.active)){b.event.trigger("ajaxStop")}}}bR.promise(bJ);bJ.success=bJ.done;bJ.error=bJ.fail;bJ.complete=bN.add;bJ.statusCode=function(bT){if(bT){var e;if(bA<2){for(e in bT){bB[e]=[bB[e],bT[e]]}}else{e=bT[bJ.status];bJ.then(e,e)}}return this};bD.url=((bz||bD.url)+"").replace(bq,"").replace(c,r[1]+"//");bD.dataTypes=b.trim(bD.dataType||"*").toLowerCase().split(h);if(bD.crossDomain==null){bI=K.exec(bD.url.toLowerCase());bD.crossDomain=!!(bI&&(bI[1]!=r[1]||bI[2]!=r[2]||(bI[3]||(bI[1]==="http:"?80:443))!=(r[3]||(r[1]==="http:"?80:443))))}if(bD.data&&bD.processData&&typeof bD.data!=="string"){bD.data=b.param(bD.data,bD.traditional)}aX(aa,bD,bx,bJ);if(bA===2){return false}bw=bD.global;bD.type=bD.type.toUpperCase();bD.hasContent=!aR.test(bD.type);if(bw&&b.active++===0){b.event.trigger("ajaxStart")}if(!bD.hasContent){if(bD.data){bD.url+=(M.test(bD.url)?"&":"?")+bD.data;delete bD.data}bC=bD.url;if(bD.cache===false){var bv=b.now(),bP=bD.url.replace(br,"$1_="+bv);bD.url=bP+((bP===bD.url)?(M.test(bD.url)?"&":"?")+"_="+bv:"")}}if(bD.data&&bD.hasContent&&bD.contentType!==false||bx.contentType){bJ.setRequestHeader("Content-Type",bD.contentType)}if(bD.ifModified){bC=bC||bD.url;if(b.lastModified[bC]){bJ.setRequestHeader("If-Modified-Since",b.lastModified[bC])}if(b.etag[bC]){bJ.setRequestHeader("If-None-Match",b.etag[bC])}}bJ.setRequestHeader("Accept",bD.dataTypes[0]&&bD.accepts[bD.dataTypes[0]]?bD.accepts[bD.dataTypes[0]]+(bD.dataTypes[0]!=="*"?", "+aW+"; q=0.01":""):bD.accepts["*"]);for(bK in bD.headers){bJ.setRequestHeader(bK,bD.headers[bK])}if(bD.beforeSend&&(bD.beforeSend.call(bS,bJ,bD)===false||bA===2)){bJ.abort();return false}for(bK in {success:1,error:1,complete:1}){bJ[bK](bD[bK])}bL=aX(q,bD,bx,bJ);if(!bL){bF(-1,"No Transport")}else{bJ.readyState=1;if(bw){bG.trigger("ajaxSend",[bJ,bD])}if(bD.async&&bD.timeout>0){bE=setTimeout(function(){bJ.abort("timeout")},bD.timeout)}try{bA=1;bL.send(bH,bF)}catch(bM){if(bA<2){bF(-1,bM)}else{throw bM}}}return bJ},param:function(e,bw){var bv=[],by=function(bz,bA){bA=b.isFunction(bA)?bA():bA;bv[bv.length]=encodeURIComponent(bz)+"="+encodeURIComponent(bA)};if(bw===L){bw=b.ajaxSettings.traditional}if(b.isArray(e)||(e.jquery&&!b.isPlainObject(e))){b.each(e,function(){by(this.name,this.value)})}else{for(var bx in e){u(bx,e[bx],bw,by)}}return bv.join("&").replace(k,"+")}});function u(bw,by,bv,bx){if(b.isArray(by)){b.each(by,function(bA,bz){if(bv||ap.test(bw)){bx(bw,bz)}else{u(bw+"["+(typeof bz==="object"?bA:"")+"]",bz,bv,bx)}})}else{if(!bv&&b.type(by)==="object"){for(var e in by){u(bw+"["+e+"]",by[e],bv,bx)}}else{bx(bw,by)}}}b.extend({active:0,lastModified:{},etag:{}});function bk(bD,bC,bz){var bv=bD.contents,bB=bD.dataTypes,bw=bD.responseFields,by,bA,bx,e;for(bA in bw){if(bA in bz){bC[bw[bA]]=bz[bA]}}while(bB[0]==="*"){bB.shift();if(by===L){by=bD.mimeType||bC.getResponseHeader("content-type")}}if(by){for(bA in bv){if(bv[bA]&&bv[bA].test(by)){bB.unshift(bA);break}}}if(bB[0] in bz){bx=bB[0]}else{for(bA in bz){if(!bB[0]||bD.converters[bA+" "+bB[0]]){bx=bA;break}if(!e){e=bA}}bx=bx||e}if(bx){if(bx!==bB[0]){bB.unshift(bx)}return bz[bx]}}function F(bH,bz){if(bH.dataFilter){bz=bH.dataFilter(bz,bH.dataType)}var bD=bH.dataTypes,bG={},bA,bE,bw=bD.length,bB,bC=bD[0],bx,by,bF,bv,e;for(bA=1;bA=bw.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();bw.animatedProperties[this.prop]=true;for(bA in bw.animatedProperties){if(bw.animatedProperties[bA]!==true){e=false}}if(e){if(bw.overflow!=null&&!b.support.shrinkWrapBlocks){b.each(["","X","Y"],function(bC,bD){bz.style["overflow"+bD]=bw.overflow[bC]})}if(bw.hide){b(bz).hide()}if(bw.hide||bw.show){for(bA in bw.animatedProperties){b.style(bz,bA,bw.orig[bA]);b.removeData(bz,"fxshow"+bA,true);b.removeData(bz,"toggle"+bA,true)}}bv=bw.complete;if(bv){bw.complete=false;bv.call(bz)}}return false}else{if(bw.duration==Infinity){this.now=bx}else{bB=bx-this.startTime;this.state=bB/bw.duration;this.pos=b.easing[bw.animatedProperties[this.prop]](this.state,bB,0,1,bw.duration);this.now=this.start+((this.end-this.start)*this.pos)}this.update()}return true}};b.extend(b.fx,{tick:function(){var bw,bv=b.timers,e=0;for(;e").appendTo(e),bw=bv.css("display");bv.remove();if(bw==="none"||bw===""){if(!ba){ba=av.createElement("iframe");ba.frameBorder=ba.width=ba.height=0}e.appendChild(ba);if(!m||!ba.createElement){m=(ba.contentWindow||ba.contentDocument).document;m.write((b.support.boxModel?"":"")+"");m.close()}bv=m.createElement(bx);m.body.appendChild(bv);bw=b.css(bv,"display");e.removeChild(ba)}Q[bx]=bw}return Q[bx]}var a8,V=/^t(?:able|d|h)$/i,ad=/^(?:body|html)$/i;if("getBoundingClientRect" in av.documentElement){a8=function(by,bH,bw,bB){try{bB=by.getBoundingClientRect()}catch(bF){}if(!bB||!b.contains(bw,by)){return bB?{top:bB.top,left:bB.left}:{top:0,left:0}}var bC=bH.body,bD=aL(bH),bA=bw.clientTop||bC.clientTop||0,bE=bw.clientLeft||bC.clientLeft||0,bv=bD.pageYOffset||b.support.boxModel&&bw.scrollTop||bC.scrollTop,bz=bD.pageXOffset||b.support.boxModel&&bw.scrollLeft||bC.scrollLeft,bG=bB.top+bv-bA,bx=bB.left+bz-bE;return{top:bG,left:bx}}}else{a8=function(bz,bE,bx){var bC,bw=bz.offsetParent,bv=bz,bA=bE.body,bB=bE.defaultView,e=bB?bB.getComputedStyle(bz,null):bz.currentStyle,bD=bz.offsetTop,by=bz.offsetLeft;while((bz=bz.parentNode)&&bz!==bA&&bz!==bx){if(b.support.fixedPosition&&e.position==="fixed"){break}bC=bB?bB.getComputedStyle(bz,null):bz.currentStyle;bD-=bz.scrollTop;by-=bz.scrollLeft;if(bz===bw){bD+=bz.offsetTop;by+=bz.offsetLeft;if(b.support.doesNotAddBorder&&!(b.support.doesAddBorderForTableAndCells&&V.test(bz.nodeName))){bD+=parseFloat(bC.borderTopWidth)||0;by+=parseFloat(bC.borderLeftWidth)||0}bv=bw;bw=bz.offsetParent}if(b.support.subtractsBorderForOverflowNotVisible&&bC.overflow!=="visible"){bD+=parseFloat(bC.borderTopWidth)||0;by+=parseFloat(bC.borderLeftWidth)||0}e=bC}if(e.position==="relative"||e.position==="static"){bD+=bA.offsetTop;by+=bA.offsetLeft}if(b.support.fixedPosition&&e.position==="fixed"){bD+=Math.max(bx.scrollTop,bA.scrollTop);by+=Math.max(bx.scrollLeft,bA.scrollLeft)}return{top:bD,left:by}}}b.fn.offset=function(e){if(arguments.length){return e===L?this:this.each(function(bx){b.offset.setOffset(this,e,bx)})}var bv=this[0],bw=bv&&bv.ownerDocument;if(!bw){return null}if(bv===bw.body){return b.offset.bodyOffset(bv)}return a8(bv,bw,bw.documentElement)};b.offset={bodyOffset:function(e){var bw=e.offsetTop,bv=e.offsetLeft;if(b.support.doesNotIncludeMarginInBodyOffset){bw+=parseFloat(b.css(e,"marginTop"))||0;bv+=parseFloat(b.css(e,"marginLeft"))||0}return{top:bw,left:bv}},setOffset:function(bx,bG,bA){var bB=b.css(bx,"position");if(bB==="static"){bx.style.position="relative"}var bz=b(bx),bv=bz.offset(),e=b.css(bx,"top"),bE=b.css(bx,"left"),bF=(bB==="absolute"||bB==="fixed")&&b.inArray("auto",[e,bE])>-1,bD={},bC={},bw,by;if(bF){bC=bz.position();bw=bC.top;by=bC.left}else{bw=parseFloat(e)||0;by=parseFloat(bE)||0}if(b.isFunction(bG)){bG=bG.call(bx,bA,bv)}if(bG.top!=null){bD.top=(bG.top-bv.top)+bw}if(bG.left!=null){bD.left=(bG.left-bv.left)+by}if("using" in bG){bG.using.call(bx,bD)}else{bz.css(bD)}}};b.fn.extend({position:function(){if(!this[0]){return null}var bw=this[0],bv=this.offsetParent(),bx=this.offset(),e=ad.test(bv[0].nodeName)?{top:0,left:0}:bv.offset();bx.top-=parseFloat(b.css(bw,"marginTop"))||0;bx.left-=parseFloat(b.css(bw,"marginLeft"))||0;e.top+=parseFloat(b.css(bv[0],"borderTopWidth"))||0;e.left+=parseFloat(b.css(bv[0],"borderLeftWidth"))||0;return{top:bx.top-e.top,left:bx.left-e.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||av.body;while(e&&(!ad.test(e.nodeName)&&b.css(e,"position")==="static")){e=e.offsetParent}return e})}});b.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(bw,bv){var e=/Y/.test(bv);b.fn[bw]=function(bx){return b.access(this,function(by,bB,bA){var bz=aL(by);if(bA===L){return bz?(bv in bz)?bz[bv]:b.support.boxModel&&bz.document.documentElement[bB]||bz.document.body[bB]:by[bB]}if(bz){bz.scrollTo(!e?bA:b(bz).scrollLeft(),e?bA:b(bz).scrollTop())}else{by[bB]=bA}},bw,bx,arguments.length,null)}});function aL(e){return b.isWindow(e)?e:e.nodeType===9?e.defaultView||e.parentWindow:false}b.each({Height:"height",Width:"width"},function(bw,bx){var bv="client"+bw,e="scroll"+bw,by="offset"+bw;b.fn["inner"+bw]=function(){var bz=this[0];return bz?bz.style?parseFloat(b.css(bz,bx,"padding")):this[bx]():null};b.fn["outer"+bw]=function(bA){var bz=this[0];return bz?bz.style?parseFloat(b.css(bz,bx,bA?"margin":"border")):this[bx]():null};b.fn[bx]=function(bz){return b.access(this,function(bC,bB,bD){var bF,bE,bG,bA;if(b.isWindow(bC)){bF=bC.document;bE=bF.documentElement[bv];return b.support.boxModel&&bE||bF.body&&bF.body[bv]||bE}if(bC.nodeType===9){bF=bC.documentElement;if(bF[bv]>=bF[e]){return bF[bv]}return Math.max(bC.body[e],bF[e],bC.body[by],bF[by])}if(bD===L){bG=b.css(bC,bB);bA=parseFloat(bG);return b.isNumeric(bA)?bA:bG}b(bC).css(bB,bD)},bx,bz,arguments.length,null)}});bd.jQuery=bd.$=b;if(typeof define==="function"&&define.amd&&define.amd.jQuery){define("jquery",[],function(){return b})}})(window);/*! + * jQuery UI 1.8.18 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI + */ +(function(a,d){a.ui=a.ui||{};if(a.ui.version){return}a.extend(a.ui,{version:"1.8.18",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(e,f){return typeof e==="number"?this.each(function(){var g=this;setTimeout(function(){a(g).focus();if(f){f.call(g)}},e)}):this._focus.apply(this,arguments)},scrollParent:function(){var e;if((a.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){e=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(a.curCSS(this,"position",1))&&(/(auto|scroll)/).test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0)}else{e=this.parents().filter(function(){return(/(auto|scroll)/).test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!e.length?a(document):e},zIndex:function(h){if(h!==d){return this.css("zIndex",h)}if(this.length){var f=a(this[0]),e,g;while(f.length&&f[0]!==document){e=f.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){g=parseInt(f.css("zIndex"),10);if(!isNaN(g)&&g!==0){return g}}f=f.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});a.each(["Width","Height"],function(g,e){var f=e==="Width"?["Left","Right"]:["Top","Bottom"],h=e.toLowerCase(),k={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};function j(m,l,i,n){a.each(f,function(){l-=parseFloat(a.curCSS(m,"padding"+this,true))||0;if(i){l-=parseFloat(a.curCSS(m,"border"+this+"Width",true))||0}if(n){l-=parseFloat(a.curCSS(m,"margin"+this,true))||0}});return l}a.fn["inner"+e]=function(i){if(i===d){return k["inner"+e].call(this)}return this.each(function(){a(this).css(h,j(this,i)+"px")})};a.fn["outer"+e]=function(i,l){if(typeof i!=="number"){return k["outer"+e].call(this,i)}return this.each(function(){a(this).css(h,j(this,i,true,l)+"px")})}});function c(g,e){var j=g.nodeName.toLowerCase();if("area"===j){var i=g.parentNode,h=i.name,f;if(!g.href||!h||i.nodeName.toLowerCase()!=="map"){return false}f=a("img[usemap=#"+h+"]")[0];return !!f&&b(f)}return(/input|select|textarea|button|object/.test(j)?!g.disabled:"a"==j?g.href||e:e)&&b(g)}function b(e){return !a(e).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}a.extend(a.expr[":"],{data:function(g,f,e){return !!a.data(g,e[3])},focusable:function(e){return c(e,!isNaN(a.attr(e,"tabindex")))},tabbable:function(g){var e=a.attr(g,"tabindex"),f=isNaN(e);return(f||e>=0)&&c(g,!f)}});a(function(){var e=document.body,f=e.appendChild(f=document.createElement("div"));f.offsetHeight;a.extend(f.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});a.support.minHeight=f.offsetHeight===100;a.support.selectstart="onselectstart" in f;e.removeChild(f).style.display="none"});a.extend(a.ui,{plugin:{add:function(f,g,j){var h=a.ui[f].prototype;for(var e in j){h.plugins[e]=h.plugins[e]||[];h.plugins[e].push([g,j[e]])}},call:function(e,g,f){var j=e.plugins[g];if(!j||!e.element[0].parentNode){return}for(var h=0;h0){return true}h[e]=1;g=(h[e]>0);h[e]=0;return g},isOverAxis:function(f,e,g){return(f>e)&&(f<(e+g))},isOver:function(j,f,i,h,e,g){return a.ui.isOverAxis(j,i,e)&&a.ui.isOverAxis(f,h,g)}})})(jQuery);/*! + * jQuery UI Widget 1.8.18 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Widget + */ +(function(b,d){if(b.cleanData){var c=b.cleanData;b.cleanData=function(f){for(var g=0,h;(h=f[g])!=null;g++){try{b(h).triggerHandler("remove")}catch(j){}}c(f)}}else{var a=b.fn.remove;b.fn.remove=function(e,f){return this.each(function(){if(!f){if(!e||b.filter(e,[this]).length){b("*",this).add([this]).each(function(){try{b(this).triggerHandler("remove")}catch(g){}})}}return a.call(b(this),e,f)})}}b.widget=function(f,h,e){var g=f.split(".")[0],j;f=f.split(".")[1];j=g+"-"+f;if(!e){e=h;h=b.Widget}b.expr[":"][j]=function(k){return !!b.data(k,f)};b[g]=b[g]||{};b[g][f]=function(k,l){if(arguments.length){this._createWidget(k,l)}};var i=new h();i.options=b.extend(true,{},i.options);b[g][f].prototype=b.extend(true,i,{namespace:g,widgetName:f,widgetEventPrefix:b[g][f].prototype.widgetEventPrefix||f,widgetBaseClass:j},e);b.widget.bridge(f,b[g][f])};b.widget.bridge=function(f,e){b.fn[f]=function(i){var g=typeof i==="string",h=Array.prototype.slice.call(arguments,1),j=this;i=!g&&h.length?b.extend.apply(null,[true,i].concat(h)):i;if(g&&i.charAt(0)==="_"){return j}if(g){this.each(function(){var k=b.data(this,f),l=k&&b.isFunction(k[i])?k[i].apply(k,h):k;if(l!==k&&l!==d){j=l;return false}})}else{this.each(function(){var k=b.data(this,f);if(k){k.option(i||{})._init()}else{b.data(this,f,new e(i,this))}})}return j}};b.Widget=function(e,f){if(arguments.length){this._createWidget(e,f)}};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(f,g){b.data(g,this.widgetName,this);this.element=b(g);this.options=b.extend(true,{},this.options,this._getCreateOptions(),f);var e=this;this.element.bind("remove."+this.widgetName,function(){e.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(f,g){var e=f;if(arguments.length===0){return b.extend({},this.options)}if(typeof f==="string"){if(g===d){return this.options[f]}e={};e[f]=g}this._setOptions(e);return this},_setOptions:function(f){var e=this;b.each(f,function(g,h){e._setOption(g,h)});return this},_setOption:function(e,f){this.options[e]=f;if(e==="disabled"){this.widget()[f?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",f)}return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(e,f,g){var j,i,h=this.options[e];g=g||{};f=b.Event(f);f.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase();f.target=this.element[0];i=f.originalEvent;if(i){for(j in i){if(!(j in f)){f[j]=i[j]}}}this.element.trigger(f,g);return !(b.isFunction(h)&&h.call(this.element[0],f,g)===false||f.isDefaultPrevented())}}})(jQuery);/*! + * jQuery UI Mouse 1.8.18 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Mouse + * + * Depends: + * jquery.ui.widget.js + */ +(function(b,c){var a=false;b(document).mouseup(function(d){a=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var d=this;this.element.bind("mousedown."+this.widgetName,function(e){return d._mouseDown(e)}).bind("click."+this.widgetName,function(e){if(true===b.data(e.target,d.widgetName+".preventClickEvent")){b.removeData(e.target,d.widgetName+".preventClickEvent");e.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(f){if(a){return}(this._mouseStarted&&this._mouseUp(f));this._mouseDownEvent=f;var e=this,g=(f.which==1),d=(typeof this.options.cancel=="string"&&f.target.nodeName?b(f.target).closest(this.options.cancel).length:false);if(!g||d||!this._mouseCapture(f)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){e.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(f)&&this._mouseDelayMet(f)){this._mouseStarted=(this._mouseStart(f)!==false);if(!this._mouseStarted){f.preventDefault();return true}}if(true===b.data(f.target,this.widgetName+".preventClickEvent")){b.removeData(f.target,this.widgetName+".preventClickEvent")}this._mouseMoveDelegate=function(h){return e._mouseMove(h)};this._mouseUpDelegate=function(h){return e._mouseUp(h)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);f.preventDefault();a=true;return true},_mouseMove:function(d){if(b.browser.msie&&!(document.documentMode>=9)&&!d.button){return this._mouseUp(d)}if(this._mouseStarted){this._mouseDrag(d);return d.preventDefault()}if(this._mouseDistanceMet(d)&&this._mouseDelayMet(d)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,d)!==false);(this._mouseStarted?this._mouseDrag(d):this._mouseUp(d))}return !this._mouseStarted},_mouseUp:function(d){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;if(d.target==this._mouseDownEvent.target){b.data(d.target,this.widgetName+".preventClickEvent",true)}this._mouseStop(d)}return false},_mouseDistanceMet:function(d){return(Math.max(Math.abs(this._mouseDownEvent.pageX-d.pageX),Math.abs(this._mouseDownEvent.pageY-d.pageY))>=this.options.distance)},_mouseDelayMet:function(d){return this.mouseDelayMet},_mouseStart:function(d){},_mouseDrag:function(d){},_mouseStop:function(d){},_mouseCapture:function(d){return true}})})(jQuery);(function(c,d){c.widget("ui.resizable",c.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,containment:false,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1000},_create:function(){var f=this,k=this.options;this.element.addClass("ui-resizable");c.extend(this,{_aspectRatio:!!(k.aspectRatio),aspectRatio:k.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:k.helper||k.ghost||k.animate?k.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)){this.element.wrap(c('
      ').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=k.handles||(!c(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all"){this.handles="n,e,s,w,se,sw,ne,nw"}var l=this.handles.split(",");this.handles={};for(var g=0;g
      ');if(/sw|se|ne|nw/.test(j)){h.css({zIndex:++k.zIndex})}if("se"==j){h.addClass("ui-icon ui-icon-gripsmall-diagonal-se")}this.handles[j]=".ui-resizable-"+j;this.element.append(h)}}this._renderAxis=function(q){q=q||this.element;for(var n in this.handles){if(this.handles[n].constructor==String){this.handles[n]=c(this.handles[n],this.element).show()}if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var o=c(this.handles[n],this.element),p=0;p=/sw|ne|nw|se|n|s/.test(n)?o.outerHeight():o.outerWidth();var m=["padding",/ne|nw|n/.test(n)?"Top":/se|sw|s/.test(n)?"Bottom":/^e$/.test(n)?"Right":"Left"].join("");q.css(m,p);this._proportionallyResize()}if(!c(this.handles[n]).length){continue}}};this._renderAxis(this.element);this._handles=c(".ui-resizable-handle",this.element).disableSelection();this._handles.mouseover(function(){if(!f.resizing){if(this.className){var i=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)}f.axis=i&&i[1]?i[1]:"se"}});if(k.autoHide){this._handles.hide();c(this.element).addClass("ui-resizable-autohide").hover(function(){if(k.disabled){return}c(this).removeClass("ui-resizable-autohide");f._handles.show()},function(){if(k.disabled){return}if(!f.resizing){c(this).addClass("ui-resizable-autohide");f._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();var e=function(g){c(g).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){e(this.element);var f=this.element;f.after(this.originalElement.css({position:f.css("position"),width:f.outerWidth(),height:f.outerHeight(),top:f.css("top"),left:f.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);e(this.originalElement);return this},_mouseCapture:function(f){var g=false;for(var e in this.handles){if(c(this.handles[e])[0]==f.target){g=true}}return !this.options.disabled&&g},_mouseStart:function(g){var j=this.options,f=this.element.position(),e=this.element;this.resizing=true;this.documentScroll={top:c(document).scrollTop(),left:c(document).scrollLeft()};if(e.is(".ui-draggable")||(/absolute/).test(e.css("position"))){e.css({position:"absolute",top:f.top,left:f.left})}this._renderProxy();var k=b(this.helper.css("left")),h=b(this.helper.css("top"));if(j.containment){k+=c(j.containment).scrollLeft()||0;h+=c(j.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:k,top:h};this.size=this._helper?{width:e.outerWidth(),height:e.outerHeight()}:{width:e.width(),height:e.height()};this.originalSize=this._helper?{width:e.outerWidth(),height:e.outerHeight()}:{width:e.width(),height:e.height()};this.originalPosition={left:k,top:h};this.sizeDiff={width:e.outerWidth()-e.width(),height:e.outerHeight()-e.height()};this.originalMousePosition={left:g.pageX,top:g.pageY};this.aspectRatio=(typeof j.aspectRatio=="number")?j.aspectRatio:((this.originalSize.width/this.originalSize.height)||1);var i=c(".ui-resizable-"+this.axis).css("cursor");c("body").css("cursor",i=="auto"?this.axis+"-resize":i);e.addClass("ui-resizable-resizing");this._propagate("start",g);return true},_mouseDrag:function(e){var h=this.helper,g=this.options,m={},q=this,j=this.originalMousePosition,n=this.axis;var r=(e.pageX-j.left)||0,p=(e.pageY-j.top)||0;var i=this._change[n];if(!i){return false}var l=i.apply(this,[e,r,p]),k=c.browser.msie&&c.browser.version<7,f=this.sizeDiff;this._updateVirtualBoundaries(e.shiftKey);if(this._aspectRatio||e.shiftKey){l=this._updateRatio(l,e)}l=this._respectSize(l,e);this._propagate("resize",e);h.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});if(!this._helper&&this._proportionallyResizeElements.length){this._proportionallyResize()}this._updateCache(l);this._trigger("resize",e,this.ui());return false},_mouseStop:function(h){this.resizing=false;var i=this.options,m=this;if(this._helper){var g=this._proportionallyResizeElements,e=g.length&&(/textarea/i).test(g[0].nodeName),f=e&&c.ui.hasScroll(g[0],"left")?0:m.sizeDiff.height,k=e?0:m.sizeDiff.width;var n={width:(m.helper.width()-k),height:(m.helper.height()-f)},j=(parseInt(m.element.css("left"),10)+(m.position.left-m.originalPosition.left))||null,l=(parseInt(m.element.css("top"),10)+(m.position.top-m.originalPosition.top))||null;if(!i.animate){this.element.css(c.extend(n,{top:l,left:j}))}m.helper.height(m.size.height);m.helper.width(m.size.width);if(this._helper&&!i.animate){this._proportionallyResize()}}c("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",h);if(this._helper){this.helper.remove()}return false},_updateVirtualBoundaries:function(g){var j=this.options,i,h,f,k,e;e={minWidth:a(j.minWidth)?j.minWidth:0,maxWidth:a(j.maxWidth)?j.maxWidth:Infinity,minHeight:a(j.minHeight)?j.minHeight:0,maxHeight:a(j.maxHeight)?j.maxHeight:Infinity};if(this._aspectRatio||g){i=e.minHeight*this.aspectRatio;f=e.minWidth/this.aspectRatio;h=e.maxHeight*this.aspectRatio;k=e.maxWidth/this.aspectRatio;if(i>e.minWidth){e.minWidth=i}if(f>e.minHeight){e.minHeight=f}if(hl.width),s=a(l.height)&&i.minHeight&&(i.minHeight>l.height);if(h){l.width=i.minWidth}if(s){l.height=i.minHeight}if(t){l.width=i.maxWidth}if(m){l.height=i.maxHeight}var f=this.originalPosition.left+this.originalSize.width,p=this.position.top+this.size.height;var k=/sw|nw|w/.test(q),e=/nw|ne|n/.test(q);if(h&&k){l.left=f-i.minWidth}if(t&&k){l.left=f-i.maxWidth}if(s&&e){l.top=p-i.minHeight}if(m&&e){l.top=p-i.maxHeight}var n=!l.width&&!l.height;if(n&&!l.left&&l.top){l.top=null}else{if(n&&!l.top&&l.left){l.left=null}}return l},_proportionallyResize:function(){var k=this.options;if(!this._proportionallyResizeElements.length){return}var g=this.helper||this.element;for(var f=0;f');var e=c.browser.msie&&c.browser.version<7,g=(e?1:0),h=(e?2:-1);this.helper.addClass(this._helper).css({width:this.element.outerWidth()+h,height:this.element.outerHeight()+h,position:"absolute",left:this.elementOffset.left-g+"px",top:this.elementOffset.top-g+"px",zIndex:++i.zIndex});this.helper.appendTo("body").disableSelection()}else{this.helper=this.element}},_change:{e:function(g,f,e){return{width:this.originalSize.width+f}},w:function(h,f,e){var j=this.options,g=this.originalSize,i=this.originalPosition;return{left:i.left+f,width:g.width-f}},n:function(h,f,e){var j=this.options,g=this.originalSize,i=this.originalPosition;return{top:i.top+e,height:g.height-e}},s:function(g,f,e){return{height:this.originalSize.height+e}},se:function(g,f,e){return c.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[g,f,e]))},sw:function(g,f,e){return c.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[g,f,e]))},ne:function(g,f,e){return c.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[g,f,e]))},nw:function(g,f,e){return c.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[g,f,e]))}},_propagate:function(f,e){c.ui.plugin.call(this,f,[e,this.ui()]);(f!="resize"&&this._trigger(f,e,this.ui()))},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});c.extend(c.ui.resizable,{version:"1.8.18"});c.ui.plugin.add("resizable","alsoResize",{start:function(f,g){var e=c(this).data("resizable"),i=e.options;var h=function(j){c(j).each(function(){var k=c(this);k.data("resizable-alsoresize",{width:parseInt(k.width(),10),height:parseInt(k.height(),10),left:parseInt(k.css("left"),10),top:parseInt(k.css("top"),10)})})};if(typeof(i.alsoResize)=="object"&&!i.alsoResize.parentNode){if(i.alsoResize.length){i.alsoResize=i.alsoResize[0];h(i.alsoResize)}else{c.each(i.alsoResize,function(j){h(j)})}}else{h(i.alsoResize)}},resize:function(g,i){var f=c(this).data("resizable"),j=f.options,h=f.originalSize,l=f.originalPosition;var k={height:(f.size.height-h.height)||0,width:(f.size.width-h.width)||0,top:(f.position.top-l.top)||0,left:(f.position.left-l.left)||0},e=function(m,n){c(m).each(function(){var q=c(this),r=c(this).data("resizable-alsoresize"),p={},o=n&&n.length?n:q.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];c.each(o,function(s,u){var t=(r[u]||0)+(k[u]||0);if(t&&t>=0){p[u]=t||null}});q.css(p)})};if(typeof(j.alsoResize)=="object"&&!j.alsoResize.nodeType){c.each(j.alsoResize,function(m,n){e(m,n)})}else{e(j.alsoResize)}},stop:function(e,f){c(this).removeData("resizable-alsoresize")}});c.ui.plugin.add("resizable","animate",{stop:function(i,n){var p=c(this).data("resizable"),j=p.options;var h=p._proportionallyResizeElements,e=h.length&&(/textarea/i).test(h[0].nodeName),f=e&&c.ui.hasScroll(h[0],"left")?0:p.sizeDiff.height,l=e?0:p.sizeDiff.width;var g={width:(p.size.width-l),height:(p.size.height-f)},k=(parseInt(p.element.css("left"),10)+(p.position.left-p.originalPosition.left))||null,m=(parseInt(p.element.css("top"),10)+(p.position.top-p.originalPosition.top))||null;p.element.animate(c.extend(g,m&&k?{top:m,left:k}:{}),{duration:j.animateDuration,easing:j.animateEasing,step:function(){var o={width:parseInt(p.element.css("width"),10),height:parseInt(p.element.css("height"),10),top:parseInt(p.element.css("top"),10),left:parseInt(p.element.css("left"),10)};if(h&&h.length){c(h[0]).css({width:o.width,height:o.height})}p._updateCache(o);p._propagate("resize",i)}})}});c.ui.plugin.add("resizable","containment",{start:function(f,r){var t=c(this).data("resizable"),j=t.options,l=t.element;var g=j.containment,k=(g instanceof c)?g.get(0):(/parent/.test(g))?l.parent().get(0):g;if(!k){return}t.containerElement=c(k);if(/document/.test(g)||g==document){t.containerOffset={left:0,top:0};t.containerPosition={left:0,top:0};t.parentData={element:c(document),left:0,top:0,width:c(document).width(),height:c(document).height()||document.body.parentNode.scrollHeight}}else{var n=c(k),i=[];c(["Top","Right","Left","Bottom"]).each(function(p,o){i[p]=b(n.css("padding"+o))});t.containerOffset=n.offset();t.containerPosition=n.position();t.containerSize={height:(n.innerHeight()-i[3]),width:(n.innerWidth()-i[1])};var q=t.containerOffset,e=t.containerSize.height,m=t.containerSize.width,h=(c.ui.hasScroll(k,"left")?k.scrollWidth:m),s=(c.ui.hasScroll(k)?k.scrollHeight:e);t.parentData={element:k,left:q.left,top:q.top,width:h,height:s}}},resize:function(g,q){var t=c(this).data("resizable"),i=t.options,f=t.containerSize,p=t.containerOffset,m=t.size,n=t.position,r=t._aspectRatio||g.shiftKey,e={top:0,left:0},h=t.containerElement;if(h[0]!=document&&(/static/).test(h.css("position"))){e=p}if(n.left<(t._helper?p.left:0)){t.size.width=t.size.width+(t._helper?(t.position.left-p.left):(t.position.left-e.left));if(r){t.size.height=t.size.width/i.aspectRatio}t.position.left=i.helper?p.left:0}if(n.top<(t._helper?p.top:0)){t.size.height=t.size.height+(t._helper?(t.position.top-p.top):t.position.top);if(r){t.size.width=t.size.height*i.aspectRatio}t.position.top=t._helper?p.top:0}t.offset.left=t.parentData.left+t.position.left;t.offset.top=t.parentData.top+t.position.top;var l=Math.abs((t._helper?t.offset.left-e.left:(t.offset.left-e.left))+t.sizeDiff.width),s=Math.abs((t._helper?t.offset.top-e.top:(t.offset.top-p.top))+t.sizeDiff.height);var k=t.containerElement.get(0)==t.element.parent().get(0),j=/relative|absolute/.test(t.containerElement.css("position"));if(k&&j){l-=t.parentData.left}if(l+t.size.width>=t.parentData.width){t.size.width=t.parentData.width-l;if(r){t.size.height=t.size.width/t.aspectRatio}}if(s+t.size.height>=t.parentData.height){t.size.height=t.parentData.height-s;if(r){t.size.width=t.size.height*t.aspectRatio}}},stop:function(f,n){var q=c(this).data("resizable"),g=q.options,l=q.position,m=q.containerOffset,e=q.containerPosition,i=q.containerElement;var j=c(q.helper),r=j.offset(),p=j.outerWidth()-q.sizeDiff.width,k=j.outerHeight()-q.sizeDiff.height;if(q._helper&&!g.animate&&(/relative/).test(i.css("position"))){c(this).css({left:r.left-e.left-m.left,width:p,height:k})}if(q._helper&&!g.animate&&(/static/).test(i.css("position"))){c(this).css({left:r.left-e.left-m.left,width:p,height:k})}}});c.ui.plugin.add("resizable","ghost",{start:function(g,h){var e=c(this).data("resizable"),i=e.options,f=e.size;e.ghost=e.originalElement.clone();e.ghost.css({opacity:0.25,display:"block",position:"relative",height:f.height,width:f.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof i.ghost=="string"?i.ghost:"");e.ghost.appendTo(e.helper)},resize:function(f,g){var e=c(this).data("resizable"),h=e.options;if(e.ghost){e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})}},stop:function(f,g){var e=c(this).data("resizable"),h=e.options;if(e.ghost&&e.helper){e.helper.get(0).removeChild(e.ghost.get(0))}}});c.ui.plugin.add("resizable","grid",{resize:function(e,m){var p=c(this).data("resizable"),h=p.options,k=p.size,i=p.originalSize,j=p.originalPosition,n=p.axis,l=h._aspectRatio||e.shiftKey;h.grid=typeof h.grid=="number"?[h.grid,h.grid]:h.grid;var g=Math.round((k.width-i.width)/(h.grid[0]||1))*(h.grid[0]||1),f=Math.round((k.height-i.height)/(h.grid[1]||1))*(h.grid[1]||1);if(/^(se|s|e)$/.test(n)){p.size.width=i.width+g;p.size.height=i.height+f}else{if(/^(ne)$/.test(n)){p.size.width=i.width+g;p.size.height=i.height+f;p.position.top=j.top-f}else{if(/^(sw)$/.test(n)){p.size.width=i.width+g;p.size.height=i.height+f;p.position.left=j.left-g}else{p.size.width=i.width+g;p.size.height=i.height+f;p.position.top=j.top-f;p.position.left=j.left-g}}}}});var b=function(e){return parseInt(e,10)||0};var a=function(e){return !isNaN(parseInt(e,10))}})(jQuery);/*! + * jQuery hashchange event - v1.3 - 7/21/2010 + * http://benalman.com/projects/jquery-hashchange-plugin/ + * + * Copyright (c) 2010 "Cowboy" Ben Alman + * Dual licensed under the MIT and GPL licenses. + * http://benalman.com/about/license/ + */ +(function($,e,b){var c="hashchange",h=document,f,g=$.event.special,i=h.documentMode,d="on"+c in e&&(i===b||i>7);function a(j){j=j||location.href;return"#"+j.replace(/^[^#]*#?(.*)$/,"$1")}$.fn[c]=function(j){return j?this.bind(c,j):this.trigger(c)};$.fn[c].delay=50;g[c]=$.extend(g[c],{setup:function(){if(d){return false}$(f.start)},teardown:function(){if(d){return false}$(f.stop)}});f=(function(){var j={},p,m=a(),k=function(q){return q},l=k,o=k;j.start=function(){p||n()};j.stop=function(){p&&clearTimeout(p);p=b};function n(){var r=a(),q=o(m);if(r!==m){l(m=r,q);$(e).trigger(c)}else{if(q!==m){location.href=location.href.replace(/#.*/,"")+q}}p=setTimeout(n,$.fn[c].delay)}$.browser.msie&&!d&&(function(){var q,r;j.start=function(){if(!q){r=$.fn[c].src;r=r&&r+a();q=$('