/* * 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 . */ /* Albatross/braille.c - Braille display library * Tivomatic's Albatross series * Author: Dave Mielke */ #include "prologue.h" #include #include #include #include #include "log.h" #include "timing.h" #include "async_wait.h" #define BRL_HAVE_STATUS_CELLS #include "brl_driver.h" #include "brldefs-at.h" BEGIN_KEY_NAME_TABLE(all) /* front left keys */ KEY_NAME_ENTRY(AT_KEY_Home1, "Home1"), KEY_NAME_ENTRY(AT_KEY_End1, "End1"), KEY_NAME_ENTRY(AT_KEY_ExtraCursor1, "ExtraCursor1"), KEY_NAME_ENTRY(AT_KEY_Cursor1, "Cursor1"), KEY_NAME_ENTRY(AT_KEY_Up1, "Up1"), KEY_NAME_ENTRY(AT_KEY_Down1, "Down1"), KEY_NAME_ENTRY(AT_KEY_Left, "Left"), /* front right keys */ KEY_NAME_ENTRY(AT_KEY_Home2, "Home2"), KEY_NAME_ENTRY(AT_KEY_End2, "End2"), KEY_NAME_ENTRY(AT_KEY_ExtraCursor2, "ExtraCursor2"), KEY_NAME_ENTRY(AT_KEY_Cursor2, "Cursor2"), KEY_NAME_ENTRY(AT_KEY_Up2, "Up2"), KEY_NAME_ENTRY(AT_KEY_Down2, "Down2"), KEY_NAME_ENTRY(AT_KEY_Right, "Right"), /* front middle keys */ KEY_NAME_ENTRY(AT_KEY_Up3, "Up3"), KEY_NAME_ENTRY(AT_KEY_Down3, "Down3"), /* top left keys */ KEY_NAME_ENTRY(AT_KEY_F1, "F1"), KEY_NAME_ENTRY(AT_KEY_F2, "F2"), KEY_NAME_ENTRY(AT_KEY_F3, "F3"), KEY_NAME_ENTRY(AT_KEY_F4, "F4"), KEY_NAME_ENTRY(AT_KEY_F5, "F5"), KEY_NAME_ENTRY(AT_KEY_F6, "F6"), KEY_NAME_ENTRY(AT_KEY_F7, "F7"), KEY_NAME_ENTRY(AT_KEY_F8, "F8"), /* top right keys */ KEY_NAME_ENTRY(AT_KEY_F9, "F9"), KEY_NAME_ENTRY(AT_KEY_F10, "F10"), KEY_NAME_ENTRY(AT_KEY_F11, "F11"), KEY_NAME_ENTRY(AT_KEY_F12, "F12"), KEY_NAME_ENTRY(AT_KEY_F13, "F13"), KEY_NAME_ENTRY(AT_KEY_F14, "F14"), KEY_NAME_ENTRY(AT_KEY_F15, "F15"), KEY_NAME_ENTRY(AT_KEY_F16, "F16"), /* attribute keys */ KEY_NAME_ENTRY(AT_KEY_Attribute1, "Attribute1"), KEY_NAME_ENTRY(AT_KEY_Attribute2, "Attribute2"), KEY_NAME_ENTRY(AT_KEY_Attribute3, "Attribute3"), KEY_NAME_ENTRY(AT_KEY_Attribute4, "Attribute4"), /* wheels */ KEY_NAME_ENTRY(AT_KEY_LeftWheelRight, "LeftWheelRight"), KEY_NAME_ENTRY(AT_KEY_LeftWheelLeft, "LeftWheelLeft"), KEY_NAME_ENTRY(AT_KEY_LeftWheelUp, "LeftWheelUp"), KEY_NAME_ENTRY(AT_KEY_LeftWheelDown, "LeftWheelDown"), KEY_NAME_ENTRY(AT_KEY_RightWheelRight, "RightWheelRight"), KEY_NAME_ENTRY(AT_KEY_RightWheelLeft, "RightWheelLeft"), KEY_NAME_ENTRY(AT_KEY_RightWheelUp, "RightWheelUp"), KEY_NAME_ENTRY(AT_KEY_RightWheelDown, "RightWheelDown"), /* routing keys */ KEY_GROUP_ENTRY(AT_GRP_RoutingKeys1, "RoutingKey1"), KEY_GROUP_ENTRY(AT_GRP_RoutingKeys2, "RoutingKey2"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLES(all) KEY_NAME_TABLE(all), END_KEY_NAME_TABLES DEFINE_KEY_TABLE(all) BEGIN_KEY_TABLE_LIST &KEY_TABLE_DEFINITION(all), END_KEY_TABLE_LIST typedef struct { int (*openPort) (const char *device); int (*configurePort) (unsigned int baud); void (*closePort) (void); int (*awaitInput) (int milliseconds); int (*readBytes) (unsigned char *buffer, size_t size, int wait); int (*writeBytes) (const unsigned char *buffer, size_t size); } InputOutputOperations; static const InputOutputOperations *io; static unsigned int charactersPerSecond; #include "io_serial.h" static SerialDevice *serialDevice = NULL; static int openSerialPort (const char *device) { if ((serialDevice = serialOpenDevice(device))) return 1; return 0; } static int configureSerialPort (unsigned int baud) { return serialRestartDevice(serialDevice, baud); } static void closeSerialPort (void) { if (serialDevice) { serialCloseDevice(serialDevice); serialDevice = NULL; } } static int awaitSerialInput (int milliseconds) { return serialAwaitInput(serialDevice, milliseconds); } static int readSerialBytes (unsigned char *buffer, size_t size, int wait) { const int timeout = 100; return serialReadData(serialDevice, buffer, size, (wait? timeout: 0), timeout); } static int writeSerialBytes (const unsigned char *buffer, size_t size) { return serialWriteData(serialDevice, buffer, size); } static const InputOutputOperations serialOperations = { openSerialPort, configureSerialPort, closeSerialPort, awaitSerialInput, readSerialBytes, writeSerialBytes }; #include "io_usb.h" static UsbChannel *usbChannel = NULL; static int openUsbPort (const char *device) { BEGIN_USB_STRING_LIST(usbManufacturers_0403_6001) "Tivomatic Oy", END_USB_STRING_LIST BEGIN_USB_CHANNEL_DEFINITIONS { /* all models */ .vendor=0X0403, .product=0X6001, .manufacturers = usbManufacturers_0403_6001, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=2 }, END_USB_CHANNEL_DEFINITIONS if ((usbChannel = usbOpenChannel(usbChannelDefinitions, (void *)device))) { return 1; } return 0; } static int configureUsbPort (unsigned int baud) { const SerialParameters parameters = { SERIAL_DEFAULT_PARAMETERS, .baud = baud }; return usbSetSerialParameters(usbChannel->device, ¶meters); } static void closeUsbPort (void) { if (usbChannel) { usbCloseChannel(usbChannel); usbChannel = NULL; } } static int awaitUsbInput (int milliseconds) { return usbAwaitInput(usbChannel->device, usbChannel->definition->inputEndpoint, milliseconds); } static int readUsbBytes (unsigned char *buffer, size_t size, int wait) { const int timeout = 100; int count = usbReadData(usbChannel->device, usbChannel->definition->inputEndpoint, buffer, size, (wait? timeout: 0), timeout); if (count != -1) return count; if (errno == EAGAIN) return 0; return -1; } static int writeUsbBytes (const unsigned char *buffer, size_t size) { return usbWriteEndpoint(usbChannel->device, usbChannel->definition->outputEndpoint, buffer, size, 1000); } static const InputOutputOperations usbOperations = { openUsbPort, configureUsbPort, closeUsbPort, awaitUsbInput, readUsbBytes, writeUsbBytes }; static TranslationTable inputMap; static const unsigned char topLeftKeys[] = { AT_KEY_F1, AT_KEY_F2, AT_KEY_F3, AT_KEY_F4, AT_KEY_F5, AT_KEY_F6, AT_KEY_F7, AT_KEY_F8 }; static const unsigned char topRightKeys[] = { AT_KEY_F9, AT_KEY_F10, AT_KEY_F11, AT_KEY_F12, AT_KEY_F13, AT_KEY_F14, AT_KEY_F15, AT_KEY_F16 }; static unsigned char controlKey; #define NO_CONTROL_KEY 0XFF static unsigned char displayContent[80]; static int displaySize; static int windowWidth; static int windowStart; static int statusCount; static int statusStart; static int readByte (unsigned char *byte) { int result = io->readBytes(byte, 1, 0); if (result > 0) { logInputPacket(byte, result); } else if (result == -1) { logSystemError("Albatross read"); } else if (!result) { errno = EAGAIN; } return result == 1; } static void discardInput (void) { unsigned char byte; while (readByte(&byte)); } static int awaitByte (unsigned char *byte) { if (readByte(byte)) return 1; if (io->awaitInput(1000)) if (readByte(byte)) return 1; return 0; } static int writeBytes (BrailleDisplay *brl, const unsigned char *bytes, int count) { brl->writeDelay += (count * 1000 / charactersPerSecond) + 1; logOutputPacket(bytes, count); if (io->writeBytes(bytes, count) != -1) return 1; logSystemError("Albatross write"); return 0; } static int acknowledgeDisplay (BrailleDisplay *brl) { unsigned char description; if (!awaitByte(&description)) return 0; if (description == 0XFF) return 0; { unsigned char byte; if (!awaitByte(&byte)) return 0; if (byte != 0XFF) return 0; if (!awaitByte(&byte)) return 0; if (byte != description) return 0; } { static const unsigned char acknowledgement[] = {0XFE, 0XFF, 0XFE, 0XFF}; if (!writeBytes(brl, acknowledgement, sizeof(acknowledgement))) return 0; discardInput(); asyncWait(100); discardInput(); } logMessage(LOG_DEBUG, "Albatross description byte: %02X", description); windowStart = statusStart = 0; displaySize = (description & 0X80)? 80: 46; if ((statusCount = description & 0X0F)) { windowWidth = displaySize - statusCount - 1; if (description & 0X20) { statusStart = windowWidth + 1; displayContent[statusStart - 1] = 0; } else { windowStart = statusCount + 1; displayContent[windowStart - 1] = 0; } } else { windowWidth = displaySize; } { int i; for (i=0; i 1) || (time(NULL) != lastUpdate)) { *byte++ = 0XFC; if (!writeBytes(brl, bytes, byte-bytes)) return 0; lastUpdate = time(NULL); } return 1; } static int updateWindow (BrailleDisplay *brl, const unsigned char *cells) { return updateDisplay(brl, cells, windowWidth, windowStart); } static int updateStatus (BrailleDisplay *brl, const unsigned char *cells) { return updateDisplay(brl, cells, statusCount, statusStart); } static int refreshDisplay (BrailleDisplay *brl) { return updateDisplay(brl, NULL, displaySize, 0); } static int brl_construct (BrailleDisplay *brl, char **parameters, const char *device) { if (isSerialDeviceIdentifier(&device)) { io = &serialOperations; } else if (isUsbDeviceIdentifier(&device)) { io = &usbOperations; } else { unsupportedDeviceIdentifier(device); return 0; } if (io->openPort(device)) { unsigned int baudTable[] = {19200, 9600, 0}; const unsigned int *baud = baudTable; while (io->configurePort(*baud)) { TimePeriod period; int count = 0; unsigned char byte; startTimePeriod(&period, 1000); charactersPerSecond = *baud / 10; controlKey = NO_CONTROL_KEY; logMessage(LOG_DEBUG, "trying Albatross at %u baud", *baud); while (awaitByte(&byte)) { if (byte == 0XFF) { if (!acknowledgeDisplay(brl)) break; brl->textColumns = windowWidth; brl->textRows = 1; setBrailleKeyTable(brl, &KEY_TABLE_DEFINITION(all)); MAKE_OUTPUT_TABLE(0X80, 0X40, 0X20, 0X10, 0X08, 0X04, 0X02, 0X01); clearDisplay(brl); return 1; } if (++count == 100) break; if (afterTimePeriod(&period, NULL)) break; } if (!*++baud) break; } io->closePort(); } return 0; } static void brl_destruct (BrailleDisplay *brl) { io->closePort(); } static int brl_writeWindow (BrailleDisplay *brl, const wchar_t *text) { updateWindow(brl, brl->buffer); return 1; } static int brl_writeStatus (BrailleDisplay *brl, const unsigned char *status) { updateStatus(brl, status); return 1; } static int brl_readCommand (BrailleDisplay *brl, KeyTableCommandContext context) { unsigned char byte; while (readByte(&byte)) { if (byte == 0XFF) { if (acknowledgeDisplay(brl)) { refreshDisplay(brl); brl->textColumns = windowWidth; brl->textRows = 1; brl->resizeRequired = 1; } continue; } byte = inputMap[byte]; { KeyGroup group; KeyNumber number; if ((byte >= 2) && (byte <= 41)) { group = AT_GRP_RoutingKeys1; number = byte - 2; } else if ((byte >= 111) && (byte <= 150)) { group = AT_GRP_RoutingKeys1; number = byte - 71; } else if ((byte >= 43) && (byte <= 82)) { group = AT_GRP_RoutingKeys2; number = byte - 43; } else if ((byte >= 152) && (byte <= 191)) { group = AT_GRP_RoutingKeys2; number = byte - 112; } else { goto notRouting; } if ((number >= windowStart) && (number < (windowStart + windowWidth))) { number -= windowStart; enqueueKey(brl, group, number); continue; } } notRouting: switch (byte) { default: break; case 0XFB: refreshDisplay(brl); continue; case AT_KEY_Attribute1: case AT_KEY_Attribute2: case AT_KEY_Attribute3: case AT_KEY_Attribute4: case AT_KEY_F1: case AT_KEY_F2: case AT_KEY_F7: case AT_KEY_F8: case AT_KEY_F9: case AT_KEY_F10: case AT_KEY_F15: case AT_KEY_F16: case AT_KEY_Home1: case AT_KEY_Home2: case AT_KEY_End1: case AT_KEY_End2: case AT_KEY_ExtraCursor1: case AT_KEY_ExtraCursor2: case AT_KEY_Cursor1: case AT_KEY_Cursor2: if (byte == controlKey) { controlKey = NO_CONTROL_KEY; enqueueKeyEvent(brl, AT_GRP_NavigationKeys, byte, 0); continue; } if (controlKey == NO_CONTROL_KEY) { controlKey = byte; enqueueKeyEvent(brl, AT_GRP_NavigationKeys, byte, 1); continue; } case AT_KEY_Up1: case AT_KEY_Down1: case AT_KEY_Left: case AT_KEY_Up2: case AT_KEY_Down2: case AT_KEY_Right: case AT_KEY_Up3: case AT_KEY_Down3: case AT_KEY_F3: case AT_KEY_F4: case AT_KEY_F5: case AT_KEY_F6: case AT_KEY_F11: case AT_KEY_F12: case AT_KEY_F13: case AT_KEY_F14: case AT_KEY_LeftWheelRight: case AT_KEY_LeftWheelLeft: case AT_KEY_LeftWheelUp: case AT_KEY_LeftWheelDown: case AT_KEY_RightWheelRight: case AT_KEY_RightWheelLeft: case AT_KEY_RightWheelUp: case AT_KEY_RightWheelDown: enqueueKey(brl, AT_GRP_NavigationKeys, byte); continue; } logUnexpectedPacket(&byte, 1); } if (errno != EAGAIN) return BRL_CMD_RESTARTBRL; return EOF; }