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