/* * 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 "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; }