Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion asyn/drvAsynSerial/drvAsynSerialPort.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ typedef struct {
double writeTimeout;
epicsTimerId timer;
volatile int timeoutFlag;
unsigned break_len; /* length of serial break to send after a write (ms) when in autobreak mode */
unsigned break_delay; /* time (ms) to delay after write and before sending a break in autobreak mode */
asynInterface common;
asynInterface option;
asynInterface octet;
Expand Down Expand Up @@ -205,6 +207,12 @@ getOption(void *drvPvt, asynUser *pasynUser,
/* request serial line break status */
l = epicsSnprintf(val, valSize, "off");
}
else if (epicsStrCaseCmp(key, "autobreak") == 0) {
l = epicsSnprintf(val, valSize, "%u", tty->break_len);
}
else if (epicsStrCaseCmp(key, "autobreak_delay") == 0) {
l = epicsSnprintf(val, valSize, "%u", tty->break_delay);
}
#endif
#ifdef ASYN_RS485_SUPPORTED
else if (epicsStrCaseCmp(key, "rs485_enable") == 0) {
Expand Down Expand Up @@ -526,6 +534,24 @@ setOption(void *drvPvt, asynUser *pasynUser, const char *key, const char *val)
}
return asynSuccess;
}
else if (epicsStrCaseCmp(key, "autobreak") == 0) {
unsigned break_len;
if(sscanf(val, "%u", &break_len) != 1) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"Bad number");
return asynError;
}
tty->break_len = break_len;
}
else if (epicsStrCaseCmp(key, "autobreak_delay") == 0) {
unsigned break_delay;
if(sscanf(val, "%u", &break_delay) != 1) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"Bad number");
return asynError;
}
tty->break_delay = break_delay;
}
#endif
#ifdef ASYN_RS485_SUPPORTED
else if (epicsStrCaseCmp(key, "rs485_enable") == 0) {
Expand Down Expand Up @@ -676,6 +702,8 @@ report(void *drvPvt, FILE *fp, int details)
fprintf(fp, " fd: %d\n", tty->fd);
fprintf(fp, " Characters written: %lu\n", tty->nWritten);
fprintf(fp, " Characters read: %lu\n", tty->nRead);
fprintf(fp, " Autobreak length (ms): %lu\n", tty->break_len);
fprintf(fp, " Autobreak delay (ms): %lu\n", tty->break_delay);
}
}

Expand Down Expand Up @@ -840,9 +868,25 @@ static asynStatus writeIt(void *drvPvt, asynUser *pasynUser,
}
}
if (timerStarted) epicsTimerCancel(tty->timer);
#ifndef vxWorks
if (tty->break_len > 0) {
tcdrain(tty->fd); /* ensure all data transmitted prior to break */
if (tty->break_delay > 0) {
epicsThreadSleep(tty->break_delay / 1000.0);
}
if (tcsendbreak(tty->fd, tty->break_len) < 0) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"%s tcsendbreak failed: %s",
tty->serialDeviceName, strerror(errno));
closeConnection(pasynUser,tty);
status = asynError;
}
}
#endif
*nbytesTransfered = numchars - nleft;
asynPrint(pasynUser, ASYN_TRACE_FLOW, "wrote %lu to %s, return %s\n",
asynPrint(pasynUser, ASYN_TRACE_FLOW, "wrote %lu %sto %s, return %s\n",
(unsigned long)*nbytesTransfered,
(tty->break_len > 0 ? "(with BREAK) " : ""),
tty->serialDeviceName,
pasynManager->strStatus(status));
return status;
Expand Down
64 changes: 61 additions & 3 deletions asyn/drvAsynSerial/drvAsynSerialPortWin32.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ typedef struct {
epicsTimerId timer;
volatile int timeoutFlag;
unsigned break_active;
unsigned break_len; /* length of serial break to send after a write (ms) when in autobreak mode */
unsigned break_delay; /* length of sleep (ms) before sending break. */
asynInterface common;
asynInterface option;
asynInterface octet;
Expand Down Expand Up @@ -148,6 +150,12 @@ getOption(void *drvPvt, asynUser *pasynUser,
/* request serial line break status */
l = epicsSnprintf(val, valSize, "%s", tty->break_active ? "on" : "off");
}
else if (epicsStrCaseCmp(key, "autobreak") == 0) {
l = epicsSnprintf(val, valSize, "%u", tty->break_len);
}
else if (epicsStrCaseCmp(key, "autobreak_delay") == 0) {
l = epicsSnprintf(val, valSize, "%u", tty->break_delay);
}
else {
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"Unsupported key \"%s\"", key);
Expand Down Expand Up @@ -320,23 +328,44 @@ setOption(void *drvPvt, asynUser *pasynUser, const char *key, const char *val)
if (on) {
FlushFileBuffers(tty->commHandle); /* ensure all data transmitted prior to break */
if (SetCommBreak(tty->commHandle) == 0) {
error = GetLastError();
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"Bad number");
"%s error calling SetCommBreak %d", tty->serialDeviceName, error);
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) {
error = GetLastError();
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"Bad number");
"%s error calling ClearCommBreak %d", tty->serialDeviceName, error);
return asynError;
}
tty->break_active = 0;
}
return asynSuccess;
}
else if (epicsStrCaseCmp(key, "autobreak") == 0) {
unsigned break_len;
if(sscanf(val, "%u", &break_len) != 1) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"Bad number");
return asynError;
}
tty->break_len = break_len;
}
else if (epicsStrCaseCmp(key, "autobreak_delay") == 0) {
unsigned break_delay;
if(sscanf(val, "%u", &break_delay) != 1) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"Bad number");
return asynError;
}
tty->break_delay = break_delay;
return asynSuccess;
}
else if (epicsStrCaseCmp(key, "") != 0) {
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"Unsupported key \"%s\"", key);
Expand Down Expand Up @@ -398,6 +427,8 @@ report(void *drvPvt, FILE *fp, int details)
fprintf(fp, " commHandle: %p\n", tty->commHandle);
fprintf(fp, " Characters written: %lu\n", tty->nWritten);
fprintf(fp, " Characters read: %lu\n", tty->nRead);
fprintf(fp, " autobreak len (ms): %lu\n", tty->break_len);
fprintf(fp, " autobreak delay (ms): %lu\n", tty->break_delay);
}
}

Expand Down Expand Up @@ -537,9 +568,36 @@ static asynStatus writeIt(void *drvPvt, asynUser *pasynUser,
}
}
if (timerStarted) epicsTimerCancel(tty->timer);
/* raise a serial break if requested */
if (!tty->break_active && tty->break_len > 0) {
FlushFileBuffers(tty->commHandle); /* ensure all data transmitted prior to break */
/* Sleep after sending bytes if requested */
if (tty->break_delay > 0) {
Sleep(tty->break_delay);
}
if ( (ret = SetCommBreak(tty->commHandle)) == 0 ) {
error = GetLastError();
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"%s SetCommBreak error: %d",
tty->serialDeviceName, error);
closeConnection(pasynUser,tty);
status = asynError;
} else {
Sleep(tty->break_len); /* wait while break is being asserted */
if ( (ret = ClearCommBreak(tty->commHandle)) == 0 ) {
error = GetLastError();
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"%s ClearCommBreak error: %d",
tty->serialDeviceName, error);
closeConnection(pasynUser,tty);
status = asynError;
}
}
}
*nbytesTransfered = numchars - nleft;
asynPrint(pasynUser, ASYN_TRACE_FLOW, "wrote %lu to %s, return %s\n",
asynPrint(pasynUser, ASYN_TRACE_FLOW, "wrote %lu %sto %s, return %s\n",
(unsigned long)*nbytesTransfered,
(tty->break_len > 0 ? "(with BREAK) " : ""),
tty->serialDeviceName,
pasynManager->strStatus(status));
return status;
Expand Down
43 changes: 43 additions & 0 deletions asyn/miscellaneous/asynInterposeCom.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ typedef struct interposePvt {
int stop;
int flow;
int break_active;
unsigned break_len; /* length of break (ms) in autobreak mode */
unsigned break_delay; /* break delay (ms) in autobreak mode */

char *xBuf; /* Buffer for transmit IAC stuffing */
size_t xBufCapacity;
Expand Down Expand Up @@ -124,6 +126,9 @@ expectChar(interposePvt *pinterposePvt, asynUser *pasynUser, int expect)
return 1;
}

static asynStatus
sbComPortOption(interposePvt *pinterposePvt, asynUser *pasynUser, const char *xBuf, int xLen, char *rBuf);

/*
* asynOctet methods
*/
Expand All @@ -141,6 +146,7 @@ writeIt(void *ppvt, asynUser *pasynUser,
const char *iac;
char *dst = pinterposePvt->xBuf;
size_t nIAC = 0;
char xBuf[5], rBuf[4];
asynStatus status;

if ((iac = memchr(data, C_IAC, numchars)) != NULL) {
Expand Down Expand Up @@ -189,6 +195,19 @@ writeIt(void *ppvt, asynUser *pasynUser,
}
status = pinterposePvt->pasynOctetDrv->write(pinterposePvt->drvOctetPvt,
pasynUser, data, numchars, nbytesTransfered);
if (!pinterposePvt->break_active && pinterposePvt->break_len > 0) {
if (pinterposePvt->break_delay > 0) {
epicsThreadSleep(pinterposePvt->break_delay / 1000.0);
}
xBuf[0] = CPO_SET_CONTROL;
xBuf[1] = CPO_CONTROL_BREAK_ON;
status = sbComPortOption(pinterposePvt, pasynUser, xBuf, 2, rBuf);
if (status != asynSuccess) return status;
epicsThreadSleep(((double)pinterposePvt->break_len) / 1000.);
xBuf[1] = CPO_CONTROL_BREAK_OFF;
status = sbComPortOption(pinterposePvt, pasynUser, xBuf, 2, rBuf);
if (status != asynSuccess) return status;
}
if (*nbytesTransfered == numchars)
*nbytesTransfered -= nIAC;
return status;
Expand Down Expand Up @@ -641,6 +660,24 @@ setOption(void *ppvt, asynUser *pasynUser, const char *key, const char *val)
}
return asynSuccess;
}
else if (epicsStrCaseCmp(key, "autobreak") == 0) {
unsigned break_len;
if(sscanf(val, "%u", &break_len) != 1) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"Bad number");
return asynError;
}
pinterposePvt->break_len = break_len;
}
else if (epicsStrCaseCmp(key, "autobreak_delay") == 0) {
unsigned break_delay;
if(sscanf(val, "%u", &break_delay) != 1) {
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
"Bad number");
return asynError;
}
pinterposePvt->break_delay = break_delay;
}
else {
if (pinterposePvt->pasynOptionDrv) {
/* Call the setOption function in the underlying driver */
Expand Down Expand Up @@ -706,6 +743,12 @@ getOption(void *ppvt, asynUser *pasynUser, const char *key, char *val, int valSi
l = epicsSnprintf(val, valSize, "%s",
pinterposePvt->break_active ? "on" : "off");
}
else if (epicsStrCaseCmp(key, "autobreak") == 0) {
l = epicsSnprintf(val, valSize, "%u", pinterposePvt->break_len);
}
else if (epicsStrCaseCmp(key, "autobreak_delay") == 0) {
l = epicsSnprintf(val, valSize, "%u", pinterposePvt->break_delay);
}
else {
if (pinterposePvt->pasynOptionDrv) {
/* Call the getOption function in the underlying driver */
Expand Down
19 changes: 14 additions & 5 deletions docs/source/asynDriver.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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",
"ixon" and "break".
"ixon", "break" and "autobreak".

asynInterposeDelay
~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -4228,7 +4228,11 @@ values. When a serial port connects the current values are fetched.
* - rs485_delay_rts_after_send
- msec_delay
* - break
off on <numeric-device-dependend-time>
- off on <numeric-device-dependent-time>
* - autobreak
- <numeric-device-dependent-time or 0 to disable>
* - autobreak_delay
- <time-in-ms or 0 to disable>

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
Expand Down Expand Up @@ -4265,9 +4269,14 @@ on hardware ports that support RS-485. The delay option units are integer millis

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.
specified time, which is in units of milliseconds on Windows and Linux but
implementation defined elsewhere (see tcsendbreak()).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.

For devices that use a serial break as a command terminator the autobreak option can
be used. This will wait autobreak_delay (ms) before asserting a serial break for
the specified length (see break above for time units) after every write. Use 0 to
disable autobreak or autobreak_delay

vxWorks IOC serial ports may need to be set up using hardware-specific commands.
Once this is done, the standard drvAsynSerialPortConfigure and asynSetOption commands
Expand Down