1 /* 2 * Minimalistic braille device kernel support. 3 * 4 * By default, shows console messages on the braille device. 5 * Pressing Insert switches to VC browsing. 6 * 7 * Copyright (C) Samuel Thibault <samuel.thibault@ens-lyon.org> 8 * 9 * This program is free software ; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation ; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY ; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with the program ; if not, write to the Free Software 21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 */ 23 24 #include <linux/autoconf.h> 25 #include <linux/kernel.h> 26 #include <linux/module.h> 27 #include <linux/moduleparam.h> 28 #include <linux/console.h> 29 #include <linux/notifier.h> 30 31 #include <linux/selection.h> 32 #include <linux/vt_kern.h> 33 #include <linux/consolemap.h> 34 35 #include <linux/keyboard.h> 36 #include <linux/kbd_kern.h> 37 #include <linux/input.h> 38 39 MODULE_AUTHOR("samuel.thibault@ens-lyon.org"); 40 MODULE_DESCRIPTION("braille device"); 41 MODULE_LICENSE("GPL"); 42 43 /* 44 * Braille device support part. 45 */ 46 47 /* Emit various sounds */ 48 static int sound; 49 module_param(sound, bool, 0); 50 MODULE_PARM_DESC(sound, "emit sounds"); 51 52 static void beep(unsigned int freq) 53 { 54 if (sound) 55 kd_mksound(freq, HZ/10); 56 } 57 58 /* mini console */ 59 #define WIDTH 40 60 #define BRAILLE_KEY KEY_INSERT 61 static u16 console_buf[WIDTH]; 62 static int console_cursor; 63 64 /* mini view of VC */ 65 static int vc_x, vc_y, lastvc_x, lastvc_y; 66 67 /* show console ? (or show VC) */ 68 static int console_show = 1; 69 /* pending newline ? */ 70 static int console_newline = 1; 71 static int lastVC = -1; 72 73 static struct console *braille_co; 74 75 /* Very VisioBraille-specific */ 76 static void braille_write(u16 *buf) 77 { 78 static u16 lastwrite[WIDTH]; 79 unsigned char data[1 + 1 + 2*WIDTH + 2 + 1], csum = 0, *c; 80 u16 out; 81 int i; 82 83 if (!braille_co) 84 return; 85 86 if (!memcmp(lastwrite, buf, WIDTH * sizeof(*buf))) 87 return; 88 memcpy(lastwrite, buf, WIDTH * sizeof(*buf)); 89 90 #define SOH 1 91 #define STX 2 92 #define ETX 2 93 #define EOT 4 94 #define ENQ 5 95 data[0] = STX; 96 data[1] = '>'; 97 csum ^= '>'; 98 c = &data[2]; 99 for (i = 0; i < WIDTH; i++) { 100 out = buf[i]; 101 if (out >= 0x100) 102 out = '?'; 103 else if (out == 0x00) 104 out = ' '; 105 csum ^= out; 106 if (out <= 0x05) { 107 *c++ = SOH; 108 out |= 0x40; 109 } 110 *c++ = out; 111 } 112 113 if (csum <= 0x05) { 114 *c++ = SOH; 115 csum |= 0x40; 116 } 117 *c++ = csum; 118 *c++ = ETX; 119 120 braille_co->write(braille_co, data, c - data); 121 } 122 123 /* Follow the VC cursor*/ 124 static void vc_follow_cursor(struct vc_data *vc) 125 { 126 vc_x = vc->vc_x - (vc->vc_x % WIDTH); 127 vc_y = vc->vc_y; 128 lastvc_x = vc->vc_x; 129 lastvc_y = vc->vc_y; 130 } 131 132 /* Maybe the VC cursor moved, if so follow it */ 133 static void vc_maybe_cursor_moved(struct vc_data *vc) 134 { 135 if (vc->vc_x != lastvc_x || vc->vc_y != lastvc_y) 136 vc_follow_cursor(vc); 137 } 138 139 /* Show portion of VC at vc_x, vc_y */ 140 static void vc_refresh(struct vc_data *vc) 141 { 142 u16 buf[WIDTH]; 143 int i; 144 145 for (i = 0; i < WIDTH; i++) { 146 u16 glyph = screen_glyph(vc, 147 2 * (vc_x + i) + vc_y * vc->vc_size_row); 148 buf[i] = inverse_translate(vc, glyph, 1); 149 } 150 braille_write(buf); 151 } 152 153 /* 154 * Link to keyboard 155 */ 156 157 static int keyboard_notifier_call(struct notifier_block *blk, 158 unsigned long code, void *_param) 159 { 160 struct keyboard_notifier_param *param = _param; 161 struct vc_data *vc = param->vc; 162 int ret = NOTIFY_OK; 163 164 if (!param->down) 165 return ret; 166 167 switch (code) { 168 case KBD_KEYCODE: 169 if (console_show) { 170 if (param->value == BRAILLE_KEY) { 171 console_show = 0; 172 beep(880); 173 vc_maybe_cursor_moved(vc); 174 vc_refresh(vc); 175 ret = NOTIFY_STOP; 176 } 177 } else { 178 ret = NOTIFY_STOP; 179 switch (param->value) { 180 case KEY_INSERT: 181 beep(440); 182 console_show = 1; 183 lastVC = -1; 184 braille_write(console_buf); 185 break; 186 case KEY_LEFT: 187 if (vc_x > 0) { 188 vc_x -= WIDTH; 189 if (vc_x < 0) 190 vc_x = 0; 191 } else if (vc_y >= 1) { 192 beep(880); 193 vc_y--; 194 vc_x = vc->vc_cols-WIDTH; 195 } else 196 beep(220); 197 break; 198 case KEY_RIGHT: 199 if (vc_x + WIDTH < vc->vc_cols) { 200 vc_x += WIDTH; 201 } else if (vc_y + 1 < vc->vc_rows) { 202 beep(880); 203 vc_y++; 204 vc_x = 0; 205 } else 206 beep(220); 207 break; 208 case KEY_DOWN: 209 if (vc_y + 1 < vc->vc_rows) 210 vc_y++; 211 else 212 beep(220); 213 break; 214 case KEY_UP: 215 if (vc_y >= 1) 216 vc_y--; 217 else 218 beep(220); 219 break; 220 case KEY_HOME: 221 vc_follow_cursor(vc); 222 break; 223 case KEY_PAGEUP: 224 vc_x = 0; 225 vc_y = 0; 226 break; 227 case KEY_PAGEDOWN: 228 vc_x = 0; 229 vc_y = vc->vc_rows-1; 230 break; 231 default: 232 ret = NOTIFY_OK; 233 break; 234 } 235 if (ret == NOTIFY_STOP) 236 vc_refresh(vc); 237 } 238 break; 239 case KBD_POST_KEYSYM: 240 { 241 unsigned char type = KTYP(param->value) - 0xf0; 242 if (type == KT_SPEC) { 243 unsigned char val = KVAL(param->value); 244 int on_off = -1; 245 246 switch (val) { 247 case KVAL(K_CAPS): 248 on_off = vc_kbd_led(kbd_table + fg_console, 249 VC_CAPSLOCK); 250 break; 251 case KVAL(K_NUM): 252 on_off = vc_kbd_led(kbd_table + fg_console, 253 VC_NUMLOCK); 254 break; 255 case KVAL(K_HOLD): 256 on_off = vc_kbd_led(kbd_table + fg_console, 257 VC_SCROLLOCK); 258 break; 259 } 260 if (on_off == 1) 261 beep(880); 262 else if (on_off == 0) 263 beep(440); 264 } 265 } 266 case KBD_UNBOUND_KEYCODE: 267 case KBD_UNICODE: 268 case KBD_KEYSYM: 269 /* Unused */ 270 break; 271 } 272 return ret; 273 } 274 275 static struct notifier_block keyboard_notifier_block = { 276 .notifier_call = keyboard_notifier_call, 277 }; 278 279 static int vt_notifier_call(struct notifier_block *blk, 280 unsigned long code, void *_param) 281 { 282 struct vt_notifier_param *param = _param; 283 struct vc_data *vc = param->vc; 284 switch (code) { 285 case VT_ALLOCATE: 286 break; 287 case VT_DEALLOCATE: 288 break; 289 case VT_WRITE: 290 { 291 unsigned char c = param->c; 292 if (vc->vc_num != fg_console) 293 break; 294 switch (c) { 295 case '\b': 296 case 127: 297 if (console_cursor > 0) { 298 console_cursor--; 299 console_buf[console_cursor] = ' '; 300 } 301 break; 302 case '\n': 303 case '\v': 304 case '\f': 305 case '\r': 306 console_newline = 1; 307 break; 308 case '\t': 309 c = ' '; 310 /* Fallthrough */ 311 default: 312 if (c < 32) 313 /* Ignore other control sequences */ 314 break; 315 if (console_newline) { 316 memset(console_buf, 0, sizeof(console_buf)); 317 console_cursor = 0; 318 console_newline = 0; 319 } 320 if (console_cursor == WIDTH) 321 memmove(console_buf, &console_buf[1], 322 (WIDTH-1) * sizeof(*console_buf)); 323 else 324 console_cursor++; 325 console_buf[console_cursor-1] = c; 326 break; 327 } 328 if (console_show) 329 braille_write(console_buf); 330 else { 331 vc_maybe_cursor_moved(vc); 332 vc_refresh(vc); 333 } 334 break; 335 } 336 case VT_UPDATE: 337 /* Maybe a VT switch, flush */ 338 if (console_show) { 339 if (vc->vc_num != lastVC) { 340 lastVC = vc->vc_num; 341 memset(console_buf, 0, sizeof(console_buf)); 342 console_cursor = 0; 343 braille_write(console_buf); 344 } 345 } else { 346 vc_maybe_cursor_moved(vc); 347 vc_refresh(vc); 348 } 349 break; 350 } 351 return NOTIFY_OK; 352 } 353 354 static struct notifier_block vt_notifier_block = { 355 .notifier_call = vt_notifier_call, 356 }; 357 358 /* 359 * Called from printk.c when console=brl is given 360 */ 361 362 int braille_register_console(struct console *console, int index, 363 char *console_options, char *braille_options) 364 { 365 int ret; 366 if (!console_options) 367 /* Only support VisioBraille for now */ 368 console_options = "57600o8"; 369 if (braille_co) 370 return -ENODEV; 371 if (console->setup) { 372 ret = console->setup(console, console_options); 373 if (ret != 0) 374 return ret; 375 } 376 console->flags |= CON_ENABLED; 377 console->index = index; 378 braille_co = console; 379 register_keyboard_notifier(&keyboard_notifier_block); 380 register_vt_notifier(&vt_notifier_block); 381 return 0; 382 } 383 384 int braille_unregister_console(struct console *console) 385 { 386 if (braille_co != console) 387 return -EINVAL; 388 unregister_keyboard_notifier(&keyboard_notifier_block); 389 unregister_vt_notifier(&vt_notifier_block); 390 braille_co = NULL; 391 return 0; 392 } 393