linux/drivers/accessibility/braille/braille_console.c
<<
>>
Prefs
   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
  25MODULE_AUTHOR("samuel.thibault@ens-lyon.org");
  26MODULE_DESCRIPTION("braille device");
  27MODULE_LICENSE("GPL");
  28
  29/*
  30 * Braille device support part.
  31 */
  32
  33/* Emit various sounds */
  34static bool sound;
  35module_param(sound, bool, 0);
  36MODULE_PARM_DESC(sound, "emit sounds");
  37
  38static 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
  47static u16 console_buf[WIDTH];
  48static int console_cursor;
  49
  50/* mini view of VC */
  51static int vc_x, vc_y, lastvc_x, lastvc_y;
  52
  53/* show console ? (or show VC) */
  54static int console_show = 1;
  55/* pending newline ? */
  56static int console_newline = 1;
  57static int lastVC = -1;
  58
  59static struct console *braille_co;
  60
  61/* Very VisioBraille-specific */
  62static 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*/
 110static 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 */
 119static 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 */
 126static 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
 143static 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
 260static struct notifier_block keyboard_notifier_block = {
 261        .notifier_call = keyboard_notifier_call,
 262};
 263
 264static 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
 341static 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
 349int 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
 372int 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