/* * 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-ce.h" #define PROBE_RETRY_LIMIT 2 #define PROBE_INPUT_TIMEOUT 1000 #define MAXIMUM_RESPONSE_SIZE (0XFF + 4) #define MAXIMUM_CELL_COUNT 140 BEGIN_KEY_NAME_TABLE(navigation) KEY_NAME_ENTRY(CE_KEY_PadLeft1, "PadLeft1"), KEY_NAME_ENTRY(CE_KEY_PadUp1, "PadUp1"), KEY_NAME_ENTRY(CE_KEY_PadCenter1, "PadCenter1"), KEY_NAME_ENTRY(CE_KEY_PadDown1, "PadDown1"), KEY_NAME_ENTRY(CE_KEY_PadRight1, "PadRight1"), KEY_NAME_ENTRY(CE_KEY_LeftUpper1, "LeftUpper1"), KEY_NAME_ENTRY(CE_KEY_LeftMiddle1, "LeftMiddle1"), KEY_NAME_ENTRY(CE_KEY_LeftLower1, "LeftLower1"), KEY_NAME_ENTRY(CE_KEY_RightUpper1, "RightUpper1"), KEY_NAME_ENTRY(CE_KEY_RightMiddle1, "RightMiddle1"), KEY_NAME_ENTRY(CE_KEY_RightLower1, "RightLower1"), KEY_NAME_ENTRY(CE_KEY_PadLeft2, "PadLeft2"), KEY_NAME_ENTRY(CE_KEY_PadUp2, "PadUp2"), KEY_NAME_ENTRY(CE_KEY_PadCenter2, "PadCenter2"), KEY_NAME_ENTRY(CE_KEY_PadDown2, "PadDown2"), KEY_NAME_ENTRY(CE_KEY_PadRight2, "PadRight2"), KEY_NAME_ENTRY(CE_KEY_LeftUpper2, "LeftUpper2"), KEY_NAME_ENTRY(CE_KEY_LeftMiddle2, "LeftMiddle2"), KEY_NAME_ENTRY(CE_KEY_LeftLower2, "LeftLower2"), KEY_NAME_ENTRY(CE_KEY_RightUpper2, "RightUpper2"), KEY_NAME_ENTRY(CE_KEY_RightMiddle2, "RightMiddle2"), KEY_NAME_ENTRY(CE_KEY_RightLower2, "RightLower2"), KEY_GROUP_ENTRY(CE_GRP_RoutingKey, "RoutingKey"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLE(novem) KEY_NAME_ENTRY(0X03, "Dot7"), KEY_NAME_ENTRY(0X07, "Dot3"), KEY_NAME_ENTRY(0X0B, "Dot2"), KEY_NAME_ENTRY(0X0F, "Dot1"), KEY_NAME_ENTRY(0X13, "Dot4"), KEY_NAME_ENTRY(0X17, "Dot5"), KEY_NAME_ENTRY(0X1B, "Dot6"), KEY_NAME_ENTRY(0X1F, "Dot8"), KEY_NAME_ENTRY(0X10, "LeftSpace"), KEY_NAME_ENTRY(0X18, "RightSpace"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLES(all) KEY_NAME_TABLE(navigation), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLES(novem) KEY_NAME_TABLE(novem), END_KEY_NAME_TABLES DEFINE_KEY_TABLE(all) DEFINE_KEY_TABLE(novem) BEGIN_KEY_TABLE_LIST &KEY_TABLE_DEFINITION(all), &KEY_TABLE_DEFINITION(novem), END_KEY_TABLE_LIST typedef struct { unsigned char identifier; unsigned char cellCount; const KeyTableDefinition *ktd; } ModelEntry; static const ModelEntry modelTable[] = { { .identifier = 0X68, .cellCount = 0, .ktd = &KEY_TABLE_DEFINITION(novem) }, { .identifier = 0X70, .cellCount = 0, .ktd = &KEY_TABLE_DEFINITION(all) }, { .identifier = 0X72, .cellCount = 20, .ktd = &KEY_TABLE_DEFINITION(all) }, { .identifier = 0X74, .cellCount = 40, .ktd = &KEY_TABLE_DEFINITION(all) }, { .identifier = 0X76, .cellCount = 60, .ktd = &KEY_TABLE_DEFINITION(all) }, { .identifier = 0X78, .cellCount = 80, .ktd = &KEY_TABLE_DEFINITION(all) }, { .identifier = 0X7A, .cellCount = 100, .ktd = &KEY_TABLE_DEFINITION(all) }, { .identifier = 0X7C, .cellCount = 120, .ktd = &KEY_TABLE_DEFINITION(all) }, { .identifier = 0X7E, .cellCount = 140, .ktd = &KEY_TABLE_DEFINITION(all) }, { .identifier = 0 } }; struct BrailleDataStruct { const ModelEntry *model; unsigned char forceRewrite; unsigned char acknowledgementPending; unsigned char textCells[MAXIMUM_CELL_COUNT]; }; static const ModelEntry * getModelEntry (unsigned char identifier) { const ModelEntry *model = modelTable; while (model->identifier) { if (identifier == model->identifier) return model; model += 1; } logMessage(LOG_WARNING, "unknown %s model: 0X%02X", STRINGIFY(DRIVER_NAME), identifier); return NULL; } static int setModel (BrailleDisplay *brl, unsigned char identifier) { const ModelEntry *model = getModelEntry(identifier); if (model) { logMessage(LOG_NOTICE, "%s Model: 0X%02X, %u cells", STRINGIFY(DRIVER_NAME), model->identifier, model->cellCount); brl->data->model = model; brl->textColumns = model->cellCount; return 1; } return 0; } static int writeBytes (BrailleDisplay *brl, const unsigned char *bytes, size_t count) { return writeBraillePacket(brl, NULL, bytes, count); } static int writePacket (BrailleDisplay *brl, unsigned char type, size_t size, const unsigned char *data) { unsigned char bytes[size + 5]; unsigned char *byte = bytes; *byte++ = CE_PKT_BEGIN; *byte++ = brl->data->model->identifier; *byte++ = size + 1; *byte++ = type; byte = mempcpy(byte, data, size); *byte++ = CE_PKT_END; return writeBytes(brl, bytes, byte-bytes); } static BraillePacketVerifierResult verifyPacket ( BrailleDisplay *brl, unsigned char *bytes, size_t size, size_t *length, void *data ) { const unsigned char byte = bytes[size-1]; if (size == 1) { switch (byte) { case CE_RSP_Identity: *length = 2; break; case CE_PKT_BEGIN: *length = 3; break; default: return BRL_PVR_INVALID; } } else { switch (bytes[0]) { case CE_PKT_BEGIN: if (size == 2) { if (byte != brl->data->model->identifier) { if (!setModel(brl, byte)) return BRL_PVR_INVALID; brl->resizeRequired = 1; } } else if (size == 3) { *length += byte + 1; } else if (size == *length) { if (byte != CE_PKT_END) return BRL_PVR_INVALID; } break; default: break; } } return BRL_PVR_INCLUDE; } static size_t readPacket (BrailleDisplay *brl, void *packet, size_t size) { return readBraillePacket(brl, NULL, packet, size, verifyPacket, NULL); } static int connectResource (BrailleDisplay *brl, const char *identifier) { static const SerialParameters serialParameters = { SERIAL_DEFAULT_PARAMETERS, .baud = 19200, .parity = SERIAL_PARITY_ODD }; BEGIN_USB_CHANNEL_DEFINITIONS { /* all models */ .vendor=0X0403, .product=0X6001, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=2, .serial = &serialParameters }, END_USB_CHANNEL_DEFINITIONS GioDescriptor descriptor; gioInitializeDescriptor(&descriptor); descriptor.usb.channelDefinitions = usbChannelDefinitions; descriptor.bluetooth.channelNumber = 1; if (connectBrailleResource(brl, identifier, &descriptor, NULL)) { return 1; } return 0; } static int writeIdentifyRequest (BrailleDisplay *brl) { static const unsigned char bytes[] = {CE_REQ_Identify}; return writeBytes(brl, bytes, sizeof(bytes)); } static BrailleResponseResult isIdentityResponse (BrailleDisplay *brl, const void *packet, size_t size) { const unsigned char *bytes = packet; return (bytes[0] == CE_RSP_Identity)? 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]; if (probeBrailleDisplay(brl, PROBE_RETRY_LIMIT, NULL, PROBE_INPUT_TIMEOUT, writeIdentifyRequest, readPacket, &response, sizeof(response), isIdentityResponse)) { if (setModel(brl, response[1])) { setBrailleKeyTable(brl, brl->data->model->ktd); makeOutputTable(dotsTable_ISO11548_1); brl->data->forceRewrite = 1; brl->data->acknowledgementPending = 0; return 1; } } disconnectBrailleResource(brl, NULL); } free(brl->data); } else { logMallocError(); } return 0; } static void brl_destruct (BrailleDisplay *brl) { disconnectBrailleResource(brl, NULL); if (brl->data) { free(brl->data); brl->data = NULL; } } static int brl_writeWindow (BrailleDisplay *brl, const wchar_t *text) { if (!brl->data->acknowledgementPending) { if (cellsHaveChanged(brl->data->textCells, brl->buffer, brl->textColumns, NULL, NULL, &brl->data->forceRewrite)) { unsigned char cells[brl->textColumns]; translateOutputCells(cells, brl->data->textCells, brl->textColumns); if (!writePacket(brl, CE_PKT_REQ_Write, brl->textColumns, cells)) return 0; brl->data->acknowledgementPending = 1; } } 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)))) { switch (packet[0]) { case CE_PKT_BEGIN: { const unsigned char *bytes = &packet[4]; size_t count = packet[2] - 1; switch (packet[3]) { case CE_PKT_RSP_NavigationKey: if (count == 1) { KeyGroup group; unsigned char key = bytes[0]; int press = !(key & CE_KEY_RELEASE); key &= ~CE_KEY_RELEASE; if ((key >= CE_KEY_ROUTING_MIN) && (key <= CE_KEY_ROUTING_MAX)) { group = CE_GRP_RoutingKey; key -= CE_KEY_ROUTING_MIN; } else { group = CE_GRP_NavigationKey; } enqueueKeyEvent(brl, group, key, press); continue; } break; case CE_PKT_RSP_Confirmation: if (count > 0) { switch (bytes[0]) { case 0X7D: brl->data->forceRewrite = 1; case 0X7E: brl->data->acknowledgementPending = 0; continue; default: break; } } break; case CE_PKT_RSP_KeyboardKey: while (count--) enqueueCommand(BRL_CMD_BLK(PASSAT) | BRL_ARG_PUT(*bytes++)); continue; default: break; } break; } default: break; } logUnexpectedPacket(packet, size); } return (errno == EAGAIN)? EOF: BRL_CMD_RESTARTBRL; }