| /* |
| * 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 <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #include "log.h" |
| #include "strfmt.h" |
| #include "parse.h" |
| #include "async_wait.h" |
| #include "ascii.h" |
| |
| typedef enum { |
| PARM_PROTOCOL, |
| PARM_VARIOKEYS |
| } DriverParameter; |
| #define BRLPARMS "protocol", "variokeys" |
| |
| #define BRLSTAT ST_TiemanStyle |
| #define BRL_HAVE_STATUS_CELLS |
| #define BRL_HAVE_PACKET_IO |
| #include "brl_driver.h" |
| #include "brldefs-bm.h" |
| |
| BEGIN_KEY_NAME_TABLE(display) |
| KEY_NAME_ENTRY(BM_KEY_DISPLAY+7, "Display8"), |
| KEY_NAME_ENTRY(BM_KEY_DISPLAY+6, "Display7"), |
| KEY_NAME_ENTRY(BM_KEY_DISPLAY+5, "Display6"), |
| KEY_NAME_ENTRY(BM_KEY_DISPLAY+4, "Display5"), |
| KEY_NAME_ENTRY(BM_KEY_DISPLAY+3, "Display4"), |
| KEY_NAME_ENTRY(BM_KEY_DISPLAY+2, "Display3"), |
| KEY_NAME_ENTRY(BM_KEY_DISPLAY+1, "Display2"), |
| KEY_NAME_ENTRY(BM_KEY_DISPLAY+0, "Display1"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(command) |
| KEY_NAME_ENTRY(BM_KEY_COMMAND+6, "Command7"), |
| KEY_NAME_ENTRY(BM_KEY_COMMAND+5, "Command6"), |
| KEY_NAME_ENTRY(BM_KEY_COMMAND+4, "Command5"), |
| KEY_NAME_ENTRY(BM_KEY_COMMAND+3, "Command4"), |
| KEY_NAME_ENTRY(BM_KEY_COMMAND+2, "Command3"), |
| KEY_NAME_ENTRY(BM_KEY_COMMAND+1, "Command2"), |
| KEY_NAME_ENTRY(BM_KEY_COMMAND+0, "Command1"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(front) |
| KEY_NAME_ENTRY(BM_KEY_FRONT+0, "Front1"), |
| KEY_NAME_ENTRY(BM_KEY_FRONT+1, "Front2"), |
| KEY_NAME_ENTRY(BM_KEY_FRONT+2, "Front3"), |
| KEY_NAME_ENTRY(BM_KEY_FRONT+3, "Front4"), |
| KEY_NAME_ENTRY(BM_KEY_FRONT+4, "Front5"), |
| KEY_NAME_ENTRY(BM_KEY_FRONT+5, "Front6"), |
| KEY_NAME_ENTRY(BM_KEY_FRONT+6, "Front7"), |
| KEY_NAME_ENTRY(BM_KEY_FRONT+7, "Front8"), |
| KEY_NAME_ENTRY(BM_KEY_FRONT+8, "Front9"), |
| KEY_NAME_ENTRY(BM_KEY_FRONT+9, "Front10"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(back) |
| KEY_NAME_ENTRY(BM_KEY_BACK+0, "Back1"), |
| KEY_NAME_ENTRY(BM_KEY_BACK+1, "Back2"), |
| KEY_NAME_ENTRY(BM_KEY_BACK+2, "Back3"), |
| KEY_NAME_ENTRY(BM_KEY_BACK+3, "Back4"), |
| KEY_NAME_ENTRY(BM_KEY_BACK+4, "Back5"), |
| KEY_NAME_ENTRY(BM_KEY_BACK+5, "Back6"), |
| KEY_NAME_ENTRY(BM_KEY_BACK+6, "Back7"), |
| KEY_NAME_ENTRY(BM_KEY_BACK+7, "Back8"), |
| KEY_NAME_ENTRY(BM_KEY_BACK+8, "Back9"), |
| KEY_NAME_ENTRY(BM_KEY_BACK+9, "Back10"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(entry) |
| KEY_NAME_ENTRY(BM_KEY_B9, "B9"), |
| KEY_NAME_ENTRY(BM_KEY_B10, "B10"), |
| KEY_NAME_ENTRY(BM_KEY_B11, "B11"), |
| |
| KEY_NAME_ENTRY(BM_KEY_F1, "F1"), |
| KEY_NAME_ENTRY(BM_KEY_F2, "F2"), |
| KEY_NAME_ENTRY(BM_KEY_F3, "F3"), |
| KEY_NAME_ENTRY(BM_KEY_F4, "F4"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(dots) |
| KEY_NAME_ENTRY(BM_KEY_DOT1, "Dot1"), |
| KEY_NAME_ENTRY(BM_KEY_DOT2, "Dot2"), |
| KEY_NAME_ENTRY(BM_KEY_DOT3, "Dot3"), |
| KEY_NAME_ENTRY(BM_KEY_DOT4, "Dot4"), |
| KEY_NAME_ENTRY(BM_KEY_DOT5, "Dot5"), |
| KEY_NAME_ENTRY(BM_KEY_DOT6, "Dot6"), |
| KEY_NAME_ENTRY(BM_KEY_DOT7, "Dot7"), |
| KEY_NAME_ENTRY(BM_KEY_DOT8, "Dot8"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(joystick) |
| KEY_NAME_ENTRY(BM_KEY_UP, "Up"), |
| KEY_NAME_ENTRY(BM_KEY_LEFT, "Left"), |
| KEY_NAME_ENTRY(BM_KEY_DOWN, "Down"), |
| KEY_NAME_ENTRY(BM_KEY_RIGHT, "Right"), |
| KEY_NAME_ENTRY(BM_KEY_PRESS, "Press"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(navpad) |
| KEY_NAME_ENTRY(BM_KEY_UP, "Up"), |
| KEY_NAME_ENTRY(BM_KEY_LEFT, "Left"), |
| KEY_NAME_ENTRY(BM_KEY_DOWN, "Down"), |
| KEY_NAME_ENTRY(BM_KEY_RIGHT, "Right"), |
| KEY_NAME_ENTRY(BM_KEY_PRESS, "Select"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(wheels) |
| KEY_NAME_ENTRY(BM_KEY_WHEEL_UP+0, "FirstWheelUp"), |
| KEY_NAME_ENTRY(BM_KEY_WHEEL_DOWN+0, "FirstWheelDown"), |
| KEY_NAME_ENTRY(BM_KEY_WHEEL_PRESS+0, "FirstWheelPress"), |
| |
| KEY_NAME_ENTRY(BM_KEY_WHEEL_UP+1, "SecondWheelUp"), |
| KEY_NAME_ENTRY(BM_KEY_WHEEL_DOWN+1, "SecondWheelDown"), |
| KEY_NAME_ENTRY(BM_KEY_WHEEL_PRESS+1, "SecondWheelPress"), |
| |
| KEY_NAME_ENTRY(BM_KEY_WHEEL_UP+2, "ThirdWheelUp"), |
| KEY_NAME_ENTRY(BM_KEY_WHEEL_DOWN+2, "ThirdWheelDown"), |
| KEY_NAME_ENTRY(BM_KEY_WHEEL_PRESS+2, "ThirdWheelPress"), |
| |
| KEY_NAME_ENTRY(BM_KEY_WHEEL_UP+3, "FourthWheelUp"), |
| KEY_NAME_ENTRY(BM_KEY_WHEEL_DOWN+3, "FourthWheelDown"), |
| KEY_NAME_ENTRY(BM_KEY_WHEEL_PRESS+3, "FourthWheelPress"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(status) |
| KEY_NAME_ENTRY(BM_KEY_STATUS+0, "StatusButton1"), |
| KEY_NAME_ENTRY(BM_KEY_STATUS+1, "StatusButton2"), |
| KEY_NAME_ENTRY(BM_KEY_STATUS+2, "StatusButton3"), |
| KEY_NAME_ENTRY(BM_KEY_STATUS+3, "StatusButton4"), |
| |
| KEY_NAME_ENTRY(BM_KEY_STATUS+4, "StatusKey1"), |
| KEY_NAME_ENTRY(BM_KEY_STATUS+5, "StatusKey2"), |
| KEY_NAME_ENTRY(BM_KEY_STATUS+6, "StatusKey3"), |
| KEY_NAME_ENTRY(BM_KEY_STATUS+7, "StatusKey4"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(routing) |
| KEY_GROUP_ENTRY(BM_GRP_RoutingKeys, "RoutingKey"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(horizontal) |
| KEY_GROUP_ENTRY(BM_GRP_HorizontalSensors, "HorizontalSensor"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(vertical) |
| KEY_GROUP_ENTRY(BM_GRP_LeftSensors, "LeftSensor"), |
| KEY_GROUP_ENTRY(BM_GRP_RightSensors, "RightSensor"), |
| KEY_GROUP_ENTRY(BM_GRP_ScaledLeftSensors, "ScaledLeftSensor"), |
| KEY_GROUP_ENTRY(BM_GRP_ScaledRightSensors, "ScaledRightSensor"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(NLS_Zoomax) |
| KEY_NAME_ENTRY(BM_KEY_B9, "BL"), |
| KEY_NAME_ENTRY(BM_KEY_B10, "Space"), |
| |
| KEY_NAME_ENTRY(BM_KEY_F1, "S1"), |
| KEY_NAME_ENTRY(BM_KEY_F2, "S2"), |
| KEY_NAME_ENTRY(BM_KEY_F3, "S3"), |
| KEY_NAME_ENTRY(BM_KEY_F4, "S4"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(orbit) |
| KEY_NAME_ENTRY(BM_KEY_B9, "Space"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLES(default) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(entry), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(joystick), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(rb) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(entry), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(joystick), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(orbit) |
| KEY_NAME_TABLE(orbit), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(navpad), |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(NLS_Zoomax) |
| KEY_NAME_TABLE(NLS_Zoomax), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(navpad), |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(b2g) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(entry), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(navpad), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(connect) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(entry), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(joystick), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(conny) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(entry), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(joystick), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(pronto) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(entry), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(joystick), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(pv) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(entry), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(joystick), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(sv) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(ultra) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(entry), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(joystick), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(inka) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(horizontal), |
| KEY_NAME_TABLE(vertical), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(dm80p) |
| KEY_NAME_SUBTABLE(display,7), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(v40) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(v80) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(command), |
| KEY_NAME_TABLE(front), |
| KEY_NAME_TABLE(back), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(pro) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(wheels), |
| KEY_NAME_TABLE(status), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(vk) |
| KEY_NAME_SUBTABLE(display,6), |
| KEY_NAME_TABLE(routing), |
| END_KEY_NAME_TABLES |
| |
| DEFINE_KEY_TABLE(default) |
| DEFINE_KEY_TABLE(rb) |
| DEFINE_KEY_TABLE(orbit) |
| DEFINE_KEY_TABLE(NLS_Zoomax) |
| DEFINE_KEY_TABLE(b2g) |
| DEFINE_KEY_TABLE(connect) |
| DEFINE_KEY_TABLE(conny) |
| DEFINE_KEY_TABLE(pronto) |
| DEFINE_KEY_TABLE(pv) |
| DEFINE_KEY_TABLE(sv) |
| DEFINE_KEY_TABLE(ultra) |
| DEFINE_KEY_TABLE(inka) |
| DEFINE_KEY_TABLE(dm80p) |
| DEFINE_KEY_TABLE(v40) |
| DEFINE_KEY_TABLE(v80) |
| DEFINE_KEY_TABLE(pro) |
| DEFINE_KEY_TABLE(vk) |
| |
| BEGIN_KEY_TABLE_LIST |
| &KEY_TABLE_DEFINITION(default), |
| &KEY_TABLE_DEFINITION(rb), |
| &KEY_TABLE_DEFINITION(orbit), |
| &KEY_TABLE_DEFINITION(NLS_Zoomax), |
| &KEY_TABLE_DEFINITION(b2g), |
| &KEY_TABLE_DEFINITION(connect), |
| &KEY_TABLE_DEFINITION(conny), |
| &KEY_TABLE_DEFINITION(pronto), |
| &KEY_TABLE_DEFINITION(pv), |
| &KEY_TABLE_DEFINITION(sv), |
| &KEY_TABLE_DEFINITION(ultra), |
| &KEY_TABLE_DEFINITION(inka), |
| &KEY_TABLE_DEFINITION(dm80p), |
| &KEY_TABLE_DEFINITION(v40), |
| &KEY_TABLE_DEFINITION(v80), |
| &KEY_TABLE_DEFINITION(pro), |
| &KEY_TABLE_DEFINITION(vk), |
| END_KEY_TABLE_LIST |
| |
| /* Global Definitions */ |
| |
| static const int probeLimit = 2; |
| static const int probeTimeout = 200; |
| |
| #define KEY_GROUP_SIZE(count) (((count) + 7) / 8) |
| #define MAXIMUM_CELL_COUNT 84 |
| #define VERTICAL_SENSOR_COUNT 27 |
| |
| static int cellCount; |
| static int cellsUpdated; |
| static unsigned char internalCells[MAXIMUM_CELL_COUNT]; |
| static unsigned char externalCells[MAXIMUM_CELL_COUNT]; |
| |
| typedef struct { |
| unsigned char navigationKeys[KEY_GROUP_SIZE(BM_KEY_COUNT)]; |
| unsigned char routingKeys[KEY_GROUP_SIZE(MAXIMUM_CELL_COUNT)]; |
| unsigned char horizontalSensors[KEY_GROUP_SIZE(MAXIMUM_CELL_COUNT)]; |
| unsigned char leftSensors[KEY_GROUP_SIZE(VERTICAL_SENSOR_COUNT)]; |
| unsigned char rightSensors[KEY_GROUP_SIZE(VERTICAL_SENSOR_COUNT)]; |
| } KeysState; |
| |
| static KeysState keysState; |
| static unsigned char switchSettings; |
| |
| typedef struct { |
| const char *name; |
| const DotsTable *dotsTable; |
| |
| unsigned int serialBaud; |
| SerialParity serialParity; |
| |
| int (*readPacket) (BrailleDisplay *brl, unsigned char *packet, int size); |
| int (*writePacket) (BrailleDisplay *brl, const unsigned char *packet, int length); |
| |
| int (*probeDevice) (BrailleDisplay *brl); |
| void (*processPackets) (BrailleDisplay *brl); |
| |
| int (*writeCells) (BrailleDisplay *brl); |
| int (*writeCellRange) (BrailleDisplay *brl, unsigned int start, unsigned int count); |
| } ProtocolOperations; |
| |
| struct BrailleDataStruct { |
| const ProtocolOperations *protocol; |
| |
| struct { |
| unsigned char routingKeys; |
| } packetSize; |
| }; |
| |
| /* Internal Routines */ |
| |
| static void |
| logTextField (const char *name, const char *address, int size) { |
| while (size > 0) { |
| const char byte = address[size - 1]; |
| |
| if (byte && (byte != ' ')) break; |
| size -= 1; |
| } |
| |
| logMessage(LOG_INFO, "%s: %.*s", name, size, address); |
| } |
| |
| static int |
| setGroupedKey (unsigned char *set, KeyNumber number, int press) { |
| unsigned char *byte = &set[number / 8]; |
| unsigned char bit = 1 << (number % 8); |
| |
| if (!(*byte & bit) == !press) return 0; |
| |
| if (press) { |
| *byte |= bit; |
| } else { |
| *byte &= ~bit; |
| } |
| |
| return 1; |
| } |
| |
| static void |
| clearKeyGroup (unsigned char *set, unsigned char count) { |
| memset(set, 0, KEY_GROUP_SIZE(count)); |
| } |
| |
| static void |
| resetKeyGroup (unsigned char *set, unsigned char count, KeyNumber key) { |
| clearKeyGroup(set, count); |
| if (key > 0) setGroupedKey(set, key-1, 1); |
| } |
| |
| static void |
| updateKeyGroup ( |
| BrailleDisplay *brl, |
| unsigned char *old, const unsigned char *new, |
| KeyGroup group, KeyNumber base, unsigned char count, int scaled |
| ) { |
| KeyNumber pressTable[count]; |
| unsigned char pressCount = 0; |
| unsigned char offset; |
| |
| for (offset=0; offset<count; offset+=1) { |
| KeyNumber number = base + offset; |
| int press = (new[offset / 8] & (1 << (offset % 8))) != 0; |
| |
| if (setGroupedKey(old, number, press)) { |
| if (scaled) number = rescaleInteger(number, count-1, BRL_MSK_ARG); |
| |
| if (press) { |
| pressTable[pressCount++] = number; |
| } else { |
| enqueueKeyEvent(brl, group, number, 0); |
| } |
| } |
| } |
| |
| while (pressCount) enqueueKeyEvent(brl, group, pressTable[--pressCount], 1); |
| } |
| |
| static void |
| updateNavigationKeys ( |
| BrailleDisplay *brl, |
| const unsigned char *new, KeyNumber base, unsigned char count |
| ) { |
| updateKeyGroup(brl, keysState.navigationKeys, new, BM_GRP_NavigationKeys, base, count, 0); |
| } |
| |
| static void |
| updateDisplayKeys (BrailleDisplay *brl, unsigned char new) { |
| updateNavigationKeys(brl, &new, BM_KEY_DISPLAY, BM_KEYS_DISPLAY); |
| } |
| |
| static void |
| updateEntryKeys (BrailleDisplay *brl, unsigned char *new) { |
| updateNavigationKeys(brl, new, BM_KEY_ENTRY, BM_KEYS_ENTRY); |
| } |
| |
| static void |
| updateJoystick (BrailleDisplay *brl, unsigned char *new) { |
| updateNavigationKeys(brl, new, BM_KEY_JOYSTICK, BM_KEYS_JOYSTICK); |
| } |
| |
| static void |
| updateRoutingKeys (BrailleDisplay *brl, const unsigned char *new, unsigned char count) { |
| updateKeyGroup(brl, keysState.routingKeys, new, BM_GRP_RoutingKeys, 0, count, 0); |
| } |
| |
| static int |
| updateCells (BrailleDisplay *brl) { |
| if (cellsUpdated) { |
| if (!brl->data->protocol->writeCells(brl)) return 0; |
| cellsUpdated = 0; |
| } |
| return 1; |
| } |
| |
| static int |
| updateCellRange (BrailleDisplay *brl, unsigned int start, unsigned int count) { |
| if (count) { |
| translateOutputCells(&externalCells[start], &internalCells[start], count); |
| cellsUpdated = 1; |
| if (!brl->data->protocol->writeCellRange(brl, start, count)) return 0; |
| } |
| |
| return 1; |
| } |
| |
| static int |
| clearCellRange (BrailleDisplay *brl, unsigned int start, unsigned int count) { |
| memset(&internalCells[start], 0, count); |
| return updateCellRange(brl, start, count); |
| } |
| |
| static int |
| putCells (BrailleDisplay *brl, const unsigned char *cells, unsigned int start, unsigned int count) { |
| unsigned int from; |
| unsigned int to; |
| |
| if (cellsHaveChanged(&internalCells[start], cells, count, &from, &to, NULL)) { |
| if (!updateCellRange(brl, start+from, to-from)) return 0; |
| } |
| |
| return 1; |
| } |
| |
| static int |
| isAcceptableCellCount (int count) { |
| return (count > 0) && (count <= MAXIMUM_CELL_COUNT); |
| } |
| |
| static void |
| logUnexpectedCellCount (int count) { |
| logMessage(LOG_DEBUG, "unexpected cell count: %d", count); |
| } |
| |
| static void |
| logCellCount (BrailleDisplay *brl) { |
| switch ((brl->textColumns = cellCount)) { |
| case 44: |
| case 68: |
| case 84: |
| brl->textColumns -= 4; |
| break; |
| |
| case 56: |
| brl->textColumns -= 16; |
| break; |
| } |
| brl->textRows = 1; |
| brl->statusRows = (brl->statusColumns = cellCount - brl->textColumns)? 1: 0; |
| |
| logMessage(LOG_INFO, "Cell Count: %d (%d text, %d status)", |
| cellCount, brl->textColumns, brl->statusColumns); |
| } |
| |
| static int |
| changeCellCount (BrailleDisplay *brl, int count) { |
| int ok = 1; |
| |
| if (count != cellCount) { |
| if (count > cellCount) { |
| if (!clearCellRange(brl, cellCount, count-cellCount)) ok = 0; |
| |
| { |
| int number; |
| for (number=cellCount; number<count; number+=1) { |
| setGroupedKey(keysState.routingKeys, number, 0); |
| setGroupedKey(keysState.horizontalSensors, number, 0); |
| } |
| } |
| } |
| |
| cellCount = count; |
| logCellCount(brl); |
| brl->resizeRequired = 1; |
| } |
| |
| return ok; |
| } |
| |
| /* Baum Protocol */ |
| |
| typedef unsigned char BaumInteger[2]; |
| #define MAKE_BAUM_INTEGER_FIRST(i) ((i) & 0XFF) |
| #define MAKE_BAUM_INTEGER_SECOND(i) (((i) >> 8) & 0XFF) |
| #define MAKE_BAUM_INTEGER(i) MAKE_BAUM_INTEGER_FIRST((i)), MAKE_BAUM_INTEGER_SECOND((i)) |
| |
| static inline uint16_t |
| getBaumInteger (const BaumInteger integer) { |
| return (integer[1] << 8) | integer[0]; |
| } |
| |
| typedef enum { |
| BAUM_REQ_DisplayData = 0X01, |
| BAUM_REQ_GetVersionNumber = 0X05, |
| BAUM_REQ_GetKeys = 0X08, |
| BAUM_REQ_GetMode = 0X11, |
| BAUM_REQ_SetMode = 0X12, |
| BAUM_REQ_SetProtocolState = 0X15, |
| BAUM_REQ_SetCommunicationChannel = 0X16, |
| BAUM_REQ_CausePowerdown = 0X17, |
| BAUM_REQ_ModuleRegistration = 0X50, |
| BAUM_REQ_DataRegisters = 0X51, |
| BAUM_REQ_ServiceRegisters = 0X52, |
| BAUM_REQ_GetDeviceIdentity = 0X84, |
| BAUM_REQ_GetSerialNumber = 0X8A, |
| BAUM_REQ_GetBluetoothName = 0X8C, |
| BAUM_REQ_SetBluetoothName = 0X8D, |
| BAUM_REQ_SetBluetoothPin = 0X8E |
| } BaumRequestCode; |
| |
| typedef enum { |
| BAUM_RSP_CellCount = 0X01, |
| BAUM_RSP_VersionNumber = 0X05, |
| BAUM_RSP_ModeSetting = 0X11, |
| BAUM_RSP_CommunicationChannel = 0X16, |
| BAUM_RSP_PowerdownSignal = 0X17, |
| BAUM_RSP_HorizontalSensors = 0X20, |
| BAUM_RSP_VerticalSensors = 0X21, |
| BAUM_RSP_RoutingKeys = 0X22, |
| BAUM_RSP_Switches = 0X23, |
| BAUM_RSP_DisplayKeys = 0X24, |
| BAUM_RSP_HorizontalSensor = 0X25, |
| BAUM_RSP_VerticalSensor = 0X26, |
| BAUM_RSP_RoutingKey = 0X27, |
| BAUM_RSP_Front6 = 0X28, |
| BAUM_RSP_Back6 = 0X29, |
| BAUM_RSP_CommandKeys = 0X2B, |
| BAUM_RSP_Front10 = 0X2C, |
| BAUM_RSP_Back10 = 0X2D, |
| BAUM_RSP_EntryKeys = 0X33, |
| BAUM_RSP_Joystick = 0X34, |
| BAUM_RSP_ErrorCode = 0X40, |
| BAUM_RSP_ModuleRegistration = 0X50, |
| BAUM_RSP_DataRegisters = 0X51, |
| BAUM_RSP_ServiceRegisters = 0X52, |
| BAUM_RSP_DeviceIdentity = 0X84, |
| BAUM_RSP_SerialNumber = 0X8A, |
| BAUM_RSP_BluetoothName = 0X8C, |
| BAUM_RSP_NLS_ZMX_BD = 0XBD, |
| BAUM_RSP_NLS_ZMX_BE = 0XBE, |
| BAUM_RSP_NLS_ZMX_BF = 0XBF, |
| } BaumResponseCode; |
| |
| typedef enum { |
| BAUM_MODE_KeyGroupCompressed = 0X01, |
| BAUM_MODE_HorizontalSensorsEnabled = 0X06, |
| BAUM_MODE_LeftSensorsEnabled = 0X07, |
| BAUM_MODE_RoutingKeysEnabled = 0X08, |
| BAUM_MODE_RightSensorsEnabled = 0X09, |
| BAUM_MODE_BackKeysEnabled = 0X0A, |
| BAUM_MODE_DisplayRotated = 0X10, |
| BAUM_MODE_DisplayEnabled = 0X20, |
| BAUM_MODE_PowerdownEnabled = 0X21, |
| BAUM_MODE_PowerdownTime = 0X22, |
| BAUM_MODE_BluetoothEnabled = 0X23, |
| BAUM_MODE_UsbCharge = 0X24 |
| } BaumMode; |
| |
| typedef enum { |
| BAUM_PDT_5Minutes = 1, |
| BAUM_PDT_10Minutes = 2, |
| BAUM_PDT_1Hour = 3, |
| BAUM_PDT_2Hours = 4 |
| } BaumPowerdownTime; |
| |
| typedef enum { |
| BAUM_PDR_ProtocolRequested = 0X01, |
| BAUM_PDR_PowerSwitch = 0X02, |
| BAUM_PDR_AutoPowerOff = 0X04, |
| BAUM_PDR_BatteryLow = 0X08, |
| BAUM_PDR_Charging = 0X80 |
| } BaumPowerdownReason; |
| |
| typedef enum { |
| BAUM_SWT_DisableSensors = 0X01, |
| BAUM_SWT_ScaledVertical = 0X02, |
| BAUM_SWT_ShowSensor = 0X40, |
| BAUM_SWT_BrailleKeyboard = 0X80 |
| } BaumSwitch; |
| |
| typedef enum { |
| BAUM_ERR_BluetoothSupport = 0X0A, |
| BAUM_ERR_TransmitOverrun = 0X10, |
| BAUM_ERR_ReceiveOverrun = 0X11, |
| BAUM_ERR_TransmitTimeout = 0X12, |
| BAUM_ERR_ReceiveTimeout = 0X13, |
| BAUM_ERR_PacketType = 0X14, |
| BAUM_ERR_PacketChecksum = 0X15, |
| BAUM_ERR_PacketData = 0X16, |
| BAUM_ERR_Test = 0X18, |
| BAUM_ERR_FlashWrite = 0X19, |
| BAUM_ERR_CommunicationChannel = 0X1F, |
| BAUM_ERR_SerialNumber = 0X20, |
| BAUM_ERR_SerialParity = 0X21, |
| BAUM_ERR_SerialOverrun = 0X22, |
| BAUM_ERR_SerialFrame = 0X24, |
| BAUM_ERR_LocalizationIdentifier = 0X25, |
| BAUM_ERR_LocalizationIndex = 0X26, |
| BAUM_ERR_LanguageIdentifier = 0X27, |
| BAUM_ERR_LanguageIndex = 0X28, |
| BAUM_ERR_BrailleTableIdentifier = 0X29, |
| BAUM_ERR_BrailleTableIndex = 0X2A |
| } BaumError; |
| |
| #define BAUM_LENGTH_DeviceIdentity 18 |
| #define BAUM_LENGTH_SerialNumber 8 |
| #define BAUM_LENGTH_BluetoothName 14 |
| |
| typedef enum { |
| BAUM_MRC_Acknowledge = 0X01, |
| BAUM_MRC_Query = 0X04 |
| } BaumModuleRegistrationCommand; |
| |
| typedef enum { |
| BAUM_MRE_Addition = 1, |
| BAUM_MRE_Removal = 2, |
| BAUM_MRE_Rejection = 3 |
| } BaumModuleRegistrationEvent; |
| |
| typedef enum { |
| BAUM_DRC_Write = 0X00, |
| BAUM_DRC_Read = 0X01, |
| BAUM_DRC_Reset = 0X80 |
| } BaumDataRegistersCommand; |
| |
| typedef enum { |
| BAUM_DRF_WheelsChanged = 0X01, |
| BAUM_DRF_ButtonsChanged = 0X02, |
| BAUM_DRF_KeysChanged = 0X04, |
| BAUM_DRF_PotsChanged = 0X04, |
| BAUM_DRF_SensorsChanged = 0X08, |
| BAUM_DRF_ErrorOccurred = 0X80 |
| } BaumDataRegistersFlag; |
| |
| typedef enum { |
| BAUM_DRE_WheelsNotConnected = 0X01, |
| BAUM_DRE_WheelsNotAdjusted = 0X02, |
| BAUM_DRE_KeyBufferFull = 0X04, |
| BAUM_DRE_SerialError = 0X80 |
| } BaumDataRegistersError; |
| |
| typedef enum { |
| BAUM_SRC_Write = 0X00, |
| BAUM_SRC_Read = 0X01 |
| } BaumServiceRegistersCommand; |
| |
| typedef union { |
| unsigned char bytes[2 + 0XFF]; |
| |
| struct { |
| unsigned char code; |
| |
| union { |
| unsigned char cellCount; |
| unsigned char versionNumber; |
| |
| struct { |
| unsigned char identifier; |
| unsigned char setting; |
| } PACKED mode; |
| |
| unsigned char communicationChannel; |
| unsigned char powerdownReason; |
| unsigned char horizontalSensors[KEY_GROUP_SIZE(MAXIMUM_CELL_COUNT)]; |
| |
| struct { |
| unsigned char left[KEY_GROUP_SIZE(VERTICAL_SENSOR_COUNT)]; |
| unsigned char right[KEY_GROUP_SIZE(VERTICAL_SENSOR_COUNT)]; |
| } PACKED verticalSensors; |
| |
| unsigned char routingKeys[KEY_GROUP_SIZE(MAXIMUM_CELL_COUNT)]; |
| unsigned char switches; |
| unsigned char displayKeys; |
| unsigned char horizontalSensor; |
| |
| union { |
| unsigned char left; |
| unsigned char right; |
| } PACKED verticalSensor; |
| |
| unsigned char routingKey; |
| unsigned char front6[1]; |
| unsigned char back6[1]; |
| unsigned char commandKeys[1]; |
| unsigned char front10[2]; |
| unsigned char back10[2]; |
| unsigned char entryKeys[2]; |
| unsigned char joystick[1]; |
| unsigned char errorCode; |
| |
| struct { |
| unsigned char length; |
| BaumInteger moduleIdentifier; |
| BaumInteger serialNumber; |
| |
| union { |
| struct { |
| BaumInteger hardwareVersion; |
| BaumInteger firmwareVersion; |
| unsigned char event; |
| } PACKED registration; |
| |
| union { |
| struct { |
| unsigned char flags; |
| unsigned char errors; |
| signed char wheels[4]; |
| unsigned char buttons; |
| unsigned char keys; |
| unsigned char sensors[KEY_GROUP_SIZE(80)]; |
| } PACKED display80; |
| |
| struct { |
| unsigned char flags; |
| unsigned char errors; |
| signed char wheels[3]; |
| unsigned char buttons; |
| unsigned char keys; |
| unsigned char sensors[KEY_GROUP_SIZE(64)]; |
| } PACKED display64; |
| |
| struct { |
| unsigned char flags; |
| unsigned char errors; |
| unsigned char buttons; |
| } PACKED status; |
| |
| struct { |
| unsigned char flags; |
| unsigned char errors; |
| signed char wheel; |
| unsigned char buttons; |
| unsigned char keypad[2]; |
| } PACKED phone; |
| |
| struct { |
| unsigned char flags; |
| unsigned char errors; |
| signed char wheel; |
| unsigned char keys; |
| unsigned char pots[6]; |
| } PACKED audio; |
| |
| struct { |
| unsigned char flags; |
| unsigned char errors; |
| unsigned char buttons; |
| unsigned char cursor; |
| unsigned char keys; |
| unsigned char pots[4]; |
| } PACKED voice; |
| } registers; |
| } data; |
| } PACKED modular; |
| |
| char deviceIdentity[BAUM_LENGTH_DeviceIdentity]; |
| char serialNumber[BAUM_LENGTH_SerialNumber]; |
| char bluetoothName[BAUM_LENGTH_BluetoothName]; |
| } PACKED values; |
| } PACKED data; |
| } PACKED BaumResponsePacket; |
| |
| typedef enum { |
| BAUM_DEVICE_Default = 0, |
| |
| BAUM_DEVICE_Refreshabraille, |
| BAUM_DEVICE_Orbit, |
| BAUM_DEVICE_NLS_Zoomax, |
| BAUM_DEVICE_B2G, |
| |
| BAUM_DEVICE_Conny, |
| BAUM_DEVICE_PocketVario, |
| BAUM_DEVICE_Pronto, |
| BAUM_DEVICE_SuperVario, |
| BAUM_DEVICE_VarioConnect, |
| BAUM_DEVICE_VarioUltra, |
| |
| BAUM_DEVICE_Inka, |
| BAUM_DEVICE_DM80P, |
| BAUM_DEVICE_Vario40, |
| BAUM_DEVICE_Vario80, |
| BAUM_DEVICE_Modular |
| } BaumDeviceType; |
| |
| typedef struct { |
| const char *string; |
| BaumDeviceType type; |
| } BaumDeviceIdentityEntry; |
| |
| static const BaumDeviceIdentityEntry baumDeviceIdentityTable[] = { |
| { .string = "Refreshabraille", |
| .type = BAUM_DEVICE_Refreshabraille |
| }, |
| |
| { .string = "Orbit", |
| .type = BAUM_DEVICE_Orbit |
| }, |
| |
| { .string = "NLS eReader Zoomax", |
| .type = BAUM_DEVICE_NLS_Zoomax |
| }, |
| |
| { .string = "Conny (NBP B2G)", |
| .type = BAUM_DEVICE_B2G |
| }, |
| |
| { .string = "BrailleConnect", |
| .type = BAUM_DEVICE_VarioConnect |
| }, |
| |
| { .string = "Brailliant", |
| .type = BAUM_DEVICE_SuperVario |
| }, |
| |
| { .string = "Conny", |
| .type = BAUM_DEVICE_Conny |
| }, |
| |
| { .string = "PocketVario", |
| .type = BAUM_DEVICE_PocketVario |
| }, |
| |
| { .string = "Pronto", |
| .type = BAUM_DEVICE_Pronto |
| }, |
| |
| { .string = "SuperVario", |
| .type = BAUM_DEVICE_SuperVario |
| }, |
| |
| { .string = "SVario", |
| .type = BAUM_DEVICE_SuperVario |
| }, |
| |
| { .string = "Vario 40", |
| .type = BAUM_DEVICE_Vario40 |
| }, |
| |
| { .string = "VarioConnect", |
| .type = BAUM_DEVICE_VarioConnect |
| }, |
| |
| { .string = "VarioUltra", |
| .type = BAUM_DEVICE_VarioUltra |
| }, |
| }; |
| |
| static const unsigned char baumDeviceIdentityCount = ARRAY_COUNT(baumDeviceIdentityTable); |
| static BaumDeviceType baumDeviceType; |
| |
| static void |
| setBaumDeviceType (const char *identity, size_t size) { |
| const BaumDeviceIdentityEntry *bdi = baumDeviceIdentityTable; |
| const BaumDeviceIdentityEntry *end = bdi + baumDeviceIdentityCount; |
| |
| while (bdi < end) { |
| size_t length = strlen(bdi->string); |
| const char *from = identity; |
| const char *to = from + size - length; |
| |
| while (from <= to) { |
| if (*from == *bdi->string) { |
| if (memcmp(from, bdi->string, length) == 0) { |
| baumDeviceType = bdi->type; |
| return; |
| } |
| } |
| |
| from += 1; |
| } |
| |
| bdi += 1; |
| } |
| } |
| |
| typedef enum { |
| BAUM_MODULE_Display80, |
| BAUM_MODULE_Display64, |
| BAUM_MODULE_Status, |
| BAUM_MODULE_Phone, |
| BAUM_MODULE_Audio, |
| BAUM_MODULE_Voice |
| } BaumModuleType; |
| |
| typedef struct { |
| uint16_t identifier; |
| unsigned char type; |
| unsigned char cellCount; |
| unsigned char keyCount; |
| unsigned char buttonCount; |
| unsigned char wheelCount; |
| unsigned char potCount; |
| unsigned isDisplay:1; |
| unsigned hasCursorKeys:1; |
| unsigned hasKeypad:1; |
| } BaumModuleDescription; |
| |
| static const BaumModuleDescription baumModuleDescriptions[] = { |
| { .identifier = 0X4180, |
| .type = BAUM_MODULE_Display80, |
| .cellCount = 80, |
| .wheelCount = 4, |
| .isDisplay = 1 |
| } |
| , |
| { .identifier = 0X4181, |
| .type = BAUM_MODULE_Display64, |
| .cellCount = 64, |
| .wheelCount = 3, |
| .isDisplay = 1 |
| } |
| , |
| { .identifier = 0X4190, |
| .type = BAUM_MODULE_Status, |
| .cellCount = 4, |
| .buttonCount = 4 |
| } |
| , |
| { .identifier = 0X4191, |
| .type = BAUM_MODULE_Phone, |
| .cellCount = 12, |
| .buttonCount = 4, |
| .wheelCount = 1, |
| .hasKeypad = 1 |
| } |
| , |
| { .identifier = 0X4192, |
| .type = BAUM_MODULE_Audio, |
| .keyCount = 5, |
| .wheelCount = 1, |
| .potCount = 6 |
| } |
| , |
| { .identifier = 0X4193, |
| .type = BAUM_MODULE_Voice, |
| .keyCount = 4, |
| .buttonCount = 3, |
| .potCount = 4, |
| .hasCursorKeys = 1 |
| } |
| , |
| { .identifier = 0 } |
| }; |
| |
| static const BaumModuleDescription * |
| getBaumModuleDescription (uint16_t identifier) { |
| const BaumModuleDescription *bmd = baumModuleDescriptions; |
| |
| while (bmd->identifier) { |
| if (bmd->identifier == identifier) return bmd; |
| bmd += 1; |
| } |
| |
| logMessage(LOG_DEBUG, "unknown module identifier: %04X", identifier); |
| return NULL; |
| } |
| |
| typedef struct { |
| const BaumModuleDescription *description; |
| uint16_t serialNumber; |
| uint16_t hardwareVersion; |
| uint16_t firmwareVersion; |
| } BaumModuleRegistration; |
| |
| static void |
| clearBaumModuleRegistration (BaumModuleRegistration *bmr) { |
| bmr->description = NULL; |
| bmr->serialNumber = 0; |
| bmr->hardwareVersion = 0; |
| bmr->firmwareVersion = 0; |
| } |
| |
| static BaumModuleRegistration baumDisplayModule; |
| static BaumModuleRegistration baumStatusModule; |
| |
| static BaumModuleRegistration *const baumModules[] = { |
| &baumDisplayModule, |
| &baumStatusModule, |
| NULL |
| }; |
| |
| static BaumModuleRegistration * |
| getBaumModuleRegistration (const BaumModuleDescription *bmd, uint16_t serialNumber) { |
| if (bmd) { |
| BaumModuleRegistration *const *bmr = baumModules; |
| |
| while (*bmr) { |
| if (((*bmr)->description == bmd) && ((*bmr)->serialNumber == serialNumber)) return *bmr; |
| bmr += 1; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static int |
| getBaumModuleCellCount (void) { |
| int count = 0; |
| |
| { |
| BaumModuleRegistration *const *bmr = baumModules; |
| |
| while (*bmr) { |
| const BaumModuleDescription *bmd = (*bmr++)->description; |
| if (bmd) count += bmd->cellCount; |
| } |
| } |
| |
| return count; |
| } |
| |
| static void |
| assumeBaumDeviceIdentity (const char *identity) { |
| logMessage(LOG_INFO, "Baum Device Identity: %s", identity); |
| } |
| |
| static void |
| handleBaumDeviceIdentity (const BaumResponsePacket *packet, int probing) { |
| const char *identity = packet->data.values.deviceIdentity; |
| size_t size = sizeof(packet->data.values.deviceIdentity); |
| |
| logTextField("Baum Device Identity", identity, size); |
| if (probing) setBaumDeviceType(identity, size); |
| } |
| |
| static void |
| logBaumSerialNumber (const BaumResponsePacket *packet) { |
| logTextField("Baum Serial Number", |
| packet->data.values.serialNumber, |
| sizeof(packet->data.values.serialNumber)); |
| } |
| |
| static int |
| logBaumPowerdownReason (BaumPowerdownReason reason) { |
| typedef struct { |
| BaumPowerdownReason bit; |
| const char *explanation; |
| } ReasonEntry; |
| |
| static const ReasonEntry reasonTable[] = { |
| {BAUM_PDR_ProtocolRequested, strtext("driver request")}, |
| {BAUM_PDR_PowerSwitch , strtext("power switch")}, |
| {BAUM_PDR_AutoPowerOff , strtext("idle timeout")}, |
| {BAUM_PDR_BatteryLow , strtext("battery low")}, |
| {0} |
| }; |
| |
| char buffer[0X100]; |
| char delimiter = ':'; |
| int length; |
| |
| STR_BEGIN(buffer, sizeof(buffer)); |
| STR_PRINTF("%s %s", STRINGIFY(DRIVER_NAME), gettext("Powerdown")); |
| |
| for (const ReasonEntry *entry=reasonTable; entry->bit; entry+=1) { |
| if (reason & entry->bit) { |
| STR_PRINTF("%c %s", delimiter, gettext(entry->explanation)); |
| delimiter = ','; |
| } |
| } |
| |
| length = STR_LENGTH; |
| STR_END; |
| |
| logMessage(LOG_WARNING, "%.*s", length, buffer); |
| return 1; |
| } |
| |
| static void |
| adjustPacketLength (const unsigned char *bytes, size_t size, size_t *length) { |
| switch (bytes[0]) { |
| case BAUM_RSP_DeviceIdentity: |
| if (size == 17) { |
| if (memcmp(&bytes[1], "Refreshabraille ", (size - 1)) == 0) { |
| *length += 2; |
| } else if (memcmp(&bytes[1], "NLS eReader Zoom", (size - 1)) == 0) { |
| *length += 2; |
| } |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| typedef enum { |
| BAUM_PVS_WAITING, |
| BAUM_PVS_STARTED, |
| BAUM_PVS_ESCAPED |
| } BaumPacketVerificationState; |
| |
| typedef struct { |
| BaumPacketVerificationState state; |
| } BaumPacketVerificationData; |
| |
| static BraillePacketVerifierResult |
| verifyBaumPacket ( |
| BrailleDisplay *brl, |
| unsigned char *bytes, size_t size, |
| size_t *length, void *data |
| ) { |
| BaumPacketVerificationData *pvd = data; |
| unsigned char byte = bytes[size-1]; |
| int escape = byte == ASCII_ESC; |
| |
| switch (pvd->state) { |
| case BAUM_PVS_WAITING: |
| if (!escape) return BRL_PVR_INVALID; |
| pvd->state = BAUM_PVS_STARTED; |
| return BRL_PVR_EXCLUDE; |
| |
| case BAUM_PVS_STARTED: |
| if (escape) { |
| pvd->state = BAUM_PVS_ESCAPED; |
| return BRL_PVR_EXCLUDE; |
| } |
| break; |
| |
| case BAUM_PVS_ESCAPED: |
| pvd->state = BAUM_PVS_STARTED; |
| break; |
| |
| default: |
| logMessage(LOG_NOTICE, "unexpected %s packet verification state: %u", |
| brl->data->protocol->name, pvd->state); |
| return BRL_PVR_INVALID; |
| } |
| |
| if (size == 1) { |
| switch (byte) { |
| case BAUM_RSP_Switches: |
| if (!cellCount) { |
| assumeBaumDeviceIdentity("DM80P"); |
| baumDeviceType = BAUM_DEVICE_DM80P; |
| cellCount = 84; |
| } |
| |
| case BAUM_RSP_CellCount: |
| case BAUM_RSP_VersionNumber: |
| case BAUM_RSP_CommunicationChannel: |
| case BAUM_RSP_PowerdownSignal: |
| case BAUM_RSP_DisplayKeys: |
| case BAUM_RSP_HorizontalSensor: |
| case BAUM_RSP_RoutingKey: |
| case BAUM_RSP_Front6: |
| case BAUM_RSP_Back6: |
| case BAUM_RSP_CommandKeys: |
| case BAUM_RSP_Joystick: |
| case BAUM_RSP_ErrorCode: |
| case BAUM_RSP_ModuleRegistration: |
| case BAUM_RSP_DataRegisters: |
| case BAUM_RSP_ServiceRegisters: |
| *length = 2; |
| break; |
| |
| case BAUM_RSP_ModeSetting: |
| case BAUM_RSP_Front10: |
| case BAUM_RSP_Back10: |
| case BAUM_RSP_EntryKeys: |
| *length = 3; |
| break; |
| |
| case BAUM_RSP_VerticalSensor: |
| *length = (baumDeviceType == BAUM_DEVICE_Inka)? 2: 3; |
| break; |
| |
| case BAUM_RSP_VerticalSensors: |
| case BAUM_RSP_SerialNumber: |
| *length = 9; |
| break; |
| |
| case BAUM_RSP_BluetoothName: |
| *length = 15; |
| break; |
| |
| case BAUM_RSP_DeviceIdentity: |
| *length = 17; |
| break; |
| |
| case BAUM_RSP_RoutingKeys: |
| if (!cellCount) { |
| assumeBaumDeviceIdentity("Inka"); |
| baumDeviceType = BAUM_DEVICE_Inka; |
| cellCount = 56; |
| } |
| |
| if (baumDeviceType == BAUM_DEVICE_Inka) { |
| *length = 2; |
| break; |
| } |
| |
| *length = brl->data->packetSize.routingKeys + 1; |
| break; |
| |
| case BAUM_RSP_HorizontalSensors: |
| *length = KEY_GROUP_SIZE(brl->textColumns) + 1; |
| break; |
| |
| case BAUM_RSP_NLS_ZMX_BD: |
| case BAUM_RSP_NLS_ZMX_BE: |
| *length = 2; |
| break; |
| |
| case BAUM_RSP_NLS_ZMX_BF: |
| *length = 2; |
| break; |
| |
| default: |
| pvd->state = BAUM_PVS_WAITING; |
| return BRL_PVR_INVALID; |
| } |
| } else if (size == 2) { |
| switch (bytes[0]) { |
| case BAUM_RSP_ModuleRegistration: |
| case BAUM_RSP_DataRegisters: |
| case BAUM_RSP_ServiceRegisters: |
| if (byte < 4) return BRL_PVR_INVALID; |
| *length += byte; |
| break; |
| |
| case BAUM_RSP_NLS_ZMX_BD: |
| case BAUM_RSP_NLS_ZMX_BE: |
| if (byte != ASCII_CR) return BRL_PVR_EXCLUDE; |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| adjustPacketLength(bytes, size, length); |
| return BRL_PVR_INCLUDE; |
| } |
| |
| static int |
| readBaumPacket (BrailleDisplay *brl, unsigned char *packet, int size) { |
| BaumPacketVerificationData pvd = { |
| .state = BAUM_PVS_WAITING |
| }; |
| |
| memset(packet, 0, size); |
| return readBraillePacket(brl, NULL, packet, size, verifyBaumPacket, &pvd); |
| } |
| |
| static int |
| getBaumPacket (BrailleDisplay *brl, BaumResponsePacket *packet) { |
| return readBaumPacket(brl, packet->bytes, sizeof(*packet)); |
| } |
| |
| static int |
| writeBaumPacket (BrailleDisplay *brl, const unsigned char *packet, int length) { |
| unsigned char buffer[1 + (length * 2)]; |
| unsigned char *byte = buffer; |
| *byte++ = ASCII_ESC; |
| |
| { |
| int index = 0; |
| while (index < length) |
| if ((*byte++ = packet[index++]) == ASCII_ESC) |
| *byte++ = ASCII_ESC; |
| } |
| |
| return writeBraillePacket(brl, NULL, buffer, (byte - buffer)); |
| } |
| |
| static int |
| writeBaumModuleRegistrationCommand ( |
| BrailleDisplay *brl, |
| uint16_t moduleIdentifier, uint16_t serialNumber, |
| BaumModuleRegistrationCommand command |
| ) { |
| const unsigned char request[] = { |
| BAUM_REQ_ModuleRegistration, |
| 5, /* data length */ |
| MAKE_BAUM_INTEGER(moduleIdentifier), |
| MAKE_BAUM_INTEGER(serialNumber), |
| command |
| }; |
| |
| return writeBaumPacket(brl, request, sizeof(request)); |
| } |
| |
| static int |
| writeBaumDataRegisters ( |
| BrailleDisplay *brl, |
| const BaumModuleRegistration *bmr, |
| const unsigned char *registers, |
| unsigned char start, unsigned char count |
| ) { |
| const BaumModuleDescription *bmd = bmr->description; |
| |
| if (bmd) { |
| if (count < bmd->cellCount) count = bmd->cellCount; |
| |
| if (count) { |
| unsigned char packet[2 + 7 + count]; |
| unsigned char *byte = packet; |
| |
| *byte++ = BAUM_REQ_DataRegisters; |
| *byte++ = 7 + count; |
| |
| *byte++ = MAKE_BAUM_INTEGER_FIRST(bmd->identifier); |
| *byte++ = MAKE_BAUM_INTEGER_SECOND(bmd->identifier); |
| |
| *byte++ = MAKE_BAUM_INTEGER_FIRST(bmr->serialNumber); |
| *byte++ = MAKE_BAUM_INTEGER_SECOND(bmr->serialNumber); |
| |
| *byte++ = BAUM_DRC_Write; |
| *byte++ = start; |
| *byte++ = count; |
| byte = mempcpy(byte, registers, count); |
| |
| if (!writeBaumPacket(brl, packet, byte-packet)) return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| typedef struct { |
| const KeyTableDefinition *keyTableDefinition; |
| int (*writeAllCells) (BrailleDisplay *brl); |
| int (*writeCellRange) (BrailleDisplay *brl, unsigned int start, unsigned int count); |
| } BaumDeviceOperations; |
| |
| static int |
| writeBaumCells_all (BrailleDisplay *brl) { |
| unsigned char packet[1 + cellCount]; |
| unsigned char *byte = packet; |
| |
| *byte++ = BAUM_REQ_DisplayData; |
| byte = mempcpy(byte, externalCells, cellCount); |
| |
| return writeBaumPacket(brl, packet, byte-packet); |
| } |
| |
| static int |
| writeBaumCells_start (BrailleDisplay *brl) { |
| unsigned char packet[1 + 1 + cellCount]; |
| unsigned char *byte = packet; |
| |
| *byte++ = BAUM_REQ_DisplayData; |
| *byte++ = 0; |
| byte = mempcpy(byte, externalCells, cellCount); |
| |
| return writeBaumPacket(brl, packet, byte-packet); |
| } |
| |
| static int |
| writeBaumCells_modular (BrailleDisplay *brl, unsigned int start, unsigned int count) { |
| if (start < brl->textColumns) { |
| unsigned int amount = MIN(count, brl->textColumns-start); |
| |
| if (amount > 0) { |
| if (!writeBaumDataRegisters(brl, &baumDisplayModule, &externalCells[start], start, amount)) return 0; |
| start += amount; |
| count -= amount; |
| } |
| } |
| |
| if (count > 0) { |
| if (!writeBaumDataRegisters(brl, &baumStatusModule, &externalCells[start], start-brl->textColumns, count)) return 0; |
| } |
| |
| return 1; |
| } |
| |
| static const BaumDeviceOperations baumDeviceOperations[] = { |
| [BAUM_DEVICE_Default] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(default), |
| .writeAllCells = writeBaumCells_all |
| }, |
| |
| [BAUM_DEVICE_Refreshabraille] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(rb), |
| .writeAllCells = writeBaumCells_all |
| }, |
| |
| [BAUM_DEVICE_Orbit] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(orbit), |
| .writeAllCells = writeBaumCells_all |
| }, |
| |
| [BAUM_DEVICE_NLS_Zoomax] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(NLS_Zoomax), |
| .writeAllCells = writeBaumCells_all |
| }, |
| |
| [BAUM_DEVICE_B2G] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(b2g), |
| .writeAllCells = writeBaumCells_all |
| }, |
| |
| [BAUM_DEVICE_Conny] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(conny), |
| .writeAllCells = writeBaumCells_all |
| }, |
| |
| [BAUM_DEVICE_PocketVario] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(pv), |
| .writeAllCells = writeBaumCells_all |
| }, |
| |
| [BAUM_DEVICE_Pronto] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(pronto), |
| .writeAllCells = writeBaumCells_all |
| }, |
| |
| [BAUM_DEVICE_SuperVario] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(sv), |
| .writeAllCells = writeBaumCells_all |
| }, |
| |
| [BAUM_DEVICE_VarioConnect] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(connect), |
| .writeAllCells = writeBaumCells_all |
| }, |
| |
| [BAUM_DEVICE_VarioUltra] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(ultra), |
| .writeAllCells = writeBaumCells_all |
| }, |
| |
| [BAUM_DEVICE_Inka] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(inka), |
| .writeAllCells = writeBaumCells_start |
| }, |
| |
| [BAUM_DEVICE_DM80P] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(dm80p), |
| .writeAllCells = writeBaumCells_start |
| }, |
| |
| [BAUM_DEVICE_Vario40] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(v40), |
| .writeAllCells = writeBaumCells_all |
| }, |
| |
| [BAUM_DEVICE_Vario80] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(v80), |
| .writeAllCells = writeBaumCells_all |
| }, |
| |
| [BAUM_DEVICE_Modular] = { |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(pro), |
| .writeCellRange = writeBaumCells_modular |
| } |
| }; |
| |
| static int |
| setBaumMode (BrailleDisplay *brl, unsigned char mode, unsigned char setting) { |
| const unsigned char request[] = {BAUM_REQ_SetMode, mode, setting}; |
| return writeBaumPacket(brl, request, sizeof(request)); |
| } |
| |
| static void |
| setBaumSwitches (BrailleDisplay *brl, unsigned char newSettings, int initialize) { |
| unsigned char changedSettings = newSettings ^ switchSettings; |
| switchSettings = newSettings; |
| |
| { |
| typedef struct { |
| unsigned char switchBit; |
| unsigned char modeNumber; |
| unsigned char offValue; |
| unsigned char onValue; |
| } SwitchEntry; |
| |
| static const SwitchEntry switchTable[] = { |
| {BAUM_SWT_ShowSensor, 0X01, 0, 2}, |
| {BAUM_SWT_BrailleKeyboard, 0X03, 0, 3}, |
| {0} |
| }; |
| const SwitchEntry *entry = switchTable; |
| |
| while (entry->switchBit) { |
| if (initialize || (changedSettings & entry->switchBit)) |
| setBaumMode(brl, entry->modeNumber, |
| ((switchSettings & entry->switchBit)? entry->onValue: |
| entry->offValue)); |
| ++entry; |
| } |
| } |
| } |
| |
| static void |
| setInkaSwitches (BrailleDisplay *brl, unsigned char newSettings, int initialize) { |
| newSettings ^= 0X0F; |
| setBaumSwitches(brl, ((newSettings & 0X03) | ((newSettings & 0X0C) << 4)), initialize); |
| } |
| |
| static int |
| handleBaumModuleRegistrationEvent (BrailleDisplay *brl, const BaumResponsePacket *packet) { |
| uint16_t moduleIdentifier = getBaumInteger(packet->data.values.modular.moduleIdentifier); |
| uint16_t serialNumber = getBaumInteger(packet->data.values.modular.serialNumber); |
| const BaumModuleDescription *bmd = getBaumModuleDescription(moduleIdentifier); |
| |
| if (packet->data.values.modular.data.registration.event == BAUM_MRE_Addition) { |
| if (!writeBaumModuleRegistrationCommand(brl, |
| moduleIdentifier, serialNumber, |
| BAUM_MRC_Acknowledge)) { |
| return 0; |
| } |
| |
| if (bmd) { |
| BaumModuleRegistration *bmr; |
| |
| if (bmd->isDisplay) { |
| bmr = &baumDisplayModule; |
| } else if (bmd->type == BAUM_MODULE_Status) { |
| bmr = &baumStatusModule; |
| } else { |
| bmr = NULL; |
| } |
| |
| if (bmr) { |
| if (bmr->description) clearBaumModuleRegistration(bmr); |
| |
| bmr->description = bmd; |
| bmr->serialNumber = serialNumber; |
| bmr->hardwareVersion = getBaumInteger(packet->data.values.modular.data.registration.hardwareVersion); |
| bmr->firmwareVersion = getBaumInteger(packet->data.values.modular.data.registration.firmwareVersion); |
| } |
| } |
| } else { |
| BaumModuleRegistration *bmr = getBaumModuleRegistration(bmd, serialNumber); |
| if (bmr) clearBaumModuleRegistration(bmr); |
| } |
| |
| return 1; |
| } |
| |
| static void |
| handleBaumDataRegistersEvent (BrailleDisplay *brl, const BaumResponsePacket *packet) { |
| const BaumModuleDescription *bmd = getBaumModuleDescription(getBaumInteger(packet->data.values.modular.moduleIdentifier)); |
| const BaumModuleRegistration *bmr = getBaumModuleRegistration(bmd, getBaumInteger(packet->data.values.modular.serialNumber)); |
| |
| if (bmr) { |
| switch (bmd->type) { |
| { |
| unsigned char flags; |
| unsigned char UNUSED errors; |
| const signed char *wheel; |
| unsigned char wheels; |
| unsigned char buttons; |
| unsigned char keys; |
| const unsigned char *sensors; |
| |
| case BAUM_MODULE_Display80: |
| flags = packet->data.values.modular.data.registers.display80.flags; |
| errors = packet->data.values.modular.data.registers.display80.errors; |
| wheel = packet->data.values.modular.data.registers.display80.wheels; |
| wheels = ARRAY_COUNT(packet->data.values.modular.data.registers.display80.wheels); |
| buttons = packet->data.values.modular.data.registers.display80.buttons; |
| keys = packet->data.values.modular.data.registers.display80.keys; |
| sensors = packet->data.values.modular.data.registers.display80.sensors; |
| goto doDisplay; |
| |
| case BAUM_MODULE_Display64: |
| flags = packet->data.values.modular.data.registers.display64.flags; |
| errors = packet->data.values.modular.data.registers.display64.errors; |
| wheel = packet->data.values.modular.data.registers.display64.wheels; |
| wheels = ARRAY_COUNT(packet->data.values.modular.data.registers.display64.wheels); |
| buttons = packet->data.values.modular.data.registers.display64.buttons; |
| keys = packet->data.values.modular.data.registers.display64.keys; |
| sensors = packet->data.values.modular.data.registers.display64.sensors; |
| goto doDisplay; |
| |
| doDisplay: |
| if (flags & BAUM_DRF_WheelsChanged) { |
| unsigned int index; |
| |
| for (index=0; index<wheels; index+=1) { |
| signed char count = wheel[index]; |
| |
| while (count > 0) { |
| enqueueKey(brl, BM_GRP_NavigationKeys, (BM_KEY_WHEEL_UP + index)); |
| count -= 1; |
| } |
| |
| while (count < 0) { |
| enqueueKey(brl, BM_GRP_NavigationKeys, (BM_KEY_WHEEL_DOWN + index)); |
| count += 1; |
| } |
| } |
| } |
| |
| if (flags & BAUM_DRF_ButtonsChanged) { |
| updateNavigationKeys(brl, &buttons, BM_KEY_WHEEL_PRESS, wheels); |
| } |
| |
| if (flags & BAUM_DRF_KeysChanged) { |
| updateDisplayKeys(brl, keys); |
| } |
| |
| if (flags & BAUM_DRF_SensorsChanged) { |
| updateRoutingKeys(brl, sensors, brl->textColumns); |
| } |
| |
| break; |
| } |
| |
| case BAUM_MODULE_Status: |
| if (packet->data.values.modular.data.registers.status.flags & BAUM_DRF_ButtonsChanged) { |
| updateNavigationKeys(brl, &packet->data.values.modular.data.registers.status.buttons, |
| BM_KEY_STATUS, BM_KEYS_STATUS); |
| } |
| |
| break; |
| |
| default: |
| logMessage(LOG_WARNING, "unsupported data register configuration: %u", bmd->type); |
| break; |
| } |
| } |
| } |
| |
| static int |
| getIdentityCellCount (char* deviceIdentity, const int length) { |
| char buffer[length+1]; |
| memcpy(buffer, deviceIdentity, length); |
| buffer[length] = 0; |
| |
| char *digits = strpbrk(buffer, "123456789"); |
| |
| if (digits) { |
| int count = atoi(digits); |
| if (isAcceptableCellCount(count)) return count; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| probeBaumDevice (BrailleDisplay *brl) { |
| int probes = 0; |
| |
| do { |
| int identityCellCount = 0; |
| |
| baumDeviceType = BAUM_DEVICE_Default; |
| cellCount = 0; |
| |
| { |
| BaumModuleRegistration *const *bmr = baumModules; |
| while (*bmr) clearBaumModuleRegistration(*bmr++); |
| } |
| |
| /* get the serial number for the log */ |
| { |
| static const unsigned char request[] = {BAUM_REQ_GetSerialNumber}; |
| if (!writeBaumPacket(brl, request, sizeof(request))) break; |
| } |
| |
| /* newer models return an identity string which contains the cell count */ |
| { |
| static const unsigned char request[] = {BAUM_REQ_GetDeviceIdentity}; |
| if (!writeBaumPacket(brl, request, sizeof(request))) break; |
| } |
| |
| /* try explicitly asking for the cell count */ |
| { |
| static const unsigned char request[] = {BAUM_REQ_DisplayData, 0}; |
| if (!writeBaumPacket(brl, request, sizeof(request))) break; |
| } |
| |
| /* enqueue a request to get the initial key states */ |
| { |
| static const unsigned char request[] = {BAUM_REQ_GetKeys}; |
| if (!writeBaumPacket(brl, request, sizeof(request))) break; |
| } |
| |
| /* the modular models need to be probed with a general call */ |
| if (!writeBaumModuleRegistrationCommand(brl, 0, 0, BAUM_MRC_Query)) break; |
| |
| while (awaitBrailleInput(brl, probeTimeout)) { |
| BaumResponsePacket response; |
| int size = getBaumPacket(brl, &response); |
| |
| if (size) { |
| switch (response.data.code) { |
| case BAUM_RSP_VersionNumber: |
| continue; |
| |
| case BAUM_RSP_RoutingKeys: /* Inka */ |
| setInkaSwitches(brl, response.data.values.switches, 1); |
| return 1; |
| |
| case BAUM_RSP_Switches: /* DM80P */ |
| setBaumSwitches(brl, response.data.values.switches, 1); |
| return 1; |
| |
| case BAUM_RSP_CellCount: { /* newer models */ |
| unsigned char count = response.data.values.cellCount; |
| |
| if (isAcceptableCellCount(count)) { |
| cellCount = count; |
| return 1; |
| } |
| |
| logUnexpectedCellCount(count); |
| continue; |
| } |
| |
| case BAUM_RSP_ModuleRegistration: /* modular models */ |
| if (!handleBaumModuleRegistrationEvent(brl, &response)) return 0; |
| if (!baumDisplayModule.description) continue; |
| baumDeviceType = BAUM_DEVICE_Modular; |
| cellCount = getBaumModuleCellCount(); |
| return 1; |
| |
| case BAUM_RSP_DeviceIdentity: { |
| /* should contain fallback cell count */ |
| int count = getIdentityCellCount(response.data.values.deviceIdentity, |
| sizeof(response.data.values.deviceIdentity)); |
| if (count) identityCellCount = count; |
| handleBaumDeviceIdentity(&response, 1); |
| continue; |
| } |
| |
| case BAUM_RSP_SerialNumber: |
| logBaumSerialNumber(&response); |
| continue; |
| |
| case BAUM_RSP_ErrorCode: |
| if (response.data.values.errorCode != BAUM_ERR_PacketType) goto unexpectedPacket; |
| logMessage(LOG_DEBUG, "unsupported request"); |
| continue; |
| |
| default: |
| unexpectedPacket: |
| logUnexpectedPacket(response.bytes, size); |
| continue; |
| } |
| } else if (errno != EAGAIN) { |
| break; |
| } |
| } |
| if (errno != EAGAIN) break; |
| |
| if (identityCellCount) { |
| /* Older models don't provide the actual cell count |
| * so it must be derived from the identity string. |
| */ |
| switch ((cellCount = identityCellCount)) { |
| case 80: /* probably a Vario 80 */ |
| baumDeviceType = BAUM_DEVICE_Vario80; |
| cellCount += 4; |
| break; |
| } |
| |
| return 1; |
| } |
| } while (++probes < probeLimit); |
| |
| return 0; |
| } |
| |
| static void |
| processBaumPackets (BrailleDisplay *brl) { |
| BaumResponsePacket packet; |
| int size; |
| |
| while ((size = getBaumPacket(brl, &packet))) { |
| switch (packet.data.code) { |
| case BAUM_RSP_CellCount: |
| if (!changeCellCount(brl, packet.data.values.cellCount)) return; |
| continue; |
| |
| case BAUM_RSP_DeviceIdentity: |
| handleBaumDeviceIdentity(&packet, 0); |
| continue; |
| |
| case BAUM_RSP_SerialNumber: |
| logBaumSerialNumber(&packet); |
| continue; |
| |
| case BAUM_RSP_CommunicationChannel: |
| continue; |
| |
| case BAUM_RSP_PowerdownSignal: |
| if (!logBaumPowerdownReason(packet.data.values.powerdownReason)) continue; |
| errno = ENODEV; |
| return; |
| |
| case BAUM_RSP_DisplayKeys: { |
| unsigned char keys; |
| unsigned char UNUSED count = 6; |
| |
| switch (baumDeviceType) { |
| case BAUM_DEVICE_Inka: |
| keys = 0; |
| #define KEY(inka,baum) if (!(packet.data.values.displayKeys & (inka))) keys |= (baum) |
| KEY(004, 001); |
| KEY(002, 002); |
| KEY(001, 004); |
| KEY(040, 010); |
| KEY(020, 020); |
| KEY(010, 040); |
| #undef KEY |
| break; |
| |
| case BAUM_DEVICE_DM80P: |
| keys = packet.data.values.displayKeys ^ 0X7F; |
| count = 7; |
| break; |
| |
| case BAUM_DEVICE_Orbit: |
| count = 8; |
| /* fall through */ |
| |
| default: |
| keys = packet.data.values.displayKeys; |
| break; |
| } |
| |
| updateDisplayKeys(brl, keys); |
| continue; |
| } |
| |
| case BAUM_RSP_CommandKeys: |
| updateNavigationKeys(brl, packet.data.values.commandKeys, |
| BM_KEY_COMMAND, BM_KEYS_COMMAND); |
| continue; |
| |
| case BAUM_RSP_Front6: |
| updateNavigationKeys(brl, packet.data.values.front6, |
| BM_KEY_FRONT, 6); |
| continue; |
| |
| case BAUM_RSP_Back6: |
| updateNavigationKeys(brl, packet.data.values.back6, |
| BM_KEY_BACK, 6); |
| continue; |
| |
| case BAUM_RSP_Front10: { |
| unsigned char keys[2]; |
| keys[0] = packet.data.values.front10[1]; |
| keys[1] = packet.data.values.front10[0]; |
| updateNavigationKeys(brl, keys, BM_KEY_FRONT, 10); |
| continue; |
| } |
| |
| case BAUM_RSP_Back10: { |
| unsigned char keys[2]; |
| keys[0] = packet.data.values.back10[1]; |
| keys[1] = packet.data.values.back10[0]; |
| updateNavigationKeys(brl, keys, BM_KEY_BACK, 10); |
| continue; |
| } |
| |
| case BAUM_RSP_EntryKeys: |
| updateEntryKeys(brl, packet.data.values.entryKeys); |
| continue; |
| |
| case BAUM_RSP_Joystick: |
| updateJoystick(brl, packet.data.values.joystick); |
| continue; |
| |
| case BAUM_RSP_HorizontalSensor: |
| resetKeyGroup(packet.data.values.horizontalSensors, brl->textColumns, packet.data.values.horizontalSensor); |
| case BAUM_RSP_HorizontalSensors: |
| if (!(switchSettings & BAUM_SWT_DisableSensors)) { |
| updateKeyGroup(brl, keysState.horizontalSensors, packet.data.values.horizontalSensors, |
| BM_GRP_HorizontalSensors, 0, brl->textColumns, 0); |
| } |
| continue; |
| |
| case BAUM_RSP_VerticalSensor: { |
| unsigned char left = packet.data.values.verticalSensor.left; |
| unsigned char right; |
| |
| if (baumDeviceType != BAUM_DEVICE_Inka) { |
| right = packet.data.values.verticalSensor.right; |
| } else if (left & 0X40) { |
| left -= 0X40; |
| right = 0; |
| } else { |
| right = left; |
| left = 0; |
| } |
| |
| resetKeyGroup(packet.data.values.verticalSensors.left, VERTICAL_SENSOR_COUNT, left); |
| resetKeyGroup(packet.data.values.verticalSensors.right, VERTICAL_SENSOR_COUNT, right); |
| } |
| |
| case BAUM_RSP_VerticalSensors: |
| if (!(switchSettings & BAUM_SWT_DisableSensors)) { |
| int scaled = (switchSettings & BAUM_SWT_ScaledVertical) != 0; |
| |
| updateKeyGroup(brl, keysState.leftSensors, packet.data.values.verticalSensors.left, |
| (scaled? BM_GRP_ScaledLeftSensors: BM_GRP_LeftSensors), |
| 0, VERTICAL_SENSOR_COUNT, scaled); |
| updateKeyGroup(brl, keysState.rightSensors, packet.data.values.verticalSensors.right, |
| (scaled? BM_GRP_ScaledRightSensors: BM_GRP_RightSensors), |
| 0, VERTICAL_SENSOR_COUNT, scaled); |
| } |
| continue; |
| |
| case BAUM_RSP_RoutingKey: |
| resetKeyGroup(packet.data.values.routingKeys, cellCount, packet.data.values.routingKey); |
| goto doRoutingKeys; |
| |
| case BAUM_RSP_RoutingKeys: |
| if (baumDeviceType == BAUM_DEVICE_Inka) { |
| setInkaSwitches(brl, packet.data.values.switches, 0); |
| continue; |
| } |
| |
| doRoutingKeys: |
| updateRoutingKeys(brl, packet.data.values.routingKeys, cellCount); |
| continue; |
| |
| case BAUM_RSP_Switches: |
| setBaumSwitches(brl, packet.data.values.switches, 0); |
| continue; |
| |
| case BAUM_RSP_ModuleRegistration: |
| if (handleBaumModuleRegistrationEvent(brl, &packet)) { |
| } |
| |
| if (!changeCellCount(brl, getBaumModuleCellCount())) return; |
| continue; |
| |
| case BAUM_RSP_DataRegisters: |
| handleBaumDataRegistersEvent(brl, &packet); |
| continue; |
| |
| case BAUM_RSP_ErrorCode: |
| if (packet.data.values.errorCode != BAUM_ERR_PacketType) goto unexpectedPacket; |
| logMessage(LOG_DEBUG, "unsupported request"); |
| continue; |
| |
| case BAUM_RSP_NLS_ZMX_BD: |
| case BAUM_RSP_NLS_ZMX_BE: |
| case BAUM_RSP_NLS_ZMX_BF: |
| continue; |
| |
| default: |
| unexpectedPacket: |
| logUnexpectedPacket(packet.bytes, size); |
| continue; |
| } |
| } |
| } |
| |
| static int |
| writeBaumCells (BrailleDisplay *brl) { |
| const BaumDeviceOperations *bdo = &baumDeviceOperations[baumDeviceType]; |
| if (!bdo->writeAllCells) return 1; |
| return bdo->writeAllCells(brl); |
| } |
| |
| static int |
| writeBaumCellRange (BrailleDisplay *brl, unsigned int start, unsigned int count) { |
| const BaumDeviceOperations *bdo = &baumDeviceOperations[baumDeviceType]; |
| if (!bdo->writeCellRange) return 1; |
| return bdo->writeCellRange(brl, start, count); |
| } |
| |
| static const ProtocolOperations baumEscapeOperations = { |
| .name = "Baum Escape", |
| .dotsTable = &dotsTable_ISO11548_1, |
| |
| .serialBaud = 19200, |
| .serialParity = SERIAL_PARITY_NONE, |
| |
| .readPacket = readBaumPacket, |
| .writePacket = writeBaumPacket, |
| |
| .probeDevice = probeBaumDevice, |
| .processPackets = processBaumPackets, |
| |
| .writeCells = writeBaumCells, |
| .writeCellRange = writeBaumCellRange |
| }; |
| |
| /* HID Protocol */ |
| |
| typedef union { |
| unsigned char bytes[0]; |
| |
| struct { |
| unsigned char type; |
| |
| union { |
| unsigned char cellCount[16]; |
| unsigned char routingKeys[16]; |
| unsigned char displayKeys[16]; |
| unsigned char routingKey[16]; |
| unsigned char entryKeys[16]; |
| unsigned char joystick[16]; |
| char deviceIdentity[BAUM_LENGTH_DeviceIdentity]; |
| char serialNumber[BAUM_LENGTH_SerialNumber]; |
| } data; |
| } PACKED fields; |
| } HidResponsePacket; |
| |
| typedef struct { |
| struct { |
| const unsigned char *table; |
| unsigned char count; |
| } const packetLengths; |
| } HidPacketVerificationData; |
| |
| static BraillePacketVerifierResult |
| verifyHidPacket ( |
| BrailleDisplay *brl, |
| unsigned char *bytes, size_t size, |
| size_t *length, void *data |
| ) { |
| HidPacketVerificationData *pvd = data; |
| unsigned char byte = bytes[size-1]; |
| |
| if (size == 1) { |
| if (byte < pvd->packetLengths.count) { |
| unsigned char l = pvd->packetLengths.table[byte]; |
| |
| if (l) { |
| *length = l; |
| return BRL_PVR_INCLUDE; |
| } |
| } |
| |
| if (!cellCount) return BRL_PVR_INVALID; |
| |
| switch (byte) { |
| case BAUM_RSP_RoutingKeys: |
| *length = brl->data->packetSize.routingKeys + 1; |
| break; |
| |
| default: |
| return BRL_PVR_INVALID; |
| } |
| } else { |
| adjustPacketLength(bytes, size, length); |
| } |
| |
| return BRL_PVR_INCLUDE; |
| } |
| |
| static int |
| readHid1Packet (BrailleDisplay *brl, unsigned char *packet, int size) { |
| static const unsigned char packetLengths[] = { |
| [BAUM_RSP_CellCount] = 2, |
| [BAUM_RSP_DisplayKeys] = 2, |
| [BAUM_RSP_RoutingKey] = 2, |
| [BAUM_RSP_EntryKeys] = 3, |
| [BAUM_RSP_Joystick] = 2, |
| [BAUM_RSP_DeviceIdentity] = 17, |
| [BAUM_RSP_SerialNumber] = 9, |
| }; |
| |
| HidPacketVerificationData pvd = { |
| .packetLengths = { |
| .table = packetLengths, |
| .count = ARRAY_COUNT(packetLengths) |
| } |
| }; |
| |
| memset(packet, 0, size); |
| return readBraillePacket(brl, NULL, packet, size, verifyHidPacket, &pvd); |
| } |
| |
| static int |
| readHid2Packet (BrailleDisplay *brl, unsigned char *packet, int size) { |
| static const unsigned char packetLengths[] = { |
| [BAUM_RSP_CellCount] = 17, |
| [BAUM_RSP_DisplayKeys] = 17, |
| [BAUM_RSP_RoutingKey] = 17, |
| [BAUM_RSP_EntryKeys] = 17, |
| [BAUM_RSP_Joystick] = 17, |
| [BAUM_RSP_DeviceIdentity] = 17, |
| [BAUM_RSP_SerialNumber] = 17, |
| }; |
| |
| HidPacketVerificationData pvd = { |
| .packetLengths = { |
| .table = packetLengths, |
| .count = ARRAY_COUNT(packetLengths) |
| } |
| }; |
| |
| memset(packet, 0, size); |
| return readBraillePacket(brl, NULL, packet, size, verifyHidPacket, &pvd); |
| } |
| |
| static int |
| getHidPacket (BrailleDisplay *brl, HidResponsePacket *packet) { |
| return brl->data->protocol->readPacket(brl, packet->bytes, sizeof(*packet)); |
| } |
| |
| static int |
| writeHidPacket (BrailleDisplay *brl, const unsigned char *packet, int length) { |
| return writeBraillePacket(brl, NULL, packet, length); |
| } |
| |
| static void |
| handleHidDeviceIdentity (const HidResponsePacket *packet, int probing) { |
| const char *identity = packet->fields.data.deviceIdentity; |
| size_t size = sizeof(packet->fields.data.deviceIdentity); |
| |
| logTextField("Baum Device Identity", identity, size); |
| if (probing) setBaumDeviceType(identity, size); |
| } |
| |
| static void |
| logHidSerialNumber (const HidResponsePacket *packet) { |
| logTextField("Baum Serial Number", |
| packet->fields.data.serialNumber, |
| sizeof(packet->fields.data.serialNumber)); |
| } |
| |
| static int |
| probeHidDevice (BrailleDisplay *brl) { |
| static const unsigned char packet[] = {0X02, 0X00}; |
| |
| if (writeBraillePacket(brl, NULL, packet, sizeof(packet))) { |
| int haveCellCount = 0; |
| int haveDeviceIdentity = 0; |
| int identityCellCount = 0; |
| |
| baumDeviceType = BAUM_DEVICE_Default; |
| cellCount = 0; |
| |
| while (awaitBrailleInput(brl, probeTimeout)) { |
| HidResponsePacket packet; |
| size_t size = getHidPacket(brl, &packet); |
| if (!size) break; |
| |
| switch (packet.fields.type) { |
| case BAUM_RSP_CellCount: { |
| unsigned char count = packet.fields.data.cellCount[0]; |
| |
| if (isAcceptableCellCount(count)) { |
| cellCount = count; |
| haveCellCount = 1; |
| } else { |
| logUnexpectedCellCount(count); |
| } |
| |
| break; |
| } |
| |
| case BAUM_RSP_DeviceIdentity: { |
| int count = getIdentityCellCount(packet.fields.data.deviceIdentity, |
| sizeof(packet.fields.data.deviceIdentity)); |
| if (count) identityCellCount = count; |
| handleHidDeviceIdentity(&packet, 1); |
| haveDeviceIdentity = 1; |
| break; |
| } |
| |
| case BAUM_RSP_SerialNumber: |
| logHidSerialNumber(&packet); |
| break; |
| |
| default: |
| logUnexpectedPacket(packet.bytes, size); |
| break; |
| } |
| |
| if (haveCellCount && haveDeviceIdentity) return 1; |
| } |
| |
| if (!cellCount && identityCellCount) { |
| /* Older models don't provide the actual cell count |
| * so it must be derived from the identity string. |
| */ |
| cellCount = identityCellCount; |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void |
| processHidPackets (BrailleDisplay *brl) { |
| HidResponsePacket packet; |
| size_t size; |
| |
| while ((size = getHidPacket(brl, &packet))) { |
| switch (packet.fields.type) { |
| case BAUM_RSP_CellCount: |
| if (!changeCellCount(brl, packet.fields.data.cellCount[0])) return; |
| continue; |
| |
| case BAUM_RSP_RoutingKey: |
| resetKeyGroup(packet.fields.data.routingKeys, cellCount, packet.fields.data.routingKey[0]); |
| case BAUM_RSP_RoutingKeys: |
| updateRoutingKeys(brl, packet.fields.data.routingKeys, cellCount); |
| continue; |
| |
| case BAUM_RSP_DisplayKeys: |
| updateDisplayKeys(brl, packet.fields.data.displayKeys[0]); |
| continue; |
| |
| case BAUM_RSP_EntryKeys: |
| updateEntryKeys(brl, packet.fields.data.entryKeys); |
| continue; |
| |
| case BAUM_RSP_Joystick: |
| updateJoystick(brl, packet.fields.data.joystick); |
| continue; |
| |
| case BAUM_RSP_DeviceIdentity: |
| handleHidDeviceIdentity(&packet, 0); |
| continue; |
| |
| case BAUM_RSP_SerialNumber: |
| logHidSerialNumber(&packet); |
| continue; |
| |
| default: |
| logUnexpectedPacket(packet.bytes, size); |
| continue; |
| } |
| } |
| } |
| |
| static int |
| writeHidCells (BrailleDisplay *brl) { |
| unsigned char packet[1 + cellCount]; |
| unsigned char *byte = packet; |
| |
| *byte++ = BAUM_REQ_DisplayData; |
| byte = mempcpy(byte, externalCells, cellCount); |
| |
| return writeHidPacket(brl, packet, byte-packet); |
| } |
| |
| static int |
| writeHidCellRange (BrailleDisplay *brl, unsigned int start, unsigned int count) { |
| return 1; |
| } |
| |
| static const ProtocolOperations baumHid1Operations = { |
| .name = "Baum HID1", |
| .dotsTable = &dotsTable_ISO11548_1, |
| |
| .readPacket = readHid1Packet, |
| .writePacket = writeHidPacket, |
| |
| .probeDevice = probeHidDevice, |
| .processPackets = processHidPackets, |
| |
| .writeCells = writeHidCells, |
| .writeCellRange = writeHidCellRange |
| }; |
| |
| static const ProtocolOperations baumHid2Operations = { |
| .name = "Baum HID2", |
| .dotsTable = &dotsTable_ISO11548_1, |
| |
| .readPacket = readHid2Packet, |
| .writePacket = writeHidPacket, |
| |
| .probeDevice = probeHidDevice, |
| .processPackets = processHidPackets, |
| |
| .writeCells = writeHidCells, |
| .writeCellRange = writeHidCellRange |
| }; |
| |
| /* HandyTech Protocol */ |
| |
| typedef enum { |
| HT_REQ_WRITE = 0X01, |
| HT_REQ_RESET = 0XFF |
| } HandyTechRequestCode; |
| |
| typedef enum { |
| HT_RSP_KEY_B1 = 0X03, |
| HT_RSP_KEY_Up = 0X04, |
| HT_RSP_KEY_B2 = 0X07, |
| HT_RSP_KEY_Dn = 0X08, |
| HT_RSP_KEY_B3 = 0X0B, |
| HT_RSP_KEY_B4 = 0X0F, |
| HT_RSP_KEY_CR1 = 0X20, |
| HT_RSP_WRITE_ACK = 0X7E, |
| HT_RSP_RELEASE = 0X80, |
| HT_RSP_IDENTITY = 0XFE |
| } HandyTechResponseCode; |
| #define HT_IS_ROUTING_KEY(code) (((code) >= HT_RSP_KEY_CR1) && ((code) < (HT_RSP_KEY_CR1 + brl->textColumns))) |
| |
| typedef union { |
| unsigned char bytes[2]; |
| |
| struct { |
| unsigned char code; |
| |
| union { |
| unsigned char identity; |
| } PACKED values; |
| } PACKED data; |
| } PACKED HandyTechResponsePacket; |
| |
| typedef struct { |
| const char *name; |
| unsigned char identity; |
| unsigned char textCount; |
| unsigned char statusCount; |
| } HandyTechModelEntry; |
| |
| static const HandyTechModelEntry handyTechModelTable[] = { |
| { "Modular 80", |
| 0X88, 80, 4 |
| } |
| , |
| { "Modular 40", |
| 0X89, 40, 4 |
| } |
| , |
| {NULL} |
| }; |
| static const HandyTechModelEntry *ht; |
| |
| static int |
| readHandyTechPacket (BrailleDisplay *brl, unsigned char *packet, int size) { |
| int offset = 0; |
| int length = 0; |
| |
| while (1) { |
| unsigned char byte; |
| |
| if (!gioReadByte(brl->gioEndpoint, &byte, offset>0)) { |
| if (offset > 0) logPartialPacket(packet, offset); |
| return 0; |
| } |
| |
| if (offset < size) { |
| if (offset == 0) { |
| switch (byte) { |
| case HT_RSP_IDENTITY: |
| length = 2; |
| break; |
| |
| case HT_RSP_WRITE_ACK: |
| length = 1; |
| break; |
| |
| default: { |
| unsigned char key = byte & ~HT_RSP_RELEASE; |
| switch (key) { |
| default: |
| if (!HT_IS_ROUTING_KEY(key)) { |
| logUnknownPacket(byte); |
| continue; |
| } |
| |
| case HT_RSP_KEY_Up: |
| case HT_RSP_KEY_Dn: |
| case HT_RSP_KEY_B1: |
| case HT_RSP_KEY_B2: |
| case HT_RSP_KEY_B3: |
| case HT_RSP_KEY_B4: |
| length = 1; |
| break; |
| } |
| break; |
| } |
| } |
| } |
| |
| packet[offset] = byte; |
| } else { |
| if (offset == size) logTruncatedPacket(packet, offset); |
| logDiscardedByte(byte); |
| } |
| |
| if (++offset == length) { |
| if (offset > size) { |
| offset = 0; |
| length = 0; |
| continue; |
| } |
| |
| logInputPacket(packet, offset); |
| return length; |
| } |
| } |
| } |
| |
| static int |
| getHandyTechPacket (BrailleDisplay *brl, HandyTechResponsePacket *packet) { |
| return readHandyTechPacket(brl, packet->bytes, sizeof(*packet)); |
| } |
| |
| static int |
| writeHandyTechPacket (BrailleDisplay *brl, const unsigned char *packet, int length) { |
| return writeBraillePacket(brl, NULL, packet, length); |
| } |
| |
| static const HandyTechModelEntry * |
| findHandyTechModel (unsigned char identity) { |
| const HandyTechModelEntry *model; |
| |
| for (model=handyTechModelTable; model->name; ++model) { |
| if (identity == model->identity) { |
| logMessage(LOG_INFO, "Baum emulation: HandyTech Model: %02X -> %s", identity, model->name); |
| return model; |
| } |
| } |
| |
| logMessage(LOG_WARNING, "Baum emulation: unknown HandyTech identity code: %02X", identity); |
| return NULL; |
| } |
| |
| static int |
| probeHandyTechDevice (BrailleDisplay *brl) { |
| int probes = 0; |
| static const unsigned char request[] = {HT_REQ_RESET}; |
| while (writeHandyTechPacket(brl, request, sizeof(request))) { |
| while (awaitBrailleInput(brl, probeTimeout)) { |
| HandyTechResponsePacket response; |
| if (getHandyTechPacket(brl, &response)) { |
| if (response.data.code == HT_RSP_IDENTITY) { |
| if (!(ht = findHandyTechModel(response.data.values.identity))) return 0; |
| cellCount = ht->textCount; |
| return 1; |
| } |
| } |
| } |
| if (errno != EAGAIN) break; |
| if (++probes == probeLimit) break; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| processHandyTechPackets (BrailleDisplay *brl) { |
| HandyTechResponsePacket packet; |
| int size; |
| |
| while ((size = getHandyTechPacket(brl, &packet))) { |
| unsigned char code = packet.data.code; |
| |
| switch (code) { |
| case HT_RSP_IDENTITY: { |
| const HandyTechModelEntry *model = findHandyTechModel(packet.data.values.identity); |
| if (model && (model != ht)) { |
| ht = model; |
| if (!changeCellCount(brl, ht->textCount)) return; |
| } |
| continue; |
| } |
| |
| case HT_RSP_WRITE_ACK: |
| continue; |
| } |
| |
| { |
| unsigned char *set; |
| KeyGroup group; |
| unsigned char key = code & ~HT_RSP_RELEASE; |
| int press = (code & HT_RSP_RELEASE) == 0; |
| |
| if (HT_IS_ROUTING_KEY(key)) { |
| set = keysState.routingKeys; |
| group = BM_GRP_RoutingKeys; |
| key -= HT_RSP_KEY_CR1; |
| } else { |
| set = keysState.navigationKeys; |
| group = BM_GRP_NavigationKeys; |
| |
| switch (key) { |
| #define KEY(ht,baum) case HT_RSP_KEY_##ht: key = BM_KEY_DISPLAY + baum; break |
| KEY(Up, 0); |
| KEY(B1, 1); |
| KEY(Dn, 2); |
| KEY(B2, 3); |
| KEY(B3, 4); |
| KEY(B4, 5); |
| #undef KEY |
| |
| default: |
| logUnexpectedPacket(packet.bytes, size); |
| continue; |
| } |
| } |
| |
| if (setGroupedKey(set, key, press)) enqueueKeyEvent(brl, group, key, press); |
| } |
| } |
| } |
| |
| static int |
| writeHandyTechCells (BrailleDisplay *brl) { |
| unsigned char packet[1 + ht->statusCount + ht->textCount]; |
| unsigned char *byte = packet; |
| |
| *byte++ = HT_REQ_WRITE; |
| |
| { |
| int count = ht->statusCount; |
| while (count-- > 0) *byte++ = 0; |
| } |
| |
| byte = mempcpy(byte, externalCells, ht->textCount); |
| |
| return writeHandyTechPacket(brl, packet, byte-packet); |
| } |
| |
| static int |
| writeHandyTechCellRange (BrailleDisplay *brl, unsigned int start, unsigned int count) { |
| return 1; |
| } |
| |
| static const ProtocolOperations handyTechOperations = { |
| .name = "HandyTech", |
| .dotsTable = &dotsTable_ISO11548_1, |
| |
| .serialBaud = 19200, |
| .serialParity = SERIAL_PARITY_ODD, |
| |
| .readPacket = readHandyTechPacket, |
| .writePacket = writeHandyTechPacket, |
| |
| .probeDevice = probeHandyTechDevice, |
| .processPackets = processHandyTechPackets, |
| |
| .writeCells = writeHandyTechCells, |
| .writeCellRange = writeHandyTechCellRange |
| }; |
| |
| /* PowerBraille Protocol */ |
| |
| #define PB_BUTTONS0_MARKER 0X60 |
| #define PB1_BUTTONS0_Display6 0X08 |
| #define PB1_BUTTONS0_Display5 0X04 |
| #define PB1_BUTTONS0_Display4 0X02 |
| #define PB1_BUTTONS0_Display2 0X01 |
| #define PB2_BUTTONS0_Display3 0X08 |
| #define PB2_BUTTONS0_Display5 0X04 |
| #define PB2_BUTTONS0_Display1 0X02 |
| #define PB2_BUTTONS0_Display2 0X01 |
| |
| #define PB_BUTTONS1_MARKER 0XE0 |
| #define PB1_BUTTONS1_Display3 0X08 |
| #define PB1_BUTTONS1_Display1 0X02 |
| #define PB2_BUTTONS1_Display6 0X08 |
| #define PB2_BUTTONS1_Display4 0X02 |
| |
| typedef enum { |
| PB_REQ_WRITE = 0X04, |
| PB_REQ_RESET = 0X0A |
| } PowerBrailleRequestCode; |
| |
| typedef enum { |
| PB_RSP_IDENTITY = 0X05, |
| PB_RSP_SENSORS = 0X08 |
| } PowerBrailleResponseCode; |
| |
| typedef union { |
| unsigned char bytes[11]; |
| |
| unsigned char buttons[2]; |
| |
| struct { |
| unsigned char zero; |
| unsigned char code; |
| |
| union { |
| struct { |
| unsigned char cells; |
| unsigned char dots; |
| unsigned char version[4]; |
| unsigned char checksum[4]; |
| } PACKED identity; |
| |
| struct { |
| unsigned char count; |
| unsigned char vertical[4]; |
| unsigned char horizontal[10]; |
| } PACKED sensors; |
| } PACKED values; |
| } PACKED data; |
| } PACKED PowerBrailleResponsePacket; |
| |
| static int |
| readPowerBraillePacket (BrailleDisplay *brl, unsigned char *packet, int size) { |
| int offset = 0; |
| int length = 0; |
| |
| while (1) { |
| unsigned char byte; |
| |
| if (!gioReadByte(brl->gioEndpoint, &byte, offset>0)) { |
| if (offset > 0) logPartialPacket(packet, offset); |
| return 0; |
| } |
| haveByte: |
| |
| if (offset == 0) { |
| if (!byte) { |
| length = 2; |
| } else if ((byte & PB_BUTTONS0_MARKER) == PB_BUTTONS0_MARKER) { |
| length = 2; |
| } else { |
| logIgnoredByte(byte); |
| continue; |
| } |
| } else if (packet[0]) { |
| if ((byte & PB_BUTTONS1_MARKER) != PB_BUTTONS1_MARKER) { |
| logShortPacket(packet, offset); |
| offset = 0; |
| length = 0; |
| goto haveByte; |
| } |
| } else { |
| if (offset == 1) { |
| switch (byte) { |
| case PB_RSP_IDENTITY: |
| length = 12; |
| break; |
| |
| case PB_RSP_SENSORS: |
| length = 3; |
| break; |
| |
| default: |
| logUnknownPacket(byte); |
| offset = 0; |
| length = 0; |
| continue; |
| } |
| } else if ((offset == 2) && (packet[1] == PB_RSP_SENSORS)) { |
| length += byte; |
| } |
| } |
| |
| if (offset < length) { |
| packet[offset] = byte; |
| } else { |
| if (offset == size) logTruncatedPacket(packet, offset); |
| logDiscardedByte(byte); |
| } |
| |
| if (++offset == length) { |
| if (offset > size) { |
| offset = 0; |
| length = 0; |
| continue; |
| } |
| |
| logInputPacket(packet, offset); |
| return length; |
| } |
| } |
| } |
| |
| static int |
| getPowerBraillePacket (BrailleDisplay *brl, PowerBrailleResponsePacket *packet) { |
| return readPowerBraillePacket(brl, packet->bytes, sizeof(*packet)); |
| } |
| |
| static int |
| writePowerBraillePacket (BrailleDisplay *brl, const unsigned char *packet, int length) { |
| unsigned char buffer[2 + length]; |
| unsigned char *byte = buffer; |
| |
| *byte++ = 0XFF; |
| *byte++ = 0XFF; |
| byte = mempcpy(byte, packet, length); |
| |
| return writeBraillePacket(brl, NULL, buffer, (byte - buffer)); |
| } |
| |
| static int |
| probePowerBrailleDevice (BrailleDisplay *brl) { |
| int probes = 0; |
| static const unsigned char request[] = {PB_REQ_RESET}; |
| while (writePowerBraillePacket(brl, request, sizeof(request))) { |
| while (awaitBrailleInput(brl, probeTimeout)) { |
| PowerBrailleResponsePacket response; |
| if (getPowerBraillePacket(brl, &response)) { |
| if (response.data.code == PB_RSP_IDENTITY) { |
| const unsigned char *version = response.data.values.identity.version; |
| logMessage(LOG_INFO, "Baum emulation: PowerBraille Version: %c%c%c%c", |
| version[0], version[1], version[2], version[3]); |
| cellCount = response.data.values.identity.cells; |
| return 1; |
| } |
| } |
| } |
| if (errno != EAGAIN) break; |
| if (++probes == probeLimit) break; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| processPowerBraillePackets (BrailleDisplay *brl) { |
| PowerBrailleResponsePacket packet; |
| int size; |
| |
| while ((size = getPowerBraillePacket(brl, &packet))) { |
| if (!packet.data.zero) { |
| switch (packet.data.code) { |
| case PB_RSP_IDENTITY: |
| if (!changeCellCount(brl, packet.data.values.identity.cells)) return; |
| continue; |
| |
| case PB_RSP_SENSORS: |
| updateKeyGroup(brl, keysState.routingKeys, packet.data.values.sensors.horizontal, |
| BM_GRP_RoutingKeys, 0, brl->textColumns, 0); |
| continue; |
| |
| default: |
| break; |
| } |
| } else { |
| unsigned char keys = 0; |
| |
| #define KEY(key,index) if (packet.buttons[index] & PB2_BUTTONS##index##_Display##key) keys |= 1 << (key - 1) |
| KEY(1, 0); |
| KEY(2, 0); |
| KEY(3, 0); |
| KEY(4, 1); |
| KEY(5, 0); |
| KEY(6, 1); |
| #undef KEY |
| |
| /* |
| * The PB emulation is deficient as the protocol doesn't report any |
| * key status when all keys are released. The ability to act on |
| * released keys as needed for multiple key combinations is, |
| * therefore, an unsolvable problem. The TSI driver works around |
| * this limitation by guessing the "key held" state based on the fact |
| * that native Navigator/PowerBraille displays send repeated key |
| * status for as long as there is at least one key pressed. Baum's PB |
| * emulation, however, doesn't do this. |
| * |
| * Let's treat each packet as a discrete set of press/release events. |
| * The limited set of single key bindings will work just fine. |
| * Multi-key combinations won't work very well at all, though, |
| * because it's unlikely that the user will be able to press and/or |
| * release all of the keys quickly enough, and because releasing one |
| * key before the others will generate press events for the rest. |
| * |
| * This is far from perfect, but that's the best we can do. The PB |
| * emulation modes (either PB1 or PB2) should simply be avoided |
| * whenever possible, and BAUM or HT should be used instead. |
| */ |
| |
| { |
| const KeyGroup group = BM_GRP_NavigationKeys; |
| KeyNumber pressedKeys[BM_KEYS_DISPLAY]; |
| unsigned char pressedCount = 0; |
| unsigned char offset; |
| |
| for (offset=0; offset<BM_KEYS_DISPLAY; offset+=1) { |
| if (keys & (1 << offset)) { |
| KeyNumber number = BM_KEY_DISPLAY + offset; |
| |
| enqueueKeyEvent(brl, group, number, 1); |
| pressedKeys[pressedCount++] = number; |
| } |
| } |
| |
| while (pressedCount) enqueueKeyEvent(brl, group, pressedKeys[--pressedCount], 0); |
| } |
| |
| continue; |
| } |
| |
| logUnexpectedPacket(packet.bytes, size); |
| } |
| } |
| |
| static int |
| writePowerBrailleCells (BrailleDisplay *brl) { |
| unsigned char packet[6 + (brl->textColumns * 2)]; |
| unsigned char *byte = packet; |
| |
| *byte++ = PB_REQ_WRITE; |
| *byte++ = 0; /* cursor mode: disabled */ |
| *byte++ = 0; /* cursor position: nowhere */ |
| *byte++ = 1; /* cursor type: command */ |
| *byte++ = brl->textColumns * 2; /* attribute-data pairs */ |
| *byte++ = 0; /* start */ |
| |
| { |
| int i; |
| for (i=0; i<brl->textColumns; ++i) { |
| *byte++ = 0; /* attributes */ |
| *byte++ = externalCells[i]; /* data */ |
| } |
| } |
| |
| return writePowerBraillePacket(brl, packet, byte-packet); |
| } |
| |
| static int |
| writePowerBrailleCellRange (BrailleDisplay *brl, unsigned int start, unsigned int count) { |
| return 1; |
| } |
| |
| static const ProtocolOperations powerBrailleOperations = { |
| .name = "PowerBraille", |
| .dotsTable = &dotsTable_ISO11548_1, |
| |
| .serialBaud = 9600, |
| .serialParity = SERIAL_PARITY_NONE, |
| |
| .readPacket = readPowerBraillePacket, |
| .writePacket = writePowerBraillePacket, |
| |
| .probeDevice = probePowerBrailleDevice, |
| .processPackets = processPowerBraillePackets, |
| |
| .writeCells = writePowerBrailleCells, |
| .writeCellRange = writePowerBrailleCellRange |
| }; |
| |
| /* Driver Handlers */ |
| |
| static int |
| connectResource (BrailleDisplay *brl, const char *identifier) { |
| static const SerialParameters serialParameters = { |
| SERIAL_DEFAULT_PARAMETERS |
| }; |
| |
| BEGIN_USB_CHANNEL_DEFINITIONS |
| { /* Vario 40 (40 cells) */ |
| .vendor=0X0403, .product=0XFE70, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* PocketVario (24 cells) */ |
| .vendor=0X0403, .product=0XFE71, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* SuperVario 40 (40 cells) */ |
| .vendor=0X0403, .product=0XFE72, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* SuperVario 32 (32 cells) */ |
| .vendor=0X0403, .product=0XFE73, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* SuperVario 64 (64 cells) */ |
| .vendor=0X0403, .product=0XFE74, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* SuperVario 80 (80 cells) */ |
| .vendor=0X0403, .product=0XFE75, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* VarioPro 80 (80 cells) */ |
| .vendor=0X0403, .product=0XFE76, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* VarioPro 64 (64 cells) */ |
| .vendor=0X0403, .product=0XFE77, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* Orbit Reader 20 (20 cells) */ |
| .vendor=0X0483, .product=0XA1D3, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&baumHid1Operations, |
| }, |
| |
| { /* Orbit Reader 40 (40 cells) */ |
| .vendor=0X0483, .product=0Xa366, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&baumHid1Operations, |
| }, |
| |
| { /* VarioPro 40 (40 cells) */ |
| .vendor=0X0904, .product=0X2000, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* EcoVario 24 (24 cells) */ |
| .vendor=0X0904, .product=0X2001, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* EcoVario 40 (40 cells) */ |
| .vendor=0X0904, .product=0X2002, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* VarioConnect 40 (40 cells) */ |
| .vendor=0X0904, .product=0X2007, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* VarioConnect 32 (32 cells) */ |
| .vendor=0X0904, .product=0X2008, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* VarioConnect 24 (24 cells) */ |
| .vendor=0X0904, .product=0X2009, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* VarioConnect 64 (64 cells) */ |
| .vendor=0X0904, .product=0X2010, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* VarioConnect 80 (80 cells) */ |
| .vendor=0X0904, .product=0X2011, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* EcoVario 32 (32 cells) */ |
| .vendor=0X0904, .product=0X2014, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* EcoVario 64 (64 cells) */ |
| .vendor=0X0904, .product=0X2015, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* EcoVario 80 (80 cells) */ |
| .vendor=0X0904, .product=0X2016, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* Refreshabraille 18 (18 cells) */ |
| .vendor=0X0904, .product=0X3000, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .disableAutosuspend=1, |
| .data=&baumEscapeOperations |
| }, |
| |
| { /* Orbit in Refreshabraille Emulation Mode (18 cells) */ |
| .vendor=0X0904, .product=0X3001, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .verifyInterface=1, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* Refreshabraille 18 (18 cells) */ |
| .vendor=0X0904, .product=0X3001, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .verifyInterface=1, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* Pronto! V3 18 (18 cells) */ |
| .vendor=0X0904, .product=0X4004, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* Pronto! V3 40 (40 cells) */ |
| .vendor=0X0904, .product=0X4005, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* Pronto! V4 18 (18 cells) */ |
| .vendor=0X0904, .product=0X4007, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid2Operations |
| }, |
| |
| { /* Pronto! V4 40 (40 cells) */ |
| .vendor=0X0904, .product=0X4008, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid2Operations |
| }, |
| |
| { /* SuperVario2 40 (40 cells) */ |
| .vendor=0X0904, .product=0X6001, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* PocketVario2 (24 cells) */ |
| .vendor=0X0904, .product=0X6002, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* SuperVario2 32 (32 cells) */ |
| .vendor=0X0904, .product=0X6003, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* SuperVario2 64 (64 cells) */ |
| .vendor=0X0904, .product=0X6004, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* SuperVario2 80 (80 cells) */ |
| .vendor=0X0904, .product=0X6005, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* Brailliant2 40 (40 cells) */ |
| .vendor=0X0904, .product=0X6006, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* Brailliant2 24 (24 cells) */ |
| .vendor=0X0904, .product=0X6007, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* Brailliant2 32 (32 cells) */ |
| .vendor=0X0904, .product=0X6008, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* Brailliant2 64 (64 cells) */ |
| .vendor=0X0904, .product=0X6009, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* Brailliant2 80 (80 cells) */ |
| .vendor=0X0904, .product=0X600A, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* VarioConnect 24 (24 cells) */ |
| .vendor=0X0904, .product=0X6011, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* VarioConnect 32 (32 cells) */ |
| .vendor=0X0904, .product=0X6012, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* VarioConnect 40 (40 cells) */ |
| .vendor=0X0904, .product=0X6013, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid1Operations |
| }, |
| |
| { /* VarioUltra 20 (20 cells) */ |
| .vendor=0X0904, .product=0X6101, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid2Operations |
| }, |
| |
| { /* VarioUltra 40 (40 cells) */ |
| .vendor=0X0904, .product=0X6102, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid2Operations |
| }, |
| |
| { /* VarioUltra 32 (32 cells) */ |
| .vendor=0X0904, .product=0X6103, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .data=&baumHid2Operations |
| }, |
| |
| { /* NLS eReader Zoomax (20 cells) */ |
| .vendor=0X1A86, .product=0X7523, |
| .parentVendor=0X1A40, .parentProduct=0X0101, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=2, .outputEndpoint=2, |
| .data=&baumEscapeOperations |
| }, |
| END_USB_CHANNEL_DEFINITIONS |
| |
| GioDescriptor descriptor; |
| gioInitializeDescriptor(&descriptor); |
| |
| descriptor.serial.parameters = &serialParameters; |
| descriptor.serial.options.applicationData = &baumEscapeOperations; |
| |
| descriptor.usb.channelDefinitions = usbChannelDefinitions; |
| descriptor.usb.options.ignoreWriteTimeouts = 1; |
| |
| descriptor.bluetooth.channelNumber = 1; |
| descriptor.bluetooth.discoverChannel = 1; |
| descriptor.bluetooth.options.applicationData = &baumEscapeOperations; |
| |
| if (connectBrailleResource(brl, identifier, &descriptor, NULL)) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| brl_construct (BrailleDisplay *brl, char **parameters, const char *device) { |
| const ProtocolOperations *requestedProtocol = NULL; |
| unsigned int useVarioKeys = 0; |
| |
| { |
| static const ProtocolOperations *const values[] = { |
| NULL, |
| &baumEscapeOperations, |
| &baumHid1Operations, |
| &baumHid2Operations, |
| &handyTechOperations, |
| &powerBrailleOperations |
| }; |
| |
| static const char *choices[] = {"default", "escape", "hid1", "hid2", "ht","pb", NULL}; |
| unsigned int index = 0; |
| |
| if (validateChoice(&index, parameters[PARM_PROTOCOL], choices)) { |
| requestedProtocol = values[index]; |
| } else { |
| logMessage(LOG_WARNING, "%s: %s", "invalid protocol setting", parameters[PARM_PROTOCOL]); |
| } |
| } |
| |
| if (!validateYesNo(&useVarioKeys, parameters[PARM_VARIOKEYS])) { |
| logMessage(LOG_WARNING, "%s: %s", "invalid vario keys setting", parameters[PARM_VARIOKEYS]); |
| } |
| |
| if ((brl->data = malloc(sizeof(*brl->data)))) { |
| memset(brl->data, 0, sizeof(*brl->data)); |
| |
| if (connectResource(brl, device)) { |
| unsigned int attempts = 0; |
| |
| while (1) { |
| brl->data->protocol = requestedProtocol; |
| if (!brl->data->protocol) brl->data->protocol = gioGetApplicationData(brl->gioEndpoint); |
| logMessage(LOG_DEBUG, "probing with %s protocol", brl->data->protocol->name); |
| |
| if (brl->data->protocol->serialBaud) { |
| const SerialParameters parameters = { |
| SERIAL_DEFAULT_PARAMETERS, |
| .baud = brl->data->protocol->serialBaud, |
| .parity = brl->data->protocol->serialParity |
| }; |
| |
| if (!gioReconfigureResource(brl->gioEndpoint, ¶meters)) goto failed; |
| } |
| |
| if (!gioDiscardInput(brl->gioEndpoint)) goto failed; |
| |
| memset(&keysState, 0, sizeof(keysState)); |
| switchSettings = 0; |
| |
| if (brl->data->protocol->probeDevice(brl)) { |
| logCellCount(brl); |
| |
| { |
| unsigned char *size = &brl->data->packetSize.routingKeys; |
| |
| *size = KEY_GROUP_SIZE(cellCount); |
| if ((*size > 2) && (*size < 5)) *size = 5; |
| } |
| |
| if ((baumDeviceType == BAUM_DEVICE_VarioConnect) && (cellCount == 12)) { |
| baumDeviceType = BAUM_DEVICE_Conny; |
| } |
| |
| makeOutputTable(brl->data->protocol->dotsTable[0]); |
| if (!clearCellRange(brl, 0, cellCount)) goto failed; |
| if (!updateCells(brl)) goto failed; |
| |
| { |
| const KeyTableDefinition *ktd; |
| |
| if (useVarioKeys) { |
| ktd = &KEY_TABLE_DEFINITION(vk); |
| } else { |
| ktd = baumDeviceOperations[baumDeviceType].keyTableDefinition; |
| } |
| |
| setBrailleKeyTable(brl, ktd); |
| } |
| |
| return 1; |
| } |
| |
| if (++attempts == 2) break; |
| asyncWait(700); |
| } |
| |
| failed: |
| disconnectBrailleResource(brl, NULL); |
| } |
| |
| free(brl->data); |
| } else { |
| logMallocError(); |
| } |
| |
| return 0; |
| } |
| |
| static void |
| brl_destruct (BrailleDisplay *brl) { |
| disconnectBrailleResource(brl, NULL); |
| free(brl->data); |
| } |
| |
| static ssize_t |
| brl_readPacket (BrailleDisplay *brl, void *buffer, size_t size) { |
| int count = brl->data->protocol->readPacket(brl, buffer, size); |
| if (!count) count = -1; |
| return count; |
| } |
| |
| static ssize_t |
| brl_writePacket (BrailleDisplay *brl, const void *packet, size_t length) { |
| return brl->data->protocol->writePacket(brl, packet, length)? length: -1; |
| } |
| |
| static int |
| brl_reset (BrailleDisplay *brl) { |
| return 0; |
| } |
| |
| static int |
| brl_writeWindow (BrailleDisplay *brl, const wchar_t *text) { |
| if (!putCells(brl, brl->buffer, 0, brl->textColumns)) return 0; |
| return updateCells(brl); |
| } |
| |
| static int |
| brl_writeStatus (BrailleDisplay *brl, const unsigned char *status) { |
| return putCells(brl, status, brl->textColumns, brl->statusColumns); |
| } |
| |
| static int |
| brl_readCommand (BrailleDisplay *brl, KeyTableCommandContext context) { |
| brl->data->protocol->processPackets(brl); |
| return (errno == EAGAIN)? EOF: BRL_CMD_RESTARTBRL; |
| } |