| /* |
| * 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 <fcntl.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <stdarg.h> |
| #include <string.h> |
| |
| #include "log.h" |
| #include "parameters.h" |
| #include "ascii.h" |
| #include "cmd.h" |
| #include "parse.h" |
| #include "async_handle.h" |
| #include "async_wait.h" |
| #include "async_alarm.h" |
| #include "timing.h" |
| #include "ports.h" |
| #include "message.h" |
| |
| #define BRL_HAVE_PACKET_IO |
| |
| typedef enum { |
| PARM_EMBEDDED, |
| PARM_LATCH_DELAY, |
| PARM_PROTOCOL |
| } DriverParameter; |
| #define BRLPARMS "embedded", "latchdelay", "protocol" |
| |
| #include "brl_driver.h" |
| #include "brldefs-ir.h" |
| |
| BEGIN_KEY_NAME_TABLE(common) |
| KEY_NAME_ENTRY(IR_KEY_L1, "L1"), |
| KEY_NAME_ENTRY(IR_KEY_L2, "L2"), |
| KEY_NAME_ENTRY(IR_KEY_L3, "L3"), |
| KEY_NAME_ENTRY(IR_KEY_L4, "L4"), |
| KEY_NAME_ENTRY(IR_KEY_L5, "L5"), |
| KEY_NAME_ENTRY(IR_KEY_L6, "L6"), |
| KEY_NAME_ENTRY(IR_KEY_L7, "L7"), |
| KEY_NAME_ENTRY(IR_KEY_L8, "L8"), |
| |
| KEY_NAME_ENTRY(IR_KEY_Menu, "Menu"), |
| KEY_NAME_ENTRY(IR_KEY_Z, "Z"), |
| |
| KEY_GROUP_ENTRY(IR_GRP_RoutingKeys, "RoutingKey"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(brl) |
| KEY_NAME_ENTRY(IR_KEY_Dot1, "Dot1"), |
| KEY_NAME_ENTRY(IR_KEY_Dot2, "Dot2"), |
| KEY_NAME_ENTRY(IR_KEY_Dot3, "Dot3"), |
| KEY_NAME_ENTRY(IR_KEY_Dot4, "Dot4"), |
| KEY_NAME_ENTRY(IR_KEY_Dot5, "Dot5"), |
| KEY_NAME_ENTRY(IR_KEY_Dot6, "Dot6"), |
| KEY_NAME_ENTRY(IR_KEY_Dot7, "Dot7"), |
| KEY_NAME_ENTRY(IR_KEY_Dot8, "Dot8"), |
| KEY_NAME_ENTRY(IR_KEY_Backspace, "Backspace"), |
| KEY_NAME_ENTRY(IR_KEY_Space, "Space"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLE(pc) |
| KEY_GROUP_ENTRY(IR_GRP_Xt, "Xt"), |
| KEY_GROUP_ENTRY(IR_GRP_XtE0, "XtE0"), |
| KEY_GROUP_ENTRY(IR_GRP_XtE1, "XtE1"), |
| END_KEY_NAME_TABLE |
| |
| BEGIN_KEY_NAME_TABLES(brl) |
| KEY_NAME_TABLE(common), |
| KEY_NAME_TABLE(brl), |
| END_KEY_NAME_TABLES |
| |
| BEGIN_KEY_NAME_TABLES(pc) |
| KEY_NAME_TABLE(common), |
| KEY_NAME_TABLE(pc), |
| END_KEY_NAME_TABLES |
| |
| DEFINE_KEY_TABLE(brl) |
| DEFINE_KEY_TABLE(pc) |
| |
| BEGIN_KEY_TABLE_LIST |
| &KEY_TABLE_DEFINITION(brl), |
| &KEY_TABLE_DEFINITION(pc), |
| END_KEY_TABLE_LIST |
| |
| #define IR_MAXIMUM_PACKET_SIZE 0X100 |
| |
| #define IR_INTERNAL_SPEED 9600 |
| #define IR_EXTERNAL_SPEED_EUROBRAILLE 9600 |
| #define IR_EXTERNAL_SPEED_NATIVE 57600 |
| |
| /* Input/output ports */ |
| #define IR_PORT_BASE 0X340 |
| #define IR_PORT_INPUT (IR_PORT_BASE + 0) |
| #define IR_PORT_OUTPUT (IR_PORT_BASE + 1) |
| #define IR_PORT_OUTPUT2 (IR_PORT_BASE + 2) |
| |
| typedef struct { |
| GioEndpoint *gioEndpoint; |
| SerialParameters serialParameters; |
| |
| const char *name; |
| int speed; |
| |
| int (*writeNativePacket) ( |
| BrailleDisplay *brl, GioEndpoint *endpoint, |
| const unsigned char *packet, size_t size |
| ); |
| |
| void (*handleNativeAcknowledgement) (BrailleDisplay *brl); |
| |
| unsigned int state; |
| unsigned int length; /* useful when reading Eurobraille packets */ |
| unsigned int escape; |
| |
| unsigned char *position; |
| unsigned char packet[IR_MAXIMUM_PACKET_SIZE]; |
| } Port; |
| |
| typedef enum { |
| IR_PROTOCOL_EUROBRAILLE, |
| IR_PROTOCOL_NATIVE |
| } ProtocolIndex; |
| |
| typedef struct { |
| const char *protocolName; |
| int externalSpeed; |
| |
| size_t (*readExternalPacket) (BrailleDisplay *brl, Port *port, void *packet, size_t size); |
| unsigned forwardAcknowledgements:1; |
| |
| int (*forwardInternalPacket) ( |
| BrailleDisplay *brl, |
| const unsigned char *packet, size_t size |
| ); |
| |
| void (*forwardExternalPacket) ( |
| BrailleDisplay *brl, |
| const unsigned char *packet, size_t size, |
| int forward |
| ); |
| |
| int (*beginForwarding) (BrailleDisplay *brl); |
| int (*endForwarding) (BrailleDisplay *brl); |
| |
| ProtocolIndex next; |
| } ProtocolEntry; |
| |
| static void setExternalProtocol (BrailleDisplay *brl, ProtocolIndex index); |
| #define IR_PROTOCOL_DEFAULT IR_PROTOCOL_EUROBRAILLE |
| |
| typedef struct { |
| unsigned char base; |
| unsigned char composite; |
| } CompositeCharacterEntry; |
| |
| static const CompositeCharacterEntry compositeCharacterTable_circumflex[] = { |
| {.base=0X61, .composite=0XE2}, // aâ |
| {.base=0X65, .composite=0XEA}, // eê |
| {.base=0X69, .composite=0XEE}, // iî |
| {.base=0X6F, .composite=0XF4}, // oô |
| {.base=0X75, .composite=0XFB}, // uû |
| |
| {.base=0X41, .composite=0XC2}, // AÂ |
| {.base=0X45, .composite=0XCA}, // EÊ |
| {.base=0X49, .composite=0XCE}, // IÎ |
| {.base=0X4F, .composite=0XD4}, // OÔ |
| {.base=0X55, .composite=0XDB}, // UÛ |
| |
| {.base=0X00, .composite=0XA8} |
| }; |
| |
| static const CompositeCharacterEntry compositeCharacterTable_trema[] = { |
| {.base=0X61, .composite=0XE4}, // aä |
| {.base=0X65, .composite=0XEB}, // eë |
| {.base=0X69, .composite=0XEF}, // iï |
| {.base=0X6F, .composite=0XF6}, // oö |
| {.base=0X75, .composite=0XFC}, // uü |
| |
| {.base=0X41, .composite=0XC4}, // AÄ |
| {.base=0X45, .composite=0XCB}, // EË |
| {.base=0X49, .composite=0XCF}, // IÏ |
| {.base=0X4F, .composite=0XD6}, // OÖ |
| {.base=0X55, .composite=0XDC}, // UÜ |
| |
| {.base=0X00, .composite=0X5E} |
| }; |
| |
| static const CompositeCharacterEntry *compositeCharacterTables[] = { |
| compositeCharacterTable_circumflex, |
| compositeCharacterTable_trema |
| }; |
| |
| typedef enum { |
| xtsLeftShiftPressed, |
| xtsRightShiftPressed, |
| xtsShiftLocked, |
| |
| xtsLeftControlPressed, |
| xtsRightControlPressed, |
| |
| xtsLeftAltPressed, |
| xtsRightAltPressed, |
| |
| xtsLeftWindowsPressed, |
| xtsRightWindowsPressed, |
| |
| xtsInsertPressed, |
| xtsFnPressed |
| } XtState; |
| |
| #define XTS_BIT(number) (1 << (number)) |
| #define XTS_TEST(bits) (brl->data->xt.state & (bits)) |
| #define XTS_SHIFT XTS_TEST(XTS_BIT(xtsLeftShiftPressed) | XTS_BIT(xtsRightShiftPressed) | XTS_BIT(xtsShiftLocked)) |
| #define XTS_CONTROL XTS_TEST(XTS_BIT(xtsLeftControlPressed) | XTS_BIT(xtsRightControlPressed)) |
| #define XTS_ALT XTS_TEST(XTS_BIT(xtsLeftAltPressed)) |
| #define XTS_ALTGR XTS_TEST(XTS_BIT(xtsRightAltPressed)) |
| #define XTS_WIN XTS_TEST(XTS_BIT(xtsLeftWindowsPressed)) |
| #define XTS_INSERT XTS_TEST(XTS_BIT(xtsInsertPressed)) |
| #define XTS_FN XTS_TEST(XTS_BIT(xtsFnPressed)) |
| |
| typedef enum { |
| XtKeyType_ignore = 0, /* required for uninitialized entries */ |
| XtKeyType_modifier, |
| XtKeyType_lock, |
| XtKeyType_character, |
| XtKeyType_function, |
| XtKeyType_complex, |
| XtKeyType_composite |
| } XtKeyType; |
| |
| typedef struct { |
| unsigned char type; |
| unsigned char arg1; |
| unsigned char arg2; |
| unsigned char arg3; |
| } XtKeyEntry; |
| |
| typedef enum { |
| XT_KEYS_00, |
| XT_KEYS_E0, |
| XT_KEYS_E1 |
| } XT_KEY_SET; |
| |
| #define XT_RELEASE 0X80 |
| #define XT_KEY(set,key) ((XT_KEYS_##set << 7) | (key)) |
| |
| static const XtKeyEntry xtKeyTable[] = { |
| /* row 1 */ |
| [XT_KEY(00,0X01)] = { // key 1: escape |
| .type = XtKeyType_function, |
| .arg1=0X1B |
| } |
| , |
| [XT_KEY(00,0X3B)] = { // key 2: F1 |
| .type = XtKeyType_function, |
| .arg1=0X70 |
| } |
| , |
| [XT_KEY(00,0X3C)] = { // key 3: F2 |
| .type = XtKeyType_function, |
| .arg1=0X71 |
| } |
| , |
| [XT_KEY(00,0X3D)] = { // key 4: F3 |
| .type = XtKeyType_function, |
| .arg1=0X72 |
| } |
| , |
| [XT_KEY(00,0X3E)] = { // key 5: F4 |
| .type = XtKeyType_function, |
| .arg1=0X73 |
| } |
| , |
| [XT_KEY(00,0X3F)] = { // key 6: F5 |
| .type = XtKeyType_function, |
| .arg1=0X74 |
| } |
| , |
| [XT_KEY(00,0X40)] = { // key 7: F6 |
| .type = XtKeyType_function, |
| .arg1=0X75 |
| } |
| , |
| [XT_KEY(00,0X41)] = { // key 8: F7 |
| .type = XtKeyType_function, |
| .arg1=0X76 |
| } |
| , |
| [XT_KEY(00,0X42)] = { // key 9: F8 |
| .type = XtKeyType_function, |
| .arg1=0X77 |
| } |
| , |
| [XT_KEY(00,0X43)] = { // key 10: F9 |
| .type = XtKeyType_function, |
| .arg1=0X78 |
| } |
| , |
| [XT_KEY(00,0X44)] = { // key 11: F10 |
| .type = XtKeyType_function, |
| .arg1=0X79 |
| } |
| , |
| [XT_KEY(00,0X57)] = { // key 12: F11 |
| .type = XtKeyType_function, |
| .arg1=0X7A |
| } |
| , |
| [XT_KEY(00,0X58)] = { // key 13: F12 |
| .type = XtKeyType_function, |
| .arg1=0X7B |
| } |
| , |
| [XT_KEY(00,0X46)] = { // key 14: scroll lock |
| .type = XtKeyType_ignore |
| } |
| , |
| [XT_KEY(E1,0X1D)] = { // key 15: pause break |
| .type = XtKeyType_ignore |
| } |
| , |
| [XT_KEY(E0,0X52)] = { // key 16: insert |
| .type = XtKeyType_complex, |
| .arg1=0X0F, .arg2=1, .arg3=xtsInsertPressed |
| } |
| , |
| [XT_KEY(E0,0X53)] = { // key 17: delete |
| .type = XtKeyType_function, |
| .arg1=0X10, .arg2=1 |
| } |
| , |
| |
| /* row 2 */ |
| [XT_KEY(00,0X02)] = { // key 1: &1 |
| .type = XtKeyType_character, |
| .arg1=0X26, .arg2=0X31 |
| } |
| , |
| [XT_KEY(00,0X03)] = { // key 2: é2~ |
| .type = XtKeyType_character, |
| .arg1=0XE9, .arg2=0X32, .arg3=0X7E |
| } |
| , |
| [XT_KEY(00,0X04)] = { // key 3: "3# |
| .type = XtKeyType_character, |
| .arg1=0X22, .arg2=0X33, .arg3=0X23 |
| } |
| , |
| [XT_KEY(00,0X05)] = { // key 4: '4{ |
| .type = XtKeyType_character, |
| .arg1=0X27, .arg2=0X34, .arg3=0X7B |
| } |
| , |
| [XT_KEY(00,0X06)] = { // key 5: (5[ |
| .type = XtKeyType_character, |
| .arg1=0X28, .arg2=0X35, .arg3=0X5B |
| } |
| , |
| [XT_KEY(00,0X07)] = { // key 6: -6| |
| .type = XtKeyType_character, |
| .arg1=0X2D, .arg2=0X36, .arg3=0X7C |
| } |
| , |
| [XT_KEY(00,0X08)] = { // key 7: è7` |
| .type = XtKeyType_character, |
| .arg1=0XE8, .arg2=0X37, .arg3=0X60 |
| } |
| , |
| [XT_KEY(00,0X09)] = { // key 8: _8 |
| .type = XtKeyType_character, |
| .arg1=0X5F, .arg2=0X38, .arg3=0X5C |
| } |
| , |
| [XT_KEY(00,0X0A)] = { // key 9: ç9^ |
| .type = XtKeyType_character, |
| .arg1=0XE7, .arg2=0X39, .arg3=0X5E |
| } |
| , |
| [XT_KEY(00,0X0B)] = { // key 10: à0@ |
| .type = XtKeyType_character, |
| .arg1=0XE0, .arg2=0X30, .arg3=0X40 |
| } |
| , |
| [XT_KEY(00,0X0C)] = { // key 11: )°] |
| .type = XtKeyType_character, |
| .arg1=0X29, .arg2=0XB0, .arg3=0X5D |
| } |
| , |
| [XT_KEY(00,0X0D)] = { // key 12: =+} |
| .type = XtKeyType_character, |
| .arg1=0X3D, .arg2=0X2B, .arg3=0X7D |
| } |
| , |
| [XT_KEY(00,0X29)] = { // key 13: ² |
| .type = XtKeyType_character, |
| .arg1=0XB2 |
| } |
| , |
| [XT_KEY(00,0X0E)] = { // key 14: backspace |
| .type = XtKeyType_function, |
| .arg1=0X08 |
| } |
| , |
| |
| /* row 3 */ |
| [XT_KEY(00,0X0F)] = { // key 1: tab |
| .type = XtKeyType_function, |
| .arg1=0X09 |
| } |
| , |
| [XT_KEY(00,0X10)] = { // key 2: aA |
| .type = XtKeyType_character, |
| .arg1=0X61, .arg2=0X41 |
| } |
| , |
| [XT_KEY(00,0X11)] = { // key 3: zZ |
| .type = XtKeyType_character, |
| .arg1=0X7A, .arg2=0X5A |
| } |
| , |
| [XT_KEY(00,0X12)] = { // key 4: eE |
| .type = XtKeyType_character, |
| .arg1=0X65, .arg2=0X45, .arg3=0X80 |
| } |
| , |
| [XT_KEY(00,0X13)] = { // key 5: rR® |
| .type = XtKeyType_character, |
| .arg1=0X72, .arg2=0X52, .arg3=0XAE |
| } |
| , |
| [XT_KEY(00,0X14)] = { // key 6: tT |
| .type = XtKeyType_character, |
| .arg1=0X74, .arg2=0X54, .arg3=0X99 |
| } |
| , |
| [XT_KEY(00,0X15)] = { // key 7: yY |
| .type = XtKeyType_character, |
| .arg1=0X79, .arg2=0X59 |
| } |
| , |
| [XT_KEY(00,0X16)] = { // key 8: uU |
| .type = XtKeyType_character, |
| .arg1=0X75, .arg2=0X55 |
| } |
| , |
| [XT_KEY(00,0X17)] = { // key 9: iI |
| .type = XtKeyType_character, |
| .arg1=0X69, .arg2=0X49 |
| } |
| , |
| [XT_KEY(00,0X18)] = { // key 10: oO |
| .type = XtKeyType_character, |
| .arg1=0X6F, .arg2=0X4F |
| } |
| , |
| [XT_KEY(00,0X19)] = { // key 11: pP |
| .type = XtKeyType_character, |
| .arg1=0X70, .arg2=0X50 |
| } |
| , |
| [XT_KEY(00,0X1A)] = { // key 12: circumflex tréma |
| .type = XtKeyType_composite, |
| .arg1=1, .arg2=2 |
| } |
| , |
| [XT_KEY(00,0X1B)] = { // key 13: $£¤ |
| .type = XtKeyType_character, |
| .arg1=0X24, .arg2=0XA3, .arg3=0XA4 |
| } |
| , |
| [XT_KEY(00,0X1C)] = { // key 14: return |
| .type = XtKeyType_function, |
| .arg1=0X0D |
| } |
| , |
| |
| /* row 4 */ |
| [XT_KEY(00,0X3A)] = { // key 1: shift lock |
| .type = XtKeyType_lock, |
| .arg1=xtsShiftLocked |
| } |
| , |
| [XT_KEY(00,0X1E)] = { // key 2: qQ |
| .type = XtKeyType_character, |
| .arg1=0X71, .arg2=0X51 |
| } |
| , |
| [XT_KEY(00,0X1F)] = { // key 3: sS |
| .type = XtKeyType_character, |
| .arg1=0X73, .arg2=0X53 |
| } |
| , |
| [XT_KEY(00,0X20)] = { // key 4: dD |
| .type = XtKeyType_character, |
| .arg1=0X64, .arg2=0X44 |
| } |
| , |
| [XT_KEY(00,0X21)] = { // key 5: fF |
| .type = XtKeyType_character, |
| .arg1=0X66, .arg2=0X46 |
| } |
| , |
| [XT_KEY(00,0X22)] = { // key 6: gG |
| .type = XtKeyType_character, |
| .arg1=0X67, .arg2=0X47 |
| } |
| , |
| [XT_KEY(00,0X23)] = { // key 7: hH |
| .type = XtKeyType_character, |
| .arg1=0X68, .arg2=0X48 |
| } |
| , |
| [XT_KEY(00,0X24)] = { // key 8: jJ |
| .type = XtKeyType_character, |
| .arg1=0X6A, .arg2=0X4A |
| } |
| , |
| [XT_KEY(00,0X25)] = { // key 9: kK |
| .type = XtKeyType_character, |
| .arg1=0X6B, .arg2=0X4B |
| } |
| , |
| [XT_KEY(00,0X26)] = { // key 10: lL |
| .type = XtKeyType_character, |
| .arg1=0X6C, .arg2=0X4C |
| } |
| , |
| [XT_KEY(00,0X27)] = { // key 11: mM |
| .type = XtKeyType_character, |
| .arg1=0X6D, .arg2=0X4D |
| } |
| , |
| [XT_KEY(00,0X28)] = { // key 12: ù% |
| .type = XtKeyType_character, |
| .arg1=0XF9, .arg2=0X25 |
| } |
| , |
| [XT_KEY(00,0X2B)] = { // key 13: *µ |
| .type = XtKeyType_character, |
| .arg1=0X2A, .arg2=0XB5 |
| } |
| , |
| [XT_KEY(00,0X1C)] = { // key 14: return |
| .type = XtKeyType_function, |
| .arg1=0X0D |
| } |
| , |
| |
| /* row 5 */ |
| [XT_KEY(00,0X2A)] = { // key 1: left shift |
| .type = XtKeyType_modifier, |
| .arg1=xtsLeftShiftPressed, .arg2=xtsShiftLocked |
| } |
| , |
| [XT_KEY(00,0X2C)] = { // key 2: wW |
| .type = XtKeyType_character, |
| .arg1=0X77, .arg2=0X57 |
| } |
| , |
| [XT_KEY(00,0X2D)] = { // key 3: xX |
| .type = XtKeyType_character, |
| .arg1=0X78, .arg2=0X58 |
| } |
| , |
| [XT_KEY(00,0X2E)] = { // key 4: cC© |
| .type = XtKeyType_character, |
| .arg1=0X63, .arg2=0X43, .arg3=0XA9 |
| } |
| , |
| [XT_KEY(00,0X2F)] = { // key 5: vV |
| .type = XtKeyType_character, |
| .arg1=0X76, .arg2=0X56 |
| } |
| , |
| [XT_KEY(00,0X30)] = { // key 6: bB |
| .type = XtKeyType_character, |
| .arg1=0X62, .arg2=0X42 |
| } |
| , |
| [XT_KEY(00,0X31)] = { // key 7: nN |
| .type = XtKeyType_character, |
| .arg1=0X6E, .arg2=0X4E |
| } |
| , |
| [XT_KEY(00,0X32)] = { // key 8: ,? |
| .type = XtKeyType_character, |
| .arg1=0X2C, .arg2=0X3F |
| } |
| , |
| [XT_KEY(00,0X33)] = { // key 9: ;. |
| .type = XtKeyType_character, |
| .arg1=0X3B, .arg2=0X2E |
| } |
| , |
| [XT_KEY(00,0X34)] = { // key 10: :/ |
| .type = XtKeyType_character, |
| .arg1=0X3A, .arg2=0X2F |
| } |
| , |
| [XT_KEY(00,0X35)] = { // key 11: !§ |
| .type = XtKeyType_character, |
| .arg1=0X21, .arg2=0XA7 |
| } |
| , |
| [XT_KEY(00,0X56)] = { // key 12: <> |
| .type = XtKeyType_character, |
| .arg1=0X3C, .arg2=0X3E |
| } |
| , |
| [XT_KEY(00,0X36)] = { // key 13: right shift |
| .type = XtKeyType_modifier, |
| .arg1=xtsRightShiftPressed, .arg2=xtsShiftLocked |
| } |
| , |
| |
| /* row 6 */ |
| [XT_KEY(00,0X1D)] = { // key 1: left control |
| .type = XtKeyType_modifier, |
| .arg1=xtsLeftControlPressed |
| } |
| , |
| [XT_KEY(E1,0X01)] = { // key 2: fn |
| .type = XtKeyType_modifier, |
| .arg1=xtsFnPressed |
| } |
| , |
| [XT_KEY(E0,0X5B)] = { // key 3: left windows |
| .type = XtKeyType_complex, |
| .arg1=0X5B, .arg3=xtsLeftWindowsPressed |
| } |
| , |
| [XT_KEY(00,0X38)] = { // key 4: left alt |
| .type = XtKeyType_modifier, |
| .arg1=xtsLeftAltPressed |
| } |
| , |
| [XT_KEY(00,0X39)] = { // key 5: space |
| .type = XtKeyType_function, |
| .arg1=0X20 |
| } |
| , |
| [XT_KEY(E0,0X38)] = { // key 6: right alt |
| .type = XtKeyType_modifier, |
| .arg1=xtsRightAltPressed |
| } |
| , |
| [XT_KEY(E0,0X5D)] = { // key 7: right windows |
| .type = XtKeyType_function, |
| .arg1=0X5D |
| } |
| , |
| [XT_KEY(E0,0X1D)] = { // key 8: right control |
| .type = XtKeyType_modifier, |
| .arg1=xtsRightControlPressed |
| } |
| , |
| |
| /* arrow keys */ |
| [XT_KEY(E0,0X48)] = { // key 1: up arrow |
| .type = XtKeyType_function, |
| .arg1=0X0D, .arg2=1 |
| } |
| , |
| [XT_KEY(E0,0X4B)] = { // key 2: left arrow |
| .type = XtKeyType_function, |
| .arg1=0X0B, .arg2=1 |
| } |
| , |
| [XT_KEY(E0,0X50)] = { // key 3: down arrow |
| .type = XtKeyType_function, |
| .arg1=0X0E, .arg2=1 |
| } |
| , |
| [XT_KEY(E0,0X4D)] = { // key 4: right arrow |
| .type = XtKeyType_function, |
| .arg1=0X0C, .arg2=1 |
| } |
| , |
| [XT_KEY(E0,0X49)] = { // fn + key 1: page up |
| .type = XtKeyType_function, |
| .arg1=0X09, .arg2=1 |
| } |
| , |
| [XT_KEY(E0,0X47)] = { // fn + key 2: home |
| .type = XtKeyType_function, |
| .arg1=0X07, .arg2=1 |
| } |
| , |
| [XT_KEY(E0,0X51)] = { // fn + key 3: page down |
| .type = XtKeyType_function, |
| .arg1=0X0A, .arg2=1 |
| } |
| , |
| [XT_KEY(E0,0X4F)] = { // fn + key 4: end |
| .type = XtKeyType_function, |
| .arg1=0X08, .arg2=1 |
| } |
| }; |
| |
| struct BrailleDataStruct { |
| unsigned isConnected:1; |
| |
| unsigned isEmbedded:1; |
| unsigned isSuspended:1; |
| unsigned isForwarding:1; |
| |
| unsigned haveVisualDisplay:1; |
| |
| struct { |
| Port port; |
| int (*handlePacket) (BrailleDisplay *brl, const void *packet, size_t size); |
| int (*isOffline) (BrailleDisplay *brl); |
| KeyNumberSet linearKeys; |
| } internal; |
| |
| struct { |
| Port port; |
| GioHandleInputObject *hio; |
| const ProtocolEntry *protocol; |
| unsigned char cells[0XFF]; |
| } external; |
| |
| struct { |
| AsyncHandle monitor; |
| |
| int delay; |
| int interval; |
| |
| TimeValue started; |
| long int elapsed; |
| unsigned pulled:1; |
| } latch; |
| |
| struct { |
| unsigned char refresh; |
| unsigned char cells[0XFF]; |
| } braille; |
| |
| struct { |
| const CompositeCharacterEntry *composite; |
| const XtKeyEntry *key; |
| uint16_t state; |
| } xt; |
| |
| unsigned char *firmwareVersion; |
| char serialNumber[5]; |
| }; |
| |
| /* Function readNativePacket */ |
| /* Returns the size of the read packet. */ |
| /* 0 means no packet has been read and there is no error. */ |
| /* -1 means an error occurred */ |
| static size_t |
| readNativePacket (BrailleDisplay *brl, Port *port, void *packet, size_t size) { |
| unsigned char byte; |
| int wait = 0; |
| |
| while (gioReadByte(port->gioEndpoint, &byte, (port->state && wait))) { |
| size_t length = port->position - port->packet; |
| |
| wait = 1; |
| |
| if (port->state) { |
| switch (byte) { |
| case ASCII_DLE: |
| if (!port->escape) { |
| port->escape = 1; |
| continue; |
| } |
| |
| case ASCII_EOT: |
| if (!port->escape) { |
| port->state = 0; |
| |
| if (length <= size) { |
| memcpy(packet, port->packet, length); |
| logInputPacket(packet, length); |
| return length; |
| } |
| |
| logInputProblem("packet buffer too small", port->packet, length); |
| break; |
| } |
| |
| default: |
| if (length < sizeof(port->packet)) { |
| *port->position = byte; |
| } else { |
| if (length == sizeof(port->packet)) logTruncatedPacket(port->packet, length); |
| logDiscardedByte(byte); |
| } |
| |
| port->position += 1; |
| port->escape = 0; |
| break; |
| } |
| } else if (byte == ASCII_SOH) { |
| port->state = 1; |
| port->escape = 0; |
| port->position = port->packet; |
| } else if (byte == ASCII_ACK) { |
| port->handleNativeAcknowledgement(brl); |
| } else { |
| logIgnoredByte(byte); |
| } |
| } |
| |
| if (errno != EAGAIN) logSystemError("readNativePacket"); |
| return 0; |
| } |
| |
| static size_t |
| readEurobraillePacket (BrailleDisplay *brl, Port *port, void *packet, size_t size) { |
| unsigned char byte; |
| int wait = 0; |
| |
| while (gioReadByte(port->gioEndpoint, &byte, (port->state && wait))) { |
| wait = 1; |
| |
| switch (port->state) { |
| case 0: |
| if (byte == ASCII_STX) { |
| port->state = 1; |
| port->position = port->packet; |
| port->length = 0; |
| } else { |
| logIgnoredByte(byte); |
| } |
| break; |
| |
| case 1: |
| port->length |= byte << 8; |
| port->state = 2; |
| break; |
| |
| case 2: |
| port->length |= byte; |
| |
| if (port->length < 3) { |
| logMessage(LOG_WARNING, "invalid Eurobraille packet declared size: %d", port->length); |
| port->state = 0; |
| } else { |
| port->length -= 2; |
| |
| if (port->length > sizeof(port->packet)) { |
| logMessage(LOG_CATEGORY(BRAILLE_DRIVER), "readEuroBraillePacket: rejecting packet whose declared size is too large"); |
| port->state = 0; |
| } else { |
| port->state = 3; |
| } |
| } |
| break; |
| |
| case 3: |
| *port->position++ = byte; |
| |
| if ((port->position - port->packet) == port->length) port->state = 4; |
| break; |
| |
| case 4: |
| if (byte == ASCII_ETX) { |
| size_t length = port->position - port->packet; |
| |
| port->state = 0; |
| |
| if (length <= size) { |
| memcpy(packet, port->packet, length); |
| logInputPacket(packet, length); |
| return length; |
| } |
| |
| logInputProblem("packet buffer too small", port->packet, length); |
| } else { |
| logMessage(LOG_WARNING, "Eurobraille packet with real size exceeding declared size"); |
| logDiscardedByte(byte); |
| port->state = 5; |
| } |
| break; |
| |
| case 5: |
| if (byte == ASCII_ETX) { |
| port->state = 0; |
| } else { |
| logDiscardedByte(byte); |
| } |
| break; |
| |
| default: |
| logMessage(LOG_WARNING, "readEurobraillePacket: reached unknown state %d", port->state); |
| port->state = 0; |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static inline int |
| needsEscape (unsigned char byte) { |
| static const unsigned char escapedChars[0X20] = { |
| [ASCII_SOH] = 1, [ASCII_EOT] = 1, [ASCII_DLE] = 1, |
| [ASCII_ACK] = 1, [ASCII_NAK] = 1, |
| }; |
| |
| if (byte < sizeof(escapedChars)) return escapedChars[byte]; |
| return 0; |
| } |
| |
| static int |
| writeNativePacket_internal ( |
| BrailleDisplay *brl, GioEndpoint *endpoint, |
| const unsigned char *packet, size_t size |
| ) { |
| return writeBrailleMessage(brl, endpoint, 0, packet, size); |
| } |
| |
| static int |
| writeNativePacket_external ( |
| BrailleDisplay *brl, GioEndpoint *endpoint, |
| const unsigned char *packet, size_t size |
| ) { |
| return writeBraillePacket(brl, endpoint, packet, size); |
| } |
| |
| static void |
| handleNativeAcknowledgement_internal (BrailleDisplay *brl) { |
| acknowledgeBrailleMessage(brl); |
| |
| if (brl->data->isForwarding && brl->data->external.protocol->forwardAcknowledgements) { |
| static const unsigned char acknowledgement[] = {ASCII_ACK}; |
| |
| writeBraillePacket(brl, brl->data->external.port.gioEndpoint, |
| acknowledgement, sizeof(acknowledgement)); |
| } |
| } |
| |
| static void |
| handleNativeAcknowledgement_external (BrailleDisplay *brl) { |
| } |
| |
| static size_t |
| writeNativePacket ( |
| BrailleDisplay *brl, Port *port, |
| const unsigned char *packet, size_t size |
| ) { |
| unsigned char buffer[(size * 2) + 2]; |
| size_t count; |
| |
| { |
| const unsigned char *source = packet; |
| unsigned char *target = buffer; |
| |
| *target++ = ASCII_SOH; |
| |
| while (size--) { |
| if (needsEscape(*source)) *target++ = ASCII_DLE; |
| *target++ = *source++; |
| } |
| |
| *target++ = ASCII_EOT; |
| count = target - buffer; |
| } |
| |
| if (!port->writeNativePacket(brl, port->gioEndpoint, buffer, count)) return 0; |
| return count; |
| } |
| |
| /* |
| static ssize_t |
| tryWriteNativePacket (BrailleDisplay *brl, Port *port, const void *packet, size_t size) { |
| ssize_t res; |
| while ( ! (res = writeNativePacket(brl, port, packet, size)) ) { |
| if (errno != EAGAIN) return 0; |
| } |
| return res; |
| } |
| */ |
| |
| static int |
| writeEurobraillePacket (BrailleDisplay *brl, Port *port, const void *data, size_t size) { |
| size_t count; |
| size_t packetSize = size + 2; |
| unsigned char packet[packetSize + 2]; |
| unsigned char *p = packet; |
| |
| *p++ = ASCII_STX; |
| *p++ = (packetSize >> 8) & 0X00FF; |
| *p++ = packetSize & 0X00FF; |
| p = mempcpy(p, data, size); |
| *p++ = ASCII_ETX; |
| |
| count = p - packet; |
| if (!writeBraillePacket(brl, port->gioEndpoint, packet, count)) return 0; |
| return count; |
| } |
| |
| static int |
| writeEurobrailleStringPacket (BrailleDisplay *brl, Port *port, const char *string) { |
| return writeEurobraillePacket(brl, port, string, strlen(string) + 1); |
| } |
| |
| /* Low-level write of dots to the braile display */ |
| /* No check is performed to avoid several consecutive identical writes at this level */ |
| static size_t |
| writeDots (BrailleDisplay *brl, Port *port, const unsigned char *dots) { |
| size_t size = brl->textColumns * brl->textRows; |
| unsigned char packet[IR_WINDOW_SIZE_MAXIMUM + 1]; |
| unsigned char *p = packet; |
| int i; |
| |
| *p++ = IR_OPT_WriteBraille; |
| for (i=0; i<IR_WINDOW_SIZE_MAXIMUM-size; i+=1) *p++ = 0; |
| for (i=0; i<size; i+=1) *p++ = dots[size-i-1]; |
| return writeNativePacket(brl, port, packet, sizeof(packet)); |
| } |
| |
| /* Low-level write of text to the braile display */ |
| /* No check is performed to avoid several consecutive identical writes at this level */ |
| static size_t |
| writeWindow (BrailleDisplay *brl, const unsigned char *text) { |
| size_t size = brl->textColumns * brl->textRows; |
| unsigned char dots[size]; |
| |
| translateOutputCells(dots, text, size); |
| return writeDots(brl, &brl->data->internal.port, dots); |
| } |
| |
| static size_t |
| clearWindow (BrailleDisplay *brl) { |
| size_t size = brl->textColumns * brl->textRows; |
| unsigned char window[size]; |
| |
| memset(window, 0, sizeof(window)); |
| return writeWindow(brl, window); |
| } |
| |
| static void |
| activateBraille(void) { |
| writePort1(IR_PORT_OUTPUT, 0X01); |
| asyncWait(9); |
| writePort1(IR_PORT_OUTPUT, 0X00); |
| } |
| |
| static void |
| deactivateBraille(void) { |
| writePort1(IR_PORT_OUTPUT, 0X02); |
| asyncWait(9); |
| writePort1(IR_PORT_OUTPUT, 0X00); |
| } |
| |
| static ssize_t brl_readPacket (BrailleDisplay *brl, void *packet, size_t size) |
| { |
| if (brl->data->isEmbedded && (brl->data->isSuspended || brl->data->isForwarding)) return 0; |
| return readNativePacket(brl, &brl->data->internal.port, packet, size); |
| } |
| |
| /* Function brl_writePacket */ |
| /* Returns 1 if the packet is actually written, 0 if the packet is not written */ |
| static ssize_t brl_writePacket (BrailleDisplay *brl, const void *packet, size_t size) |
| { |
| if (brl->data->isSuspended || brl->data->isForwarding) { |
| errno = EAGAIN; |
| return 0; |
| } |
| return writeNativePacket(brl, &brl->data->internal.port, packet, size); |
| } |
| |
| static int brl_reset (BrailleDisplay *brl) |
| { |
| return 0; |
| } |
| |
| static int |
| sendInteractiveKey (BrailleDisplay *brl, Port *port, unsigned char key) { |
| const unsigned char packet[] = {IR_IPT_InteractiveKey, key}; |
| |
| return writeNativePacket(brl, port, packet, sizeof(packet)); |
| } |
| |
| static int |
| sendMenuKey (BrailleDisplay *brl, Port *port) { |
| return sendInteractiveKey(brl, port, 'Q'); |
| } |
| |
| typedef struct { |
| int (*handleZKey) (BrailleDisplay *brl, Port *port); |
| int (*handleRoutingKey) (BrailleDisplay *brl, Port *port, unsigned char key); |
| int (*handlePCKey) (BrailleDisplay *brl, Port *port, int repeat, unsigned char escape, unsigned char key); |
| int (*handleFunctionKeys) (BrailleDisplay *brl, Port *port, KeyNumberSet keys); |
| int (*handleBrailleKeys) (BrailleDisplay *brl, Port *port, KeyNumberSet keys); |
| } KeyHandlers; |
| |
| static int |
| null_handleZKey(BrailleDisplay *brl, Port *port) { |
| logMessage(LOG_CATEGORY(BRAILLE_DRIVER), "ignoring Z key"); |
| return 1; |
| } |
| |
| static int |
| core_handleZKey(BrailleDisplay *brl, Port *port) { |
| logMessage(LOG_CATEGORY(BRAILLE_DRIVER), "Z key pressed"); |
| setExternalProtocol(brl, brl->data->external.protocol->next); |
| |
| { |
| Port *port = &brl->data->external.port; |
| |
| port->speed = brl->data->external.protocol->externalSpeed; |
| port->serialParameters.baud = port->speed; |
| if (!gioReconfigureResource(port->gioEndpoint, &port->serialParameters)) return 0; |
| } |
| |
| return 1; |
| } |
| |
| static int |
| core_handleRoutingKey(BrailleDisplay *brl, Port *port, unsigned char key) { |
| return enqueueKey(brl, IR_GRP_RoutingKeys, key-1); |
| } |
| |
| static int |
| core_handlePCKey(BrailleDisplay *brl, Port *port, int repeat, unsigned char escape, unsigned char code) { |
| return enqueueXtScanCode(brl, code, escape, IR_GRP_Xt, IR_GRP_XtE0, IR_GRP_XtE1); |
| } |
| |
| static int |
| core_handleFunctionKeys(BrailleDisplay *brl, Port *port, KeyNumberSet keys) { |
| return enqueueUpdatedKeys(brl, keys, &brl->data->internal.linearKeys, IR_GRP_NavigationKeys, IR_KEY_L1); |
| } |
| |
| static int |
| core_handleBrailleKeys(BrailleDisplay *brl, Port *port, KeyNumberSet keys) { |
| return enqueueKeys(brl, keys, IR_GRP_NavigationKeys, IR_KEY_Dot1); |
| } |
| |
| static const KeyHandlers keyHandlers_embedded = { |
| .handleZKey = core_handleZKey, |
| .handleRoutingKey = core_handleRoutingKey, |
| .handlePCKey = core_handlePCKey, |
| .handleFunctionKeys = core_handleFunctionKeys, |
| .handleBrailleKeys = core_handleBrailleKeys |
| }; |
| |
| static const KeyHandlers keyHandlers_nonembedded = { |
| .handleZKey = null_handleZKey, |
| .handleRoutingKey = core_handleRoutingKey, |
| .handlePCKey = core_handlePCKey, |
| .handleFunctionKeys = core_handleFunctionKeys, |
| .handleBrailleKeys = core_handleBrailleKeys |
| }; |
| |
| static int |
| eurobrl_handleRoutingKey(BrailleDisplay *brl, Port *port, unsigned char key) { |
| unsigned char data[] = { |
| 0X4B, 0X49, 1, key |
| }; |
| return writeEurobraillePacket(brl, port, data, sizeof(data)); |
| } |
| |
| static int |
| eurobrl_handlePCKey(BrailleDisplay *brl, Port *port, int repeat, unsigned char escape, unsigned char key) { |
| unsigned char data[] = {0X4B, 0X5A, 0, 0, 0, 0}; |
| const XtKeyEntry *xke = &xtKeyTable[key & ~XT_RELEASE]; |
| |
| switch (escape) { |
| case 0XE0: |
| xke += XT_KEY(E0, 0); |
| break; |
| |
| case 0XE1: |
| xke += XT_KEY(E1, 0); |
| break; |
| |
| default: |
| case 0X00: |
| xke += XT_KEY(00, 0); |
| break; |
| } |
| |
| if (xke >= (xtKeyTable + ARRAY_COUNT(xtKeyTable))) { |
| static const XtKeyEntry xtKeyEntry = { |
| .type = XtKeyType_ignore |
| }; |
| |
| xke = &xtKeyEntry; |
| } |
| |
| if (key & XT_RELEASE) { |
| int current = xke == brl->data->xt.key; |
| brl->data->xt.key = NULL; |
| |
| switch (xke->type) { |
| case XtKeyType_modifier: |
| brl->data->xt.state &= ~XTS_BIT(xke->arg1); |
| return 1; |
| |
| case XtKeyType_complex: |
| brl->data->xt.state &= ~XTS_BIT(xke->arg3); |
| if (current) goto isFunction; |
| return 1; |
| |
| default: |
| return 1; |
| } |
| } else { |
| brl->data->xt.key = xke; |
| |
| switch (xke->type) { |
| case XtKeyType_modifier: |
| brl->data->xt.state |= XTS_BIT(xke->arg1); |
| brl->data->xt.state &= ~XTS_BIT(xke->arg2); |
| return 1; |
| |
| case XtKeyType_complex: |
| brl->data->xt.state |= XTS_BIT(xke->arg3); |
| return 1; |
| |
| case XtKeyType_lock: |
| brl->data->xt.state |= XTS_BIT(xke->arg1); |
| return 1; |
| |
| case XtKeyType_character: |
| if (xke->arg3 && XTS_ALTGR) { |
| data[5] = xke->arg3; |
| } else if (xke->arg2 && XTS_SHIFT) { |
| data[5] = xke->arg2; |
| } else { |
| data[5] = xke->arg1; |
| } |
| break; |
| |
| case XtKeyType_function: |
| isFunction: |
| data[3] = xke->arg1; |
| data[2] = xke->arg2; |
| break; |
| |
| case XtKeyType_composite: { |
| unsigned char index; |
| |
| if (xke->arg2 && XTS_SHIFT) { |
| index = xke->arg2; |
| } else { |
| index = xke->arg1; |
| } |
| |
| if (index) brl->data->xt.composite = compositeCharacterTables[index - 1]; |
| return 1; |
| } |
| |
| default: |
| return 1; |
| } |
| } |
| |
| if (XTS_TEST(XTS_BIT(xtsLeftShiftPressed) | XTS_BIT(xtsRightShiftPressed))) data[4] |= 0X01; |
| if (XTS_CONTROL) data[4] |= 0X02; |
| if (XTS_ALT) data[4] |= 0X04; |
| if (XTS_TEST(XTS_BIT(xtsShiftLocked))) data[4] |= 0X08; |
| if (XTS_WIN) data[4] |= 0X10; |
| if (XTS_ALTGR) data[4] |= 0X20; |
| if (XTS_INSERT) data[4] |= 0X80; |
| |
| if (brl->data->xt.composite) { |
| unsigned char *byte = &data[5]; |
| |
| if (*byte) { |
| const CompositeCharacterEntry *cce = brl->data->xt.composite; |
| |
| while (cce->base) { |
| if (cce->base == *byte) { |
| *byte = cce->composite; |
| break; |
| } |
| |
| cce += 1; |
| } |
| |
| if (!cce->base && cce->composite) { |
| unsigned char original = *byte; |
| *byte = cce->composite; |
| if (!writeEurobraillePacket(brl, port, data, sizeof(data))) return 0; |
| *byte = original; |
| } |
| } |
| |
| brl->data->xt.composite = NULL; |
| } |
| |
| return writeEurobraillePacket(brl, port, data, sizeof(data)); |
| } |
| |
| static int |
| eurobrl_handleFunctionKeys(BrailleDisplay *brl, Port *port, KeyNumberSet keys) { |
| if (keys) { |
| unsigned char data[] = { |
| 0X4B, 0X43, 0, ( |
| (keys & 0XF) | |
| ((keys >> 1) & 0XF0) |
| ) |
| }; |
| |
| if (!writeEurobraillePacket(brl, port, data, sizeof(data))) return 0; |
| } |
| |
| return 1; |
| } |
| |
| static int |
| eurobrl_handleBrailleKeys(BrailleDisplay *brl, Port *port, KeyNumberSet keys) { |
| unsigned char data[] = { |
| 0X4B, 0X42, |
| (keys >> 8) & 0XFF, |
| keys & 0XFF |
| }; |
| |
| return writeEurobraillePacket(brl, port, data, sizeof(data)); |
| } |
| |
| static const KeyHandlers keyHandlers_eurobraille = { |
| .handleZKey = null_handleZKey, |
| .handleRoutingKey = eurobrl_handleRoutingKey, |
| .handlePCKey = eurobrl_handlePCKey, |
| .handleFunctionKeys = eurobrl_handleFunctionKeys, |
| .handleBrailleKeys = eurobrl_handleBrailleKeys |
| }; |
| |
| static int |
| writeExternalCells (BrailleDisplay *brl) { |
| return writeDots(brl, &brl->data->internal.port, brl->data->external.cells); |
| } |
| |
| static void |
| saveExternalCells (BrailleDisplay *brl, const unsigned char *cells) { |
| memcpy(brl->data->external.cells, cells, brl->textColumns); |
| } |
| |
| static int |
| handleNativePacket (BrailleDisplay *brl, Port *port, const KeyHandlers *keyHandlers, const unsigned char *packet, size_t size) { |
| if (size == 2) { |
| if (packet[0] == IR_IPT_InteractiveKey) { |
| if (packet[1] == 'W') { |
| return keyHandlers->handleZKey(brl, port); |
| } |
| |
| if ((1 <= packet[1]) && (packet[1] <= (brl->textColumns * brl->textRows))) { |
| return keyHandlers->handleRoutingKey(brl, port, packet[1]); |
| } |
| } |
| } else if (size == 3) { |
| int repeat = (packet[0] == IR_IPT_XtKeyCodeRepeat); |
| |
| if ((packet[0] == IR_IPT_XtKeyCode) || repeat) { |
| return keyHandlers->handlePCKey(brl, port, repeat, packet[1], packet[2]); |
| } |
| |
| if (packet[0] == IR_IPT_LinearKeys) { |
| KeyNumberSet keys = (packet[1] << 8) | packet[2]; |
| |
| return keyHandlers->handleFunctionKeys(brl, port, keys); |
| } |
| |
| if (packet[0] == IR_IPT_BrailleKeys) { |
| KeyNumberSet keys = (packet[1] << 8) | packet[2]; |
| |
| return keyHandlers->handleBrailleKeys(brl, port, keys); |
| } |
| } |
| |
| logUnexpectedPacket(packet, size); |
| return 0; |
| } |
| |
| static int |
| forwardInternalPacket_native ( |
| BrailleDisplay *brl, |
| const unsigned char *packet, size_t size |
| ) { |
| return writeNativePacket(brl, &brl->data->external.port, packet, size); |
| } |
| |
| static int |
| forwardInternalPacket_eurobraille ( |
| BrailleDisplay *brl, |
| const unsigned char *packet, size_t size |
| ) { |
| handleNativePacket(brl, &brl->data->external.port, &keyHandlers_eurobraille, packet, size); |
| return 1; |
| } |
| |
| static void |
| forwardExternalPacket_native ( |
| BrailleDisplay *brl, |
| const unsigned char *packet, size_t size, |
| int forward |
| ) { |
| if (forward) { |
| writeNativePacket(brl, &brl->data->internal.port, packet, size); |
| } |
| } |
| |
| static void |
| forwardExternalPacket_eurobraille ( |
| BrailleDisplay *brl, |
| const unsigned char *packet, size_t size, |
| int forward |
| ) { |
| if (size==2 && packet[0]=='S' && packet[1]=='I') { |
| /* Send system information */ |
| Port *port = &brl->data->external.port; |
| char str[256]; |
| |
| writeEurobrailleStringPacket(brl, port, "SNIRIS_KB_40"); |
| |
| writeEurobrailleStringPacket(brl, port, "SHIR4"); |
| |
| snprintf(str, sizeof(str), "SS%s", brl->data->serialNumber); |
| writeEurobrailleStringPacket(brl, port, str); |
| |
| writeEurobrailleStringPacket(brl, port, "SLFR"); |
| |
| str[0] = 'S'; |
| str[1] = 'G'; |
| str[2] = brl->textColumns; |
| writeEurobraillePacket(brl, port, str, 3); |
| |
| str[0] = 'S'; |
| str[1] = 'T'; |
| str[2] = 6; |
| writeEurobraillePacket(brl, port, str, 3); |
| |
| snprintf(str, sizeof(str), "So%d%da", 0XEF, 0XF8); |
| writeEurobrailleStringPacket(brl, port, str); |
| |
| writeEurobrailleStringPacket(brl, port, "SW1.92"); |
| |
| writeEurobrailleStringPacket(brl, port, "SP1.00 30-10-2006"); |
| |
| snprintf(str, sizeof(str), "SM%d", 0X08); |
| writeEurobrailleStringPacket(brl, port, str); |
| |
| writeEurobrailleStringPacket(brl, port, "SI"); |
| } else if (size==brl->textColumns+2 && packet[0]=='B' && packet[1]=='S') { |
| /* Write dots to braille display */ |
| saveExternalCells(brl, packet+2); |
| if (forward) writeExternalCells(brl); |
| } else { |
| logBytes(LOG_WARNING, "forwardEurobraillePacket could not handle this packet: ", packet, size); |
| } |
| } |
| |
| static int |
| beginForwarding_native (BrailleDisplay *brl) { |
| return sendMenuKey(brl, &brl->data->external.port); |
| } |
| |
| static int |
| endForwarding_native (BrailleDisplay *brl) { |
| return sendMenuKey(brl, &brl->data->external.port); |
| } |
| |
| static int |
| beginForwarding_eurobraille (BrailleDisplay *brl) { |
| brl->data->xt.composite = NULL; |
| brl->data->xt.key = NULL; |
| brl->data->xt.state = 0; |
| |
| writeExternalCells(brl); |
| return 1; |
| } |
| |
| static int |
| endForwarding_eurobraille (BrailleDisplay *brl) { |
| return 1; |
| } |
| |
| static const ProtocolEntry protocolTable[] = { |
| [IR_PROTOCOL_EUROBRAILLE] = { |
| .protocolName = strtext("eurobraille"), |
| .externalSpeed = IR_EXTERNAL_SPEED_EUROBRAILLE, |
| |
| .readExternalPacket = readEurobraillePacket, |
| .forwardAcknowledgements = 0, |
| |
| .forwardInternalPacket = forwardInternalPacket_eurobraille, |
| .forwardExternalPacket = forwardExternalPacket_eurobraille, |
| |
| .beginForwarding = beginForwarding_eurobraille, |
| .endForwarding = endForwarding_eurobraille, |
| |
| .next = IR_PROTOCOL_NATIVE |
| }, |
| |
| [IR_PROTOCOL_NATIVE] = { |
| .protocolName = strtext("native"), |
| .externalSpeed = IR_EXTERNAL_SPEED_NATIVE, |
| |
| .readExternalPacket = readNativePacket, |
| .forwardAcknowledgements = 1, |
| |
| .forwardInternalPacket = forwardInternalPacket_native, |
| .forwardExternalPacket = forwardExternalPacket_native, |
| |
| .beginForwarding = beginForwarding_native, |
| .endForwarding = endForwarding_native, |
| |
| .next = IR_PROTOCOL_EUROBRAILLE |
| }, |
| }; |
| |
| static const unsigned char protocolCount = ARRAY_COUNT(protocolTable); |
| |
| static void |
| setExternalProtocol (BrailleDisplay *brl, ProtocolIndex index) { |
| brl->data->external.protocol = &protocolTable[index]; |
| } |
| |
| static int |
| enterPacketForwardMode (BrailleDisplay *brl) { |
| logMessage(LOG_INFO, |
| "entering packet forward mode (port=%s, protocol=%s, speed=%d)", |
| brl->data->external.port.name, |
| brl->data->external.protocol->protocolName, |
| brl->data->external.port.speed); |
| |
| { |
| char msg[brl->textColumns+1]; |
| |
| snprintf(msg, sizeof(msg), "%s (%s)", |
| gettext("PC mode"), |
| gettext(brl->data->external.protocol->protocolName)); |
| message(NULL, msg, MSG_NODELAY); |
| } |
| |
| if (!brl->data->external.protocol->beginForwarding(brl)) return 0; |
| brl->data->isForwarding = 1; |
| return 1; |
| } |
| |
| static int |
| leavePacketForwardMode (BrailleDisplay *brl) { |
| logMessage(LOG_INFO, "leaving packet forward mode"); |
| if (!brl->data->external.protocol->endForwarding(brl)) return 0; |
| brl->data->isForwarding = 0; |
| brl->data->braille.refresh = 1; |
| return 1; |
| } |
| |
| static int |
| forwardExternalPackets (BrailleDisplay *brl) { |
| const ProtocolEntry *protocol = brl->data->external.protocol; |
| unsigned char packet[IR_MAXIMUM_PACKET_SIZE]; |
| size_t size; |
| |
| while ((size = protocol->readExternalPacket(brl, &brl->data->external.port, packet, sizeof(packet)))) { |
| protocol->forwardExternalPacket(brl, packet, size, |
| (brl->data->isForwarding && !brl->data->isSuspended)); |
| } |
| |
| return errno == EAGAIN; |
| } |
| |
| GIO_INPUT_HANDLER(irHandleExternalInput) { |
| BrailleDisplay *brl = parameters->data; |
| |
| if (!forwardExternalPackets(brl)) brl->hasFailed = 1; |
| return 0; |
| } |
| |
| static inline int |
| isMenuKeyPacket (const unsigned char *packet, size_t size) { |
| return (size == 2) && (packet[0] == IR_IPT_InteractiveKey) && (packet[1] == 'Q'); |
| } |
| |
| static int |
| handleInternalPacket_embedded (BrailleDisplay *brl, const void *packet, size_t size) { |
| if (brl->data->isSuspended) return 1; |
| |
| /* The test for Menu key should come first since this key toggles |
| * packet forward mode on/off |
| */ |
| if (isMenuKeyPacket(packet, size)) { |
| logMessage(LOG_CATEGORY(BRAILLE_DRIVER), "menu key pressed"); |
| |
| if (brl->data->isForwarding) { |
| if (!leavePacketForwardMode(brl)) return 0; |
| } else { |
| if (!enterPacketForwardMode(brl)) return 0; |
| } |
| } else if (brl->data->isForwarding) { |
| if (!brl->data->external.protocol->forwardInternalPacket(brl, packet, size)) return 0; |
| } else { |
| handleNativePacket(brl, NULL, &keyHandlers_embedded, packet, size); |
| } |
| |
| return 1; |
| } |
| |
| static int |
| isOffline_embedded (BrailleDisplay *brl) { |
| return brl->data->isForwarding || brl->data->isSuspended; |
| } |
| |
| static int |
| handleInternalPacket_nonembedded (BrailleDisplay *brl, const void *packet, size_t size) { |
| int menuKeyPressed = isMenuKeyPacket(packet, size); |
| |
| if (menuKeyPressed) { |
| logMessage(LOG_CATEGORY(BRAILLE_DRIVER), "menu key pressed"); |
| |
| if (brl->data->isConnected) { |
| logMessage(LOG_INFO, "device disconnected"); |
| brl->data->isConnected = 0; |
| return 1; |
| } |
| } |
| |
| if (!brl->data->isConnected) { |
| logMessage(LOG_INFO, "device reconnected"); |
| brl->data->isConnected = 1; |
| brl->data->braille.refresh = 1; |
| if (menuKeyPressed) return 1; |
| } |
| |
| handleNativePacket(brl, NULL, &keyHandlers_nonembedded, packet, size); |
| return 1; |
| } |
| |
| static int |
| isOffline_nonembedded (BrailleDisplay *brl) { |
| return !brl->data->isConnected; |
| } |
| |
| static int |
| brl_readCommand (BrailleDisplay *brl, KeyTableCommandContext context) { |
| unsigned char packet[IR_MAXIMUM_PACKET_SIZE]; |
| size_t size; |
| |
| while ((size = readNativePacket(brl, &brl->data->internal.port, packet, sizeof(packet)))) { |
| if (!brl->data->internal.handlePacket(brl, packet, size)) goto failure; |
| } |
| |
| if (errno != EAGAIN) goto failure; |
| if (brl->data->internal.isOffline(brl)) return BRL_CMD_OFFLINE; |
| return EOF; |
| |
| failure: |
| return BRL_CMD_RESTARTBRL; |
| } |
| |
| static int |
| brl_writeWindow (BrailleDisplay *brl, const wchar_t *characters) { |
| const size_t size = brl->textColumns * brl->textRows; |
| |
| if (brl->data->isForwarding) return 1; |
| |
| if (cellsHaveChanged(brl->data->braille.cells, brl->buffer, size, NULL, NULL, &brl->data->braille.refresh)) { |
| size_t size = writeWindow(brl, brl->buffer); |
| |
| if (!size) return 0; |
| } |
| |
| return 1; |
| } |
| |
| static ssize_t askDevice(BrailleDisplay *brl, IrisOutputPacketType request, unsigned char *response, size_t size) |
| { |
| { |
| const unsigned char data[] = {request}; |
| if (! writeNativePacket(brl, &brl->data->internal.port, data, sizeof(data)) ) return 0; |
| drainBrailleOutput(brl, 0); |
| } |
| |
| while (gioAwaitInput(brl->data->internal.port.gioEndpoint, 1000)) { |
| size_t res = readNativePacket(brl, &brl->data->internal.port, response, size); |
| if (res) return res; |
| if (errno != EAGAIN) break; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| suspendDevice (BrailleDisplay *brl) { |
| if (!brl->data->isEmbedded) return 1; |
| logMessage(LOG_CATEGORY(BRAILLE_DRIVER), "suspending device"); |
| brl->data->isSuspended = 1; |
| |
| if (brl->data->isForwarding) { |
| if (!sendMenuKey(brl, &brl->data->external.port)) return 0; |
| } |
| |
| if (!clearWindow(brl)) return 0; |
| drainBrailleOutput(brl, 50); |
| deactivateBraille(); |
| setBrailleOffline(brl); |
| return 1; |
| } |
| |
| static int |
| resumeDevice (BrailleDisplay *brl) { |
| if (!brl->data->isEmbedded) return 1; |
| logMessage(LOG_CATEGORY(BRAILLE_DRIVER), "resuming device"); |
| activateBraille(); |
| |
| if (brl->data->isForwarding) { |
| if (!sendMenuKey(brl, &brl->data->external.port)) return 0; |
| } else { |
| brl->data->braille.refresh = 1; |
| setBrailleOnline(brl); |
| } |
| |
| brl->data->isSuspended = 0; |
| return 1; |
| } |
| |
| static void |
| closePort (Port *port) { |
| if (port->gioEndpoint) { |
| gioDisconnectResource(port->gioEndpoint); |
| port->gioEndpoint = NULL; |
| } |
| } |
| |
| static int |
| openPort (Port *port) { |
| static const SerialParameters serialParameters = { |
| SERIAL_DEFAULT_PARAMETERS, |
| .parity = SERIAL_PARITY_EVEN |
| }; |
| |
| GioDescriptor gioDescriptor; |
| gioInitializeDescriptor(&gioDescriptor); |
| |
| port->serialParameters = serialParameters; |
| port->serialParameters.baud = port->speed; |
| gioDescriptor.serial.parameters = &port->serialParameters; |
| |
| closePort(port); |
| |
| if ((port->gioEndpoint = gioConnectResource(port->name, &gioDescriptor))) { |
| port->state = 0; |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| openInternalPort (BrailleDisplay *brl) { |
| Port *port = &brl->data->internal.port; |
| |
| if (openPort(port)) { |
| brl->gioEndpoint = port->gioEndpoint; |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| closeInternalPort (BrailleDisplay *brl) { |
| brl->gioEndpoint = NULL; |
| closePort(&brl->data->internal.port); |
| } |
| |
| static void |
| stopExternalInputHandler (BrailleDisplay *brl) { |
| if (brl->data->external.hio) { |
| gioDestroyHandleInputObject(brl->data->external.hio); |
| brl->data->external.hio = NULL; |
| } |
| } |
| |
| static int |
| openExternalPort (BrailleDisplay *brl) { |
| stopExternalInputHandler(brl); |
| |
| if (openPort(&brl->data->external.port)) { |
| brl->data->external.hio = |
| gioNewHandleInputObject(brl->data->external.port.gioEndpoint, |
| BRAILLE_DRIVER_INPUT_POLL_INTERVAL, |
| irHandleExternalInput, brl); |
| |
| if (brl->data->external.hio) return 1; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| closeExternalPort (BrailleDisplay *brl) { |
| stopExternalInputHandler(brl); |
| closePort(&brl->data->external.port); |
| } |
| |
| static int |
| checkLatchState (BrailleDisplay *brl) { |
| unsigned char pulled = !(readPort1(IR_PORT_INPUT) & 0X04); |
| |
| if (brl->data->latch.pulled) { |
| if (pulled) { |
| long int elapsed = getMonotonicElapsed(&brl->data->latch.started); |
| int result = (brl->data->latch.elapsed <= brl->data->latch.delay) && |
| (elapsed > brl->data->latch.delay); |
| |
| brl->data->latch.elapsed = elapsed; |
| return result; |
| } |
| |
| brl->data->latch.pulled = 0; |
| logMessage(LOG_INFO, "latch released"); |
| } else if (pulled) { |
| getMonotonicTime(&brl->data->latch.started); |
| brl->data->latch.elapsed = 0; |
| brl->data->latch.pulled = 1; |
| logMessage(LOG_INFO, "latch pulled"); |
| } |
| |
| return 0; |
| } |
| |
| ASYNC_ALARM_CALLBACK(irMonitorLatch) { |
| BrailleDisplay *brl = parameters->data; |
| |
| if (checkLatchState(brl)) { |
| if (!(brl->data->isSuspended? resumeDevice(brl): suspendDevice(brl))) brl->hasFailed = 1; |
| } |
| } |
| |
| static int |
| startLatchMonitor (BrailleDisplay *brl) { |
| if (brl->data->latch.monitor) return 1; |
| if (!brl->data->latch.delay) return 1; |
| |
| if (asyncNewRelativeAlarm(&brl->data->latch.monitor, 0, irMonitorLatch, brl)) { |
| if (asyncResetAlarmInterval(brl->data->latch.monitor, brl->data->latch.interval)) { |
| brl->data->latch.pulled = 0; |
| return 1; |
| } |
| |
| asyncCancelRequest(brl->data->latch.monitor); |
| brl->data->latch.monitor = NULL; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| stopLatchMonitor (BrailleDisplay *brl) { |
| if (brl->data->latch.monitor) { |
| asyncCancelRequest(brl->data->latch.monitor); |
| brl->data->latch.monitor = NULL; |
| } |
| } |
| |
| static int |
| brl_construct (BrailleDisplay *brl, char **parameters, const char *device) { |
| if ((brl->data = malloc(sizeof(*brl->data)))) { |
| unsigned int embedded; |
| |
| memset(brl->data, 0, sizeof(*brl->data)); |
| |
| brl->data->isConnected = 1; |
| |
| brl->data->isSuspended = 0; |
| brl->data->isForwarding = 0; |
| |
| brl->data->haveVisualDisplay = 0; |
| |
| brl->data->internal.port.gioEndpoint = NULL; |
| brl->data->internal.port.writeNativePacket = writeNativePacket_internal; |
| brl->data->internal.port.handleNativeAcknowledgement = handleNativeAcknowledgement_internal; |
| brl->data->internal.linearKeys = 0; |
| |
| brl->data->external.port.gioEndpoint = NULL; |
| brl->data->external.port.writeNativePacket = writeNativePacket_external; |
| brl->data->external.port.handleNativeAcknowledgement = handleNativeAcknowledgement_external; |
| brl->data->external.hio = NULL; |
| memset(brl->data->external.cells, 0, sizeof(brl->data->external.cells)); |
| |
| brl->data->latch.monitor = NULL; |
| brl->data->latch.delay = IR_DEFAULT_LATCH_DELAY; |
| brl->data->latch.interval = IR_DEFAULT_LATCH_INTERVAL; |
| |
| brl->data->braille.refresh = 1; |
| |
| if (validateYesNo(&embedded, parameters[PARM_EMBEDDED])) { |
| int internalPortOpened = 0; |
| |
| brl->data->isEmbedded = !!embedded; |
| logMessage(LOG_INFO, "Driver Mode: %s", |
| (brl->data->isEmbedded? "embedded": "non-embedded")); |
| |
| if (brl->data->isEmbedded) { |
| { |
| const char *parameter = parameters[PARM_PROTOCOL]; |
| const char *choices[protocolCount + 1]; |
| unsigned int choice; |
| |
| for (choice=0; choice<protocolCount; choice+=1) { |
| choices[choice] = protocolTable[choice].protocolName; |
| } |
| choices[protocolCount] = NULL; |
| |
| if (!validateChoice(&choice, parameter, choices)) { |
| choice = IR_PROTOCOL_DEFAULT; |
| logMessage(LOG_WARNING, "invalid protocol setting: %s", parameter); |
| } |
| |
| setExternalProtocol(brl, choice); |
| logMessage(LOG_INFO, "External Protocol: %s", brl->data->external.protocol->protocolName); |
| } |
| |
| { |
| const char *parameter = parameters[PARM_LATCH_DELAY]; |
| |
| if (*parameter) { |
| static const int minimum = 0; |
| static const int maximum = 100; |
| int value; |
| |
| if (validateInteger(&value, parameter, &minimum, &maximum)) { |
| brl->data->latch.delay = value * 100; |
| } else { |
| logMessage(LOG_WARNING, "invalid latch delay setting: %s", parameter); |
| } |
| } |
| } |
| |
| if (startLatchMonitor(brl)) { |
| if (enablePorts(LOG_ERR, IR_PORT_BASE, 3) != -1) { |
| brl->data->external.port.name = device; |
| brl->data->external.port.speed = brl->data->external.protocol->externalSpeed; |
| |
| if (openExternalPort(brl)) { |
| brl->data->internal.port.name = "serial:ttyS1"; |
| brl->data->internal.port.speed = IR_INTERNAL_SPEED; |
| |
| if (openInternalPort(brl)) { |
| brl->data->internal.handlePacket = handleInternalPacket_embedded; |
| brl->data->internal.isOffline = isOffline_embedded; |
| |
| activateBraille(); |
| internalPortOpened = 1; |
| } |
| } |
| } else { |
| logSystemError("ioperm"); |
| } |
| } |
| } else { |
| brl->data->internal.port.name = device; |
| brl->data->internal.port.speed = IR_EXTERNAL_SPEED_NATIVE; |
| |
| if (openInternalPort(brl)) { |
| brl->data->internal.handlePacket = handleInternalPacket_nonembedded; |
| brl->data->internal.isOffline = isOffline_nonembedded; |
| |
| brl->data->isConnected = 1; |
| internalPortOpened = 1; |
| } |
| } |
| |
| if (internalPortOpened) { |
| unsigned char deviceResponse[IR_MAXIMUM_PACKET_SIZE]; |
| ssize_t size; |
| |
| if (!(size = askDevice(brl, IR_OPT_VersionRequest, deviceResponse, sizeof(deviceResponse)) )) { |
| logMessage(LOG_WARNING, "received no response to version request"); |
| } else if (size < 3) { |
| logBytes(LOG_WARNING, "short firmware version response", deviceResponse, size); |
| } else if (deviceResponse[0] != IR_IPT_VersionResponse) { |
| logBytes(LOG_WARNING, "unexpected firmware version response", deviceResponse, size); |
| } else { |
| const KeyTableDefinition *ktd; |
| |
| switch (deviceResponse[1]) { |
| case 'a': |
| case 'A': |
| ktd = &KEY_TABLE_DEFINITION(pc); |
| brl->textColumns = IR_WINDOW_SIZE_MAXIMUM; |
| break; |
| |
| case 'l': |
| case 'L': |
| ktd = &KEY_TABLE_DEFINITION(brl); |
| brl->textColumns = IR_WINDOW_SIZE_MAXIMUM; |
| brl->data->haveVisualDisplay = 1; |
| break; |
| |
| case 's': |
| case 'S': |
| ktd = &KEY_TABLE_DEFINITION(brl); |
| brl->textColumns = IR_WINDOW_SIZE_SMALL; |
| break; |
| |
| default: |
| logBytes(LOG_WARNING, "unrecognized device type in firmware version response", deviceResponse, size); |
| ktd = NULL; |
| break; |
| } |
| |
| if (ktd) { |
| setBrailleKeyTable(brl, ktd); |
| |
| if ((brl->data->firmwareVersion = malloc(size - 1))) { |
| memcpy(brl->data->firmwareVersion, deviceResponse+2, size-2); |
| brl->data->firmwareVersion[size-2] = 0; |
| logMessage(LOG_INFO, "Firmware Version: %s", brl->data->firmwareVersion); |
| |
| if (!(size = askDevice(brl, IR_OPT_SerialNumberRequest, deviceResponse, sizeof(deviceResponse)))) { |
| logMessage(LOG_WARNING, "Received no response to serial number request."); |
| } else if (size != IR_OPT_SERIALNUMBERRESPONSE_LENGTH) { |
| logBytes(LOG_WARNING, "short serial number response", deviceResponse, size); |
| } else if (deviceResponse[0] != IR_IPT_SerialNumberResponse) { |
| logBytes(LOG_WARNING, "unexpected serial number response", deviceResponse, size); |
| } else { |
| if (deviceResponse[1] != IR_OPT_SERIALNUMBERRESPONSE_NOWINDOWLENGTH) { |
| brl->textColumns = deviceResponse[1]; |
| } |
| |
| { |
| char *byte = brl->data->serialNumber; |
| |
| byte = mempcpy(byte, deviceResponse+2, |
| (sizeof(brl->data->serialNumber) - 1)); |
| *byte = 0; |
| logMessage(LOG_INFO, "Serial Number: %s", brl->data->serialNumber); |
| } |
| |
| logMessage(LOG_INFO, "Display Size: %u", brl->textColumns); |
| logMessage(LOG_INFO, "Visual Display: %s", |
| (brl->data->haveVisualDisplay? "yes": "no")); |
| |
| makeOutputTable(dotsTable_ISO11548_1); |
| return 1; |
| } |
| |
| free(brl->data->firmwareVersion); |
| } else { |
| logMallocError(); |
| } |
| } |
| } |
| } |
| } else { |
| logMessage(LOG_WARNING, "invalid embedded setting: %s", parameters[PARM_EMBEDDED]); |
| } |
| |
| stopLatchMonitor(brl); |
| closeExternalPort(brl); |
| closeInternalPort(brl); |
| free(brl->data); |
| } else { |
| logMallocError(); |
| } |
| |
| return 0; |
| } |
| |
| static void |
| brl_destruct (BrailleDisplay *brl) { |
| if (brl->data->isEmbedded) { |
| clearWindow(brl); |
| drainBrailleOutput(brl, 50); |
| deactivateBraille(); |
| } |
| |
| if (brl->data) { |
| stopLatchMonitor(brl); |
| closeExternalPort(brl); |
| closeInternalPort(brl); |
| free(brl->data->firmwareVersion); |
| |
| free(brl->data); |
| brl->data = NULL; |
| } |
| } |