/* * BRLTTY - A background process providing access to the console screen (when in * text mode) for a blind person using a refreshable braille display. * * Copyright (C) 1995-2021 by The BRLTTY Developers. * * BRLTTY comes with ABSOLUTELY NO WARRANTY. * * This is free software, placed under the terms of the * GNU Lesser General Public License, as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any * later version. Please see the file LICENSE-LGPL for details. * * Web Page: http://brltty.app/ * * This software is maintained by Dave Mielke . */ #include "prologue.h" #include #include #include #include #include #include "log.h" #include "parameters.h" #include "ascii.h" #include "cmd.h" #include "parse.h" #include "async_wait.h" #include "async_alarm.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 DLE: if (!port->escape) { port->escape = 1; continue; } case 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 == SOH) { port->state = 1; port->escape = 0; port->position = port->packet; } else if (byte == 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 == 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 == 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 == 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] = { [SOH] = 1, [EOT] = 1, [DLE] = 1, [ACK] = 1, [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[] = {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++ = SOH; while (size--) { if (needsEscape(*source)) *target++ = DLE; *target++ = *source++; } *target++ = 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++ = STX; *p++ = (packetSize >> 8) & 0X00FF; *p++ = packetSize & 0X00FF; p = mempcpy(p, data, size); *p++ = 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; itextColumns * 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; choicedata->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; } }