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