/* * 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);