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