| /* |
| * 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 <string.h> |
| |
| #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; i<brl->textColumns; 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; |
| } |