diff --git a/asyn/drvAsynSerial/drvAsynSerialPort.c b/asyn/drvAsynSerial/drvAsynSerialPort.c index 1ed6ab475..26dc8fb7e 100644 --- a/asyn/drvAsynSerial/drvAsynSerialPort.c +++ b/asyn/drvAsynSerial/drvAsynSerialPort.c @@ -200,6 +200,12 @@ getOption(void *drvPvt, asynUser *pasynUser, l = epicsSnprintf(val, valSize, "%c", (tty->termios.c_iflag & IXOFF) ? 'Y' : 'N'); #endif } +#ifndef vxWorks + else if (epicsStrCaseCmp(key, "break") == 0) { + /* request serial line break status */ + l = epicsSnprintf(val, valSize, "off"); + } +#endif #ifdef ASYN_RS485_SUPPORTED else if (epicsStrCaseCmp(key, "rs485_enable") == 0) { l = epicsSnprintf(val, valSize, "%c", (tty->rs485.flags & SER_RS485_ENABLED) ? 'Y' : 'N'); @@ -497,6 +503,30 @@ setOption(void *drvPvt, asynUser *pasynUser, const char *key, const char *val) } #endif } +#ifndef vxWorks + else if (epicsStrCaseCmp(key, "break") == 0) { + /* signal serial line break */ + unsigned break_len; + if (*val == '\0' || epicsStrCaseCmp(val, "on") == 0) break_len = 0; + else if (epicsStrCaseCmp(val, "off") == 0) return asynSuccess; + else { + if (sscanf(val, "%u", &break_len) != 1) { + epicsSnprintf(pasynUser->errorMessage, + pasynUser->errorMessageSize, "Bad number"); + return asynError; + } + } + tcdrain(tty->fd); /* ensure all data transmitted prior to break */ + if (tcsendbreak(tty->fd, break_len) < 0) { + epicsSnprintf(pasynUser->errorMessage, + pasynUser->errorMessageSize, + "%s tcsendbreak failed: %s", + tty->serialDeviceName, strerror(errno)); + return asynError; + } + return asynSuccess; + } +#endif #ifdef ASYN_RS485_SUPPORTED else if (epicsStrCaseCmp(key, "rs485_enable") == 0) { if (epicsStrCaseCmp(val, "Y") == 0) { diff --git a/asyn/drvAsynSerial/drvAsynSerialPortWin32.c b/asyn/drvAsynSerial/drvAsynSerialPortWin32.c index 2ffbf1548..9fc0dfb4f 100644 --- a/asyn/drvAsynSerial/drvAsynSerialPortWin32.c +++ b/asyn/drvAsynSerial/drvAsynSerialPortWin32.c @@ -61,6 +61,7 @@ typedef struct { double writeTimeout; epicsTimerId timer; volatile int timeoutFlag; + unsigned break_active; asynInterface common; asynInterface option; asynInterface octet; @@ -143,6 +144,10 @@ getOption(void *drvPvt, asynUser *pasynUser, else if (epicsStrCaseCmp(key, "ixoff") == 0) { l = epicsSnprintf(val, valSize, "%c", (tty->commConfig.dcb.fInX == TRUE) ? 'Y' : 'N'); } + else if (epicsStrCaseCmp(key, "break") == 0) { + /* request serial line break status */ + l = epicsSnprintf(val, valSize, "%s", tty->break_active ? "on" : "off"); + } else { epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, "Unsupported key \"%s\"", key); @@ -294,6 +299,44 @@ setOption(void *drvPvt, asynUser *pasynUser, const char *key, const char *val) return asynError; } } + else if (epicsStrCaseCmp(key, "break") == 0) { + /* signal serial line break */ + unsigned on, off, len, break_len; + on = off = len = break_len = 0; + if (epicsStrCaseCmp(val, "on") == 0) /* switch break condition on */ + on = tty->break_active ? 0 : 1; + else if (epicsStrCaseCmp(val, "off") == 0) /* switch break condition off */ + off = tty->break_active ? 1 : 0; + else { + /* switch break condition on for a period of time and then off */ + if (*val != '\0' && sscanf(val, "%u", &break_len) != 1) { + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "Bad number"); + return asynError; + } + on = tty->break_active ? 0 : 1; + off = len = 1; + } + if (on) { + FlushFileBuffers(tty->commHandle); /* ensure all data transmitted prior to break */ + if (SetCommBreak(tty->commHandle) == 0) { + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "Bad number"); + return asynError; + } + tty->break_active = 1; + } + if (len) Sleep(break_len > 0 ? break_len : 250); /* wait while break is being asserted */ + if (off) { + if (ClearCommBreak(tty->commHandle) == 0) { + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "Bad number"); + return asynError; + } + tty->break_active = 0; + } + return asynSuccess; + } else if (epicsStrCaseCmp(key, "") != 0) { epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, "Unsupported key \"%s\"", key); @@ -399,6 +442,7 @@ connectIt(void *drvPvt, asynUser *pasynUser) } /* setOption(tty, tty->pasynUser, "baud", "9600"); */ + ClearCommBreak(tty->commHandle); /* in case there is one leftover from an ioc termination */ /* * Turn off non-blocking mode diff --git a/asyn/miscellaneous/asynInterposeCom.c b/asyn/miscellaneous/asynInterposeCom.c index 021675dce..24c715079 100644 --- a/asyn/miscellaneous/asynInterposeCom.c +++ b/asyn/miscellaneous/asynInterposeCom.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "asynDriver.h" #include "asynOctet.h" @@ -50,8 +51,11 @@ #define CPO_SET_STOPSIZE 4 /* Comand port option set stop size */ #define CPO_SET_CONTROL 5 /* Comand port option set control mode */ # define CPO_CONTROL_NOFLOW 1 /* No flow control */ -# define CPO_CONTROL_IXON 2 /* XON/XOFF Flow control*/ +# define CPO_CONTROL_IXON 2 /* XON/XOFF Flow control*/ # define CPO_CONTROL_HWFLOW 3 /* Hardware flow control */ +# define CPO_CONTROL_BREAK 4 /* request break state */ +# define CPO_CONTROL_BREAK_ON 5 /* break state ON */ +# define CPO_CONTROL_BREAK_OFF 6 /* break state OFF */ #define CPO_SET_LINESTATE_MASK 10 /* Comand port option set linestate mask */ #define CPO_SET_MODEMSTATE_MASK 11 /* Comand port option set modemstate mask */ #define CPO_SERVER_NOTIFY_LINESTATE 106 @@ -75,6 +79,7 @@ typedef struct interposePvt { int bits; int stop; int flow; + int break_active; char *xBuf; /* Buffer for transmit IAC stuffing */ size_t xBufCapacity; @@ -597,6 +602,45 @@ setOption(void *ppvt, asynUser *pasynUser, const char *key, const char *val) printf("XON/XOFF not set.\n"); } } + else if (epicsStrCaseCmp(key, "break") == 0) { + /* signal serial line break */ + unsigned on, off, len, break_len; + on = off = len = break_len = 0; + if (epicsStrCaseCmp(val, "on") == 0) /* switch break condition on */ + on = pinterposePvt->break_active ? 0 : 1; + else if (epicsStrCaseCmp(val, "off") == 0) /* switch break condition off */ + off = pinterposePvt->break_active ? 1 : 0; + else { + /* switch break condition on for a period of time and then off */ + if (*val != '\0' && sscanf(val, "%u", &break_len) != 1) { + epicsSnprintf(pasynUser->errorMessage, + pasynUser->errorMessageSize, "Bad number"); + return asynError; + } + on = pinterposePvt->break_active ? 0 : 1; + off = len = 1; + } + if (on) { + xBuf[0] = CPO_SET_CONTROL; + xBuf[1] = CPO_CONTROL_BREAK_ON; + status = sbComPortOption(pinterposePvt, pasynUser, xBuf, 2, rBuf); + if (status != asynSuccess) return status; + pinterposePvt->break_active = (rBuf[0] == CPO_CONTROL_BREAK_ON) ? 1 : 0; + } + if (len) { + /* wait while break is being asserted */ + if (!break_len) break_len = 250; + epicsThreadSleep(((double)break_len) / 1000.); + } + if (off) { + xBuf[0] = CPO_SET_CONTROL; + xBuf[1] = CPO_CONTROL_BREAK_OFF; + status = sbComPortOption(pinterposePvt, pasynUser, xBuf, 2, rBuf); + if (status != asynSuccess) return status; + pinterposePvt->break_active = (rBuf[0] == CPO_CONTROL_BREAK_ON) ? 1 : 0; + } + return asynSuccess; + } else { if (pinterposePvt->pasynOptionDrv) { /* Call the setOption function in the underlying driver */ @@ -657,6 +701,11 @@ getOption(void *ppvt, asynUser *pasynUser, const char *key, char *val, int valSi return asynError; } } + else if (epicsStrCaseCmp(key, "break") == 0) { + /* request serial line break status */ + l = epicsSnprintf(val, valSize, "%s", + pinterposePvt->break_active ? "on" : "off"); + } else { if (pinterposePvt->pasynOptionDrv) { /* Call the getOption function in the underlying driver */ diff --git a/docs/source/asynDriver.rst b/docs/source/asynDriver.rst index 62dc6ecea..35cfe5fec 100644 --- a/docs/source/asynDriver.rst +++ b/docs/source/asynDriver.rst @@ -3143,7 +3143,7 @@ This provides the ability to configure serial ports on terminal servers using th RFC 2117 protocol. It is not configured from the iocsh directly, but is rather configured automatically by the drvAsynIPPort driver if the COM protocol is specified. It supports the same options as drvAsynSerialPort, i.e. "baud", "bits", "parity", "stop", "crtscts", -and "ixon". +"ixon" and "break". asynInterposeDelay ~~~~~~~~~~~~~~~~~~ @@ -4227,6 +4227,8 @@ values. When a serial port connects the current values are fetched. - msec_delay * - rs485_delay_rts_after_send - msec_delay + * - break + off on On some systems (e.g. Windows, Darwin) the driver accepts any numeric value for the baud rate, which must, of course be supported by the system hardware. On Linux @@ -4261,6 +4263,12 @@ This flag is not available on all systems, including WIN32. The rs485 options are only supported on Linux, only kernels ≥ 2.6.35, and only on hardware ports that support RS-485. The delay option units are integer milliseconds. +The break option should send a serial break state on supported systems (Linux, +Windows work, vxWorks does not). A numeric value should send a break for the +specified time, which is device depended (deci seconds, milli seconds, ...). +A zero value means a default time. A value "on" should set the break state on +for a unlimited time and "off" should clear the break state. + vxWorks IOC serial ports may need to be set up using hardware-specific commands. Once this is done, the standard drvAsynSerialPortConfigure and asynSetOption commands can be issued. For example, the following example shows the configuration procedure