|  | /* | 
|  | * QEMU Baum Braille Device | 
|  | * | 
|  | * Copyright (c) 2008, 2010-2011, 2016-2017 Samuel Thibault | 
|  | * | 
|  | * Permission is hereby granted, free of charge, to any person obtaining a copy | 
|  | * of this software and associated documentation files (the "Software"), to deal | 
|  | * in the Software without restriction, including without limitation the rights | 
|  | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
|  | * copies of the Software, and to permit persons to whom the Software is | 
|  | * furnished to do so, subject to the following conditions: | 
|  | * | 
|  | * The above copyright notice and this permission notice shall be included in | 
|  | * all copies or substantial portions of the Software. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|  | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
|  | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
|  | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
|  | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
|  | * THE SOFTWARE. | 
|  | */ | 
|  |  | 
|  | #include "qemu/osdep.h" | 
|  | #include "qapi/error.h" | 
|  | #include "chardev/char.h" | 
|  | #include "qemu/main-loop.h" | 
|  | #include "qemu/module.h" | 
|  | #include "qemu/timer.h" | 
|  | #include "hw/usb.h" | 
|  | #include "ui/console.h" | 
|  | #include <brlapi.h> | 
|  | #include <brlapi_constants.h> | 
|  | #include <brlapi_keycodes.h> | 
|  | #include "qom/object.h" | 
|  |  | 
|  | #if 0 | 
|  | #define DPRINTF(fmt, ...) \ | 
|  | printf(fmt, ## __VA_ARGS__) | 
|  | #else | 
|  | #define DPRINTF(fmt, ...) | 
|  | #endif | 
|  |  | 
|  | #define ESC 0x1B | 
|  |  | 
|  | #define BAUM_REQ_DisplayData            0x01 | 
|  | #define BAUM_REQ_GetVersionNumber       0x05 | 
|  | #define BAUM_REQ_GetKeys                0x08 | 
|  | #define BAUM_REQ_SetMode                0x12 | 
|  | #define BAUM_REQ_SetProtocol            0x15 | 
|  | #define BAUM_REQ_GetDeviceIdentity      0x84 | 
|  | #define BAUM_REQ_GetSerialNumber        0x8A | 
|  |  | 
|  | #define BAUM_RSP_CellCount              0x01 | 
|  | #define BAUM_RSP_VersionNumber          0x05 | 
|  | #define BAUM_RSP_ModeSetting            0x11 | 
|  | #define BAUM_RSP_CommunicationChannel   0x16 | 
|  | #define BAUM_RSP_PowerdownSignal        0x17 | 
|  | #define BAUM_RSP_HorizontalSensors      0x20 | 
|  | #define BAUM_RSP_VerticalSensors        0x21 | 
|  | #define BAUM_RSP_RoutingKeys            0x22 | 
|  | #define BAUM_RSP_Switches               0x23 | 
|  | #define BAUM_RSP_TopKeys                0x24 | 
|  | #define BAUM_RSP_HorizontalSensor       0x25 | 
|  | #define BAUM_RSP_VerticalSensor         0x26 | 
|  | #define BAUM_RSP_RoutingKey             0x27 | 
|  | #define BAUM_RSP_FrontKeys6             0x28 | 
|  | #define BAUM_RSP_BackKeys6              0x29 | 
|  | #define BAUM_RSP_CommandKeys            0x2B | 
|  | #define BAUM_RSP_FrontKeys10            0x2C | 
|  | #define BAUM_RSP_BackKeys10             0x2D | 
|  | #define BAUM_RSP_EntryKeys              0x33 | 
|  | #define BAUM_RSP_JoyStick               0x34 | 
|  | #define BAUM_RSP_ErrorCode              0x40 | 
|  | #define BAUM_RSP_InfoBlock              0x42 | 
|  | #define BAUM_RSP_DeviceIdentity         0x84 | 
|  | #define BAUM_RSP_SerialNumber           0x8A | 
|  | #define BAUM_RSP_BluetoothName          0x8C | 
|  |  | 
|  | #define BAUM_TL1 0x01 | 
|  | #define BAUM_TL2 0x02 | 
|  | #define BAUM_TL3 0x04 | 
|  | #define BAUM_TR1 0x08 | 
|  | #define BAUM_TR2 0x10 | 
|  | #define BAUM_TR3 0x20 | 
|  |  | 
|  | #define BUF_SIZE 256 | 
|  |  | 
|  | #define X_MAX   84 | 
|  | #define Y_MAX   1 | 
|  |  | 
|  | struct BaumChardev { | 
|  | Chardev parent; | 
|  |  | 
|  | brlapi_handle_t *brlapi; | 
|  | int brlapi_fd; | 
|  | unsigned int x, y; | 
|  | bool deferred_init; | 
|  |  | 
|  | uint8_t in_buf[BUF_SIZE]; | 
|  | uint8_t in_buf_used; | 
|  | uint8_t out_buf[BUF_SIZE]; | 
|  | uint8_t out_buf_used, out_buf_ptr; | 
|  |  | 
|  | QEMUTimer *cellCount_timer; | 
|  | }; | 
|  | typedef struct BaumChardev BaumChardev; | 
|  |  | 
|  | #define TYPE_CHARDEV_BRAILLE "chardev-braille" | 
|  | DECLARE_INSTANCE_CHECKER(BaumChardev, BAUM_CHARDEV, | 
|  | TYPE_CHARDEV_BRAILLE) | 
|  |  | 
|  | /* Let's assume NABCC by default */ | 
|  | enum way { | 
|  | DOTS2ASCII, | 
|  | ASCII2DOTS | 
|  | }; | 
|  | static const uint8_t nabcc_translation[2][256] = { | 
|  | #ifndef BRLAPI_DOTS | 
|  | #define BRLAPI_DOTS(d1,d2,d3,d4,d5,d6,d7,d8) \ | 
|  | ((d1?BRLAPI_DOT1:0)|\ | 
|  | (d2?BRLAPI_DOT2:0)|\ | 
|  | (d3?BRLAPI_DOT3:0)|\ | 
|  | (d4?BRLAPI_DOT4:0)|\ | 
|  | (d5?BRLAPI_DOT5:0)|\ | 
|  | (d6?BRLAPI_DOT6:0)|\ | 
|  | (d7?BRLAPI_DOT7:0)|\ | 
|  | (d8?BRLAPI_DOT8:0)) | 
|  | #endif | 
|  | #define DO(dots, ascii) \ | 
|  | [DOTS2ASCII][dots] = ascii, \ | 
|  | [ASCII2DOTS][ascii] = dots | 
|  | DO(0, ' '), | 
|  | DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 0, 0, 0), 'a'), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 0, 0, 0), 'b'), | 
|  | DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 0, 0, 0), 'c'), | 
|  | DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 0, 0, 0), 'd'), | 
|  | DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 0, 0, 0), 'e'), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 0, 0, 0), 'f'), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 0, 0, 0), 'g'), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 0, 0, 0), 'h'), | 
|  | DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 0, 0, 0), 'i'), | 
|  | DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 0, 0, 0), 'j'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 0, 0, 0), 'k'), | 
|  | DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 0, 0, 0), 'l'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 0, 0, 0), 'm'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 0, 0, 0), 'n'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 0, 0, 0), 'o'), | 
|  | DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 0, 0, 0), 'p'), | 
|  | DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 0, 0, 0), 'q'), | 
|  | DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 0, 0, 0), 'r'), | 
|  | DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 0, 0, 0), 's'), | 
|  | DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 0, 0, 0), 't'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 1, 0, 0), 'u'), | 
|  | DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 1, 0, 0), 'v'), | 
|  | DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 1, 0, 0), 'w'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 1, 0, 0), 'x'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 1, 0, 0), 'y'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 1, 0, 0), 'z'), | 
|  |  | 
|  | DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 0, 1, 0), 'A'), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 0, 1, 0), 'B'), | 
|  | DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 0, 1, 0), 'C'), | 
|  | DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 0, 1, 0), 'D'), | 
|  | DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 0, 1, 0), 'E'), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 0, 1, 0), 'F'), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 0, 1, 0), 'G'), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 0, 1, 0), 'H'), | 
|  | DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 0, 1, 0), 'I'), | 
|  | DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 0, 1, 0), 'J'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 0, 1, 0), 'K'), | 
|  | DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 0, 1, 0), 'L'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 0, 1, 0), 'M'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 0, 1, 0), 'N'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 0, 1, 0), 'O'), | 
|  | DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 0, 1, 0), 'P'), | 
|  | DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 0, 1, 0), 'Q'), | 
|  | DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 0, 1, 0), 'R'), | 
|  | DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 0, 1, 0), 'S'), | 
|  | DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 0, 1, 0), 'T'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 1, 1, 0), 'U'), | 
|  | DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 1, 1, 0), 'V'), | 
|  | DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 1, 1, 0), 'W'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 1, 1, 0), 'X'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 1, 1, 0), 'Y'), | 
|  | DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 1, 1, 0), 'Z'), | 
|  |  | 
|  | DO(BRLAPI_DOTS(0, 0, 1, 0, 1, 1, 0, 0), '0'), | 
|  | DO(BRLAPI_DOTS(0, 1, 0, 0, 0, 0, 0, 0), '1'), | 
|  | DO(BRLAPI_DOTS(0, 1, 1, 0, 0, 0, 0, 0), '2'), | 
|  | DO(BRLAPI_DOTS(0, 1, 0, 0, 1, 0, 0, 0), '3'), | 
|  | DO(BRLAPI_DOTS(0, 1, 0, 0, 1, 1, 0, 0), '4'), | 
|  | DO(BRLAPI_DOTS(0, 1, 0, 0, 0, 1, 0, 0), '5'), | 
|  | DO(BRLAPI_DOTS(0, 1, 1, 0, 1, 0, 0, 0), '6'), | 
|  | DO(BRLAPI_DOTS(0, 1, 1, 0, 1, 1, 0, 0), '7'), | 
|  | DO(BRLAPI_DOTS(0, 1, 1, 0, 0, 1, 0, 0), '8'), | 
|  | DO(BRLAPI_DOTS(0, 0, 1, 0, 1, 0, 0, 0), '9'), | 
|  |  | 
|  | DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 1, 0, 0), '.'), | 
|  | DO(BRLAPI_DOTS(0, 0, 1, 1, 0, 1, 0, 0), '+'), | 
|  | DO(BRLAPI_DOTS(0, 0, 1, 0, 0, 1, 0, 0), '-'), | 
|  | DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 1, 0, 0), '*'), | 
|  | DO(BRLAPI_DOTS(0, 0, 1, 1, 0, 0, 0, 0), '/'), | 
|  | DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 1, 0, 0), '('), | 
|  | DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 1, 0, 0), ')'), | 
|  |  | 
|  | DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 1, 0, 0), '&'), | 
|  | DO(BRLAPI_DOTS(0, 0, 1, 1, 1, 1, 0, 0), '#'), | 
|  |  | 
|  | DO(BRLAPI_DOTS(0, 0, 0, 0, 0, 1, 0, 0), ','), | 
|  | DO(BRLAPI_DOTS(0, 0, 0, 0, 1, 1, 0, 0), ';'), | 
|  | DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 1, 0, 0), ':'), | 
|  | DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 1, 0, 0), '!'), | 
|  | DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 1, 0, 0), '?'), | 
|  | DO(BRLAPI_DOTS(0, 0, 0, 0, 1, 0, 0, 0), '"'), | 
|  | DO(BRLAPI_DOTS(0, 0, 1, 0, 0, 0, 0, 0), '\''), | 
|  | DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 0, 0, 0), '`'), | 
|  | DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 0, 1, 0), '^'), | 
|  | DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 0, 0, 0), '~'), | 
|  | DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 1, 1, 0), '['), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 1, 1, 0), ']'), | 
|  | DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 1, 0, 0), '{'), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 1, 0, 0), '}'), | 
|  | DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 1, 0, 0), '='), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 1, 0, 0), '<'), | 
|  | DO(BRLAPI_DOTS(0, 0, 1, 1, 1, 0, 0, 0), '>'), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 1, 0, 0), '$'), | 
|  | DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 1, 0, 0), '%'), | 
|  | DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 0, 1, 0), '@'), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 1, 0, 0), '|'), | 
|  | DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 1, 1, 0), '\\'), | 
|  | DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 1, 0, 0), '_'), | 
|  | }; | 
|  |  | 
|  | /* The guest OS has started discussing with us, finish initializing BrlAPI */ | 
|  | static int baum_deferred_init(BaumChardev *baum) | 
|  | { | 
|  | int tty = BRLAPI_TTY_DEFAULT; | 
|  | QemuConsole *con; | 
|  |  | 
|  | if (baum->deferred_init) { | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if (brlapi__getDisplaySize(baum->brlapi, &baum->x, &baum->y) == -1) { | 
|  | brlapi_perror("baum: brlapi__getDisplaySize"); | 
|  | return 0; | 
|  | } | 
|  | if (baum->y > Y_MAX) { | 
|  | baum->y = Y_MAX; | 
|  | } | 
|  | if (baum->x > X_MAX) { | 
|  | baum->x = X_MAX; | 
|  | } | 
|  |  | 
|  | con = qemu_console_lookup_by_index(0); | 
|  | if (con && qemu_console_is_graphic(con)) { | 
|  | tty = qemu_console_get_window_id(con); | 
|  | if (tty == -1) | 
|  | tty = BRLAPI_TTY_DEFAULT; | 
|  | } | 
|  |  | 
|  | if (brlapi__enterTtyMode(baum->brlapi, tty, NULL) == -1) { | 
|  | brlapi_perror("baum: brlapi__enterTtyMode"); | 
|  | return 0; | 
|  | } | 
|  | baum->deferred_init = 1; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* The serial port can receive more of our data */ | 
|  | static void baum_chr_accept_input(struct Chardev *chr) | 
|  | { | 
|  | BaumChardev *baum = BAUM_CHARDEV(chr); | 
|  | int room, first; | 
|  |  | 
|  | if (!baum->out_buf_used) | 
|  | return; | 
|  | room = qemu_chr_be_can_write(chr); | 
|  | if (!room) | 
|  | return; | 
|  | if (room > baum->out_buf_used) | 
|  | room = baum->out_buf_used; | 
|  |  | 
|  | first = BUF_SIZE - baum->out_buf_ptr; | 
|  | if (room > first) { | 
|  | qemu_chr_be_write(chr, baum->out_buf + baum->out_buf_ptr, first); | 
|  | baum->out_buf_ptr = 0; | 
|  | baum->out_buf_used -= first; | 
|  | room -= first; | 
|  | } | 
|  | qemu_chr_be_write(chr, baum->out_buf + baum->out_buf_ptr, room); | 
|  | baum->out_buf_ptr += room; | 
|  | baum->out_buf_used -= room; | 
|  | } | 
|  |  | 
|  | /* We want to send a packet */ | 
|  | static void baum_write_packet(BaumChardev *baum, const uint8_t *buf, int len) | 
|  | { | 
|  | Chardev *chr = CHARDEV(baum); | 
|  | g_autofree uint8_t *io_buf = g_malloc(1 + 2 * len); | 
|  | uint8_t *cur = io_buf; | 
|  | int room; | 
|  | *cur++ = ESC; | 
|  | while (len--) | 
|  | if ((*cur++ = *buf++) == ESC) | 
|  | *cur++ = ESC; | 
|  | room = qemu_chr_be_can_write(chr); | 
|  | len = cur - io_buf; | 
|  | if (len <= room) { | 
|  | /* Fits */ | 
|  | qemu_chr_be_write(chr, io_buf, len); | 
|  | } else { | 
|  | int first; | 
|  | uint8_t out; | 
|  | /* Can't fit all, send what can be, and store the rest. */ | 
|  | qemu_chr_be_write(chr, io_buf, room); | 
|  | len -= room; | 
|  | cur = io_buf + room; | 
|  | if (len > BUF_SIZE - baum->out_buf_used) { | 
|  | /* Can't even store it, drop the previous data... */ | 
|  | assert(len <= BUF_SIZE); | 
|  | baum->out_buf_used = 0; | 
|  | baum->out_buf_ptr = 0; | 
|  | } | 
|  | out = baum->out_buf_ptr; | 
|  | baum->out_buf_used += len; | 
|  | first = BUF_SIZE - baum->out_buf_ptr; | 
|  | if (len > first) { | 
|  | memcpy(baum->out_buf + out, cur, first); | 
|  | out = 0; | 
|  | len -= first; | 
|  | cur += first; | 
|  | } | 
|  | memcpy(baum->out_buf + out, cur, len); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Called when the other end seems to have a wrong idea of our display size */ | 
|  | static void baum_cellCount_timer_cb(void *opaque) | 
|  | { | 
|  | BaumChardev *baum = BAUM_CHARDEV(opaque); | 
|  | uint8_t cell_count[] = { BAUM_RSP_CellCount, baum->x * baum->y }; | 
|  | DPRINTF("Timeout waiting for DisplayData, sending cell count\n"); | 
|  | baum_write_packet(baum, cell_count, sizeof(cell_count)); | 
|  | } | 
|  |  | 
|  | /* Try to interpret a whole incoming packet */ | 
|  | static int baum_eat_packet(BaumChardev *baum, const uint8_t *buf, int len) | 
|  | { | 
|  | const uint8_t *cur = buf; | 
|  | uint8_t req = 0; | 
|  |  | 
|  | if (!len--) | 
|  | return 0; | 
|  | if (*cur++ != ESC) { | 
|  | while (*cur != ESC) { | 
|  | if (!len--) | 
|  | return 0; | 
|  | cur++; | 
|  | } | 
|  | DPRINTF("Dropped %td bytes!\n", cur - buf); | 
|  | } | 
|  |  | 
|  | #define EAT(c) do {\ | 
|  | if (!len--) \ | 
|  | return 0; \ | 
|  | if ((c = *cur++) == ESC) { \ | 
|  | if (!len--) \ | 
|  | return 0; \ | 
|  | if (*cur++ != ESC) { \ | 
|  | DPRINTF("Broken packet %#2x, tossing\n", req); \ | 
|  | if (timer_pending(baum->cellCount_timer)) {    \ | 
|  | timer_del(baum->cellCount_timer);     \ | 
|  | baum_cellCount_timer_cb(baum);             \ | 
|  | } \ | 
|  | return (cur - 2 - buf); \ | 
|  | } \ | 
|  | } \ | 
|  | } while (0) | 
|  |  | 
|  | EAT(req); | 
|  | switch (req) { | 
|  | case BAUM_REQ_DisplayData: | 
|  | { | 
|  | uint8_t cells[X_MAX * Y_MAX], c; | 
|  | uint8_t text[X_MAX * Y_MAX]; | 
|  | uint8_t zero[X_MAX * Y_MAX]; | 
|  | int cursor = BRLAPI_CURSOR_OFF; | 
|  | int i; | 
|  |  | 
|  | /* Allow 100ms to complete the DisplayData packet */ | 
|  | timer_mod(baum->cellCount_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + | 
|  | NANOSECONDS_PER_SECOND / 10); | 
|  | for (i = 0; i < baum->x * baum->y ; i++) { | 
|  | EAT(c); | 
|  | cells[i] = c; | 
|  | if ((c & (BRLAPI_DOT7|BRLAPI_DOT8)) | 
|  | == (BRLAPI_DOT7|BRLAPI_DOT8)) { | 
|  | cursor = i + 1; | 
|  | c &= ~(BRLAPI_DOT7|BRLAPI_DOT8); | 
|  | } | 
|  | c = nabcc_translation[DOTS2ASCII][c]; | 
|  | if (!c) { | 
|  | c = '?'; | 
|  | } | 
|  | text[i] = c; | 
|  | } | 
|  | timer_del(baum->cellCount_timer); | 
|  |  | 
|  | memset(zero, 0, baum->x * baum->y); | 
|  |  | 
|  | brlapi_writeArguments_t wa = { | 
|  | .displayNumber = BRLAPI_DISPLAY_DEFAULT, | 
|  | .regionBegin = 1, | 
|  | .regionSize = baum->x * baum->y, | 
|  | .text = (char *)text, | 
|  | .textSize = baum->x * baum->y, | 
|  | .andMask = zero, | 
|  | .orMask = cells, | 
|  | .cursor = cursor, | 
|  | .charset = (char *)"ISO-8859-1", | 
|  | }; | 
|  |  | 
|  | if (brlapi__write(baum->brlapi, &wa) == -1) | 
|  | brlapi_perror("baum brlapi_write"); | 
|  | break; | 
|  | } | 
|  | case BAUM_REQ_SetMode: | 
|  | { | 
|  | uint8_t mode, setting; | 
|  | DPRINTF("SetMode\n"); | 
|  | EAT(mode); | 
|  | EAT(setting); | 
|  | /* ignore */ | 
|  | break; | 
|  | } | 
|  | case BAUM_REQ_SetProtocol: | 
|  | { | 
|  | uint8_t protocol; | 
|  | DPRINTF("SetProtocol\n"); | 
|  | EAT(protocol); | 
|  | /* ignore */ | 
|  | break; | 
|  | } | 
|  | case BAUM_REQ_GetDeviceIdentity: | 
|  | { | 
|  | uint8_t identity[17] = { BAUM_RSP_DeviceIdentity, | 
|  | 'B','a','u','m',' ','V','a','r','i','o' }; | 
|  | DPRINTF("GetDeviceIdentity\n"); | 
|  | identity[11] = '0' + baum->x / 10; | 
|  | identity[12] = '0' + baum->x % 10; | 
|  | baum_write_packet(baum, identity, sizeof(identity)); | 
|  | break; | 
|  | } | 
|  | case BAUM_REQ_GetVersionNumber: | 
|  | { | 
|  | uint8_t version[] = { BAUM_RSP_VersionNumber, 1 }; /* ? */ | 
|  | DPRINTF("GetVersionNumber\n"); | 
|  | baum_write_packet(baum, version, sizeof(version)); | 
|  | break; | 
|  | } | 
|  | case BAUM_REQ_GetSerialNumber: | 
|  | { | 
|  | uint8_t serial[] = { BAUM_RSP_SerialNumber, | 
|  | '0','0','0','0','0','0','0','0' }; | 
|  | DPRINTF("GetSerialNumber\n"); | 
|  | baum_write_packet(baum, serial, sizeof(serial)); | 
|  | break; | 
|  | } | 
|  | case BAUM_REQ_GetKeys: | 
|  | { | 
|  | DPRINTF("Get%0#2x\n", req); | 
|  | /* ignore */ | 
|  | break; | 
|  | } | 
|  | default: | 
|  | DPRINTF("unrecognized request %0#2x\n", req); | 
|  | do | 
|  | if (!len--) | 
|  | return 0; | 
|  | while (*cur++ != ESC); | 
|  | cur--; | 
|  | break; | 
|  | } | 
|  | return cur - buf; | 
|  | } | 
|  |  | 
|  | /* The other end is writing some data.  Store it and try to interpret */ | 
|  | static int baum_chr_write(Chardev *chr, const uint8_t *buf, int len) | 
|  | { | 
|  | BaumChardev *baum = BAUM_CHARDEV(chr); | 
|  | int tocopy, cur, eaten, orig_len = len; | 
|  |  | 
|  | if (!len) | 
|  | return 0; | 
|  | if (!baum->brlapi) | 
|  | return len; | 
|  | if (!baum_deferred_init(baum)) | 
|  | return len; | 
|  |  | 
|  | while (len) { | 
|  | /* Complete our buffer as much as possible */ | 
|  | tocopy = len; | 
|  | if (tocopy > BUF_SIZE - baum->in_buf_used) | 
|  | tocopy = BUF_SIZE - baum->in_buf_used; | 
|  |  | 
|  | memcpy(baum->in_buf + baum->in_buf_used, buf, tocopy); | 
|  | baum->in_buf_used += tocopy; | 
|  | buf += tocopy; | 
|  | len -= tocopy; | 
|  |  | 
|  | /* Interpret it as much as possible */ | 
|  | cur = 0; | 
|  | while (cur < baum->in_buf_used && | 
|  | (eaten = baum_eat_packet(baum, baum->in_buf + cur, baum->in_buf_used - cur))) | 
|  | cur += eaten; | 
|  |  | 
|  | /* Shift the remainder */ | 
|  | if (cur) { | 
|  | memmove(baum->in_buf, baum->in_buf + cur, baum->in_buf_used - cur); | 
|  | baum->in_buf_used -= cur; | 
|  | } | 
|  |  | 
|  | /* And continue if any data left */ | 
|  | } | 
|  | return orig_len; | 
|  | } | 
|  |  | 
|  | /* Send the key code to the other end */ | 
|  | static void baum_send_key(BaumChardev *baum, uint8_t type, uint8_t value) | 
|  | { | 
|  | uint8_t packet[] = { type, value }; | 
|  | DPRINTF("writing key %x %x\n", type, value); | 
|  | baum_write_packet(baum, packet, sizeof(packet)); | 
|  | } | 
|  |  | 
|  | static void baum_send_key2(BaumChardev *baum, uint8_t type, uint8_t value, | 
|  | uint8_t value2) | 
|  | { | 
|  | uint8_t packet[] = { type, value, value2 }; | 
|  | DPRINTF("writing key %x %x\n", type, value); | 
|  | baum_write_packet(baum, packet, sizeof(packet)); | 
|  | } | 
|  |  | 
|  | /* We got some data on the BrlAPI socket */ | 
|  | static void baum_chr_read(void *opaque) | 
|  | { | 
|  | BaumChardev *baum = BAUM_CHARDEV(opaque); | 
|  | brlapi_keyCode_t code; | 
|  | int ret; | 
|  | if (!baum->brlapi) | 
|  | return; | 
|  | if (!baum_deferred_init(baum)) | 
|  | return; | 
|  | while ((ret = brlapi__readKey(baum->brlapi, 0, &code)) == 1) { | 
|  | DPRINTF("got key %"BRLAPI_PRIxKEYCODE"\n", code); | 
|  | /* Emulate */ | 
|  | switch (code & BRLAPI_KEY_TYPE_MASK) { | 
|  | case BRLAPI_KEY_TYPE_CMD: | 
|  | switch (code & BRLAPI_KEY_CMD_BLK_MASK) { | 
|  | case BRLAPI_KEY_CMD_ROUTE: | 
|  | baum_send_key(baum, BAUM_RSP_RoutingKey, (code & BRLAPI_KEY_CMD_ARG_MASK)+1); | 
|  | baum_send_key(baum, BAUM_RSP_RoutingKey, 0); | 
|  | break; | 
|  | case 0: | 
|  | switch (code & BRLAPI_KEY_CMD_ARG_MASK) { | 
|  | case BRLAPI_KEY_CMD_FWINLT: | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2); | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, 0); | 
|  | break; | 
|  | case BRLAPI_KEY_CMD_FWINRT: | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR2); | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, 0); | 
|  | break; | 
|  | case BRLAPI_KEY_CMD_LNUP: | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR1); | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, 0); | 
|  | break; | 
|  | case BRLAPI_KEY_CMD_LNDN: | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR3); | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, 0); | 
|  | break; | 
|  | case BRLAPI_KEY_CMD_TOP: | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL1|BAUM_TR1); | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, 0); | 
|  | break; | 
|  | case BRLAPI_KEY_CMD_BOT: | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL3|BAUM_TR3); | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, 0); | 
|  | break; | 
|  | case BRLAPI_KEY_CMD_TOP_LEFT: | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR1); | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, 0); | 
|  | break; | 
|  | case BRLAPI_KEY_CMD_BOT_LEFT: | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR3); | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, 0); | 
|  | break; | 
|  | case BRLAPI_KEY_CMD_HOME: | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR1|BAUM_TR3); | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, 0); | 
|  | break; | 
|  | case BRLAPI_KEY_CMD_PREFMENU: | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL1|BAUM_TL3|BAUM_TR1); | 
|  | baum_send_key(baum, BAUM_RSP_TopKeys, 0); | 
|  | break; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case BRLAPI_KEY_TYPE_SYM: | 
|  | { | 
|  | brlapi_keyCode_t keysym = code & BRLAPI_KEY_CODE_MASK; | 
|  | if (keysym < 0x100) { | 
|  | uint8_t dots = nabcc_translation[ASCII2DOTS][keysym]; | 
|  | if (dots) { | 
|  | baum_send_key2(baum, BAUM_RSP_EntryKeys, 0, dots); | 
|  | baum_send_key2(baum, BAUM_RSP_EntryKeys, 0, 0); | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (ret == -1 && (brlapi_errno != BRLAPI_ERROR_LIBCERR || errno != EINTR)) { | 
|  | brlapi_perror("baum: brlapi_readKey"); | 
|  | brlapi__closeConnection(baum->brlapi); | 
|  | g_free(baum->brlapi); | 
|  | baum->brlapi = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void char_braille_finalize(Object *obj) | 
|  | { | 
|  | BaumChardev *baum = BAUM_CHARDEV(obj); | 
|  |  | 
|  | timer_free(baum->cellCount_timer); | 
|  | if (baum->brlapi) { | 
|  | brlapi__closeConnection(baum->brlapi); | 
|  | g_free(baum->brlapi); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void baum_chr_open(Chardev *chr, | 
|  | ChardevBackend *backend, | 
|  | bool *be_opened, | 
|  | Error **errp) | 
|  | { | 
|  | BaumChardev *baum = BAUM_CHARDEV(chr); | 
|  | brlapi_handle_t *handle; | 
|  |  | 
|  | handle = g_malloc0(brlapi_getHandleSize()); | 
|  | baum->brlapi = handle; | 
|  |  | 
|  | baum->brlapi_fd = brlapi__openConnection(handle, NULL, NULL); | 
|  | if (baum->brlapi_fd == -1) { | 
|  | error_setg(errp, "brlapi__openConnection: %s", | 
|  | brlapi_strerror(brlapi_error_location())); | 
|  | g_free(handle); | 
|  | baum->brlapi = NULL; | 
|  | return; | 
|  | } | 
|  | baum->deferred_init = 0; | 
|  |  | 
|  | baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum); | 
|  |  | 
|  | qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum); | 
|  | } | 
|  |  | 
|  | static void char_braille_class_init(ObjectClass *oc, void *data) | 
|  | { | 
|  | ChardevClass *cc = CHARDEV_CLASS(oc); | 
|  |  | 
|  | cc->open = baum_chr_open; | 
|  | cc->chr_write = baum_chr_write; | 
|  | cc->chr_accept_input = baum_chr_accept_input; | 
|  | } | 
|  |  | 
|  | static const TypeInfo char_braille_type_info = { | 
|  | .name = TYPE_CHARDEV_BRAILLE, | 
|  | .parent = TYPE_CHARDEV, | 
|  | .instance_size = sizeof(BaumChardev), | 
|  | .instance_finalize = char_braille_finalize, | 
|  | .class_init = char_braille_class_init, | 
|  | }; | 
|  | module_obj(TYPE_CHARDEV_BRAILLE); | 
|  |  | 
|  | static void register_types(void) | 
|  | { | 
|  | type_register_static(&char_braille_type_info); | 
|  | } | 
|  |  | 
|  | type_init(register_types); |