/** * ERRORS: * ERROR_INVALID_HANDLE */ BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts) { WINPR_COMM* pComm = (WINPR_COMM*) hFile; DWORD bytesReturned; if (!CommInitialized()) return FALSE; if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_TIMEOUTS, lpCommTimeouts, sizeof(COMMTIMEOUTS), NULL, 0, &bytesReturned, NULL)) { CommLog_Print(WLOG_WARN, "SetCommTimeouts failure."); return FALSE; } return TRUE; }
BOOL SetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue) { WINPR_COMM* pComm = (WINPR_COMM*) hFile; SERIAL_QUEUE_SIZE queueSize; DWORD bytesReturned = 0; if (!CommInitialized()) return FALSE; if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd ) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } queueSize.InSize = dwInQueue; queueSize.OutSize = dwOutQueue; if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_QUEUE_SIZE, &queueSize, sizeof(SERIAL_QUEUE_SIZE), NULL, 0, &bytesReturned, NULL)) { CommLog_Print(WLOG_WARN, "SetCommTimeouts failure."); return FALSE; } return TRUE; }
BOOL CommCloseHandle(HANDLE handle) { WINPR_COMM *pComm; if (!CommInitialized()) return FALSE; pComm = (WINPR_COMM*)handle; if (!pComm || pComm->Type != HANDLE_TYPE_COMM) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } if (pComm->PendingEvents & SERIAL_EV_FREERDP_WAITING) { ULONG WaitMask = 0; DWORD BytesReturned = 0; /* ensures to gracefully stop the WAIT_ON_MASK's loop */ if (!CommDeviceIoControl(handle, IOCTL_SERIAL_SET_WAIT_MASK, &WaitMask, sizeof(ULONG), NULL, 0, &BytesReturned, NULL)) { CommLog_Print(WLOG_WARN, "failure to WAIT_ON_MASK's loop!"); } } DeleteCriticalSection(&pComm->ReadLock); DeleteCriticalSection(&pComm->WriteLock); DeleteCriticalSection(&pComm->EventsLock); if (pComm->fd > 0) close(pComm->fd); if (pComm->fd_write > 0) close(pComm->fd_write); if (pComm->fd_write_event > 0) close(pComm->fd_write_event); if (pComm->fd_read > 0) close(pComm->fd_read); if (pComm->fd_read_event > 0) close(pComm->fd_read_event); free(pComm); return TRUE; }
BOOL PurgeComm(HANDLE hFile, DWORD dwFlags) { WINPR_COMM* pComm = (WINPR_COMM*) hFile; DWORD bytesReturned = 0; if (!CommInitialized()) return FALSE; if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd ) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_PURGE, &dwFlags, sizeof(DWORD), NULL, 0, &bytesReturned, NULL)) { CommLog_Print(WLOG_WARN, "PurgeComm failure."); return FALSE; } return TRUE; }
/** * ERRORS: * ERROR_DLL_INIT_FAILED * ERROR_INVALID_HANDLE */ BOOL GetCommProperties(HANDLE hFile, LPCOMMPROP lpCommProp) { WINPR_COMM* pComm = (WINPR_COMM*) hFile; DWORD bytesReturned; if (!CommInitialized()) return FALSE; if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd ) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_PROPERTIES, NULL, 0, lpCommProp, sizeof(COMMPROP), &bytesReturned, NULL)) { CommLog_Print(WLOG_WARN, "GetCommProperties failure."); return FALSE; } return TRUE; }
/** * Function description * * @return 0 on success, otherwise a Win32 error code */ static UINT serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp) { UINT32 IoControlCode; UINT32 InputBufferLength; BYTE* InputBuffer = NULL; UINT32 OutputBufferLength; BYTE* OutputBuffer = NULL; DWORD BytesReturned = 0; if (Stream_GetRemainingLength(irp->input) < 32) return ERROR_INVALID_DATA; Stream_Read_UINT32(irp->input, OutputBufferLength); /* OutputBufferLength (4 bytes) */ Stream_Read_UINT32(irp->input, InputBufferLength); /* InputBufferLength (4 bytes) */ Stream_Read_UINT32(irp->input, IoControlCode); /* IoControlCode (4 bytes) */ Stream_Seek(irp->input, 20); /* Padding (20 bytes) */ if (Stream_GetRemainingLength(irp->input) < InputBufferLength) return ERROR_INVALID_DATA; OutputBuffer = (BYTE*)calloc(OutputBufferLength, sizeof(BYTE)); if (OutputBuffer == NULL) { irp->IoStatus = STATUS_NO_MEMORY; goto error_handle; } InputBuffer = (BYTE*)calloc(InputBufferLength, sizeof(BYTE)); if (InputBuffer == NULL) { irp->IoStatus = STATUS_NO_MEMORY; goto error_handle; } Stream_Read(irp->input, InputBuffer, InputBufferLength); WLog_Print(serial->log, WLOG_DEBUG, "CommDeviceIoControl: CompletionId=%"PRIu32", IoControlCode=[0x%"PRIX32"] %s", irp->CompletionId, IoControlCode, _comm_serial_ioctl_name(IoControlCode)); /* FIXME: CommDeviceIoControl to be replaced by DeviceIoControl() */ if (CommDeviceIoControl(serial->hComm, IoControlCode, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength, &BytesReturned, NULL)) { /* WLog_Print(serial->log, WLOG_DEBUG, "CommDeviceIoControl: CompletionId=%"PRIu32", IoControlCode=[0x%"PRIX32"] %s done", irp->CompletionId, IoControlCode, _comm_serial_ioctl_name(IoControlCode)); */ irp->IoStatus = STATUS_SUCCESS; } else { WLog_Print(serial->log, WLOG_DEBUG, "CommDeviceIoControl failure: IoControlCode=[0x%"PRIX32"] %s, last-error: 0x%08"PRIX32"", IoControlCode, _comm_serial_ioctl_name(IoControlCode), GetLastError()); irp->IoStatus = _GetLastErrorToIoStatus(serial); } error_handle: /* FIXME: find out whether it's required or not to get * BytesReturned == OutputBufferLength when * CommDeviceIoControl returns FALSE */ assert(OutputBufferLength == BytesReturned); Stream_Write_UINT32(irp->output, BytesReturned); /* OutputBufferLength (4 bytes) */ if (BytesReturned > 0) { if (!Stream_EnsureRemainingCapacity(irp->output, BytesReturned)) { WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); free(InputBuffer); free(OutputBuffer); return CHANNEL_RC_NO_MEMORY; } Stream_Write(irp->output, OutputBuffer, BytesReturned); /* OutputBuffer */ } /* FIXME: Why at least Windows 2008R2 gets lost with this * extra byte and likely on a IOCTL_SERIAL_SET_BAUD_RATE? The * extra byte is well required according MS-RDPEFS * 2.2.1.5.5 */ /* else */ /* { */ /* Stream_Write_UINT8(irp->output, 0); /\* Padding (1 byte) *\/ */ /* } */ free(InputBuffer); free(OutputBuffer); return CHANNEL_RC_OK; }
/** * @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; }
/** * * * ERRORS: * ERROR_INVALID_HANDLE * ERROR_INVALID_DATA * ERROR_IO_DEVICE * ERROR_OUTOFMEMORY */ BOOL GetCommState(HANDLE hFile, LPDCB lpDCB) { DCB *lpLocalDcb; struct termios currentState; WINPR_COMM* pComm = (WINPR_COMM*) hFile; DWORD bytesReturned; 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; } if (lpDCB->DCBlength < sizeof(DCB)) { SetLastError(ERROR_INVALID_DATA); return FALSE; } if (tcgetattr(pComm->fd, ¤tState) < 0) { SetLastError(ERROR_IO_DEVICE); return FALSE; } lpLocalDcb = (DCB*)calloc(1, lpDCB->DCBlength); if (lpLocalDcb == NULL) { SetLastError(ERROR_OUTOFMEMORY); return FALSE; } /* error_handle */ lpLocalDcb->DCBlength = lpDCB->DCBlength; SERIAL_BAUD_RATE baudRate; if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_BAUD_RATE, NULL, 0, &baudRate, sizeof(SERIAL_BAUD_RATE), &bytesReturned, NULL)) { CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the baud rate."); goto error_handle; } lpLocalDcb->BaudRate = baudRate.BaudRate; lpLocalDcb->fBinary = (currentState.c_cflag & ICANON) == 0; if (!lpLocalDcb->fBinary) { CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag."); } lpLocalDcb->fParity = (currentState.c_iflag & INPCK) != 0; SERIAL_HANDFLOW handflow; if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_HANDFLOW, NULL, 0, &handflow, sizeof(SERIAL_HANDFLOW), &bytesReturned, NULL)) { CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the handflow settings."); goto error_handle; } lpLocalDcb->fOutxCtsFlow = (handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE) != 0; lpLocalDcb->fOutxDsrFlow = (handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE) != 0; if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) { lpLocalDcb->fDtrControl = DTR_CONTROL_HANDSHAKE; } else if (handflow.ControlHandShake & SERIAL_DTR_CONTROL) { lpLocalDcb->fDtrControl = DTR_CONTROL_ENABLE; } else { lpLocalDcb->fDtrControl = DTR_CONTROL_DISABLE; } lpLocalDcb->fDsrSensitivity = (handflow.ControlHandShake & SERIAL_DSR_SENSITIVITY) != 0; lpLocalDcb->fTXContinueOnXoff = (handflow.FlowReplace & SERIAL_XOFF_CONTINUE) != 0; lpLocalDcb->fOutX = (handflow.FlowReplace & SERIAL_AUTO_TRANSMIT) != 0; lpLocalDcb->fInX = (handflow.FlowReplace & SERIAL_AUTO_RECEIVE) != 0; lpLocalDcb->fErrorChar = (handflow.FlowReplace & SERIAL_ERROR_CHAR) != 0; lpLocalDcb->fNull = (handflow.FlowReplace & SERIAL_NULL_STRIPPING) != 0; if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE) { lpLocalDcb->fRtsControl = RTS_CONTROL_HANDSHAKE; } else if (handflow.FlowReplace & SERIAL_RTS_CONTROL) { lpLocalDcb->fRtsControl = RTS_CONTROL_ENABLE; } else { lpLocalDcb->fRtsControl = RTS_CONTROL_DISABLE; } // FIXME: how to get the RTS_CONTROL_TOGGLE state? Does it match the UART 16750's Autoflow Control Enabled bit in its Modem Control Register (MCR) lpLocalDcb->fAbortOnError = (handflow.ControlHandShake & SERIAL_ERROR_ABORT) != 0; /* lpLocalDcb->fDummy2 not used */ lpLocalDcb->wReserved = 0; /* must be zero */ lpLocalDcb->XonLim = handflow.XonLimit; lpLocalDcb->XoffLim = handflow.XoffLimit; SERIAL_LINE_CONTROL lineControl; if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL, NULL, 0, &lineControl, sizeof(SERIAL_LINE_CONTROL), &bytesReturned, NULL)) { CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the control settings."); goto error_handle; } lpLocalDcb->ByteSize = lineControl.WordLength; lpLocalDcb->Parity = lineControl.Parity; lpLocalDcb->StopBits = lineControl.StopBits; SERIAL_CHARS serialChars; if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars, sizeof(SERIAL_CHARS), &bytesReturned, NULL)) { CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the serial chars."); goto error_handle; } lpLocalDcb->XonChar = serialChars.XonChar; lpLocalDcb->XoffChar = serialChars.XoffChar; lpLocalDcb->ErrorChar = serialChars.ErrorChar; lpLocalDcb->EofChar = serialChars.EofChar; lpLocalDcb->EvtChar = serialChars.EventChar; memcpy(lpDCB, lpLocalDcb, lpDCB->DCBlength); free(lpLocalDcb); return TRUE; error_handle: if (lpLocalDcb) free(lpLocalDcb); return FALSE; }