| /* |
| * 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 <string.h> |
| #include <errno.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| |
| #include "log.h" |
| #include "async_wait.h" |
| #include "timing.h" |
| #include "io_misc.h" |
| #include "ports.h" |
| |
| #include "serial_msdos.h" |
| #include "serial_internal.h" |
| |
| #include <dos.h> |
| #include <dpmi.h> |
| #include <bios.h> |
| #include <go32.h> |
| #include <sys/farptr.h> |
| |
| #define SERIAL_DIVISOR_BASE 115200 |
| #define SERIAL_DIVISOR(baud) (SERIAL_DIVISOR_BASE / (baud)) |
| #define SERIAL_SPEED(baud) {.divisor=SERIAL_DIVISOR(baud), .bps=SERIAL_BIOS_BAUD_##baud} |
| #define SERIAL_BAUD_ENTRY(baud) {baud, .speed=SERIAL_SPEED(baud)} |
| |
| BEGIN_SERIAL_BAUD_TABLE |
| SERIAL_BAUD_ENTRY(110), |
| SERIAL_BAUD_ENTRY(150), |
| SERIAL_BAUD_ENTRY(300), |
| SERIAL_BAUD_ENTRY(600), |
| SERIAL_BAUD_ENTRY(1200), |
| SERIAL_BAUD_ENTRY(2400), |
| SERIAL_BAUD_ENTRY(4800), |
| SERIAL_BAUD_ENTRY(9600), |
| SERIAL_BAUD_ENTRY(19200), |
| SERIAL_BAUD_ENTRY(38400), |
| SERIAL_BAUD_ENTRY(57600), |
| SERIAL_BAUD_ENTRY(115200), |
| END_SERIAL_BAUD_TABLE |
| |
| static inline int |
| serialGetPort (SerialDevice *serial) { |
| return serial->package.deviceIndex; |
| } |
| |
| static unsigned short |
| serialPortBase (SerialDevice *serial) { |
| return _farpeekw(_dos_ds, (0X0400 + (2 * serialGetPort(serial)))); |
| } |
| |
| static unsigned char |
| serialReadPort (SerialDevice *serial, unsigned char port) { |
| return readPort1(serialPortBase(serial)+port); |
| } |
| |
| static void |
| serialWritePort (SerialDevice *serial, unsigned char port, unsigned char value) { |
| writePort1(serialPortBase(serial)+port, value); |
| } |
| |
| static unsigned int |
| serialBiosCommand (SerialDevice *serial, int command, unsigned char data) { |
| return _bios_serialcom(command, serialGetPort(serial), data); |
| } |
| |
| static int |
| serialTestInput (SerialDevice *serial) { |
| return !!(serialBiosCommand(serial, _COM_STATUS, 0) & SERIAL_BIOS_STATUS_DATA_READY); |
| } |
| |
| void |
| serialPutInitialAttributes (SerialAttributes *attributes) { |
| attributes->speed = serialGetBaudEntry(9600)->speed; |
| attributes->bios.fields.bps = attributes->speed.bps; |
| attributes->bios.fields.dataBits = SERIAL_BIOS_DATA_8; |
| attributes->bios.fields.stopBits = SERIAL_BIOS_STOP_1; |
| attributes->bios.fields.parity = SERIAL_BIOS_PARITY_NONE; |
| } |
| |
| int |
| serialPutSpeed (SerialAttributes *attributes, SerialSpeed speed) { |
| logMessage(LOG_CATEGORY(SERIAL_IO), "put speed: bps=%u divisor=%u", |
| speed.bps, speed.divisor); |
| |
| attributes->speed = speed; |
| attributes->bios.fields.bps = attributes->speed.bps; |
| return 1; |
| } |
| |
| int |
| serialPutDataBits (SerialAttributes *attributes, unsigned int bits) { |
| if (bits == 8) { |
| attributes->bios.fields.dataBits = SERIAL_BIOS_DATA_8; |
| } else if (bits == 7) { |
| attributes->bios.fields.dataBits = SERIAL_BIOS_DATA_7; |
| } else { |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| int |
| serialPutStopBits (SerialAttributes *attributes, SerialStopBits bits) { |
| if (bits == SERIAL_STOP_1) { |
| attributes->bios.fields.stopBits = SERIAL_BIOS_STOP_1; |
| } else if (bits == SERIAL_STOP_2) { |
| attributes->bios.fields.stopBits = SERIAL_BIOS_STOP_2; |
| } else { |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| int |
| serialPutParity (SerialAttributes *attributes, SerialParity parity) { |
| switch (parity) { |
| case SERIAL_PARITY_NONE: |
| attributes->bios.fields.parity = SERIAL_BIOS_PARITY_NONE; |
| break; |
| |
| case SERIAL_PARITY_ODD: |
| attributes->bios.fields.parity = SERIAL_BIOS_PARITY_ODD; |
| break; |
| |
| case SERIAL_PARITY_EVEN: |
| attributes->bios.fields.parity = SERIAL_BIOS_PARITY_EVEN; |
| break; |
| |
| default: |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| SerialFlowControl |
| serialPutFlowControl (SerialAttributes *attributes, SerialFlowControl flow) { |
| return flow; |
| } |
| |
| int |
| serialPutModemState (SerialAttributes *attributes, int enabled) { |
| return !enabled; |
| } |
| |
| unsigned int |
| serialGetDataBits (const SerialAttributes *attributes) { |
| switch (attributes->bios.fields.dataBits) { |
| default: |
| case SERIAL_BIOS_DATA_8: return 8; |
| case SERIAL_BIOS_DATA_7: return 7; |
| } |
| } |
| |
| unsigned int |
| serialGetStopBits (const SerialAttributes *attributes) { |
| switch (attributes->bios.fields.stopBits) { |
| default: |
| case SERIAL_BIOS_STOP_1: return 1; |
| case SERIAL_BIOS_STOP_2: return 2; |
| } |
| } |
| |
| unsigned int |
| serialGetParityBits (const SerialAttributes *attributes) { |
| return (attributes->bios.fields.parity == SERIAL_BIOS_PARITY_NONE)? 0: 1; |
| } |
| |
| int |
| serialGetAttributes (SerialDevice *serial, SerialAttributes *attributes) { |
| unsigned char lcr; |
| int divisor; |
| |
| { |
| int wasEnabled = disable(); |
| |
| lcr = serialReadPort(serial, UART_PORT_LCR); |
| serialWritePort(serial, UART_PORT_LCR, (lcr | UART_FLAG_LCR_DLAB)); |
| |
| divisor = (serialReadPort(serial, UART_PORT_DLH) << 8) | |
| serialReadPort(serial, UART_PORT_DLL); |
| serialWritePort(serial, UART_PORT_LCR, lcr); |
| |
| if (wasEnabled) enable(); |
| } |
| |
| attributes->bios.byte = lcr; |
| |
| { |
| const SerialBaudEntry *baud = serialGetBaudEntry(SERIAL_DIVISOR_BASE/divisor); |
| |
| if (baud) { |
| attributes->speed = baud->speed; |
| } else { |
| logMessage(LOG_WARNING, "unsupported serial divisor: %d", divisor); |
| memset(&attributes->speed, 0, sizeof(attributes->speed)); |
| } |
| } |
| |
| attributes->bios.fields.bps = attributes->speed.bps; |
| return 1; |
| } |
| |
| int |
| serialPutAttributes (SerialDevice *serial, const SerialAttributes *attributes) { |
| if (attributes->speed.bps < (0X1 << 3)) { |
| unsigned char byte = attributes->bios.byte; |
| |
| logMessage(LOG_CATEGORY(SERIAL_IO), "put attributes: port=%d byte=0X%02X", |
| serialGetPort(serial), byte); |
| serialBiosCommand(serial, _COM_INIT, byte); |
| } else { |
| SerialBiosConfiguration lcr = attributes->bios; |
| |
| lcr.fields.bps = 0; |
| logMessage(LOG_CATEGORY(SERIAL_IO), "put attributes: port=%d lcr=0X%02X divisor=%u", |
| serialGetPort(serial), lcr.byte, attributes->speed.divisor); |
| |
| { |
| int wasEnabled = disable(); |
| |
| serialWritePort(serial, UART_PORT_LCR, (lcr.byte | UART_FLAG_LCR_DLAB)); |
| serialWritePort(serial, UART_PORT_DLL, (attributes->speed.divisor & 0XFF)); |
| serialWritePort(serial, UART_PORT_DLH, (attributes->speed.divisor >> 8)); |
| serialWritePort(serial, UART_PORT_LCR, lcr.byte); |
| |
| if (wasEnabled) enable(); |
| } |
| } |
| |
| return 1; |
| } |
| |
| int |
| serialCancelInput (SerialDevice *serial) { |
| return 1; |
| } |
| |
| int |
| serialCancelOutput (SerialDevice *serial) { |
| return 1; |
| } |
| |
| int |
| serialMonitorInput (SerialDevice *serial, AsyncMonitorCallback *callback, void *data) { |
| return 0; |
| } |
| |
| int |
| serialPollInput (SerialDevice *serial, int timeout) { |
| TimePeriod period; |
| |
| if (timeout) startTimePeriod(&period, timeout); |
| |
| while (1) { |
| if (serialTestInput(serial)) return 1; |
| if (!timeout) break; |
| if (afterTimePeriod(&period, NULL)) break; |
| asyncWait(1); |
| } |
| |
| errno = EAGAIN; |
| return 0; |
| } |
| |
| int |
| serialDrainOutput (SerialDevice *serial) { |
| return 1; |
| } |
| |
| ssize_t |
| serialGetData ( |
| SerialDevice *serial, |
| void *buffer, size_t size, |
| int initialTimeout, int subsequentTimeout |
| ) { |
| unsigned char *const start = buffer; |
| unsigned char *const end = start + size; |
| unsigned char *byte = start; |
| int timeout = initialTimeout; |
| |
| while (byte < end) { |
| if (!serialPollInput(serial, timeout)) break; |
| timeout = subsequentTimeout; |
| |
| { |
| int status = serialBiosCommand(serial, _COM_RECEIVE, 0); |
| |
| *byte++ = status & 0XFF; |
| } |
| } |
| |
| return byte - start; |
| } |
| |
| ssize_t |
| serialPutData ( |
| SerialDevice *serial, |
| const void *data, size_t size |
| ) { |
| return writeFile(serial->fileDescriptor, data, size); |
| } |
| |
| int |
| serialGetLines (SerialDevice *serial) { |
| serial->linesState = serialReadPort(serial, UART_PORT_MSR) & 0XF0; |
| return 1; |
| } |
| |
| int |
| serialPutLines (SerialDevice *serial, SerialLines high, SerialLines low) { |
| int wasEnabled = disable(); |
| unsigned char oldMCR = serialReadPort(serial, UART_PORT_MCR); |
| |
| serialWritePort(serial, UART_PORT_MCR, |
| (oldMCR | high) & ~low); |
| if (wasEnabled) enable(); |
| return 1; |
| } |
| |
| int |
| serialRegisterWaitLines (SerialDevice *serial, SerialLines lines) { |
| return 1; |
| } |
| |
| int |
| serialMonitorWaitLines (SerialDevice *serial) { |
| return 0; |
| } |
| |
| int |
| serialConnectDevice (SerialDevice *serial, const char *device) { |
| if ((serial->fileDescriptor = open(device, O_RDWR|O_NOCTTY|O_NONBLOCK)) != -1) { |
| serial->package.deviceIndex = -1; |
| |
| { |
| char *truePath; |
| |
| if ((truePath = _truename(device, NULL))) { |
| char *com; |
| |
| { |
| char *c = truePath; |
| |
| while (*c) { |
| *c = toupper(*c); |
| c += 1; |
| } |
| } |
| |
| if ((com = strstr(truePath, "COM"))) { |
| serial->package.deviceIndex = atoi(com+3) - 1; |
| } |
| |
| free(truePath); |
| } |
| } |
| |
| if (serial->package.deviceIndex >= 0) { |
| if (serialPrepareDevice(serial)) { |
| logMessage(LOG_CATEGORY(SERIAL_IO), "device opened: %s: fd=%d", |
| device, serial->fileDescriptor); |
| return 1; |
| } |
| } else { |
| logMessage(LOG_ERR, "could not determine serial device number: %s", device); |
| } |
| |
| close(serial->fileDescriptor); |
| } else { |
| logMessage(LOG_ERR, "cannot open serial device: %s: %s", device, strerror(errno)); |
| } |
| |
| return 0; |
| } |
| |
| void |
| serialDisconnectDevice (SerialDevice *serial) { |
| } |
| |
| int |
| serialEnsureFileDescriptor (SerialDevice *serial) { |
| return 1; |
| } |
| |
| void |
| serialClearError (SerialDevice *serial) { |
| } |
| |