23#include <winpr/assert.h>
30#include "comm_serial_sys.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
53static 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 }
155static const char* get_modem_flag_str(
int flag)
159 if (flag & TIOCM_DTR)
161 if (flag & TIOCM_RTS)
164 return "Secondary TXD";
166 return "Secondary RXD";
167 if (flag & TIOCM_CTS)
169 if (flag & TIOCM_CAR)
173 if (flag & TIOCM_RNG)
177 if (flag & TIOCM_DSR)
182static const char* get_modem_status_str(
int status,
char* buffer,
size_t size)
184 const int flags[] = { TIOCM_LE, TIOCM_DTR, TIOCM_RTS, TIOCM_ST, TIOCM_SR, TIOCM_CTS,
185 TIOCM_CAR, TIOCM_CD, TIOCM_RNG, TIOCM_RI, TIOCM_DSR };
186 winpr_str_append(
"{", buffer, size,
"");
188 const char* sep =
"";
189 for (
size_t x = 0; x < ARRAYSIZE(flags); x++)
191 const int flag = flags[x];
194 winpr_str_append(get_modem_flag_str(flag), buffer, size, sep);
199 char number[32] = { 0 };
200 (void)_snprintf(number,
sizeof(number),
"}[0x%08x]", (unsigned)status);
201 winpr_str_append(number, buffer, size,
"");
205static BOOL get_properties(WINPR_ATTR_UNUSED WINPR_COMM* pComm,
COMMPROP* pProperties)
215 WINPR_ASSERT(pProperties);
216 if (pProperties->dwProvSpec1 != COMMPROP_INITIALIZED)
218 ZeroMemory(pProperties,
sizeof(
COMMPROP));
219 pProperties->wPacketLength =
sizeof(
COMMPROP);
222 pProperties->wPacketVersion = 2;
224 pProperties->dwServiceMask = SERIAL_SP_SERIALCOMM;
229 pProperties->dwMaxTxQueue = N_TTY_BUF_SIZE;
230 pProperties->dwMaxRxQueue = N_TTY_BUF_SIZE;
233 pProperties->dwMaxBaud = BAUD_USER;
236 pProperties->dwProvSubType = PST_UNSPECIFIED;
239 pProperties->dwProvCapabilities =
240 PCF_DTRDSR | PCF_INTTIMEOUTS | PCF_PARITY_CHECK |
241 PCF_RTSCTS | PCF_SETXCHAR | PCF_TOTALTIMEOUTS | PCF_XONXOFF;
244 pProperties->dwSettableParams = SP_BAUD | SP_DATABITS | SP_HANDSHAKING | SP_PARITY |
245 SP_PARITY_CHECK | SP_STOPBITS;
247 pProperties->dwSettableBaud = 0;
248 for (
int i = 0; BAUD_TABLE[i][0] < BAUD_TABLE_END; i++)
250 pProperties->dwSettableBaud |= BAUD_TABLE[i][2];
253 pProperties->wSettableData =
254 DATABITS_5 | DATABITS_6 | DATABITS_7 | DATABITS_8 ;
256 pProperties->wSettableStopParity = STOPBITS_10 | STOPBITS_20 | PARITY_NONE |
257 PARITY_ODD | PARITY_EVEN | PARITY_MARK | PARITY_SPACE;
260 pProperties->dwCurrentTxQueue = N_TTY_BUF_SIZE;
261 pProperties->dwCurrentRxQueue = N_TTY_BUF_SIZE;
270static BOOL set_baud_rate(WINPR_COMM* pComm,
const SERIAL_BAUD_RATE* pBaudRate)
272 speed_t newSpeed = 0;
273 struct termios futureState = { 0 };
276 WINPR_ASSERT(pBaudRate);
278 if (tcgetattr(pComm->fd, &futureState) <
281 SetLastError(ERROR_IO_DEVICE);
285 for (
int i = 0; BAUD_TABLE[i][0] < BAUD_TABLE_END; i++)
287 if (BAUD_TABLE[i][1] == pBaudRate->BaudRate)
289 newSpeed = BAUD_TABLE[i][0];
290 if (cfsetspeed(&futureState, newSpeed) < 0)
292 CommLog_Print(WLOG_WARN,
"failed to set speed 0x%x (%" PRIu32
")", newSpeed,
293 pBaudRate->BaudRate);
297 WINPR_ASSERT(cfgetispeed(&futureState) == newSpeed);
299 if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &futureState) < 0)
301 CommLog_Print(WLOG_WARN,
"comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32
"",
310 CommLog_Print(WLOG_WARN,
"could not find a matching speed for the baud rate %" PRIu32
"",
311 pBaudRate->BaudRate);
312 SetLastError(ERROR_INVALID_DATA);
318 speed_t currentSpeed = 0;
319 struct termios currentState = { 0 };
322 WINPR_ASSERT(pBaudRate);
324 if (tcgetattr(pComm->fd, ¤tState) < 0)
326 SetLastError(ERROR_IO_DEVICE);
330 currentSpeed = cfgetispeed(¤tState);
332 for (
int i = 0; BAUD_TABLE[i][0] < BAUD_TABLE_END; i++)
334 if (BAUD_TABLE[i][0] == currentSpeed)
336 pBaudRate->BaudRate = BAUD_TABLE[i][1];
341 CommLog_Print(WLOG_WARN,
"could not find a matching baud rate for the speed 0x%x",
343 SetLastError(ERROR_INVALID_DATA);
356static BOOL set_serial_chars(WINPR_COMM* pComm,
const SERIAL_CHARS* pSerialChars)
359 struct termios upcomingTermios = { 0 };
362 WINPR_ASSERT(pSerialChars);
364 if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
366 SetLastError(ERROR_IO_DEVICE);
381 if (pSerialChars->EofChar !=
'\0')
383 CommLog_Print(WLOG_WARN,
"EofChar %02" PRIX8
" cannot be set\n", pSerialChars->EofChar);
384 SetLastError(ERROR_NOT_SUPPORTED);
394 if (pSerialChars->ErrorChar !=
'\0')
396 CommLog_Print(WLOG_WARN,
"ErrorChar 0x%02" PRIX8
" ('%c') cannot be set (unsupported).\n",
397 pSerialChars->ErrorChar, (
char)pSerialChars->ErrorChar);
398 SetLastError(ERROR_NOT_SUPPORTED);
403 if (pSerialChars->BreakChar !=
'\0')
405 CommLog_Print(WLOG_WARN,
"BreakChar 0x%02" PRIX8
" ('%c') cannot be set (unsupported).\n",
406 pSerialChars->BreakChar, (
char)pSerialChars->BreakChar);
407 SetLastError(ERROR_NOT_SUPPORTED);
411 if (pSerialChars->EventChar !=
'\0')
413 pComm->eventChar = pSerialChars->EventChar;
416 upcomingTermios.c_cc[VSTART] = pSerialChars->XonChar;
418 upcomingTermios.c_cc[VSTOP] = pSerialChars->XoffChar;
420 if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
422 CommLog_Print(WLOG_WARN,
"comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32
"",
430static BOOL get_serial_chars(WINPR_COMM* pComm,
SERIAL_CHARS* pSerialChars)
432 struct termios currentTermios = { 0 };
435 WINPR_ASSERT(pSerialChars);
437 if (tcgetattr(pComm->fd, ¤tTermios) < 0)
439 SetLastError(ERROR_IO_DEVICE);
454 pSerialChars->XonChar = currentTermios.c_cc[VSTART];
456 pSerialChars->XoffChar = currentTermios.c_cc[VSTOP];
464 struct termios upcomingTermios = { 0 };
467 WINPR_ASSERT(pLineControl);
478 if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
480 SetLastError(ERROR_IO_DEVICE);
486 switch (pLineControl->StopBits)
489 upcomingTermios.c_cflag &= (uint32_t)~CSTOPB;
493 CommLog_Print(WLOG_WARN,
"Unsupported one and a half stop bits.");
497 upcomingTermios.c_cflag |= CSTOPB;
501 CommLog_Print(WLOG_WARN,
"unexpected number of stop bits: %" PRIu8
"\n",
502 pLineControl->StopBits);
507 switch (pLineControl->Parity)
510 upcomingTermios.c_cflag &= (uint32_t)~(PARENB | PARODD | CMSPAR);
514 upcomingTermios.c_cflag &= (uint32_t)~CMSPAR;
515 upcomingTermios.c_cflag |= PARENB | PARODD;
519 upcomingTermios.c_cflag &= (uint32_t)~(PARODD | CMSPAR);
520 upcomingTermios.c_cflag |= PARENB;
524 upcomingTermios.c_cflag |= PARENB | PARODD | CMSPAR;
528 upcomingTermios.c_cflag &= (uint32_t)~PARODD;
529 upcomingTermios.c_cflag |= PARENB | CMSPAR;
533 CommLog_Print(WLOG_WARN,
"unexpected type of parity: %" PRIu8
"\n",
534 pLineControl->Parity);
539 switch (pLineControl->WordLength)
542 upcomingTermios.c_cflag &= (uint32_t)~CSIZE;
543 upcomingTermios.c_cflag |= CS5;
547 upcomingTermios.c_cflag &= (uint32_t)~CSIZE;
548 upcomingTermios.c_cflag |= CS6;
552 upcomingTermios.c_cflag &= (uint32_t)~CSIZE;
553 upcomingTermios.c_cflag |= CS7;
557 upcomingTermios.c_cflag &= (uint32_t)~CSIZE;
558 upcomingTermios.c_cflag |= CS8;
562 CommLog_Print(WLOG_WARN,
"unexpected number od data bits per character: %" PRIu8
"\n",
563 pLineControl->WordLength);
568 if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
570 CommLog_Print(WLOG_WARN,
"comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32
"",
580 struct termios currentTermios = { 0 };
583 WINPR_ASSERT(pLineControl);
585 if (tcgetattr(pComm->fd, ¤tTermios) < 0)
587 SetLastError(ERROR_IO_DEVICE);
591 pLineControl->StopBits = (currentTermios.c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BIT_1;
593 if (!(currentTermios.c_cflag & PARENB))
595 pLineControl->Parity = NO_PARITY;
597 else if (currentTermios.c_cflag & CMSPAR)
599 pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? MARK_PARITY : SPACE_PARITY;
604 pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY;
607 switch (currentTermios.c_cflag & CSIZE)
610 pLineControl->WordLength = 5;
613 pLineControl->WordLength = 6;
616 pLineControl->WordLength = 7;
619 pLineControl->WordLength = 8;
626static BOOL set_handflow(WINPR_COMM* pComm,
const SERIAL_HANDFLOW* pHandflow)
629 struct termios upcomingTermios = { 0 };
632 WINPR_ASSERT(pHandflow);
634 if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
636 SetLastError(ERROR_IO_DEVICE);
643 if ((!(pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) &&
644 (pHandflow->FlowReplace & SERIAL_RTS_CONTROL)) ||
645 ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) &&
646 !(pHandflow->FlowReplace & SERIAL_RTS_CONTROL)))
648 CommLog_Print(WLOG_WARN,
649 "SERIAL_DTR_CONTROL:%s and SERIAL_RTS_CONTROL:%s cannot be different, HUPCL "
650 "will be set since it is claimed for one of the both lines.",
651 (pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ?
"ON" :
"OFF",
652 (pHandflow->FlowReplace & SERIAL_RTS_CONTROL) ?
"ON" :
"OFF");
655 if ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ||
656 (pHandflow->FlowReplace & SERIAL_RTS_CONTROL))
658 upcomingTermios.c_cflag |= HUPCL;
662 upcomingTermios.c_cflag &= (uint32_t)~HUPCL;
673 if ((!(pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
674 (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)) ||
675 ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
676 !(pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)))
678 CommLog_Print(WLOG_WARN,
679 "SERIAL_CTS_HANDSHAKE:%s and SERIAL_RTS_HANDSHAKE:%s cannot be different, "
680 "CRTSCTS will be set since it is claimed for one of the both lines.",
681 (pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ?
"ON" :
"OFF",
682 (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE) ?
"ON" :
"OFF");
685 if ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ||
686 (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE))
688 upcomingTermios.c_cflag |= CRTSCTS;
692 upcomingTermios.c_cflag &= ~CRTSCTS;
697 if (pHandflow->ControlHandShake & SERIAL_DTR_HANDSHAKE)
700 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_DTR_HANDSHAKE feature.");
701 SetLastError(ERROR_NOT_SUPPORTED);
705 if (pHandflow->ControlHandShake & SERIAL_DSR_HANDSHAKE)
708 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_DSR_HANDSHAKE feature.");
709 SetLastError(ERROR_NOT_SUPPORTED);
713 if (pHandflow->ControlHandShake & SERIAL_DCD_HANDSHAKE)
716 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_DCD_HANDSHAKE feature.");
717 SetLastError(ERROR_NOT_SUPPORTED);
722 if (pHandflow->ControlHandShake & SERIAL_DSR_SENSITIVITY)
725 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_DSR_SENSITIVITY feature.");
726 SetLastError(ERROR_NOT_SUPPORTED);
731 if (pHandflow->ControlHandShake & SERIAL_ERROR_ABORT)
734 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_ERROR_ABORT feature.");
735 SetLastError(ERROR_NOT_SUPPORTED);
741 if (pHandflow->FlowReplace & SERIAL_AUTO_TRANSMIT)
743 upcomingTermios.c_iflag |= IXON;
747 upcomingTermios.c_iflag &= (uint32_t)~IXON;
750 if (pHandflow->FlowReplace & SERIAL_AUTO_RECEIVE)
752 upcomingTermios.c_iflag |= IXOFF;
756 upcomingTermios.c_iflag &= (uint32_t)~IXOFF;
760 if (pHandflow->FlowReplace & SERIAL_ERROR_CHAR)
763 upcomingTermios.c_iflag &= (uint32_t)~IGNPAR;
767 upcomingTermios.c_iflag |= IGNPAR;
770 if (pHandflow->FlowReplace & SERIAL_NULL_STRIPPING)
772 upcomingTermios.c_iflag |= IGNBRK;
776 upcomingTermios.c_iflag &= (uint32_t)~IGNBRK;
780 if (pHandflow->FlowReplace & SERIAL_BREAK_CHAR)
782 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_BREAK_CHAR feature.");
783 SetLastError(ERROR_NOT_SUPPORTED);
788 if (pHandflow->FlowReplace & SERIAL_XOFF_CONTINUE)
791 CommLog_Print(WLOG_WARN,
"Attempt to use the unsupported SERIAL_XOFF_CONTINUE feature.");
792 SetLastError(ERROR_NOT_SUPPORTED);
798 pComm->XOffLimit = pHandflow->XoffLimit;
799 pComm->XOnLimit = pHandflow->XonLimit;
801 if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
803 CommLog_Print(WLOG_WARN,
"comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32
"",
813 struct termios currentTermios = { 0 };
816 WINPR_ASSERT(pHandflow);
818 if (tcgetattr(pComm->fd, ¤tTermios) < 0)
820 SetLastError(ERROR_IO_DEVICE);
826 pHandflow->ControlHandShake = 0;
828 if (currentTermios.c_cflag & HUPCL)
829 pHandflow->ControlHandShake |= SERIAL_DTR_CONTROL;
833 if (currentTermios.c_cflag & CRTSCTS)
834 pHandflow->ControlHandShake |= SERIAL_CTS_HANDSHAKE;
846 pHandflow->FlowReplace = 0;
848 if (currentTermios.c_iflag & IXON)
849 pHandflow->FlowReplace |= SERIAL_AUTO_TRANSMIT;
851 if (currentTermios.c_iflag & IXOFF)
852 pHandflow->FlowReplace |= SERIAL_AUTO_RECEIVE;
854 if (!(currentTermios.c_iflag & IGNPAR))
855 pHandflow->FlowReplace |= SERIAL_ERROR_CHAR;
857 if (currentTermios.c_iflag & IGNBRK)
858 pHandflow->FlowReplace |= SERIAL_NULL_STRIPPING;
862 if (currentTermios.c_cflag & HUPCL)
863 pHandflow->FlowReplace |= SERIAL_RTS_CONTROL;
865 if (currentTermios.c_cflag & CRTSCTS)
866 pHandflow->FlowReplace |= SERIAL_RTS_HANDSHAKE;
870 pHandflow->XonLimit = pComm->XOnLimit;
871 pHandflow->XoffLimit = pComm->XOffLimit;
876static BOOL set_timeouts(WINPR_COMM* pComm,
const SERIAL_TIMEOUTS* pTimeouts)
879 WINPR_ASSERT(pTimeouts);
884 if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
885 (pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
889 "ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
890 SetLastError(ERROR_INVALID_PARAMETER);
894 pComm->timeouts.ReadIntervalTimeout = pTimeouts->ReadIntervalTimeout;
895 pComm->timeouts.ReadTotalTimeoutMultiplier = pTimeouts->ReadTotalTimeoutMultiplier;
896 pComm->timeouts.ReadTotalTimeoutConstant = pTimeouts->ReadTotalTimeoutConstant;
897 pComm->timeouts.WriteTotalTimeoutMultiplier = pTimeouts->WriteTotalTimeoutMultiplier;
898 pComm->timeouts.WriteTotalTimeoutConstant = pTimeouts->WriteTotalTimeoutConstant;
900 CommLog_Print(WLOG_DEBUG,
"ReadIntervalTimeout %" PRIu32
"",
901 pComm->timeouts.ReadIntervalTimeout);
902 CommLog_Print(WLOG_DEBUG,
"ReadTotalTimeoutMultiplier %" PRIu32
"",
903 pComm->timeouts.ReadTotalTimeoutMultiplier);
904 CommLog_Print(WLOG_DEBUG,
"ReadTotalTimeoutConstant %" PRIu32
"",
905 pComm->timeouts.ReadTotalTimeoutConstant);
906 CommLog_Print(WLOG_DEBUG,
"WriteTotalTimeoutMultiplier %" PRIu32
"",
907 pComm->timeouts.WriteTotalTimeoutMultiplier);
908 CommLog_Print(WLOG_DEBUG,
"WriteTotalTimeoutConstant %" PRIu32
"",
909 pComm->timeouts.WriteTotalTimeoutConstant);
917 WINPR_ASSERT(pTimeouts);
919 pTimeouts->ReadIntervalTimeout = pComm->timeouts.ReadIntervalTimeout;
920 pTimeouts->ReadTotalTimeoutMultiplier = pComm->timeouts.ReadTotalTimeoutMultiplier;
921 pTimeouts->ReadTotalTimeoutConstant = pComm->timeouts.ReadTotalTimeoutConstant;
922 pTimeouts->WriteTotalTimeoutMultiplier = pComm->timeouts.WriteTotalTimeoutMultiplier;
923 pTimeouts->WriteTotalTimeoutConstant = pComm->timeouts.WriteTotalTimeoutConstant;
928static BOOL set_lines(WINPR_COMM* pComm, UINT32 lines)
932 return CommIoCtl(pComm, TIOCMBIS, &lines);
935static BOOL clear_lines(WINPR_COMM* pComm, UINT32 lines)
939 return CommIoCtl(pComm, TIOCMBIC, &lines);
942static BOOL set_dtr(WINPR_COMM* pComm)
947 if (!get_handflow(pComm, &handflow))
951 WINPR_ASSERT((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0);
953 if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
955 SetLastError(ERROR_INVALID_PARAMETER);
959 return set_lines(pComm, TIOCM_DTR);
962static BOOL clear_dtr(WINPR_COMM* pComm)
967 if (!get_handflow(pComm, &handflow))
971 WINPR_ASSERT((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0);
973 if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
975 SetLastError(ERROR_INVALID_PARAMETER);
979 return clear_lines(pComm, TIOCM_DTR);
982static BOOL set_rts(WINPR_COMM* pComm)
987 if (!get_handflow(pComm, &handflow))
990 if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
992 SetLastError(ERROR_INVALID_PARAMETER);
996 return set_lines(pComm, TIOCM_RTS);
999static BOOL clear_rts(WINPR_COMM* pComm)
1002 WINPR_ASSERT(pComm);
1003 if (!get_handflow(pComm, &handflow))
1006 if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
1008 SetLastError(ERROR_INVALID_PARAMETER);
1012 return clear_lines(pComm, TIOCM_RTS);
1015static BOOL get_raw_modemstatus(WINPR_COMM* pComm,
int* pRegister)
1017 WINPR_ASSERT(pComm);
1018 WINPR_ASSERT(pRegister);
1020 const BOOL rc = CommIoCtl(pComm, TIOCMGET, pRegister);
1022 char buffer[128] = { 0 };
1023 CommLog_Print(WLOG_DEBUG,
"status %s",
1024 get_modem_status_str(*pRegister, buffer,
sizeof(buffer)));
1028static BOOL get_modemstatus(WINPR_COMM* pComm, ULONG* pRegister)
1032 if (!get_raw_modemstatus(pComm, &lines))
1036 if (lines & TIOCM_CTS)
1037 *pRegister |= SERIAL_MSR_CTS | SERIAL_MSR_DCTS;
1038 if (lines & TIOCM_DSR)
1039 *pRegister |= SERIAL_MSR_DSR | SERIAL_MSR_DDSR;
1040 if (lines & TIOCM_RI)
1041 *pRegister |= SERIAL_MSR_RI | SERIAL_MSR_TERI;
1042 if (lines & TIOCM_CD)
1043 *pRegister |= SERIAL_MSR_DCD | SERIAL_MSR_DDCD;
1049static const ULONG SERIAL_SYS_SUPPORTED_EV_MASK =
1050 SERIAL_EV_RXCHAR | SERIAL_EV_RXFLAG | SERIAL_EV_TXEMPTY | SERIAL_EV_CTS | SERIAL_EV_DSR |
1051 SERIAL_EV_RLSD | SERIAL_EV_BREAK | SERIAL_EV_ERR | SERIAL_EV_RING |
1058static BOOL is_wait_set(WINPR_COMM* pComm)
1060 WINPR_ASSERT(pComm);
1062 EnterCriticalSection(&pComm->EventsLock);
1063 const BOOL isWaiting = (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING) != 0;
1064 LeaveCriticalSection(&pComm->EventsLock);
1068static BOOL set_wait_mask(WINPR_COMM* pComm,
const ULONG* pWaitMask)
1070 ULONG possibleMask = 0;
1072 WINPR_ASSERT(pComm);
1073 WINPR_ASSERT(pWaitMask);
1078 if (is_wait_set(pComm))
1082 EnterCriticalSection(&pComm->EventsLock);
1083 pComm->PendingEvents |= SERIAL_EV_WINPR_STOP;
1084 LeaveCriticalSection(&pComm->EventsLock);
1087 while (is_wait_set(pComm))
1090 EnterCriticalSection(&pComm->EventsLock);
1091 pComm->PendingEvents &= (uint32_t)~SERIAL_EV_WINPR_STOP;
1092 LeaveCriticalSection(&pComm->EventsLock);
1096 EnterCriticalSection(&pComm->EventsLock);
1098 if (*pWaitMask == 0)
1101 if (!CommUpdateIOCount(pComm, FALSE))
1103 LeaveCriticalSection(&pComm->EventsLock);
1107 pComm->PendingEvents = 0;
1110 possibleMask = *pWaitMask & SERIAL_SYS_SUPPORTED_EV_MASK;
1112 if (possibleMask != *pWaitMask)
1114 CommLog_Print(WLOG_WARN,
1115 "Not all wait events supported (Serial.sys), requested events= 0x%08" PRIX32
1116 ", possible events= 0x%08" PRIX32
"",
1117 *pWaitMask, possibleMask);
1120 pComm->WaitEventMask = possibleMask;
1122 LeaveCriticalSection(&pComm->EventsLock);
1126 pComm->WaitEventMask = possibleMask;
1128 LeaveCriticalSection(&pComm->EventsLock);
1132static BOOL get_wait_mask(WINPR_COMM* pComm, ULONG* pWaitMask)
1134 WINPR_ASSERT(pComm);
1135 WINPR_ASSERT(pWaitMask);
1137 *pWaitMask = pComm->WaitEventMask;
1141static BOOL set_queue_size(WINPR_ATTR_UNUSED WINPR_COMM* pComm,
const SERIAL_QUEUE_SIZE* pQueueSize)
1143 WINPR_ASSERT(pComm);
1144 WINPR_ASSERT(pQueueSize);
1146 if ((pQueueSize->InSize <= N_TTY_BUF_SIZE) && (pQueueSize->OutSize <= N_TTY_BUF_SIZE))
1151 if (pQueueSize->InSize > N_TTY_BUF_SIZE)
1152 CommLog_Print(WLOG_WARN,
1153 "Requested an incompatible input buffer size: %" PRIu32
1154 ", keeping on with a %" PRIu32
" bytes buffer.",
1155 pQueueSize->InSize, N_TTY_BUF_SIZE);
1157 if (pQueueSize->OutSize > N_TTY_BUF_SIZE)
1158 CommLog_Print(WLOG_WARN,
1159 "Requested an incompatible output buffer size: %" PRIu32
1160 ", keeping on with a %" PRIu32
" bytes buffer.",
1161 pQueueSize->OutSize, N_TTY_BUF_SIZE);
1163 SetLastError(ERROR_CANCELLED);
1167static BOOL purge(WINPR_COMM* pComm,
const ULONG* pPurgeMask)
1169 WINPR_ASSERT(pComm);
1170 WINPR_ASSERT(pPurgeMask);
1172 if ((*pPurgeMask & (uint32_t)~(SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT |
1173 SERIAL_PURGE_TXCLEAR | SERIAL_PURGE_RXCLEAR)) > 0)
1175 CommLog_Print(WLOG_WARN,
"Invalid purge mask: 0x%" PRIX32
"\n", *pPurgeMask);
1176 SetLastError(ERROR_INVALID_PARAMETER);
1185 if (*pPurgeMask & SERIAL_PURGE_TXABORT)
1188#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1189 if (eventfd_write(pComm->fd_write_event, WINPR_PURGE_TXABORT) < 0)
1191 if (errno != EAGAIN)
1193 char ebuffer[256] = { 0 };
1194 CommLog_Print(WLOG_WARN,
"eventfd_write failed, errno=[%d] %s", errno,
1195 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1198 WINPR_ASSERT(errno == EAGAIN);
1203 if (*pPurgeMask & SERIAL_PURGE_RXABORT)
1206#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1207 if (eventfd_write(pComm->fd_read_event, WINPR_PURGE_RXABORT) < 0)
1209 if (errno != EAGAIN)
1211 char ebuffer[256] = { 0 };
1212 CommLog_Print(WLOG_WARN,
"eventfd_write failed, errno=[%d] %s", errno,
1213 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1216 WINPR_ASSERT(errno == EAGAIN);
1221 if (*pPurgeMask & SERIAL_PURGE_TXCLEAR)
1225 if (tcflush(pComm->fd, TCOFLUSH) < 0)
1227 char ebuffer[256] = { 0 };
1228 CommLog_Print(WLOG_WARN,
"tcflush(TCOFLUSH) failure, errno=[%d] %s", errno,
1229 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1230 SetLastError(ERROR_CANCELLED);
1235 if (*pPurgeMask & SERIAL_PURGE_RXCLEAR)
1239 if (tcflush(pComm->fd, TCIFLUSH) < 0)
1241 char ebuffer[256] = { 0 };
1242 CommLog_Print(WLOG_WARN,
"tcflush(TCIFLUSH) failure, errno=[%d] %s", errno,
1243 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1244 SetLastError(ERROR_CANCELLED);
1256static BOOL get_commstatus(WINPR_COMM* pComm,
SERIAL_STATUS* pCommstatus)
1260#if defined(WINPR_HAVE_COMM_COUNTERS)
1261 struct serial_icounter_struct currentCounters = { 0 };
1263 WINPR_ASSERT(pComm);
1264 WINPR_ASSERT(pCommstatus);
1267 EnterCriticalSection(&pComm->EventsLock);
1272 if (!get_raw_modemstatus(pComm, &status))
1275#if defined(WINPR_HAVE_COMM_COUNTERS)
1276 if (!CommUpdateIOCount(pComm, FALSE))
1278 currentCounters = pComm->counters;
1285 if (currentCounters.buf_overrun != pComm->counters.buf_overrun)
1287 pCommstatus->Errors |= SERIAL_ERROR_QUEUEOVERRUN;
1290 if (currentCounters.overrun != pComm->counters.overrun)
1292 pCommstatus->Errors |= SERIAL_ERROR_OVERRUN;
1293 pComm->PendingEvents |= SERIAL_EV_ERR;
1296 if (currentCounters.brk != pComm->counters.brk)
1298 pCommstatus->Errors |= SERIAL_ERROR_BREAK;
1299 pComm->PendingEvents |= SERIAL_EV_BREAK;
1302 if (currentCounters.parity != pComm->counters.parity)
1304 pCommstatus->Errors |= SERIAL_ERROR_PARITY;
1305 pComm->PendingEvents |= SERIAL_EV_ERR;
1308 if (currentCounters.frame != pComm->counters.frame)
1310 pCommstatus->Errors |= SERIAL_ERROR_FRAMING;
1311 pComm->PendingEvents |= SERIAL_EV_ERR;
1317 if (status & TIOCM_CTS)
1318 pComm->PendingEvents |= SERIAL_EV_CTS;
1326 if (status & TIOCM_SR)
1327 pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
1331 if (!CommIoCtl(pComm, FIONREAD, &available))
1334#if defined(__linux__)
1335 if (!CommIoCtl(pComm, TIOCINQ, &pCommstatus->AmountInInQueue))
1341 if (!CommIoCtl(pComm, TIOCOUTQ, &pCommstatus->AmountInOutQueue))
1351 pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
1353 if (pCommstatus->AmountInOutQueue == 0)
1355 pComm->PendingEvents |= SERIAL_EV_TXEMPTY;
1361 pComm->PendingEvents &= (uint32_t)~SERIAL_EV_TXEMPTY;
1364#if defined(WINPR_HAVE_COMM_COUNTERS)
1365 if (currentCounters.tx != pComm->counters.tx)
1367 pComm->PendingEvents &= (uint32_t)~SERIAL_EV_TXEMPTY;
1371 pComm->PendingEvents |= SERIAL_EV_TXEMPTY;
1374 if (currentCounters.rx != pComm->counters.rx)
1376 pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
1379 if (currentCounters.cts != pComm->counters.cts)
1381 pComm->PendingEvents |= SERIAL_EV_CTS;
1384 if (currentCounters.dsr != pComm->counters.dsr)
1386 pComm->PendingEvents |= SERIAL_EV_DSR;
1389 if (currentCounters.dcd != pComm->counters.dcd)
1391 pComm->PendingEvents |= SERIAL_EV_RLSD;
1394 if (currentCounters.rng != pComm->counters.rng)
1396 pComm->PendingEvents |= SERIAL_EV_RING;
1398 pComm->counters = currentCounters;
1401 if (pCommstatus->AmountInInQueue > (0.8 * N_TTY_BUF_SIZE))
1403 pComm->PendingEvents |= SERIAL_EV_RX80FULL;
1409 pComm->PendingEvents &= (uint32_t)~SERIAL_EV_RX80FULL;
1414 LeaveCriticalSection(&pComm->EventsLock);
1418static BOOL refresh_PendingEvents(WINPR_COMM* pComm)
1422 WINPR_ASSERT(pComm);
1425 if (!get_commstatus(pComm, &serialStatus))
1433static void consume_event(WINPR_COMM* pComm, ULONG* pOutputMask, ULONG event)
1435 WINPR_ASSERT(pComm);
1436 WINPR_ASSERT(pOutputMask);
1438 if ((pComm->WaitEventMask & event) && (pComm->PendingEvents & event))
1440 pComm->PendingEvents &= ~event;
1441 *pOutputMask |= event;
1445static BOOL unlock_return(WINPR_COMM* pComm, BOOL res)
1447 EnterCriticalSection(&pComm->EventsLock);
1448 pComm->PendingEvents &= (uint32_t)~SERIAL_EV_WINPR_WAITING;
1449 LeaveCriticalSection(&pComm->EventsLock);
1456static BOOL wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
1458 WINPR_ASSERT(pComm);
1459 WINPR_ASSERT(*pOutputMask == 0);
1461 EnterCriticalSection(&pComm->EventsLock);
1462 pComm->PendingEvents |= SERIAL_EV_WINPR_WAITING;
1463 LeaveCriticalSection(&pComm->EventsLock);
1468 if (!refresh_PendingEvents(pComm))
1469 return unlock_return(pComm, FALSE);
1472 EnterCriticalSection(&pComm->EventsLock);
1474 if (pComm->PendingEvents & SERIAL_EV_WINPR_STOP)
1481 WINPR_ASSERT(*pOutputMask == 0);
1483 LeaveCriticalSection(&pComm->EventsLock);
1487 consume_event(pComm, pOutputMask, SERIAL_EV_RXCHAR);
1488 consume_event(pComm, pOutputMask, SERIAL_EV_RXFLAG);
1489 consume_event(pComm, pOutputMask, SERIAL_EV_TXEMPTY);
1490 consume_event(pComm, pOutputMask, SERIAL_EV_DSR);
1491 consume_event(pComm, pOutputMask, SERIAL_EV_RLSD);
1492 consume_event(pComm, pOutputMask, SERIAL_EV_BREAK);
1493 consume_event(pComm, pOutputMask, SERIAL_EV_ERR);
1494 consume_event(pComm, pOutputMask, SERIAL_EV_RING);
1495 consume_event(pComm, pOutputMask, SERIAL_EV_RX80FULL);
1497 LeaveCriticalSection(&pComm->EventsLock);
1502 if (*pOutputMask != 0)
1516 return unlock_return(pComm, TRUE);
1519static BOOL set_break_on(WINPR_COMM* pComm)
1521 WINPR_ASSERT(pComm);
1522 return CommIoCtl(pComm, TIOCSBRK, NULL);
1525static BOOL set_break_off(WINPR_COMM* pComm)
1527 WINPR_ASSERT(pComm);
1529 return CommIoCtl(pComm, TIOCCBRK, NULL);
1532static BOOL set_xoff(WINPR_COMM* pComm)
1534 WINPR_ASSERT(pComm);
1536 if (tcflow(pComm->fd, TCIOFF) < 0)
1538 char ebuffer[256] = { 0 };
1539 CommLog_Print(WLOG_WARN,
"TCIOFF failure, errno=[%d] %s", errno,
1540 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1541 SetLastError(ERROR_IO_DEVICE);
1548static BOOL set_xon(WINPR_COMM* pComm)
1550 WINPR_ASSERT(pComm);
1552 if (tcflow(pComm->fd, TCION) < 0)
1554 char ebuffer[256] = { 0 };
1555 CommLog_Print(WLOG_WARN,
"TCION failure, errno=[%d] %s", errno,
1556 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
1557 SetLastError(ERROR_IO_DEVICE);
1564static BOOL get_dtrrts(WINPR_COMM* pComm, ULONG* pMask)
1568 WINPR_ASSERT(pComm);
1569 WINPR_ASSERT(pMask);
1571 if (!get_raw_modemstatus(pComm, &lines))
1576 if (!(lines & TIOCM_DTR))
1577 *pMask |= SERIAL_DTR_STATE;
1578 if (!(lines & TIOCM_RTS))
1579 *pMask |= SERIAL_RTS_STATE;
1584static BOOL config_size(WINPR_ATTR_UNUSED WINPR_COMM* pComm, ULONG* pSize)
1586 WINPR_ASSERT(pComm);
1587 WINPR_ASSERT(pSize);
1597static BOOL immediate_char(WINPR_COMM* pComm,
const UCHAR* pChar)
1600 DWORD nbBytesWritten = 0;
1602 WINPR_ASSERT(pComm);
1603 WINPR_ASSERT(pChar);
1611 result = CommWriteFile(pComm, pChar, 1, &nbBytesWritten, NULL);
1613 WINPR_ASSERT(nbBytesWritten == 1);
1618static BOOL reset_device(WINPR_ATTR_UNUSED WINPR_COMM* pComm)
1621 WINPR_ASSERT(pComm);
1623 pComm->XOnLimit = TTY_THRESHOLD_UNTHROTTLE;
1624 pComm->XOffLimit = TTY_THRESHOLD_THROTTLE;
1626 (void)CommUpdateIOCount(pComm, TRUE);
1628 return CommIoCtl(pComm, TIOCMSET, 0);
1632 .id = SerialDriverSerialSys,
1633 .name = _T(
"Serial.sys"),
1634 .set_baud_rate = set_baud_rate,
1635 .get_baud_rate = get_baud_rate,
1636 .get_properties = get_properties,
1637 .set_serial_chars = set_serial_chars,
1638 .get_serial_chars = get_serial_chars,
1639 .set_line_control = set_line_control,
1640 .get_line_control = get_line_control,
1641 .set_handflow = set_handflow,
1642 .get_handflow = get_handflow,
1643 .set_timeouts = set_timeouts,
1644 .get_timeouts = get_timeouts,
1646 .clear_dtr = clear_dtr,
1648 .clear_rts = clear_rts,
1649 .get_modemstatus = get_modemstatus,
1650 .set_wait_mask = set_wait_mask,
1651 .get_wait_mask = get_wait_mask,
1652 .wait_on_mask = wait_on_mask,
1653 .set_queue_size = set_queue_size,
1655 .get_commstatus = get_commstatus,
1656 .set_break_on = set_break_on,
1657 .set_break_off = set_break_off,
1658 .set_xoff = set_xoff,
1660 .get_dtrrts = get_dtrrts,
1661 .config_size = config_size,
1662 .immediate_char = immediate_char,
1663 .reset_device = reset_device,