blob: d2a1a7ac14ebf69ca62fd826a95c7846ab1f2dd6 [file] [log] [blame] [edit]
/*
* 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) {
}