Add qemu 2.4.0
[kvmfornfv.git] / qemu / backends / baum.c
diff --git a/qemu/backends/baum.c b/qemu/backends/baum.c
new file mode 100644 (file)
index 0000000..a69aaff
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ * QEMU Baum Braille Device
+ *
+ * Copyright (c) 2008 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-common.h"
+#include "sysemu/char.h"
+#include "qemu/timer.h"
+#include "hw/usb.h"
+#include <brlapi.h>
+#include <brlapi_constants.h>
+#include <brlapi_keycodes.h>
+#ifdef CONFIG_SDL
+#include <SDL_syswm.h>
+#endif
+
+#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
+
+typedef struct {
+    CharDriverState *chr;
+
+    brlapi_handle_t *brlapi;
+    int brlapi_fd;
+    unsigned int x, y;
+
+    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;
+} BaumDriverState;
+
+/* Let's assume NABCC by default */
+static const uint8_t nabcc_translation[256] = {
+    [0] = ' ',
+#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
+    [BRLAPI_DOTS(1,0,0,0,0,0,0,0)] = 'a',
+    [BRLAPI_DOTS(1,1,0,0,0,0,0,0)] = 'b',
+    [BRLAPI_DOTS(1,0,0,1,0,0,0,0)] = 'c',
+    [BRLAPI_DOTS(1,0,0,1,1,0,0,0)] = 'd',
+    [BRLAPI_DOTS(1,0,0,0,1,0,0,0)] = 'e',
+    [BRLAPI_DOTS(1,1,0,1,0,0,0,0)] = 'f',
+    [BRLAPI_DOTS(1,1,0,1,1,0,0,0)] = 'g',
+    [BRLAPI_DOTS(1,1,0,0,1,0,0,0)] = 'h',
+    [BRLAPI_DOTS(0,1,0,1,0,0,0,0)] = 'i',
+    [BRLAPI_DOTS(0,1,0,1,1,0,0,0)] = 'j',
+    [BRLAPI_DOTS(1,0,1,0,0,0,0,0)] = 'k',
+    [BRLAPI_DOTS(1,1,1,0,0,0,0,0)] = 'l',
+    [BRLAPI_DOTS(1,0,1,1,0,0,0,0)] = 'm',
+    [BRLAPI_DOTS(1,0,1,1,1,0,0,0)] = 'n',
+    [BRLAPI_DOTS(1,0,1,0,1,0,0,0)] = 'o',
+    [BRLAPI_DOTS(1,1,1,1,0,0,0,0)] = 'p',
+    [BRLAPI_DOTS(1,1,1,1,1,0,0,0)] = 'q',
+    [BRLAPI_DOTS(1,1,1,0,1,0,0,0)] = 'r',
+    [BRLAPI_DOTS(0,1,1,1,0,0,0,0)] = 's',
+    [BRLAPI_DOTS(0,1,1,1,1,0,0,0)] = 't',
+    [BRLAPI_DOTS(1,0,1,0,0,1,0,0)] = 'u',
+    [BRLAPI_DOTS(1,1,1,0,0,1,0,0)] = 'v',
+    [BRLAPI_DOTS(0,1,0,1,1,1,0,0)] = 'w',
+    [BRLAPI_DOTS(1,0,1,1,0,1,0,0)] = 'x',
+    [BRLAPI_DOTS(1,0,1,1,1,1,0,0)] = 'y',
+    [BRLAPI_DOTS(1,0,1,0,1,1,0,0)] = 'z',
+
+    [BRLAPI_DOTS(1,0,0,0,0,0,1,0)] = 'A',
+    [BRLAPI_DOTS(1,1,0,0,0,0,1,0)] = 'B',
+    [BRLAPI_DOTS(1,0,0,1,0,0,1,0)] = 'C',
+    [BRLAPI_DOTS(1,0,0,1,1,0,1,0)] = 'D',
+    [BRLAPI_DOTS(1,0,0,0,1,0,1,0)] = 'E',
+    [BRLAPI_DOTS(1,1,0,1,0,0,1,0)] = 'F',
+    [BRLAPI_DOTS(1,1,0,1,1,0,1,0)] = 'G',
+    [BRLAPI_DOTS(1,1,0,0,1,0,1,0)] = 'H',
+    [BRLAPI_DOTS(0,1,0,1,0,0,1,0)] = 'I',
+    [BRLAPI_DOTS(0,1,0,1,1,0,1,0)] = 'J',
+    [BRLAPI_DOTS(1,0,1,0,0,0,1,0)] = 'K',
+    [BRLAPI_DOTS(1,1,1,0,0,0,1,0)] = 'L',
+    [BRLAPI_DOTS(1,0,1,1,0,0,1,0)] = 'M',
+    [BRLAPI_DOTS(1,0,1,1,1,0,1,0)] = 'N',
+    [BRLAPI_DOTS(1,0,1,0,1,0,1,0)] = 'O',
+    [BRLAPI_DOTS(1,1,1,1,0,0,1,0)] = 'P',
+    [BRLAPI_DOTS(1,1,1,1,1,0,1,0)] = 'Q',
+    [BRLAPI_DOTS(1,1,1,0,1,0,1,0)] = 'R',
+    [BRLAPI_DOTS(0,1,1,1,0,0,1,0)] = 'S',
+    [BRLAPI_DOTS(0,1,1,1,1,0,1,0)] = 'T',
+    [BRLAPI_DOTS(1,0,1,0,0,1,1,0)] = 'U',
+    [BRLAPI_DOTS(1,1,1,0,0,1,1,0)] = 'V',
+    [BRLAPI_DOTS(0,1,0,1,1,1,1,0)] = 'W',
+    [BRLAPI_DOTS(1,0,1,1,0,1,1,0)] = 'X',
+    [BRLAPI_DOTS(1,0,1,1,1,1,1,0)] = 'Y',
+    [BRLAPI_DOTS(1,0,1,0,1,1,1,0)] = 'Z',
+
+    [BRLAPI_DOTS(0,0,1,0,1,1,0,0)] = '0',
+    [BRLAPI_DOTS(0,1,0,0,0,0,0,0)] = '1',
+    [BRLAPI_DOTS(0,1,1,0,0,0,0,0)] = '2',
+    [BRLAPI_DOTS(0,1,0,0,1,0,0,0)] = '3',
+    [BRLAPI_DOTS(0,1,0,0,1,1,0,0)] = '4',
+    [BRLAPI_DOTS(0,1,0,0,0,1,0,0)] = '5',
+    [BRLAPI_DOTS(0,1,1,0,1,0,0,0)] = '6',
+    [BRLAPI_DOTS(0,1,1,0,1,1,0,0)] = '7',
+    [BRLAPI_DOTS(0,1,1,0,0,1,0,0)] = '8',
+    [BRLAPI_DOTS(0,0,1,0,1,0,0,0)] = '9',
+
+    [BRLAPI_DOTS(0,0,0,1,0,1,0,0)] = '.',
+    [BRLAPI_DOTS(0,0,1,1,0,1,0,0)] = '+',
+    [BRLAPI_DOTS(0,0,1,0,0,1,0,0)] = '-',
+    [BRLAPI_DOTS(1,0,0,0,0,1,0,0)] = '*',
+    [BRLAPI_DOTS(0,0,1,1,0,0,0,0)] = '/',
+    [BRLAPI_DOTS(1,1,1,0,1,1,0,0)] = '(',
+    [BRLAPI_DOTS(0,1,1,1,1,1,0,0)] = ')',
+
+    [BRLAPI_DOTS(1,1,1,1,0,1,0,0)] = '&',
+    [BRLAPI_DOTS(0,0,1,1,1,1,0,0)] = '#',
+
+    [BRLAPI_DOTS(0,0,0,0,0,1,0,0)] = ',',
+    [BRLAPI_DOTS(0,0,0,0,1,1,0,0)] = ';',
+    [BRLAPI_DOTS(1,0,0,0,1,1,0,0)] = ':',
+    [BRLAPI_DOTS(0,1,1,1,0,1,0,0)] = '!',
+    [BRLAPI_DOTS(1,0,0,1,1,1,0,0)] = '?',
+    [BRLAPI_DOTS(0,0,0,0,1,0,0,0)] = '"',
+    [BRLAPI_DOTS(0,0,1,0,0,0,0,0)] ='\'',
+    [BRLAPI_DOTS(0,0,0,1,0,0,0,0)] = '`',
+    [BRLAPI_DOTS(0,0,0,1,1,0,1,0)] = '^',
+    [BRLAPI_DOTS(0,0,0,1,1,0,0,0)] = '~',
+    [BRLAPI_DOTS(0,1,0,1,0,1,1,0)] = '[',
+    [BRLAPI_DOTS(1,1,0,1,1,1,1,0)] = ']',
+    [BRLAPI_DOTS(0,1,0,1,0,1,0,0)] = '{',
+    [BRLAPI_DOTS(1,1,0,1,1,1,0,0)] = '}',
+    [BRLAPI_DOTS(1,1,1,1,1,1,0,0)] = '=',
+    [BRLAPI_DOTS(1,1,0,0,0,1,0,0)] = '<',
+    [BRLAPI_DOTS(0,0,1,1,1,0,0,0)] = '>',
+    [BRLAPI_DOTS(1,1,0,1,0,1,0,0)] = '$',
+    [BRLAPI_DOTS(1,0,0,1,0,1,0,0)] = '%',
+    [BRLAPI_DOTS(0,0,0,1,0,0,1,0)] = '@',
+    [BRLAPI_DOTS(1,1,0,0,1,1,0,0)] = '|',
+    [BRLAPI_DOTS(1,1,0,0,1,1,1,0)] ='\\',
+    [BRLAPI_DOTS(0,0,0,1,1,1,0,0)] = '_',
+};
+
+/* The serial port can receive more of our data */
+static void baum_accept_input(struct CharDriverState *chr)
+{
+    BaumDriverState *baum = chr->opaque;
+    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(BaumDriverState *baum, const uint8_t *buf, int len)
+{
+    uint8_t io_buf[1 + 2 * len], *cur = io_buf;
+    int room;
+    *cur++ = ESC;
+    while (len--)
+        if ((*cur++ = *buf++) == ESC)
+            *cur++ = ESC;
+    room = qemu_chr_be_can_write(baum->chr);
+    len = cur - io_buf;
+    if (len <= room) {
+        /* Fits */
+        qemu_chr_be_write(baum->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(baum->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)
+{
+    BaumDriverState *baum = 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(BaumDriverState *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 %d 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[baum->x * baum->y], c;
+        uint8_t text[baum->x * baum->y];
+        uint8_t zero[baum->x * baum->y];
+        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) +
+                       get_ticks_per_sec() / 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);
+            }
+            if (!(c = nabcc_translation[c]))
+                c = '?';
+            text[i] = c;
+        }
+        timer_del(baum->cellCount_timer);
+
+        memset(zero, 0, sizeof(zero));
+
+        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_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    BaumDriverState *baum = chr->opaque;
+    int tocopy, cur, eaten, orig_len = len;
+
+    if (!len)
+        return 0;
+    if (!baum->brlapi)
+        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(BaumDriverState *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));
+}
+
+/* We got some data on the BrlAPI socket */
+static void baum_chr_read(void *opaque)
+{
+    BaumDriverState *baum = opaque;
+    brlapi_keyCode_t code;
+    int ret;
+    if (!baum->brlapi)
+        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:
+            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 baum_close(struct CharDriverState *chr)
+{
+    BaumDriverState *baum = chr->opaque;
+
+    timer_free(baum->cellCount_timer);
+    if (baum->brlapi) {
+        brlapi__closeConnection(baum->brlapi);
+        g_free(baum->brlapi);
+    }
+    g_free(baum);
+}
+
+CharDriverState *chr_baum_init(void)
+{
+    BaumDriverState *baum;
+    CharDriverState *chr;
+    brlapi_handle_t *handle;
+#if defined(CONFIG_SDL)
+#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
+    SDL_SysWMinfo info;
+#endif
+#endif
+    int tty;
+
+    baum = g_malloc0(sizeof(BaumDriverState));
+    baum->chr = chr = qemu_chr_alloc();
+
+    chr->opaque = baum;
+    chr->chr_write = baum_write;
+    chr->chr_accept_input = baum_accept_input;
+    chr->chr_close = baum_close;
+
+    handle = g_malloc0(brlapi_getHandleSize());
+    baum->brlapi = handle;
+
+    baum->brlapi_fd = brlapi__openConnection(handle, NULL, NULL);
+    if (baum->brlapi_fd == -1) {
+        brlapi_perror("baum_init: brlapi_openConnection");
+        goto fail_handle;
+    }
+
+    baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum);
+
+    if (brlapi__getDisplaySize(handle, &baum->x, &baum->y) == -1) {
+        brlapi_perror("baum_init: brlapi_getDisplaySize");
+        goto fail;
+    }
+
+#if defined(CONFIG_SDL)
+#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
+    memset(&info, 0, sizeof(info));
+    SDL_VERSION(&info.version);
+    if (SDL_GetWMInfo(&info))
+        tty = info.info.x11.wmwindow;
+    else
+#endif
+#endif
+        tty = BRLAPI_TTY_DEFAULT;
+
+    if (brlapi__enterTtyMode(handle, tty, NULL) == -1) {
+        brlapi_perror("baum_init: brlapi_enterTtyMode");
+        goto fail;
+    }
+
+    qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum);
+
+    return chr;
+
+fail:
+    timer_free(baum->cellCount_timer);
+    brlapi__closeConnection(handle);
+fail_handle:
+    g_free(handle);
+    g_free(chr);
+    g_free(baum);
+    return NULL;
+}
+
+static void register_types(void)
+{
+    register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL);
+}
+
+type_init(register_types);