root/drivers/accessibility/braille/braille_console.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. beep
  2. braille_write
  3. vc_follow_cursor
  4. vc_maybe_cursor_moved
  5. vc_refresh
  6. keyboard_notifier_call
  7. vt_notifier_call
  8. braille_register_console
  9. braille_unregister_console

   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->vc_x - (vc->vc_x % WIDTH);
 113         vc_y = vc->vc_y;
 114         lastvc_x = vc->vc_x;
 115         lastvc_y = vc->vc_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->vc_x != lastvc_x || vc->vc_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                 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         case KBD_UNBOUND_KEYCODE:
 250         case KBD_UNICODE:
 251         case KBD_KEYSYM:
 252                 /* Unused */
 253                 break;
 254         }
 255         return ret;
 256 }
 257 
 258 static struct notifier_block keyboard_notifier_block = {
 259         .notifier_call = keyboard_notifier_call,
 260 };
 261 
 262 static int vt_notifier_call(struct notifier_block *blk,
 263                             unsigned long code, void *_param)
 264 {
 265         struct vt_notifier_param *param = _param;
 266         struct vc_data *vc = param->vc;
 267         switch (code) {
 268         case VT_ALLOCATE:
 269                 break;
 270         case VT_DEALLOCATE:
 271                 break;
 272         case VT_WRITE:
 273         {
 274                 unsigned char c = param->c;
 275                 if (vc->vc_num != fg_console)
 276                         break;
 277                 switch (c) {
 278                 case '\b':
 279                 case 127:
 280                         if (console_cursor > 0) {
 281                                 console_cursor--;
 282                                 console_buf[console_cursor] = ' ';
 283                         }
 284                         break;
 285                 case '\n':
 286                 case '\v':
 287                 case '\f':
 288                 case '\r':
 289                         console_newline = 1;
 290                         break;
 291                 case '\t':
 292                         c = ' ';
 293                         /* Fallthrough */
 294                 default:
 295                         if (c < 32)
 296                                 /* Ignore other control sequences */
 297                                 break;
 298                         if (console_newline) {
 299                                 memset(console_buf, 0, sizeof(console_buf));
 300                                 console_cursor = 0;
 301                                 console_newline = 0;
 302                         }
 303                         if (console_cursor == WIDTH)
 304                                 memmove(console_buf, &console_buf[1],
 305                                         (WIDTH-1) * sizeof(*console_buf));
 306                         else
 307                                 console_cursor++;
 308                         console_buf[console_cursor-1] = c;
 309                         break;
 310                 }
 311                 if (console_show)
 312                         braille_write(console_buf);
 313                 else {
 314                         vc_maybe_cursor_moved(vc);
 315                         vc_refresh(vc);
 316                 }
 317                 break;
 318         }
 319         case VT_UPDATE:
 320                 /* Maybe a VT switch, flush */
 321                 if (console_show) {
 322                         if (vc->vc_num != lastVC) {
 323                                 lastVC = vc->vc_num;
 324                                 memset(console_buf, 0, sizeof(console_buf));
 325                                 console_cursor = 0;
 326                                 braille_write(console_buf);
 327                         }
 328                 } else {
 329                         vc_maybe_cursor_moved(vc);
 330                         vc_refresh(vc);
 331                 }
 332                 break;
 333         }
 334         return NOTIFY_OK;
 335 }
 336 
 337 static struct notifier_block vt_notifier_block = {
 338         .notifier_call = vt_notifier_call,
 339 };
 340 
 341 /*
 342  * Called from printk.c when console=brl is given
 343  */
 344 
 345 int braille_register_console(struct console *console, int index,
 346                 char *console_options, char *braille_options)
 347 {
 348         int ret;
 349 
 350         if (!(console->flags & CON_BRL))
 351                 return 0;
 352         if (!console_options)
 353                 /* Only support VisioBraille for now */
 354                 console_options = "57600o8";
 355         if (braille_co)
 356                 return -ENODEV;
 357         if (console->setup) {
 358                 ret = console->setup(console, console_options);
 359                 if (ret != 0)
 360                         return ret;
 361         }
 362         console->flags |= CON_ENABLED;
 363         console->index = index;
 364         braille_co = console;
 365         register_keyboard_notifier(&keyboard_notifier_block);
 366         register_vt_notifier(&vt_notifier_block);
 367         return 1;
 368 }
 369 
 370 int braille_unregister_console(struct console *console)
 371 {
 372         if (braille_co != console)
 373                 return -EINVAL;
 374         if (!(console->flags & CON_BRL))
 375                 return 0;
 376         unregister_keyboard_notifier(&keyboard_notifier_block);
 377         unregister_vt_notifier(&vt_notifier_block);
 378         braille_co = NULL;
 379         return 1;
 380 }

/* [<][>][^][v][top][bottom][index][help] */