blob: d7fb45708e20ead09647f420d1bdc33860f9ea73 [file] [log] [blame]
/*
* 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>.
*/
/* Alva/braille.cc - Braille display library for Alva braille displays
* Copyright (C) 1995-2002 by Nicolas Pitre <nico@fluxnic.net>
* See the GNU Lesser General Public License for details in the LICENSE-LGPL file
*
*/
/* Changes:
* january 2004:
* - Added USB support.
* - Improved key bindings for Satellite models.
* - Moved autorepeat (typematic) support to the core.
* september 2002:
* - This pesky binary only parallel port library is just
* causing trouble (not compatible with new compilers, etc).
* It is also unclear if distribution of such closed source
* library is allowed within a GPL'ed program archive.
* Let's just nuke it until we can write an open source one.
* - Converted this file back to pure C source.
* may 21, 1999:
* - Added Alva Delphi 80 support. Thanks to ???
* <cstrobel@crosslink.net>.
* mar 14, 1999:
* - Added LogPrint's (which is a good thing...)
* - Ugly ugly hack for parallel port support: seems there
* is a bug in the parallel port library so that the display
* completely hang after an arbitrary period of time.
* J. Lemmens didn't respond to my query yet... and since
* the F***ing library isn't Open Source, I can't fix it.
* feb 05, 1999:
* - Added Alva Delphi support (thanks to Terry Barnaby
* <terry@beam.demon.co.uk>).
* - Renamed Alva_ABT3 to Alva.
* - Some improvements to the autodetection stuff.
* dec 06, 1998:
* - added parallel port communication support using
* J. lemmens <jlemmens@inter.nl.net> 's library.
* This required brl.o to be sourced with C++ for the parallel
* stuff to link. Now brl.o is a partial link of brlmain.o
* and the above library.
* jun 21, 1998:
* - replaced CMD_WINUP/DN with CMD_ATTRUP/DN wich seems
* to be a more useful binding. Modified help files
* acordingly.
* apr 23, 1998:
* - I finally had the chance to test with an ABT380... and
* corrected the ABT380 model ID for autodetection.
* - Added a refresh delay to force redrawing the whole display
* in order to minimize garbage due to noise on the
* serial line
* oct 02, 1996:
* - bound CMD_SAY_LINE and CMD_MUTE
* sep 22, 1996:
* - bound CMD_PRDIFLN and CMD_NXDIFLN.
* aug 15, 1996:
* - adeded automatic model detection for new firmware.
* - support for selectable help screen.
* feb 19, 1996:
* - added small hack for automatic rewrite of display when
* the terminal is turned off and back on, replugged, etc.
* feb 15, 1996:
* - Modified writebrl() for lower bandwith
* - Joined the forced ReWrite function to the CURSOR key
* jan 31, 1996:
* - moved user configurable parameters into brlconf.h
* - added identbrl()
* - added overide parameter for serial device
* - added keybindings for BRLTTY preferences menu
* jan 23, 1996:
* - modifications to be compatible with the BRLTTY braille
* mapping standard.
* dec 27, 1995:
* - Added conditions to support all ABT3xx series
* - changed directory Alva_ABT40 to Alva_ABT3
* dec 02, 1995:
* - made changes to support latest Alva ABT3 firmware (new
* serial protocol).
* nov 05, 1995:
* - added typematic facility
* - added key bindings for Stephane Doyon's cut'n paste.
* - added cursor routing key block marking
* - fixed a bug in readbrl() about released keys
* sep 30' 1995:
* - initial Alva driver code, inspired from the
* (old) BrailleLite code.
*/
#include "prologue.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "log.h"
#include "strfmt.h"
#include "parse.h"
#include "bitfield.h"
#include "timing.h"
#include "ascii.h"
#include "hidkeys.h"
#include "io_generic.h"
#include "io_usb.h"
#include "usb_hid.h"
typedef enum {
PARM_ROTATED_CELLS,
PARM_SECONDARY_ROUTING_KEY_EMULATION
} DriverParameter;
#define BRLPARMS "rotatedcells", "secondaryroutingkeyemulation"
#define BRL_STATUS_FIELDS sfAlphabeticCursorCoordinates, sfAlphabeticWindowCoordinates, sfStateLetter
#define BRL_HAVE_STATUS_CELLS
#include "brl_driver.h"
#include "brldefs-al.h"
#include "braille.h"
BEGIN_KEY_NAME_TABLE(routing1)
KEY_GROUP_ENTRY(AL_GRP_RoutingKeys1, "RoutingKey1"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(routing2)
KEY_GROUP_ENTRY(AL_GRP_RoutingKeys2, "RoutingKey2"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(status1)
KEY_NAME_ENTRY(AL_KEY_STATUS1+0, "Status1A"),
KEY_NAME_ENTRY(AL_KEY_STATUS1+1, "Status1B"),
KEY_NAME_ENTRY(AL_KEY_STATUS1+2, "Status1C"),
KEY_NAME_ENTRY(AL_KEY_STATUS1+3, "Status1D"),
KEY_NAME_ENTRY(AL_KEY_STATUS1+4, "Status1E"),
KEY_NAME_ENTRY(AL_KEY_STATUS1+5, "Status1F"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(status2)
KEY_NAME_ENTRY(AL_KEY_STATUS2+0, "Status2A"),
KEY_NAME_ENTRY(AL_KEY_STATUS2+1, "Status2B"),
KEY_NAME_ENTRY(AL_KEY_STATUS2+2, "Status2C"),
KEY_NAME_ENTRY(AL_KEY_STATUS2+3, "Status2D"),
KEY_NAME_ENTRY(AL_KEY_STATUS2+4, "Status2E"),
KEY_NAME_ENTRY(AL_KEY_STATUS2+5, "Status2F"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(abt_basic)
KEY_NAME_ENTRY(AL_KEY_Prog, "Prog"),
KEY_NAME_ENTRY(AL_KEY_Home, "Home"),
KEY_NAME_ENTRY(AL_KEY_Cursor, "Cursor"),
KEY_NAME_ENTRY(AL_KEY_Up, "Up"),
KEY_NAME_ENTRY(AL_KEY_Left, "Left"),
KEY_NAME_ENTRY(AL_KEY_Right, "Right"),
KEY_NAME_ENTRY(AL_KEY_Down, "Down"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(abt_extra)
KEY_NAME_ENTRY(AL_KEY_Cursor2, "Cursor2"),
KEY_NAME_ENTRY(AL_KEY_Home2, "Home2"),
KEY_NAME_ENTRY(AL_KEY_Prog2, "Prog2"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(sat_basic)
KEY_NAME_ENTRY(AL_KEY_Home, "Home"),
KEY_NAME_ENTRY(AL_KEY_Cursor, "Cursor"),
KEY_NAME_ENTRY(AL_KEY_Up, "Up"),
KEY_NAME_ENTRY(AL_KEY_Left, "Left"),
KEY_NAME_ENTRY(AL_KEY_Right, "Right"),
KEY_NAME_ENTRY(AL_KEY_Down, "Down"),
KEY_NAME_ENTRY(AL_KEY_SpeechPadF1, "SpeechPadF1"),
KEY_NAME_ENTRY(AL_KEY_SpeechPadUp, "SpeechPadUp"),
KEY_NAME_ENTRY(AL_KEY_SpeechPadLeft, "SpeechPadLeft"),
KEY_NAME_ENTRY(AL_KEY_SpeechPadDown, "SpeechPadDown"),
KEY_NAME_ENTRY(AL_KEY_SpeechPadRight, "SpeechPadRight"),
KEY_NAME_ENTRY(AL_KEY_SpeechPadF2, "SpeechPadF2"),
KEY_NAME_ENTRY(AL_KEY_NavPadF1, "NavPadF1"),
KEY_NAME_ENTRY(AL_KEY_NavPadUp, "NavPadUp"),
KEY_NAME_ENTRY(AL_KEY_NavPadLeft, "NavPadLeft"),
KEY_NAME_ENTRY(AL_KEY_NavPadDown, "NavPadDown"),
KEY_NAME_ENTRY(AL_KEY_NavPadRight, "NavPadRight"),
KEY_NAME_ENTRY(AL_KEY_NavPadF2, "NavPadF2"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(sat_extra)
KEY_NAME_ENTRY(AL_KEY_LeftTumblerLeft, "LeftTumblerLeft"),
KEY_NAME_ENTRY(AL_KEY_LeftTumblerRight, "LeftTumblerRight"),
KEY_NAME_ENTRY(AL_KEY_RightTumblerLeft, "RightTumblerLeft"),
KEY_NAME_ENTRY(AL_KEY_RightTumblerRight, "RightTumblerRight"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(etouch)
KEY_NAME_ENTRY(AL_KEY_ETouchLeftRear, "ETouchLeftRear"),
KEY_NAME_ENTRY(AL_KEY_ETouchLeftFront, "ETouchLeftFront"),
KEY_NAME_ENTRY(AL_KEY_ETouchRightRear, "ETouchRightRear"),
KEY_NAME_ENTRY(AL_KEY_ETouchRightFront, "ETouchRightFront"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(smartpad)
KEY_NAME_ENTRY(AL_KEY_SmartpadF1, "SmartpadF1"),
KEY_NAME_ENTRY(AL_KEY_SmartpadF2, "SmartpadF2"),
KEY_NAME_ENTRY(AL_KEY_SmartpadLeft, "SmartpadLeft"),
KEY_NAME_ENTRY(AL_KEY_SmartpadEnter, "SmartpadEnter"),
KEY_NAME_ENTRY(AL_KEY_SmartpadUp, "SmartpadUp"),
KEY_NAME_ENTRY(AL_KEY_SmartpadDown, "SmartpadDown"),
KEY_NAME_ENTRY(AL_KEY_SmartpadRight, "SmartpadRight"),
KEY_NAME_ENTRY(AL_KEY_SmartpadF3, "SmartpadF3"),
KEY_NAME_ENTRY(AL_KEY_SmartpadF4, "SmartpadF4"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(thumb)
KEY_NAME_ENTRY(AL_KEY_THUMB+0, "ThumbLeft"),
KEY_NAME_ENTRY(AL_KEY_THUMB+1, "ThumbUp"),
KEY_NAME_ENTRY(AL_KEY_THUMB+2, "ThumbHome"),
KEY_NAME_ENTRY(AL_KEY_THUMB+3, "ThumbDown"),
KEY_NAME_ENTRY(AL_KEY_THUMB+4, "ThumbRight"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(featurepack)
KEY_NAME_ENTRY(AL_KEY_Dot1, "Dot1"),
KEY_NAME_ENTRY(AL_KEY_Dot2, "Dot2"),
KEY_NAME_ENTRY(AL_KEY_Dot3, "Dot3"),
KEY_NAME_ENTRY(AL_KEY_Dot4, "Dot4"),
KEY_NAME_ENTRY(AL_KEY_Dot5, "Dot5"),
KEY_NAME_ENTRY(AL_KEY_Dot6, "Dot6"),
KEY_NAME_ENTRY(AL_KEY_Dot7, "Dot7"),
KEY_NAME_ENTRY(AL_KEY_Dot8, "Dot8"),
KEY_NAME_ENTRY(AL_KEY_Control, "Control"),
KEY_NAME_ENTRY(AL_KEY_Windows, "Windows"),
KEY_NAME_ENTRY(AL_KEY_Space, "Space"),
KEY_NAME_ENTRY(AL_KEY_Alt, "Alt"),
KEY_NAME_ENTRY(AL_KEY_Enter, "Enter"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(el)
KEY_NAME_ENTRY(AL_KEY_Dot1, "Dot1"),
KEY_NAME_ENTRY(AL_KEY_Dot2, "Dot2"),
KEY_NAME_ENTRY(AL_KEY_Dot3, "Dot3"),
KEY_NAME_ENTRY(AL_KEY_Dot4, "Dot4"),
KEY_NAME_ENTRY(AL_KEY_Dot5, "Dot5"),
KEY_NAME_ENTRY(AL_KEY_Dot6, "Dot6"),
KEY_NAME_ENTRY(AL_KEY_Dot7, "Shift"),
KEY_NAME_ENTRY(AL_KEY_Space, "Space"),
KEY_NAME_ENTRY(AL_KEY_Dot8, "Control"),
KEY_NAME_ENTRY(AL_KEY_SmartpadEnter, "JoystickEnter"),
KEY_NAME_ENTRY(AL_KEY_SmartpadLeft, "JoystickLeft"),
KEY_NAME_ENTRY(AL_KEY_SmartpadRight, "JoystickRight"),
KEY_NAME_ENTRY(AL_KEY_SmartpadUp, "JoystickUp"),
KEY_NAME_ENTRY(AL_KEY_SmartpadDown, "JoystickDown"),
KEY_NAME_ENTRY(AL_KEY_THUMB+0, "ScrollLeft"),
KEY_NAME_ENTRY(AL_KEY_THUMB+4, "ScrollRight"),
KEY_GROUP_ENTRY(AL_GRP_RoutingKeys1, "RoutingKey"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(voyager)
KEY_NAME_ENTRY(AL_KEY_THUMB+0, "Thumb1"),
KEY_NAME_ENTRY(AL_KEY_THUMB+1, "Thumb2"),
KEY_NAME_ENTRY(AL_KEY_THUMB+3, "Thumb3"),
KEY_NAME_ENTRY(AL_KEY_THUMB+4, "Thumb4"),
KEY_NAME_ENTRY(AL_KEY_SmartpadLeft, "Left"),
KEY_NAME_ENTRY(AL_KEY_SmartpadUp, "Up"),
KEY_NAME_ENTRY(AL_KEY_SmartpadDown, "Down"),
KEY_NAME_ENTRY(AL_KEY_SmartpadRight, "Right"),
KEY_NAME_ENTRY(AL_KEY_SmartpadF1, "Dot1"),
KEY_NAME_ENTRY(AL_KEY_SmartpadF2, "Dot2"),
KEY_NAME_ENTRY(AL_KEY_SmartpadF3, "Dot3"),
KEY_NAME_ENTRY(AL_KEY_SmartpadF4, "Dot4"),
KEY_NAME_ENTRY(AL_KEY_ETouchLeftRear, "Dot5"),
KEY_NAME_ENTRY(AL_KEY_ETouchLeftFront, "Dot6"),
KEY_NAME_ENTRY(AL_KEY_ETouchRightRear, "Dot7"),
KEY_NAME_ENTRY(AL_KEY_ETouchRightFront, "Dot8"),
KEY_GROUP_ENTRY(AL_GRP_RoutingKeys1, "RoutingKey"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLES(abt_small)
KEY_NAME_TABLE(abt_basic),
KEY_NAME_TABLE(status1),
KEY_NAME_TABLE(routing1),
END_KEY_NAME_TABLES
BEGIN_KEY_NAME_TABLES(abt_large)
KEY_NAME_TABLE(abt_basic),
KEY_NAME_TABLE(abt_extra),
KEY_NAME_TABLE(status1),
KEY_NAME_TABLE(routing1),
END_KEY_NAME_TABLES
BEGIN_KEY_NAME_TABLES(sat_small)
KEY_NAME_TABLE(sat_basic),
KEY_NAME_TABLE(status1),
KEY_NAME_TABLE(status2),
KEY_NAME_TABLE(routing1),
KEY_NAME_TABLE(routing2),
END_KEY_NAME_TABLES
BEGIN_KEY_NAME_TABLES(sat_large)
KEY_NAME_TABLE(sat_basic),
KEY_NAME_TABLE(sat_extra),
KEY_NAME_TABLE(status1),
KEY_NAME_TABLE(status2),
KEY_NAME_TABLE(routing1),
KEY_NAME_TABLE(routing2),
END_KEY_NAME_TABLES
BEGIN_KEY_NAME_TABLES(bc640)
KEY_NAME_TABLE(etouch),
KEY_NAME_TABLE(smartpad),
KEY_NAME_TABLE(thumb),
KEY_NAME_TABLE(featurepack),
KEY_NAME_TABLE(routing1),
KEY_NAME_TABLE(routing2),
END_KEY_NAME_TABLES
BEGIN_KEY_NAME_TABLES(bc680)
KEY_NAME_TABLE(etouch),
KEY_NAME_TABLE(smartpad),
KEY_NAME_TABLE(thumb),
KEY_NAME_TABLE(featurepack),
KEY_NAME_TABLE(routing1),
KEY_NAME_TABLE(routing2),
END_KEY_NAME_TABLES
BEGIN_KEY_NAME_TABLES(el)
KEY_NAME_TABLE(el),
END_KEY_NAME_TABLES
BEGIN_KEY_NAME_TABLES(voyager)
KEY_NAME_TABLE(voyager),
END_KEY_NAME_TABLES
DEFINE_KEY_TABLE(abt_small)
DEFINE_KEY_TABLE(abt_large)
DEFINE_KEY_TABLE(sat_small)
DEFINE_KEY_TABLE(sat_large)
DEFINE_KEY_TABLE(bc640)
DEFINE_KEY_TABLE(bc680)
DEFINE_KEY_TABLE(el)
DEFINE_KEY_TABLE(voyager)
BEGIN_KEY_TABLE_LIST
&KEY_TABLE_DEFINITION(abt_small),
&KEY_TABLE_DEFINITION(abt_large),
&KEY_TABLE_DEFINITION(sat_small),
&KEY_TABLE_DEFINITION(sat_large),
&KEY_TABLE_DEFINITION(bc640),
&KEY_TABLE_DEFINITION(bc680),
&KEY_TABLE_DEFINITION(el),
&KEY_TABLE_DEFINITION(voyager),
END_KEY_TABLE_LIST
struct BrailleDataStruct {
unsigned int rotatedCells;
struct {
unsigned char buffer[0X20];
unsigned char *end;
} restore;
union {
struct {
unsigned int secondaryRoutingKeyEmulation;
unsigned char splitOffset;
HidKeyboardPacket hidKeyboardPacket;
struct {
uint32_t hardware;
uint32_t firmware;
uint32_t btBase;
uint32_t btFP;
} version;
struct {
uint64_t base;
uint64_t featurePack;
} macAddress;
} bc;
} protocol;
};
typedef struct {
const char *name;
const KeyTableDefinition *keyTableDefinition;
unsigned char identifier;
unsigned char columns;
unsigned char statusCells;
unsigned char flags;
} ModelEntry;
static const ModelEntry *model; /* points to terminal model config struct */
#define MOD_FLAG_CAN_CONFIGURE 0X01
#define MOD_FLAG_FORCE_FROM_0 0X02
static const ModelEntry modelTable[] = {
{ .identifier = 0X00,
.name = "ABT 320",
.columns = 20,
.statusCells = 3,
.flags = 0,
.keyTableDefinition = &KEY_TABLE_DEFINITION(abt_small)
}
,
{ .identifier = 0X01,
.name = "ABT 340",
.columns = 40,
.statusCells = 3,
.flags = 0,
.keyTableDefinition = &KEY_TABLE_DEFINITION(abt_small)
}
,
{ .identifier = 0X02,
.name = "ABT 340 Desktop",
.columns = 40,
.statusCells = 5,
.flags = 0,
.keyTableDefinition = &KEY_TABLE_DEFINITION(abt_small)
}
,
{ .identifier = 0X03,
.name = "ABT 380",
.columns = 80,
.statusCells = 5,
.flags = 0,
.keyTableDefinition = &KEY_TABLE_DEFINITION(abt_large)
}
,
{ .identifier = 0X04,
.name = "ABT 382 Twin Space",
.columns = 80,
.statusCells = 5,
.flags = 0,
.keyTableDefinition = &KEY_TABLE_DEFINITION(abt_large)
}
,
{ .identifier = 0X0A,
.name = "Delphi 420",
.columns = 20,
.statusCells = 3,
.flags = 0,
.keyTableDefinition = &KEY_TABLE_DEFINITION(abt_small)
}
,
{ .identifier = 0X0B,
.name = "Delphi 440",
.columns = 40,
.statusCells = 3,
.flags = 0,
.keyTableDefinition = &KEY_TABLE_DEFINITION(abt_small)
}
,
{ .identifier = 0X0C,
.name = "Delphi 440 Desktop",
.columns = 40,
.statusCells = 5,
.flags = 0,
.keyTableDefinition = &KEY_TABLE_DEFINITION(abt_small)
}
,
{ .identifier = 0X0D,
.name = "Delphi 480",
.columns = 80,
.statusCells = 5,
.flags = 0,
.keyTableDefinition = &KEY_TABLE_DEFINITION(abt_large)
}
,
{ .identifier = 0X0E,
.name = "Satellite 544",
.columns = 40,
.statusCells = 3,
.flags = MOD_FLAG_CAN_CONFIGURE,
.keyTableDefinition = &KEY_TABLE_DEFINITION(sat_small)
}
,
{ .identifier = 0X0F,
.name = "Satellite 570 Pro",
.columns = 66,
.statusCells = 3,
.flags = MOD_FLAG_CAN_CONFIGURE,
.keyTableDefinition = &KEY_TABLE_DEFINITION(sat_large)
}
,
{ .identifier = 0X10,
.name = "Satellite 584 Pro",
.columns = 80,
.statusCells = 3,
.flags = MOD_FLAG_CAN_CONFIGURE,
.keyTableDefinition = &KEY_TABLE_DEFINITION(sat_large)
}
,
{ .identifier = 0X11,
.name = "Satellite 544 Traveller",
.columns = 40,
.statusCells = 3,
.flags = MOD_FLAG_CAN_CONFIGURE,
.keyTableDefinition = &KEY_TABLE_DEFINITION(sat_small)
}
,
{ .identifier = 0X13,
.name = "Braille System 40",
.columns = 40,
.statusCells = 0,
.flags = MOD_FLAG_CAN_CONFIGURE,
.keyTableDefinition = &KEY_TABLE_DEFINITION(sat_small)
}
,
{ .name = NULL }
};
static const ModelEntry modelBC624 = {
.identifier = 0X24,
.name = "BC624",
.columns = 24,
.keyTableDefinition = &KEY_TABLE_DEFINITION(bc640)
};
static const ModelEntry modelBC640 = {
.identifier = 0X40,
.name = "BC640",
.columns = 40,
.keyTableDefinition = &KEY_TABLE_DEFINITION(bc640)
};
static const ModelEntry modelBC680 = {
.identifier = 0X80,
.name = "BC680",
.columns = 80,
.keyTableDefinition = &KEY_TABLE_DEFINITION(bc680)
};
static const ModelEntry modelEL12 = {
.identifier = 0X40,
.name = "EasyLink 12 Touch",
.columns = 12,
.flags = MOD_FLAG_FORCE_FROM_0,
.keyTableDefinition = &KEY_TABLE_DEFINITION(el)
};
static const ModelEntry modelVoyager = {
.identifier = 0X00,
.name = "Voyager Protocol Converter",
.columns = 70,
.keyTableDefinition = &KEY_TABLE_DEFINITION(voyager)
};
typedef struct {
int (*test) (BrailleDisplay *brl);
unsigned char feature;
unsigned char offset;
unsigned char disable;
unsigned char enable;
} SettingsUpdateEntry;
typedef struct {
void (*initializeVariables) (BrailleDisplay *brl, char **parameters);
BraillePacketVerifier *verifyPacket;
int (*readPacket) (BrailleDisplay *brl, unsigned char *packet, int size);
const SettingsUpdateEntry *requiredSettings;
int (*setFeature) (BrailleDisplay *brl, const unsigned char *data, size_t size);
size_t (*getFeature) (BrailleDisplay *brl, unsigned char feature, unsigned char *buffer, size_t size);
int (*updateConfiguration) (BrailleDisplay *brl, int autodetecting, const unsigned char *packet);
int (*detectModel) (BrailleDisplay *brl);
int (*readCommand) (BrailleDisplay *brl);
int (*writeBraille) (BrailleDisplay *brl, const unsigned char *cells, int start, int count);
} ProtocolOperations;
static const ProtocolOperations *protocol;
typedef enum {
STATUS_FIRST,
STATUS_LEFT,
STATUS_RIGHT
} StatusType;
static unsigned char *previousText = NULL;
static unsigned char *previousStatus = NULL;
static unsigned char actualColumns;
static unsigned char textOffset;
static unsigned char statusOffset;
static unsigned char textRewriteRequired = 0;
static unsigned char statusRewriteRequired;
typedef unsigned char FieldByteConverter (unsigned char byte);
static uint64_t
parseNumericField (
const unsigned char **bytes, size_t *count,
size_t size, size_t width,
FieldByteConverter *convertByte
) {
uint64_t result = 0;
while (width > 0) {
result <<= 8;
if (size > 0) {
if (*count > 0) {
result |= convertByte(*(*bytes)++);
*count -= 1;
}
size -= 1;
}
width -= 1;
}
return result;
}
static unsigned char
convertHexadecimalByte (unsigned char byte) {
return byte;
}
static uint64_t
parseHexadecimalField (
const unsigned char **bytes, size_t *count,
size_t size, size_t width
) {
return parseNumericField(bytes, count, size, width, convertHexadecimalByte);
}
static unsigned char
convertDecimalByte (unsigned char byte) {
return byte - '0';
}
static uint64_t
parseDecimalField (
const unsigned char **bytes, size_t *count,
size_t size, size_t width
) {
return parseNumericField(bytes, count, size, width, convertDecimalByte);
}
static int
readPacket (BrailleDisplay *brl, unsigned char *packet, int size) {
return readBraillePacket(brl, NULL, packet, size, protocol->verifyPacket, NULL);
}
static int
flushSettingsUpdate (
BrailleDisplay *brl, size_t length,
const unsigned char *old, const unsigned char *new
) {
if (length) {
if (memcmp(old, new, length) != 0) {
if (!protocol->setFeature(brl, new, length)) return 0;
{
unsigned char **const end = &brl->data->restore.end;
if (length > UINT8_MAX) {
logBytes(LOG_WARNING, "settings update too long", new, length);
} else if ((*end + length + 1) > (brl->data->restore.buffer + sizeof(brl->data->restore.buffer))) {
logBytes(LOG_WARNING, "settings update not saved", new, length);
} else {
*end = mempcpy(*end, old, length);
*(*end)++ = length;
}
}
}
}
return 1;
}
static int
updateSettings (BrailleDisplay *brl) {
size_t length = 0;
const size_t size = 0X20;
unsigned char old[size];
unsigned char new[size];
const SettingsUpdateEntry *settings = protocol->requiredSettings;
if (settings) {
unsigned char previous = 0;
while (settings->feature) {
if (!settings->test || settings->test(brl)) {
if (settings->feature != previous) {
if (!flushSettingsUpdate(brl, length, old, new)) return 0;
if (!(length = protocol->getFeature(brl, settings->feature, old, size))) {
if (errno == EAGAIN) goto next;
#ifdef ETIMEDOUT
if (errno == ETIMEDOUT) goto next;
#endif /* ETIMEDOUT */
return 0;
}
memcpy(new, old, length);
previous = settings->feature;
}
{
unsigned char *byte = &new[settings->offset];
*byte &= ~settings->disable;
*byte |= settings->enable;
}
}
next:
settings += 1;
}
}
return flushSettingsUpdate(brl, length, old, new);
}
static int
restoreSettings (BrailleDisplay *brl) {
const unsigned char *request = brl->data->restore.end;
while (request > brl->data->restore.buffer) {
unsigned char length = *--request;
request -= length;
if (!protocol->setFeature(brl, request, length)) return 0;
}
return 1;
}
static int
reallocateBuffer (unsigned char **buffer, int size) {
void *address = realloc(*buffer, size);
if (size && !address) return 0;
*buffer = address;
return 1;
}
static int
reallocateBuffers (BrailleDisplay *brl) {
if (reallocateBuffer(&previousText, brl->textColumns*brl->textRows))
if (reallocateBuffer(&previousStatus, brl->statusColumns*brl->statusRows))
return 1;
logMessage(LOG_ERR, "cannot allocate braille buffers");
return 0;
}
static int
setDefaultConfiguration (BrailleDisplay *brl) {
logMessage(LOG_INFO, "detected Alva %s: %d columns, %d status cells",
model->name, model->columns, model->statusCells);
brl->textColumns = model->columns;
brl->textRows = 1;
brl->statusColumns = model->statusCells;
brl->statusRows = 1;
actualColumns = model->columns;
statusOffset = 0;
textOffset = statusOffset + model->statusCells;
textRewriteRequired = 1; /* To write whole display at first time */
statusRewriteRequired = 1;
return reallocateBuffers(brl);
}
static int
updateConfiguration (BrailleDisplay *brl, int autodetecting, int textColumns, int statusColumns, StatusType statusType) {
int changed = 0;
int separator = 0;
actualColumns = textColumns;
if (statusType == STATUS_FIRST) {
statusOffset = 0;
textOffset = statusOffset + statusColumns;
} else if ((statusColumns = MIN(statusColumns, (actualColumns-1)/2))) {
separator = 1;
textColumns -= statusColumns + separator;
switch (statusType) {
case STATUS_LEFT:
statusOffset = 0;
textOffset = statusOffset + statusColumns + separator;
break;
case STATUS_RIGHT:
textOffset = 0;
statusOffset = textOffset + textColumns + separator;
break;
default:
break;
}
} else {
statusOffset = 0;
textOffset = 0;
}
if (statusColumns != brl->statusColumns) {
logMessage(LOG_INFO, "status cell count changed to %d", statusColumns);
brl->statusColumns = statusColumns;
changed = 1;
}
if (textColumns != brl->textColumns) {
logMessage(LOG_INFO, "text column count changed to %d", textColumns);
brl->textColumns = textColumns;
if (!autodetecting) brl->resizeRequired = 1;
changed = 1;
}
if (changed)
if (!reallocateBuffers(brl))
return 0;
if (separator) {
unsigned char cell = 0;
if (!protocol->writeBraille(brl, &cell, MAX(textOffset, statusOffset)-1, 1)) return 0;
}
textRewriteRequired = 1;
statusRewriteRequired = 1;
return 1;
}
#define PACKET_SIZE(count) (((count) * 2) + 4)
#define MAXIMUM_PACKET_SIZE PACKET_SIZE(0XFF)
#define PACKET_BYTE(packet, index) ((packet)[PACKET_SIZE((index)) - 1])
static const unsigned char BRL_ID[] = {ASCII_ESC, 'I', 'D', '='};
#define BRL_ID_LENGTH (sizeof(BRL_ID))
#define BRL_ID_SIZE (BRL_ID_LENGTH + 1)
static int
writeFunction1 (BrailleDisplay *brl, unsigned char code) {
unsigned char bytes[] = {ASCII_ESC, 'F', 'U', 'N', code, ASCII_CR};
return writeBraillePacket(brl, NULL, bytes, sizeof(bytes));
}
static int
writeParameter1 (BrailleDisplay *brl, unsigned char parameter, unsigned char setting) {
unsigned char bytes[] = {ASCII_ESC, 'P', 'A', 3, 0, parameter, setting, ASCII_CR};
return writeBraillePacket(brl, NULL, bytes, sizeof(bytes));
}
static int
updateConfiguration1 (BrailleDisplay *brl, int autodetecting, const unsigned char *packet) {
int textColumns = brl->textColumns;
int statusColumns = brl->statusColumns;
int count = PACKET_BYTE(packet, 0);
if (count >= 3) statusColumns = PACKET_BYTE(packet, 3);
if (count >= 4) textColumns = PACKET_BYTE(packet, 4);
return updateConfiguration(brl, autodetecting, textColumns, statusColumns, STATUS_FIRST);
}
static int
setBrailleFirmness1 (BrailleDisplay *brl, BrailleFirmness setting) {
return writeParameter1(brl, 3,
setting * 4 / BRL_FIRMNESS_MAXIMUM);
}
static int
identifyModel1 (BrailleDisplay *brl, unsigned char identifier) {
/* Find out which model we are connected to... */
for (
model = modelTable;
model->name && (model->identifier != identifier);
model += 1
);
if (model->name) {
if (setDefaultConfiguration(brl)) {
if (model->flags & MOD_FLAG_CAN_CONFIGURE) {
brl->setBrailleFirmness = setBrailleFirmness1;
if (!writeFunction1(brl, 0X07)) return 0;
while (awaitBrailleInput(brl, 200)) {
unsigned char packet[MAXIMUM_PACKET_SIZE];
int count = protocol->readPacket(brl, packet, sizeof(packet));
if (count == -1) break;
if (count == 0) continue;
if ((packet[0] == 0X7F) && (packet[1] == 0X07)) {
updateConfiguration1(brl, 1, packet);
break;
}
}
if (!writeFunction1(brl, 0X0B)) return 0;
}
return 1;
}
} else {
logMessage(LOG_ERR, "detected unknown Alva model with ID %02X (hex)", identifier);
}
return 0;
}
static void
initializeVariables1 (BrailleDisplay *brl, char **parameters) {
}
static int
readPacket1 (BrailleDisplay *brl, unsigned char *packet, int size) {
int offset = 0;
int length = 0;
while (1) {
unsigned char byte;
{
int started = offset > 0;
if (!gioReadByte(brl->gioEndpoint, &byte, started)) {
int result = (errno == EAGAIN)? 0: -1;
if (started) logPartialPacket(packet, offset);
return result;
}
}
gotByte:
if (offset == 0) {
if (byte == 0X7F) {
length = PACKET_SIZE(0);
} else if ((byte & 0XF0) == 0X70) {
length = 2;
} else if (byte == BRL_ID[0]) {
length = BRL_ID_SIZE;
} else if (!byte) {
length = 2;
} else {
logIgnoredByte(byte);
continue;
}
} else {
int unexpected = 0;
unsigned char type = packet[0];
if (type == 0X7F) {
if (offset == 3) length = PACKET_SIZE(byte);
if (((offset % 2) == 0) && (byte != 0X7E)) unexpected = 1;
} else if (type == BRL_ID[0]) {
if ((offset < BRL_ID_LENGTH) && (byte != BRL_ID[offset])) unexpected = 1;
} else if (!type) {
if (byte) unexpected = 1;
}
if (unexpected) {
logShortPacket(packet, offset);
offset = 0;
length = 0;
goto gotByte;
}
}
if (offset < size) {
packet[offset] = byte;
} else {
if (offset == size) logTruncatedPacket(packet, offset);
logDiscardedByte(byte);
}
if (++offset == length) {
if ((offset > size) || !packet[0]) {
offset = 0;
length = 0;
continue;
}
logInputPacket(packet, offset);
return length;
}
}
}
static int
detectModel1 (BrailleDisplay *brl) {
int probes = 0;
while (writeFunction1(brl, 0X06)) {
while (awaitBrailleInput(brl, 200)) {
unsigned char packet[MAXIMUM_PACKET_SIZE];
if (protocol->readPacket(brl, packet, sizeof(packet)) > 0) {
if (memcmp(packet, BRL_ID, BRL_ID_LENGTH) == 0) {
if (identifyModel1(brl, packet[BRL_ID_LENGTH])) {
return 1;
}
}
}
}
if (errno != EAGAIN) break;
if (++probes == 3) break;
}
return 0;
}
static int
readCommand1 (BrailleDisplay *brl) {
unsigned char packet[MAXIMUM_PACKET_SIZE];
int length;
while ((length = protocol->readPacket(brl, packet, sizeof(packet))) > 0) {
unsigned char group = packet[0];
unsigned char key = packet[1];
int press = !(key & AL_KEY_RELEASE);
key &= ~AL_KEY_RELEASE;
switch (group) {
case 0X71: /* operating keys and status keys */
if (key <= 0X0D) {
enqueueKeyEvent(brl, AL_GRP_NavigationKeys, key+AL_KEY_OPERATION, press);
continue;
}
if ((key >= 0X20) && (key <= 0X25)) {
enqueueKeyEvent(brl, AL_GRP_NavigationKeys, key-0X20+AL_KEY_STATUS1, press);
continue;
}
if ((key >= 0X30) && (key <= 0X35)) {
enqueueKeyEvent(brl, AL_GRP_NavigationKeys, key-0X30+AL_KEY_STATUS2, press);
continue;
}
break;
case 0X72: /* primary (lower) routing keys */
if (key <= 0X5F) { /* make */
enqueueKeyEvent(brl, AL_GRP_RoutingKeys1, key, press);
continue;
}
break;
case 0X75: /* secondary (upper) routing keys */
if (key <= 0X5F) { /* make */
enqueueKeyEvent(brl, AL_GRP_RoutingKeys2, key, press);
continue;
}
break;
case 0X77: /* satellite keypads */
if (key <= 0X05) {
enqueueKeyEvent(brl, AL_GRP_NavigationKeys, key+AL_KEY_SPEECH_PAD, press);
continue;
}
if ((key >= 0X20) && (key <= 0X25)) {
enqueueKeyEvent(brl, AL_GRP_NavigationKeys, key-0X20+AL_KEY_NAV_PAD, press);
continue;
}
continue;
case 0X7F:
switch (packet[1]) {
case 0X07: /* text/status cells reconfigured */
if (!updateConfiguration1(brl, 0, packet)) return BRL_CMD_RESTARTBRL;
continue;
case 0X0B: { /* display parameters reconfigured */
int count = PACKET_BYTE(packet, 0);
if (count >= 8) {
unsigned char frontKeys = PACKET_BYTE(packet, 8);
const unsigned char progKey = 0X02;
if (frontKeys & progKey) {
unsigned char newSetting = frontKeys & ~progKey;
logMessage(LOG_DEBUG, "Reconfiguring front keys: %02X -> %02X",
frontKeys, newSetting);
writeParameter1(brl, 6, newSetting);
}
}
continue;
}
}
break;
default:
if (length >= BRL_ID_SIZE) {
if (memcmp(packet, BRL_ID, BRL_ID_LENGTH) == 0) {
/* The terminal has been turned off and back on. */
if (!identifyModel1(brl, packet[BRL_ID_LENGTH])) return BRL_CMD_RESTARTBRL;
brl->resizeRequired = 1;
continue;
}
}
break;
}
logUnexpectedPacket(packet, length);
}
return (length < 0)? BRL_CMD_RESTARTBRL: EOF;
}
static int
writeBraille1 (BrailleDisplay *brl, const unsigned char *cells, int start, int count) {
static const unsigned char header[] = {ASCII_CR, ASCII_ESC, 'B'}; /* escape code to display braille */
static const unsigned char trailer[] = {ASCII_CR}; /* to send after the braille sequence */
unsigned char packet[sizeof(header) + 2 + count + sizeof(trailer)];
unsigned char *byte = packet;
byte = mempcpy(byte, header, sizeof(header));
*byte++ = start;
*byte++ = count;
byte = mempcpy(byte, cells, count);
byte = mempcpy(byte, trailer, sizeof(trailer));
return writeBraillePacket(brl, NULL, packet, byte-packet);
}
static const ProtocolOperations protocol1Operations = {
.initializeVariables = initializeVariables1,
.readPacket = readPacket1,
.updateConfiguration = updateConfiguration1,
.detectModel = detectModel1,
.readCommand = readCommand1,
.writeBraille = writeBraille1
};
static void
initializeVariables2 (BrailleDisplay *brl, char **parameters) {
brl->data->protocol.bc.secondaryRoutingKeyEmulation = 0;
if (*parameters[PARM_SECONDARY_ROUTING_KEY_EMULATION]) {
if (!validateYesNo(&brl->data->protocol.bc.secondaryRoutingKeyEmulation, parameters[PARM_SECONDARY_ROUTING_KEY_EMULATION])) {
logMessage(LOG_WARNING, "%s: %s", "invalid secondary routing key emulation setting",
parameters[PARM_SECONDARY_ROUTING_KEY_EMULATION]);
}
}
initializeHidKeyboardPacket(&brl->data->protocol.bc.hidKeyboardPacket);
brl->data->protocol.bc.version.hardware = 0;
brl->data->protocol.bc.version.firmware = 0;
brl->data->protocol.bc.version.btBase = 0;
brl->data->protocol.bc.version.btFP = 0;
brl->data->protocol.bc.macAddress.base = 0;
brl->data->protocol.bc.macAddress.featurePack = 0;
}
static int
testHaveFeaturePack2 (BrailleDisplay *brl) {
return brl->data->protocol.bc.macAddress.featurePack != 0;
}
static int
testHaveRawKeyboard2 (BrailleDisplay *brl) {
return testHaveFeaturePack2(brl) &&
(brl->data->protocol.bc.version.firmware >= 0X020801);
}
static void
logVersion2 (uint32_t version, const char *label) {
BytesOverlay overlay;
unsigned char *byte = &overlay.bytes[2];
char string[0X40];
putLittleEndian32(&overlay.u32, version);
STR_BEGIN(string, sizeof(string));
while (1) {
STR_PRINTF("%u", *byte);
if (byte == overlay.bytes) break;
*byte = 0;
if (!overlay.u32) break;
STR_PRINTF(".");
byte -= 1;
}
STR_END;
logMessage(LOG_DEBUG, "%s: %s", label, string);
}
static uint64_t
parseHardwareVersion2 (
const unsigned char **bytes, size_t *count
) {
return parseDecimalField(bytes, count, 2, 3);
}
static uint64_t
parseFirmwareVersion2 (
const unsigned char **bytes, size_t *count
) {
return parseHexadecimalField(bytes, count, 3, 3);
}
static void
setVersions2 (BrailleDisplay *brl, const unsigned char *bytes, size_t count) {
brl->data->protocol.bc.version.hardware = parseHardwareVersion2(&bytes, &count);
logVersion2(brl->data->protocol.bc.version.hardware, "Hardware Version");
brl->data->protocol.bc.version.firmware = parseFirmwareVersion2(&bytes, &count);
logVersion2(brl->data->protocol.bc.version.firmware, "Firmware Version");
brl->data->protocol.bc.version.btBase = parseFirmwareVersion2(&bytes, &count);
logVersion2(brl->data->protocol.bc.version.btBase, "Base Bluetooth Module Version");
brl->data->protocol.bc.version.btFP = parseFirmwareVersion2(&bytes, &count);
logVersion2(brl->data->protocol.bc.version.btFP, "Feature Pack Bluetooth Module Version");
}
static void
logMacAddress2 (uint64_t address, const char *label) {
BytesOverlay overlay;
const unsigned char *byte = &overlay.bytes[5];
char string[0X20];
putLittleEndian64(&overlay.u64, address);
STR_BEGIN(string, sizeof(string));
while (1) {
STR_PRINTF("%02X", *byte);
if (byte == overlay.bytes) break;
byte -= 1;
STR_PRINTF("%c", ':');
}
STR_END;
logMessage(LOG_DEBUG, "%s: %s", label, string);
}
static uint64_t
parseMacAddress2 (
const unsigned char **bytes, size_t *count
) {
BytesOverlay overlay;
putLittleEndian64(&overlay.u64, parseHexadecimalField(bytes, count, 6, 6));
swapBytes(&overlay.bytes[5], &overlay.bytes[4]);
swapBytes(&overlay.bytes[2], &overlay.bytes[0]);
return getLittleEndian64(overlay.u64);
}
static void
setMacAddresses2 (BrailleDisplay *brl, const unsigned char *bytes, size_t count) {
brl->data->protocol.bc.macAddress.base = parseMacAddress2(&bytes, &count);
logMacAddress2(brl->data->protocol.bc.macAddress.base, "Base Mac Address");
brl->data->protocol.bc.macAddress.featurePack = parseMacAddress2(&bytes, &count);
logMacAddress2(brl->data->protocol.bc.macAddress.featurePack, "Feature Pack Mac Address");
}
static int
interpretKeyboardEvent2 (BrailleDisplay *brl, const unsigned char *packet) {
const void *newPacket = packet;
processHidKeyboardPacket(&brl->data->protocol.bc.hidKeyboardPacket, newPacket);
return EOF;
}
static int
interpretKeyEvent2 (BrailleDisplay *brl, unsigned char group, unsigned char key) {
unsigned char release = group & 0X80;
int press = !release;
group &= ~release;
switch (group) {
case 0X01:
switch (key) {
case 0X01:
if (!protocol->updateConfiguration(brl, 0, NULL)) return BRL_CMD_RESTARTBRL;
return EOF;
default:
break;
}
break;
{
unsigned int base;
unsigned int count;
int secondary;
case 0X71: /* thumb key */
base = AL_KEY_THUMB;
count = AL_KEYS_THUMB;
secondary = 1;
goto doKey;
case 0X72: /* etouch key */
base = AL_KEY_ETOUCH;
count = AL_KEYS_ETOUCH;
secondary = 0;
goto doKey;
case 0X73: /* smartpad key */
base = AL_KEY_SMARTPAD;
count = AL_KEYS_SMARTPAD;
secondary = 1;
goto doKey;
case 0X78: /* feature pack key */
base = AL_KEY_FEATUREPACK;
count = AL_KEYS_FEATUREPACK;
secondary = 0;
goto doKey;
doKey:
if (secondary) {
if ((key / count) == 1) {
key -= count;
}
}
if (key < count) {
enqueueKeyEvent(brl, AL_GRP_NavigationKeys, base+key, press);
return EOF;
}
break;
}
case 0X74: { /* routing key */
unsigned char secondary = key & 0X80;
key &= ~secondary;
/*
* The 6xx series don't have a second row of routing keys but
* emulate them (in order to aid compatibility with the 5xx series)
* using an annoying press delay. It is adviseable to turn this
* functionality off in the device's menu, but, in case it's left
* on, we just interpret these keys as primary routing keys by
* default, unless overriden by a driver parameter.
*/
if (!brl->data->protocol.bc.secondaryRoutingKeyEmulation) secondary = 0;
if (brl->data->protocol.bc.version.firmware < 0X011102) {
if (key >= brl->data->protocol.bc.splitOffset) {
key -= brl->data->protocol.bc.splitOffset;
}
}
if (key >= textOffset) {
if ((key -= textOffset) < brl->textColumns) {
KeyGroup group = secondary? AL_GRP_RoutingKeys2: AL_GRP_RoutingKeys1;
enqueueKeyEvent(brl, group, key, press);
return EOF;
}
}
break;
}
default:
break;
}
logMessage(LOG_WARNING, "unknown key: group=%02X key=%02X", group, key);
return EOF;
}
static BraillePacketVerifierResult
verifyPacket2s (
BrailleDisplay *brl,
unsigned char *bytes, size_t size,
size_t *length, void *data
) {
unsigned char byte = bytes[size-1];
switch (size) {
case 1:
switch (byte) {
case ASCII_ESC:
*length = 2;
break;
default:
return BRL_PVR_INVALID;
}
break;
case 2:
switch (byte) {
case 0X32: /* 2 */ *length = 5; break;
case 0X3F: /* ? */ *length = 3; break;
case 0X45: /* E */ *length = 3; break;
case 0X4B: /* K */ *length = 4; break;
case 0X4E: /* N */ *length = 14; break;
case 0X50: /* P */ *length = 3; break;
case 0X54: /* T */ *length = 4; break;
case 0X56: /* V */ *length = 13; break;
case 0X68: /* h */ *length = 10; break;
case 0X72: /* r */ *length = 3; break;
default:
return BRL_PVR_INVALID;
}
break;
default:
break;
}
return BRL_PVR_INCLUDE;
}
static int
setFeature2s (BrailleDisplay *brl, const unsigned char *request, size_t size) {
return writeBraillePacket(brl, NULL, request, size);
}
static size_t
getFeature2s (BrailleDisplay *brl, unsigned char feature, unsigned char *response, size_t size) {
const unsigned char request[] = {ASCII_ESC, feature, 0X3F};
if (protocol->setFeature(brl, request, sizeof(request))) {
while (awaitBrailleInput(brl, 1000)) {
int length = protocol->readPacket(brl, response, size);
if (length <= 0) break;
if ((response[0] == ASCII_ESC) && (response[1] == feature)) return length;
logUnexpectedPacket(response, length);
}
}
return 0;
}
static int
updateConfiguration2s (BrailleDisplay *brl, int autodetecting, const unsigned char *packet) {
unsigned char response[0X20];
if (protocol->getFeature(brl, 0X45, response, sizeof(response))) {
unsigned char textColumns = response[2];
if (autodetecting) {
if (brl->data->protocol.bc.version.firmware < 0X010A00) {
switch (textColumns) {
case 12:
if (model == &modelBC640) {
model = &modelEL12;
logMessage(LOG_INFO, "switched to model %s", model->name);
}
break;
default:
break;
}
}
}
if (protocol->getFeature(brl, 0X54, response, sizeof(response))) {
unsigned char statusColumns = response[2];
unsigned char statusSide = response[3];
if (updateConfiguration(brl, autodetecting, textColumns, statusColumns,
(statusSide == 'R')? STATUS_RIGHT: STATUS_LEFT)) {
brl->data->protocol.bc.splitOffset = (model->columns == actualColumns)? 0: actualColumns+1;
return 1;
}
}
}
return 0;
}
static int
identifyModel2s (BrailleDisplay *brl, unsigned char identifier) {
static const ModelEntry *const models[] = {
&modelBC624, &modelBC640, &modelBC680,
NULL
};
unsigned char response[0X20];
const ModelEntry *const *modelEntry = models;
while ((model = *modelEntry++)) {
if (model->identifier == identifier) {
if (protocol->getFeature(brl, 0X56, response, sizeof(response))) {
setVersions2(brl, &response[2], sizeof(response)-2);
if (protocol->getFeature(brl, 0X4E, response, sizeof(response))) {
setMacAddresses2(brl, &response[2], sizeof(response)-2);
if (setDefaultConfiguration(brl)) {
if (updateConfiguration2s(brl, 1, NULL)) {
return 1;
}
}
}
}
return 0;
}
}
logMessage(LOG_ERR, "detected unknown Alva model with ID %02X (hex)", identifier);
return 0;
}
static int
detectModel2s (BrailleDisplay *brl) {
int probes = 0;
do {
unsigned char response[0X20];
if (protocol->getFeature(brl, 0X3F, response, sizeof(response))) {
if (identifyModel2s(brl, response[2])) {
return 1;
}
} else if (errno != EAGAIN) {
break;
}
} while (++probes < 3);
return 0;
}
static int
readCommand2s (BrailleDisplay *brl) {
while (1) {
unsigned char packet[MAXIMUM_PACKET_SIZE];
int length = protocol->readPacket(brl, packet, sizeof(packet));
if (!length) return EOF;
if (length < 0) return BRL_CMD_RESTARTBRL;
switch (packet[0]) {
case ASCII_ESC:
switch (packet[1]) {
case 0X4B: /* K */ {
int command = interpretKeyEvent2(brl, packet[2], packet[3]);
if (command != EOF) return command;
continue;
}
case 0X68: /* h */ {
int command = interpretKeyboardEvent2(brl, &packet[2]);
if (command != EOF) return command;
continue;
}
default:
break;
}
break;
default:
break;
}
logUnexpectedPacket(packet, length);
}
}
static int
writeBraille2s (BrailleDisplay *brl, const unsigned char *cells, int start, int count) {
unsigned char packet[4 + count];
unsigned char *byte = packet;
*byte++ = ASCII_ESC;
*byte++ = 0X42;
*byte++ = start;
*byte++ = count;
byte = mempcpy(byte, cells, count);
return writeBraillePacket(brl, NULL, packet, byte-packet);
}
static const SettingsUpdateEntry requiredSettings2s[] = {
{ /* enable raw feature pack keys */
.feature = 0X72 /* r */,
.test = testHaveRawKeyboard2,
.offset = 2,
.disable = 0XFF,
.enable = 0X01
},
{ /* disable key repeat */
.feature = 0X50 /* P */,
.offset = 2,
.disable = 0XFF
},
{ /* disable second routing key row emulation */
.feature = 0X32 /* 2 */,
.offset = 2,
.disable = 0XFF
},
{ .feature = 0 }
};
static const ProtocolOperations protocol2sOperations = {
.initializeVariables = initializeVariables2,
.verifyPacket = verifyPacket2s,
.readPacket = readPacket,
.requiredSettings = requiredSettings2s,
.setFeature = setFeature2s,
.getFeature = getFeature2s,
.updateConfiguration = updateConfiguration2s,
.detectModel = detectModel2s,
.readCommand = readCommand2s,
.writeBraille = writeBraille2s
};
static BraillePacketVerifierResult
verifyPacket2u (
BrailleDisplay *brl,
unsigned char *bytes, size_t size,
size_t *length, void *data
) {
unsigned char byte = bytes[size-1];
switch (size) {
case 1:
switch (byte) {
case 0X01: *length = 9; break;
case 0X04: *length = 3; break;
default:
return BRL_PVR_INVALID;
}
break;
default:
break;
}
return BRL_PVR_INCLUDE;
}
static int
setFeature2u (BrailleDisplay *brl, const unsigned char *request, size_t size) {
logOutputPacket(request, size);
return gioWriteHidFeature(brl->gioEndpoint, request, size) != -1;
}
static size_t
getFeature2u (BrailleDisplay *brl, HidReportIdentifier identifier, unsigned char *response, size_t size) {
ssize_t length = gioGetHidFeature(brl->gioEndpoint, identifier, response, size);
if (length > 0) {
logInputPacket(response, length);
return length;
}
return 0;
}
static int
updateConfiguration2u (BrailleDisplay *brl, int autodetecting, const unsigned char *packet) {
unsigned char buffer[0X20];
size_t length = protocol->getFeature(brl, 0X05, buffer, sizeof(buffer));
if (length > 0) {
int textColumns = brl->textColumns;
int statusColumns = brl->statusColumns;
int statusSide = 0;
if (length >= 2) statusColumns = buffer[1];
if (length >= 3) statusSide = buffer[2];
if (length >= 7) textColumns = buffer[6];
if (updateConfiguration(brl, autodetecting, textColumns, statusColumns,
statusSide? STATUS_RIGHT: STATUS_LEFT)) {
brl->data->protocol.bc.splitOffset = model->columns - actualColumns;
return 1;
}
}
return 0;
}
static int
detectModel2u (BrailleDisplay *brl) {
{
unsigned char buffer[0X20];
size_t length = protocol->getFeature(brl, 0X09, buffer, sizeof(buffer));
if (length > 3) setVersions2(brl, &buffer[3], length-3);
}
{
unsigned char buffer[0X20];
size_t length = protocol->getFeature(brl, 0X0D, buffer, sizeof(buffer));
if (length > 1) setMacAddresses2(brl, &buffer[1], length-1);
}
if (setDefaultConfiguration(brl))
if (updateConfiguration2u(brl, 1, NULL))
return 1;
return 0;
}
static int
readCommand2u (BrailleDisplay *brl) {
while (1) {
unsigned char packet[MAXIMUM_PACKET_SIZE];
int length = protocol->readPacket(brl, packet, sizeof(packet));
if (!length) return EOF;
if (length < 0) return BRL_CMD_RESTARTBRL;
switch (packet[0]) {
case 0X01: {
int command = interpretKeyboardEvent2(brl, &packet[1]);
if (command != EOF) return command;
continue;
}
case 0X04: {
int command = interpretKeyEvent2(brl, packet[2], packet[1]);
if (command != EOF) return command;
continue;
}
default:
break;
}
logUnexpectedPacket(packet, length);
}
}
static int
writeBraille2u (BrailleDisplay *brl, const unsigned char *cells, int start, int count) {
while (count > 0) {
int length = MIN(count, 40);
unsigned char packet[3 + length];
unsigned char *byte = packet;
*byte++ = 0X02;
*byte++ = start;
*byte++ = length;
byte = mempcpy(byte, cells, length);
if (!writeBraillePacket(brl, NULL, packet, byte-packet)) return 0;
cells += length;
start += length;
count -= length;
}
return 1;
}
static ssize_t
writeData2u (
UsbDevice *device, const UsbChannelDefinition *definition,
const void *data, size_t size, int timeout
) {
const unsigned char *bytes = data;
return usbHidSetReport(device, definition->interface,
bytes[0], bytes, size, timeout);
}
static const SettingsUpdateEntry requiredSettings2u[] = {
{ /* enable raw feature pack keys */
.feature = 6 /* Key Settings Report */,
.test = testHaveRawKeyboard2,
.offset = 1,
.enable = 0X20
},
{ /* disable key repeat */
.feature = 6 /* Key Settings Report */,
.offset = 1,
.disable = 0X08
},
{ /* disable second routing key row emulation */
.feature = 7 /* CR Key Settings Report */,
.offset = 1,
.disable = 0X02
},
{ .feature = 0 }
};
static const ProtocolOperations protocol2uOperations = {
.initializeVariables = initializeVariables2,
.verifyPacket = verifyPacket2u,
.readPacket = readPacket,
.requiredSettings = requiredSettings2u,
.setFeature = setFeature2u,
.getFeature = getFeature2u,
.updateConfiguration = updateConfiguration2u,
.detectModel = detectModel2u,
.readCommand = readCommand2u,
.writeBraille = writeBraille2u
};
static BrailleDisplay *brailleDisplay = NULL;
int
AL_writeData (unsigned char *data, int len ) {
return writeBraillePacket(brailleDisplay, NULL, data, len);
}
static void
setUsbConnectionProperties (
GioUsbConnectionProperties *properties,
const UsbChannelDefinition *definition
) {
model = properties->applicationData;
if (definition->outputEndpoint) {
properties->applicationData = &protocol1Operations;
} else {
properties->applicationData = &protocol2uOperations;
properties->writeData = writeData2u;
}
}
static int
connectResource (BrailleDisplay *brl, const char *identifier) {
static const SerialParameters serialParameters = {
SERIAL_DEFAULT_PARAMETERS,
.baud = 9600
};
BEGIN_USB_CHANNEL_DEFINITIONS
{ /* Satellite (5nn) */
.vendor=0X06B0, .product=0X0001,
.configuration=1, .interface=0, .alternative=0,
.inputEndpoint=1, .outputEndpoint=2
},
{ /* Voyager Protocol Converter */
.vendor=0X0798, .product=0X0600,
.configuration=1, .interface=0, .alternative=0,
.inputEndpoint=1, .outputEndpoint=0,
.data=&modelVoyager
},
{ /* BC624 */
.vendor=0X0798, .product=0X0624,
.configuration=1, .interface=0, .alternative=0,
.inputEndpoint=1, .outputEndpoint=0,
.data=&modelBC624
},
{ /* BC640 */
.vendor=0X0798, .product=0X0640,
.configuration=1, .interface=0, .alternative=0,
.inputEndpoint=1, .outputEndpoint=0,
.data=&modelBC640
},
{ /* BC680 */
.vendor=0X0798, .product=0X0680,
.configuration=1, .interface=0, .alternative=0,
.inputEndpoint=1, .outputEndpoint=0,
.data=&modelBC680
},
END_USB_CHANNEL_DEFINITIONS
GioDescriptor descriptor;
gioInitializeDescriptor(&descriptor);
descriptor.serial.parameters = &serialParameters;
descriptor.serial.options.applicationData = &protocol1Operations;
descriptor.usb.channelDefinitions = usbChannelDefinitions;
descriptor.usb.setConnectionProperties = setUsbConnectionProperties;
descriptor.usb.options.inputTimeout = 100;
descriptor.bluetooth.channelNumber = 1;
descriptor.bluetooth.discoverChannel = 1;
descriptor.bluetooth.options.applicationData = &protocol2sOperations;
descriptor.bluetooth.options.inputTimeout = 200;
if (connectBrailleResource(brl, identifier, &descriptor, NULL)) {
protocol = gioGetApplicationData(brl->gioEndpoint);
return 1;
}
return 0;
}
static int
brl_construct (BrailleDisplay *brl, char **parameters, const char *device) {
if ((brl->data = malloc(sizeof(*brl->data)))) {
memset(brl->data, 0, sizeof(*brl->data));
brl->data->restore.end = brl->data->restore.buffer;
if (connectResource(brl, device)) {
protocol->initializeVariables(brl, parameters);
brl->data->rotatedCells = 0;
if (*parameters[PARM_ROTATED_CELLS]) {
if (!validateYesNo(&brl->data->rotatedCells, parameters[PARM_ROTATED_CELLS])) {
logMessage(LOG_WARNING, "%s: %s", "invalid rotated cells setting",
parameters[PARM_ROTATED_CELLS]);
}
}
if (protocol->detectModel(brl)) {
if (updateSettings(brl)) {
setBrailleKeyTable(brl, model->keyTableDefinition);
if (brl->data->rotatedCells) {
makeOutputTable(dotsTable_rotated);
} else {
makeOutputTable(dotsTable_ISO11548_1);
}
brailleDisplay = brl;
return 1;
}
}
disconnectBrailleResource(brl, NULL);
}
free(brl->data);
} else {
logMallocError();
}
return 0;
}
static void
brl_destruct (BrailleDisplay *brl) {
brailleDisplay = NULL;
restoreSettings(brl);
disconnectBrailleResource(brl, NULL);
free(brl->data);
if (previousText) {
free(previousText);
previousText = NULL;
}
if (previousStatus) {
free(previousStatus);
previousStatus = NULL;
}
}
static int
brl_writeWindow (BrailleDisplay *brl, const wchar_t *text) {
unsigned int from;
unsigned int to;
if (cellsHaveChanged(previousText, brl->buffer, brl->textColumns, &from, &to, &textRewriteRequired)) {
if (model->flags & MOD_FLAG_FORCE_FROM_0) from = 0;
{
size_t count = to - from;
unsigned char cells[count];
translateOutputCells(cells, &brl->buffer[from], count);
if (!protocol->writeBraille(brl, cells, textOffset+from, count)) return 0;
}
}
return 1;
}
static int
brl_writeStatus (BrailleDisplay *brl, const unsigned char *status) {
size_t cellCount = brl->statusColumns;
if (cellsHaveChanged(previousStatus, status, cellCount, NULL, NULL, &statusRewriteRequired)) {
unsigned char cells[cellCount];
translateOutputCells(cells, status, cellCount);
if (!protocol->writeBraille(brl, cells, statusOffset, cellCount)) return 0;
}
return 1;
}
static int
brl_readCommand (BrailleDisplay *brl, KeyTableCommandContext context) {
return protocol->readCommand(brl);
}