From d4cde0b8e022b6d6f82ef0e7474cb22abbf42ef3 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Wed, 19 Feb 2025 10:09:03 +0100 Subject: [PATCH] [winpr,comm] fix serial port redirection * Unify ioctl logging * Fix device reset calls * Unify TIOCGICOUNT calls, only use them if they are supported * Fix XOn/XOff support * Fix modem status flags --- channels/serial/client/serial_main.c | 9 + winpr/libwinpr/comm/CMakeLists.txt | 2 +- winpr/libwinpr/comm/comm.c | 242 +++++++++++++++++++-- winpr/libwinpr/comm/comm.h | 12 ++ winpr/libwinpr/comm/comm_io.c | 3 +- winpr/libwinpr/comm/comm_ioctl.c | 86 +++----- winpr/libwinpr/comm/comm_ioctl.h | 6 +- winpr/libwinpr/comm/comm_ioctl_dummy.c | 2 +- winpr/libwinpr/comm/comm_serial_sys.c | 283 +++++++++++-------------- 9 files changed, 412 insertions(+), 233 deletions(-) diff --git a/channels/serial/client/serial_main.c b/channels/serial/client/serial_main.c index ee04433bc..1edf544f2 100644 --- a/channels/serial/client/serial_main.c +++ b/channels/serial/client/serial_main.c @@ -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 */ diff --git a/winpr/libwinpr/comm/CMakeLists.txt b/winpr/libwinpr/comm/CMakeLists.txt index 5210587c6..c959bd4ca 100644 --- a/winpr/libwinpr/comm/CMakeLists.txt +++ b/winpr/libwinpr/comm/CMakeLists.txt @@ -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 diff --git a/winpr/libwinpr/comm/comm.c b/winpr/libwinpr/comm/comm.c index e2186258e..9735f2ce0 100644 --- a/winpr/libwinpr/comm/comm.c +++ b/winpr/libwinpr/comm/comm.c @@ -728,7 +728,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 +742,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 +756,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 +1384,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 +1397,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 +1411,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 +1489,216 @@ 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; +} diff --git a/winpr/libwinpr/comm/comm.h b/winpr/libwinpr/comm/comm.h index 4a561d969..dc7f023ea 100644 --- a/winpr/libwinpr/comm/comm.h +++ b/winpr/libwinpr/comm/comm.h @@ -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; @@ -107,6 +113,12 @@ 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); + #if defined(WINPR_HAVE_SYS_EVENTFD_H) #ifndef WITH_EVENTFD_READ_WRITE int eventfd_read(int fd, eventfd_t* value); diff --git a/winpr/libwinpr/comm/comm_io.c b/winpr/libwinpr/comm/comm_io.c index fd1526212..201fc90ba 100644 --- a/winpr/libwinpr/comm/comm_io.c +++ b/winpr/libwinpr/comm/comm_io.c @@ -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) diff --git a/winpr/libwinpr/comm/comm_ioctl.c b/winpr/libwinpr/comm/comm_ioctl.c index d60e21319..3eb12cfb0 100644 --- a/winpr/libwinpr/comm/comm_ioctl.c +++ b/winpr/libwinpr/comm/comm_ioctl.c @@ -75,7 +75,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 +375,7 @@ static BOOL s_CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID if (!pServerSerialDriver->get_modemstatus(pComm, pRegister)) return FALSE; + CommLog_Print(WLOG_DEBUG, "modem status 0x%08" PRIx32, *pRegister); *lpBytesReturned = sizeof(ULONG); return TRUE; } @@ -392,7 +394,9 @@ 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 0x%08" PRIx32 " -> %d", *pWaitMask, rc); + return rc; } break; } @@ -412,6 +416,7 @@ 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 0x%08" PRIx32, *pWaitMask); *lpBytesReturned = sizeof(ULONG); return TRUE; } @@ -430,14 +435,11 @@ 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 0x%08" PRIx32 " -> %d", *pOutputMask, rc); + return rc; } break; } @@ -647,19 +649,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 +670,32 @@ 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, ¤tState)) < 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(¤tState, 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(¤tState, sizeof(struct termios)); - if ((result = tcgetattr(fd, ¤tState)) < 0) + /* NB: tcsetattr() can succeed even if not all changes have been applied. */ + const int rrc = tcgetattr(fd, ¤tState); + 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(¤tState, termios_p, sizeof(struct termios)) != 0) - { - CommLog_Print(WLOG_WARN, - "Failure: all termios parameters are still not set on a second attempt"); - return -1; - } - } + // NOLINTNEXTLINE(bugprone-suspicious-memory-comparison) + } while ((memcmp(¤tState, termios_p, sizeof(struct termios)) != 0) && (count++ < 2)); return 0; } diff --git a/winpr/libwinpr/comm/comm_ioctl.h b/winpr/libwinpr/comm/comm_ioctl.h index 9d46df536..4349174e2 100644 --- a/winpr/libwinpr/comm/comm_ioctl.h +++ b/winpr/libwinpr/comm/comm_ioctl.h @@ -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 } diff --git a/winpr/libwinpr/comm/comm_ioctl_dummy.c b/winpr/libwinpr/comm/comm_ioctl_dummy.c index 3c9910c96..8dca0cf98 100644 --- a/winpr/libwinpr/comm/comm_ioctl_dummy.c +++ b/winpr/libwinpr/comm/comm_ioctl_dummy.c @@ -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); diff --git a/winpr/libwinpr/comm/comm_serial_sys.c b/winpr/libwinpr/comm/comm_serial_sys.c index e286a27da..038642945 100644 --- a/winpr/libwinpr/comm/comm_serial_sys.c +++ b/winpr/libwinpr/comm/comm_serial_sys.c @@ -152,7 +152,53 @@ 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(ULONG flag) +{ + if (flag & TIOCM_DTR) + return "DTR (data terminal ready)"; + if (flag & TIOCM_RTS) + return "RTS (request to send)"; + if (flag & TIOCM_ST) + return "Secondary TXD (transmit)"; + if (flag & TIOCM_SR) + return "Secondary RXD (receive)"; + if (flag & TIOCM_CTS) + return "CTS (clear to send)"; + if (flag & TIOCM_CAR) + return "DCD (data carrier detect)"; + if (flag & TIOCM_CD) + return "CD (carrier detect)"; + if (flag & TIOCM_RNG) + return "RNG (ring)"; + if (flag & TIOCM_RI) + return "RI (ring)"; + if (flag & TIOCM_DSR) + return "DSR"; + return "UNKNOWN"; +} + +static const char* get_modem_status_str(ULONG status, char* buffer, size_t size) +{ + const ULONG flags[] = { 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 ULONG flag = flags[x]; + if (status & flag) + { + winpr_str_append(get_modem_flag_str(flag), buffer, size, sep); + sep = "|"; + } + } + + char number[32] = { 0 }; + _snprintf(number, sizeof(number), "}[0x%08" PRIx32 "]", status); + winpr_str_append(number, buffer, size, ""); + return buffer; +} static BOOL get_properties(WINPR_ATTR_UNUSED WINPR_COMM* pComm, COMMPROP* pProperties) { @@ -248,9 +294,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 +415,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 +563,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 +793,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 +865,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 +927,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 +1010,30 @@ 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; - } + return CommIoCtl(pComm, TIOCMGET, pRegister); +} +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 +1091,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 +1242,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 +1261,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, ¤tCounters) < 0) - { - if (!commstatus_error(pComm, "TIOCGICOUNT")) - goto fail; - ZeroMemory(¤tCounters, 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 +1307,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 +1316,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 +1354,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 +1388,6 @@ static BOOL get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus) { pComm->PendingEvents |= SERIAL_EV_RING; } - pComm->counters = currentCounters; #endif @@ -1494,7 +1480,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 +1512,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 +1556,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 +1611,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 = {