baum.c (22875B)
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 90struct BaumChardev { 91 Chardev parent; 92 93 brlapi_handle_t *brlapi; 94 int brlapi_fd; 95 unsigned int x, y; 96 bool deferred_init; 97 98 uint8_t in_buf[BUF_SIZE]; 99 uint8_t in_buf_used; 100 uint8_t out_buf[BUF_SIZE]; 101 uint8_t out_buf_used, out_buf_ptr; 102 103 QEMUTimer *cellCount_timer; 104}; 105typedef struct BaumChardev BaumChardev; 106 107#define TYPE_CHARDEV_BRAILLE "chardev-braille" 108DECLARE_INSTANCE_CHECKER(BaumChardev, BAUM_CHARDEV, 109 TYPE_CHARDEV_BRAILLE) 110 111/* Let's assume NABCC by default */ 112enum way { 113 DOTS2ASCII, 114 ASCII2DOTS 115}; 116static const uint8_t nabcc_translation[2][256] = { 117#ifndef BRLAPI_DOTS 118#define BRLAPI_DOTS(d1,d2,d3,d4,d5,d6,d7,d8) \ 119 ((d1?BRLAPI_DOT1:0)|\ 120 (d2?BRLAPI_DOT2:0)|\ 121 (d3?BRLAPI_DOT3:0)|\ 122 (d4?BRLAPI_DOT4:0)|\ 123 (d5?BRLAPI_DOT5:0)|\ 124 (d6?BRLAPI_DOT6:0)|\ 125 (d7?BRLAPI_DOT7:0)|\ 126 (d8?BRLAPI_DOT8:0)) 127#endif 128#define DO(dots, ascii) \ 129 [DOTS2ASCII][dots] = ascii, \ 130 [ASCII2DOTS][ascii] = dots 131 DO(0, ' '), 132 DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 0, 0, 0), 'a'), 133 DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 0, 0, 0), 'b'), 134 DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 0, 0, 0), 'c'), 135 DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 0, 0, 0), 'd'), 136 DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 0, 0, 0), 'e'), 137 DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 0, 0, 0), 'f'), 138 DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 0, 0, 0), 'g'), 139 DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 0, 0, 0), 'h'), 140 DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 0, 0, 0), 'i'), 141 DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 0, 0, 0), 'j'), 142 DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 0, 0, 0), 'k'), 143 DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 0, 0, 0), 'l'), 144 DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 0, 0, 0), 'm'), 145 DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 0, 0, 0), 'n'), 146 DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 0, 0, 0), 'o'), 147 DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 0, 0, 0), 'p'), 148 DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 0, 0, 0), 'q'), 149 DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 0, 0, 0), 'r'), 150 DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 0, 0, 0), 's'), 151 DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 0, 0, 0), 't'), 152 DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 1, 0, 0), 'u'), 153 DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 1, 0, 0), 'v'), 154 DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 1, 0, 0), 'w'), 155 DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 1, 0, 0), 'x'), 156 DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 1, 0, 0), 'y'), 157 DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 1, 0, 0), 'z'), 158 159 DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 0, 1, 0), 'A'), 160 DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 0, 1, 0), 'B'), 161 DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 0, 1, 0), 'C'), 162 DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 0, 1, 0), 'D'), 163 DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 0, 1, 0), 'E'), 164 DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 0, 1, 0), 'F'), 165 DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 0, 1, 0), 'G'), 166 DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 0, 1, 0), 'H'), 167 DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 0, 1, 0), 'I'), 168 DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 0, 1, 0), 'J'), 169 DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 0, 1, 0), 'K'), 170 DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 0, 1, 0), 'L'), 171 DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 0, 1, 0), 'M'), 172 DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 0, 1, 0), 'N'), 173 DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 0, 1, 0), 'O'), 174 DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 0, 1, 0), 'P'), 175 DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 0, 1, 0), 'Q'), 176 DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 0, 1, 0), 'R'), 177 DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 0, 1, 0), 'S'), 178 DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 0, 1, 0), 'T'), 179 DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 1, 1, 0), 'U'), 180 DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 1, 1, 0), 'V'), 181 DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 1, 1, 0), 'W'), 182 DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 1, 1, 0), 'X'), 183 DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 1, 1, 0), 'Y'), 184 DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 1, 1, 0), 'Z'), 185 186 DO(BRLAPI_DOTS(0, 0, 1, 0, 1, 1, 0, 0), '0'), 187 DO(BRLAPI_DOTS(0, 1, 0, 0, 0, 0, 0, 0), '1'), 188 DO(BRLAPI_DOTS(0, 1, 1, 0, 0, 0, 0, 0), '2'), 189 DO(BRLAPI_DOTS(0, 1, 0, 0, 1, 0, 0, 0), '3'), 190 DO(BRLAPI_DOTS(0, 1, 0, 0, 1, 1, 0, 0), '4'), 191 DO(BRLAPI_DOTS(0, 1, 0, 0, 0, 1, 0, 0), '5'), 192 DO(BRLAPI_DOTS(0, 1, 1, 0, 1, 0, 0, 0), '6'), 193 DO(BRLAPI_DOTS(0, 1, 1, 0, 1, 1, 0, 0), '7'), 194 DO(BRLAPI_DOTS(0, 1, 1, 0, 0, 1, 0, 0), '8'), 195 DO(BRLAPI_DOTS(0, 0, 1, 0, 1, 0, 0, 0), '9'), 196 197 DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 1, 0, 0), '.'), 198 DO(BRLAPI_DOTS(0, 0, 1, 1, 0, 1, 0, 0), '+'), 199 DO(BRLAPI_DOTS(0, 0, 1, 0, 0, 1, 0, 0), '-'), 200 DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 1, 0, 0), '*'), 201 DO(BRLAPI_DOTS(0, 0, 1, 1, 0, 0, 0, 0), '/'), 202 DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 1, 0, 0), '('), 203 DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 1, 0, 0), ')'), 204 205 DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 1, 0, 0), '&'), 206 DO(BRLAPI_DOTS(0, 0, 1, 1, 1, 1, 0, 0), '#'), 207 208 DO(BRLAPI_DOTS(0, 0, 0, 0, 0, 1, 0, 0), ','), 209 DO(BRLAPI_DOTS(0, 0, 0, 0, 1, 1, 0, 0), ';'), 210 DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 1, 0, 0), ':'), 211 DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 1, 0, 0), '!'), 212 DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 1, 0, 0), '?'), 213 DO(BRLAPI_DOTS(0, 0, 0, 0, 1, 0, 0, 0), '"'), 214 DO(BRLAPI_DOTS(0, 0, 1, 0, 0, 0, 0, 0), '\''), 215 DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 0, 0, 0), '`'), 216 DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 0, 1, 0), '^'), 217 DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 0, 0, 0), '~'), 218 DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 1, 1, 0), '['), 219 DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 1, 1, 0), ']'), 220 DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 1, 0, 0), '{'), 221 DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 1, 0, 0), '}'), 222 DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 1, 0, 0), '='), 223 DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 1, 0, 0), '<'), 224 DO(BRLAPI_DOTS(0, 0, 1, 1, 1, 0, 0, 0), '>'), 225 DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 1, 0, 0), '$'), 226 DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 1, 0, 0), '%'), 227 DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 0, 1, 0), '@'), 228 DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 1, 0, 0), '|'), 229 DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 1, 1, 0), '\\'), 230 DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 1, 0, 0), '_'), 231}; 232 233/* The guest OS has started discussing with us, finish initializing BrlAPI */ 234static int baum_deferred_init(BaumChardev *baum) 235{ 236 int tty = BRLAPI_TTY_DEFAULT; 237 QemuConsole *con; 238 239 if (baum->deferred_init) { 240 return 1; 241 } 242 243 if (brlapi__getDisplaySize(baum->brlapi, &baum->x, &baum->y) == -1) { 244 brlapi_perror("baum: brlapi__getDisplaySize"); 245 return 0; 246 } 247 if (baum->y > 1) { 248 baum->y = 1; 249 } 250 if (baum->x > 84) { 251 baum->x = 84; 252 } 253 254 con = qemu_console_lookup_by_index(0); 255 if (con && qemu_console_is_graphic(con)) { 256 tty = qemu_console_get_window_id(con); 257 if (tty == -1) 258 tty = BRLAPI_TTY_DEFAULT; 259 } 260 261 if (brlapi__enterTtyMode(baum->brlapi, tty, NULL) == -1) { 262 brlapi_perror("baum: brlapi__enterTtyMode"); 263 return 0; 264 } 265 baum->deferred_init = 1; 266 return 1; 267} 268 269/* The serial port can receive more of our data */ 270static void baum_chr_accept_input(struct Chardev *chr) 271{ 272 BaumChardev *baum = BAUM_CHARDEV(chr); 273 int room, first; 274 275 if (!baum->out_buf_used) 276 return; 277 room = qemu_chr_be_can_write(chr); 278 if (!room) 279 return; 280 if (room > baum->out_buf_used) 281 room = baum->out_buf_used; 282 283 first = BUF_SIZE - baum->out_buf_ptr; 284 if (room > first) { 285 qemu_chr_be_write(chr, baum->out_buf + baum->out_buf_ptr, first); 286 baum->out_buf_ptr = 0; 287 baum->out_buf_used -= first; 288 room -= first; 289 } 290 qemu_chr_be_write(chr, baum->out_buf + baum->out_buf_ptr, room); 291 baum->out_buf_ptr += room; 292 baum->out_buf_used -= room; 293} 294 295/* We want to send a packet */ 296static void baum_write_packet(BaumChardev *baum, const uint8_t *buf, int len) 297{ 298 Chardev *chr = CHARDEV(baum); 299 uint8_t io_buf[1 + 2 * len], *cur = io_buf; 300 int room; 301 *cur++ = ESC; 302 while (len--) 303 if ((*cur++ = *buf++) == ESC) 304 *cur++ = ESC; 305 room = qemu_chr_be_can_write(chr); 306 len = cur - io_buf; 307 if (len <= room) { 308 /* Fits */ 309 qemu_chr_be_write(chr, io_buf, len); 310 } else { 311 int first; 312 uint8_t out; 313 /* Can't fit all, send what can be, and store the rest. */ 314 qemu_chr_be_write(chr, io_buf, room); 315 len -= room; 316 cur = io_buf + room; 317 if (len > BUF_SIZE - baum->out_buf_used) { 318 /* Can't even store it, drop the previous data... */ 319 assert(len <= BUF_SIZE); 320 baum->out_buf_used = 0; 321 baum->out_buf_ptr = 0; 322 } 323 out = baum->out_buf_ptr; 324 baum->out_buf_used += len; 325 first = BUF_SIZE - baum->out_buf_ptr; 326 if (len > first) { 327 memcpy(baum->out_buf + out, cur, first); 328 out = 0; 329 len -= first; 330 cur += first; 331 } 332 memcpy(baum->out_buf + out, cur, len); 333 } 334} 335 336/* Called when the other end seems to have a wrong idea of our display size */ 337static void baum_cellCount_timer_cb(void *opaque) 338{ 339 BaumChardev *baum = BAUM_CHARDEV(opaque); 340 uint8_t cell_count[] = { BAUM_RSP_CellCount, baum->x * baum->y }; 341 DPRINTF("Timeout waiting for DisplayData, sending cell count\n"); 342 baum_write_packet(baum, cell_count, sizeof(cell_count)); 343} 344 345/* Try to interpret a whole incoming packet */ 346static int baum_eat_packet(BaumChardev *baum, const uint8_t *buf, int len) 347{ 348 const uint8_t *cur = buf; 349 uint8_t req = 0; 350 351 if (!len--) 352 return 0; 353 if (*cur++ != ESC) { 354 while (*cur != ESC) { 355 if (!len--) 356 return 0; 357 cur++; 358 } 359 DPRINTF("Dropped %td bytes!\n", cur - buf); 360 } 361 362#define EAT(c) do {\ 363 if (!len--) \ 364 return 0; \ 365 if ((c = *cur++) == ESC) { \ 366 if (!len--) \ 367 return 0; \ 368 if (*cur++ != ESC) { \ 369 DPRINTF("Broken packet %#2x, tossing\n", req); \ 370 if (timer_pending(baum->cellCount_timer)) { \ 371 timer_del(baum->cellCount_timer); \ 372 baum_cellCount_timer_cb(baum); \ 373 } \ 374 return (cur - 2 - buf); \ 375 } \ 376 } \ 377} while (0) 378 379 EAT(req); 380 switch (req) { 381 case BAUM_REQ_DisplayData: 382 { 383 uint8_t cells[baum->x * baum->y], c; 384 uint8_t text[baum->x * baum->y]; 385 uint8_t zero[baum->x * baum->y]; 386 int cursor = BRLAPI_CURSOR_OFF; 387 int i; 388 389 /* Allow 100ms to complete the DisplayData packet */ 390 timer_mod(baum->cellCount_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 391 NANOSECONDS_PER_SECOND / 10); 392 for (i = 0; i < baum->x * baum->y ; i++) { 393 EAT(c); 394 cells[i] = c; 395 if ((c & (BRLAPI_DOT7|BRLAPI_DOT8)) 396 == (BRLAPI_DOT7|BRLAPI_DOT8)) { 397 cursor = i + 1; 398 c &= ~(BRLAPI_DOT7|BRLAPI_DOT8); 399 } 400 c = nabcc_translation[DOTS2ASCII][c]; 401 if (!c) { 402 c = '?'; 403 } 404 text[i] = c; 405 } 406 timer_del(baum->cellCount_timer); 407 408 memset(zero, 0, sizeof(zero)); 409 410 brlapi_writeArguments_t wa = { 411 .displayNumber = BRLAPI_DISPLAY_DEFAULT, 412 .regionBegin = 1, 413 .regionSize = baum->x * baum->y, 414 .text = (char *)text, 415 .textSize = baum->x * baum->y, 416 .andMask = zero, 417 .orMask = cells, 418 .cursor = cursor, 419 .charset = (char *)"ISO-8859-1", 420 }; 421 422 if (brlapi__write(baum->brlapi, &wa) == -1) 423 brlapi_perror("baum brlapi_write"); 424 break; 425 } 426 case BAUM_REQ_SetMode: 427 { 428 uint8_t mode, setting; 429 DPRINTF("SetMode\n"); 430 EAT(mode); 431 EAT(setting); 432 /* ignore */ 433 break; 434 } 435 case BAUM_REQ_SetProtocol: 436 { 437 uint8_t protocol; 438 DPRINTF("SetProtocol\n"); 439 EAT(protocol); 440 /* ignore */ 441 break; 442 } 443 case BAUM_REQ_GetDeviceIdentity: 444 { 445 uint8_t identity[17] = { BAUM_RSP_DeviceIdentity, 446 'B','a','u','m',' ','V','a','r','i','o' }; 447 DPRINTF("GetDeviceIdentity\n"); 448 identity[11] = '0' + baum->x / 10; 449 identity[12] = '0' + baum->x % 10; 450 baum_write_packet(baum, identity, sizeof(identity)); 451 break; 452 } 453 case BAUM_REQ_GetVersionNumber: 454 { 455 uint8_t version[] = { BAUM_RSP_VersionNumber, 1 }; /* ? */ 456 DPRINTF("GetVersionNumber\n"); 457 baum_write_packet(baum, version, sizeof(version)); 458 break; 459 } 460 case BAUM_REQ_GetSerialNumber: 461 { 462 uint8_t serial[] = { BAUM_RSP_SerialNumber, 463 '0','0','0','0','0','0','0','0' }; 464 DPRINTF("GetSerialNumber\n"); 465 baum_write_packet(baum, serial, sizeof(serial)); 466 break; 467 } 468 case BAUM_REQ_GetKeys: 469 { 470 DPRINTF("Get%0#2x\n", req); 471 /* ignore */ 472 break; 473 } 474 default: 475 DPRINTF("unrecognized request %0#2x\n", req); 476 do 477 if (!len--) 478 return 0; 479 while (*cur++ != ESC); 480 cur--; 481 break; 482 } 483 return cur - buf; 484} 485 486/* The other end is writing some data. Store it and try to interpret */ 487static int baum_chr_write(Chardev *chr, const uint8_t *buf, int len) 488{ 489 BaumChardev *baum = BAUM_CHARDEV(chr); 490 int tocopy, cur, eaten, orig_len = len; 491 492 if (!len) 493 return 0; 494 if (!baum->brlapi) 495 return len; 496 if (!baum_deferred_init(baum)) 497 return len; 498 499 while (len) { 500 /* Complete our buffer as much as possible */ 501 tocopy = len; 502 if (tocopy > BUF_SIZE - baum->in_buf_used) 503 tocopy = BUF_SIZE - baum->in_buf_used; 504 505 memcpy(baum->in_buf + baum->in_buf_used, buf, tocopy); 506 baum->in_buf_used += tocopy; 507 buf += tocopy; 508 len -= tocopy; 509 510 /* Interpret it as much as possible */ 511 cur = 0; 512 while (cur < baum->in_buf_used && 513 (eaten = baum_eat_packet(baum, baum->in_buf + cur, baum->in_buf_used - cur))) 514 cur += eaten; 515 516 /* Shift the remainder */ 517 if (cur) { 518 memmove(baum->in_buf, baum->in_buf + cur, baum->in_buf_used - cur); 519 baum->in_buf_used -= cur; 520 } 521 522 /* And continue if any data left */ 523 } 524 return orig_len; 525} 526 527/* Send the key code to the other end */ 528static void baum_send_key(BaumChardev *baum, uint8_t type, uint8_t value) 529{ 530 uint8_t packet[] = { type, value }; 531 DPRINTF("writing key %x %x\n", type, value); 532 baum_write_packet(baum, packet, sizeof(packet)); 533} 534 535static void baum_send_key2(BaumChardev *baum, uint8_t type, uint8_t value, 536 uint8_t value2) 537{ 538 uint8_t packet[] = { type, value, value2 }; 539 DPRINTF("writing key %x %x\n", type, value); 540 baum_write_packet(baum, packet, sizeof(packet)); 541} 542 543/* We got some data on the BrlAPI socket */ 544static void baum_chr_read(void *opaque) 545{ 546 BaumChardev *baum = BAUM_CHARDEV(opaque); 547 brlapi_keyCode_t code; 548 int ret; 549 if (!baum->brlapi) 550 return; 551 if (!baum_deferred_init(baum)) 552 return; 553 while ((ret = brlapi__readKey(baum->brlapi, 0, &code)) == 1) { 554 DPRINTF("got key %"BRLAPI_PRIxKEYCODE"\n", code); 555 /* Emulate */ 556 switch (code & BRLAPI_KEY_TYPE_MASK) { 557 case BRLAPI_KEY_TYPE_CMD: 558 switch (code & BRLAPI_KEY_CMD_BLK_MASK) { 559 case BRLAPI_KEY_CMD_ROUTE: 560 baum_send_key(baum, BAUM_RSP_RoutingKey, (code & BRLAPI_KEY_CMD_ARG_MASK)+1); 561 baum_send_key(baum, BAUM_RSP_RoutingKey, 0); 562 break; 563 case 0: 564 switch (code & BRLAPI_KEY_CMD_ARG_MASK) { 565 case BRLAPI_KEY_CMD_FWINLT: 566 baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2); 567 baum_send_key(baum, BAUM_RSP_TopKeys, 0); 568 break; 569 case BRLAPI_KEY_CMD_FWINRT: 570 baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR2); 571 baum_send_key(baum, BAUM_RSP_TopKeys, 0); 572 break; 573 case BRLAPI_KEY_CMD_LNUP: 574 baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR1); 575 baum_send_key(baum, BAUM_RSP_TopKeys, 0); 576 break; 577 case BRLAPI_KEY_CMD_LNDN: 578 baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR3); 579 baum_send_key(baum, BAUM_RSP_TopKeys, 0); 580 break; 581 case BRLAPI_KEY_CMD_TOP: 582 baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL1|BAUM_TR1); 583 baum_send_key(baum, BAUM_RSP_TopKeys, 0); 584 break; 585 case BRLAPI_KEY_CMD_BOT: 586 baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL3|BAUM_TR3); 587 baum_send_key(baum, BAUM_RSP_TopKeys, 0); 588 break; 589 case BRLAPI_KEY_CMD_TOP_LEFT: 590 baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR1); 591 baum_send_key(baum, BAUM_RSP_TopKeys, 0); 592 break; 593 case BRLAPI_KEY_CMD_BOT_LEFT: 594 baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR3); 595 baum_send_key(baum, BAUM_RSP_TopKeys, 0); 596 break; 597 case BRLAPI_KEY_CMD_HOME: 598 baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR1|BAUM_TR3); 599 baum_send_key(baum, BAUM_RSP_TopKeys, 0); 600 break; 601 case BRLAPI_KEY_CMD_PREFMENU: 602 baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL1|BAUM_TL3|BAUM_TR1); 603 baum_send_key(baum, BAUM_RSP_TopKeys, 0); 604 break; 605 } 606 } 607 break; 608 case BRLAPI_KEY_TYPE_SYM: 609 { 610 brlapi_keyCode_t keysym = code & BRLAPI_KEY_CODE_MASK; 611 if (keysym < 0x100) { 612 uint8_t dots = nabcc_translation[ASCII2DOTS][keysym]; 613 if (dots) { 614 baum_send_key2(baum, BAUM_RSP_EntryKeys, 0, dots); 615 baum_send_key2(baum, BAUM_RSP_EntryKeys, 0, 0); 616 } 617 } 618 break; 619 } 620 } 621 } 622 if (ret == -1 && (brlapi_errno != BRLAPI_ERROR_LIBCERR || errno != EINTR)) { 623 brlapi_perror("baum: brlapi_readKey"); 624 brlapi__closeConnection(baum->brlapi); 625 g_free(baum->brlapi); 626 baum->brlapi = NULL; 627 } 628} 629 630static void char_braille_finalize(Object *obj) 631{ 632 BaumChardev *baum = BAUM_CHARDEV(obj); 633 634 timer_free(baum->cellCount_timer); 635 if (baum->brlapi) { 636 brlapi__closeConnection(baum->brlapi); 637 g_free(baum->brlapi); 638 } 639} 640 641static void baum_chr_open(Chardev *chr, 642 ChardevBackend *backend, 643 bool *be_opened, 644 Error **errp) 645{ 646 BaumChardev *baum = BAUM_CHARDEV(chr); 647 brlapi_handle_t *handle; 648 649 handle = g_malloc0(brlapi_getHandleSize()); 650 baum->brlapi = handle; 651 652 baum->brlapi_fd = brlapi__openConnection(handle, NULL, NULL); 653 if (baum->brlapi_fd == -1) { 654 error_setg(errp, "brlapi__openConnection: %s", 655 brlapi_strerror(brlapi_error_location())); 656 g_free(handle); 657 baum->brlapi = NULL; 658 return; 659 } 660 baum->deferred_init = 0; 661 662 baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum); 663 664 qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum); 665} 666 667static void char_braille_class_init(ObjectClass *oc, void *data) 668{ 669 ChardevClass *cc = CHARDEV_CLASS(oc); 670 671 cc->open = baum_chr_open; 672 cc->chr_write = baum_chr_write; 673 cc->chr_accept_input = baum_chr_accept_input; 674} 675 676static const TypeInfo char_braille_type_info = { 677 .name = TYPE_CHARDEV_BRAILLE, 678 .parent = TYPE_CHARDEV, 679 .instance_size = sizeof(BaumChardev), 680 .instance_finalize = char_braille_finalize, 681 .class_init = char_braille_class_init, 682}; 683module_obj(TYPE_CHARDEV_BRAILLE); 684 685static void register_types(void) 686{ 687 type_register_static(&char_braille_type_info); 688} 689 690type_init(register_types);