qemu/chardev/baum.c
<<
>>
Prefs
   1/*
   2 * QEMU Baum Braille Device
   3 *
   4 * Copyright (c) 2008, 2010-2011, 2016-2017 Samuel Thibault
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25#include "qemu/osdep.h"
  26#include "qapi/error.h"
  27#include "chardev/char.h"
  28#include "qemu/main-loop.h"
  29#include "qemu/module.h"
  30#include "qemu/timer.h"
  31#include "hw/usb.h"
  32#include "ui/console.h"
  33#include <brlapi.h>
  34#include <brlapi_constants.h>
  35#include <brlapi_keycodes.h>
  36#include "qom/object.h"
  37
  38#if 0
  39#define DPRINTF(fmt, ...) \
  40        printf(fmt, ## __VA_ARGS__)
  41#else
  42#define DPRINTF(fmt, ...)
  43#endif
  44
  45#define ESC 0x1B
  46
  47#define BAUM_REQ_DisplayData            0x01
  48#define BAUM_REQ_GetVersionNumber       0x05
  49#define BAUM_REQ_GetKeys                0x08
  50#define BAUM_REQ_SetMode                0x12
  51#define BAUM_REQ_SetProtocol            0x15
  52#define BAUM_REQ_GetDeviceIdentity      0x84
  53#define BAUM_REQ_GetSerialNumber        0x8A
  54
  55#define BAUM_RSP_CellCount              0x01
  56#define BAUM_RSP_VersionNumber          0x05
  57#define BAUM_RSP_ModeSetting            0x11
  58#define BAUM_RSP_CommunicationChannel   0x16
  59#define BAUM_RSP_PowerdownSignal        0x17
  60#define BAUM_RSP_HorizontalSensors      0x20
  61#define BAUM_RSP_VerticalSensors        0x21
  62#define BAUM_RSP_RoutingKeys            0x22
  63#define BAUM_RSP_Switches               0x23
  64#define BAUM_RSP_TopKeys                0x24
  65#define BAUM_RSP_HorizontalSensor       0x25
  66#define BAUM_RSP_VerticalSensor         0x26
  67#define BAUM_RSP_RoutingKey             0x27
  68#define BAUM_RSP_FrontKeys6             0x28
  69#define BAUM_RSP_BackKeys6              0x29
  70#define BAUM_RSP_CommandKeys            0x2B
  71#define BAUM_RSP_FrontKeys10            0x2C
  72#define BAUM_RSP_BackKeys10             0x2D
  73#define BAUM_RSP_EntryKeys              0x33
  74#define BAUM_RSP_JoyStick               0x34
  75#define BAUM_RSP_ErrorCode              0x40
  76#define BAUM_RSP_InfoBlock              0x42
  77#define BAUM_RSP_DeviceIdentity         0x84
  78#define BAUM_RSP_SerialNumber           0x8A
  79#define BAUM_RSP_BluetoothName          0x8C
  80
  81#define BAUM_TL1 0x01
  82#define BAUM_TL2 0x02
  83#define BAUM_TL3 0x04
  84#define BAUM_TR1 0x08
  85#define BAUM_TR2 0x10
  86#define BAUM_TR3 0x20
  87
  88#define BUF_SIZE 256
  89
  90#define X_MAX   84
  91#define Y_MAX   1
  92
  93struct BaumChardev {
  94    Chardev parent;
  95
  96    brlapi_handle_t *brlapi;
  97    int brlapi_fd;
  98    unsigned int x, y;
  99    bool deferred_init;
 100
 101    uint8_t in_buf[BUF_SIZE];
 102    uint8_t in_buf_used;
 103    uint8_t out_buf[BUF_SIZE];
 104    uint8_t out_buf_used, out_buf_ptr;
 105
 106    QEMUTimer *cellCount_timer;
 107};
 108typedef struct BaumChardev BaumChardev;
 109
 110#define TYPE_CHARDEV_BRAILLE "chardev-braille"
 111DECLARE_INSTANCE_CHECKER(BaumChardev, BAUM_CHARDEV,
 112                         TYPE_CHARDEV_BRAILLE)
 113
 114/* Let's assume NABCC by default */
 115enum way {
 116    DOTS2ASCII,
 117    ASCII2DOTS
 118};
 119static const uint8_t nabcc_translation[2][256] = {
 120#ifndef BRLAPI_DOTS
 121#define BRLAPI_DOTS(d1,d2,d3,d4,d5,d6,d7,d8) \
 122    ((d1?BRLAPI_DOT1:0)|\
 123     (d2?BRLAPI_DOT2:0)|\
 124     (d3?BRLAPI_DOT3:0)|\
 125     (d4?BRLAPI_DOT4:0)|\
 126     (d5?BRLAPI_DOT5:0)|\
 127     (d6?BRLAPI_DOT6:0)|\
 128     (d7?BRLAPI_DOT7:0)|\
 129     (d8?BRLAPI_DOT8:0))
 130#endif
 131#define DO(dots, ascii) \
 132    [DOTS2ASCII][dots] = ascii, \
 133    [ASCII2DOTS][ascii] = dots
 134    DO(0, ' '),
 135    DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 0, 0, 0), 'a'),
 136    DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 0, 0, 0), 'b'),
 137    DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 0, 0, 0), 'c'),
 138    DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 0, 0, 0), 'd'),
 139    DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 0, 0, 0), 'e'),
 140    DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 0, 0, 0), 'f'),
 141    DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 0, 0, 0), 'g'),
 142    DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 0, 0, 0), 'h'),
 143    DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 0, 0, 0), 'i'),
 144    DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 0, 0, 0), 'j'),
 145    DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 0, 0, 0), 'k'),
 146    DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 0, 0, 0), 'l'),
 147    DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 0, 0, 0), 'm'),
 148    DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 0, 0, 0), 'n'),
 149    DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 0, 0, 0), 'o'),
 150    DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 0, 0, 0), 'p'),
 151    DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 0, 0, 0), 'q'),
 152    DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 0, 0, 0), 'r'),
 153    DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 0, 0, 0), 's'),
 154    DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 0, 0, 0), 't'),
 155    DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 1, 0, 0), 'u'),
 156    DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 1, 0, 0), 'v'),
 157    DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 1, 0, 0), 'w'),
 158    DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 1, 0, 0), 'x'),
 159    DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 1, 0, 0), 'y'),
 160    DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 1, 0, 0), 'z'),
 161
 162    DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 0, 1, 0), 'A'),
 163    DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 0, 1, 0), 'B'),
 164    DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 0, 1, 0), 'C'),
 165    DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 0, 1, 0), 'D'),
 166    DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 0, 1, 0), 'E'),
 167    DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 0, 1, 0), 'F'),
 168    DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 0, 1, 0), 'G'),
 169    DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 0, 1, 0), 'H'),
 170    DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 0, 1, 0), 'I'),
 171    DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 0, 1, 0), 'J'),
 172    DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 0, 1, 0), 'K'),
 173    DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 0, 1, 0), 'L'),
 174    DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 0, 1, 0), 'M'),
 175    DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 0, 1, 0), 'N'),
 176    DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 0, 1, 0), 'O'),
 177    DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 0, 1, 0), 'P'),
 178    DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 0, 1, 0), 'Q'),
 179    DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 0, 1, 0), 'R'),
 180    DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 0, 1, 0), 'S'),
 181    DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 0, 1, 0), 'T'),
 182    DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 1, 1, 0), 'U'),
 183    DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 1, 1, 0), 'V'),
 184    DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 1, 1, 0), 'W'),
 185    DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 1, 1, 0), 'X'),
 186    DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 1, 1, 0), 'Y'),
 187    DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 1, 1, 0), 'Z'),
 188
 189    DO(BRLAPI_DOTS(0, 0, 1, 0, 1, 1, 0, 0), '0'),
 190    DO(BRLAPI_DOTS(0, 1, 0, 0, 0, 0, 0, 0), '1'),
 191    DO(BRLAPI_DOTS(0, 1, 1, 0, 0, 0, 0, 0), '2'),
 192    DO(BRLAPI_DOTS(0, 1, 0, 0, 1, 0, 0, 0), '3'),
 193    DO(BRLAPI_DOTS(0, 1, 0, 0, 1, 1, 0, 0), '4'),
 194    DO(BRLAPI_DOTS(0, 1, 0, 0, 0, 1, 0, 0), '5'),
 195    DO(BRLAPI_DOTS(0, 1, 1, 0, 1, 0, 0, 0), '6'),
 196    DO(BRLAPI_DOTS(0, 1, 1, 0, 1, 1, 0, 0), '7'),
 197    DO(BRLAPI_DOTS(0, 1, 1, 0, 0, 1, 0, 0), '8'),
 198    DO(BRLAPI_DOTS(0, 0, 1, 0, 1, 0, 0, 0), '9'),
 199
 200    DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 1, 0, 0), '.'),
 201    DO(BRLAPI_DOTS(0, 0, 1, 1, 0, 1, 0, 0), '+'),
 202    DO(BRLAPI_DOTS(0, 0, 1, 0, 0, 1, 0, 0), '-'),
 203    DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 1, 0, 0), '*'),
 204    DO(BRLAPI_DOTS(0, 0, 1, 1, 0, 0, 0, 0), '/'),
 205    DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 1, 0, 0), '('),
 206    DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 1, 0, 0), ')'),
 207
 208    DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 1, 0, 0), '&'),
 209    DO(BRLAPI_DOTS(0, 0, 1, 1, 1, 1, 0, 0), '#'),
 210
 211    DO(BRLAPI_DOTS(0, 0, 0, 0, 0, 1, 0, 0), ','),
 212    DO(BRLAPI_DOTS(0, 0, 0, 0, 1, 1, 0, 0), ';'),
 213    DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 1, 0, 0), ':'),
 214    DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 1, 0, 0), '!'),
 215    DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 1, 0, 0), '?'),
 216    DO(BRLAPI_DOTS(0, 0, 0, 0, 1, 0, 0, 0), '"'),
 217    DO(BRLAPI_DOTS(0, 0, 1, 0, 0, 0, 0, 0), '\''),
 218    DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 0, 0, 0), '`'),
 219    DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 0, 1, 0), '^'),
 220    DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 0, 0, 0), '~'),
 221    DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 1, 1, 0), '['),
 222    DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 1, 1, 0), ']'),
 223    DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 1, 0, 0), '{'),
 224    DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 1, 0, 0), '}'),
 225    DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 1, 0, 0), '='),
 226    DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 1, 0, 0), '<'),
 227    DO(BRLAPI_DOTS(0, 0, 1, 1, 1, 0, 0, 0), '>'),
 228    DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 1, 0, 0), '$'),
 229    DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 1, 0, 0), '%'),
 230    DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 0, 1, 0), '@'),
 231    DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 1, 0, 0), '|'),
 232    DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 1, 1, 0), '\\'),
 233    DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 1, 0, 0), '_'),
 234};
 235
 236/* The guest OS has started discussing with us, finish initializing BrlAPI */
 237static int baum_deferred_init(BaumChardev *baum)
 238{
 239    int tty = BRLAPI_TTY_DEFAULT;
 240    QemuConsole *con;
 241
 242    if (baum->deferred_init) {
 243        return 1;
 244    }
 245
 246    if (brlapi__getDisplaySize(baum->brlapi, &baum->x, &baum->y) == -1) {
 247        brlapi_perror("baum: brlapi__getDisplaySize");
 248        return 0;
 249    }
 250    if (baum->y > Y_MAX) {
 251        baum->y = Y_MAX;
 252    }
 253    if (baum->x > X_MAX) {
 254        baum->x = X_MAX;
 255    }
 256
 257    con = qemu_console_lookup_by_index(0);
 258    if (con && qemu_console_is_graphic(con)) {
 259        tty = qemu_console_get_window_id(con);
 260        if (tty == -1)
 261            tty = BRLAPI_TTY_DEFAULT;
 262    }
 263
 264    if (brlapi__enterTtyMode(baum->brlapi, tty, NULL) == -1) {
 265        brlapi_perror("baum: brlapi__enterTtyMode");
 266        return 0;
 267    }
 268    baum->deferred_init = 1;
 269    return 1;
 270}
 271
 272/* The serial port can receive more of our data */
 273static void baum_chr_accept_input(struct Chardev *chr)
 274{
 275    BaumChardev *baum = BAUM_CHARDEV(chr);
 276    int room, first;
 277
 278    if (!baum->out_buf_used)
 279        return;
 280    room = qemu_chr_be_can_write(chr);
 281    if (!room)
 282        return;
 283    if (room > baum->out_buf_used)
 284        room = baum->out_buf_used;
 285
 286    first = BUF_SIZE - baum->out_buf_ptr;
 287    if (room > first) {
 288        qemu_chr_be_write(chr, baum->out_buf + baum->out_buf_ptr, first);
 289        baum->out_buf_ptr = 0;
 290        baum->out_buf_used -= first;
 291        room -= first;
 292    }
 293    qemu_chr_be_write(chr, baum->out_buf + baum->out_buf_ptr, room);
 294    baum->out_buf_ptr += room;
 295    baum->out_buf_used -= room;
 296}
 297
 298/* We want to send a packet */
 299static void baum_write_packet(BaumChardev *baum, const uint8_t *buf, int len)
 300{
 301    Chardev *chr = CHARDEV(baum);
 302    g_autofree uint8_t *io_buf = g_malloc(1 + 2 * len);
 303    uint8_t *cur = io_buf;
 304    int room;
 305    *cur++ = ESC;
 306    while (len--)
 307        if ((*cur++ = *buf++) == ESC)
 308            *cur++ = ESC;
 309    room = qemu_chr_be_can_write(chr);
 310    len = cur - io_buf;
 311    if (len <= room) {
 312        /* Fits */
 313        qemu_chr_be_write(chr, io_buf, len);
 314    } else {
 315        int first;
 316        uint8_t out;
 317        /* Can't fit all, send what can be, and store the rest. */
 318        qemu_chr_be_write(chr, io_buf, room);
 319        len -= room;
 320        cur = io_buf + room;
 321        if (len > BUF_SIZE - baum->out_buf_used) {
 322            /* Can't even store it, drop the previous data... */
 323            assert(len <= BUF_SIZE);
 324            baum->out_buf_used = 0;
 325            baum->out_buf_ptr = 0;
 326        }
 327        out = baum->out_buf_ptr;
 328        baum->out_buf_used += len;
 329        first = BUF_SIZE - baum->out_buf_ptr;
 330        if (len > first) {
 331            memcpy(baum->out_buf + out, cur, first);
 332            out = 0;
 333            len -= first;
 334            cur += first;
 335        }
 336        memcpy(baum->out_buf + out, cur, len);
 337    }
 338}
 339
 340/* Called when the other end seems to have a wrong idea of our display size */
 341static void baum_cellCount_timer_cb(void *opaque)
 342{
 343    BaumChardev *baum = BAUM_CHARDEV(opaque);
 344    uint8_t cell_count[] = { BAUM_RSP_CellCount, baum->x * baum->y };
 345    DPRINTF("Timeout waiting for DisplayData, sending cell count\n");
 346    baum_write_packet(baum, cell_count, sizeof(cell_count));
 347}
 348
 349/* Try to interpret a whole incoming packet */
 350static int baum_eat_packet(BaumChardev *baum, const uint8_t *buf, int len)
 351{
 352    const uint8_t *cur = buf;
 353    uint8_t req = 0;
 354
 355    if (!len--)
 356        return 0;
 357    if (*cur++ != ESC) {
 358        while (*cur != ESC) {
 359            if (!len--)
 360                return 0;
 361            cur++;
 362        }
 363        DPRINTF("Dropped %td bytes!\n", cur - buf);
 364    }
 365
 366#define EAT(c) do {\
 367    if (!len--) \
 368        return 0; \
 369    if ((c = *cur++) == ESC) { \
 370        if (!len--) \
 371            return 0; \
 372        if (*cur++ != ESC) { \
 373            DPRINTF("Broken packet %#2x, tossing\n", req); \
 374            if (timer_pending(baum->cellCount_timer)) {    \
 375                timer_del(baum->cellCount_timer);     \
 376                baum_cellCount_timer_cb(baum);             \
 377            } \
 378            return (cur - 2 - buf); \
 379        } \
 380    } \
 381} while (0)
 382
 383    EAT(req);
 384    switch (req) {
 385    case BAUM_REQ_DisplayData:
 386    {
 387        uint8_t cells[X_MAX * Y_MAX], c;
 388        uint8_t text[X_MAX * Y_MAX];
 389        uint8_t zero[X_MAX * Y_MAX];
 390        int cursor = BRLAPI_CURSOR_OFF;
 391        int i;
 392
 393        /* Allow 100ms to complete the DisplayData packet */
 394        timer_mod(baum->cellCount_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
 395                       NANOSECONDS_PER_SECOND / 10);
 396        for (i = 0; i < baum->x * baum->y ; i++) {
 397            EAT(c);
 398            cells[i] = c;
 399            if ((c & (BRLAPI_DOT7|BRLAPI_DOT8))
 400                    == (BRLAPI_DOT7|BRLAPI_DOT8)) {
 401                cursor = i + 1;
 402                c &= ~(BRLAPI_DOT7|BRLAPI_DOT8);
 403            }
 404            c = nabcc_translation[DOTS2ASCII][c];
 405            if (!c) {
 406                c = '?';
 407            }
 408            text[i] = c;
 409        }
 410        timer_del(baum->cellCount_timer);
 411
 412        memset(zero, 0, baum->x * baum->y);
 413
 414        brlapi_writeArguments_t wa = {
 415            .displayNumber = BRLAPI_DISPLAY_DEFAULT,
 416            .regionBegin = 1,
 417            .regionSize = baum->x * baum->y,
 418            .text = (char *)text,
 419            .textSize = baum->x * baum->y,
 420            .andMask = zero,
 421            .orMask = cells,
 422            .cursor = cursor,
 423            .charset = (char *)"ISO-8859-1",
 424        };
 425
 426        if (brlapi__write(baum->brlapi, &wa) == -1)
 427            brlapi_perror("baum brlapi_write");
 428        break;
 429    }
 430    case BAUM_REQ_SetMode:
 431    {
 432        uint8_t mode, setting;
 433        DPRINTF("SetMode\n");
 434        EAT(mode);
 435        EAT(setting);
 436        /* ignore */
 437        break;
 438    }
 439    case BAUM_REQ_SetProtocol:
 440    {
 441        uint8_t protocol;
 442        DPRINTF("SetProtocol\n");
 443        EAT(protocol);
 444        /* ignore */
 445        break;
 446    }
 447    case BAUM_REQ_GetDeviceIdentity:
 448    {
 449        uint8_t identity[17] = { BAUM_RSP_DeviceIdentity,
 450            'B','a','u','m',' ','V','a','r','i','o' };
 451        DPRINTF("GetDeviceIdentity\n");
 452        identity[11] = '0' + baum->x / 10;
 453        identity[12] = '0' + baum->x % 10;
 454        baum_write_packet(baum, identity, sizeof(identity));
 455        break;
 456    }
 457    case BAUM_REQ_GetVersionNumber:
 458    {
 459        uint8_t version[] = { BAUM_RSP_VersionNumber, 1 }; /* ? */
 460        DPRINTF("GetVersionNumber\n");
 461        baum_write_packet(baum, version, sizeof(version));
 462        break;
 463    }
 464    case BAUM_REQ_GetSerialNumber:
 465    {
 466        uint8_t serial[] = { BAUM_RSP_SerialNumber,
 467            '0','0','0','0','0','0','0','0' };
 468        DPRINTF("GetSerialNumber\n");
 469        baum_write_packet(baum, serial, sizeof(serial));
 470        break;
 471    }
 472    case BAUM_REQ_GetKeys:
 473    {
 474        DPRINTF("Get%0#2x\n", req);
 475        /* ignore */
 476        break;
 477    }
 478    default:
 479        DPRINTF("unrecognized request %0#2x\n", req);
 480        do
 481            if (!len--)
 482                return 0;
 483        while (*cur++ != ESC);
 484        cur--;
 485        break;
 486    }
 487    return cur - buf;
 488}
 489
 490/* The other end is writing some data.  Store it and try to interpret */
 491static int baum_chr_write(Chardev *chr, const uint8_t *buf, int len)
 492{
 493    BaumChardev *baum = BAUM_CHARDEV(chr);
 494    int tocopy, cur, eaten, orig_len = len;
 495
 496    if (!len)
 497        return 0;
 498    if (!baum->brlapi)
 499        return len;
 500    if (!baum_deferred_init(baum))
 501        return len;
 502
 503    while (len) {
 504        /* Complete our buffer as much as possible */
 505        tocopy = len;
 506        if (tocopy > BUF_SIZE - baum->in_buf_used)
 507            tocopy = BUF_SIZE - baum->in_buf_used;
 508
 509        memcpy(baum->in_buf + baum->in_buf_used, buf, tocopy);
 510        baum->in_buf_used += tocopy;
 511        buf += tocopy;
 512        len -= tocopy;
 513
 514        /* Interpret it as much as possible */
 515        cur = 0;
 516        while (cur < baum->in_buf_used &&
 517                (eaten = baum_eat_packet(baum, baum->in_buf + cur, baum->in_buf_used - cur)))
 518            cur += eaten;
 519
 520        /* Shift the remainder */
 521        if (cur) {
 522            memmove(baum->in_buf, baum->in_buf + cur, baum->in_buf_used - cur);
 523            baum->in_buf_used -= cur;
 524        }
 525
 526        /* And continue if any data left */
 527    }
 528    return orig_len;
 529}
 530
 531/* Send the key code to the other end */
 532static void baum_send_key(BaumChardev *baum, uint8_t type, uint8_t value)
 533{
 534    uint8_t packet[] = { type, value };
 535    DPRINTF("writing key %x %x\n", type, value);
 536    baum_write_packet(baum, packet, sizeof(packet));
 537}
 538
 539static void baum_send_key2(BaumChardev *baum, uint8_t type, uint8_t value,
 540                           uint8_t value2)
 541{
 542    uint8_t packet[] = { type, value, value2 };
 543    DPRINTF("writing key %x %x\n", type, value);
 544    baum_write_packet(baum, packet, sizeof(packet));
 545}
 546
 547/* We got some data on the BrlAPI socket */
 548static void baum_chr_read(void *opaque)
 549{
 550    BaumChardev *baum = BAUM_CHARDEV(opaque);
 551    brlapi_keyCode_t code;
 552    int ret;
 553    if (!baum->brlapi)
 554        return;
 555    if (!baum_deferred_init(baum))
 556        return;
 557    while ((ret = brlapi__readKey(baum->brlapi, 0, &code)) == 1) {
 558        DPRINTF("got key %"BRLAPI_PRIxKEYCODE"\n", code);
 559        /* Emulate */
 560        switch (code & BRLAPI_KEY_TYPE_MASK) {
 561        case BRLAPI_KEY_TYPE_CMD:
 562            switch (code & BRLAPI_KEY_CMD_BLK_MASK) {
 563            case BRLAPI_KEY_CMD_ROUTE:
 564                baum_send_key(baum, BAUM_RSP_RoutingKey, (code & BRLAPI_KEY_CMD_ARG_MASK)+1);
 565                baum_send_key(baum, BAUM_RSP_RoutingKey, 0);
 566                break;
 567            case 0:
 568                switch (code & BRLAPI_KEY_CMD_ARG_MASK) {
 569                case BRLAPI_KEY_CMD_FWINLT:
 570                    baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2);
 571                    baum_send_key(baum, BAUM_RSP_TopKeys, 0);
 572                    break;
 573                case BRLAPI_KEY_CMD_FWINRT:
 574                    baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR2);
 575                    baum_send_key(baum, BAUM_RSP_TopKeys, 0);
 576                    break;
 577                case BRLAPI_KEY_CMD_LNUP:
 578                    baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR1);
 579                    baum_send_key(baum, BAUM_RSP_TopKeys, 0);
 580                    break;
 581                case BRLAPI_KEY_CMD_LNDN:
 582                    baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR3);
 583                    baum_send_key(baum, BAUM_RSP_TopKeys, 0);
 584                    break;
 585                case BRLAPI_KEY_CMD_TOP:
 586                    baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL1|BAUM_TR1);
 587                    baum_send_key(baum, BAUM_RSP_TopKeys, 0);
 588                    break;
 589                case BRLAPI_KEY_CMD_BOT:
 590                    baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL3|BAUM_TR3);
 591                    baum_send_key(baum, BAUM_RSP_TopKeys, 0);
 592                    break;
 593                case BRLAPI_KEY_CMD_TOP_LEFT:
 594                    baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR1);
 595                    baum_send_key(baum, BAUM_RSP_TopKeys, 0);
 596                    break;
 597                case BRLAPI_KEY_CMD_BOT_LEFT:
 598                    baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR3);
 599                    baum_send_key(baum, BAUM_RSP_TopKeys, 0);
 600                    break;
 601                case BRLAPI_KEY_CMD_HOME:
 602                    baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR1|BAUM_TR3);
 603                    baum_send_key(baum, BAUM_RSP_TopKeys, 0);
 604                    break;
 605                case BRLAPI_KEY_CMD_PREFMENU:
 606                    baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL1|BAUM_TL3|BAUM_TR1);
 607                    baum_send_key(baum, BAUM_RSP_TopKeys, 0);
 608                    break;
 609                }
 610            }
 611            break;
 612        case BRLAPI_KEY_TYPE_SYM:
 613            {
 614                brlapi_keyCode_t keysym = code & BRLAPI_KEY_CODE_MASK;
 615                if (keysym < 0x100) {
 616                    uint8_t dots = nabcc_translation[ASCII2DOTS][keysym];
 617                    if (dots) {
 618                        baum_send_key2(baum, BAUM_RSP_EntryKeys, 0, dots);
 619                        baum_send_key2(baum, BAUM_RSP_EntryKeys, 0, 0);
 620                    }
 621                }
 622                break;
 623            }
 624        }
 625    }
 626    if (ret == -1 && (brlapi_errno != BRLAPI_ERROR_LIBCERR || errno != EINTR)) {
 627        brlapi_perror("baum: brlapi_readKey");
 628        brlapi__closeConnection(baum->brlapi);
 629        g_free(baum->brlapi);
 630        baum->brlapi = NULL;
 631    }
 632}
 633
 634static void char_braille_finalize(Object *obj)
 635{
 636    BaumChardev *baum = BAUM_CHARDEV(obj);
 637
 638    timer_free(baum->cellCount_timer);
 639    if (baum->brlapi) {
 640        brlapi__closeConnection(baum->brlapi);
 641        g_free(baum->brlapi);
 642    }
 643}
 644
 645static void baum_chr_open(Chardev *chr,
 646                          ChardevBackend *backend,
 647                          bool *be_opened,
 648                          Error **errp)
 649{
 650    BaumChardev *baum = BAUM_CHARDEV(chr);
 651    brlapi_handle_t *handle;
 652
 653    handle = g_malloc0(brlapi_getHandleSize());
 654    baum->brlapi = handle;
 655
 656    baum->brlapi_fd = brlapi__openConnection(handle, NULL, NULL);
 657    if (baum->brlapi_fd == -1) {
 658        error_setg(errp, "brlapi__openConnection: %s",
 659                   brlapi_strerror(brlapi_error_location()));
 660        g_free(handle);
 661        baum->brlapi = NULL;
 662        return;
 663    }
 664    baum->deferred_init = 0;
 665
 666    baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum);
 667
 668    qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum);
 669}
 670
 671static void char_braille_class_init(ObjectClass *oc, void *data)
 672{
 673    ChardevClass *cc = CHARDEV_CLASS(oc);
 674
 675    cc->open = baum_chr_open;
 676    cc->chr_write = baum_chr_write;
 677    cc->chr_accept_input = baum_chr_accept_input;
 678}
 679
 680static const TypeInfo char_braille_type_info = {
 681    .name = TYPE_CHARDEV_BRAILLE,
 682    .parent = TYPE_CHARDEV,
 683    .instance_size = sizeof(BaumChardev),
 684    .instance_finalize = char_braille_finalize,
 685    .class_init = char_braille_class_init,
 686};
 687module_obj(TYPE_CHARDEV_BRAILLE);
 688
 689static void register_types(void)
 690{
 691    type_register_static(&char_braille_type_info);
 692}
 693
 694type_init(register_types);
 695