GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / accessibility / braille / braille_console.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Minimalistic braille device kernel support.
4  *
5  * By default, shows console messages on the braille device.
6  * Pressing Insert switches to VC browsing.
7  *
8  *  Copyright (C) Samuel Thibault <samuel.thibault@ens-lyon.org>
9  */
10
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/moduleparam.h>
14 #include <linux/console.h>
15 #include <linux/notifier.h>
16
17 #include <linux/selection.h>
18 #include <linux/vt_kern.h>
19 #include <linux/consolemap.h>
20
21 #include <linux/keyboard.h>
22 #include <linux/kbd_kern.h>
23 #include <linux/input.h>
24
25 MODULE_AUTHOR("samuel.thibault@ens-lyon.org");
26 MODULE_DESCRIPTION("braille device");
27
28 /*
29  * Braille device support part.
30  */
31
32 /* Emit various sounds */
33 static bool sound;
34 module_param(sound, bool, 0);
35 MODULE_PARM_DESC(sound, "emit sounds");
36
37 static void beep(unsigned int freq)
38 {
39         if (sound)
40                 kd_mksound(freq, HZ/10);
41 }
42
43 /* mini console */
44 #define WIDTH 40
45 #define BRAILLE_KEY KEY_INSERT
46 static u16 console_buf[WIDTH];
47 static int console_cursor;
48
49 /* mini view of VC */
50 static int vc_x, vc_y, lastvc_x, lastvc_y;
51
52 /* show console ? (or show VC) */
53 static int console_show = 1;
54 /* pending newline ? */
55 static int console_newline = 1;
56 static int lastVC = -1;
57
58 static struct console *braille_co;
59
60 /* Very VisioBraille-specific */
61 static void braille_write(u16 *buf)
62 {
63         static u16 lastwrite[WIDTH];
64         unsigned char data[1 + 1 + 2*WIDTH + 2 + 1], csum = 0, *c;
65         u16 out;
66         int i;
67
68         if (!braille_co)
69                 return;
70
71         if (!memcmp(lastwrite, buf, WIDTH * sizeof(*buf)))
72                 return;
73         memcpy(lastwrite, buf, WIDTH * sizeof(*buf));
74
75 #define SOH 1
76 #define STX 2
77 #define ETX 2
78 #define EOT 4
79 #define ENQ 5
80         data[0] = STX;
81         data[1] = '>';
82         csum ^= '>';
83         c = &data[2];
84         for (i = 0; i < WIDTH; i++) {
85                 out = buf[i];
86                 if (out >= 0x100)
87                         out = '?';
88                 else if (out == 0x00)
89                         out = ' ';
90                 csum ^= out;
91                 if (out <= 0x05) {
92                         *c++ = SOH;
93                         out |= 0x40;
94                 }
95                 *c++ = out;
96         }
97
98         if (csum <= 0x05) {
99                 *c++ = SOH;
100                 csum |= 0x40;
101         }
102         *c++ = csum;
103         *c++ = ETX;
104
105         braille_co->write(braille_co, data, c - data);
106 }
107
108 /* Follow the VC cursor*/
109 static void vc_follow_cursor(struct vc_data *vc)
110 {
111         vc_x = vc->state.x - (vc->state.x % WIDTH);
112         vc_y = vc->state.y;
113         lastvc_x = vc->state.x;
114         lastvc_y = vc->state.y;
115 }
116
117 /* Maybe the VC cursor moved, if so follow it */
118 static void vc_maybe_cursor_moved(struct vc_data *vc)
119 {
120         if (vc->state.x != lastvc_x || vc->state.y != lastvc_y)
121                 vc_follow_cursor(vc);
122 }
123
124 /* Show portion of VC at vc_x, vc_y */
125 static void vc_refresh(struct vc_data *vc)
126 {
127         u16 buf[WIDTH];
128         int i;
129
130         for (i = 0; i < WIDTH; i++) {
131                 u16 glyph = screen_glyph(vc,
132                                 2 * (vc_x + i) + vc_y * vc->vc_size_row);
133                 buf[i] = inverse_translate(vc, glyph, true);
134         }
135         braille_write(buf);
136 }
137
138 /*
139  * Link to keyboard
140  */
141
142 static int keyboard_notifier_call(struct notifier_block *blk,
143                                   unsigned long code, void *_param)
144 {
145         struct keyboard_notifier_param *param = _param;
146         struct vc_data *vc = param->vc;
147         int ret = NOTIFY_OK;
148
149         if (!param->down)
150                 return ret;
151
152         switch (code) {
153         case KBD_KEYCODE:
154                 if (console_show) {
155                         if (param->value == BRAILLE_KEY) {
156                                 console_show = 0;
157                                 beep(880);
158                                 vc_maybe_cursor_moved(vc);
159                                 vc_refresh(vc);
160                                 ret = NOTIFY_STOP;
161                         }
162                 } else {
163                         ret = NOTIFY_STOP;
164                         switch (param->value) {
165                         case KEY_INSERT:
166                                 beep(440);
167                                 console_show = 1;
168                                 lastVC = -1;
169                                 braille_write(console_buf);
170                                 break;
171                         case KEY_LEFT:
172                                 if (vc_x > 0) {
173                                         vc_x -= WIDTH;
174                                         if (vc_x < 0)
175                                                 vc_x = 0;
176                                 } else if (vc_y >= 1) {
177                                         beep(880);
178                                         vc_y--;
179                                         vc_x = vc->vc_cols-WIDTH;
180                                 } else
181                                         beep(220);
182                                 break;
183                         case KEY_RIGHT:
184                                 if (vc_x + WIDTH < vc->vc_cols) {
185                                         vc_x += WIDTH;
186                                 } else if (vc_y + 1 < vc->vc_rows) {
187                                         beep(880);
188                                         vc_y++;
189                                         vc_x = 0;
190                                 } else
191                                         beep(220);
192                                 break;
193                         case KEY_DOWN:
194                                 if (vc_y + 1 < vc->vc_rows)
195                                         vc_y++;
196                                 else
197                                         beep(220);
198                                 break;
199                         case KEY_UP:
200                                 if (vc_y >= 1)
201                                         vc_y--;
202                                 else
203                                         beep(220);
204                                 break;
205                         case KEY_HOME:
206                                 vc_follow_cursor(vc);
207                                 break;
208                         case KEY_PAGEUP:
209                                 vc_x = 0;
210                                 vc_y = 0;
211                                 break;
212                         case KEY_PAGEDOWN:
213                                 vc_x = 0;
214                                 vc_y = vc->vc_rows-1;
215                                 break;
216                         default:
217                                 ret = NOTIFY_OK;
218                                 break;
219                         }
220                         if (ret == NOTIFY_STOP)
221                                 vc_refresh(vc);
222                 }
223                 break;
224         case KBD_POST_KEYSYM:
225         {
226                 unsigned char type = KTYP(param->value) - 0xf0;
227
228                 if (type == KT_SPEC) {
229                         unsigned char val = KVAL(param->value);
230                         int on_off = -1;
231
232                         switch (val) {
233                         case KVAL(K_CAPS):
234                                 on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
235                                 break;
236                         case KVAL(K_NUM):
237                                 on_off = vt_get_leds(fg_console, VC_NUMLOCK);
238                                 break;
239                         case KVAL(K_HOLD):
240                                 on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
241                                 break;
242                         }
243                         if (on_off == 1)
244                                 beep(880);
245                         else if (on_off == 0)
246                                 beep(440);
247                 }
248         }
249                 break;
250         case KBD_UNBOUND_KEYCODE:
251         case KBD_UNICODE:
252         case KBD_KEYSYM:
253                 /* Unused */
254                 break;
255         }
256         return ret;
257 }
258
259 static struct notifier_block keyboard_notifier_block = {
260         .notifier_call = keyboard_notifier_call,
261 };
262
263 static int vt_notifier_call(struct notifier_block *blk,
264                             unsigned long code, void *_param)
265 {
266         struct vt_notifier_param *param = _param;
267         struct vc_data *vc = param->vc;
268
269         switch (code) {
270         case VT_ALLOCATE:
271                 break;
272         case VT_DEALLOCATE:
273                 break;
274         case VT_WRITE:
275         {
276                 unsigned char c = param->c;
277
278                 if (vc->vc_num != fg_console)
279                         break;
280                 switch (c) {
281                 case '\b':
282                 case 127:
283                         if (console_cursor > 0) {
284                                 console_cursor--;
285                                 console_buf[console_cursor] = ' ';
286                         }
287                         break;
288                 case '\n':
289                 case '\v':
290                 case '\f':
291                 case '\r':
292                         console_newline = 1;
293                         break;
294                 case '\t':
295                         c = ' ';
296                         fallthrough;
297                 default:
298                         if (c < 32)
299                                 /* Ignore other control sequences */
300                                 break;
301                         if (console_newline) {
302                                 memset(console_buf, 0, sizeof(console_buf));
303                                 console_cursor = 0;
304                                 console_newline = 0;
305                         }
306                         if (console_cursor == WIDTH)
307                                 memmove(console_buf, &console_buf[1],
308                                         (WIDTH-1) * sizeof(*console_buf));
309                         else
310                                 console_cursor++;
311                         console_buf[console_cursor-1] = c;
312                         break;
313                 }
314                 if (console_show)
315                         braille_write(console_buf);
316                 else {
317                         vc_maybe_cursor_moved(vc);
318                         vc_refresh(vc);
319                 }
320                 break;
321         }
322         case VT_UPDATE:
323                 /* Maybe a VT switch, flush */
324                 if (console_show) {
325                         if (vc->vc_num != lastVC) {
326                                 lastVC = vc->vc_num;
327                                 memset(console_buf, 0, sizeof(console_buf));
328                                 console_cursor = 0;
329                                 braille_write(console_buf);
330                         }
331                 } else {
332                         vc_maybe_cursor_moved(vc);
333                         vc_refresh(vc);
334                 }
335                 break;
336         }
337         return NOTIFY_OK;
338 }
339
340 static struct notifier_block vt_notifier_block = {
341         .notifier_call = vt_notifier_call,
342 };
343
344 /*
345  * Called from printk.c when console=brl is given
346  */
347
348 int braille_register_console(struct console *console, int index,
349                 char *console_options, char *braille_options)
350 {
351         int ret;
352
353         if (!console_options)
354                 /* Only support VisioBraille for now */
355                 console_options = "57600o8";
356         if (braille_co)
357                 return -ENODEV;
358         if (console->setup) {
359                 ret = console->setup(console, console_options);
360                 if (ret != 0)
361                         return ret;
362         }
363         console->flags |= CON_ENABLED;
364         console->index = index;
365         braille_co = console;
366         register_keyboard_notifier(&keyboard_notifier_block);
367         register_vt_notifier(&vt_notifier_block);
368         return 1;
369 }
370
371 int braille_unregister_console(struct console *console)
372 {
373         if (braille_co != console)
374                 return -EINVAL;
375         unregister_keyboard_notifier(&keyboard_notifier_block);
376         unregister_vt_notifier(&vt_notifier_block);
377         braille_co = NULL;
378         return 1;
379 }