Path: blob/master/drivers/accessibility/braille/braille_console.c
15112 views
/*1* Minimalistic braille device kernel support.2*3* By default, shows console messages on the braille device.4* Pressing Insert switches to VC browsing.5*6* Copyright (C) Samuel Thibault <[email protected]>7*8* This program is free software ; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation ; either version 2 of the License, or11* (at your option) any later version.12*13* This program is distributed in the hope that it will be useful,14* but WITHOUT ANY WARRANTY ; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with the program ; if not, write to the Free Software20* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.21*/2223#include <linux/kernel.h>24#include <linux/module.h>25#include <linux/moduleparam.h>26#include <linux/console.h>27#include <linux/notifier.h>2829#include <linux/selection.h>30#include <linux/vt_kern.h>31#include <linux/consolemap.h>3233#include <linux/keyboard.h>34#include <linux/kbd_kern.h>35#include <linux/input.h>3637MODULE_AUTHOR("[email protected]");38MODULE_DESCRIPTION("braille device");39MODULE_LICENSE("GPL");4041/*42* Braille device support part.43*/4445/* Emit various sounds */46static int sound;47module_param(sound, bool, 0);48MODULE_PARM_DESC(sound, "emit sounds");4950static void beep(unsigned int freq)51{52if (sound)53kd_mksound(freq, HZ/10);54}5556/* mini console */57#define WIDTH 4058#define BRAILLE_KEY KEY_INSERT59static u16 console_buf[WIDTH];60static int console_cursor;6162/* mini view of VC */63static int vc_x, vc_y, lastvc_x, lastvc_y;6465/* show console ? (or show VC) */66static int console_show = 1;67/* pending newline ? */68static int console_newline = 1;69static int lastVC = -1;7071static struct console *braille_co;7273/* Very VisioBraille-specific */74static void braille_write(u16 *buf)75{76static u16 lastwrite[WIDTH];77unsigned char data[1 + 1 + 2*WIDTH + 2 + 1], csum = 0, *c;78u16 out;79int i;8081if (!braille_co)82return;8384if (!memcmp(lastwrite, buf, WIDTH * sizeof(*buf)))85return;86memcpy(lastwrite, buf, WIDTH * sizeof(*buf));8788#define SOH 189#define STX 290#define ETX 291#define EOT 492#define ENQ 593data[0] = STX;94data[1] = '>';95csum ^= '>';96c = &data[2];97for (i = 0; i < WIDTH; i++) {98out = buf[i];99if (out >= 0x100)100out = '?';101else if (out == 0x00)102out = ' ';103csum ^= out;104if (out <= 0x05) {105*c++ = SOH;106out |= 0x40;107}108*c++ = out;109}110111if (csum <= 0x05) {112*c++ = SOH;113csum |= 0x40;114}115*c++ = csum;116*c++ = ETX;117118braille_co->write(braille_co, data, c - data);119}120121/* Follow the VC cursor*/122static void vc_follow_cursor(struct vc_data *vc)123{124vc_x = vc->vc_x - (vc->vc_x % WIDTH);125vc_y = vc->vc_y;126lastvc_x = vc->vc_x;127lastvc_y = vc->vc_y;128}129130/* Maybe the VC cursor moved, if so follow it */131static void vc_maybe_cursor_moved(struct vc_data *vc)132{133if (vc->vc_x != lastvc_x || vc->vc_y != lastvc_y)134vc_follow_cursor(vc);135}136137/* Show portion of VC at vc_x, vc_y */138static void vc_refresh(struct vc_data *vc)139{140u16 buf[WIDTH];141int i;142143for (i = 0; i < WIDTH; i++) {144u16 glyph = screen_glyph(vc,1452 * (vc_x + i) + vc_y * vc->vc_size_row);146buf[i] = inverse_translate(vc, glyph, 1);147}148braille_write(buf);149}150151/*152* Link to keyboard153*/154155static int keyboard_notifier_call(struct notifier_block *blk,156unsigned long code, void *_param)157{158struct keyboard_notifier_param *param = _param;159struct vc_data *vc = param->vc;160int ret = NOTIFY_OK;161162if (!param->down)163return ret;164165switch (code) {166case KBD_KEYCODE:167if (console_show) {168if (param->value == BRAILLE_KEY) {169console_show = 0;170beep(880);171vc_maybe_cursor_moved(vc);172vc_refresh(vc);173ret = NOTIFY_STOP;174}175} else {176ret = NOTIFY_STOP;177switch (param->value) {178case KEY_INSERT:179beep(440);180console_show = 1;181lastVC = -1;182braille_write(console_buf);183break;184case KEY_LEFT:185if (vc_x > 0) {186vc_x -= WIDTH;187if (vc_x < 0)188vc_x = 0;189} else if (vc_y >= 1) {190beep(880);191vc_y--;192vc_x = vc->vc_cols-WIDTH;193} else194beep(220);195break;196case KEY_RIGHT:197if (vc_x + WIDTH < vc->vc_cols) {198vc_x += WIDTH;199} else if (vc_y + 1 < vc->vc_rows) {200beep(880);201vc_y++;202vc_x = 0;203} else204beep(220);205break;206case KEY_DOWN:207if (vc_y + 1 < vc->vc_rows)208vc_y++;209else210beep(220);211break;212case KEY_UP:213if (vc_y >= 1)214vc_y--;215else216beep(220);217break;218case KEY_HOME:219vc_follow_cursor(vc);220break;221case KEY_PAGEUP:222vc_x = 0;223vc_y = 0;224break;225case KEY_PAGEDOWN:226vc_x = 0;227vc_y = vc->vc_rows-1;228break;229default:230ret = NOTIFY_OK;231break;232}233if (ret == NOTIFY_STOP)234vc_refresh(vc);235}236break;237case KBD_POST_KEYSYM:238{239unsigned char type = KTYP(param->value) - 0xf0;240if (type == KT_SPEC) {241unsigned char val = KVAL(param->value);242int on_off = -1;243244switch (val) {245case KVAL(K_CAPS):246on_off = vc_kbd_led(kbd_table + fg_console,247VC_CAPSLOCK);248break;249case KVAL(K_NUM):250on_off = vc_kbd_led(kbd_table + fg_console,251VC_NUMLOCK);252break;253case KVAL(K_HOLD):254on_off = vc_kbd_led(kbd_table + fg_console,255VC_SCROLLOCK);256break;257}258if (on_off == 1)259beep(880);260else if (on_off == 0)261beep(440);262}263}264case KBD_UNBOUND_KEYCODE:265case KBD_UNICODE:266case KBD_KEYSYM:267/* Unused */268break;269}270return ret;271}272273static struct notifier_block keyboard_notifier_block = {274.notifier_call = keyboard_notifier_call,275};276277static int vt_notifier_call(struct notifier_block *blk,278unsigned long code, void *_param)279{280struct vt_notifier_param *param = _param;281struct vc_data *vc = param->vc;282switch (code) {283case VT_ALLOCATE:284break;285case VT_DEALLOCATE:286break;287case VT_WRITE:288{289unsigned char c = param->c;290if (vc->vc_num != fg_console)291break;292switch (c) {293case '\b':294case 127:295if (console_cursor > 0) {296console_cursor--;297console_buf[console_cursor] = ' ';298}299break;300case '\n':301case '\v':302case '\f':303case '\r':304console_newline = 1;305break;306case '\t':307c = ' ';308/* Fallthrough */309default:310if (c < 32)311/* Ignore other control sequences */312break;313if (console_newline) {314memset(console_buf, 0, sizeof(console_buf));315console_cursor = 0;316console_newline = 0;317}318if (console_cursor == WIDTH)319memmove(console_buf, &console_buf[1],320(WIDTH-1) * sizeof(*console_buf));321else322console_cursor++;323console_buf[console_cursor-1] = c;324break;325}326if (console_show)327braille_write(console_buf);328else {329vc_maybe_cursor_moved(vc);330vc_refresh(vc);331}332break;333}334case VT_UPDATE:335/* Maybe a VT switch, flush */336if (console_show) {337if (vc->vc_num != lastVC) {338lastVC = vc->vc_num;339memset(console_buf, 0, sizeof(console_buf));340console_cursor = 0;341braille_write(console_buf);342}343} else {344vc_maybe_cursor_moved(vc);345vc_refresh(vc);346}347break;348}349return NOTIFY_OK;350}351352static struct notifier_block vt_notifier_block = {353.notifier_call = vt_notifier_call,354};355356/*357* Called from printk.c when console=brl is given358*/359360int braille_register_console(struct console *console, int index,361char *console_options, char *braille_options)362{363int ret;364if (!console_options)365/* Only support VisioBraille for now */366console_options = "57600o8";367if (braille_co)368return -ENODEV;369if (console->setup) {370ret = console->setup(console, console_options);371if (ret != 0)372return ret;373}374console->flags |= CON_ENABLED;375console->index = index;376braille_co = console;377register_keyboard_notifier(&keyboard_notifier_block);378register_vt_notifier(&vt_notifier_block);379return 0;380}381382int braille_unregister_console(struct console *console)383{384if (braille_co != console)385return -EINVAL;386unregister_keyboard_notifier(&keyboard_notifier_block);387unregister_vt_notifier(&vt_notifier_block);388braille_co = NULL;389return 0;390}391392393