| /* |
| * 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 <time.h> |
| #include <errno.h> |
| |
| #include "log.h" |
| #include "parameters.h" |
| #include "bitfield.h" |
| #include "parse.h" |
| #include "timing.h" |
| #include "async_wait.h" |
| #include "ascii.h" |
| |
| typedef enum { |
| PARM_SETTIME |
| } DriverParameter; |
| #define BRLPARMS "settime" |
| |
| #define BRLSTAT ST_AlvaStyle |
| #define BRL_HAVE_STATUS_CELLS |
| #define BRL_HAVE_PACKET_IO |
| #include "brl_driver.h" |
| #include "brldefs-ht.h" |
| |
| BEGIN_KEY_NAME_TABLE(routing) |
| KEY_GROUP_ENTRY(HT_GRP_RoutingKeys, "RoutingKey"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(dots) |
| KEY_NAME_ENTRY(HT_KEY_B1, "B1"), |
| KEY_NAME_ENTRY(HT_KEY_B2, "B2"), |
| KEY_NAME_ENTRY(HT_KEY_B3, "B3"), |
| KEY_NAME_ENTRY(HT_KEY_B4, "B4"), |
| |
| KEY_NAME_ENTRY(HT_KEY_B5, "B5"), |
| KEY_NAME_ENTRY(HT_KEY_B6, "B6"), |
| KEY_NAME_ENTRY(HT_KEY_B7, "B7"), |
| KEY_NAME_ENTRY(HT_KEY_B8, "B8"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(keypad) |
| KEY_NAME_ENTRY(HT_KEY_B12, "B12"), |
| KEY_NAME_ENTRY(HT_KEY_Zero, "Zero"), |
| KEY_NAME_ENTRY(HT_KEY_B13, "B13"), |
| KEY_NAME_ENTRY(HT_KEY_B14, "B14"), |
| |
| KEY_NAME_ENTRY(HT_KEY_B11, "B11"), |
| KEY_NAME_ENTRY(HT_KEY_One, "One"), |
| KEY_NAME_ENTRY(HT_KEY_Two, "Two"), |
| KEY_NAME_ENTRY(HT_KEY_Three, "Three"), |
| |
| KEY_NAME_ENTRY(HT_KEY_B10, "B10"), |
| KEY_NAME_ENTRY(HT_KEY_Four, "Four"), |
| KEY_NAME_ENTRY(HT_KEY_Five, "Five"), |
| KEY_NAME_ENTRY(HT_KEY_Six, "Six"), |
| |
| KEY_NAME_ENTRY(HT_KEY_B9, "B9"), |
| KEY_NAME_ENTRY(HT_KEY_Seven, "Seven"), |
| KEY_NAME_ENTRY(HT_KEY_Eight, "Eight"), |
| KEY_NAME_ENTRY(HT_KEY_Nine, "Nine"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(rockers) |
| KEY_NAME_ENTRY(HT_KEY_Escape, "LeftRockerTop"), |
| KEY_NAME_ENTRY(HT_KEY_Return, "LeftRockerBottom"), |
| |
| KEY_NAME_ENTRY(HT_KEY_Up, "RightRockerTop"), |
| KEY_NAME_ENTRY(HT_KEY_Down, "RightRockerBottom"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(navigation) |
| KEY_NAME_ENTRY(HT_KEY_Escape, "Display1"), |
| KEY_NAME_ENTRY(HT_KEY_LeftCenter, "Display2"), |
| KEY_NAME_ENTRY(HT_KEY_Return, "Display3"), |
| |
| KEY_NAME_ENTRY(HT_KEY_Up, "Display4"), |
| KEY_NAME_ENTRY(HT_KEY_RightCenter, "Display5"), |
| KEY_NAME_ENTRY(HT_KEY_Down, "Display6"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(joystick) |
| KEY_NAME_ENTRY(HT_KEY_JoystickLeft, "Left"), |
| KEY_NAME_ENTRY(HT_KEY_JoystickRight, "Right"), |
| KEY_NAME_ENTRY(HT_KEY_JoystickUp, "Up"), |
| KEY_NAME_ENTRY(HT_KEY_JoystickDown, "Down"), |
| KEY_NAME_ENTRY(HT_KEY_JoystickAction, "Action"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(modular) |
| KEY_NAME_ENTRY(HT_KEY_Up, "Left"), |
| KEY_NAME_ENTRY(HT_KEY_Down, "Right"), |
| |
| KEY_NAME_ENTRY(HT_KEY_STATUS+0, "Status1"), |
| KEY_NAME_ENTRY(HT_KEY_STATUS+1, "Status2"), |
| KEY_NAME_ENTRY(HT_KEY_STATUS+2, "Status3"), |
| KEY_NAME_ENTRY(HT_KEY_STATUS+3, "Status4"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLES(mdlr) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(keypad), |
| KEY_NAME_TABLE(modular), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLE(modularEvolution) |
| KEY_NAME_ENTRY(HT_KEY_Space, "Left"), |
| KEY_NAME_ENTRY(HT_KEY_SpaceRight, "Right"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLES(me64) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(rockers), |
| KEY_NAME_TABLE(modularEvolution), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(me88) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(rockers), |
| KEY_NAME_TABLE(keypad), |
| KEY_NAME_TABLE(modularEvolution), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(mc88) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(rockers), |
| KEY_NAME_TABLE(keypad), |
| KEY_NAME_TABLE(modularEvolution), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLE(brailleStar) |
| KEY_NAME_ENTRY(HT_KEY_Space, "SpaceLeft"), |
| KEY_NAME_ENTRY(HT_KEY_SpaceRight, "SpaceRight"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLES(bs40) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(rockers), |
| KEY_NAME_TABLE(brailleStar), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(bs80) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(rockers), |
| KEY_NAME_TABLE(keypad), |
| KEY_NAME_TABLE(brailleStar), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(brln) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(rockers), |
| KEY_NAME_TABLE(brailleStar), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(as40) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(rockers), |
| KEY_NAME_TABLE(brailleStar), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(ab) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(rockers), |
| KEY_NAME_TABLE(brailleStar), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(ab_s) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(rockers), |
| KEY_NAME_TABLE(brailleStar), |
| KEY_NAME_TABLE(joystick), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(cb40) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(rockers), |
| KEY_NAME_TABLE(brailleStar), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLE(brailleWave) |
| KEY_NAME_ENTRY(HT_KEY_Up, "Left"), |
| KEY_NAME_ENTRY(HT_KEY_Down, "Right"), |
| |
| KEY_NAME_ENTRY(HT_KEY_Escape, "Escape"), |
| KEY_NAME_ENTRY(HT_KEY_Space, "Space"), |
| KEY_NAME_ENTRY(HT_KEY_Return, "Return"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLES(wave) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(brailleWave), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLE(easyBraille) |
| KEY_NAME_ENTRY(HT_KEY_Up, "Left"), |
| KEY_NAME_ENTRY(HT_KEY_Down, "Right"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLES(easy) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(easyBraille), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLE(basicBraille) |
| KEY_NAME_ENTRY(HT_KEY_B2, "Display3"), |
| KEY_NAME_ENTRY(HT_KEY_B3, "Display2"), |
| KEY_NAME_ENTRY(HT_KEY_B4, "Display1"), |
| KEY_NAME_ENTRY(HT_KEY_B5, "Display4"), |
| KEY_NAME_ENTRY(HT_KEY_B6, "Display5"), |
| KEY_NAME_ENTRY(HT_KEY_B7, "Display6"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLES(bb) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(basicBraille), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(bbp) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(rockers), |
| KEY_NAME_TABLE(brailleStar), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(alo) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(rockers), |
| KEY_NAME_TABLE(brailleStar), |
| KEY_NAME_TABLE(joystick), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(ac4) |
| KEY_NAME_TABLE(routing), |
| KEY_NAME_TABLE(dots), |
| KEY_NAME_TABLE(navigation), |
| KEY_NAME_TABLE(brailleStar), |
| KEY_NAME_TABLE(joystick), |
| END_KEY_NAME_TABLES |
| |
| typedef enum { |
| HT_BWK_Backward = 0X01, |
| HT_BWK_Forward = 0X08, |
| |
| HT_BWK_Escape = 0X02, |
| HT_BWK_Enter = 0X04 |
| } HT_BookwormKey; |
| |
| BEGIN_KEY_NAME_TABLE(bookworm) |
| KEY_NAME_ENTRY(HT_BWK_Backward, "Backward"), |
| KEY_NAME_ENTRY(HT_BWK_Forward, "Forward"), |
| |
| KEY_NAME_ENTRY(HT_BWK_Escape, "Escape"), |
| KEY_NAME_ENTRY(HT_BWK_Enter, "Enter"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLES(bkwm) |
| KEY_NAME_TABLE(bookworm), |
| END_KEY_NAME_TABLES |
| |
| DEFINE_KEY_TABLE(mdlr) |
| DEFINE_KEY_TABLE(me64) |
| DEFINE_KEY_TABLE(me88) |
| DEFINE_KEY_TABLE(mc88) |
| DEFINE_KEY_TABLE(bs40) |
| DEFINE_KEY_TABLE(bs80) |
| DEFINE_KEY_TABLE(brln) |
| DEFINE_KEY_TABLE(as40) |
| DEFINE_KEY_TABLE(ab) |
| DEFINE_KEY_TABLE(ab_s) |
| DEFINE_KEY_TABLE(cb40) |
| DEFINE_KEY_TABLE(wave) |
| DEFINE_KEY_TABLE(easy) |
| DEFINE_KEY_TABLE(bb) |
| DEFINE_KEY_TABLE(bbp) |
| DEFINE_KEY_TABLE(alo) |
| DEFINE_KEY_TABLE(ac4) |
| DEFINE_KEY_TABLE(bkwm) |
| |
| BEGIN_KEY_TABLE_LIST |
| &KEY_TABLE_DEFINITION(mdlr), |
| &KEY_TABLE_DEFINITION(me64), |
| &KEY_TABLE_DEFINITION(me88), |
| &KEY_TABLE_DEFINITION(mc88), |
| &KEY_TABLE_DEFINITION(bs40), |
| &KEY_TABLE_DEFINITION(bs80), |
| &KEY_TABLE_DEFINITION(brln), |
| &KEY_TABLE_DEFINITION(as40), |
| &KEY_TABLE_DEFINITION(ab), |
| &KEY_TABLE_DEFINITION(ab_s), |
| &KEY_TABLE_DEFINITION(cb40), |
| &KEY_TABLE_DEFINITION(wave), |
| &KEY_TABLE_DEFINITION(easy), |
| &KEY_TABLE_DEFINITION(bb), |
| &KEY_TABLE_DEFINITION(bbp), |
| &KEY_TABLE_DEFINITION(alo), |
| &KEY_TABLE_DEFINITION(ac4), |
| &KEY_TABLE_DEFINITION(bkwm), |
| END_KEY_TABLE_LIST |
| |
| static int |
| endSession_Bookworm (BrailleDisplay *brl) { |
| static const unsigned char sessionEnd[] = {0X05, 0X07}; |
| return writeBrailleMessage(brl, NULL, 0, sessionEnd, sizeof(sessionEnd)); |
| } |
| |
| typedef int ByteInterpreter (BrailleDisplay *brl, unsigned char byte); |
| static ByteInterpreter interpretByte_key; |
| static ByteInterpreter interpretByte_Bookworm; |
| |
| typedef int (CellWriter) (BrailleDisplay *brl); |
| static CellWriter writeCells_statusAndText; |
| static CellWriter writeCells_Bookworm; |
| static CellWriter writeCells_Evolution; |
| |
| static SetBrailleFirmnessMethod setBrailleFirmness; |
| |
| static SetTouchSensitivityMethod setTouchSensitivity_Evolution; |
| static SetTouchSensitivityMethod setTouchSensitivity_ActiveBraille; |
| |
| typedef struct { |
| const char *name; |
| const KeyTableDefinition *keyTableDefinition; |
| |
| ByteInterpreter *interpretByte; |
| CellWriter *writeCells; |
| SetBrailleFirmnessMethod *setBrailleFirmness; |
| SetTouchSensitivityMethod *setTouchSensitivity; |
| |
| BrailleSessionEnder *sessionEnder; |
| |
| HT_ModelIdentifier identifier:8; |
| unsigned char textCells; |
| unsigned char statusCells; |
| |
| unsigned hasATC:1; /* Active Tactile Control */ |
| unsigned hasTime:1; |
| } ModelEntry; |
| |
| static const ModelEntry modelTable[] = { |
| { .identifier = HT_MODEL_Modular20, |
| .name = "Modular 20+4", |
| .textCells = 20, |
| .statusCells = 4, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(mdlr), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_statusAndText |
| }, |
| |
| { .identifier = HT_MODEL_Modular40, |
| .name = "Modular 40+4", |
| .textCells = 40, |
| .statusCells = 4, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(mdlr), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_statusAndText |
| }, |
| |
| { .identifier = HT_MODEL_Modular80, |
| .name = "Modular 80+4", |
| .textCells = 80, |
| .statusCells = 4, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(mdlr), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_statusAndText |
| }, |
| |
| { .identifier = HT_MODEL_ModularEvolution64, |
| .name = "Modular Evolution 64", |
| .textCells = 64, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(me64), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_Evolution, |
| .setTouchSensitivity = setTouchSensitivity_Evolution, |
| .hasATC = 1 |
| }, |
| |
| { .identifier = HT_MODEL_ModularEvolution88, |
| .name = "Modular Evolution 88", |
| .textCells = 88, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(me88), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_Evolution, |
| .setTouchSensitivity = setTouchSensitivity_Evolution, |
| .hasATC = 1 |
| }, |
| |
| { .identifier = HT_MODEL_BrailleWave, |
| .name = "Braille Wave", |
| .textCells = 40, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(wave), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_statusAndText |
| }, |
| |
| { .identifier = HT_MODEL_Bookworm, |
| .name = "Bookworm", |
| .textCells = 8, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(bkwm), |
| .interpretByte = interpretByte_Bookworm, |
| .writeCells = writeCells_Bookworm, |
| .sessionEnder = endSession_Bookworm |
| }, |
| |
| { .identifier = HT_MODEL_Braillino, |
| .name = "Braillino", |
| .textCells = 20, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(brln), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_statusAndText |
| }, |
| |
| { .identifier = HT_MODEL_BrailleStar40, |
| .name = "Braille Star 40", |
| .textCells = 40, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(bs40), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_statusAndText |
| }, |
| |
| { .identifier = HT_MODEL_BrailleStar80, |
| .name = "Braille Star 80", |
| .textCells = 80, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(bs80), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_statusAndText |
| }, |
| |
| { .identifier = HT_MODEL_EasyBraille, |
| .name = "Easy Braille", |
| .textCells = 40, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(easy), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_Evolution |
| }, |
| |
| { .identifier = HT_MODEL_ActiveBraille, |
| .name = "Active Braille", |
| .textCells = 40, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(ab), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_Evolution, |
| .setBrailleFirmness = setBrailleFirmness, |
| .setTouchSensitivity = setTouchSensitivity_ActiveBraille, |
| .hasATC = 1, |
| .hasTime = 1 |
| }, |
| |
| #define HT_BASIC_BRAILLE(cells) \ |
| { .identifier = HT_MODEL_BasicBraille##cells, \ |
| .name = "Basic Braille " STRINGIFY(cells), \ |
| .textCells = cells, \ |
| .statusCells = 0, \ |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(bb),\ |
| .interpretByte = interpretByte_key, \ |
| .writeCells = writeCells_Evolution \ |
| } |
| HT_BASIC_BRAILLE(16), |
| HT_BASIC_BRAILLE(20), |
| HT_BASIC_BRAILLE(32), |
| HT_BASIC_BRAILLE(40), |
| HT_BASIC_BRAILLE(48), |
| HT_BASIC_BRAILLE(64), |
| HT_BASIC_BRAILLE(80), |
| HT_BASIC_BRAILLE(160), |
| #undef HT_BASIC_BRAILLE |
| |
| #define HT_BASIC_BRAILLE_PLUS(cells) \ |
| { .identifier = HT_MODEL_BasicBraillePlus##cells, \ |
| .name = "Basic Braille Plus " STRINGIFY(cells), \ |
| .textCells = cells, \ |
| .statusCells = 0, \ |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(bbp),\ |
| .interpretByte = interpretByte_key, \ |
| .writeCells = writeCells_Evolution \ |
| } |
| HT_BASIC_BRAILLE_PLUS(20), |
| HT_BASIC_BRAILLE_PLUS(32), |
| HT_BASIC_BRAILLE_PLUS(40), |
| HT_BASIC_BRAILLE_PLUS(48), |
| HT_BASIC_BRAILLE_PLUS(64), |
| HT_BASIC_BRAILLE_PLUS(80), |
| HT_BASIC_BRAILLE_PLUS(84), |
| #undef HT_BASIC_BRAILLE_PLUS |
| |
| { .identifier = HT_MODEL_Actilino, |
| .name = "Actilino", |
| .textCells = 16, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(alo), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_Evolution, |
| .setBrailleFirmness = setBrailleFirmness, |
| .setTouchSensitivity = setTouchSensitivity_ActiveBraille, |
| .hasATC = 1, |
| .hasTime = 1 |
| }, |
| |
| { .identifier = HT_MODEL_Activator, |
| .name = "Activator", |
| .textCells = 40, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(ac4), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_Evolution, |
| .setBrailleFirmness = setBrailleFirmness, |
| .setTouchSensitivity = setTouchSensitivity_ActiveBraille, |
| .hasATC = 1, |
| .hasTime = 1 |
| }, |
| |
| { .identifier = HT_MODEL_ActiveStar40, |
| .name = "Active Star 40", |
| .textCells = 40, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(as40), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_Evolution, |
| .setBrailleFirmness = setBrailleFirmness, |
| .setTouchSensitivity = setTouchSensitivity_ActiveBraille, |
| .hasATC = 1, |
| .hasTime = 1 |
| }, |
| |
| { .identifier = HT_MODEL_ModularConnect88, |
| .name = "Modular Connect 88", |
| .textCells = 88, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(mc88), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_Evolution, |
| }, |
| |
| { .identifier = HT_MODEL_ConnectBraille40, |
| .name = "Connect Braille 40", |
| .textCells = 40, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(cb40), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_Evolution, |
| .setBrailleFirmness = setBrailleFirmness, |
| .hasTime = 1 |
| }, |
| |
| { /* end of table */ |
| .name = NULL |
| } |
| }; |
| |
| static const ModelEntry modelEntry_ab_s = { |
| .identifier = HT_MODEL_ActiveBraille, |
| .name = "Active Braille S", |
| .textCells = 40, |
| .statusCells = 0, |
| .keyTableDefinition = &KEY_TABLE_DEFINITION(ab_s), |
| .interpretByte = interpretByte_key, |
| .writeCells = writeCells_Evolution, |
| .setBrailleFirmness = setBrailleFirmness, |
| .setTouchSensitivity = setTouchSensitivity_ActiveBraille, |
| .hasATC = 1, |
| .hasTime = 1 |
| }; |
| |
| #define MAXIMUM_TEXT_CELLS 160 |
| #define MAXIMUM_STATUS_CELLS 4 |
| |
| typedef enum { |
| BDS_OFF, |
| BDS_READY |
| } BrailleDisplayState; |
| |
| struct BrailleDataStruct { |
| const ModelEntry *model; /* points to terminal model config struct */ |
| |
| unsigned char rawData[MAXIMUM_TEXT_CELLS]; /* translated data to send to Braille */ |
| unsigned char prevData[MAXIMUM_TEXT_CELLS]; /* previously sent raw data */ |
| |
| unsigned char rawStatus[MAXIMUM_STATUS_CELLS]; /* to hold status info */ |
| unsigned char prevStatus[MAXIMUM_STATUS_CELLS]; /* to hold previous status */ |
| |
| BrailleDisplayState currentState; |
| TimePeriod statePeriod; |
| |
| unsigned int retryCount; |
| unsigned char updateRequired; |
| }; |
| |
| /* USB IO */ |
| #include "io_usb.h" |
| #include "usb_hid.h" |
| |
| #define HT_HID_REPORT_TIMEOUT 100 |
| |
| typedef enum { |
| HT_HID_RPT_OutData = 0X01, /* receive data from device */ |
| HT_HID_RPT_InData = 0X02, /* send data to device */ |
| HT_HID_RPT_InCommand = 0XFB, /* run USB-HID firmware command */ |
| HT_HID_RPT_OutVersion = 0XFC, /* get version of USB-HID firmware */ |
| HT_HID_RPT_OutBaud = 0XFD, /* get baud rate of serial connection */ |
| HT_HID_RPT_InBaud = 0XFE, /* set baud rate of serial connection */ |
| } HT_HidReportNumber; |
| |
| typedef enum { |
| HT_HID_CMD_FlushBuffers = 0X01, /* flush input and output buffers */ |
| } HtHidCommand; |
| |
| static size_t hidOutDataSize = 0; |
| static size_t hidInDataSize = 0; |
| static size_t hidInCommandSize = 0; |
| static size_t hidOutVersionSize = 0; |
| static size_t hidOutBaudSize = 0; |
| static size_t hidInBaudSize = 0; |
| |
| static uint16_t hidFirmwareVersion; |
| static unsigned char *hidInputReport = NULL; |
| #define hidInputLength (hidInputReport[1]) |
| #define hidInputBuffer (&hidInputReport[2]) |
| static unsigned char hidInputOffset; |
| |
| static ssize_t |
| getHidReport ( |
| UsbDevice *device, const UsbChannelDefinition *definition, |
| unsigned char number, unsigned char *buffer, uint16_t size |
| ) { |
| ssize_t result = usbHidGetReport(device, definition->interface, |
| number, buffer, size, HT_HID_REPORT_TIMEOUT); |
| if (result > 0 && buffer[0] != number) { |
| logMessage(LOG_WARNING, "unexpected HID report number: expected %02X, received %02X", |
| number, buffer[0]); |
| errno = EIO; |
| result = -1; |
| } |
| |
| return result; |
| } |
| |
| static int |
| allocateHidInputBuffer (void) { |
| if (hidOutDataSize) { |
| if ((hidInputReport = malloc(hidOutDataSize))) { |
| hidInputLength = 0; |
| hidInputOffset = 0; |
| return 1; |
| } else { |
| logMallocError(); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void |
| deallocateHidInputBuffer (void) { |
| if (hidInputReport) { |
| free(hidInputReport); |
| hidInputReport = NULL; |
| } |
| } |
| |
| static int |
| getHidFirmwareVersion (BrailleDisplay *brl) { |
| hidFirmwareVersion = 0; |
| |
| if (hidOutVersionSize) { |
| unsigned char report[hidOutVersionSize]; |
| ssize_t result = gioGetHidReport(brl->gioEndpoint, |
| HT_HID_RPT_OutVersion, report, sizeof(report)); |
| |
| if (result > 0) { |
| hidFirmwareVersion = (report[1] << 8) | report[2]; |
| logMessage(LOG_INFO, "USB-HID Firmware Version: %u.%u", report[1], report[2]); |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int |
| executeHidFirmwareCommand (BrailleDisplay *brl, HtHidCommand command) { |
| if (hidInCommandSize) { |
| unsigned char report[hidInCommandSize]; |
| |
| report[0] = HT_HID_RPT_InCommand; |
| report[1] = command; |
| |
| if (gioWriteHidReport(brl->gioEndpoint, report, sizeof(report)) != -1) { |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| typedef struct { |
| int (*initializeSession) (BrailleDisplay *brl); |
| } GeneralOperations; |
| |
| typedef struct { |
| const GeneralOperations *general; |
| GioUsbAwaitInputMethod *awaitInput; |
| GioUsbReadDataMethod *readData; |
| GioUsbWriteDataMethod *writeData; |
| UsbInputFilter *inputFilter; |
| } UsbOperations; |
| |
| static int |
| initializeUsbSession2 (BrailleDisplay *brl) { |
| static const BrailleReportSizeEntry reportTable[] = { |
| {.identifier=HT_HID_RPT_OutData, .input=&hidOutDataSize}, |
| {.identifier=HT_HID_RPT_InData, .output=&hidInDataSize}, |
| {.identifier=HT_HID_RPT_InCommand, .output=&hidInCommandSize}, |
| {.identifier=HT_HID_RPT_OutVersion, .input=&hidOutVersionSize}, |
| {.identifier=HT_HID_RPT_OutBaud, .input=&hidOutBaudSize}, |
| {.identifier=HT_HID_RPT_InBaud, .output=&hidInBaudSize}, |
| {.identifier=0} |
| }; |
| |
| if (getBrailleReportSizes(brl, reportTable)) { |
| if (allocateHidInputBuffer()) { |
| if (getHidFirmwareVersion(brl)) { |
| if (executeHidFirmwareCommand(brl, HT_HID_CMD_FlushBuffers)) { |
| return 1; |
| } |
| } |
| |
| deallocateHidInputBuffer(); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int |
| awaitUsbInput2 ( |
| UsbDevice *device, const UsbChannelDefinition *definition, int milliseconds |
| ) { |
| if (hidOutDataSize) { |
| if (hidInputOffset < hidInputLength) return 1; |
| |
| TimePeriod period; |
| startTimePeriod(&period, milliseconds); |
| |
| while (1) { |
| ssize_t result = getHidReport(device, definition, HT_HID_RPT_OutData, |
| hidInputReport, hidOutDataSize); |
| |
| if (result == -1) return 0; |
| hidInputOffset = 0; |
| if (hidInputLength > 0) return 1; |
| |
| if (afterTimePeriod(&period, NULL)) break; |
| asyncWait(BRAILLE_DRIVER_INPUT_POLL_INTERVAL); |
| } |
| } |
| |
| errno = EAGAIN; |
| return 0; |
| } |
| |
| static ssize_t |
| readUsbData2 ( |
| UsbDevice *device, const UsbChannelDefinition *definition, |
| void *data, size_t size, |
| int initialTimeout, int subsequentTimeout |
| ) { |
| unsigned char *buffer = data; |
| int count = 0; |
| |
| while (count < size) { |
| if (!awaitUsbInput2(device, definition, |
| count? subsequentTimeout: initialTimeout)) { |
| if (errno != EAGAIN) count = -1; |
| break; |
| } |
| |
| { |
| size_t amount = MIN(size-count, hidInputLength-hidInputOffset); |
| |
| memcpy(&buffer[count], &hidInputBuffer[hidInputOffset], amount); |
| hidInputOffset += amount; |
| count += amount; |
| } |
| } |
| |
| return count; |
| } |
| |
| static ssize_t |
| writeUsbData2 ( |
| UsbDevice *device, const UsbChannelDefinition *definition, |
| const void *data, size_t size, int timeout |
| ) { |
| const unsigned char *buffer = data; |
| int index = 0; |
| |
| if (hidInDataSize) { |
| while (size) { |
| unsigned char report[hidInDataSize]; |
| unsigned char count = MIN(size, (sizeof(report) - 2)); |
| int result; |
| |
| report[0] = HT_HID_RPT_InData; |
| report[1] = count; |
| memcpy(report+2, &buffer[index], count); |
| memset(&report[count+2], 0, sizeof(report)-count-2); |
| |
| result = usbHidSetReport(device, definition->interface, |
| report[0], report, sizeof(report), |
| HT_HID_REPORT_TIMEOUT); |
| if (result == -1) return -1; |
| |
| index += count; |
| size -= count; |
| } |
| } |
| |
| return index; |
| } |
| |
| static const GeneralOperations generalOperations2 = { |
| .initializeSession = initializeUsbSession2 |
| }; |
| |
| static const UsbOperations usbOperations2 = { |
| .general = &generalOperations2, |
| .awaitInput = awaitUsbInput2, |
| .readData = readUsbData2, |
| .writeData = writeUsbData2 |
| }; |
| |
| static int |
| initializeUsbSession3 (BrailleDisplay *brl) { |
| static const BrailleReportSizeEntry reportTable[] = { |
| {.identifier=HT_HID_RPT_OutData, .input=&hidOutDataSize}, |
| {.identifier=HT_HID_RPT_InData, .output=&hidInDataSize}, |
| {.identifier=0} |
| }; |
| |
| return getBrailleReportSizes(brl, reportTable); |
| } |
| |
| static ssize_t |
| writeUsbData3 ( |
| UsbDevice *device, const UsbChannelDefinition *definition, |
| const void *data, size_t size, int timeout |
| ) { |
| const unsigned char *buffer = data; |
| int index = 0; |
| |
| if (hidInDataSize) { |
| while (size) { |
| unsigned char report[hidInDataSize]; |
| const unsigned char count = MIN(size, (sizeof(report) - 2)); |
| int result; |
| |
| report[0] = HT_HID_RPT_InData; |
| report[1] = count; |
| memset(mempcpy(report+2, &buffer[index], count), 0, sizeof(report)-count-2); |
| |
| result = usbWriteEndpoint(device, definition->outputEndpoint, |
| report, sizeof(report), 1000); |
| if (result == -1) return -1; |
| |
| index += count; |
| size -= count; |
| } |
| } |
| |
| return index; |
| } |
| |
| static int |
| filterUsbInput3 (UsbInputFilterData *data) { |
| unsigned char *buffer = data->buffer; |
| |
| if ((data->length >= 2) && |
| (data->length == hidOutDataSize) && |
| (buffer[0] == HT_HID_RPT_OutData) && |
| (buffer[1] <= (data->length - 2))) { |
| data->length = buffer[1]; |
| memmove(data->buffer, data->buffer+2, data->length); |
| } |
| |
| return 1; |
| } |
| |
| static const GeneralOperations generalOperations3 = { |
| .initializeSession = initializeUsbSession3 |
| }; |
| |
| static const UsbOperations usbOperations3 = { |
| .general = &generalOperations3, |
| .writeData = writeUsbData3, |
| .inputFilter = filterUsbInput3 |
| }; |
| |
| static BraillePacketVerifierResult |
| verifyPacket ( |
| BrailleDisplay *brl, |
| unsigned char *bytes, size_t size, |
| size_t *length, void *data |
| ) { |
| unsigned char byte = bytes[size-1]; |
| |
| switch (size) { |
| case 1: |
| switch (byte) { |
| default: |
| *length = 1; |
| break; |
| |
| case HT_PKT_OK: |
| *length = 2; |
| break; |
| |
| case HT_PKT_Extended: |
| *length = 4; |
| break; |
| } |
| break; |
| |
| case 3: |
| if (bytes[0] == HT_PKT_Extended) *length += byte; |
| break; |
| |
| case 5: |
| if ((bytes[0] == HT_PKT_Extended) && |
| (bytes[1] == HT_MODEL_ActiveBraille) && |
| (bytes[2] == 2) && |
| (bytes[3] == HT_EXTPKT_Confirmation) && |
| (byte == 0X15)) |
| *length += 1; |
| break; |
| |
| default: |
| break; |
| } |
| |
| if ((size == *length) && (bytes[0] == HT_PKT_Extended) && (byte != ASCII_SYN)) { |
| return BRL_PVR_INVALID; |
| } |
| |
| return BRL_PVR_INCLUDE; |
| } |
| |
| static size_t |
| readPacket (BrailleDisplay *brl, void *buffer, size_t size) { |
| return readBraillePacket(brl, NULL, buffer, size, verifyPacket, NULL); |
| } |
| |
| static ssize_t |
| brl_readPacket (BrailleDisplay *brl, void *buffer, size_t size) { |
| const size_t length = readPacket(brl, buffer, size); |
| |
| if (length == 0 && errno != EAGAIN) return -1; |
| return length; |
| } |
| |
| static ssize_t |
| brl_writePacket (BrailleDisplay *brl, const void *packet, size_t length) { |
| return writeBrailleMessage(brl, NULL, 0, packet, length)? length: -1; |
| } |
| |
| static void |
| setState (BrailleDisplay *brl, BrailleDisplayState state) { |
| if (state == brl->data->currentState) { |
| ++brl->data->retryCount; |
| } else { |
| brl->data->retryCount = 0; |
| brl->data->currentState = state; |
| } |
| |
| startTimePeriod(&brl->data->statePeriod, 1000); |
| // logMessage(LOG_DEBUG, "State: %d+%d", brl->data->currentState, brl->data->retryCount); |
| } |
| |
| static int |
| brl_reset (BrailleDisplay *brl) { |
| static const unsigned char packet[] = {HT_PKT_Reset}; |
| return writeBraillePacket(brl, NULL, packet, sizeof(packet)); |
| } |
| |
| static int |
| identifyModel (BrailleDisplay *brl, unsigned char identifier) { |
| for ( |
| brl->data->model = modelTable; |
| brl->data->model->name && (brl->data->model->identifier != identifier); |
| brl->data->model++ |
| ); |
| |
| if (!brl->data->model->name) { |
| logMessage(LOG_ERR, "Detected unknown HandyTech model with ID %02X.", |
| identifier); |
| return 0; |
| } |
| |
| if (brl->data->model->identifier == HT_MODEL_ActiveBraille) { |
| GioEndpoint *endpoint = brl->gioEndpoint; |
| char *serialNumber = NULL; |
| |
| switch (gioGetResourceType(endpoint)) { |
| case GIO_TYPE_USB: { |
| UsbChannel *channel = gioGetResourceObject(endpoint); |
| serialNumber = usbGetSerialNumber(channel->device, 1000); |
| break; |
| } |
| |
| default: { |
| serialNumber = gioGetResourceName(endpoint); |
| break; |
| } |
| } |
| |
| if (serialNumber) { |
| const char *slash = strchr(serialNumber, '/'); |
| |
| if (slash) { |
| if (slash[1] == 'S') brl->data->model = &modelEntry_ab_s; |
| } |
| |
| free(serialNumber); |
| } |
| } |
| |
| logMessage(LOG_INFO, "Detected %s: %d data %s, %d status %s.", |
| brl->data->model->name, |
| brl->data->model->textCells, (brl->data->model->textCells == 1)? "cell": "cells", |
| brl->data->model->statusCells, (brl->data->model->statusCells == 1)? "cell": "cells"); |
| |
| brl->textColumns = brl->data->model->textCells; /* initialise size of display */ |
| brl->textRows = 1; |
| brl->statusColumns = brl->data->model->statusCells; |
| brl->statusRows = 1; |
| |
| setBrailleKeyTable(brl, brl->data->model->keyTableDefinition); |
| brl->setBrailleFirmness = brl->data->model->setBrailleFirmness; |
| brl->setTouchSensitivity = brl->data->model->setTouchSensitivity; |
| |
| memset(brl->data->rawStatus, 0, brl->data->model->statusCells); |
| memset(brl->data->rawData, 0, brl->data->model->textCells); |
| |
| brl->data->retryCount = 0; |
| brl->data->updateRequired = 0; |
| brl->data->currentState = BDS_OFF; |
| setState(brl, BDS_READY); |
| |
| return 1; |
| } |
| |
| static int |
| writeExtendedPacket ( |
| BrailleDisplay *brl, HT_ExtendedPacketType type, |
| const unsigned char *data, unsigned char size |
| ) { |
| HT_Packet packet; |
| packet.fields.type = HT_PKT_Extended; |
| packet.fields.data.extended.model = brl->data->model->identifier; |
| packet.fields.data.extended.length = size + 1; /* type byte is included */ |
| packet.fields.data.extended.type = type; |
| if (data) memcpy(packet.fields.data.extended.data.bytes, data, size); |
| packet.fields.data.extended.data.bytes[size] = ASCII_SYN; |
| size += 5; /* EXT, ID, LEN, TYPE, ..., SYN */ |
| return writeBrailleMessage(brl, NULL, type, &packet, size); |
| } |
| |
| static int |
| setAtcMode (BrailleDisplay *brl, unsigned char value) { |
| const unsigned char data[] = {value}; |
| return writeExtendedPacket(brl, HT_EXTPKT_SetAtcMode, data, sizeof(data)); |
| } |
| |
| static int |
| setBrailleFirmness (BrailleDisplay *brl, BrailleFirmness setting) { |
| const unsigned char data[] = {setting * 2 / BRL_FIRMNESS_MAXIMUM}; |
| return writeExtendedPacket(brl, HT_EXTPKT_SetFirmness, data, sizeof(data)); |
| } |
| |
| static int |
| setTouchSensitivity_Evolution (BrailleDisplay *brl, TouchSensitivity setting) { |
| const unsigned char data[] = {0XFF - (setting * 0XF0 / BRL_SENSITIVITY_MAXIMUM)}; |
| return writeExtendedPacket(brl, HT_EXTPKT_SetAtcSensitivity, data, sizeof(data)); |
| } |
| |
| static int |
| setTouchSensitivity_ActiveBraille (BrailleDisplay *brl, TouchSensitivity setting) { |
| const unsigned char data[] = {setting * 6 / BRL_SENSITIVITY_MAXIMUM}; |
| return writeExtendedPacket(brl, HT_EXTPKT_SetAtcSensitivity2, data, sizeof(data)); |
| } |
| |
| typedef int (DateTimeProcessor) (BrailleDisplay *brl, const HT_DateTime *dateTime); |
| static DateTimeProcessor *dateTimeProcessor = NULL; |
| |
| static int |
| requestDateTime (BrailleDisplay *brl, DateTimeProcessor *processor) { |
| int result = writeExtendedPacket(brl, HT_EXTPKT_GetRTC, NULL, 0); |
| |
| if (result) { |
| dateTimeProcessor = processor; |
| } |
| |
| return result; |
| } |
| |
| static int |
| logDateTime (BrailleDisplay *brl, const HT_DateTime *dateTime) { |
| logMessage(LOG_INFO, |
| "date and time of %s:" |
| " %04" PRIu16 "-%02" PRIu8 "-%02" PRIu8 |
| " %02" PRIu8 ":%02" PRIu8 ":%02" PRIu8, |
| brl->data->model->name, |
| getBigEndian16(dateTime->year), dateTime->month, dateTime->day, |
| dateTime->hour, dateTime->minute, dateTime->second); |
| |
| return 1; |
| } |
| |
| static int |
| synchronizeDateTime (BrailleDisplay *brl, const HT_DateTime *dateTime) { |
| long int delta; |
| TimeValue hostTime; |
| getCurrentTime(&hostTime); |
| |
| { |
| TimeValue deviceTime; |
| |
| { |
| TimeComponents components = { |
| .year = getBigEndian16(dateTime->year), |
| .month = dateTime->month - 1, |
| .day = dateTime->day - 1, |
| .hour = dateTime->hour, |
| .minute = dateTime->minute, |
| .second = dateTime->second |
| }; |
| |
| makeTimeValue(&deviceTime, &components); |
| } |
| |
| delta = millisecondsBetween(&hostTime, &deviceTime); |
| if (delta < 0) delta = -delta; |
| } |
| |
| if (delta > 1000) { |
| TimeComponents components; |
| HT_DateTime payload; |
| |
| expandTimeValue(&hostTime, &components); |
| putLittleEndian16(&payload.year, components.year); |
| payload.month = components.month + 1; |
| payload.day = components.day + 1; |
| payload.hour = components.hour; |
| payload.minute = components.minute; |
| payload.second = components.second; |
| |
| logMessage(LOG_DEBUG, "Time difference between host and device: %ld.%03ld", |
| (delta / MSECS_PER_SEC), (delta % MSECS_PER_SEC)); |
| |
| if (writeExtendedPacket(brl, HT_EXTPKT_SetRTC, |
| (unsigned char *)&payload, sizeof(payload))) { |
| return requestDateTime(brl, logDateTime); |
| } |
| } |
| |
| return 1; |
| } |
| |
| static int |
| initializeSession (BrailleDisplay *brl) { |
| const GeneralOperations *ops = gioGetApplicationData(brl->gioEndpoint); |
| |
| if (ops) { |
| if (ops->initializeSession) { |
| if (!ops->initializeSession(brl)) { |
| return 0; |
| } |
| } |
| } |
| |
| return 1; |
| } |
| |
| static void |
| setUsbConnectionProperties ( |
| GioUsbConnectionProperties *properties, |
| const UsbChannelDefinition *definition |
| ) { |
| if (definition->data) { |
| const UsbOperations *usbOps = definition->data; |
| |
| properties->applicationData = usbOps->general; |
| properties->writeData = usbOps->writeData; |
| properties->readData = usbOps->readData; |
| properties->awaitInput = usbOps->awaitInput; |
| properties->inputFilter = usbOps->inputFilter; |
| } |
| } |
| |
| static int |
| connectResource (BrailleDisplay *brl, const char *identifier) { |
| static const SerialParameters serialParameters = { |
| SERIAL_DEFAULT_PARAMETERS, |
| .baud = 19200, |
| .parity = SERIAL_PARITY_ODD |
| }; |
| |
| BEGIN_USB_STRING_LIST(usbManufacturers_0403_6001) |
| "FTDI", |
| END_USB_STRING_LIST |
| |
| BEGIN_USB_CHANNEL_DEFINITIONS |
| { /* GoHubs chip */ |
| .vendor=0X0921, .product=0X1200, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .serial = &serialParameters |
| }, |
| |
| { /* FTDI chip */ |
| .vendor=0X0403, .product=0X6001, |
| .manufacturers = usbManufacturers_0403_6001, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=2, |
| .serial = &serialParameters |
| }, |
| |
| { /* Easy Braille (HID) */ |
| .vendor=0X1FE4, .product=0X0044, |
| .configuration=1, .interface=0, .alternative=0, |
| .data=&usbOperations2 |
| }, |
| |
| { /* Braille Star 40 (HID) */ |
| .vendor=0X1FE4, .product=0X0074, |
| .configuration=1, .interface=0, .alternative=0, |
| .data=&usbOperations2 |
| }, |
| |
| { /* USB-HID adapter */ |
| .vendor=0X1FE4, .product=0X0003, |
| .configuration=1, .interface=0, .alternative=0, |
| .data=&usbOperations2 |
| }, |
| |
| { /* Active Braille */ |
| .vendor=0X1FE4, .product=0X0054, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&usbOperations3 |
| }, |
| |
| { /* Basic Braille 16 */ |
| .vendor=0X1FE4, .product=0X0081, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&usbOperations3 |
| }, |
| |
| { /* Basic Braille 20 */ |
| .vendor=0X1FE4, .product=0X0082, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&usbOperations3 |
| }, |
| |
| { /* Basic Braille 32 */ |
| .vendor=0X1FE4, .product=0X0083, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&usbOperations3 |
| }, |
| |
| { /* Basic Braille 40 */ |
| .vendor=0X1FE4, .product=0X0084, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&usbOperations3 |
| }, |
| |
| { /* Basic Braille 48 */ |
| .vendor=0X1FE4, .product=0X008A, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&usbOperations3 |
| }, |
| |
| { /* Basic Braille 64 */ |
| .vendor=0X1FE4, .product=0X0086, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&usbOperations3 |
| }, |
| |
| { /* Basic Braille 80 */ |
| .vendor=0X1FE4, .product=0X0087, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&usbOperations3 |
| }, |
| |
| { /* Basic Braille 160 */ |
| .vendor=0X1FE4, .product=0X008B, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&usbOperations3 |
| }, |
| |
| { /* Actilino */ |
| .vendor=0X1FE4, .product=0X0061, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&usbOperations3 |
| }, |
| |
| { /* Activator */ |
| .vendor=0X1FE4, .product=0X00A4, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&usbOperations3 |
| }, |
| |
| { /* Active Star 40 */ |
| .vendor=0X1FE4, .product=0X0064, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&usbOperations3 |
| }, |
| |
| { /* Connect Braille 40 */ |
| .vendor=0X1FE4, .product=0X0055, |
| .configuration=1, .interface=0, .alternative=0, |
| .inputEndpoint=1, .outputEndpoint=1, |
| .data=&usbOperations3 |
| }, |
| END_USB_CHANNEL_DEFINITIONS |
| |
| GioDescriptor descriptor; |
| gioInitializeDescriptor(&descriptor); |
| |
| descriptor.serial.parameters = &serialParameters; |
| |
| descriptor.usb.channelDefinitions = usbChannelDefinitions; |
| descriptor.usb.setConnectionProperties = setUsbConnectionProperties; |
| descriptor.usb.options.inputTimeout = 100; |
| descriptor.usb.options.requestTimeout = 100; |
| |
| descriptor.bluetooth.channelNumber = 1; |
| descriptor.bluetooth.discoverChannel = 1; |
| |
| if (connectBrailleResource(brl, identifier, &descriptor, initializeSession)) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static BrailleResponseResult |
| isIdentityResponse (BrailleDisplay *brl, const void *packet, size_t size) { |
| const HT_Packet *response = packet; |
| |
| return (response->fields.type == HT_PKT_OK)? BRL_RSP_DONE: BRL_RSP_UNEXPECTED; |
| } |
| |
| static int |
| brl_construct (BrailleDisplay *brl, char **parameters, const char *device) { |
| if ((brl->data = malloc(sizeof(*brl->data)))) { |
| memset(brl->data, 0, sizeof(*brl->data)); |
| |
| if (connectResource(brl, device)) { |
| unsigned int setTime = 0; |
| HT_Packet response; |
| |
| if (*parameters[PARM_SETTIME]) { |
| if (!validateYesNo(&setTime, parameters[PARM_SETTIME])) { |
| logMessage(LOG_WARNING, "%s: %s", "invalid set time setting", |
| parameters[PARM_SETTIME]); |
| } |
| } |
| |
| setTime = !!setTime; |
| |
| if (probeBrailleDisplay(brl, 3, NULL, 100, |
| brl_reset, |
| readPacket, &response, sizeof(response), |
| isIdentityResponse)) { |
| if (identifyModel(brl, response.fields.data.ok.model)) { |
| makeOutputTable(dotsTable_ISO11548_1); |
| |
| if (brl->data->model->hasATC) { |
| setAtcMode(brl, 1); |
| } |
| |
| if (setTime) { |
| if (brl->data->model->hasTime) { |
| requestDateTime(brl, synchronizeDateTime); |
| } else { |
| logMessage(LOG_INFO, "%s does not support setting the clock", |
| brl->data->model->name); |
| } |
| } |
| |
| return 1; |
| } |
| } |
| |
| disconnectBrailleResource(brl, NULL); |
| } |
| |
| free(brl->data); |
| } else { |
| logMallocError(); |
| } |
| |
| return 0; |
| } |
| |
| static void |
| brl_destruct (BrailleDisplay *brl) { |
| if (brl->data) { |
| disconnectBrailleResource(brl, brl->data->model->sessionEnder); |
| |
| free(brl->data); |
| brl->data = NULL; |
| } |
| |
| deallocateHidInputBuffer(); |
| } |
| |
| static int |
| writeCells (BrailleDisplay *brl) { |
| return brl->data->model->writeCells(brl); |
| } |
| |
| static int |
| writeCells_statusAndText (BrailleDisplay *brl) { |
| unsigned char buffer[1 + brl->data->model->statusCells + brl->data->model->textCells]; |
| unsigned char *byte = buffer; |
| |
| *byte++ = HT_PKT_Braille; |
| byte = mempcpy(byte, brl->data->rawStatus, brl->data->model->statusCells); |
| byte = mempcpy(byte, brl->data->rawData, brl->data->model->textCells); |
| |
| return writeBrailleMessage(brl, NULL, HT_PKT_Braille, buffer, byte-buffer); |
| } |
| |
| static int |
| writeCells_Bookworm (BrailleDisplay *brl) { |
| unsigned char buffer[1 + brl->data->model->statusCells + brl->data->model->textCells + 1]; |
| |
| buffer[0] = 0X01; |
| memcpy(buffer+1, brl->data->rawData, brl->data->model->textCells); |
| buffer[sizeof(buffer)-1] = ASCII_SYN; |
| return writeBrailleMessage(brl, NULL, 0X01, buffer, sizeof(buffer)); |
| } |
| |
| static int |
| writeCells_Evolution (BrailleDisplay *brl) { |
| return writeExtendedPacket(brl, HT_EXTPKT_Braille, |
| brl->data->rawData, brl->data->model->textCells); |
| } |
| |
| static int |
| updateCells (BrailleDisplay *brl) { |
| if (!brl->data->updateRequired) return 1; |
| if (brl->data->currentState != BDS_READY) return 1; |
| |
| if (!writeCells(brl)) return 0; |
| brl->data->updateRequired = 0; |
| return 1; |
| } |
| |
| static int |
| brl_writeWindow (BrailleDisplay *brl, const wchar_t *text) { |
| const size_t cellCount = brl->data->model->textCells; |
| |
| if (cellsHaveChanged(brl->data->prevData, brl->buffer, cellCount, NULL, NULL, NULL)) { |
| translateOutputCells(brl->data->rawData, brl->data->prevData, cellCount); |
| brl->data->updateRequired = 1; |
| } |
| |
| return updateCells(brl); |
| } |
| |
| static int |
| brl_writeStatus (BrailleDisplay *brl, const unsigned char *st) { |
| const size_t cellCount = brl->data->model->statusCells; |
| |
| if (cellsHaveChanged(brl->data->prevStatus, st, cellCount, NULL, NULL, NULL)) { |
| translateOutputCells(brl->data->rawStatus, brl->data->prevStatus, cellCount); |
| brl->data->updateRequired = 1; |
| } |
| |
| return 1; |
| } |
| |
| static int |
| interpretByte_key (BrailleDisplay *brl, unsigned char byte) { |
| int release = (byte & HT_KEY_RELEASE) != 0; |
| if (release) byte ^= HT_KEY_RELEASE; |
| |
| if ((byte >= HT_KEY_ROUTING) && |
| (byte < (HT_KEY_ROUTING + brl->data->model->textCells))) { |
| return enqueueKeyEvent(brl, HT_GRP_RoutingKeys, byte - HT_KEY_ROUTING, !release); |
| } |
| |
| if ((byte >= HT_KEY_STATUS) && |
| (byte < (HT_KEY_STATUS + brl->data->model->statusCells))) { |
| return enqueueKeyEvent(brl, HT_GRP_NavigationKeys, byte, !release); |
| } |
| |
| if (byte > 0) { |
| return enqueueKeyEvent(brl, HT_GRP_NavigationKeys, byte, !release); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| interpretByte_Bookworm (BrailleDisplay *brl, unsigned char byte) { |
| static const KeyNumber keys[] = { |
| HT_BWK_Backward, |
| HT_BWK_Forward, |
| HT_BWK_Escape, |
| HT_BWK_Enter, |
| 0 |
| }; |
| |
| const KeyNumber *key = keys; |
| const KeyGroup group = HT_GRP_NavigationKeys; |
| |
| if (!byte) return 0; |
| { |
| unsigned char bits = byte; |
| |
| while (*key) bits &= ~*key++; |
| if (bits) return 0; |
| key = keys; |
| } |
| |
| while (*key) { |
| if ((byte & *key) && !enqueueKeyEvent(brl, group, *key, 1)) return 0; |
| key += 1; |
| } |
| |
| do { |
| key -= 1; |
| if ((byte & *key) && !enqueueKeyEvent(brl, group, *key, 0)) return 0; |
| } while (key != keys); |
| |
| return 1; |
| } |
| |
| static int |
| brl_readCommand (BrailleDisplay *brl, KeyTableCommandContext context) { |
| while (1) { |
| HT_Packet packet; |
| size_t size = readPacket(brl, &packet, sizeof(packet)); |
| |
| if (size == 0) { |
| if (errno != EAGAIN) return BRL_CMD_RESTARTBRL; |
| break; |
| } |
| |
| /* a kludge to handle the Bookworm going offline */ |
| if (brl->data->model->identifier == HT_MODEL_Bookworm) { |
| if (packet.fields.type == 0X06) { |
| if (brl->data->currentState != BDS_OFF) { |
| /* if we get another byte right away then the device |
| * has gone offline and is echoing its display |
| */ |
| if (awaitBrailleInput(brl, 10)) { |
| setState(brl, BDS_OFF); |
| continue; |
| } |
| |
| /* if an input error occurred then restart the driver */ |
| if (errno != EAGAIN) return BRL_CMD_RESTARTBRL; |
| |
| /* no additional input so fall through and interpret the packet as keys */ |
| } |
| } |
| } |
| |
| switch (packet.fields.type) { |
| case HT_PKT_OK: |
| if (packet.fields.data.ok.model == brl->data->model->identifier) { |
| releaseBrailleKeys(brl); |
| brl->data->updateRequired = 1; |
| continue; |
| } |
| break; |
| |
| default: |
| switch (brl->data->currentState) { |
| case BDS_OFF: |
| continue; |
| |
| case BDS_READY: |
| switch (packet.fields.type) { |
| case HT_PKT_NAK: |
| brl->data->updateRequired = 1; |
| case HT_PKT_ACK: |
| acknowledgeBrailleMessage(brl); |
| continue; |
| |
| case HT_PKT_Extended: { |
| unsigned char length = packet.fields.data.extended.length - 1; |
| const unsigned char *bytes = &packet.fields.data.extended.data.bytes[0]; |
| |
| switch (packet.fields.data.extended.type) { |
| case HT_EXTPKT_Confirmation: |
| switch (bytes[0]) { |
| case HT_PKT_NAK: |
| brl->data->updateRequired = 1; |
| case HT_PKT_ACK: |
| acknowledgeBrailleMessage(brl); |
| continue; |
| |
| default: |
| break; |
| } |
| break; |
| |
| case HT_EXTPKT_Key: |
| if (brl->data->model->interpretByte(brl, bytes[0])) { |
| updateCells(brl); |
| return EOF; |
| } |
| break; |
| |
| case HT_EXTPKT_Scancode: { |
| while (length--) |
| enqueueCommand(BRL_CMD_BLK(PASSAT) | BRL_ARG_PUT(*bytes++)); |
| continue; |
| } |
| |
| case HT_EXTPKT_GetRTC: { |
| const HT_DateTime *const payload = (HT_DateTime *)bytes; |
| DateTimeProcessor *processor = dateTimeProcessor; |
| dateTimeProcessor = NULL; |
| |
| if (processor) { |
| if (!processor(brl, payload)) { |
| break; |
| } |
| } |
| |
| continue; |
| } |
| |
| case HT_EXTPKT_AtcInfo: { |
| unsigned int readingPosition = BRL_MSK_ARG; |
| unsigned int highestPressure = 0; |
| |
| if (bytes[0]) { |
| const unsigned int cellCount = brl->data->model->textCells + brl->data->model->statusCells; |
| unsigned int cellIndex = bytes[0] - 1; |
| unsigned int dataIndex; |
| |
| for (dataIndex=1; dataIndex<length; dataIndex+=1) { |
| const unsigned char byte = bytes[dataIndex]; |
| |
| const unsigned char pressures[] = { |
| HIGH_NIBBLE(byte) >> 4, |
| LOW_NIBBLE(byte) |
| }; |
| |
| const unsigned int pressureCount = ARRAY_COUNT(pressures); |
| unsigned int pressureIndex; |
| |
| for (pressureIndex=0; pressureIndex<pressureCount; pressureIndex+=1) { |
| const unsigned char pressure = pressures[pressureIndex]; |
| |
| if (pressure > highestPressure) { |
| highestPressure = pressure; |
| readingPosition = cellIndex; |
| } |
| |
| cellIndex += 1; |
| } |
| } |
| |
| if (readingPosition >= cellCount) readingPosition = BRL_MSK_ARG; |
| } |
| |
| enqueueCommand(BRL_CMD_BLK(TOUCH_AT) | readingPosition); |
| continue; |
| } |
| |
| case HT_EXTPKT_ReadingPosition: { |
| const unsigned int cellCount = brl->data->model->textCells + brl->data->model->statusCells; |
| unsigned int readingPosition = bytes[0]; |
| |
| if ((readingPosition == 0XFF) || (readingPosition >= cellCount)) { |
| readingPosition = BRL_MSK_ARG; |
| } |
| |
| enqueueCommand(BRL_CMD_BLK(TOUCH_AT) | readingPosition); |
| continue; |
| } |
| |
| default: |
| break; |
| } |
| break; |
| } |
| |
| default: |
| if (brl->data->model->interpretByte(brl, packet.fields.type)) { |
| updateCells(brl); |
| return EOF; |
| } |
| break; |
| } |
| break; |
| } |
| break; |
| } |
| |
| logUnexpectedPacket(packet.bytes, size); |
| logMessage(LOG_WARNING, "state %d", brl->data->currentState); |
| } |
| |
| updateCells(brl); |
| |
| return EOF; |
| } |