| /* |
| * BRLTTY - A background process providing access to the console screen (when in |
| * text mode) for a blind person using a refreshable braille display. |
| * |
| * Copyright (C) 1995-2023 by The BRLTTY Developers. |
| * |
| * BRLTTY comes with ABSOLUTELY NO WARRANTY. |
| * |
| * This is free software, placed under the terms of the |
| * GNU Lesser General Public License, as published by the Free Software |
| * Foundation; either version 2.1 of the License, or (at your option) any |
| * later version. Please see the file LICENSE-LGPL for details. |
| * |
| * Web Page: http://brltty.app/ |
| * |
| * This software is maintained by Dave Mielke <dave@mielke.cc>. |
| */ |
| |
| #include "prologue.h" |
| |
| #include <errno.h> |
| #include <io.h> |
| #include <fcntl.h> |
| |
| #include "log.h" |
| #include "ascii.h" |
| |
| #include "serial_windows.h" |
| #include "serial_internal.h" |
| |
| BEGIN_SERIAL_BAUD_TABLE |
| #ifdef CBR_110 |
| {110, CBR_110}, |
| #endif /* CBR_110 */ |
| |
| #ifdef CBR_300 |
| {300, CBR_300}, |
| #endif /* CBR_300 */ |
| |
| #ifdef CBR_600 |
| {600, CBR_600}, |
| #endif /* CBR_600 */ |
| |
| #ifdef CBR_1200 |
| {1200, CBR_1200}, |
| #endif /* CBR_1200 */ |
| |
| #ifdef CBR_2400 |
| {2400, CBR_2400}, |
| #endif /* CBR_2400 */ |
| |
| #ifdef CBR_4800 |
| {4800, CBR_4800}, |
| #endif /* CBR_4800 */ |
| |
| #ifdef CBR_9600 |
| {9600, CBR_9600}, |
| #endif /* CBR_9600 */ |
| |
| #ifdef CBR_14400 |
| {14400, CBR_14400}, |
| #endif /* CBR_14400 */ |
| |
| #ifdef CBR_19200 |
| {19200, CBR_19200}, |
| #endif /* CBR_19200 */ |
| |
| #ifdef CBR_38400 |
| {38400, CBR_38400}, |
| #endif /* CBR_38400 */ |
| |
| #ifdef CBR_56000 |
| {56000, CBR_56000}, |
| #endif /* CBR_56000 */ |
| |
| #ifdef CBR_57600 |
| {57600, CBR_57600}, |
| #endif /* CBR_57600 */ |
| |
| #ifdef CBR_115200 |
| {115200, CBR_115200}, |
| #endif /* CBR_115200 */ |
| |
| #ifdef CBR_128000 |
| {128000, CBR_128000}, |
| #endif /* CBR_128000 */ |
| |
| #ifdef CBR_256000 |
| {256000, CBR_256000}, |
| #endif /* CBR_256000 */ |
| END_SERIAL_BAUD_TABLE |
| |
| void |
| serialPutInitialAttributes (SerialAttributes *attributes) { |
| attributes->DCBlength = sizeof(*attributes); |
| attributes->fBinary = TRUE; |
| attributes->fTXContinueOnXoff = TRUE; |
| attributes->XonChar = ASCII_DC1; |
| attributes->XoffChar = ASCII_DC3; |
| } |
| |
| int |
| serialPutSpeed (SerialAttributes *attributes, SerialSpeed speed) { |
| attributes->BaudRate = speed; |
| return 1; |
| } |
| |
| int |
| serialPutDataBits (SerialAttributes *attributes, unsigned int bits) { |
| if ((bits < 5) || (bits > 8)) return 0; |
| attributes->ByteSize = bits; |
| return 1; |
| } |
| |
| int |
| serialPutStopBits (SerialAttributes *attributes, SerialStopBits bits) { |
| if (bits == SERIAL_STOP_1) { |
| attributes->StopBits = ONESTOPBIT; |
| } else if (bits == SERIAL_STOP_1_5) { |
| attributes->StopBits = ONE5STOPBITS; |
| } else if (bits == SERIAL_STOP_2) { |
| attributes->StopBits = TWOSTOPBITS; |
| } else { |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| int |
| serialPutParity (SerialAttributes *attributes, SerialParity parity) { |
| attributes->fParity = FALSE; |
| attributes->Parity = NOPARITY; |
| |
| if (parity != SERIAL_PARITY_NONE) { |
| switch (parity) { |
| case SERIAL_PARITY_ODD: |
| attributes->Parity = ODDPARITY; |
| break; |
| |
| case SERIAL_PARITY_EVEN: |
| attributes->Parity = EVENPARITY; |
| break; |
| |
| case SERIAL_PARITY_MARK: |
| attributes->Parity = MARKPARITY; |
| break; |
| |
| case SERIAL_PARITY_SPACE: |
| attributes->Parity = SPACEPARITY; |
| break; |
| |
| default: |
| return 0; |
| } |
| |
| attributes->fParity = TRUE; |
| } |
| |
| return 1; |
| } |
| |
| SerialFlowControl |
| serialPutFlowControl (SerialAttributes *attributes, SerialFlowControl flow) { |
| if (flow & SERIAL_FLOW_OUTPUT_RTS) { |
| flow &= ~SERIAL_FLOW_OUTPUT_RTS; |
| attributes->fRtsControl = RTS_CONTROL_TOGGLE; |
| } else if (flow & SERIAL_FLOW_INPUT_RTS) { |
| flow &= ~SERIAL_FLOW_INPUT_RTS; |
| attributes->fRtsControl = RTS_CONTROL_HANDSHAKE; |
| } else { |
| attributes->fRtsControl = RTS_CONTROL_ENABLE; |
| } |
| |
| if (flow & SERIAL_FLOW_INPUT_XON) { |
| flow &= ~SERIAL_FLOW_INPUT_XON; |
| attributes->fInX = TRUE; |
| } else { |
| attributes->fInX = FALSE; |
| } |
| |
| if (flow & SERIAL_FLOW_OUTPUT_CTS) { |
| flow &= ~SERIAL_FLOW_OUTPUT_CTS; |
| attributes->fOutxCtsFlow = TRUE; |
| } else { |
| attributes->fOutxCtsFlow = FALSE; |
| } |
| |
| if (flow & SERIAL_FLOW_OUTPUT_DSR) { |
| flow &= ~SERIAL_FLOW_OUTPUT_DSR; |
| attributes->fOutxDsrFlow = TRUE; |
| } else { |
| attributes->fOutxDsrFlow = FALSE; |
| } |
| |
| if (flow & SERIAL_FLOW_OUTPUT_XON) { |
| flow &= ~SERIAL_FLOW_OUTPUT_XON; |
| attributes->fOutX = TRUE; |
| } else { |
| attributes->fOutX = FALSE; |
| } |
| |
| return flow; |
| } |
| |
| int |
| serialPutModemState (SerialAttributes *attributes, int enabled) { |
| if (enabled) { |
| attributes->fDtrControl = DTR_CONTROL_HANDSHAKE; |
| attributes->fDsrSensitivity = TRUE; |
| } else { |
| attributes->fDtrControl = DTR_CONTROL_ENABLE; |
| attributes->fDsrSensitivity = FALSE; |
| } |
| |
| return 1; |
| } |
| |
| unsigned int |
| serialGetDataBits (const SerialAttributes *attributes) { |
| return attributes->ByteSize; |
| } |
| |
| unsigned int |
| serialGetStopBits (const SerialAttributes *attributes) { |
| if (attributes->StopBits == ONESTOPBIT) return 1; |
| if (attributes->StopBits == TWOSTOPBITS) return 2; |
| |
| logMessage(LOG_WARNING, "unsupported Windows serial stop bits value: %X", attributes->StopBits); |
| return 0; |
| } |
| |
| unsigned int |
| serialGetParityBits (const SerialAttributes *attributes) { |
| return (attributes->fParity && (attributes->Parity != NOPARITY))? 1: 0; |
| } |
| |
| int |
| serialGetAttributes (SerialDevice *serial, SerialAttributes *attributes) { |
| attributes->DCBlength = sizeof(serial->currentAttributes); |
| if (GetCommState(serial->package.fileHandle, attributes)) return 1; |
| logWindowsSystemError("GetCommState"); |
| return 0; |
| } |
| |
| int |
| serialPutAttributes (SerialDevice *serial, const SerialAttributes *attributes) { |
| if (SetCommState(serial->package.fileHandle, (SerialAttributes *)attributes)) return 1; |
| logWindowsSystemError("SetCommState"); |
| return 0; |
| } |
| |
| int |
| serialCancelInput (SerialDevice *serial) { |
| if (PurgeComm(serial->package.fileHandle, PURGE_RXCLEAR)) return 1; |
| logWindowsSystemError("PurgeComm"); |
| return 0; |
| } |
| |
| int |
| serialCancelOutput (SerialDevice *serial) { |
| if (PurgeComm(serial->package.fileHandle, PURGE_TXCLEAR)) return 1; |
| logWindowsSystemError("PurgeComm"); |
| return 0; |
| } |
| |
| int |
| serialMonitorInput (SerialDevice *serial, AsyncMonitorCallback *callback, void *data) { |
| return 0; |
| } |
| |
| int |
| serialPollInput (SerialDevice *serial, int timeout) { |
| if (serial->package.pendingCharacter != -1) return 1; |
| |
| { |
| COMMTIMEOUTS timeouts = {MAXDWORD, 0, timeout, 0, 0}; |
| DWORD bytesRead; |
| char c; |
| |
| if (!(SetCommTimeouts(serial->package.fileHandle, &timeouts))) { |
| logWindowsSystemError("SetCommTimeouts serialAwaitInput"); |
| setSystemErrno(); |
| return 0; |
| } |
| |
| if (!ReadFile(serial->package.fileHandle, &c, 1, &bytesRead, NULL)) { |
| logWindowsSystemError("ReadFile"); |
| setSystemErrno(); |
| return 0; |
| } |
| |
| if (bytesRead) { |
| serial->package.pendingCharacter = (unsigned char)c; |
| return 1; |
| } |
| } |
| errno = EAGAIN; |
| |
| return 0; |
| } |
| |
| int |
| serialDrainOutput (SerialDevice *serial) { |
| if (FlushFileBuffers(serial->package.fileHandle)) return 1; |
| logWindowsSystemError("FlushFileBuffers"); |
| return 0; |
| } |
| |
| ssize_t |
| serialGetData ( |
| SerialDevice *serial, |
| void *buffer, size_t size, |
| int initialTimeout, int subsequentTimeout |
| ) { |
| size_t length = 0; |
| COMMTIMEOUTS timeouts = {MAXDWORD, 0, initialTimeout, 0, 0}; |
| DWORD bytesRead; |
| |
| if (serial->package.pendingCharacter != -1) { |
| * (unsigned char *) buffer = serial->package.pendingCharacter; |
| serial->package.pendingCharacter = -1; |
| bytesRead = 1; |
| } else { |
| if (!(SetCommTimeouts(serial->package.fileHandle, &timeouts))) { |
| logWindowsSystemError("SetCommTimeouts serialReadChunk1"); |
| setSystemErrno(); |
| return -1; |
| } |
| |
| if (!ReadFile(serial->package.fileHandle, buffer, size, &bytesRead, NULL)) { |
| logWindowsSystemError("ReadFile"); |
| setSystemErrno(); |
| return -1; |
| } |
| |
| if (!bytesRead) return 0; |
| } |
| |
| size -= bytesRead; |
| length += bytesRead; |
| timeouts.ReadTotalTimeoutConstant = subsequentTimeout; |
| |
| if (!(SetCommTimeouts(serial->package.fileHandle, &timeouts))) { |
| logWindowsSystemError("SetCommTimeouts serialReadChunk2"); |
| setSystemErrno(); |
| return -1; |
| } |
| |
| while (size && ReadFile(serial->package.fileHandle, buffer + length, size, &bytesRead, NULL)) { |
| if (!bytesRead) return length; |
| size -= bytesRead; |
| length += bytesRead; |
| } |
| |
| if (!size) return length; |
| logWindowsSystemError("ReadFile"); |
| setSystemErrno(); |
| return -1; |
| } |
| |
| ssize_t |
| serialPutData ( |
| SerialDevice *serial, |
| const void *data, size_t size |
| ) { |
| COMMTIMEOUTS timeouts = {MAXDWORD, 0, 0, 0, 15000}; |
| size_t left = size; |
| DWORD bytesWritten; |
| |
| if (!(SetCommTimeouts(serial->package.fileHandle, &timeouts))) { |
| logWindowsSystemError("SetCommTimeouts serialWriteData"); |
| setSystemErrno(); |
| return -1; |
| } |
| |
| while (left && WriteFile(serial->package.fileHandle, data, left, &bytesWritten, NULL)) { |
| if (!bytesWritten) break; |
| left -= bytesWritten; |
| data += bytesWritten; |
| } |
| |
| if (!left) return size; |
| logWindowsSystemError("WriteFile"); |
| return -1; |
| } |
| |
| int |
| serialGetLines (SerialDevice *serial) { |
| if (!GetCommModemStatus(serial->package.fileHandle, &serial->linesState)) { |
| logWindowsSystemError("GetCommModemStatus"); |
| return 0; |
| } |
| |
| { |
| DCB dcb; |
| dcb.DCBlength = sizeof(dcb); |
| |
| if (!GetCommState(serial->package.fileHandle, &dcb)) { |
| logWindowsSystemError("GetCommState"); |
| return 0; |
| } |
| |
| if (dcb.fRtsControl == RTS_CONTROL_ENABLE) serial->linesState |= SERIAL_LINE_RTS; |
| if (dcb.fDtrControl == DTR_CONTROL_ENABLE) serial->linesState |= SERIAL_LINE_DTR; |
| } |
| |
| return 1; |
| } |
| |
| int |
| serialPutLines (SerialDevice *serial, SerialLines high, SerialLines low) { |
| DCB dcb; |
| dcb.DCBlength = sizeof(dcb); |
| |
| if (GetCommState(serial->package.fileHandle, &dcb)) { |
| if (low & SERIAL_LINE_RTS) { |
| dcb.fRtsControl = RTS_CONTROL_DISABLE; |
| } else if (high & SERIAL_LINE_RTS) { |
| dcb.fRtsControl = RTS_CONTROL_ENABLE; |
| } |
| |
| if (low & SERIAL_LINE_DTR) { |
| dcb.fDtrControl = DTR_CONTROL_DISABLE; |
| } else if (high & SERIAL_LINE_DTR) { |
| dcb.fDtrControl = DTR_CONTROL_ENABLE; |
| } |
| |
| if (SetCommState(serial->package.fileHandle, &dcb)) return 1; |
| logWindowsSystemError("SetCommState"); |
| } else { |
| logWindowsSystemError("GetCommState"); |
| } |
| |
| return 0; |
| } |
| |
| int |
| serialRegisterWaitLines (SerialDevice *serial, SerialLines lines) { |
| DWORD eventMask = 0; |
| |
| if (lines & SERIAL_LINE_CTS) eventMask |= EV_CTS; |
| if (lines & SERIAL_LINE_DSR) eventMask |= EV_DSR; |
| if (lines & SERIAL_LINE_RNG) eventMask |= EV_RING; |
| if (lines & SERIAL_LINE_CAR) eventMask |= EV_RLSD; |
| |
| if (SetCommMask(serial->package.fileHandle, eventMask)) return 1; |
| logWindowsSystemError("SetCommMask"); |
| return 0; |
| } |
| |
| int |
| serialMonitorWaitLines (SerialDevice *serial) { |
| DWORD event; |
| |
| if (WaitCommEvent(serial->package.fileHandle, &event, NULL)) return 1; |
| logWindowsSystemError("WaitCommEvent"); |
| return 0; |
| } |
| |
| int |
| serialConnectDevice (SerialDevice *serial, const char *device) { |
| if ((serial->package.fileHandle = CreateFile(device, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE) { |
| serial->package.pendingCharacter = -1; |
| |
| if (serialPrepareDevice(serial)) { |
| logMessage(LOG_CATEGORY(SERIAL_IO), "device opened: %s: fh=%" PRIfd, |
| device, serial->package.fileHandle); |
| return 1; |
| } |
| |
| CloseHandle(serial->package.fileHandle); |
| } else { |
| logWindowsSystemError("CreateFile"); |
| logMessage(LOG_ERR, "cannot open serial device: %s", device); |
| } |
| |
| return 0; |
| } |
| |
| void |
| serialDisconnectDevice (SerialDevice *serial) { |
| CloseHandle(serial->package.fileHandle); |
| } |
| |
| int |
| serialEnsureFileDescriptor (SerialDevice *serial) { |
| #ifdef __CYGWIN__ |
| if ((serial->fileDescriptor = cygwin_attach_handle_to_fd("serialdevice", -1, serial->package.fileHandle, TRUE, GENERIC_READ|GENERIC_WRITE)) >= 0) return 1; |
| logSystemError("cygwin_attach_handle_to_fd"); |
| #else /* __CYGWIN__ */ |
| if ((serial->fileDescriptor = _open_osfhandle((long)serial->package.fileHandle, O_RDWR)) >= 0) return 1; |
| logSystemError("open_osfhandle"); |
| #endif /* __CYGWIN__ */ |
| |
| return 0; |
| } |
| |
| void |
| serialClearError (SerialDevice *serial) { |
| ClearCommError(serial->package.fileHandle, NULL, NULL); |
| } |
| |