static BOOL _set_baud_rate(WINPR_COMM *pComm, const SERIAL_BAUD_RATE *pBaudRate) { int i; speed_t newSpeed; struct termios futureState; ZeroMemory(&futureState, sizeof(struct termios)); if (tcgetattr(pComm->fd, &futureState) < 0) /* NB: preserves current settings not directly handled by the Communication Functions */ { SetLastError(ERROR_IO_DEVICE); return FALSE; } for (i=0; _BAUD_TABLE[i][0]<_BAUD_TABLE_END; i++) { if (_BAUD_TABLE[i][1] == pBaudRate->BaudRate) { newSpeed = _BAUD_TABLE[i][0]; if (cfsetspeed(&futureState, newSpeed) < 0) { CommLog_Print(WLOG_WARN, "failed to set speed 0x%x (%"PRIu32")", newSpeed, pBaudRate->BaudRate); return FALSE; } assert(cfgetispeed(&futureState) == newSpeed); if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &futureState) < 0) { CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%"PRIX32"", GetLastError()); return FALSE; } return TRUE; } } CommLog_Print(WLOG_WARN, "could not find a matching speed for the baud rate %"PRIu32"", pBaudRate->BaudRate); SetLastError(ERROR_INVALID_DATA); return FALSE; }
static BOOL _set_baud_rate(WINPR_COMM *pComm, const SERIAL_BAUD_RATE *pBaudRate) { int i; speed_t newSpeed; struct termios upcomingTermios; ZeroMemory(&upcomingTermios, sizeof(struct termios)); if (tcgetattr(pComm->fd, &upcomingTermios) < 0) { SetLastError(ERROR_IO_DEVICE); return FALSE; } for (i=0; _SERIAL_SYS_BAUD_TABLE[i][0]<=_SERIAL_MAX_BAUD; i++) { if (_SERIAL_SYS_BAUD_TABLE[i][1] == pBaudRate->BaudRate) { newSpeed = _SERIAL_SYS_BAUD_TABLE[i][0]; if (cfsetspeed(&upcomingTermios, newSpeed) < 0) { CommLog_Print(WLOG_WARN, "failed to set speed %u (%lu)", newSpeed, pBaudRate->BaudRate); return FALSE; } if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0) { CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%lX", GetLastError()); return FALSE; } return TRUE; } } CommLog_Print(WLOG_WARN, "could not find a matching speed for the baud rate %lu", pBaudRate->BaudRate); SetLastError(ERROR_INVALID_DATA); return FALSE; }
/** * @return TRUE on success, FALSE otherwise. * * As of today, SetCommState() can fail half-way with some settings * applied and some others not. SetCommState() returns on the first * failure met. FIXME: or is it correct? * * ERRORS: * ERROR_INVALID_HANDLE * ERROR_IO_DEVICE */ BOOL SetCommState(HANDLE hFile, LPDCB lpDCB) { struct termios upcomingTermios; WINPR_COMM* pComm = (WINPR_COMM*) hFile; DWORD bytesReturned; /* FIXME: validate changes according GetCommProperties? */ if (!CommInitialized()) return FALSE; if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd ) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } if (!lpDCB) { SetLastError(ERROR_INVALID_DATA); return FALSE; } /* NB: did the choice to call ioctls first when available and then to setup upcomingTermios. Don't mix both stages. */ /** ioctl calls stage **/ SERIAL_BAUD_RATE baudRate; baudRate.BaudRate = lpDCB->BaudRate; if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_BAUD_RATE, &baudRate, sizeof(SERIAL_BAUD_RATE), NULL, 0, &bytesReturned, NULL)) { CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the baud rate."); return FALSE; } SERIAL_CHARS serialChars; if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars, sizeof(SERIAL_CHARS), &bytesReturned, NULL)) /* as of today, required for BreakChar */ { CommLog_Print(WLOG_WARN, "SetCommState failure: could not get the initial serial chars."); return FALSE; } serialChars.XonChar = lpDCB->XonChar; serialChars.XoffChar = lpDCB->XoffChar; serialChars.ErrorChar = lpDCB->ErrorChar; serialChars.EofChar = lpDCB->EofChar; serialChars.EventChar = lpDCB->EvtChar; if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_CHARS, &serialChars, sizeof(SERIAL_CHARS), NULL, 0, &bytesReturned, NULL)) { CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the serial chars."); return FALSE; } SERIAL_LINE_CONTROL lineControl; lineControl.StopBits = lpDCB->StopBits; lineControl.Parity = lpDCB->Parity; lineControl.WordLength = lpDCB->ByteSize; if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_LINE_CONTROL, &lineControl, sizeof(SERIAL_LINE_CONTROL), NULL, 0, &bytesReturned, NULL)) { CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the control settings."); return FALSE; } SERIAL_HANDFLOW handflow; ZeroMemory(&handflow, sizeof(SERIAL_HANDFLOW)); if (lpDCB->fOutxCtsFlow) { handflow.ControlHandShake |= SERIAL_CTS_HANDSHAKE; } if (lpDCB->fOutxDsrFlow) { handflow.ControlHandShake |= SERIAL_DSR_HANDSHAKE; } switch (lpDCB->fDtrControl) { case SERIAL_DTR_HANDSHAKE: handflow.ControlHandShake |= DTR_CONTROL_HANDSHAKE; break; case SERIAL_DTR_CONTROL: handflow.ControlHandShake |= DTR_CONTROL_ENABLE; break; case DTR_CONTROL_DISABLE: /* do nothing since handflow is init-zeroed */ break; default: CommLog_Print(WLOG_WARN, "Unexpected fDtrControl value: %d\n", lpDCB->fDtrControl); return FALSE; } if (lpDCB->fDsrSensitivity) { handflow.ControlHandShake |= SERIAL_DSR_SENSITIVITY; } if (lpDCB->fTXContinueOnXoff) { handflow.FlowReplace |= SERIAL_XOFF_CONTINUE; } if (lpDCB->fOutX) { handflow.FlowReplace |= SERIAL_AUTO_TRANSMIT; } if (lpDCB->fInX) { handflow.FlowReplace |= SERIAL_AUTO_RECEIVE; } if (lpDCB->fErrorChar) { handflow.FlowReplace |= SERIAL_ERROR_CHAR; } if (lpDCB->fNull) { handflow.FlowReplace |= SERIAL_NULL_STRIPPING; } switch (lpDCB->fRtsControl) { case RTS_CONTROL_TOGGLE: CommLog_Print(WLOG_WARN, "Unsupported RTS_CONTROL_TOGGLE feature"); // FIXME: see also GetCommState() return FALSE; case RTS_CONTROL_HANDSHAKE: handflow.FlowReplace |= SERIAL_RTS_HANDSHAKE; break; case RTS_CONTROL_ENABLE: handflow.FlowReplace |= SERIAL_RTS_CONTROL; break; case RTS_CONTROL_DISABLE: /* do nothing since handflow is init-zeroed */ break; default: CommLog_Print(WLOG_WARN, "Unexpected fRtsControl value: %d\n", lpDCB->fRtsControl); return FALSE; } if (lpDCB->fAbortOnError) { handflow.ControlHandShake |= SERIAL_ERROR_ABORT; } /* lpDCB->fDummy2 not used */ /* lpLocalDcb->wReserved ignored */ handflow.XonLimit = lpDCB->XonLim; handflow.XoffLimit = lpDCB->XoffLim; if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_HANDFLOW, &handflow, sizeof(SERIAL_HANDFLOW), NULL, 0, &bytesReturned, NULL)) { CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the handflow settings."); return FALSE; } /** upcomingTermios stage **/ ZeroMemory(&upcomingTermios, sizeof(struct termios)); if (tcgetattr(pComm->fd, &upcomingTermios) < 0) /* NB: preserves current settings not directly handled by the Communication Functions */ { SetLastError(ERROR_IO_DEVICE); return FALSE; } if (lpDCB->fBinary) { upcomingTermios.c_lflag &= ~ICANON; } else { upcomingTermios.c_lflag |= ICANON; CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag."); } if (lpDCB->fParity) { upcomingTermios.c_iflag |= INPCK; } else { upcomingTermios.c_iflag &= ~INPCK; } /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363423%28v=vs.85%29.aspx * * The SetCommState function reconfigures the communications * resource, but it does not affect the internal output and * input buffers of the specified driver. The buffers are not * flushed, and pending read and write operations are not * terminated prematurely. * * TCSANOW matches the best this definition */ if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0) { SetLastError(ERROR_IO_DEVICE); return FALSE; } return TRUE; }
/** * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363198%28v=vs.85%29.aspx * * @param lpDeviceName e.g. COM1, \\.\COM1, ... * * @param dwDesiredAccess expects GENERIC_READ | GENERIC_WRITE, a * warning message is printed otherwise. TODO: better support. * * @param dwShareMode must be zero, INVALID_HANDLE_VALUE is returned * otherwise and GetLastError() should return ERROR_SHARING_VIOLATION. * * @param lpSecurityAttributes NULL expected, a warning message is printed * otherwise. TODO: better support. * * @param dwCreationDisposition must be OPEN_EXISTING. If the * communication device doesn't exist INVALID_HANDLE_VALUE is returned * and GetLastError() returns ERROR_FILE_NOT_FOUND. * * @param dwFlagsAndAttributes zero expected, a warning message is * printed otherwise. * * @param hTemplateFile must be NULL. * * @return INVALID_HANDLE_VALUE on error. */ HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { CHAR devicePath[MAX_PATH]; struct stat deviceStat; WINPR_COMM* pComm = NULL; struct termios upcomingTermios; if (!CommInitialized()) return INVALID_HANDLE_VALUE; if (dwDesiredAccess != (GENERIC_READ | GENERIC_WRITE)) { CommLog_Print(WLOG_WARN, "unexpected access to the device: 0x%lX", dwDesiredAccess); } if (dwShareMode != 0) { SetLastError(ERROR_SHARING_VIOLATION); return INVALID_HANDLE_VALUE; } /* TODO: Prevents other processes from opening a file or * device if they request delete, read, or write access. */ if (lpSecurityAttributes != NULL) { CommLog_Print(WLOG_WARN, "unexpected security attributes, nLength=%lu", lpSecurityAttributes->nLength); } if (dwCreationDisposition != OPEN_EXISTING) { SetLastError(ERROR_FILE_NOT_FOUND); /* FIXME: ERROR_NOT_SUPPORTED better? */ return INVALID_HANDLE_VALUE; } if (QueryCommDevice(lpDeviceName, devicePath, MAX_PATH) <= 0) { /* SetLastError(GetLastError()); */ return INVALID_HANDLE_VALUE; } if (stat(devicePath, &deviceStat) < 0) { CommLog_Print(WLOG_WARN, "device not found %s", devicePath); SetLastError(ERROR_FILE_NOT_FOUND); return INVALID_HANDLE_VALUE; } if (!S_ISCHR(deviceStat.st_mode)) { CommLog_Print(WLOG_WARN, "bad device %s", devicePath); SetLastError(ERROR_BAD_DEVICE); return INVALID_HANDLE_VALUE; } if (dwFlagsAndAttributes != 0) { CommLog_Print(WLOG_WARN, "unexpected flags and attributes: 0x%lX", dwFlagsAndAttributes); } if (hTemplateFile != NULL) { SetLastError(ERROR_NOT_SUPPORTED); /* FIXME: other proper error? */ return INVALID_HANDLE_VALUE; } pComm = (WINPR_COMM*) calloc(1, sizeof(WINPR_COMM)); if (pComm == NULL) { SetLastError(ERROR_OUTOFMEMORY); return INVALID_HANDLE_VALUE; } WINPR_HANDLE_SET_TYPE(pComm, HANDLE_TYPE_COMM); pComm->ops = &ops; /* error_handle */ pComm->fd = open(devicePath, O_RDWR | O_NOCTTY | O_NONBLOCK); if (pComm->fd < 0) { CommLog_Print(WLOG_WARN, "failed to open device %s", devicePath); SetLastError(ERROR_BAD_DEVICE); goto error_handle; } pComm->fd_read = open(devicePath, O_RDONLY | O_NOCTTY | O_NONBLOCK); if (pComm->fd_read < 0) { CommLog_Print(WLOG_WARN, "failed to open fd_read, device: %s", devicePath); SetLastError(ERROR_BAD_DEVICE); goto error_handle; } pComm->fd_read_event = eventfd(0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */ if (pComm->fd_read_event < 0) { CommLog_Print(WLOG_WARN, "failed to open fd_read_event, device: %s", devicePath); SetLastError(ERROR_BAD_DEVICE); goto error_handle; } InitializeCriticalSection(&pComm->ReadLock); pComm->fd_write = open(devicePath, O_WRONLY | O_NOCTTY | O_NONBLOCK); if (pComm->fd_write < 0) { CommLog_Print(WLOG_WARN, "failed to open fd_write, device: %s", devicePath); SetLastError(ERROR_BAD_DEVICE); goto error_handle; } pComm->fd_write_event = eventfd(0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */ if (pComm->fd_write_event < 0) { CommLog_Print(WLOG_WARN, "failed to open fd_write_event, device: %s", devicePath); SetLastError(ERROR_BAD_DEVICE); goto error_handle; } InitializeCriticalSection(&pComm->WriteLock); /* can also be setup later on with _comm_setServerSerialDriver() */ pComm->serverSerialDriverId = SerialDriverUnknown; InitializeCriticalSection(&pComm->EventsLock); if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0) { CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno, strerror(errno)); 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)); } /* The binary/raw mode is required for the redirection but * only flags that are not handle somewhere-else, except * ICANON, are forced here. */ ZeroMemory(&upcomingTermios, sizeof(struct termios)); if (tcgetattr(pComm->fd, &upcomingTermios) < 0) { SetLastError(ERROR_IO_DEVICE); goto error_handle; } upcomingTermios.c_iflag &= ~(/*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); */ /* upcomingTermios.c_cflag |= CS8; */ /* About missing flags recommended by termios(3): * * IGNBRK and IXON, see: IOCTL_SERIAL_SET_HANDFLOW * CSIZE, PARENB and CS8, see: IOCTL_SERIAL_SET_LINE_CONTROL */ /* a few more settings required for the redirection */ upcomingTermios.c_cflag |= CLOCAL | CREAD; if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0) { SetLastError(ERROR_IO_DEVICE); goto error_handle; } return (HANDLE)pComm; error_handle: if (pComm != NULL) { CloseHandle(pComm); } return INVALID_HANDLE_VALUE; }
static BOOL _set_handflow(WINPR_COMM *pComm, const SERIAL_HANDFLOW *pHandflow) { BOOL result = TRUE; struct termios upcomingTermios; ZeroMemory(&upcomingTermios, sizeof(struct termios)); if (tcgetattr(pComm->fd, &upcomingTermios) < 0) { SetLastError(ERROR_IO_DEVICE); return FALSE; } /* HUPCL */ /* logical XOR */ if ((!(pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) && (pHandflow->FlowReplace & SERIAL_RTS_CONTROL)) || ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) && !(pHandflow->FlowReplace & SERIAL_RTS_CONTROL))) { CommLog_Print(WLOG_WARN, "SERIAL_DTR_CONTROL:%s and SERIAL_RTS_CONTROL:%s cannot be different, HUPCL will be set since it is claimed for one of the both lines.", (pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ? "ON" : "OFF", (pHandflow->FlowReplace & SERIAL_RTS_CONTROL) ? "ON" : "OFF"); } if ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) || (pHandflow->FlowReplace & SERIAL_RTS_CONTROL)) { upcomingTermios.c_cflag |= HUPCL; } else { upcomingTermios.c_cflag &= ~HUPCL; /* FIXME: is the DTR line also needs to be forced to a disable state according SERIAL_DTR_CONTROL? */ /* FIXME: is the RTS line also needs to be forced to a disable state according SERIAL_RTS_CONTROL? */ } /* CRTSCTS */ /* logical XOR */ if ((!(pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) && (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)) || ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) && !(pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE))) { CommLog_Print(WLOG_WARN, "SERIAL_CTS_HANDSHAKE:%s and SERIAL_RTS_HANDSHAKE:%s cannot be different, CRTSCTS will be set since it is claimed for one of the both lines.", (pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ? "ON" : "OFF", (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE) ? "ON" : "OFF"); } if ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) || (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)) { upcomingTermios.c_cflag |= CRTSCTS; } else { upcomingTermios.c_cflag &= ~CRTSCTS; } /* ControlHandShake */ if (pHandflow->ControlHandShake & SERIAL_DTR_HANDSHAKE) { /* DTR/DSR flow control not supported on Linux */ CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DTR_HANDSHAKE feature."); SetLastError(ERROR_NOT_SUPPORTED); result = FALSE; /* but keep on */ } if (pHandflow->ControlHandShake & SERIAL_DSR_HANDSHAKE) { /* DTR/DSR flow control not supported on Linux */ CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DSR_HANDSHAKE feature."); SetLastError(ERROR_NOT_SUPPORTED); result = FALSE; /* but keep on */ } if (pHandflow->ControlHandShake & SERIAL_DCD_HANDSHAKE) { /* DCD flow control not supported on Linux */ CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DCD_HANDSHAKE feature."); SetLastError(ERROR_NOT_SUPPORTED); result = FALSE; /* but keep on */ } // FIXME: could be implemented during read/write I/O if (pHandflow->ControlHandShake & SERIAL_DSR_SENSITIVITY) { /* DSR line control not supported on Linux */ CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DSR_SENSITIVITY feature."); SetLastError(ERROR_NOT_SUPPORTED); result = FALSE; /* but keep on */ } // FIXME: could be implemented during read/write I/O if (pHandflow->ControlHandShake & SERIAL_ERROR_ABORT) { /* Aborting operations on error not supported on Linux */ CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_ERROR_ABORT feature."); SetLastError(ERROR_NOT_SUPPORTED); result = FALSE; /* but keep on */ } /* FlowReplace */ if (pHandflow->FlowReplace & SERIAL_AUTO_TRANSMIT) { upcomingTermios.c_iflag |= IXON; } else { upcomingTermios.c_iflag &= ~IXON; } if (pHandflow->FlowReplace & SERIAL_AUTO_RECEIVE) { upcomingTermios.c_iflag |= IXOFF; } else { upcomingTermios.c_iflag &= ~IXOFF; } // FIXME: could be implemented during read/write I/O, as of today ErrorChar is necessary '\0' if (pHandflow->FlowReplace & SERIAL_ERROR_CHAR) { /* errors will be replaced by the character '\0'. */ upcomingTermios.c_iflag &= ~IGNPAR; } else { upcomingTermios.c_iflag |= IGNPAR; } if (pHandflow->FlowReplace & SERIAL_NULL_STRIPPING) { upcomingTermios.c_iflag |= IGNBRK; } else { upcomingTermios.c_iflag &= ~IGNBRK; } // FIXME: could be implemented during read/write I/O if (pHandflow->FlowReplace & SERIAL_BREAK_CHAR) { CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_BREAK_CHAR feature."); SetLastError(ERROR_NOT_SUPPORTED); result = FALSE; /* but keep on */ } // FIXME: could be implemented during read/write I/O if (pHandflow->FlowReplace & SERIAL_XOFF_CONTINUE) { /* not supported on Linux */ CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_XOFF_CONTINUE feature."); SetLastError(ERROR_NOT_SUPPORTED); result = FALSE; /* but keep on */ } /* 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 */ } /* XoffChar */ // FIXME: could be implemented during read/write I/O if (pHandflow->XoffLimit != TTY_THRESHOLD_THROTTLE) { 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"", GetLastError()); return FALSE; } return result; }
static BOOL _set_line_control(WINPR_COMM *pComm, const SERIAL_LINE_CONTROL *pLineControl) { BOOL result = TRUE; struct termios upcomingTermios; /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214%28v=vs.85%29.aspx * * The use of 5 data bits with 2 stop bits is an invalid * combination, as is 6, 7, or 8 data bits with 1.5 stop bits. * * FIXME: prefered to let the underlying driver to deal with * this issue. At least produce a warning message? */ ZeroMemory(&upcomingTermios, sizeof(struct termios)); if (tcgetattr(pComm->fd, &upcomingTermios) < 0) { SetLastError(ERROR_IO_DEVICE); return FALSE; } /* FIXME: use of a COMMPROP to validate new settings? */ switch (pLineControl->StopBits) { case STOP_BIT_1: upcomingTermios.c_cflag &= ~CSTOPB; break; case STOP_BITS_1_5: CommLog_Print(WLOG_WARN, "Unsupported one and a half stop bits."); break; case STOP_BITS_2: upcomingTermios.c_cflag |= CSTOPB; break; default: CommLog_Print(WLOG_WARN, "unexpected number of stop bits: %"PRIu8"\n", pLineControl->StopBits); result = FALSE; /* but keep on */ break; } switch (pLineControl->Parity) { case NO_PARITY: upcomingTermios.c_cflag &= ~(PARENB | PARODD | CMSPAR); break; case ODD_PARITY: upcomingTermios.c_cflag &= ~CMSPAR; upcomingTermios.c_cflag |= PARENB | PARODD; break; case EVEN_PARITY: upcomingTermios.c_cflag &= ~(PARODD | CMSPAR); upcomingTermios.c_cflag |= PARENB; break; case MARK_PARITY: upcomingTermios.c_cflag |= PARENB | PARODD | CMSPAR; break; case SPACE_PARITY: upcomingTermios.c_cflag &= ~PARODD; upcomingTermios.c_cflag |= PARENB | CMSPAR; break; default: CommLog_Print(WLOG_WARN, "unexpected type of parity: %"PRIu8"\n", pLineControl->Parity); result = FALSE; /* but keep on */ break; } switch (pLineControl->WordLength) { case 5: upcomingTermios.c_cflag &= ~CSIZE; upcomingTermios.c_cflag |= CS5; break; case 6: upcomingTermios.c_cflag &= ~CSIZE; upcomingTermios.c_cflag |= CS6; break; case 7: upcomingTermios.c_cflag &= ~CSIZE; upcomingTermios.c_cflag |= CS7; break; case 8: upcomingTermios.c_cflag &= ~CSIZE; upcomingTermios.c_cflag |= CS8; break; default: CommLog_Print(WLOG_WARN, "unexpected number od data bits per character: %"PRIu8"\n", pLineControl->WordLength); result = FALSE; /* but keep on */ break; } if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0) { CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%08"PRIX32"", GetLastError()); return FALSE; } return result; }
/** * NOTE: Only XonChar and XoffChar are plenty supported with the Linux * N_TTY line discipline. * * ERRORS: * ERROR_IO_DEVICE * ERROR_INVALID_PARAMETER when Xon and Xoff chars are the same; * ERROR_NOT_SUPPORTED */ static BOOL _set_serial_chars(WINPR_COMM *pComm, const SERIAL_CHARS *pSerialChars) { BOOL result = TRUE; struct termios upcomingTermios; ZeroMemory(&upcomingTermios, sizeof(struct termios)); if (tcgetattr(pComm->fd, &upcomingTermios) < 0) { SetLastError(ERROR_IO_DEVICE); return FALSE; } if (pSerialChars->XonChar == pSerialChars->XoffChar) { /* https://msdn.microsoft.com/en-us/library/windows/hardware/ff546688?v=vs.85.aspx */ SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } /* termios(3): (..) above symbolic subscript values are all * different, except that VTIME, VMIN may have the same value * as VEOL, VEOF, respectively. In noncanonical mode the * special character meaning is replaced by the timeout * meaning. * * EofChar and c_cc[VEOF] are not quite the same, prefer to * don't use c_cc[VEOF] at all. * * FIXME: might be implemented during read/write I/O */ if (pSerialChars->EofChar != '\0') { CommLog_Print(WLOG_WARN, "EofChar %02"PRIX8" cannot be set\n", pSerialChars->EofChar); SetLastError(ERROR_NOT_SUPPORTED); result = FALSE; /* but keep on */ } /* According the Linux's n_tty discipline, charaters with a * parity error can only be let unchanged, replaced by \0 or * get the prefix the prefix \377 \0 */ /* FIXME: see also: _set_handflow() */ if (pSerialChars->ErrorChar != '\0') { CommLog_Print(WLOG_WARN, "ErrorChar 0x%02"PRIX8" ('%c') cannot be set (unsupported).\n", pSerialChars->ErrorChar, (char) pSerialChars->ErrorChar); SetLastError(ERROR_NOT_SUPPORTED); result = FALSE; /* but keep on */ } /* FIXME: see also: _set_handflow() */ if (pSerialChars->BreakChar != '\0') { CommLog_Print(WLOG_WARN, "BreakChar 0x%02"PRIX8" ('%c') cannot be set (unsupported).\n", pSerialChars->BreakChar, (char) pSerialChars->BreakChar); SetLastError(ERROR_NOT_SUPPORTED); result = FALSE; /* but keep on */ } if (pSerialChars->EventChar != '\0') { pComm->eventChar = pSerialChars->EventChar; } upcomingTermios.c_cc[VSTART] = pSerialChars->XonChar; upcomingTermios.c_cc[VSTOP] = pSerialChars->XoffChar; if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0) { CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%08"PRIX32"", GetLastError()); return FALSE; } return result; }