diff options
| author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2008-04-30 00:54:51 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-30 08:29:52 -0700 | 
| commit | f7511d5f66f01fc451747b24e79f3ada7a3af9af (patch) | |
| tree | 934196c15e43077641e35286078a6753700a3e3d /drivers/accessibility | |
| parent | 730f412c08c13858f7681bac0a2770fbc9159fed (diff) | |
| download | linux-f7511d5f66f01fc451747b24e79f3ada7a3af9af.tar.bz2 | |
Basic braille screen reader support
This adds a minimalistic braille screen reader support.  This is meant to
be used by blind people e.g.  on boot failures or when / cannot be mounted
etc and thus the userland screen readers can not work.
[akpm@linux-foundation.org: fix exports]
Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Cc: Jiri Kosina <jikos@jikos.cz>
Cc: Dmitry Torokhov <dtor@mail.ru>
Acked-by: Alan Cox <alan@redhat.com>
Cc: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/accessibility')
| -rw-r--r-- | drivers/accessibility/Kconfig | 23 | ||||
| -rw-r--r-- | drivers/accessibility/Makefile | 1 | ||||
| -rw-r--r-- | drivers/accessibility/braille/Makefile | 1 | ||||
| -rw-r--r-- | drivers/accessibility/braille/braille_console.c | 397 | 
4 files changed, 422 insertions, 0 deletions
| diff --git a/drivers/accessibility/Kconfig b/drivers/accessibility/Kconfig new file mode 100644 index 000000000000..1264c4b98094 --- /dev/null +++ b/drivers/accessibility/Kconfig @@ -0,0 +1,23 @@ +menuconfig ACCESSIBILITY +	bool "Accessibility support" +	---help--- +	  Enable a submenu where accessibility items may be enabled. + +	  If unsure, say N. + +if ACCESSIBILITY +config A11Y_BRAILLE_CONSOLE +	bool "Console on braille device" +	depends on VT +	depends on SERIAL_CORE_CONSOLE +	---help--- +	  Enables console output on a braille device connected to a 8250 +	  serial port. For now only the VisioBraille device is supported. + +	  To actually enable it, you need to pass option +	  console=brl,ttyS0 +	  to the kernel. Options are the same as for serial console. + +	  If unsure, say N. + +endif # ACCESSIBILITY diff --git a/drivers/accessibility/Makefile b/drivers/accessibility/Makefile new file mode 100644 index 000000000000..72b01a46546f --- /dev/null +++ b/drivers/accessibility/Makefile @@ -0,0 +1 @@ +obj-y				+= braille/ diff --git a/drivers/accessibility/braille/Makefile b/drivers/accessibility/braille/Makefile new file mode 100644 index 000000000000..2e9f16c91347 --- /dev/null +++ b/drivers/accessibility/braille/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_A11Y_BRAILLE_CONSOLE)		+= braille_console.o diff --git a/drivers/accessibility/braille/braille_console.c b/drivers/accessibility/braille/braille_console.c new file mode 100644 index 000000000000..0a5f6b2114c5 --- /dev/null +++ b/drivers/accessibility/braille/braille_console.c @@ -0,0 +1,397 @@ +/* + * Minimalistic braille device kernel support. + * + * By default, shows console messages on the braille device. + * Pressing Insert switches to VC browsing. + * + *  Copyright (C) Samuel Thibault <samuel.thibault@ens-lyon.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the program ; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/autoconf.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/console.h> +#include <linux/notifier.h> + +#include <linux/selection.h> +#include <linux/vt_kern.h> +#include <linux/consolemap.h> + +#include <linux/keyboard.h> +#include <linux/kbd_kern.h> +#include <linux/input.h> + +MODULE_AUTHOR("samuel.thibault@ens-lyon.org"); +MODULE_DESCRIPTION("braille device"); +MODULE_LICENSE("GPL"); + +/* + * Braille device support part. + */ + +/* Emit various sounds */ +static int sound; +module_param(sound, bool, 0); +MODULE_PARM_DESC(sound, "emit sounds"); + +static void beep(unsigned int freq) +{ +	if (sound) +		kd_mksound(freq, HZ/10); +} + +/* mini console */ +#define WIDTH 40 +#define BRAILLE_KEY KEY_INSERT +static u16 console_buf[WIDTH]; +static int console_cursor; + +/* mini view of VC */ +static int vc_x, vc_y, lastvc_x, lastvc_y; + +/* show console ? (or show VC) */ +static int console_show = 1; +/* pending newline ? */ +static int console_newline = 1; +static int lastVC = -1; + +static struct console *braille_co; + +/* Very VisioBraille-specific */ +static void braille_write(u16 *buf) +{ +	static u16 lastwrite[WIDTH]; +	unsigned char data[1 + 1 + 2*WIDTH + 2 + 1], csum = 0, *c; +	u16 out; +	int i; + +	if (!braille_co) +		return; + +	if (!memcmp(lastwrite, buf, WIDTH * sizeof(*buf))) +		return; +	memcpy(lastwrite, buf, WIDTH * sizeof(*buf)); + +#define SOH 1 +#define STX 2 +#define ETX 2 +#define EOT 4 +#define ENQ 5 +	data[0] = STX; +	data[1] = '>'; +	csum ^= '>'; +	c = &data[2]; +	for (i = 0; i < WIDTH; i++) { +		out = buf[i]; +		if (out >= 0x100) +			out = '?'; +		else if (out == 0x00) +			out = ' '; +		csum ^= out; +		if (out <= 0x05) { +			*c++ = SOH; +			out |= 0x40; +		} +		*c++ = out; +	} + +	if (csum <= 0x05) { +		*c++ = SOH; +		csum |= 0x40; +	} +	*c++ = csum; +	*c++ = ETX; + +	braille_co->write(braille_co, data, c - data); +} + +/* Follow the VC cursor*/ +static void vc_follow_cursor(struct vc_data *vc) +{ +	vc_x = vc->vc_x - (vc->vc_x % WIDTH); +	vc_y = vc->vc_y; +	lastvc_x = vc->vc_x; +	lastvc_y = vc->vc_y; +} + +/* Maybe the VC cursor moved, if so follow it */ +static void vc_maybe_cursor_moved(struct vc_data *vc) +{ +	if (vc->vc_x != lastvc_x || vc->vc_y != lastvc_y) +		vc_follow_cursor(vc); +} + +/* Show portion of VC at vc_x, vc_y */ +static void vc_refresh(struct vc_data *vc) +{ +	u16 buf[WIDTH]; +	int i; + +	for (i = 0; i < WIDTH; i++) { +		u16 glyph = screen_glyph(vc, +				2 * (vc_x + i) + vc_y * vc->vc_size_row); +		buf[i] = inverse_translate(vc, glyph, 1); +	} +	braille_write(buf); +} + +/* + * Link to keyboard + */ + +static int keyboard_notifier_call(struct notifier_block *blk, +				  unsigned long code, void *_param) +{ +	struct keyboard_notifier_param *param = _param; +	struct vc_data *vc = param->vc; +	int ret = NOTIFY_OK; + +	if (!param->down) +		return ret; + +	switch (code) { +	case KBD_KEYCODE: +		if (console_show) { +			if (param->value == BRAILLE_KEY) { +				console_show = 0; +				beep(880); +				vc_maybe_cursor_moved(vc); +				vc_refresh(vc); +				ret = NOTIFY_STOP; +			} +		} else { +			ret = NOTIFY_STOP; +			switch (param->value) { +			case KEY_INSERT: +				beep(440); +				console_show = 1; +				lastVC = -1; +				braille_write(console_buf); +				break; +			case KEY_LEFT: +				if (vc_x > 0) { +					vc_x -= WIDTH; +					if (vc_x < 0) +						vc_x = 0; +				} else if (vc_y >= 1) { +					beep(880); +					vc_y--; +					vc_x = vc->vc_cols-WIDTH; +				} else +					beep(220); +				break; +			case KEY_RIGHT: +				if (vc_x + WIDTH < vc->vc_cols) { +					vc_x += WIDTH; +				} else if (vc_y + 1 < vc->vc_rows) { +					beep(880); +					vc_y++; +					vc_x = 0; +				} else +					beep(220); +				break; +			case KEY_DOWN: +				if (vc_y + 1 < vc->vc_rows) +					vc_y++; +				else +					beep(220); +				break; +			case KEY_UP: +				if (vc_y >= 1) +					vc_y--; +				else +					beep(220); +				break; +			case KEY_HOME: +				vc_follow_cursor(vc); +				break; +			case KEY_PAGEUP: +				vc_x = 0; +				vc_y = 0; +				break; +			case KEY_PAGEDOWN: +				vc_x = 0; +				vc_y = vc->vc_rows-1; +				break; +			default: +				ret = NOTIFY_OK; +				break; +			} +			if (ret == NOTIFY_STOP) +				vc_refresh(vc); +		} +		break; +	case KBD_POST_KEYSYM: +	{ +		unsigned char type = KTYP(param->value) - 0xf0; +		if (type == KT_SPEC) { +			unsigned char val = KVAL(param->value); +			int on_off = -1; + +			switch (val) { +			case KVAL(K_CAPS): +				on_off = vc_kbd_led(kbd_table + fg_console, +						VC_CAPSLOCK); +				break; +			case KVAL(K_NUM): +				on_off = vc_kbd_led(kbd_table + fg_console, +						VC_NUMLOCK); +				break; +			case KVAL(K_HOLD): +				on_off = vc_kbd_led(kbd_table + fg_console, +						VC_SCROLLOCK); +				break; +			} +			if (on_off == 1) +				beep(880); +			else if (on_off == 0) +				beep(440); +		} +	} +	case KBD_UNBOUND_KEYCODE: +	case KBD_UNICODE: +	case KBD_KEYSYM: +		/* Unused */ +		break; +	} +	return ret; +} + +static struct notifier_block keyboard_notifier_block = { +	.notifier_call = keyboard_notifier_call, +}; + +static int vt_notifier_call(struct notifier_block *blk, +			    unsigned long code, void *_param) +{ +	struct vt_notifier_param *param = _param; +	struct vc_data *vc = param->vc; +	switch (code) { +	case VT_ALLOCATE: +		break; +	case VT_DEALLOCATE: +		break; +	case VT_WRITE: +	{ +		unsigned char c = param->c; +		if (vc->vc_num != fg_console) +			break; +		switch (c) { +		case '\b': +		case 127: +			if (console_cursor > 0) { +				console_cursor--; +				console_buf[console_cursor] = ' '; +			} +			break; +		case '\n': +		case '\v': +		case '\f': +		case '\r': +			console_newline = 1; +			break; +		case '\t': +			c = ' '; +			/* Fallthrough */ +		default: +			if (c < 32) +				/* Ignore other control sequences */ +				break; +			if (console_newline) { +				memset(console_buf, 0, sizeof(console_buf)); +				console_cursor = 0; +				console_newline = 0; +			} +			if (console_cursor == WIDTH) +				memmove(console_buf, &console_buf[1], +					(WIDTH-1) * sizeof(*console_buf)); +			else +				console_cursor++; +			console_buf[console_cursor-1] = c; +			break; +		} +		if (console_show) +			braille_write(console_buf); +		else { +			vc_maybe_cursor_moved(vc); +			vc_refresh(vc); +		} +		break; +	} +	case VT_UPDATE: +		/* Maybe a VT switch, flush */ +		if (console_show) { +			if (vc->vc_num != lastVC) { +				lastVC = vc->vc_num; +				memset(console_buf, 0, sizeof(console_buf)); +				console_cursor = 0; +				braille_write(console_buf); +			} +		} else { +			vc_maybe_cursor_moved(vc); +			vc_refresh(vc); +		} +		break; +	} +	return NOTIFY_OK; +} + +static struct notifier_block vt_notifier_block = { +	.notifier_call = vt_notifier_call, +}; + +/* + * Called from printk.c when console=brl is given + */ + +int braille_register_console(struct console *console, int index, +		char *console_options, char *braille_options) +{ +	int ret; +	if (!console_options) +		/* Only support VisioBraille for now */ +		console_options = "57600o8"; +	if (braille_co) +		return -ENODEV; +	if (console->setup) { +		ret = console->setup(console, console_options); +		if (ret != 0) +			return ret; +	} +	console->flags |= CON_ENABLED; +	console->index = index; +	braille_co = console; +	return 0; +} + +int braille_unregister_console(struct console *console) +{ +	if (braille_co != console) +		return -EINVAL; +	braille_co = NULL; +	return 0; +} + +static int __init braille_init(void) +{ +	register_keyboard_notifier(&keyboard_notifier_block); +	register_vt_notifier(&vt_notifier_block); +	return 0; +} + +console_initcall(braille_init); |