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