/* * 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-2021 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 . */ #include "prologue.h" #include #include #include "io_serial.h" typedef enum { PARM_BAUD } DriverParameter; #define BRLPARMS "baud" #define BRL_STATUS_FIELDS sfGeneric #define BRL_HAVE_STATUS_CELLS #include "brl_driver.h" static const int showOutputMapping = 0; static SerialDevice *serialDevice = NULL; static unsigned int serialBaud; static unsigned int charactersPerSecond; static const int *initialCommand; typedef enum { IPT_MINIMUM_LINE = 1, IPT_MAXIMUM_LINE = 25, IPT_SEARCH_ATTRIBUTE = 90, IPT_CURRENT_LINE = 100, IPT_CURRENT_LOCATION = 101, } InputPacketType; typedef union { unsigned char bytes[4]; struct { unsigned char type; union { struct { unsigned char line; unsigned char column; unsigned char attributes; } PACKED search; } fields; } PACKED data; } InputPacket; typedef int (*WriteFunction) (BrailleDisplay *brl); static WriteFunction writeFunction; static unsigned char statusCells[GSC_COUNT]; static int brl_construct (BrailleDisplay *brl, char **parameters, const char *device) { { static const unsigned int baudTable[] = {9600, 19200, 0}; const char *baudParameter = parameters[PARM_BAUD]; if (!*baudParameter || !serialValidateBaud(&serialBaud, "baud", baudParameter, baudTable)) serialBaud = baudTable[0]; } if (!isSerialDeviceIdentifier(&device)) { unsupportedDeviceIdentifier(device); return 0; } if ((serialDevice = serialOpenDevice(device))) { if (serialRestartDevice(serialDevice, serialBaud)) { charactersPerSecond = serialBaud / 10; writeFunction = NULL; { static const TranslationTable outputTable = { #define MAP(byte,cell) [cell] = byte #include "brl-out.h" #undef MAP }; setOutputTable(outputTable); } { static const int initialCommands[] = { BRL_CMD_TUNES | BRL_FLG_TOGGLE_OFF, BRL_CMD_CSRTRK | BRL_FLG_TOGGLE_OFF, BRL_CMD_CSRVIS | BRL_FLG_TOGGLE_OFF, BRL_CMD_ATTRVIS | BRL_FLG_TOGGLE_OFF, EOF }; initialCommand = initialCommands; } brl->textColumns = 80; return 1; } serialCloseDevice(serialDevice); serialDevice = NULL; } return 0; } static void brl_destruct (BrailleDisplay *brl) { if (serialDevice) { serialCloseDevice(serialDevice); serialDevice = NULL; } } static int writePacket (BrailleDisplay *brl, const unsigned char *packet, size_t size) { logOutputPacket(packet, size); brl->writeDelay += (size * 1000 / charactersPerSecond) + 1; return serialWriteData(serialDevice, packet, size) != -1; } static int writeLine (BrailleDisplay *brl) { unsigned char packet[2 + (brl->textColumns * 2)]; unsigned char *byte = packet; *byte++ = statusCells[gscScreenCursorRow]; *byte++ = statusCells[gscScreenCursorColumn]; { int i; for (i=0; itextColumns; i+=1) { *byte++ = translateOutputCell(brl->buffer[i]); *byte++ = 0X07; } } if (showOutputMapping) { int row = statusCells[gscBrailleWindowRow]; if (--row < 0X10) { int column; for (column=0; column<80; column+=1) packet[2+(column*2)] = ' '; for (column=0; column<0X10; column+=1) { unsigned char *byte = &packet[2 + (column * 8)]; static const unsigned char hex[] = "0123456789ABCDEF"; *byte = hex[row]; byte += 2; *byte = hex[column]; byte += 2; *byte = (row << 4) | column; } } } return writePacket(brl, packet, byte-packet); } static int writeLocation (BrailleDisplay *brl) { unsigned char packet[2]; unsigned char *byte = packet; *byte++ = statusCells[gscScreenCursorRow]; *byte++ = statusCells[gscScreenCursorColumn]; return writePacket(brl, packet, byte-packet); } static int brl_writeWindow (BrailleDisplay *brl, const wchar_t *text) { if (writeFunction) { int ok = writeFunction(brl); writeFunction = NULL; if (!ok) return 0; } return 1; } static int brl_writeStatus (BrailleDisplay *brl, const unsigned char *status) { memcpy(statusCells, status, GSC_COUNT); return 1; } static int readByte (unsigned char *byte, int wait) { const int timeout = 100; ssize_t result = serialReadData(serialDevice, byte, sizeof(*byte), (wait? timeout: 0), timeout); if (result > 0) return 1; if (result == 0) errno = EAGAIN; return 0; } static int readPacket (BrailleDisplay *brl, InputPacket *packet) { int length = 1; int offset = 0; while (1) { unsigned char byte; { int started = offset > 0; if (!readByte(&byte, started)) { if (started) logPartialPacket(packet->bytes, offset); return 0; } } if (!offset) { switch (byte) { case IPT_CURRENT_LINE: case IPT_CURRENT_LOCATION: length = 1; break; case IPT_SEARCH_ATTRIBUTE: length = 4; break; default: if ((byte >= IPT_MINIMUM_LINE) && (byte <= IPT_MAXIMUM_LINE)) { length = 1; } else { logIgnoredByte(byte); continue; } break; } } packet->bytes[offset++] = byte; if (offset == length) { logInputPacket(packet->bytes, offset); return length; } } } static int brl_readCommand (BrailleDisplay *brl, KeyTableCommandContext context) { InputPacket packet; int length; if (context == KTB_CTX_WAITING) return BRL_CMD_NOOP; if (writeFunction) return EOF; while (*initialCommand != EOF) enqueueCommand(*initialCommand++); while ((length = readPacket(brl, &packet))) { if ((packet.data.type >= IPT_MINIMUM_LINE) && (packet.data.type <= IPT_MAXIMUM_LINE)) { enqueueCommand(BRL_CMD_BLK(GOTOLINE) | BRL_FLG_MOTION_TOLEFT | (packet.data.type - IPT_MINIMUM_LINE)); writeFunction = writeLine; return EOF; } switch (packet.data.type) { case IPT_SEARCH_ATTRIBUTE: case IPT_CURRENT_LINE: enqueueCommand(BRL_CMD_HOME); enqueueCommand(BRL_CMD_LNBEG); writeFunction = writeLine; return EOF; case IPT_CURRENT_LOCATION: writeFunction = writeLocation; return EOF; default: logUnexpectedPacket(&packet, length); break; } } return (errno == EAGAIN)? EOF: BRL_CMD_RESTARTBRL; }