blob: 20867bb4551ff83bc682b038f625f3849f90195a [file] [log] [blame] [edit]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <string.h>
#include <errno.h>
#include "log.h"
#include "brl_driver.h"
#include "brldefs-hd.h"
#define PROBE_RETRY_LIMIT 2
#define PROBE_INPUT_TIMEOUT 1000
#define MAXIMUM_RESPONSE_SIZE 3
#define MAXIMUM_TEXT_CELL_COUNT 80
#define MAXIMUM_STATUS_CELL_COUNT 4
BEGIN_KEY_NAME_TABLE(pfl)
KEY_NAME_ENTRY(HD_PFL_K1, "K1"),
KEY_NAME_ENTRY(HD_PFL_K2, "K2"),
KEY_NAME_ENTRY(HD_PFL_K3, "K3"),
KEY_NAME_ENTRY(HD_PFL_B1, "B1"),
KEY_NAME_ENTRY(HD_PFL_B2, "B2"),
KEY_NAME_ENTRY(HD_PFL_B3, "B3"),
KEY_NAME_ENTRY(HD_PFL_B4, "B4"),
KEY_NAME_ENTRY(HD_PFL_B5, "B5"),
KEY_NAME_ENTRY(HD_PFL_B6, "B6"),
KEY_NAME_ENTRY(HD_PFL_B7, "B7"),
KEY_NAME_ENTRY(HD_PFL_B8, "B8"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(mbl)
KEY_NAME_ENTRY(HD_MBL_B1, "B1"),
KEY_NAME_ENTRY(HD_MBL_B2, "B2"),
KEY_NAME_ENTRY(HD_MBL_B3, "B3"),
KEY_NAME_ENTRY(HD_MBL_B4, "B4"),
KEY_NAME_ENTRY(HD_MBL_B5, "B5"),
KEY_NAME_ENTRY(HD_MBL_B6, "B6"),
KEY_NAME_ENTRY(HD_MBL_K1, "K1"),
KEY_NAME_ENTRY(HD_MBL_K2, "K2"),
KEY_NAME_ENTRY(HD_MBL_K3, "K3"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(routing)
KEY_GROUP_ENTRY(HD_GRP_RoutingKeys, "RoutingKey"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLES(pfl)
KEY_NAME_TABLE(pfl),
KEY_NAME_TABLE(routing),
END_KEY_NAME_TABLES
BEGIN_KEY_NAME_TABLES(mbl)
KEY_NAME_TABLE(mbl),
KEY_NAME_TABLE(routing),
END_KEY_NAME_TABLES
DEFINE_KEY_TABLE(pfl)
DEFINE_KEY_TABLE(mbl)
BEGIN_KEY_TABLE_LIST
&KEY_TABLE_DEFINITION(pfl),
&KEY_TABLE_DEFINITION(mbl),
END_KEY_TABLE_LIST
typedef int KeysPacketInterpreter (BrailleDisplay *brl, const unsigned char *packet);
typedef struct {
const char *modelName;
const KeyTableDefinition *keyTableDefinition;
BraillePacketVerifier *verifyPacket;
KeysPacketInterpreter *interpretKeysPacket;
unsigned char textCellCount;
unsigned char statusCellCount;
unsigned char firstRoutingKey;
unsigned char acknowledgementResponse;
} ModelEntry;
struct BrailleDataStruct {
const ModelEntry *model;
unsigned char forceRewrite;
unsigned char textCells[MAXIMUM_TEXT_CELL_COUNT];
unsigned char statusCells[MAXIMUM_STATUS_CELL_COUNT];
KeyNumberSet navigationKeys;
};
static BraillePacketVerifierResult
verifyPacket_ProfiLine (
BrailleDisplay *brl,
unsigned char *bytes, size_t size,
size_t *length, void *data
) {
switch (size) {
case 1:
*length = 1;
break;
default:
break;
}
return BRL_PVR_INCLUDE;
}
static int
interpretKeysPacket_ProfiLine (BrailleDisplay *brl, const unsigned char *packet) {
const unsigned char code = packet[0];
const unsigned char release = 0X80;
const int press = !(code & release);
unsigned char key = code & ~release;
KeyGroup group;
if (key < brl->data->model->firstRoutingKey) {
group = HD_GRP_NavigationKeys;
} else if (key < (brl->data->model->firstRoutingKey + brl->textColumns)) {
group = HD_GRP_RoutingKeys;
key -= brl->data->model->firstRoutingKey;
} else {
return 0;
}
enqueueKeyEvent(brl, group, key, press);
return 1;
}
static const ModelEntry modelEntry_ProfiLine = {
.modelName = "ProfiLine USB",
.keyTableDefinition = &KEY_TABLE_DEFINITION(pfl),
.verifyPacket = verifyPacket_ProfiLine,
.interpretKeysPacket = interpretKeysPacket_ProfiLine,
.textCellCount = 80,
.statusCellCount = 4,
.firstRoutingKey = 0X20,
.acknowledgementResponse = 0X7E
};
static BraillePacketVerifierResult
verifyPacket_MobilLine (
BrailleDisplay *brl,
unsigned char *bytes, size_t size,
size_t *length, void *data
) {
off_t index = size - 1;
unsigned char byte = bytes[index];
if ((byte >> 4) == index) {
if (index == 0) *length = 3;
} else if (size == 1) {
*length = 1;
} else {
return BRL_PVR_INVALID;
}
return BRL_PVR_INCLUDE;
}
static int
interpretKeysPacket_MobilLine (BrailleDisplay *brl, const unsigned char *packet) {
const unsigned char *byte = packet;
if (!(*byte >> 4)) {
const unsigned char *end = packet + 3;
KeyNumberSet keys = 0;
unsigned char shift = 0;
while (byte < end) {
keys |= (*byte++ & 0XF) << shift;
shift += 4;
}
enqueueUpdatedKeys(brl, keys, &brl->data->navigationKeys,
HD_GRP_NavigationKeys, 0);
return 1;
}
if (*byte >= brl->data->model->firstRoutingKey) {
unsigned char key = *byte - brl->data->model->firstRoutingKey;
if (key < brl->textColumns) {
enqueueKey(brl, HD_GRP_RoutingKeys, key);
return 1;
}
}
return 0;
}
static const ModelEntry modelEntry_MobilLine = {
.modelName = "MobilLine USB",
.keyTableDefinition = &KEY_TABLE_DEFINITION(mbl),
.verifyPacket = verifyPacket_MobilLine,
.interpretKeysPacket = interpretKeysPacket_MobilLine,
.textCellCount = 40,
.statusCellCount = 2,
.firstRoutingKey = 0X40,
.acknowledgementResponse = 0X30
};
static size_t
readPacket (BrailleDisplay *brl, void *packet, size_t size) {
return readBraillePacket(brl, NULL, packet, size, brl->data->model->verifyPacket, NULL);
}
static int
connectResource (BrailleDisplay *brl, const char *identifier) {
static const SerialParameters serialParameters_ProfiLine = {
SERIAL_DEFAULT_PARAMETERS,
.baud = 19200,
.parity = SERIAL_PARITY_ODD
};
static const SerialParameters serialParameters_MobilLine = {
SERIAL_DEFAULT_PARAMETERS,
.baud = 9600,
.parity = SERIAL_PARITY_ODD
};
BEGIN_USB_STRING_LIST(usbManufacturers_0403_6001)
"Hedo Reha Technik GmbH",
END_USB_STRING_LIST
BEGIN_USB_CHANNEL_DEFINITIONS
{ /* ProfiLine */
.vendor=0X0403, .product=0XDE59,
.configuration=1, .interface=0, .alternative=0,
.inputEndpoint=1, .outputEndpoint=2,
.serial = &serialParameters_ProfiLine,
.data = &modelEntry_ProfiLine
},
{ /* MobilLine */
.vendor=0X0403, .product=0XDE58,
.configuration=1, .interface=0, .alternative=0,
.inputEndpoint=1, .outputEndpoint=2,
.serial = &serialParameters_MobilLine,
.data = &modelEntry_MobilLine
},
{ /* MobilLine */
.vendor=0X0403, .product=0X6001,
.manufacturers = usbManufacturers_0403_6001,
.configuration=1, .interface=0, .alternative=0,
.inputEndpoint=1, .outputEndpoint=2,
.serial = &serialParameters_MobilLine,
.data = &modelEntry_MobilLine
},
END_USB_CHANNEL_DEFINITIONS
GioDescriptor descriptor;
gioInitializeDescriptor(&descriptor);
descriptor.usb.channelDefinitions = usbChannelDefinitions;
if (connectBrailleResource(brl, identifier, &descriptor, NULL)) {
return 1;
}
return 0;
}
static void
disconnectResource (BrailleDisplay *brl) {
disconnectBrailleResource(brl, NULL);
}
static int
writeCells (BrailleDisplay *brl, int wait) {
unsigned char packet[1 + brl->data->model->statusCellCount + brl->data->model->textCellCount];
unsigned char *byte = packet;
*byte++ = HD_REQ_WRITE_CELLS;
byte = mempcpy(byte, brl->data->statusCells, brl->data->model->statusCellCount);
byte = translateOutputCells(byte, brl->data->textCells, brl->data->model->textCellCount);
{
size_t count = byte - packet;
if (wait) return writeBrailleMessage(brl, NULL, 0, packet, count);
return writeBraillePacket(brl, NULL, packet, count);
}
}
static int
writeIdentifyRequest (BrailleDisplay *brl) {
memset(brl->data->textCells, 0, sizeof(brl->data->textCells));
memset(brl->data->statusCells, 0, sizeof(brl->data->statusCells));
return writeCells(brl, 0);
}
static BrailleResponseResult
isIdentityResponse (BrailleDisplay *brl, const void *packet, size_t size) {
const unsigned char *bytes = packet;
return (bytes[0] == brl->data->model->acknowledgementResponse)? BRL_RSP_DONE: BRL_RSP_UNEXPECTED;
}
static int
brl_construct (BrailleDisplay *brl, char **parameters, const char *device) {
if ((brl->data = malloc(sizeof(*brl->data)))) {
memset(brl->data, 0, sizeof(*brl->data));
if (connectResource(brl, device)) {
unsigned char response[MAXIMUM_RESPONSE_SIZE];
brl->data->model = gioGetApplicationData(brl->gioEndpoint);
brl->textColumns = brl->data->model->textCellCount;
makeOutputTable(dotsTable_ISO11548_1);
if (probeBrailleDisplay(brl, PROBE_RETRY_LIMIT, NULL, PROBE_INPUT_TIMEOUT,
writeIdentifyRequest,
readPacket, &response, sizeof(response),
isIdentityResponse)) {
setBrailleKeyTable(brl, brl->data->model->keyTableDefinition);
brl->data->forceRewrite = 1;
return 1;
}
disconnectResource(brl);
}
free(brl->data);
} else {
logMallocError();
}
return 0;
}
static void
brl_destruct (BrailleDisplay *brl) {
disconnectResource(brl);
if (brl->data) {
free(brl->data);
brl->data = NULL;
}
}
static int
brl_writeWindow (BrailleDisplay *brl, const wchar_t *text) {
if (cellsHaveChanged(brl->data->textCells, brl->buffer, brl->textColumns, NULL, NULL, &brl->data->forceRewrite)) {
if (!writeCells(brl, 1)) return 0;
}
return 1;
}
static int
brl_readCommand (BrailleDisplay *brl, KeyTableCommandContext context) {
unsigned char packet[MAXIMUM_RESPONSE_SIZE];
size_t size;
while ((size = readPacket(brl, packet, sizeof(packet)))) {
if (packet[0] == brl->data->model->acknowledgementResponse) {
acknowledgeBrailleMessage(brl);
} else if (!brl->data->model->interpretKeysPacket(brl, packet)) {
logUnexpectedPacket(packet, size);
}
}
return (errno == EAGAIN)? EOF: BRL_CMD_RESTARTBRL;
}