/* * 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 #include "log.h" #include "brl_driver.h" static unsigned char brailleCells[0XFF]; static wchar_t visualText[0XFF]; #include "io_serial.h" static SerialDevice *serialDevice = NULL; static unsigned int charactersPerSecond; static int readPacket (BrailleDisplay *brl, unsigned char *packet, int length) { size_t offset = 0; int size = -1; while (offset < length) { const unsigned char *byte = &packet[offset]; if (!serialReadChunk(serialDevice, packet, &offset, 1, 0, 1000)) { if (errno == EAGAIN) { if (!offset) return 0; logPartialPacket(packet, offset); } return -1; } if (offset == 1) { if (*byte) { logDiscardedByte(packet[0]); offset = 0; } } else { if (offset == 2) { switch (*byte) { default: size = 1; break; } size += offset; } if (offset == size) { logInputPacket(packet, offset); return offset; } } } logTruncatedPacket(packet, offset); return 0; } static int writePacket (BrailleDisplay *brl, unsigned char function, unsigned char *data, unsigned char count) { unsigned char buffer[count + 4]; unsigned char *byte = buffer; *byte++ = 0; *byte++ = function; *byte++ = count; byte = mempcpy(byte, data, count); { unsigned char checksum = 0; const unsigned char *ptr = buffer; while (ptr < byte) checksum ^= *ptr++; *byte++ = checksum; } { int size = byte - buffer; logOutputPacket(buffer, size); brl->writeDelay += (count * 1000 / charactersPerSecond) + 1; if (serialWriteData(serialDevice, buffer, size) != -1) return 1; } logSystemError("serial write"); return 0; } static int writeBrailleCells (BrailleDisplay *brl) { size_t count = brl->textColumns; unsigned char cells[count]; translateOutputCells(cells, brailleCells, count); return writePacket(brl, 1, cells, count); } static int clearBrailleCells (BrailleDisplay *brl) { memset(brailleCells, 0, brl->textColumns); return writeBrailleCells(brl); } static int writeVisualText (BrailleDisplay *brl) { unsigned char bytes[brl->textColumns]; int i; for (i=0; itextColumns; ++i) { wchar_t character = visualText[i]; bytes[i] = iswLatin1(character)? character: '?'; } return writePacket(brl, 2, bytes, brl->textColumns); } static int clearVisualText (BrailleDisplay *brl) { wmemset(visualText, WC_C(' '), brl->textColumns); return writeVisualText(brl); } static int brl_construct (BrailleDisplay *brl, char **parameters, const char *device) { if (!isSerialDeviceIdentifier(&device)) { unsupportedDeviceIdentifier(device); return 0; } if ((serialDevice = serialOpenDevice(device))) { unsigned int baud = 19200; charactersPerSecond = baud / 11; if (serialRestartDevice(serialDevice, baud)) { if (serialSetParity(serialDevice, SERIAL_PARITY_EVEN)) { if (writePacket(brl, 4, NULL, 0)) { while (serialAwaitInput(serialDevice, 500)) { unsigned char response[3]; int size = readPacket(brl, response, sizeof(response)); if (size <= 0) break; if (response[1] == 4) { brl->textColumns = response[2]; brl->textRows = 1; makeOutputTable(dotsTable_ISO11548_1); makeInputTable(); if (!clearBrailleCells(brl)) break; if (!clearVisualText(brl)) break; if (!writeBrailleCells(brl)) break; return 1; } } } } } serialCloseDevice(serialDevice); serialDevice = NULL; } return 0; } static void brl_destruct (BrailleDisplay *brl) { if (serialDevice) { serialCloseDevice(serialDevice); serialDevice = NULL; } } static int brl_writeWindow (BrailleDisplay *brl, const wchar_t *text) { if (text) { if (wmemcmp(text, visualText, brl->textColumns) != 0) { wmemcpy(visualText, text, brl->textColumns); if (!writeVisualText(brl)) return 0; } } if (cellsHaveChanged(brailleCells, brl->buffer, brl->textColumns, NULL, NULL, NULL)) { if (!writeBrailleCells(brl)) return 0; } return 1; } static int brl_readCommand (BrailleDisplay *brl, KeyTableCommandContext context) { while (1) { unsigned char packet[3]; int size = readPacket(brl, packet, sizeof(packet)); if (size == 0) break; if (size < 0) return BRL_CMD_RESTARTBRL; switch (packet[1]) { default: break; case 1: return BRL_CMD_BLK(PASSDOTS) | translateInputCell(packet[2]); case 2: { unsigned char column = packet[2]; if (column && (column <= brl->textColumns)) return BRL_CMD_BLK(ROUTE) + (column - 1); break; } case 3: switch (packet[2]) { default: break; // left rear: two columns, one row case 0X02: // ESC return BRL_CMD_LEARN; case 0X01: // M return BRL_CMD_PREFMENU; // left middle: cross case 0X06: // up return BRL_CMD_LNUP; case 0X03: // left return BRL_CMD_FWINLT; case 0X05: // right return BRL_CMD_FWINRT; case 0X04: // down return BRL_CMD_LNDN; // left front: two columns, three rows case 0X09: // ins return BRL_CMD_RETURN; case 0X0A: // E return BRL_CMD_TOP; case 0X0B: // supp return BRL_CMD_CSRTRK; case 0X0C: // L return BRL_CMD_BOT; case 0X07: // extra 1 (40s only) return BRL_CMD_CHRLT; case 0X08: // extra 2 (40s only) return BRL_CMD_CHRRT; case 0x0E: // left thumb return BRL_CMD_KEY(BACKSPACE); case 0x0F: // right thumb return BRL_CMD_BLK(PASSDOTS); case 0x3F: // both thumbs return BRL_CMD_KEY(ENTER); case 0X29: // key under dot 7 return BRL_CMD_KEY(ESCAPE); case 0X2A: // key under dot 8 return BRL_CMD_KEY(TAB); // right rear: one column, one row case 0X19: // extra 3 (40s only) return BRL_CMD_INFO; // right middle: one column, two rows case 0X1B: // extra 4 (40s only) return BRL_CMD_PRDIFLN; case 0X1A: // extra 5 (40s only) return BRL_CMD_NXDIFLN; // right front: one column, four rows case 0X2B: // slash (40s only) return BRL_CMD_FREEZE; case 0X2C: // asterisk (40s only) return BRL_CMD_DISPMD; case 0X2D: // minus (40s only) return BRL_CMD_ATTRVIS; case 0X2E: // plus (40s only) return BRL_CMD_CSRVIS; // first (top) row of numeric pad case 0X37: // seven (40s only) return BRL_CMD_KEY(HOME); case 0X38: // eight (40s only) return BRL_CMD_KEY(CURSOR_UP); case 0X39: // nine (40s only) return BRL_CMD_KEY(PAGE_UP); // second row of numeric pad case 0X34: // four (40s only) return BRL_CMD_KEY(CURSOR_LEFT); case 0X35: // five (40s only) return BRL_CMD_CSRJMP_VERT; case 0X36: // six (40s only) return BRL_CMD_KEY(CURSOR_RIGHT); // third row of numeric pad case 0X31: // one (40s only) return BRL_CMD_KEY(END); case 0X32: // two (40s only) return BRL_CMD_KEY(CURSOR_DOWN); case 0X33: // three (40s only) return BRL_CMD_KEY(PAGE_DOWN); // fourth (bottom) row of numeric pad case 0X28: // verr num (40s only) return BRL_CMD_SIXDOTS; case 0X30: // zero (40s only) return BRL_CMD_KEY(INSERT); case 0X2F: // supp (40s only) return BRL_CMD_KEY(DELETE); } break; /* When data is written to the display it acknowledges with: * 0X00 0X04 0Xxx * where xx is the number of bytes written. */ case 4: continue; } logUnexpectedPacket(packet, size); } return EOF; }