Merge pull request #11253 from akallabeth/serial-term-fixes

Serial term fixes
This commit is contained in:
akallabeth
2025-03-05 14:07:47 +01:00
committed by GitHub
9 changed files with 553 additions and 238 deletions

View File

@@ -193,6 +193,12 @@ static UINT serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
irp->IoStatus = STATUS_SUCCESS;
WLog_Print(serial->log, WLOG_DEBUG, "%s (DeviceId: %" PRIu32 ", FileId: %" PRIu32 ") created.",
serial->device.name, irp->device->id, irp->FileId);
DWORD BytesReturned = 0;
if (!CommDeviceIoControl(serial->hComm, IOCTL_SERIAL_RESET_DEVICE, NULL, 0, NULL, 0,
&BytesReturned, NULL))
goto error_handle;
error_handle:
Stream_Write_UINT32(irp->output, irp->FileId); /* FileId (4 bytes) */
Stream_Write_UINT8(irp->output, 0); /* Information (1 byte) */
@@ -530,6 +536,9 @@ error_out:
if (error && data->serial->rdpcontext)
setChannelError(data->serial->rdpcontext, error, "irp_thread_func reported an error");
if (error)
data->irp->Discard(data->irp);
/* NB: At this point, the server might already being reusing
* the CompletionId whereas the thread is not yet
* terminated */

View File

@@ -20,7 +20,7 @@ set(MODULE_PREFIX "WINPR_COMM")
if(NOT WIN32)
set(${MODULE_PREFIX}_SRCS comm.c comm.h)
if(NOT EMSCRIPTEN)
if(NOT EMSCRIPTEN AND NOT APPLE)
winpr_definition_add(WINPR_HAVE_SERIAL_SUPPORT)
list(
APPEND

View File

@@ -126,6 +126,7 @@ static const _SERIAL_IOCTL_NAME S_SERIAL_IOCTL_NAMES[] = {
{ IOCTL_USBPRINT_GET_1284_ID, "IOCTL_USBPRINT_GET_1284_ID" }
};
const char* _comm_serial_ioctl_name(ULONG number)
{
for (size_t x = 0; x < ARRAYSIZE(S_SERIAL_IOCTL_NAMES); x++)
@@ -195,14 +196,16 @@ static BOOL CommInitialized(void)
return TRUE;
}
void CommLog_Print(DWORD level, ...)
void CommLog_PrintEx(DWORD level, const char* file, size_t line, const char* fkt, ...)
{
if (!CommInitialized())
return;
if (!WLog_IsLevelActive(sLog, level))
return;
va_list ap = { 0 };
va_start(ap, level);
WLog_PrintVA(sLog, level, ap);
va_start(ap, fkt);
WLog_PrintMessageVA(sLog, WLOG_MESSAGE_TEXT, level, line, file, fkt, ap);
va_end(ap);
}
@@ -501,8 +504,8 @@ BOOL GetCommState(HANDLE hFile, LPDCB lpDCB)
lpLocalDcb->fAbortOnError = (handflow.ControlHandShake & SERIAL_ERROR_ABORT) != 0;
/* lpLocalDcb->fDummy2 not used */
lpLocalDcb->wReserved = 0; /* must be zero */
lpLocalDcb->XonLim = handflow.XonLimit;
lpLocalDcb->XoffLim = handflow.XoffLimit;
lpLocalDcb->XonLim = WINPR_ASSERTING_INT_CAST(WORD, handflow.XonLimit);
lpLocalDcb->XoffLim = WINPR_ASSERTING_INT_CAST(WORD, handflow.XoffLimit);
SERIAL_LINE_CONTROL lineControl = { 0 };
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL, NULL, 0, &lineControl,
@@ -728,7 +731,7 @@ BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
if (lpDCB->fBinary)
{
upcomingTermios.c_lflag &= WINPR_ASSERTING_INT_CAST(tcflag_t, ~ICANON);
upcomingTermios.c_lflag &= (tcflag_t)~ICANON;
}
else
{
@@ -742,7 +745,7 @@ BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
}
else
{
upcomingTermios.c_iflag &= WINPR_ASSERTING_INT_CAST(tcflag_t, ~INPCK);
upcomingTermios.c_iflag &= (tcflag_t)~INPCK;
}
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363423%28v=vs.85%29.aspx
@@ -756,7 +759,7 @@ BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
* TCSANOW matches the best this definition
*/
if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
{
SetLastError(ERROR_IO_DEVICE);
return FALSE;
@@ -1384,22 +1387,7 @@ HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShare
pComm->serverSerialDriverId = SerialDriverUnknown;
InitializeCriticalSection(&pComm->EventsLock);
#if defined(WINPR_HAVE_COMM_COUNTERS)
if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0)
{
char ebuffer[256] = { 0 };
CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
CommLog_Print(WLOG_WARN, "could not read counters.");
/* could not initialize counters but keep on.
*
* Not all drivers, especially for USB to serial
* adapters (e.g. those based on pl2303), does support
* this call.
*/
ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
}
#endif
(void)CommUpdateIOCount(pComm, TRUE);
/* The binary/raw mode is required for the redirection but
* only flags that are not handle somewhere-else, except
@@ -1412,8 +1400,8 @@ HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShare
goto error_handle;
}
upcomingTermios.c_iflag &= WINPR_ASSERTING_INT_CAST(
tcflag_t, ~(/*IGNBRK |*/ BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL /*| IXON*/));
upcomingTermios.c_iflag &=
(tcflag_t) ~(/*IGNBRK |*/ BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL /*| IXON*/);
upcomingTermios.c_oflag = 0; /* <=> &= ~OPOST */
upcomingTermios.c_lflag = 0; /* <=> &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); */
/* upcomingTermios.c_cflag &= ~(CSIZE | PARENB); */
@@ -1426,7 +1414,7 @@ HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShare
/* a few more settings required for the redirection */
upcomingTermios.c_cflag |= CLOCAL | CREAD;
if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
{
SetLastError(ERROR_IO_DEVICE);
goto error_handle;
@@ -1504,3 +1492,282 @@ int eventfd_write(int fd, eventfd_t value)
}
#endif
#endif
static const char* CommIoCtlToStr(unsigned long int io)
{
switch (io)
{
#if defined(WINPR_HAVE_SERIAL_SUPPORT)
case TCGETS:
return "TCGETS";
case TCSETS:
return "TCSETS";
case TCSETSW:
return "TCSETSW";
case TCSETSF:
return "TCSETSF";
case TCGETA:
return "TCGETA";
case TCSETA:
return "TCSETA";
case TCSETAW:
return "TCSETAW";
case TCSETAF:
return "TCSETAF";
case TCSBRK:
return "TCSBRK";
case TCXONC:
return "TCXONC";
case TCFLSH:
return "TCFLSH";
case TIOCEXCL:
return "TIOCEXCL";
case TIOCNXCL:
return "TIOCNXCL";
case TIOCSCTTY:
return "TIOCSCTTY";
case TIOCGPGRP:
return "TIOCGPGRP";
case TIOCSPGRP:
return "TIOCSPGRP";
case TIOCOUTQ:
return "TIOCOUTQ";
case TIOCSTI:
return "TIOCSTI";
case TIOCGWINSZ:
return "TIOCGWINSZ";
case TIOCSWINSZ:
return "TIOCSWINSZ";
case TIOCMGET:
return "TIOCMGET";
case TIOCMBIS:
return "TIOCMBIS";
case TIOCMBIC:
return "TIOCMBIC";
case TIOCMSET:
return "TIOCMSET";
case TIOCGSOFTCAR:
return "TIOCGSOFTCAR";
case TIOCSSOFTCAR:
return "TIOCSSOFTCAR";
case FIONREAD:
return "FIONREAD/TIOCINQ";
case TIOCLINUX:
return "TIOCLINUX";
case TIOCCONS:
return "TIOCCONS";
case TIOCGSERIAL:
return "TIOCGSERIAL";
case TIOCSSERIAL:
return "TIOCSSERIAL";
case TIOCPKT:
return "TIOCPKT";
case FIONBIO:
return "FIONBIO";
case TIOCNOTTY:
return "TIOCNOTTY";
case TIOCSETD:
return "TIOCSETD";
case TIOCGETD:
return "TIOCGETD";
case TCSBRKP:
return "TCSBRKP";
case TIOCSBRK:
return "TIOCSBRK";
case TIOCCBRK:
return "TIOCCBRK";
case TIOCGSID:
return "TIOCGSID";
case TIOCGRS485:
return "TIOCGRS485";
case TIOCSRS485:
return "TIOCSRS485";
case TIOCSPTLCK:
return "TIOCSPTLCK";
case TCGETX:
return "TCGETX";
case TCSETX:
return "TCSETX";
case TCSETXF:
return "TCSETXF";
case TCSETXW:
return "TCSETXW";
case TIOCSIG:
return "TIOCSIG";
case TIOCVHANGUP:
return "TIOCVHANGUP";
case TIOCGPTPEER:
return "TIOCGPTPEER";
case FIONCLEX:
return "FIONCLEX";
case FIOCLEX:
return "FIOCLEX";
case FIOASYNC:
return "FIOASYNC";
case TIOCSERCONFIG:
return "TIOCSERCONFIG";
case TIOCSERGWILD:
return "TIOCSERGWILD";
case TIOCSERSWILD:
return "TIOCSERSWILD";
case TIOCGLCKTRMIOS:
return "TIOCGLCKTRMIOS";
case TIOCSLCKTRMIOS:
return "TIOCSLCKTRMIOS";
case TIOCSERGSTRUCT:
return "TIOCSERGSTRUCT";
case TIOCSERGETLSR:
return "TIOCSERGETLSR";
case TIOCSERGETMULTI:
return "TIOCSERGETMULTI";
case TIOCSERSETMULTI:
return "TIOCSERSETMULTI";
case TIOCMIWAIT:
return "TIOCMIWAIT";
case TIOCGICOUNT:
return "TIOCGICOUNT";
case FIOQSIZE:
return "FIOQSIZE";
case TIOCPKT_DATA:
return "TIOCPKT_DATA";
case TIOCPKT_FLUSHWRITE:
return "TIOCPKT_FLUSHWRITE";
case TIOCPKT_STOP:
return "TIOCPKT_STOP";
case TIOCPKT_START:
return "TIOCPKT_START";
case TIOCPKT_NOSTOP:
return "TIOCPKT_NOSTOP";
case TIOCPKT_DOSTOP:
return "TIOCPKT_DOSTOP";
case TIOCPKT_IOCTL:
return "TIOCPKT_IOCTL";
#endif
default:
return "UNKNOWN";
}
}
static BOOL CommStatusErrorEx(WINPR_COMM* pComm, unsigned long int ctl, const char* file,
const char* fkt, size_t line)
{
WINPR_ASSERT(pComm);
BOOL rc = pComm->permissive ? TRUE : FALSE;
const DWORD level = rc ? WLOG_DEBUG : WLOG_WARN;
char ebuffer[256] = { 0 };
const char* str = CommIoCtlToStr(ctl);
if (CommInitialized())
{
if (WLog_IsLevelActive(sLog, level))
{
WLog_PrintMessage(sLog, WLOG_MESSAGE_TEXT, level, line, file, fkt,
"%s [0x%08" PRIx32 "] ioctl failed, errno=[%d] %s.", str, ctl, errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
}
}
if (!rc)
SetLastError(ERROR_IO_DEVICE);
return rc;
}
BOOL CommIoCtl_int(WINPR_COMM* pComm, unsigned long int ctl, void* data, const char* file,
const char* fkt, size_t line)
{
if (ioctl(pComm->fd, ctl, data) < 0)
{
if (!CommStatusErrorEx(pComm, ctl, file, fkt, line))
return FALSE;
}
return TRUE;
}
BOOL CommUpdateIOCount(WINPR_ATTR_UNUSED HANDLE handle, WINPR_ATTR_UNUSED BOOL checkSupportStatus)
{
WINPR_COMM* pComm = (WINPR_COMM*)handle;
WINPR_ASSERT(pComm);
#if defined(WINPR_HAVE_COMM_COUNTERS)
ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
if (pComm->TIOCGICOUNTSupported || checkSupportStatus)
{
const int rc = ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters));
if (checkSupportStatus)
pComm->TIOCGICOUNTSupported = rc >= 0;
else if (rc < 0)
{
if (!CommStatusErrorEx(pComm, TIOCGICOUNT, __FILE__, __func__, __LINE__))
return FALSE;
}
}
#endif
return TRUE;
}
static const char* CommSerialEvFlagString(ULONG flag)
{
switch (flag)
{
case SERIAL_EV_RXCHAR:
return "SERIAL_EV_RXCHAR";
case SERIAL_EV_RXFLAG:
return "SERIAL_EV_RXFLAG";
case SERIAL_EV_TXEMPTY:
return "SERIAL_EV_TXEMPTY";
case SERIAL_EV_CTS:
return "SERIAL_EV_CTS ";
case SERIAL_EV_DSR:
return "SERIAL_EV_DSR ";
case SERIAL_EV_RLSD:
return "SERIAL_EV_RLSD";
case SERIAL_EV_BREAK:
return "SERIAL_EV_BREAK";
case SERIAL_EV_ERR:
return "SERIAL_EV_ERR ";
case SERIAL_EV_RING:
return "SERIAL_EV_RING";
case SERIAL_EV_PERR:
return "SERIAL_EV_PERR";
case SERIAL_EV_RX80FULL:
return "SERIAL_EV_RX80FULL";
case SERIAL_EV_EVENT1:
return "SERIAL_EV_EVENT1";
case SERIAL_EV_EVENT2:
return "SERIAL_EV_EVENT2";
case SERIAL_EV_WINPR_WAITING:
return "SERIAL_EV_WINPR_WAITING";
case SERIAL_EV_WINPR_STOP:
return "SERIAL_EV_WINPR_STOP";
default:
return "SERIAL_EV_UNKNOWN";
}
}
const char* CommSerialEvString(ULONG status, char* buffer, size_t size)
{
const ULONG flags[] = { SERIAL_EV_RXCHAR, SERIAL_EV_RXFLAG, SERIAL_EV_TXEMPTY,
SERIAL_EV_CTS, SERIAL_EV_DSR, SERIAL_EV_RLSD,
SERIAL_EV_BREAK, SERIAL_EV_ERR, SERIAL_EV_RING,
SERIAL_EV_PERR, SERIAL_EV_RX80FULL, SERIAL_EV_EVENT1,
SERIAL_EV_EVENT2, SERIAL_EV_WINPR_WAITING, SERIAL_EV_WINPR_STOP };
winpr_str_append("{", buffer, size, "");
const char* sep = "";
for (size_t x = 0; x < ARRAYSIZE(flags); x++)
{
const ULONG flag = flags[x];
if (status & flag)
{
winpr_str_append(CommSerialEvFlagString(flag), buffer, size, sep);
sep = "|";
}
}
char number[32] = { 0 };
(void)_snprintf(number, sizeof(number), "}[0x%08" PRIx32 "]", status);
winpr_str_append(number, buffer, size, "");
return buffer;
}

View File

@@ -77,6 +77,12 @@ struct winpr_comm
BYTE eventChar;
/* NB: CloseHandle() has to free resources */
ULONG XOnLimit;
ULONG XOffLimit;
#if defined(WINPR_HAVE_COMM_COUNTERS)
BOOL TIOCGICOUNTSupported;
#endif
};
typedef struct winpr_comm WINPR_COMM;
@@ -100,13 +106,22 @@ typedef struct winpr_comm WINPR_COMM;
#define WINPR_PURGE_TXABORT 0x00000001 /* abort pending transmission */
#define WINPR_PURGE_RXABORT 0x00000002 /* abort pending reception */
void CommLog_Print(DWORD wlog_level, ...);
#define CommLog_Print(level, ...) CommLog_PrintEx(level, __FILE__, __LINE__, __func__, __VA_ARGS__)
void CommLog_PrintEx(DWORD wlog_level, const char* file, size_t line, const char* fkt, ...);
BOOL CommIsHandled(HANDLE handle);
BOOL CommIsHandleValid(HANDLE handle);
BOOL CommCloseHandle(HANDLE handle);
const HANDLE_CREATOR* GetCommHandleCreator(void);
#define CommIoCtl(pComm, ctl, data) \
CommIoCtl_int((pComm), (ctl), (data), __FILE__, __func__, __LINE__)
BOOL CommIoCtl_int(WINPR_COMM* pComm, unsigned long int ctl, void* data, const char* file,
const char* fkt, size_t line);
BOOL CommUpdateIOCount(HANDLE handle, BOOL checkSupportStatus);
const char* CommSerialEvString(ULONG status, char* buffer, size_t size);
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
#ifndef WITH_EVENTFD_READ_WRITE
int eventfd_read(int fd, eventfd_t* value);

View File

@@ -507,7 +507,8 @@ BOOL CommWriteFile(HANDLE hDevice, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite
if (FD_ISSET(pComm->fd_write, &write_set))
{
ssize_t nbWritten = 0;
nbWritten = write(pComm->fd_write, ((const BYTE*)lpBuffer) + (*lpNumberOfBytesWritten),
const BYTE* ptr = lpBuffer;
nbWritten = write(pComm->fd_write, &ptr[*lpNumberOfBytesWritten],
nNumberOfBytesToWrite - (*lpNumberOfBytesWritten));
if (nbWritten < 0)

View File

@@ -33,6 +33,8 @@
#include "comm_sercx_sys.h"
#include "comm_sercx2_sys.h"
static const char* comm_ioctl_modem_status_string(ULONG status, char* buffer, size_t size);
/* NB: MS-RDPESP's recommendation:
*
* <2> Section 3.2.5.1.6: Windows Implementations use IOCTL constants
@@ -52,6 +54,7 @@ static BOOL s_CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID
DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize,
LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
{
char buffer[128] = { 0 };
WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
const SERIAL_DRIVER* pServerSerialDriver = NULL;
@@ -75,7 +78,8 @@ static BOOL s_CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID
*lpBytesReturned = 0; /* will be adjusted if required ... */
CommLog_Print(WLOG_DEBUG, "CommDeviceIoControl: IoControlCode: 0x%0.8x", dwIoControlCode);
CommLog_Print(WLOG_DEBUG, "CommDeviceIoControl: IoControlCode: %s [0x%08" PRIx32 "]",
_comm_serial_ioctl_name(dwIoControlCode), dwIoControlCode);
/* remoteSerialDriver to be use ...
*
@@ -374,6 +378,8 @@ static BOOL s_CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID
if (!pServerSerialDriver->get_modemstatus(pComm, pRegister))
return FALSE;
CommLog_Print(WLOG_DEBUG, "modem status %s" PRIx32,
comm_ioctl_modem_status_string(*pRegister, buffer, sizeof(buffer)));
*lpBytesReturned = sizeof(ULONG);
return TRUE;
}
@@ -392,7 +398,10 @@ static BOOL s_CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID
return FALSE;
}
return pServerSerialDriver->set_wait_mask(pComm, pWaitMask);
const BOOL rc = pServerSerialDriver->set_wait_mask(pComm, pWaitMask);
CommLog_Print(WLOG_DEBUG, "set_wait_mask %s -> %d",
CommSerialEvString(*pWaitMask, buffer, sizeof(buffer)), rc);
return rc;
}
break;
}
@@ -412,6 +421,8 @@ static BOOL s_CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID
if (!pServerSerialDriver->get_wait_mask(pComm, pWaitMask))
return FALSE;
CommLog_Print(WLOG_DEBUG, "get_wait_mask %s",
CommSerialEvString(*pWaitMask, buffer, sizeof(buffer)));
*lpBytesReturned = sizeof(ULONG);
return TRUE;
}
@@ -430,14 +441,12 @@ static BOOL s_CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID
return FALSE;
}
if (!pServerSerialDriver->wait_on_mask(pComm, pOutputMask))
{
*lpBytesReturned = sizeof(ULONG);
return FALSE;
}
const BOOL rc = pServerSerialDriver->wait_on_mask(pComm, pOutputMask);
*lpBytesReturned = sizeof(ULONG);
return TRUE;
CommLog_Print(WLOG_DEBUG, "wait_on_mask %s -> %d",
CommSerialEvString(*pOutputMask, buffer, sizeof(buffer)), rc);
return rc;
}
break;
}
@@ -647,19 +656,19 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
{
/* This might be a hint for a bug, especially when result==TRUE */
CommLog_Print(WLOG_WARN,
"lpBytesReturned=%" PRIu32 " and nOutBufferSize=%" PRIu32 " are different!",
*lpBytesReturned, nOutBufferSize);
"IoControlCode=[0x%08" PRIX32 "] %s: lpBytesReturned=%" PRIu32
" and nOutBufferSize=%" PRIu32 " are different!",
dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode), *lpBytesReturned,
nOutBufferSize);
}
if (pComm->permissive)
{
if (!result)
{
CommLog_Print(
WLOG_WARN,
"[permissive]: whereas it failed, made to succeed IoControlCode=[0x%08" PRIX32
"] %s, last-error: 0x%08" PRIX32 "",
dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode), GetLastError());
CommLog_Print(WLOG_WARN,
"[permissive]: IoControlCode=[0x%08" PRIX32 "] %s failed, ignoring",
dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode));
}
return TRUE; /* always! */
@@ -668,50 +677,82 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
return result;
}
int _comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios* termios_p)
int comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios* termios_p)
{
int result = 0;
struct termios currentState = { 0 };
if ((result = tcsetattr(fd, optional_actions, termios_p)) < 0)
size_t count = 0;
do
{
CommLog_Print(WLOG_WARN, "tcsetattr failure, errno: %d", errno);
return result;
}
/* NB: tcsetattr() can succeed even if not all changes have been applied. */
if ((result = tcgetattr(fd, &currentState)) < 0)
{
CommLog_Print(WLOG_WARN, "tcgetattr failure, errno: %d", errno);
return result;
}
// NOLINTNEXTLINE(bugprone-suspicious-memory-comparison,cert-exp42-c,cert-flp37-c)
if (memcmp(&currentState, termios_p, sizeof(struct termios)) != 0)
{
CommLog_Print(WLOG_DEBUG,
"all termios parameters are not set yet, doing a second attempt...");
if ((result = tcsetattr(fd, optional_actions, termios_p)) < 0)
const int src = tcsetattr(fd, optional_actions, termios_p);
if (src < 0)
{
CommLog_Print(WLOG_WARN, "2nd tcsetattr failure, errno: %d", errno);
return result;
char buffer[64] = { 0 };
CommLog_Print(WLOG_WARN, "[%" PRIuz "] tcsetattr failure, errno: %s [%d]", count,
winpr_strerror(errno, buffer, sizeof(buffer)), errno);
return src;
}
ZeroMemory(&currentState, sizeof(struct termios));
if ((result = tcgetattr(fd, &currentState)) < 0)
/* NB: tcsetattr() can succeed even if not all changes have been applied. */
const int rrc = tcgetattr(fd, &currentState);
if (rrc < 0)
{
CommLog_Print(WLOG_WARN, "tcgetattr failure, errno: %d", errno);
return result;
char buffer[64] = { 0 };
CommLog_Print(WLOG_WARN, "[%" PRIuz "] tcgetattr failure, errno: %s [%d]", count,
winpr_strerror(errno, buffer, sizeof(buffer)), errno);
return rrc;
}
// NOLINTNEXTLINE(bugprone-suspicious-memory-comparison,cert-exp42-c,cert-flp37-c)
if (memcmp(&currentState, termios_p, sizeof(struct termios)) != 0)
{
CommLog_Print(WLOG_WARN,
"Failure: all termios parameters are still not set on a second attempt");
return -1;
}
}
} while ((memcmp(&currentState, termios_p, sizeof(struct termios)) != 0) && (count++ < 2));
return 0;
}
static const char* comm_ioctl_modem_flag_str(ULONG flag)
{
switch (flag)
{
case SERIAL_MSR_DCTS:
return "SERIAL_MSR_DCTS";
case SERIAL_MSR_DDSR:
return "SERIAL_MSR_DDSR";
case SERIAL_MSR_TERI:
return "SERIAL_MSR_TERI";
case SERIAL_MSR_DDCD:
return "SERIAL_MSR_DDCD";
case SERIAL_MSR_CTS:
return "SERIAL_MSR_CTS";
case SERIAL_MSR_DSR:
return "SERIAL_MSR_DSR";
case SERIAL_MSR_RI:
return "SERIAL_MSR_RI";
case SERIAL_MSR_DCD:
return "SERIAL_MSR_DCD";
default:
return "SERIAL_MSR_UNKNOWN";
}
}
const char* comm_ioctl_modem_status_string(ULONG status, char* buffer, size_t size)
{
const ULONG flags[] = { SERIAL_MSR_DCTS, SERIAL_MSR_DDSR, SERIAL_MSR_TERI, SERIAL_MSR_DDCD,
SERIAL_MSR_CTS, SERIAL_MSR_DSR, SERIAL_MSR_RI, SERIAL_MSR_DCD
};
winpr_str_append("{", buffer, size, "");
const char* sep = "";
for (size_t x = 0; x < ARRAYSIZE(flags); x++)
{
const ULONG flag = flags[x];
if (status & flag)
{
winpr_str_append(comm_ioctl_modem_flag_str(flag), buffer, size, sep);
sep = "|";
}
}
char number[32] = { 0 };
(void)_snprintf(number, sizeof(number), "}[0x%08" PRIx32 "]", status);
winpr_str_append(number, buffer, size, "");
return buffer;
}

View File

@@ -79,8 +79,8 @@ extern "C"
{
ULONG ControlHandShake;
ULONG FlowReplace;
WORD XonLimit;
WORD XoffLimit;
ULONG XonLimit;
ULONG XoffLimit;
} SERIAL_HANDFLOW, *PSERIAL_HANDFLOW;
#define SERIAL_DTR_MASK ((ULONG)0x03)
@@ -223,7 +223,7 @@ extern "C"
} SERIAL_DRIVER;
int _comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios* termios_p);
int comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios* termios_p);
#ifdef __cplusplus
}

View File

@@ -41,7 +41,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
return FALSE;
}
int _comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios* termios_p)
int comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios* termios_p)
{
WINPR_UNUSED(fd);
WINPR_UNUSED(optional_actions);

View File

@@ -152,7 +152,55 @@ static const speed_t BAUD_TABLE[][3] = {
{ BAUD_TABLE_END, 0, 0 }
};
static BOOL commstatus_error(WINPR_COMM* pComm, const char* ctrl);
static const char* get_modem_flag_str(int flag)
{
if (flag & TIOCM_LE)
return "DSR";
if (flag & TIOCM_DTR)
return "DTR";
if (flag & TIOCM_RTS)
return "RTS";
if (flag & TIOCM_ST)
return "Secondary TXD";
if (flag & TIOCM_SR)
return "Secondary RXD";
if (flag & TIOCM_CTS)
return "CTS";
if (flag & TIOCM_CAR)
return "DCD";
if (flag & TIOCM_CD)
return "CD";
if (flag & TIOCM_RNG)
return "RNG";
if (flag & TIOCM_RI)
return "RI";
if (flag & TIOCM_DSR)
return "DSR";
return "UNKNOWN";
}
static const char* get_modem_status_str(int status, char* buffer, size_t size)
{
const int flags[] = { TIOCM_LE, TIOCM_DTR, TIOCM_RTS, TIOCM_ST, TIOCM_SR, TIOCM_CTS,
TIOCM_CAR, TIOCM_CD, TIOCM_RNG, TIOCM_RI, TIOCM_DSR };
winpr_str_append("{", buffer, size, "");
const char* sep = "";
for (size_t x = 0; x < ARRAYSIZE(flags); x++)
{
const int flag = flags[x];
if (status & flag)
{
winpr_str_append(get_modem_flag_str(flag), buffer, size, sep);
sep = "|";
}
}
char number[32] = { 0 };
(void)_snprintf(number, sizeof(number), "}[0x%08x]", (unsigned)status);
winpr_str_append(number, buffer, size, "");
return buffer;
}
static BOOL get_properties(WINPR_ATTR_UNUSED WINPR_COMM* pComm, COMMPROP* pProperties)
{
@@ -248,9 +296,9 @@ static BOOL set_baud_rate(WINPR_COMM* pComm, const SERIAL_BAUD_RATE* pBaudRate)
WINPR_ASSERT(cfgetispeed(&futureState) == newSpeed);
if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &futureState) < 0)
if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &futureState) < 0)
{
CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "",
CommLog_Print(WLOG_WARN, "comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "",
GetLastError());
return FALSE;
}
@@ -369,9 +417,9 @@ static BOOL set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars
upcomingTermios.c_cc[VSTOP] = pSerialChars->XoffChar;
if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
{
CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "",
CommLog_Print(WLOG_WARN, "comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "",
GetLastError());
return FALSE;
}
@@ -517,9 +565,9 @@ static BOOL set_line_control(WINPR_COMM* pComm, const SERIAL_LINE_CONTROL* pLine
break;
}
if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
{
CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "",
CommLog_Print(WLOG_WARN, "comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "",
GetLastError());
return FALSE;
}
@@ -747,29 +795,12 @@ static BOOL set_handflow(WINPR_COMM* pComm, const SERIAL_HANDFLOW* pHandflow)
/* XonLimit */
// FIXME: could be implemented during read/write I/O
if (pHandflow->XonLimit != TTY_THRESHOLD_UNTHROTTLE)
{
CommLog_Print(WLOG_WARN, "Attempt to set XonLimit with an unsupported value: %" PRId32 "",
pHandflow->XonLimit);
SetLastError(ERROR_NOT_SUPPORTED);
result = FALSE; /* but keep on */
}
pComm->XOffLimit = pHandflow->XoffLimit;
pComm->XOnLimit = pHandflow->XonLimit;
/* XoffChar */
// FIXME: could be implemented during read/write I/O
if (pHandflow->XoffLimit != TTY_THRESHOLD_THROTTLE)
if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
{
CommLog_Print(WLOG_WARN, "Attempt to set XoffLimit with an unsupported value: %" PRId32 "",
pHandflow->XoffLimit);
SetLastError(ERROR_NOT_SUPPORTED);
result = FALSE; /* but keep on */
}
if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
{
CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "",
CommLog_Print(WLOG_WARN, "comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "",
GetLastError());
return FALSE;
}
@@ -836,13 +867,8 @@ static BOOL get_handflow(WINPR_COMM* pComm, SERIAL_HANDFLOW* pHandflow)
/* SERIAL_XOFF_CONTINUE unsupported */
/* XonLimit */
pHandflow->XonLimit = TTY_THRESHOLD_UNTHROTTLE;
/* XoffLimit */
pHandflow->XoffLimit = TTY_THRESHOLD_THROTTLE;
pHandflow->XonLimit = pComm->XOnLimit;
pHandflow->XoffLimit = pComm->XOffLimit;
return TRUE;
}
@@ -903,32 +929,14 @@ static BOOL set_lines(WINPR_COMM* pComm, UINT32 lines)
{
WINPR_ASSERT(pComm);
if (ioctl(pComm->fd, TIOCMBIS, &lines) < 0)
{
char ebuffer[256] = { 0 };
CommLog_Print(WLOG_WARN, "TIOCMBIS ioctl failed, lines=0x%" PRIX32 ", errno=[%d] %s", lines,
errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
SetLastError(ERROR_IO_DEVICE);
return FALSE;
}
return TRUE;
return CommIoCtl(pComm, TIOCMBIS, &lines);
}
static BOOL clear_lines(WINPR_COMM* pComm, UINT32 lines)
{
WINPR_ASSERT(pComm);
if (ioctl(pComm->fd, TIOCMBIC, &lines) < 0)
{
char ebuffer[256] = { 0 };
CommLog_Print(WLOG_WARN, "TIOCMBIC ioctl failed, lines=0x%" PRIX32 ", errno=[%d] %s", lines,
errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
SetLastError(ERROR_IO_DEVICE);
return FALSE;
}
return TRUE;
return CommIoCtl(pComm, TIOCMBIC, &lines);
}
static BOOL set_dtr(WINPR_COMM* pComm)
@@ -1004,28 +1012,35 @@ static BOOL clear_rts(WINPR_COMM* pComm)
return clear_lines(pComm, TIOCM_RTS);
}
static BOOL get_modemstatus(WINPR_COMM* pComm, ULONG* pRegister)
static BOOL get_raw_modemstatus(WINPR_COMM* pComm, int* pRegister)
{
UINT32 lines = 0;
WINPR_ASSERT(pComm);
WINPR_ASSERT(pRegister);
*pRegister = 0;
if (ioctl(pComm->fd, TIOCMGET, &lines) < 0)
{
if (!commstatus_error(pComm, "TIOCMGET"))
return FALSE;
}
const BOOL rc = CommIoCtl(pComm, TIOCMGET, pRegister);
char buffer[128] = { 0 };
CommLog_Print(WLOG_DEBUG, "status %s",
get_modem_status_str(*pRegister, buffer, sizeof(buffer)));
return rc;
}
static BOOL get_modemstatus(WINPR_COMM* pComm, ULONG* pRegister)
{
int lines = 0;
if (!get_raw_modemstatus(pComm, &lines))
return FALSE;
*pRegister = 0;
if (lines & TIOCM_CTS)
*pRegister |= SERIAL_MSR_CTS;
*pRegister |= SERIAL_MSR_CTS | SERIAL_MSR_DCTS;
if (lines & TIOCM_DSR)
*pRegister |= SERIAL_MSR_DSR;
*pRegister |= SERIAL_MSR_DSR | SERIAL_MSR_DDSR;
if (lines & TIOCM_RI)
*pRegister |= SERIAL_MSR_RI;
*pRegister |= SERIAL_MSR_RI | SERIAL_MSR_TERI;
if (lines & TIOCM_CD)
*pRegister |= SERIAL_MSR_DCD;
*pRegister |= SERIAL_MSR_DCD | SERIAL_MSR_DDCD;
return TRUE;
}
@@ -1083,17 +1098,12 @@ static BOOL set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask)
if (*pWaitMask == 0)
{
/* clearing pending events */
#if defined(WINPR_HAVE_COMM_COUNTERS)
if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0)
if (!CommUpdateIOCount(pComm, FALSE))
{
if (!commstatus_error(pComm, "TIOCGICOUNT"))
{
LeaveCriticalSection(&pComm->EventsLock);
return FALSE;
}
ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
LeaveCriticalSection(&pComm->EventsLock);
return FALSE;
}
#endif
pComm->PendingEvents = 0;
}
@@ -1239,20 +1249,6 @@ static BOOL purge(WINPR_COMM* pComm, const ULONG* pPurgeMask)
return TRUE;
}
BOOL commstatus_error(WINPR_COMM* pComm, const char* ctrl)
{
char ebuffer[256] = { 0 };
CommLog_Print(WLOG_WARN, "%s ioctl failed, errno=[%d] %s.", ctrl, errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
if (!pComm->permissive)
{
SetLastError(ERROR_IO_DEVICE);
return FALSE;
}
return TRUE;
}
/* NB: get_commstatus also produces most of the events consumed by wait_on_mask(). Exceptions:
* - SERIAL_EV_RXFLAG: FIXME: once EventChar supported
*
@@ -1272,25 +1268,14 @@ static BOOL get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus)
ZeroMemory(pCommstatus, sizeof(SERIAL_STATUS));
ULONG status = 0;
if (!get_modemstatus(pComm, &status))
{
if (!commstatus_error(pComm, "TIOCGICOUNT"))
goto fail;
/* Errors and events based on counters could not be
* detected but keep on.
*/
SetLastError(0);
status = 0;
}
int status = 0;
if (!get_raw_modemstatus(pComm, &status))
goto fail;
#if defined(WINPR_HAVE_COMM_COUNTERS)
if (ioctl(pComm->fd, TIOCGICOUNT, &currentCounters) < 0)
{
if (!commstatus_error(pComm, "TIOCGICOUNT"))
goto fail;
ZeroMemory(&currentCounters, sizeof(struct serial_icounter_struct));
}
if (!CommUpdateIOCount(pComm, FALSE))
goto fail;
currentCounters = pComm->counters;
/* NB: preferred below (currentCounters.* != pComm->counters.*) over (currentCounters.* >
* pComm->counters.*) thinking the counters can loop */
@@ -1329,11 +1314,8 @@ static BOOL get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus)
/* HoldReasons */
/* TODO: SERIAL_TX_WAITING_FOR_CTS */
/* TODO: SERIAL_TX_WAITING_FOR_DSR */
/* TODO: SERIAL_TX_WAITING_FOR_DCD */
if (status & TIOCM_CTS)
pComm->PendingEvents |= SERIAL_EV_CTS;
/* TODO: SERIAL_TX_WAITING_FOR_XON */
@@ -1341,37 +1323,34 @@ static BOOL get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus)
/* TODO: SERIAL_TX_WAITING_XOFF_SENT */
if (status & TIOCM_SR)
pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
/* AmountInInQueue */
int available = 0;
if (!CommIoCtl(pComm, FIONREAD, &available))
goto fail;
#if defined(__linux__)
if (ioctl(pComm->fd, TIOCINQ, &(pCommstatus->AmountInInQueue)) < 0)
{
if (!commstatus_error(pComm, "TIOCINQ"))
goto fail;
}
if (!CommIoCtl(pComm, TIOCINQ, &pCommstatus->AmountInInQueue))
goto fail;
#endif
/* AmountInOutQueue */
if (ioctl(pComm->fd, TIOCOUTQ, &(pCommstatus->AmountInOutQueue)) < 0)
{
if (!commstatus_error(pComm, "TIOCOUTQ"))
goto fail;
}
if (!CommIoCtl(pComm, TIOCOUTQ, &pCommstatus->AmountInOutQueue))
goto fail;
/* BOOLEAN EofReceived; FIXME: once EofChar supported */
/* BOOLEAN WaitForImmediate; TODO: once IOCTL_SERIAL_IMMEDIATE_CHAR fully supported */
/* other events based on counters */
#if defined(WINPR_HAVE_COMM_COUNTERS)
if (currentCounters.rx != pComm->counters.rx)
{
pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
}
if ((currentCounters.tx != pComm->counters.tx) && /* at least a transmission occurred AND ...*/
(pCommstatus->AmountInOutQueue == 0)) /* output buffer is now empty */
if (available > 0)
pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
if (pCommstatus->AmountInOutQueue == 0) /* output buffer is now empty */
{
pComm->PendingEvents |= SERIAL_EV_TXEMPTY;
}
@@ -1382,6 +1361,21 @@ static BOOL get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus)
pComm->PendingEvents &= (uint32_t)~SERIAL_EV_TXEMPTY;
}
#if defined(WINPR_HAVE_COMM_COUNTERS)
if (currentCounters.tx != pComm->counters.tx)
{
pComm->PendingEvents |= SERIAL_EV_TXEMPTY;
}
else
{
pComm->PendingEvents |= SERIAL_EV_TXEMPTY;
}
if (currentCounters.rx != pComm->counters.rx)
{
pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
}
if (currentCounters.cts != pComm->counters.cts)
{
pComm->PendingEvents |= SERIAL_EV_CTS;
@@ -1401,7 +1395,6 @@ static BOOL get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus)
{
pComm->PendingEvents |= SERIAL_EV_RING;
}
pComm->counters = currentCounters;
#endif
@@ -1494,7 +1487,6 @@ static BOOL wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
consume_event(pComm, pOutputMask, SERIAL_EV_RXCHAR);
consume_event(pComm, pOutputMask, SERIAL_EV_RXFLAG);
consume_event(pComm, pOutputMask, SERIAL_EV_TXEMPTY);
consume_event(pComm, pOutputMask, SERIAL_EV_CTS);
consume_event(pComm, pOutputMask, SERIAL_EV_DSR);
consume_event(pComm, pOutputMask, SERIAL_EV_RLSD);
consume_event(pComm, pOutputMask, SERIAL_EV_BREAK);
@@ -1527,31 +1519,14 @@ static BOOL wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
static BOOL set_break_on(WINPR_COMM* pComm)
{
WINPR_ASSERT(pComm);
if (ioctl(pComm->fd, TIOCSBRK, NULL) < 0)
{
char ebuffer[256] = { 0 };
CommLog_Print(WLOG_WARN, "TIOCSBRK ioctl failed, errno=[%d] %s", errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
SetLastError(ERROR_IO_DEVICE);
return FALSE;
}
return TRUE;
return CommIoCtl(pComm, TIOCSBRK, NULL);
}
static BOOL set_break_off(WINPR_COMM* pComm)
{
WINPR_ASSERT(pComm);
if (ioctl(pComm->fd, TIOCCBRK, NULL) < 0)
{
char ebuffer[256] = { 0 };
CommLog_Print(WLOG_WARN, "TIOCSBRK ioctl failed, errno=[%d] %s", errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
SetLastError(ERROR_IO_DEVICE);
return FALSE;
}
return TRUE;
return CommIoCtl(pComm, TIOCCBRK, NULL);
}
static BOOL set_xoff(WINPR_COMM* pComm)
@@ -1588,12 +1563,12 @@ static BOOL set_xon(WINPR_COMM* pComm)
static BOOL get_dtrrts(WINPR_COMM* pComm, ULONG* pMask)
{
UINT32 lines = 0;
int lines = 0;
WINPR_ASSERT(pComm);
WINPR_ASSERT(pMask);
if (!get_modemstatus(pComm, &lines))
if (!get_raw_modemstatus(pComm, &lines))
return FALSE;
*pMask = 0;
@@ -1643,7 +1618,14 @@ static BOOL immediate_char(WINPR_COMM* pComm, const UCHAR* pChar)
static BOOL reset_device(WINPR_ATTR_UNUSED WINPR_COMM* pComm)
{
/* http://msdn.microsoft.com/en-us/library/dn265347%28v=vs.85%29.aspx */
return TRUE;
WINPR_ASSERT(pComm);
pComm->XOnLimit = TTY_THRESHOLD_UNTHROTTLE;
pComm->XOffLimit = TTY_THRESHOLD_THROTTLE;
(void)CommUpdateIOCount(pComm, TRUE);
return CommIoCtl(pComm, TIOCMSET, 0);
}
static const SERIAL_DRIVER SerialSys = {