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