Merge branch 'linus' into core/rcu
[linux-2.6] / 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         register_keyboard_notifier(&keyboard_notifier_block);
380         register_vt_notifier(&vt_notifier_block);
381         return 0;
382 }
383
384 int braille_unregister_console(struct console *console)
385 {
386         if (braille_co != console)
387                 return -EINVAL;
388         unregister_keyboard_notifier(&keyboard_notifier_block);
389         unregister_vt_notifier(&vt_notifier_block);
390         braille_co = NULL;
391         return 0;
392 }