diff options
author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2008-04-30 03:54:51 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-30 11:29:52 -0400 |
commit | f7511d5f66f01fc451747b24e79f3ada7a3af9af (patch) | |
tree | 934196c15e43077641e35286078a6753700a3e3d /drivers | |
parent | 730f412c08c13858f7681bac0a2770fbc9159fed (diff) |
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')
-rw-r--r-- | drivers/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Makefile | 1 | ||||
-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 | ||||
-rw-r--r-- | drivers/char/consolemap.c | 1 | ||||
-rw-r--r-- | drivers/char/keyboard.c | 2 | ||||
-rw-r--r-- | drivers/char/vt.c | 1 |
9 files changed, 429 insertions, 0 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 80f0ec91e2cf..59f33fa6af3e 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig | |||
@@ -84,6 +84,8 @@ source "drivers/memstick/Kconfig" | |||
84 | 84 | ||
85 | source "drivers/leds/Kconfig" | 85 | source "drivers/leds/Kconfig" |
86 | 86 | ||
87 | source "drivers/accessibility/Kconfig" | ||
88 | |||
87 | source "drivers/infiniband/Kconfig" | 89 | source "drivers/infiniband/Kconfig" |
88 | 90 | ||
89 | source "drivers/edac/Kconfig" | 91 | source "drivers/edac/Kconfig" |
diff --git a/drivers/Makefile b/drivers/Makefile index e5e394a7e6c0..f65deda72d61 100644 --- a/drivers/Makefile +++ b/drivers/Makefile | |||
@@ -70,6 +70,7 @@ obj-$(CONFIG_WATCHDOG) += watchdog/ | |||
70 | obj-$(CONFIG_PHONE) += telephony/ | 70 | obj-$(CONFIG_PHONE) += telephony/ |
71 | obj-$(CONFIG_MD) += md/ | 71 | obj-$(CONFIG_MD) += md/ |
72 | obj-$(CONFIG_BT) += bluetooth/ | 72 | obj-$(CONFIG_BT) += bluetooth/ |
73 | obj-$(CONFIG_ACCESSIBILITY) += accessibility/ | ||
73 | obj-$(CONFIG_ISDN) += isdn/ | 74 | obj-$(CONFIG_ISDN) += isdn/ |
74 | obj-$(CONFIG_EDAC) += edac/ | 75 | obj-$(CONFIG_EDAC) += edac/ |
75 | obj-$(CONFIG_MCA) += mca/ | 76 | obj-$(CONFIG_MCA) += mca/ |
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 @@ | |||
1 | menuconfig ACCESSIBILITY | ||
2 | bool "Accessibility support" | ||
3 | ---help--- | ||
4 | Enable a submenu where accessibility items may be enabled. | ||
5 | |||
6 | If unsure, say N. | ||
7 | |||
8 | if ACCESSIBILITY | ||
9 | config A11Y_BRAILLE_CONSOLE | ||
10 | bool "Console on braille device" | ||
11 | depends on VT | ||
12 | depends on SERIAL_CORE_CONSOLE | ||
13 | ---help--- | ||
14 | Enables console output on a braille device connected to a 8250 | ||
15 | serial port. For now only the VisioBraille device is supported. | ||
16 | |||
17 | To actually enable it, you need to pass option | ||
18 | console=brl,ttyS0 | ||
19 | to the kernel. Options are the same as for serial console. | ||
20 | |||
21 | If unsure, say N. | ||
22 | |||
23 | 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 @@ | |||
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 | |||
39 | MODULE_AUTHOR("samuel.thibault@ens-lyon.org"); | ||
40 | MODULE_DESCRIPTION("braille device"); | ||
41 | MODULE_LICENSE("GPL"); | ||
42 | |||
43 | /* | ||
44 | * Braille device support part. | ||
45 | */ | ||
46 | |||
47 | /* Emit various sounds */ | ||
48 | static int sound; | ||
49 | module_param(sound, bool, 0); | ||
50 | MODULE_PARM_DESC(sound, "emit sounds"); | ||
51 | |||
52 | static 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 | ||
61 | static u16 console_buf[WIDTH]; | ||
62 | static int console_cursor; | ||
63 | |||
64 | /* mini view of VC */ | ||
65 | static int vc_x, vc_y, lastvc_x, lastvc_y; | ||
66 | |||
67 | /* show console ? (or show VC) */ | ||
68 | static int console_show = 1; | ||
69 | /* pending newline ? */ | ||
70 | static int console_newline = 1; | ||
71 | static int lastVC = -1; | ||
72 | |||
73 | static struct console *braille_co; | ||
74 | |||
75 | /* Very VisioBraille-specific */ | ||
76 | static 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*/ | ||
124 | static 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 */ | ||
133 | static 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 */ | ||
140 | static 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 | |||
157 | static 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 | |||
275 | static struct notifier_block keyboard_notifier_block = { | ||
276 | .notifier_call = keyboard_notifier_call, | ||
277 | }; | ||
278 | |||
279 | static 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 | |||
354 | static 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 | |||
362 | int 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 | return 0; | ||
380 | } | ||
381 | |||
382 | int braille_unregister_console(struct console *console) | ||
383 | { | ||
384 | if (braille_co != console) | ||
385 | return -EINVAL; | ||
386 | braille_co = NULL; | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static int __init braille_init(void) | ||
391 | { | ||
392 | register_keyboard_notifier(&keyboard_notifier_block); | ||
393 | register_vt_notifier(&vt_notifier_block); | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | console_initcall(braille_init); | ||
diff --git a/drivers/char/consolemap.c b/drivers/char/consolemap.c index 6b104e45a322..4246b8e36cb3 100644 --- a/drivers/char/consolemap.c +++ b/drivers/char/consolemap.c | |||
@@ -277,6 +277,7 @@ u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode) | |||
277 | return p->inverse_translations[m][glyph]; | 277 | return p->inverse_translations[m][glyph]; |
278 | } | 278 | } |
279 | } | 279 | } |
280 | EXPORT_SYMBOL_GPL(inverse_translate); | ||
280 | 281 | ||
281 | static void update_user_maps(void) | 282 | static void update_user_maps(void) |
282 | { | 283 | { |
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index d1c50b3302e5..7f7e798c1384 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c | |||
@@ -110,6 +110,7 @@ const int max_vals[] = { | |||
110 | const int NR_TYPES = ARRAY_SIZE(max_vals); | 110 | const int NR_TYPES = ARRAY_SIZE(max_vals); |
111 | 111 | ||
112 | struct kbd_struct kbd_table[MAX_NR_CONSOLES]; | 112 | struct kbd_struct kbd_table[MAX_NR_CONSOLES]; |
113 | EXPORT_SYMBOL_GPL(kbd_table); | ||
113 | static struct kbd_struct *kbd = kbd_table; | 114 | static struct kbd_struct *kbd = kbd_table; |
114 | 115 | ||
115 | struct vt_spawn_console vt_spawn_con = { | 116 | struct vt_spawn_console vt_spawn_con = { |
@@ -260,6 +261,7 @@ void kd_mksound(unsigned int hz, unsigned int ticks) | |||
260 | } else | 261 | } else |
261 | kd_nosound(0); | 262 | kd_nosound(0); |
262 | } | 263 | } |
264 | EXPORT_SYMBOL(kd_mksound); | ||
263 | 265 | ||
264 | /* | 266 | /* |
265 | * Setting the keyboard rate. | 267 | * Setting the keyboard rate. |
diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 71cf203d282d..e458b08139af 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c | |||
@@ -4004,6 +4004,7 @@ u16 screen_glyph(struct vc_data *vc, int offset) | |||
4004 | c |= 0x100; | 4004 | c |= 0x100; |
4005 | return c; | 4005 | return c; |
4006 | } | 4006 | } |
4007 | EXPORT_SYMBOL_GPL(screen_glyph); | ||
4007 | 4008 | ||
4008 | /* used by vcs - note the word offset */ | 4009 | /* used by vcs - note the word offset */ |
4009 | unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed) | 4010 | unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed) |