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