23 #include <winpr/assert.h>
26 #include <sys/ioctl.h>
30 #include "comm_serial_sys.h"
33 #include <winpr/crt.h>
34 #include <winpr/wlog.h>
39 #define CMSPAR 010000000000
43 #define TTY_THRESHOLD_THROTTLE 128
44 #define TTY_THRESHOLD_UNTHROTTLE 128
45 #define N_TTY_BUF_SIZE 4096
47 #define BAUD_TABLE_END 0010020
53 static const speed_t BAUD_TABLE[][3] = {
61 { B75, 75, BAUD_075 },
64 { B110, CBR_110, BAUD_110 },
70 { B150, 150, BAUD_150 },
76 { B300, CBR_300, BAUD_300 },
79 { B600, CBR_600, BAUD_600 },
82 { B1200, CBR_1200, BAUD_1200 },
85 { B1800, 1800, BAUD_1800 },
88 { B2400, CBR_2400, BAUD_2400 },
91 { B4800, CBR_4800, BAUD_4800 },
95 { B9600, CBR_9600, BAUD_9600 },
99 { B19200, CBR_19200, BAUD_19200 },
102 { B38400, CBR_38400, BAUD_38400 },
106 { B57600, CBR_57600, BAUD_57600 },
109 { B115200, CBR_115200, BAUD_115200 },
114 { B230400, 230400, BAUD_USER },
117 { B460800, 460800, BAUD_USER },
120 { B500000, 500000, BAUD_USER },
123 { B576000, 576000, BAUD_USER },
126 { B921600, 921600, BAUD_USER },
129 { B1000000, 1000000, BAUD_USER },
132 { B1152000, 1152000, BAUD_USER },
135 { B1500000, 1500000, BAUD_USER },
138 { B2000000, 2000000, BAUD_USER },
141 { B2500000, 2500000, BAUD_USER },
144 { B3000000, 3000000, BAUD_USER },
147 { B3500000, 3500000, BAUD_USER },
150 { B4000000, 4000000, BAUD_USER },
152 { BAUD_TABLE_END, 0, 0 }
155 static BOOL commstatus_error(WINPR_COMM* pComm,
const char* ctrl);
157 static BOOL get_properties(WINPR_COMM* pComm,
COMMPROP* pProperties)
167 WINPR_ASSERT(pProperties);
168 if (pProperties->dwProvSpec1 != COMMPROP_INITIALIZED)
170 ZeroMemory(pProperties,
sizeof(
COMMPROP));
171 pProperties->wPacketLength =
sizeof(
COMMPROP);
174 pProperties->wPacketVersion = 2;
176 pProperties->dwServiceMask = SERIAL_SP_SERIALCOMM;
181 pProperties->dwMaxTxQueue = N_TTY_BUF_SIZE;
182 pProperties->dwMaxRxQueue = N_TTY_BUF_SIZE;
185 pProperties->dwMaxBaud = BAUD_USER;
188 pProperties->dwProvSubType = PST_UNSPECIFIED;
191 pProperties->dwProvCapabilities =
192 PCF_DTRDSR | PCF_INTTIMEOUTS | PCF_PARITY_CHECK |
193 PCF_RTSCTS | PCF_SETXCHAR | PCF_TOTALTIMEOUTS | PCF_XONXOFF;
196 pProperties->dwSettableParams = SP_BAUD | SP_DATABITS | SP_HANDSHAKING | SP_PARITY |
197 SP_PARITY_CHECK | SP_STOPBITS;
199 pProperties->dwSettableBaud = 0;
200 for (
int i = 0; BAUD_TABLE[i][0] < BAUD_TABLE_END; i++)
202 pProperties->dwSettableBaud |= BAUD_TABLE[i][2];
205 pProperties->wSettableData =
206 DATABITS_5 | DATABITS_6 | DATABITS_7 | DATABITS_8 ;
208 pProperties->wSettableStopParity = STOPBITS_10 | STOPBITS_20 | PARITY_NONE |
209 PARITY_ODD | PARITY_EVEN | PARITY_MARK | PARITY_SPACE;
212 pProperties->dwCurrentTxQueue = N_TTY_BUF_SIZE;
213 pProperties->dwCurrentRxQueue = N_TTY_BUF_SIZE;
222 static BOOL set_baud_rate(WINPR_COMM* pComm,
const SERIAL_BAUD_RATE* pBaudRate)
224 speed_t newSpeed = 0;
225 struct termios futureState = { 0 };
228 WINPR_ASSERT(pBaudRate);
230 if (tcgetattr(pComm->fd, &futureState) <
233 SetLastError(ERROR_IO_DEVICE);
237 for (
int i = 0; BAUD_TABLE[i][0] < BAUD_TABLE_END; i++)
239 if (BAUD_TABLE[i][1] == pBaudRate->BaudRate)
241 newSpeed = BAUD_TABLE[i][0];
242 if (cfsetspeed(&futureState, newSpeed) < 0)
244 CommLog_Print(WLOG_WARN,
"failed to set speed 0x%x (%" PRIu32
")", newSpeed,
245 pBaudRate->BaudRate);
249 WINPR_ASSERT(cfgetispeed(&futureState) == newSpeed);
251 if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &futureState) < 0)
253 CommLog_Print(WLOG_WARN,
"_comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32
"",
262 CommLog_Print(WLOG_WARN,
"could not find a matching speed for the baud rate %" PRIu32
"",
263 pBaudRate->BaudRate);
264 SetLastError(ERROR_INVALID_DATA);
270 speed_t currentSpeed = 0;
271 struct termios currentState = { 0 };
274 WINPR_ASSERT(pBaudRate);
276 if (tcgetattr(pComm->fd, ¤tState) < 0)
278 SetLastError(ERROR_IO_DEVICE);
282 currentSpeed = cfgetispeed(¤tState);
284 for (
int i = 0; BAUD_TABLE[i][0] < BAUD_TABLE_END; i++)
286 if (BAUD_TABLE[i][0] == currentSpeed)
288 pBaudRate->BaudRate = BAUD_TABLE[i][1];
293 CommLog_Print(WLOG_WARN,
"could not find a matching baud rate for the speed 0x%x",
295 SetLastError(ERROR_INVALID_DATA);
308 static BOOL set_serial_chars(WINPR_COMM* pComm,
const SERIAL_CHARS* pSerialChars)
311 struct termios upcomingTermios = { 0 };
314 WINPR_ASSERT(pSerialChars);
316 if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
318 SetLastError(ERROR_IO_DEVICE);
333 if (pSerialChars->EofChar !=
'\0')
335 CommLog_Print(WLOG_WARN,
"EofChar %02" PRIX8
" cannot be set\n", pSerialChars->EofChar);
336 SetLastError(ERROR_NOT_SUPPORTED);
346 if (pSerialChars->ErrorChar !=
'\0')
348 CommLog_Print(WLOG_WARN,
"ErrorChar 0x%02" PRIX8
" ('%c') cannot be set (unsupported).\n",
349 pSerialChars->ErrorChar, (
char)pSerialChars->ErrorChar);
350 SetLastError(ERROR_NOT_SUPPORTED);
355 if (pSerialChars->BreakChar !=
'\0')
357 CommLog_Print(WLOG_WARN,
"BreakChar 0x%02" PRIX8
" ('%c') cannot be set (unsupported).\n",
358 pSerialChars->BreakChar, (
char)pSerialChars->BreakChar);
359 SetLastError(ERROR_NOT_SUPPORTED);
363 if (pSerialChars->EventChar !=
'\0')
365 pComm->eventChar = pSerialChars->EventChar;
368 upcomingTermios.c_cc[VSTART] = pSerialChars->XonChar;
370 upcomingTermios.c_cc[VSTOP] = pSerialChars->XoffChar;
372 if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
374 CommLog_Print(WLOG_WARN,
"_comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32
"",
382 static BOOL get_serial_chars(WINPR_COMM* pComm,
SERIAL_CHARS* pSerialChars)
384 struct termios currentTermios = { 0 };
387 WINPR_ASSERT(pSerialChars);
389 if (tcgetattr(pComm->fd, ¤tTermios) < 0)
391 SetLastError(ERROR_IO_DEVICE);
406 pSerialChars->XonChar = currentTermios.c_cc[VSTART];
408 pSerialChars->XoffChar = currentTermios.c_cc[VSTOP];
416 struct termios upcomingTermios = { 0 };
419 WINPR_ASSERT(pLineControl);
430 if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
432 SetLastError(ERROR_IO_DEVICE);
438 switch (pLineControl->StopBits)
441 upcomingTermios.c_cflag &= ~CSTOPB;
445 CommLog_Print(WLOG_WARN,
"Unsupported one and a half stop bits.");
449 upcomingTermios.c_cflag |= CSTOPB;
453 CommLog_Print(WLOG_WARN,
"unexpected number of stop bits: %" PRIu8
"\n",
454 pLineControl->StopBits);
459 switch (pLineControl->Parity)
462 upcomingTermios.c_cflag &= ~(PARENB | PARODD | CMSPAR);
466 upcomingTermios.c_cflag &= ~CMSPAR;
467 upcomingTermios.c_cflag |= PARENB | PARODD;
471 upcomingTermios.c_cflag &= ~(PARODD | CMSPAR);
472 upcomingTermios.c_cflag |= PARENB;
476 upcomingTermios.c_cflag |= PARENB | PARODD | CMSPAR;
480 upcomingTermios.c_cflag &= ~PARODD;
481 upcomingTermios.c_cflag |= PARENB | CMSPAR;
485 CommLog_Print(WLOG_WARN,
"unexpected type of parity: %" PRIu8
"\n",
486 pLineControl->Parity);
491 switch (pLineControl->WordLength)
494 upcomingTermios.c_cflag &= ~CSIZE;
495 upcomingTermios.c_cflag |= CS5;
499 upcomingTermios.c_cflag &= ~CSIZE;
500 upcomingTermios.c_cflag |= CS6;
504 upcomingTermios.c_cflag &= ~CSIZE;
505 upcomingTermios.c_cflag |= CS7;
509 upcomingTermios.c_cflag &= ~CSIZE;
510 upcomingTermios.c_cflag |= CS8;
514 CommLog_Print(WLOG_WARN,
"unexpected number od data bits per character: %" PRIu8
"\n",
515 pLineControl->WordLength);
520 if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
522 CommLog_Print(WLOG_WARN,
"_comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32
"",
532 struct termios currentTermios = { 0 };
535 WINPR_ASSERT(pLineControl);
537 if (tcgetattr(pComm->fd, ¤tTermios) < 0)
539 SetLastError(ERROR_IO_DEVICE);
543 pLineControl->StopBits = (currentTermios.c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BIT_1;
545 if (!(currentTermios.c_cflag & PARENB))
547 pLineControl->Parity = NO_PARITY;
549 else if (currentTermios.c_cflag & CMSPAR)
551 pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? MARK_PARITY : SPACE_PARITY;
556 pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY;
559 switch (currentTermios.c_cflag & CSIZE)
562 pLineControl->WordLength = 5;
565 pLineControl->WordLength = 6;
568 pLineControl->WordLength = 7;
571 pLineControl->WordLength = 8;
578 static BOOL set_handflow(WINPR_COMM* pComm,
const SERIAL_HANDFLOW* pHandflow)
581 struct termios upcomingTermios = { 0 };
584 WINPR_ASSERT(pHandflow);
586 if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
588 SetLastError(ERROR_IO_DEVICE);
595 if ((!(pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) &&
596 (pHandflow->FlowReplace & SERIAL_RTS_CONTROL)) ||
597 ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) &&
598 !(pHandflow->FlowReplace & SERIAL_RTS_CONTROL)))
600 CommLog_Print(WLOG_WARN,
601 "SERIAL_DTR_CONTROL:%s and SERIAL_RTS_CONTROL:%s cannot be different, HUPCL "
602 "will be set since it is claimed for one of the both lines.",
603 (pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ?
"ON" :
"OFF",
604 (pHandflow->FlowReplace & SERIAL_RTS_CONTROL) ?
"ON" :
"OFF");
607 if ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ||
608 (pHandflow->FlowReplace & SERIAL_RTS_CONTROL))
610 upcomingTermios.c_cflag |= HUPCL;
614 upcomingTermios.c_cflag &= ~HUPCL;
625 if ((!(pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
626 (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)) ||
627 ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
628 !(pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)))
630 CommLog_Print(WLOG_WARN,
631 "SERIAL_CTS_HANDSHAKE:%s and SERIAL_RTS_HANDSHAKE:%s cannot be different, "
632 "CRTSCTS will be set since it is claimed for one of the both lines.",
633 (pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ?
"ON" :
"OFF",
634 (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE) ?
"ON" :
"OFF");
637 if ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ||
638 (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE))
640 upcomingTermios.c_cflag |= CRTSCTS;
644 upcomingTermios.c_cflag &= ~CRTSCTS;
649 if (pHandflow->ControlHandShake & SERIAL_DTR_HANDSHAKE)
652 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_DTR_HANDSHAKE feature.");
653 SetLastError(ERROR_NOT_SUPPORTED);
657 if (pHandflow->ControlHandShake & SERIAL_DSR_HANDSHAKE)
660 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_DSR_HANDSHAKE feature.");
661 SetLastError(ERROR_NOT_SUPPORTED);
665 if (pHandflow->ControlHandShake & SERIAL_DCD_HANDSHAKE)
668 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_DCD_HANDSHAKE feature.");
669 SetLastError(ERROR_NOT_SUPPORTED);
674 if (pHandflow->ControlHandShake & SERIAL_DSR_SENSITIVITY)
677 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_DSR_SENSITIVITY feature.");
678 SetLastError(ERROR_NOT_SUPPORTED);
683 if (pHandflow->ControlHandShake & SERIAL_ERROR_ABORT)
686 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_ERROR_ABORT feature.");
687 SetLastError(ERROR_NOT_SUPPORTED);
693 if (pHandflow->FlowReplace & SERIAL_AUTO_TRANSMIT)
695 upcomingTermios.c_iflag |= IXON;
699 upcomingTermios.c_iflag &= ~IXON;
702 if (pHandflow->FlowReplace & SERIAL_AUTO_RECEIVE)
704 upcomingTermios.c_iflag |= IXOFF;
708 upcomingTermios.c_iflag &= ~IXOFF;
712 if (pHandflow->FlowReplace & SERIAL_ERROR_CHAR)
715 upcomingTermios.c_iflag &= ~IGNPAR;
719 upcomingTermios.c_iflag |= IGNPAR;
722 if (pHandflow->FlowReplace & SERIAL_NULL_STRIPPING)
724 upcomingTermios.c_iflag |= IGNBRK;
728 upcomingTermios.c_iflag &= ~IGNBRK;
732 if (pHandflow->FlowReplace & SERIAL_BREAK_CHAR)
734 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_BREAK_CHAR feature.");
735 SetLastError(ERROR_NOT_SUPPORTED);
740 if (pHandflow->FlowReplace & SERIAL_XOFF_CONTINUE)
743 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_XOFF_CONTINUE feature.");
744 SetLastError(ERROR_NOT_SUPPORTED);
751 if (pHandflow->XonLimit != TTY_THRESHOLD_UNTHROTTLE)
753 CommLog_Print(WLOG_WARN,
"Attempt to set XonLimit with an unsupported value: %" PRId32
"",
754 pHandflow->XonLimit);
755 SetLastError(ERROR_NOT_SUPPORTED);
762 if (pHandflow->XoffLimit != TTY_THRESHOLD_THROTTLE)
764 CommLog_Print(WLOG_WARN,
"Attempt to set XoffLimit with an unsupported value: %" PRId32
"",
765 pHandflow->XoffLimit);
766 SetLastError(ERROR_NOT_SUPPORTED);
770 if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
772 CommLog_Print(WLOG_WARN,
"_comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32
"",
780 static BOOL get_handflow(WINPR_COMM* pComm,
SERIAL_HANDFLOW* pHandflow)
782 struct termios currentTermios = { 0 };
785 WINPR_ASSERT(pHandflow);
787 if (tcgetattr(pComm->fd, ¤tTermios) < 0)
789 SetLastError(ERROR_IO_DEVICE);
795 pHandflow->ControlHandShake = 0;
797 if (currentTermios.c_cflag & HUPCL)
798 pHandflow->ControlHandShake |= SERIAL_DTR_CONTROL;
802 if (currentTermios.c_cflag & CRTSCTS)
803 pHandflow->ControlHandShake |= SERIAL_CTS_HANDSHAKE;
815 pHandflow->FlowReplace = 0;
817 if (currentTermios.c_iflag & IXON)
818 pHandflow->FlowReplace |= SERIAL_AUTO_TRANSMIT;
820 if (currentTermios.c_iflag & IXOFF)
821 pHandflow->FlowReplace |= SERIAL_AUTO_RECEIVE;
823 if (!(currentTermios.c_iflag & IGNPAR))
824 pHandflow->FlowReplace |= SERIAL_ERROR_CHAR;
826 if (currentTermios.c_iflag & IGNBRK)
827 pHandflow->FlowReplace |= SERIAL_NULL_STRIPPING;
831 if (currentTermios.c_cflag & HUPCL)
832 pHandflow->FlowReplace |= SERIAL_RTS_CONTROL;
834 if (currentTermios.c_cflag & CRTSCTS)
835 pHandflow->FlowReplace |= SERIAL_RTS_HANDSHAKE;
841 pHandflow->XonLimit = TTY_THRESHOLD_UNTHROTTLE;
845 pHandflow->XoffLimit = TTY_THRESHOLD_THROTTLE;
850 static BOOL set_timeouts(WINPR_COMM* pComm,
const SERIAL_TIMEOUTS* pTimeouts)
853 WINPR_ASSERT(pTimeouts);
858 if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
859 (pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
863 "ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
864 SetLastError(ERROR_INVALID_PARAMETER);
868 pComm->timeouts.ReadIntervalTimeout = pTimeouts->ReadIntervalTimeout;
869 pComm->timeouts.ReadTotalTimeoutMultiplier = pTimeouts->ReadTotalTimeoutMultiplier;
870 pComm->timeouts.ReadTotalTimeoutConstant = pTimeouts->ReadTotalTimeoutConstant;
871 pComm->timeouts.WriteTotalTimeoutMultiplier = pTimeouts->WriteTotalTimeoutMultiplier;
872 pComm->timeouts.WriteTotalTimeoutConstant = pTimeouts->WriteTotalTimeoutConstant;
874 CommLog_Print(WLOG_DEBUG,
"ReadIntervalTimeout %" PRIu32
"",
875 pComm->timeouts.ReadIntervalTimeout);
876 CommLog_Print(WLOG_DEBUG,
"ReadTotalTimeoutMultiplier %" PRIu32
"",
877 pComm->timeouts.ReadTotalTimeoutMultiplier);
878 CommLog_Print(WLOG_DEBUG,
"ReadTotalTimeoutConstant %" PRIu32
"",
879 pComm->timeouts.ReadTotalTimeoutConstant);
880 CommLog_Print(WLOG_DEBUG,
"WriteTotalTimeoutMultiplier %" PRIu32
"",
881 pComm->timeouts.WriteTotalTimeoutMultiplier);
882 CommLog_Print(WLOG_DEBUG,
"WriteTotalTimeoutConstant %" PRIu32
"",
883 pComm->timeouts.WriteTotalTimeoutConstant);
888 static BOOL get_timeouts(WINPR_COMM* pComm,
SERIAL_TIMEOUTS* pTimeouts)
891 WINPR_ASSERT(pTimeouts);
893 pTimeouts->ReadIntervalTimeout = pComm->timeouts.ReadIntervalTimeout;
894 pTimeouts->ReadTotalTimeoutMultiplier = pComm->timeouts.ReadTotalTimeoutMultiplier;
895 pTimeouts->ReadTotalTimeoutConstant = pComm->timeouts.ReadTotalTimeoutConstant;
896 pTimeouts->WriteTotalTimeoutMultiplier = pComm->timeouts.WriteTotalTimeoutMultiplier;
897 pTimeouts->WriteTotalTimeoutConstant = pComm->timeouts.WriteTotalTimeoutConstant;
902 static BOOL set_lines(WINPR_COMM* pComm, UINT32 lines)
906 if (ioctl(pComm->fd, TIOCMBIS, &lines) < 0)
908 char ebuffer[256] = { 0 };
909 CommLog_Print(WLOG_WARN,
"TIOCMBIS ioctl failed, lines=0x%" PRIX32
", errno=[%d] %s", lines,
910 errno, winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
911 SetLastError(ERROR_IO_DEVICE);
918 static BOOL clear_lines(WINPR_COMM* pComm, UINT32 lines)
922 if (ioctl(pComm->fd, TIOCMBIC, &lines) < 0)
924 char ebuffer[256] = { 0 };
925 CommLog_Print(WLOG_WARN,
"TIOCMBIC ioctl failed, lines=0x%" PRIX32
", errno=[%d] %s", lines,
926 errno, winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
927 SetLastError(ERROR_IO_DEVICE);
934 static BOOL set_dtr(WINPR_COMM* pComm)
939 if (!get_handflow(pComm, &handflow))
943 WINPR_ASSERT((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0);
945 if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
947 SetLastError(ERROR_INVALID_PARAMETER);
951 return set_lines(pComm, TIOCM_DTR);
954 static BOOL clear_dtr(WINPR_COMM* pComm)
959 if (!get_handflow(pComm, &handflow))
963 WINPR_ASSERT((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0);
965 if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
967 SetLastError(ERROR_INVALID_PARAMETER);
971 return clear_lines(pComm, TIOCM_DTR);
974 static BOOL set_rts(WINPR_COMM* pComm)
979 if (!get_handflow(pComm, &handflow))
982 if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
984 SetLastError(ERROR_INVALID_PARAMETER);
988 return set_lines(pComm, TIOCM_RTS);
991 static BOOL clear_rts(WINPR_COMM* pComm)
995 if (!get_handflow(pComm, &handflow))
998 if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
1000 SetLastError(ERROR_INVALID_PARAMETER);
1004 return clear_lines(pComm, TIOCM_RTS);
1007 static BOOL get_modemstatus(WINPR_COMM* pComm, ULONG* pRegister)
1011 WINPR_ASSERT(pComm);
1012 WINPR_ASSERT(pRegister);
1015 if (ioctl(pComm->fd, TIOCMGET, &lines) < 0)
1017 if (!commstatus_error(pComm,
"TIOCMGET"))
1021 if (lines & TIOCM_CTS)
1022 *pRegister |= SERIAL_MSR_CTS;
1023 if (lines & TIOCM_DSR)
1024 *pRegister |= SERIAL_MSR_DSR;
1025 if (lines & TIOCM_RI)
1026 *pRegister |= SERIAL_MSR_RI;
1027 if (lines & TIOCM_CD)
1028 *pRegister |= SERIAL_MSR_DCD;
1034 static const ULONG SERIAL_SYS_SUPPORTED_EV_MASK =
1035 SERIAL_EV_RXCHAR | SERIAL_EV_RXFLAG | SERIAL_EV_TXEMPTY | SERIAL_EV_CTS | SERIAL_EV_DSR |
1036 SERIAL_EV_RLSD | SERIAL_EV_BREAK | SERIAL_EV_ERR | SERIAL_EV_RING |
1043 static BOOL is_wait_set(WINPR_COMM* pComm)
1045 WINPR_ASSERT(pComm);
1047 EnterCriticalSection(&pComm->EventsLock);
1048 const BOOL isWaiting = (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING) != 0;
1049 LeaveCriticalSection(&pComm->EventsLock);
1053 static BOOL set_wait_mask(WINPR_COMM* pComm,
const ULONG* pWaitMask)
1055 ULONG possibleMask = 0;
1057 WINPR_ASSERT(pComm);
1058 WINPR_ASSERT(pWaitMask);
1063 if (is_wait_set(pComm))
1067 EnterCriticalSection(&pComm->EventsLock);
1068 pComm->PendingEvents |= SERIAL_EV_WINPR_STOP;
1069 LeaveCriticalSection(&pComm->EventsLock);
1072 while (is_wait_set(pComm))
1075 EnterCriticalSection(&pComm->EventsLock);
1076 pComm->PendingEvents &= ~SERIAL_EV_WINPR_STOP;
1077 LeaveCriticalSection(&pComm->EventsLock);
1081 EnterCriticalSection(&pComm->EventsLock);
1083 if (*pWaitMask == 0)
1086 #if defined(WINPR_HAVE_COMM_COUNTERS)
1087 if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0)
1089 if (!commstatus_error(pComm,
"TIOCGICOUNT"))
1091 LeaveCriticalSection(&pComm->EventsLock);
1094 ZeroMemory(&(pComm->counters),
sizeof(
struct serial_icounter_struct));
1097 pComm->PendingEvents = 0;
1100 possibleMask = *pWaitMask & SERIAL_SYS_SUPPORTED_EV_MASK;
1102 if (possibleMask != *pWaitMask)
1104 CommLog_Print(WLOG_WARN,
1105 "Not all wait events supported (Serial.sys), requested events= 0x%08" PRIX32
1106 ", possible events= 0x%08" PRIX32
"",
1107 *pWaitMask, possibleMask);
1110 pComm->WaitEventMask = possibleMask;
1112 LeaveCriticalSection(&pComm->EventsLock);
1116 pComm->WaitEventMask = possibleMask;
1118 LeaveCriticalSection(&pComm->EventsLock);
1122 static BOOL get_wait_mask(WINPR_COMM* pComm, ULONG* pWaitMask)
1124 WINPR_ASSERT(pComm);
1125 WINPR_ASSERT(pWaitMask);
1127 *pWaitMask = pComm->WaitEventMask;
1131 static BOOL set_queue_size(WINPR_COMM* pComm,
const SERIAL_QUEUE_SIZE* pQueueSize)
1133 WINPR_ASSERT(pComm);
1134 WINPR_ASSERT(pQueueSize);
1136 if ((pQueueSize->InSize <= N_TTY_BUF_SIZE) && (pQueueSize->OutSize <= N_TTY_BUF_SIZE))
1141 if (pQueueSize->InSize > N_TTY_BUF_SIZE)
1142 CommLog_Print(WLOG_WARN,
1143 "Requested an incompatible input buffer size: %" PRIu32
1144 ", keeping on with a %" PRIu32
" bytes buffer.",
1145 pQueueSize->InSize, N_TTY_BUF_SIZE);
1147 if (pQueueSize->OutSize > N_TTY_BUF_SIZE)
1148 CommLog_Print(WLOG_WARN,
1149 "Requested an incompatible output buffer size: %" PRIu32
1150 ", keeping on with a %" PRIu32
" bytes buffer.",
1151 pQueueSize->OutSize, N_TTY_BUF_SIZE);
1153 SetLastError(ERROR_CANCELLED);
1157 static BOOL purge(WINPR_COMM* pComm,
const ULONG* pPurgeMask)
1159 WINPR_ASSERT(pComm);
1160 WINPR_ASSERT(pPurgeMask);
1162 if ((*pPurgeMask & ~(SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT | SERIAL_PURGE_TXCLEAR |
1163 SERIAL_PURGE_RXCLEAR)) > 0)
1165 CommLog_Print(WLOG_WARN,
"Invalid purge mask: 0x%" PRIX32
"\n", *pPurgeMask);
1166 SetLastError(ERROR_INVALID_PARAMETER);
1175 if (*pPurgeMask & SERIAL_PURGE_TXABORT)
1178 #if defined(WINPR_HAVE_SYS_EVENTFD_H)
1179 if (eventfd_write(pComm->fd_write_event, WINPR_PURGE_TXABORT) < 0)
1181 if (errno != EAGAIN)
1183 char ebuffer[256] = { 0 };
1184 CommLog_Print(WLOG_WARN,
"eventfd_write failed, errno=[%d] %s", errno,
1185 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1188 WINPR_ASSERT(errno == EAGAIN);
1193 if (*pPurgeMask & SERIAL_PURGE_RXABORT)
1196 #if defined(WINPR_HAVE_SYS_EVENTFD_H)
1197 if (eventfd_write(pComm->fd_read_event, WINPR_PURGE_RXABORT) < 0)
1199 if (errno != EAGAIN)
1201 char ebuffer[256] = { 0 };
1202 CommLog_Print(WLOG_WARN,
"eventfd_write failed, errno=[%d] %s", errno,
1203 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1206 WINPR_ASSERT(errno == EAGAIN);
1211 if (*pPurgeMask & SERIAL_PURGE_TXCLEAR)
1215 if (tcflush(pComm->fd, TCOFLUSH) < 0)
1217 char ebuffer[256] = { 0 };
1218 CommLog_Print(WLOG_WARN,
"tcflush(TCOFLUSH) failure, errno=[%d] %s", errno,
1219 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1220 SetLastError(ERROR_CANCELLED);
1225 if (*pPurgeMask & SERIAL_PURGE_RXCLEAR)
1229 if (tcflush(pComm->fd, TCIFLUSH) < 0)
1231 char ebuffer[256] = { 0 };
1232 CommLog_Print(WLOG_WARN,
"tcflush(TCIFLUSH) failure, errno=[%d] %s", errno,
1233 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1234 SetLastError(ERROR_CANCELLED);
1242 BOOL commstatus_error(WINPR_COMM* pComm,
const char* ctrl)
1244 char ebuffer[256] = { 0 };
1245 CommLog_Print(WLOG_WARN,
"%s ioctl failed, errno=[%d] %s.", ctrl, errno,
1246 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1248 if (!pComm->permissive)
1250 SetLastError(ERROR_IO_DEVICE);
1260 static BOOL get_commstatus(WINPR_COMM* pComm,
SERIAL_STATUS* pCommstatus)
1264 #if defined(WINPR_HAVE_COMM_COUNTERS)
1265 struct serial_icounter_struct currentCounters = { 0 };
1267 WINPR_ASSERT(pComm);
1268 WINPR_ASSERT(pCommstatus);
1271 EnterCriticalSection(&pComm->EventsLock);
1276 if (!get_modemstatus(pComm, &status))
1278 if (!commstatus_error(pComm,
"TIOCGICOUNT"))
1287 #if defined(WINPR_HAVE_COMM_COUNTERS)
1288 if (ioctl(pComm->fd, TIOCGICOUNT, ¤tCounters) < 0)
1290 if (!commstatus_error(pComm,
"TIOCGICOUNT"))
1292 ZeroMemory(¤tCounters,
sizeof(
struct serial_icounter_struct));
1300 if (currentCounters.buf_overrun != pComm->counters.buf_overrun)
1302 pCommstatus->Errors |= SERIAL_ERROR_QUEUEOVERRUN;
1305 if (currentCounters.overrun != pComm->counters.overrun)
1307 pCommstatus->Errors |= SERIAL_ERROR_OVERRUN;
1308 pComm->PendingEvents |= SERIAL_EV_ERR;
1311 if (currentCounters.brk != pComm->counters.brk)
1313 pCommstatus->Errors |= SERIAL_ERROR_BREAK;
1314 pComm->PendingEvents |= SERIAL_EV_BREAK;
1317 if (currentCounters.parity != pComm->counters.parity)
1319 pCommstatus->Errors |= SERIAL_ERROR_PARITY;
1320 pComm->PendingEvents |= SERIAL_EV_ERR;
1323 if (currentCounters.frame != pComm->counters.frame)
1325 pCommstatus->Errors |= SERIAL_ERROR_FRAMING;
1326 pComm->PendingEvents |= SERIAL_EV_ERR;
1346 #if defined(__linux__)
1347 if (ioctl(pComm->fd, TIOCINQ, &(pCommstatus->AmountInInQueue)) < 0)
1349 if (!commstatus_error(pComm,
"TIOCINQ"))
1356 if (ioctl(pComm->fd, TIOCOUTQ, &(pCommstatus->AmountInOutQueue)) < 0)
1358 if (!commstatus_error(pComm,
"TIOCOUTQ"))
1367 #if defined(WINPR_HAVE_COMM_COUNTERS)
1368 if (currentCounters.rx != pComm->counters.rx)
1370 pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
1373 if ((currentCounters.tx != pComm->counters.tx) &&
1374 (pCommstatus->AmountInOutQueue == 0))
1376 pComm->PendingEvents |= SERIAL_EV_TXEMPTY;
1382 pComm->PendingEvents &= ~SERIAL_EV_TXEMPTY;
1385 if (currentCounters.cts != pComm->counters.cts)
1387 pComm->PendingEvents |= SERIAL_EV_CTS;
1390 if (currentCounters.dsr != pComm->counters.dsr)
1392 pComm->PendingEvents |= SERIAL_EV_DSR;
1395 if (currentCounters.dcd != pComm->counters.dcd)
1397 pComm->PendingEvents |= SERIAL_EV_RLSD;
1400 if (currentCounters.rng != pComm->counters.rng)
1402 pComm->PendingEvents |= SERIAL_EV_RING;
1405 pComm->counters = currentCounters;
1408 if (pCommstatus->AmountInInQueue > (0.8 * N_TTY_BUF_SIZE))
1410 pComm->PendingEvents |= SERIAL_EV_RX80FULL;
1416 pComm->PendingEvents &= ~SERIAL_EV_RX80FULL;
1421 LeaveCriticalSection(&pComm->EventsLock);
1425 static BOOL refresh_PendingEvents(WINPR_COMM* pComm)
1429 WINPR_ASSERT(pComm);
1432 if (!get_commstatus(pComm, &serialStatus))
1440 static void consume_event(WINPR_COMM* pComm, ULONG* pOutputMask, ULONG event)
1442 WINPR_ASSERT(pComm);
1443 WINPR_ASSERT(pOutputMask);
1445 if ((pComm->WaitEventMask & event) && (pComm->PendingEvents & event))
1447 pComm->PendingEvents &= ~event;
1448 *pOutputMask |= event;
1452 static BOOL unlock_return(WINPR_COMM* pComm, BOOL res)
1454 EnterCriticalSection(&pComm->EventsLock);
1455 pComm->PendingEvents &= ~SERIAL_EV_WINPR_WAITING;
1456 LeaveCriticalSection(&pComm->EventsLock);
1463 static BOOL wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
1465 WINPR_ASSERT(pComm);
1466 WINPR_ASSERT(*pOutputMask == 0);
1468 EnterCriticalSection(&pComm->EventsLock);
1469 pComm->PendingEvents |= SERIAL_EV_WINPR_WAITING;
1470 LeaveCriticalSection(&pComm->EventsLock);
1475 if (!refresh_PendingEvents(pComm))
1476 return unlock_return(pComm, FALSE);
1479 EnterCriticalSection(&pComm->EventsLock);
1481 if (pComm->PendingEvents & SERIAL_EV_WINPR_STOP)
1488 WINPR_ASSERT(*pOutputMask == 0);
1490 LeaveCriticalSection(&pComm->EventsLock);
1494 consume_event(pComm, pOutputMask, SERIAL_EV_RXCHAR);
1495 consume_event(pComm, pOutputMask, SERIAL_EV_RXFLAG);
1496 consume_event(pComm, pOutputMask, SERIAL_EV_TXEMPTY);
1497 consume_event(pComm, pOutputMask, SERIAL_EV_CTS);
1498 consume_event(pComm, pOutputMask, SERIAL_EV_DSR);
1499 consume_event(pComm, pOutputMask, SERIAL_EV_RLSD);
1500 consume_event(pComm, pOutputMask, SERIAL_EV_BREAK);
1501 consume_event(pComm, pOutputMask, SERIAL_EV_ERR);
1502 consume_event(pComm, pOutputMask, SERIAL_EV_RING);
1503 consume_event(pComm, pOutputMask, SERIAL_EV_RX80FULL);
1505 LeaveCriticalSection(&pComm->EventsLock);
1510 if (*pOutputMask != 0)
1524 return unlock_return(pComm, TRUE);
1527 static BOOL set_break_on(WINPR_COMM* pComm)
1529 WINPR_ASSERT(pComm);
1530 if (ioctl(pComm->fd, TIOCSBRK, NULL) < 0)
1532 char ebuffer[256] = { 0 };
1533 CommLog_Print(WLOG_WARN,
"TIOCSBRK ioctl failed, errno=[%d] %s", errno,
1534 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1535 SetLastError(ERROR_IO_DEVICE);
1542 static BOOL set_break_off(WINPR_COMM* pComm)
1544 WINPR_ASSERT(pComm);
1545 if (ioctl(pComm->fd, TIOCCBRK, NULL) < 0)
1547 char ebuffer[256] = { 0 };
1548 CommLog_Print(WLOG_WARN,
"TIOCSBRK ioctl failed, errno=[%d] %s", errno,
1549 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1550 SetLastError(ERROR_IO_DEVICE);
1557 static BOOL set_xoff(WINPR_COMM* pComm)
1559 WINPR_ASSERT(pComm);
1560 if (tcflow(pComm->fd, TCIOFF) < 0)
1562 char ebuffer[256] = { 0 };
1563 CommLog_Print(WLOG_WARN,
"TCIOFF failure, errno=[%d] %s", errno,
1564 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1565 SetLastError(ERROR_IO_DEVICE);
1572 static BOOL set_xon(WINPR_COMM* pComm)
1574 WINPR_ASSERT(pComm);
1575 if (tcflow(pComm->fd, TCION) < 0)
1577 char ebuffer[256] = { 0 };
1578 CommLog_Print(WLOG_WARN,
"TCION failure, errno=[%d] %s", errno,
1579 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1580 SetLastError(ERROR_IO_DEVICE);
1587 static BOOL get_dtrrts(WINPR_COMM* pComm, ULONG* pMask)
1591 WINPR_ASSERT(pComm);
1592 WINPR_ASSERT(pMask);
1594 if (!get_modemstatus(pComm, &lines))
1599 if (!(lines & TIOCM_DTR))
1600 *pMask |= SERIAL_DTR_STATE;
1601 if (!(lines & TIOCM_RTS))
1602 *pMask |= SERIAL_RTS_STATE;
1607 static BOOL config_size(WINPR_COMM* pComm, ULONG* pSize)
1609 WINPR_ASSERT(pComm);
1610 WINPR_ASSERT(pSize);
1620 static BOOL immediate_char(WINPR_COMM* pComm,
const UCHAR* pChar)
1623 DWORD nbBytesWritten = -1;
1625 WINPR_ASSERT(pComm);
1626 WINPR_ASSERT(pChar);
1634 result = CommWriteFile(pComm, pChar, 1, &nbBytesWritten, NULL);
1636 WINPR_ASSERT(nbBytesWritten == 1);
1641 static BOOL reset_device(WINPR_COMM* pComm)
1648 .id = SerialDriverSerialSys,
1649 .name = _T(
"Serial.sys"),
1650 .set_baud_rate = set_baud_rate,
1651 .get_baud_rate = get_baud_rate,
1652 .get_properties = get_properties,
1653 .set_serial_chars = set_serial_chars,
1654 .get_serial_chars = get_serial_chars,
1655 .set_line_control = set_line_control,
1656 .get_line_control = get_line_control,
1657 .set_handflow = set_handflow,
1658 .get_handflow = get_handflow,
1659 .set_timeouts = set_timeouts,
1660 .get_timeouts = get_timeouts,
1662 .clear_dtr = clear_dtr,
1664 .clear_rts = clear_rts,
1665 .get_modemstatus = get_modemstatus,
1666 .set_wait_mask = set_wait_mask,
1667 .get_wait_mask = get_wait_mask,
1668 .wait_on_mask = wait_on_mask,
1669 .set_queue_size = set_queue_size,
1671 .get_commstatus = get_commstatus,
1672 .set_break_on = set_break_on,
1673 .set_break_off = set_break_off,
1674 .set_xoff = set_xoff,
1676 .get_dtrrts = get_dtrrts,
1677 .config_size = config_size,
1678 .immediate_char = immediate_char,
1679 .reset_device = reset_device,