/* * 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 "bitfield.h" #include "log.h" #include "parse.h" #include "timing.h" #include "async_wait.h" #include "ascii.h" typedef enum { PARM_SETTIME } DriverParameter; #define BRLPARMS "settime" #define BRLSTAT ST_AlvaStyle #define BRL_HAVE_STATUS_CELLS #define BRL_HAVE_PACKET_IO #include "brl_driver.h" #include "brldefs-ht.h" BEGIN_KEY_NAME_TABLE(routing) KEY_GROUP_ENTRY(HT_GRP_RoutingKeys, "RoutingKey"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLE(dots) KEY_NAME_ENTRY(HT_KEY_B1, "B1"), KEY_NAME_ENTRY(HT_KEY_B2, "B2"), KEY_NAME_ENTRY(HT_KEY_B3, "B3"), KEY_NAME_ENTRY(HT_KEY_B4, "B4"), KEY_NAME_ENTRY(HT_KEY_B5, "B5"), KEY_NAME_ENTRY(HT_KEY_B6, "B6"), KEY_NAME_ENTRY(HT_KEY_B7, "B7"), KEY_NAME_ENTRY(HT_KEY_B8, "B8"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLE(keypad) KEY_NAME_ENTRY(HT_KEY_B12, "B12"), KEY_NAME_ENTRY(HT_KEY_Zero, "Zero"), KEY_NAME_ENTRY(HT_KEY_B13, "B13"), KEY_NAME_ENTRY(HT_KEY_B14, "B14"), KEY_NAME_ENTRY(HT_KEY_B11, "B11"), KEY_NAME_ENTRY(HT_KEY_One, "One"), KEY_NAME_ENTRY(HT_KEY_Two, "Two"), KEY_NAME_ENTRY(HT_KEY_Three, "Three"), KEY_NAME_ENTRY(HT_KEY_B10, "B10"), KEY_NAME_ENTRY(HT_KEY_Four, "Four"), KEY_NAME_ENTRY(HT_KEY_Five, "Five"), KEY_NAME_ENTRY(HT_KEY_Six, "Six"), KEY_NAME_ENTRY(HT_KEY_B9, "B9"), KEY_NAME_ENTRY(HT_KEY_Seven, "Seven"), KEY_NAME_ENTRY(HT_KEY_Eight, "Eight"), KEY_NAME_ENTRY(HT_KEY_Nine, "Nine"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLE(rockers) KEY_NAME_ENTRY(HT_KEY_Escape, "LeftRockerTop"), KEY_NAME_ENTRY(HT_KEY_Return, "LeftRockerBottom"), KEY_NAME_ENTRY(HT_KEY_Up, "RightRockerTop"), KEY_NAME_ENTRY(HT_KEY_Down, "RightRockerBottom"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLE(joystick) KEY_NAME_ENTRY(HT_KEY_JoystickLeft, "Left"), KEY_NAME_ENTRY(HT_KEY_JoystickRight, "Right"), KEY_NAME_ENTRY(HT_KEY_JoystickUp, "Up"), KEY_NAME_ENTRY(HT_KEY_JoystickDown, "Down"), KEY_NAME_ENTRY(HT_KEY_JoystickAction, "Action"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLE(modular) KEY_NAME_ENTRY(HT_KEY_Up, "Left"), KEY_NAME_ENTRY(HT_KEY_Down, "Right"), KEY_NAME_ENTRY(HT_KEY_STATUS+0, "Status1"), KEY_NAME_ENTRY(HT_KEY_STATUS+1, "Status2"), KEY_NAME_ENTRY(HT_KEY_STATUS+2, "Status3"), KEY_NAME_ENTRY(HT_KEY_STATUS+3, "Status4"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLES(mdlr) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(keypad), KEY_NAME_TABLE(modular), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLE(modularEvolution) KEY_NAME_ENTRY(HT_KEY_Space, "Left"), KEY_NAME_ENTRY(HT_KEY_SpaceRight, "Right"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLES(me64) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(rockers), KEY_NAME_TABLE(modularEvolution), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLES(me88) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(rockers), KEY_NAME_TABLE(keypad), KEY_NAME_TABLE(modularEvolution), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLES(mc88) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(rockers), KEY_NAME_TABLE(keypad), KEY_NAME_TABLE(modularEvolution), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLE(brailleStar) KEY_NAME_ENTRY(HT_KEY_Space, "SpaceLeft"), KEY_NAME_ENTRY(HT_KEY_SpaceRight, "SpaceRight"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLES(bs40) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(rockers), KEY_NAME_TABLE(brailleStar), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLES(bs80) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(rockers), KEY_NAME_TABLE(keypad), KEY_NAME_TABLE(brailleStar), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLES(brln) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(rockers), KEY_NAME_TABLE(brailleStar), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLES(as40) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(rockers), KEY_NAME_TABLE(brailleStar), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLES(ab) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(rockers), KEY_NAME_TABLE(brailleStar), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLES(cb40) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(rockers), KEY_NAME_TABLE(brailleStar), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLE(brailleWave) KEY_NAME_ENTRY(HT_KEY_Up, "Left"), KEY_NAME_ENTRY(HT_KEY_Down, "Right"), KEY_NAME_ENTRY(HT_KEY_Escape, "Escape"), KEY_NAME_ENTRY(HT_KEY_Space, "Space"), KEY_NAME_ENTRY(HT_KEY_Return, "Return"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLES(wave) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(brailleWave), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLE(easyBraille) KEY_NAME_ENTRY(HT_KEY_Up, "Left"), KEY_NAME_ENTRY(HT_KEY_Down, "Right"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLES(easy) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(easyBraille), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLE(basicBraille) KEY_NAME_ENTRY(HT_KEY_B2, "Display3"), KEY_NAME_ENTRY(HT_KEY_B3, "Display2"), KEY_NAME_ENTRY(HT_KEY_B4, "Display1"), KEY_NAME_ENTRY(HT_KEY_B5, "Display4"), KEY_NAME_ENTRY(HT_KEY_B6, "Display5"), KEY_NAME_ENTRY(HT_KEY_B7, "Display6"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLES(bb) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(basicBraille), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLES(bbp) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(rockers), KEY_NAME_TABLE(brailleStar), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLES(alo) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(rockers), KEY_NAME_TABLE(brailleStar), KEY_NAME_TABLE(joystick), END_KEY_NAME_TABLES BEGIN_KEY_NAME_TABLES(ac4) KEY_NAME_TABLE(routing), KEY_NAME_TABLE(dots), KEY_NAME_TABLE(rockers), KEY_NAME_TABLE(brailleStar), KEY_NAME_TABLE(joystick), END_KEY_NAME_TABLES typedef enum { HT_BWK_Backward = 0X01, HT_BWK_Forward = 0X08, HT_BWK_Escape = 0X02, HT_BWK_Enter = 0X04 } HT_BookwormKey; BEGIN_KEY_NAME_TABLE(bookworm) KEY_NAME_ENTRY(HT_BWK_Backward, "Backward"), KEY_NAME_ENTRY(HT_BWK_Forward, "Forward"), KEY_NAME_ENTRY(HT_BWK_Escape, "Escape"), KEY_NAME_ENTRY(HT_BWK_Enter, "Enter"), END_KEY_NAME_TABLE BEGIN_KEY_NAME_TABLES(bkwm) KEY_NAME_TABLE(bookworm), END_KEY_NAME_TABLES DEFINE_KEY_TABLE(mdlr) DEFINE_KEY_TABLE(me64) DEFINE_KEY_TABLE(me88) DEFINE_KEY_TABLE(mc88) DEFINE_KEY_TABLE(bs40) DEFINE_KEY_TABLE(bs80) DEFINE_KEY_TABLE(brln) DEFINE_KEY_TABLE(as40) DEFINE_KEY_TABLE(ab) DEFINE_KEY_TABLE(cb40) DEFINE_KEY_TABLE(wave) DEFINE_KEY_TABLE(easy) DEFINE_KEY_TABLE(bb) DEFINE_KEY_TABLE(bbp) DEFINE_KEY_TABLE(alo) DEFINE_KEY_TABLE(ac4) DEFINE_KEY_TABLE(bkwm) BEGIN_KEY_TABLE_LIST &KEY_TABLE_DEFINITION(mdlr), &KEY_TABLE_DEFINITION(me64), &KEY_TABLE_DEFINITION(me88), &KEY_TABLE_DEFINITION(mc88), &KEY_TABLE_DEFINITION(bs40), &KEY_TABLE_DEFINITION(bs80), &KEY_TABLE_DEFINITION(brln), &KEY_TABLE_DEFINITION(as40), &KEY_TABLE_DEFINITION(ab), &KEY_TABLE_DEFINITION(cb40), &KEY_TABLE_DEFINITION(wave), &KEY_TABLE_DEFINITION(easy), &KEY_TABLE_DEFINITION(bb), &KEY_TABLE_DEFINITION(bbp), &KEY_TABLE_DEFINITION(alo), &KEY_TABLE_DEFINITION(ac4), &KEY_TABLE_DEFINITION(bkwm), END_KEY_TABLE_LIST static int endSession_Bookworm (BrailleDisplay *brl) { static const unsigned char sessionEnd[] = {0X05, 0X07}; return writeBrailleMessage(brl, NULL, 0, sessionEnd, sizeof(sessionEnd)); } typedef int ByteInterpreter (BrailleDisplay *brl, unsigned char byte); static ByteInterpreter interpretByte_key; static ByteInterpreter interpretByte_Bookworm; typedef int (CellWriter) (BrailleDisplay *brl); static CellWriter writeCells_statusAndText; static CellWriter writeCells_Bookworm; static CellWriter writeCells_Evolution; static SetBrailleFirmnessMethod setBrailleFirmness; static SetTouchSensitivityMethod setTouchSensitivity_Evolution; static SetTouchSensitivityMethod setTouchSensitivity_ActiveBraille; typedef struct { const char *name; const KeyTableDefinition *keyTableDefinition; ByteInterpreter *interpretByte; CellWriter *writeCells; SetBrailleFirmnessMethod *setBrailleFirmness; SetTouchSensitivityMethod *setTouchSensitivity; BrailleSessionEnder *sessionEnder; HT_ModelIdentifier identifier:8; unsigned char textCells; unsigned char statusCells; unsigned hasATC:1; /* Active Tactile Control */ unsigned hasTime:1; } ModelEntry; static const ModelEntry modelTable[] = { { .identifier = HT_MODEL_Modular20, .name = "Modular 20+4", .textCells = 20, .statusCells = 4, .keyTableDefinition = &KEY_TABLE_DEFINITION(mdlr), .interpretByte = interpretByte_key, .writeCells = writeCells_statusAndText }, { .identifier = HT_MODEL_Modular40, .name = "Modular 40+4", .textCells = 40, .statusCells = 4, .keyTableDefinition = &KEY_TABLE_DEFINITION(mdlr), .interpretByte = interpretByte_key, .writeCells = writeCells_statusAndText }, { .identifier = HT_MODEL_Modular80, .name = "Modular 80+4", .textCells = 80, .statusCells = 4, .keyTableDefinition = &KEY_TABLE_DEFINITION(mdlr), .interpretByte = interpretByte_key, .writeCells = writeCells_statusAndText }, { .identifier = HT_MODEL_ModularEvolution64, .name = "Modular Evolution 64", .textCells = 64, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(me64), .interpretByte = interpretByte_key, .writeCells = writeCells_Evolution, .setTouchSensitivity = setTouchSensitivity_Evolution, .hasATC = 1 }, { .identifier = HT_MODEL_ModularEvolution88, .name = "Modular Evolution 88", .textCells = 88, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(me88), .interpretByte = interpretByte_key, .writeCells = writeCells_Evolution, .setTouchSensitivity = setTouchSensitivity_Evolution, .hasATC = 1 }, { .identifier = HT_MODEL_BrailleWave, .name = "Braille Wave", .textCells = 40, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(wave), .interpretByte = interpretByte_key, .writeCells = writeCells_statusAndText }, { .identifier = HT_MODEL_Bookworm, .name = "Bookworm", .textCells = 8, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(bkwm), .interpretByte = interpretByte_Bookworm, .writeCells = writeCells_Bookworm, .sessionEnder = endSession_Bookworm }, { .identifier = HT_MODEL_Braillino, .name = "Braillino", .textCells = 20, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(brln), .interpretByte = interpretByte_key, .writeCells = writeCells_statusAndText }, { .identifier = HT_MODEL_BrailleStar40, .name = "Braille Star 40", .textCells = 40, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(bs40), .interpretByte = interpretByte_key, .writeCells = writeCells_statusAndText }, { .identifier = HT_MODEL_BrailleStar80, .name = "Braille Star 80", .textCells = 80, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(bs80), .interpretByte = interpretByte_key, .writeCells = writeCells_statusAndText }, { .identifier = HT_MODEL_EasyBraille, .name = "Easy Braille", .textCells = 40, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(easy), .interpretByte = interpretByte_key, .writeCells = writeCells_statusAndText }, { .identifier = HT_MODEL_ActiveBraille, .name = "Active Braille", .textCells = 40, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(ab), .interpretByte = interpretByte_key, .writeCells = writeCells_Evolution, .setBrailleFirmness = setBrailleFirmness, .setTouchSensitivity = setTouchSensitivity_ActiveBraille, .hasATC = 1, .hasTime = 1 }, #define HT_BASIC_BRAILLE(cells) \ { .identifier = HT_MODEL_BasicBraille##cells, \ .name = "Basic Braille " STRINGIFY(cells), \ .textCells = cells, \ .statusCells = 0, \ .keyTableDefinition = &KEY_TABLE_DEFINITION(bb),\ .interpretByte = interpretByte_key, \ .writeCells = writeCells_Evolution \ } HT_BASIC_BRAILLE(16), HT_BASIC_BRAILLE(20), HT_BASIC_BRAILLE(32), HT_BASIC_BRAILLE(40), HT_BASIC_BRAILLE(48), HT_BASIC_BRAILLE(64), HT_BASIC_BRAILLE(80), HT_BASIC_BRAILLE(160), #undef HT_BASIC_BRAILLE #define HT_BASIC_BRAILLE_PLUS(cells) \ { .identifier = HT_MODEL_BasicBraillePlus##cells, \ .name = "Basic Braille Plus " STRINGIFY(cells), \ .textCells = cells, \ .statusCells = 0, \ .keyTableDefinition = &KEY_TABLE_DEFINITION(bbp),\ .interpretByte = interpretByte_key, \ .writeCells = writeCells_Evolution \ } HT_BASIC_BRAILLE_PLUS(20), HT_BASIC_BRAILLE_PLUS(32), HT_BASIC_BRAILLE_PLUS(40), HT_BASIC_BRAILLE_PLUS(48), HT_BASIC_BRAILLE_PLUS(64), HT_BASIC_BRAILLE_PLUS(80), HT_BASIC_BRAILLE_PLUS(84), #undef HT_BASIC_BRAILLE_PLUS { .identifier = HT_MODEL_Actilino, .name = "Actilino", .textCells = 16, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(alo), .interpretByte = interpretByte_key, .writeCells = writeCells_Evolution, .setBrailleFirmness = setBrailleFirmness, .setTouchSensitivity = setTouchSensitivity_ActiveBraille, .hasATC = 1, .hasTime = 1 }, { .identifier = HT_MODEL_Activator, .name = "Activator", .textCells = 40, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(ac4), .interpretByte = interpretByte_key, .writeCells = writeCells_Evolution, .setBrailleFirmness = setBrailleFirmness, .setTouchSensitivity = setTouchSensitivity_ActiveBraille, .hasATC = 1, .hasTime = 1 }, { .identifier = HT_MODEL_ActiveStar40, .name = "Active Star 40", .textCells = 40, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(as40), .interpretByte = interpretByte_key, .writeCells = writeCells_Evolution, .setBrailleFirmness = setBrailleFirmness, .setTouchSensitivity = setTouchSensitivity_ActiveBraille, .hasATC = 1, .hasTime = 1 }, { .identifier = HT_MODEL_ModularConnect88, .name = "Modular Connect 88", .textCells = 88, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(mc88), .interpretByte = interpretByte_key, .writeCells = writeCells_Evolution, }, { .identifier = HT_MODEL_ConnectBraille40, .name = "Connect Braille 40", .textCells = 40, .statusCells = 0, .keyTableDefinition = &KEY_TABLE_DEFINITION(cb40), .interpretByte = interpretByte_key, .writeCells = writeCells_Evolution, .setBrailleFirmness = setBrailleFirmness, .hasTime = 1 }, { /* end of table */ .name = NULL } }; #define MAXIMUM_TEXT_CELLS 160 #define MAXIMUM_STATUS_CELLS 4 typedef enum { BDS_OFF, BDS_READY } BrailleDisplayState; struct BrailleDataStruct { const ModelEntry *model; /* points to terminal model config struct */ unsigned char rawData[MAXIMUM_TEXT_CELLS]; /* translated data to send to Braille */ unsigned char prevData[MAXIMUM_TEXT_CELLS]; /* previously sent raw data */ unsigned char rawStatus[MAXIMUM_STATUS_CELLS]; /* to hold status info */ unsigned char prevStatus[MAXIMUM_STATUS_CELLS]; /* to hold previous status */ BrailleDisplayState currentState; TimePeriod statePeriod; unsigned int retryCount; unsigned char updateRequired; }; /* USB IO */ #include "io_usb.h" #define HT_HID_REPORT_TIMEOUT 100 typedef enum { HT_HID_RPT_OutData = 0X01, /* receive data from device */ HT_HID_RPT_InData = 0X02, /* send data to device */ HT_HID_RPT_InCommand = 0XFB, /* run USB-HID firmware command */ HT_HID_RPT_OutVersion = 0XFC, /* get version of USB-HID firmware */ HT_HID_RPT_OutBaud = 0XFD, /* get baud rate of serial connection */ HT_HID_RPT_InBaud = 0XFE, /* set baud rate of serial connection */ } HT_HidReportNumber; typedef enum { HT_HID_CMD_FlushBuffers = 0X01, /* flush input and output buffers */ } HtHidCommand; static size_t hidReportSize_OutData; static size_t hidReportSize_InData; static size_t hidReportSize_InCommand; static size_t hidReportSize_OutVersion; static size_t hidReportSize_OutBaud; static size_t hidReportSize_InBaud; static uint16_t hidFirmwareVersion; static unsigned char *hidInputReport = NULL; #define hidInputLength (hidInputReport[1]) #define hidInputBuffer (&hidInputReport[2]) static unsigned char hidInputOffset; static ssize_t getHidReport ( UsbDevice *device, const UsbChannelDefinition *definition, unsigned char number, unsigned char *buffer, uint16_t size ) { ssize_t result = usbHidGetReport(device, definition->interface, number, buffer, size, HT_HID_REPORT_TIMEOUT); if (result > 0 && buffer[0] != number) { logMessage(LOG_WARNING, "unexpected HID report number: expected %02X, received %02X", number, buffer[0]); errno = EIO; result = -1; } return result; } typedef struct { HT_HidReportNumber number; size_t *size; } ReportEntry; static int getHidReportSizes (BrailleDisplay *brl, const ReportEntry *table) { const ReportEntry *report = table; while (report->number) { if (!(*report->size = gioGetHidReportSize(brl->gioEndpoint, report->number))) return 0; report += 1; } return 1; } static int allocateHidInputBuffer (void) { if (hidReportSize_OutData) { if ((hidInputReport = malloc(hidReportSize_OutData))) { hidInputLength = 0; hidInputOffset = 0; return 1; } else { logMallocError(); } } return 0; } static int getHidFirmwareVersion (BrailleDisplay *brl) { hidFirmwareVersion = 0; if (hidReportSize_OutVersion) { unsigned char report[hidReportSize_OutVersion]; ssize_t result = gioGetHidReport(brl->gioEndpoint, HT_HID_RPT_OutVersion, report, sizeof(report)); if (result > 0) { hidFirmwareVersion = (report[1] << 8) | report[2]; logMessage(LOG_INFO, "Firmware Version: %u.%u", report[1], report[2]); return 1; } } return 0; } static int executeHidFirmwareCommand (BrailleDisplay *brl, HtHidCommand command) { if (hidReportSize_InCommand) { unsigned char report[hidReportSize_InCommand]; report[0] = HT_HID_RPT_InCommand; report[1] = command; if (gioWriteHidReport(brl->gioEndpoint, report, sizeof(report)) != -1) { return 1; } } return 0; } typedef struct { int (*initializeSession) (BrailleDisplay *brl); } GeneralOperations; typedef struct { const GeneralOperations *general; GioUsbAwaitInputMethod *awaitInput; GioUsbReadDataMethod *readData; GioUsbWriteDataMethod *writeData; UsbInputFilter *inputFilter; } UsbOperations; static int initializeUsbSession2 (BrailleDisplay *brl) { static const ReportEntry reportTable[] = { {.number=HT_HID_RPT_OutData, .size=&hidReportSize_OutData}, {.number=HT_HID_RPT_InData, .size=&hidReportSize_InData}, {.number=HT_HID_RPT_InCommand, .size=&hidReportSize_InCommand}, {.number=HT_HID_RPT_OutVersion, .size=&hidReportSize_OutVersion}, {.number=HT_HID_RPT_OutBaud, .size=&hidReportSize_OutBaud}, {.number=HT_HID_RPT_InBaud, .size=&hidReportSize_InBaud}, {.number=0} }; if (getHidReportSizes(brl, reportTable)) { if (allocateHidInputBuffer()) { if (getHidFirmwareVersion(brl)) { if (executeHidFirmwareCommand(brl, HT_HID_CMD_FlushBuffers)) { return 1; } } } } return 0; } static int awaitUsbInput2 ( UsbDevice *device, const UsbChannelDefinition *definition, int milliseconds ) { if (hidReportSize_OutData) { TimePeriod period; if (hidInputOffset < hidInputLength) return 1; startTimePeriod(&period, milliseconds); while (1) { ssize_t result = getHidReport(device, definition, HT_HID_RPT_OutData, hidInputReport, hidReportSize_OutData); if (result == -1) return 0; hidInputOffset = 0; if (hidInputLength > 0) return 1; if (afterTimePeriod(&period, NULL)) break; asyncWait(10); } } errno = EAGAIN; return 0; } static ssize_t readUsbData2 ( UsbDevice *device, const UsbChannelDefinition *definition, void *data, size_t size, int initialTimeout, int subsequentTimeout ) { unsigned char *buffer = data; int count = 0; while (count < size) { if (!awaitUsbInput2(device, definition, count? subsequentTimeout: initialTimeout)) { if (errno != EAGAIN) count = -1; break; } { size_t amount = MIN(size-count, hidInputLength-hidInputOffset); memcpy(&buffer[count], &hidInputBuffer[hidInputOffset], amount); hidInputOffset += amount; count += amount; } } return count; } static ssize_t writeUsbData2 ( UsbDevice *device, const UsbChannelDefinition *definition, const void *data, size_t size, int timeout ) { const unsigned char *buffer = data; int index = 0; if (hidReportSize_InData) { while (size) { unsigned char report[hidReportSize_InData]; unsigned char count = MIN(size, (sizeof(report) - 2)); int result; report[0] = HT_HID_RPT_InData; report[1] = count; memcpy(report+2, &buffer[index], count); memset(&report[count+2], 0, sizeof(report)-count-2); result = usbHidSetReport(device, definition->interface, report[0], report, sizeof(report), HT_HID_REPORT_TIMEOUT); if (result == -1) return -1; index += count; size -= count; } } return index; } static const GeneralOperations generalOperations2 = { .initializeSession = initializeUsbSession2 }; static const UsbOperations usbOperations2 = { .general = &generalOperations2, .awaitInput = awaitUsbInput2, .readData = readUsbData2, .writeData = writeUsbData2 }; static int initializeUsbSession3 (BrailleDisplay *brl) { static const ReportEntry reportTable[] = { {.number=HT_HID_RPT_OutData, .size=&hidReportSize_OutData}, {.number=HT_HID_RPT_InData, .size=&hidReportSize_InData}, {.number=0} }; return getHidReportSizes(brl, reportTable); } static ssize_t writeUsbData3 ( UsbDevice *device, const UsbChannelDefinition *definition, const void *data, size_t size, int timeout ) { const unsigned char *buffer = data; int index = 0; if (hidReportSize_InData) { while (size) { unsigned char report[hidReportSize_InData]; const unsigned char count = MIN(size, (sizeof(report) - 2)); int result; report[0] = HT_HID_RPT_InData; report[1] = count; memset(mempcpy(report+2, &buffer[index], count), 0, sizeof(report)-count-2); result = usbWriteEndpoint(device, definition->outputEndpoint, report, sizeof(report), 1000); if (result == -1) return -1; index += count; size -= count; } } return index; } static int filterUsbInput3 (UsbInputFilterData *data) { unsigned char *buffer = data->buffer; if ((data->length >= 2) && (data->length == hidReportSize_OutData) && (buffer[0] == HT_HID_RPT_OutData) && (buffer[1] <= (data->length - 2))) { data->length = buffer[1]; memmove(data->buffer, data->buffer+2, data->length); } return 1; } static const GeneralOperations generalOperations3 = { .initializeSession = initializeUsbSession3 }; static const UsbOperations usbOperations3 = { .general = &generalOperations3, .writeData = writeUsbData3, .inputFilter = filterUsbInput3 }; static BraillePacketVerifierResult verifyPacket ( 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) { default: *length = 1; break; case HT_PKT_OK: *length = 2; break; case HT_PKT_Extended: *length = 4; break; } break; case 3: if (bytes[0] == HT_PKT_Extended) *length += byte; break; case 5: if ((bytes[0] == HT_PKT_Extended) && (bytes[1] == HT_MODEL_ActiveBraille) && (bytes[2] == 2) && (bytes[3] == HT_EXTPKT_Confirmation) && (byte == 0X15)) *length += 1; break; default: break; } if ((size == *length) && (bytes[0] == HT_PKT_Extended) && (byte != SYN)) { return BRL_PVR_INVALID; } return BRL_PVR_INCLUDE; } static size_t readPacket (BrailleDisplay *brl, void *buffer, size_t size) { return readBraillePacket(brl, NULL, buffer, size, verifyPacket, NULL); } static ssize_t brl_readPacket (BrailleDisplay *brl, void *buffer, size_t size) { const size_t length = readPacket(brl, buffer, size); if (length == 0 && errno != EAGAIN) return -1; return length; } static ssize_t brl_writePacket (BrailleDisplay *brl, const void *packet, size_t length) { return writeBrailleMessage(brl, NULL, 0, packet, length)? length: -1; } static void setState (BrailleDisplay *brl, BrailleDisplayState state) { if (state == brl->data->currentState) { ++brl->data->retryCount; } else { brl->data->retryCount = 0; brl->data->currentState = state; } startTimePeriod(&brl->data->statePeriod, 1000); // logMessage(LOG_DEBUG, "State: %d+%d", brl->data->currentState, brl->data->retryCount); } static int brl_reset (BrailleDisplay *brl) { static const unsigned char packet[] = {HT_PKT_Reset}; return writeBraillePacket(brl, NULL, packet, sizeof(packet)); } static int identifyModel (BrailleDisplay *brl, unsigned char identifier) { for ( brl->data->model = modelTable; brl->data->model->name && (brl->data->model->identifier != identifier); brl->data->model++ ); if (!brl->data->model->name) { logMessage(LOG_ERR, "Detected unknown HandyTech model with ID %02X.", identifier); return 0; } logMessage(LOG_INFO, "Detected %s: %d data %s, %d status %s.", brl->data->model->name, brl->data->model->textCells, (brl->data->model->textCells == 1)? "cell": "cells", brl->data->model->statusCells, (brl->data->model->statusCells == 1)? "cell": "cells"); brl->textColumns = brl->data->model->textCells; /* initialise size of display */ brl->textRows = 1; brl->statusColumns = brl->data->model->statusCells; brl->statusRows = 1; setBrailleKeyTable(brl, brl->data->model->keyTableDefinition); brl->setBrailleFirmness = brl->data->model->setBrailleFirmness; brl->setTouchSensitivity = brl->data->model->setTouchSensitivity; memset(brl->data->rawStatus, 0, brl->data->model->statusCells); memset(brl->data->rawData, 0, brl->data->model->textCells); brl->data->retryCount = 0; brl->data->updateRequired = 0; brl->data->currentState = BDS_OFF; setState(brl, BDS_READY); return 1; } static int writeExtendedPacket ( BrailleDisplay *brl, HT_ExtendedPacketType type, const unsigned char *data, unsigned char size ) { HT_Packet packet; packet.fields.type = HT_PKT_Extended; packet.fields.data.extended.model = brl->data->model->identifier; packet.fields.data.extended.length = size + 1; /* type byte is included */ packet.fields.data.extended.type = type; if (data) memcpy(packet.fields.data.extended.data.bytes, data, size); packet.fields.data.extended.data.bytes[size] = SYN; size += 5; /* EXT, ID, LEN, TYPE, ..., SYN */ return writeBrailleMessage(brl, NULL, type, &packet, size); } static int setAtcMode (BrailleDisplay *brl, unsigned char value) { const unsigned char data[] = {value}; return writeExtendedPacket(brl, HT_EXTPKT_SetAtcMode, data, sizeof(data)); } static int setBrailleFirmness (BrailleDisplay *brl, BrailleFirmness setting) { const unsigned char data[] = {setting * 2 / BRL_FIRMNESS_MAXIMUM}; return writeExtendedPacket(brl, HT_EXTPKT_SetFirmness, data, sizeof(data)); } static int setTouchSensitivity_Evolution (BrailleDisplay *brl, TouchSensitivity setting) { const unsigned char data[] = {0XFF - (setting * 0XF0 / BRL_SENSITIVITY_MAXIMUM)}; return writeExtendedPacket(brl, HT_EXTPKT_SetAtcSensitivity, data, sizeof(data)); } static int setTouchSensitivity_ActiveBraille (BrailleDisplay *brl, TouchSensitivity setting) { const unsigned char data[] = {setting * 6 / BRL_SENSITIVITY_MAXIMUM}; return writeExtendedPacket(brl, HT_EXTPKT_SetAtcSensitivity2, data, sizeof(data)); } typedef int (DateTimeProcessor) (BrailleDisplay *brl, const HT_DateTime *dateTime); static DateTimeProcessor *dateTimeProcessor = NULL; static int requestDateTime (BrailleDisplay *brl, DateTimeProcessor *processor) { int result = writeExtendedPacket(brl, HT_EXTPKT_GetRTC, NULL, 0); if (result) { dateTimeProcessor = processor; } return result; } static int logDateTime (BrailleDisplay *brl, const HT_DateTime *dateTime) { logMessage(LOG_INFO, "date and time of %s:" " %04" PRIu16 "-%02" PRIu8 "-%02" PRIu8 " %02" PRIu8 ":%02" PRIu8 ":%02" PRIu8, brl->data->model->name, getBigEndian16(dateTime->year), dateTime->month, dateTime->day, dateTime->hour, dateTime->minute, dateTime->second); return 1; } static int synchronizeDateTime (BrailleDisplay *brl, const HT_DateTime *dateTime) { long int delta; TimeValue hostTime; getCurrentTime(&hostTime); { TimeValue deviceTime; { TimeComponents components = { .year = getBigEndian16(dateTime->year), .month = dateTime->month - 1, .day = dateTime->day - 1, .hour = dateTime->hour, .minute = dateTime->minute, .second = dateTime->second }; makeTimeValue(&deviceTime, &components); } delta = millisecondsBetween(&hostTime, &deviceTime); if (delta < 0) delta = -delta; } if (delta > 1000) { TimeComponents components; HT_DateTime payload; expandTimeValue(&hostTime, &components); putLittleEndian16(&payload.year, components.year); payload.month = components.month + 1; payload.day = components.day + 1; payload.hour = components.hour; payload.minute = components.minute; payload.second = components.second; logMessage(LOG_DEBUG, "Time difference between host and device: %ld.%03ld", (delta / MSECS_PER_SEC), (delta % MSECS_PER_SEC)); if (writeExtendedPacket(brl, HT_EXTPKT_SetRTC, (unsigned char *)&payload, sizeof(payload))) { return requestDateTime(brl, logDateTime); } } return 1; } static int initializeSession (BrailleDisplay *brl) { const GeneralOperations *ops = gioGetApplicationData(brl->gioEndpoint); if (ops) { if (ops->initializeSession) { if (!ops->initializeSession(brl)) { return 0; } } } return 1; } static void setUsbConnectionProperties ( GioUsbConnectionProperties *properties, const UsbChannelDefinition *definition ) { if (definition->data) { const UsbOperations *usbOps = definition->data; properties->applicationData = usbOps->general; properties->writeData = usbOps->writeData; properties->readData = usbOps->readData; properties->awaitInput = usbOps->awaitInput; properties->inputFilter = usbOps->inputFilter; } } static int connectResource (BrailleDisplay *brl, const char *identifier) { static const SerialParameters serialParameters = { SERIAL_DEFAULT_PARAMETERS, .baud = 19200, .parity = SERIAL_PARITY_ODD }; BEGIN_USB_STRING_LIST(usbManufacturers_0403_6001) "FTDI", END_USB_STRING_LIST BEGIN_USB_CHANNEL_DEFINITIONS { /* GoHubs chip */ .vendor=0X0921, .product=0X1200, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .serial = &serialParameters }, { /* FTDI chip */ .vendor=0X0403, .product=0X6001, .manufacturers = usbManufacturers_0403_6001, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=2, .serial = &serialParameters }, { /* Easy Braille (HID) */ .vendor=0X1FE4, .product=0X0044, .configuration=1, .interface=0, .alternative=0, .data=&usbOperations2 }, { /* Braille Star 40 (HID) */ .vendor=0X1FE4, .product=0X0074, .configuration=1, .interface=0, .alternative=0, .data=&usbOperations2 }, { /* USB-HID adapter */ .vendor=0X1FE4, .product=0X0003, .configuration=1, .interface=0, .alternative=0, .data=&usbOperations2 }, { /* Active Braille */ .vendor=0X1FE4, .product=0X0054, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .data=&usbOperations3 }, { /* Basic Braille 16 */ .vendor=0X1FE4, .product=0X0081, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .data=&usbOperations3 }, { /* Basic Braille 20 */ .vendor=0X1FE4, .product=0X0082, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .data=&usbOperations3 }, { /* Basic Braille 32 */ .vendor=0X1FE4, .product=0X0083, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .data=&usbOperations3 }, { /* Basic Braille 40 */ .vendor=0X1FE4, .product=0X0084, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .data=&usbOperations3 }, { /* Basic Braille 48 */ .vendor=0X1FE4, .product=0X008A, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .data=&usbOperations3 }, { /* Basic Braille 64 */ .vendor=0X1FE4, .product=0X0086, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .data=&usbOperations3 }, { /* Basic Braille 80 */ .vendor=0X1FE4, .product=0X0087, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .data=&usbOperations3 }, { /* Basic Braille 160 */ .vendor=0X1FE4, .product=0X008B, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .data=&usbOperations3 }, { /* Actilino */ .vendor=0X1FE4, .product=0X0061, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .data=&usbOperations3 }, { /* Activator */ .vendor=0X1FE4, .product=0X00A4, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .data=&usbOperations3 }, { /* Active Star 40 */ .vendor=0X1FE4, .product=0X0064, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .data=&usbOperations3 }, { /* Connect Braille 40 */ .vendor=0X1FE4, .product=0X0055, .configuration=1, .interface=0, .alternative=0, .inputEndpoint=1, .outputEndpoint=1, .data=&usbOperations3 }, END_USB_CHANNEL_DEFINITIONS GioDescriptor descriptor; gioInitializeDescriptor(&descriptor); descriptor.serial.parameters = &serialParameters; descriptor.usb.channelDefinitions = usbChannelDefinitions; descriptor.usb.setConnectionProperties = setUsbConnectionProperties; descriptor.usb.options.inputTimeout = 100; descriptor.usb.options.requestTimeout = 100; descriptor.bluetooth.channelNumber = 1; if (connectBrailleResource(brl, identifier, &descriptor, initializeSession)) { return 1; } return 0; } static BrailleResponseResult isIdentityResponse (BrailleDisplay *brl, const void *packet, size_t size) { const HT_Packet *response = packet; return (response->fields.type == HT_PKT_OK)? 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 int setTime = 0; HT_Packet response; if (*parameters[PARM_SETTIME]) { if (!validateYesNo(&setTime, parameters[PARM_SETTIME])) { logMessage(LOG_WARNING, "%s: %s", "invalid set time setting", parameters[PARM_SETTIME]); } } setTime = !!setTime; if (probeBrailleDisplay(brl, 3, NULL, 100, brl_reset, readPacket, &response, sizeof(response), isIdentityResponse)) { if (identifyModel(brl, response.fields.data.ok.model)) { makeOutputTable(dotsTable_ISO11548_1); if (brl->data->model->hasATC) { setAtcMode(brl, 1); } if (setTime) { if (brl->data->model->hasTime) { requestDateTime(brl, synchronizeDateTime); } else { logMessage(LOG_INFO, "%s does not support setting the clock", brl->data->model->name); } } return 1; } } disconnectBrailleResource(brl, NULL); } free(brl->data); } else { logMallocError(); } return 0; } static void brl_destruct (BrailleDisplay *brl) { if (brl->data) { disconnectBrailleResource(brl, brl->data->model->sessionEnder); free(brl->data); brl->data = NULL; } if (hidInputReport) { free(hidInputReport); hidInputReport = NULL; } } static int writeCells (BrailleDisplay *brl) { return brl->data->model->writeCells(brl); } static int writeCells_statusAndText (BrailleDisplay *brl) { unsigned char buffer[1 + brl->data->model->statusCells + brl->data->model->textCells]; unsigned char *byte = buffer; *byte++ = HT_PKT_Braille; byte = mempcpy(byte, brl->data->rawStatus, brl->data->model->statusCells); byte = mempcpy(byte, brl->data->rawData, brl->data->model->textCells); return writeBrailleMessage(brl, NULL, HT_PKT_Braille, buffer, byte-buffer); } static int writeCells_Bookworm (BrailleDisplay *brl) { unsigned char buffer[1 + brl->data->model->statusCells + brl->data->model->textCells + 1]; buffer[0] = 0X01; memcpy(buffer+1, brl->data->rawData, brl->data->model->textCells); buffer[sizeof(buffer)-1] = SYN; return writeBrailleMessage(brl, NULL, 0X01, buffer, sizeof(buffer)); } static int writeCells_Evolution (BrailleDisplay *brl) { return writeExtendedPacket(brl, HT_EXTPKT_Braille, brl->data->rawData, brl->data->model->textCells); } static int updateCells (BrailleDisplay *brl) { if (!brl->data->updateRequired) return 1; if (brl->data->currentState != BDS_READY) return 1; if (!writeCells(brl)) return 0; brl->data->updateRequired = 0; return 1; } static int brl_writeWindow (BrailleDisplay *brl, const wchar_t *text) { const size_t cellCount = brl->data->model->textCells; if (cellsHaveChanged(brl->data->prevData, brl->buffer, cellCount, NULL, NULL, NULL)) { translateOutputCells(brl->data->rawData, brl->data->prevData, cellCount); brl->data->updateRequired = 1; } return updateCells(brl); } static int brl_writeStatus (BrailleDisplay *brl, const unsigned char *st) { const size_t cellCount = brl->data->model->statusCells; if (cellsHaveChanged(brl->data->prevStatus, st, cellCount, NULL, NULL, NULL)) { translateOutputCells(brl->data->rawStatus, brl->data->prevStatus, cellCount); brl->data->updateRequired = 1; } return 1; } static int interpretByte_key (BrailleDisplay *brl, unsigned char byte) { int release = (byte & HT_KEY_RELEASE) != 0; if (release) byte ^= HT_KEY_RELEASE; if ((byte >= HT_KEY_ROUTING) && (byte < (HT_KEY_ROUTING + brl->data->model->textCells))) { return enqueueKeyEvent(brl, HT_GRP_RoutingKeys, byte - HT_KEY_ROUTING, !release); } if ((byte >= HT_KEY_STATUS) && (byte < (HT_KEY_STATUS + brl->data->model->statusCells))) { return enqueueKeyEvent(brl, HT_GRP_NavigationKeys, byte, !release); } if (byte > 0) { return enqueueKeyEvent(brl, HT_GRP_NavigationKeys, byte, !release); } return 0; } static int interpretByte_Bookworm (BrailleDisplay *brl, unsigned char byte) { static const KeyNumber keys[] = { HT_BWK_Backward, HT_BWK_Forward, HT_BWK_Escape, HT_BWK_Enter, 0 }; const KeyNumber *key = keys; const KeyGroup group = HT_GRP_NavigationKeys; if (!byte) return 0; { unsigned char bits = byte; while (*key) bits &= ~*key++; if (bits) return 0; key = keys; } while (*key) { if ((byte & *key) && !enqueueKeyEvent(brl, group, *key, 1)) return 0; key += 1; } do { key -= 1; if ((byte & *key) && !enqueueKeyEvent(brl, group, *key, 0)) return 0; } while (key != keys); return 1; } static int brl_readCommand (BrailleDisplay *brl, KeyTableCommandContext context) { while (1) { HT_Packet packet; size_t size = readPacket(brl, &packet, sizeof(packet)); if (size == 0) { if (errno != EAGAIN) return BRL_CMD_RESTARTBRL; break; } /* a kludge to handle the Bookworm going offline */ if (brl->data->model->identifier == HT_MODEL_Bookworm) { if (packet.fields.type == 0X06) { if (brl->data->currentState != BDS_OFF) { /* if we get another byte right away then the device * has gone offline and is echoing its display */ if (awaitBrailleInput(brl, 10)) { setState(brl, BDS_OFF); continue; } /* if an input error occurred then restart the driver */ if (errno != EAGAIN) return BRL_CMD_RESTARTBRL; /* no additional input so fall through and interpret the packet as keys */ } } } switch (packet.fields.type) { case HT_PKT_OK: if (packet.fields.data.ok.model == brl->data->model->identifier) { releaseBrailleKeys(brl); brl->data->updateRequired = 1; continue; } break; default: switch (brl->data->currentState) { case BDS_OFF: continue; case BDS_READY: switch (packet.fields.type) { case HT_PKT_NAK: brl->data->updateRequired = 1; case HT_PKT_ACK: acknowledgeBrailleMessage(brl); continue; case HT_PKT_Extended: { unsigned char length = packet.fields.data.extended.length - 1; const unsigned char *bytes = &packet.fields.data.extended.data.bytes[0]; switch (packet.fields.data.extended.type) { case HT_EXTPKT_Confirmation: switch (bytes[0]) { case HT_PKT_NAK: brl->data->updateRequired = 1; case HT_PKT_ACK: acknowledgeBrailleMessage(brl); continue; default: break; } break; case HT_EXTPKT_Key: if (brl->data->model->interpretByte(brl, bytes[0])) { updateCells(brl); return EOF; } break; case HT_EXTPKT_Scancode: { while (length--) enqueueCommand(BRL_CMD_BLK(PASSAT) | BRL_ARG_PUT(*bytes++)); continue; } case HT_EXTPKT_GetRTC: { const HT_DateTime *const payload = (HT_DateTime *)bytes; DateTimeProcessor *processor = dateTimeProcessor; dateTimeProcessor = NULL; if (processor) { if (!processor(brl, payload)) { break; } } continue; } case HT_EXTPKT_AtcInfo: { unsigned int readingPosition = BRL_MSK_ARG; unsigned int highestPressure = 0; if (bytes[0]) { const unsigned int cellCount = brl->data->model->textCells + brl->data->model->statusCells; unsigned int cellIndex = bytes[0] - 1; unsigned int dataIndex; for (dataIndex=1; dataIndex> 4, LOW_NIBBLE(byte) }; const unsigned int pressureCount = ARRAY_COUNT(pressures); unsigned int pressureIndex; for (pressureIndex=0; pressureIndex highestPressure) { highestPressure = pressure; readingPosition = cellIndex; } cellIndex += 1; } } if (readingPosition >= cellCount) readingPosition = BRL_MSK_ARG; } enqueueCommand(BRL_CMD_BLK(TOUCH_AT) | readingPosition); continue; } case HT_EXTPKT_ReadingPosition: { const unsigned int cellCount = brl->data->model->textCells + brl->data->model->statusCells; unsigned int readingPosition = bytes[0]; if ((readingPosition == 0XFF) || (readingPosition >= cellCount)) { readingPosition = BRL_MSK_ARG; } enqueueCommand(BRL_CMD_BLK(TOUCH_AT) | readingPosition); continue; } default: break; } break; } default: if (brl->data->model->interpretByte(brl, packet.fields.type)) { updateCells(brl); return EOF; } break; } break; } break; } logUnexpectedPacket(packet.bytes, size); logMessage(LOG_WARNING, "state %d", brl->data->currentState); } updateCells(brl); return EOF; }