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