]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/accessibility/braille/braille_console.c
Basic braille screen reader support
[mirror_ubuntu-artful-kernel.git] / drivers / accessibility / braille / braille_console.c
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);