Merge tag 'selinux-pr-20210629' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.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 MODULE_LICENSE("GPL");
28
29 /*
30  * Braille device support part.
31  */
32
33 /* Emit various sounds */
34 static bool sound;
35 module_param(sound, bool, 0);
36 MODULE_PARM_DESC(sound, "emit sounds");
37
38 static void beep(unsigned int freq)
39 {
40         if (sound)
41                 kd_mksound(freq, HZ/10);
42 }
43
44 /* mini console */
45 #define WIDTH 40
46 #define BRAILLE_KEY KEY_INSERT
47 static u16 console_buf[WIDTH];
48 static int console_cursor;
49
50 /* mini view of VC */
51 static int vc_x, vc_y, lastvc_x, lastvc_y;
52
53 /* show console ? (or show VC) */
54 static int console_show = 1;
55 /* pending newline ? */
56 static int console_newline = 1;
57 static int lastVC = -1;
58
59 static struct console *braille_co;
60
61 /* Very VisioBraille-specific */
62 static void braille_write(u16 *buf)
63 {
64         static u16 lastwrite[WIDTH];
65         unsigned char data[1 + 1 + 2*WIDTH + 2 + 1], csum = 0, *c;
66         u16 out;
67         int i;
68
69         if (!braille_co)
70                 return;
71
72         if (!memcmp(lastwrite, buf, WIDTH * sizeof(*buf)))
73                 return;
74         memcpy(lastwrite, buf, WIDTH * sizeof(*buf));
75
76 #define SOH 1
77 #define STX 2
78 #define ETX 2
79 #define EOT 4
80 #define ENQ 5
81         data[0] = STX;
82         data[1] = '>';
83         csum ^= '>';
84         c = &data[2];
85         for (i = 0; i < WIDTH; i++) {
86                 out = buf[i];
87                 if (out >= 0x100)
88                         out = '?';
89                 else if (out == 0x00)
90                         out = ' ';
91                 csum ^= out;
92                 if (out <= 0x05) {
93                         *c++ = SOH;
94                         out |= 0x40;
95                 }
96                 *c++ = out;
97         }
98
99         if (csum <= 0x05) {
100                 *c++ = SOH;
101                 csum |= 0x40;
102         }
103         *c++ = csum;
104         *c++ = ETX;
105
106         braille_co->write(braille_co, data, c - data);
107 }
108
109 /* Follow the VC cursor*/
110 static void vc_follow_cursor(struct vc_data *vc)
111 {
112         vc_x = vc->state.x - (vc->state.x % WIDTH);
113         vc_y = vc->state.y;
114         lastvc_x = vc->state.x;
115         lastvc_y = vc->state.y;
116 }
117
118 /* Maybe the VC cursor moved, if so follow it */
119 static void vc_maybe_cursor_moved(struct vc_data *vc)
120 {
121         if (vc->state.x != lastvc_x || vc->state.y != lastvc_y)
122                 vc_follow_cursor(vc);
123 }
124
125 /* Show portion of VC at vc_x, vc_y */
126 static void vc_refresh(struct vc_data *vc)
127 {
128         u16 buf[WIDTH];
129         int i;
130
131         for (i = 0; i < WIDTH; i++) {
132                 u16 glyph = screen_glyph(vc,
133                                 2 * (vc_x + i) + vc_y * vc->vc_size_row);
134                 buf[i] = inverse_translate(vc, glyph, 1);
135         }
136         braille_write(buf);
137 }
138
139 /*
140  * Link to keyboard
141  */
142
143 static int keyboard_notifier_call(struct notifier_block *blk,
144                                   unsigned long code, void *_param)
145 {
146         struct keyboard_notifier_param *param = _param;
147         struct vc_data *vc = param->vc;
148         int ret = NOTIFY_OK;
149
150         if (!param->down)
151                 return ret;
152
153         switch (code) {
154         case KBD_KEYCODE:
155                 if (console_show) {
156                         if (param->value == BRAILLE_KEY) {
157                                 console_show = 0;
158                                 beep(880);
159                                 vc_maybe_cursor_moved(vc);
160                                 vc_refresh(vc);
161                                 ret = NOTIFY_STOP;
162                         }
163                 } else {
164                         ret = NOTIFY_STOP;
165                         switch (param->value) {
166                         case KEY_INSERT:
167                                 beep(440);
168                                 console_show = 1;
169                                 lastVC = -1;
170                                 braille_write(console_buf);
171                                 break;
172                         case KEY_LEFT:
173                                 if (vc_x > 0) {
174                                         vc_x -= WIDTH;
175                                         if (vc_x < 0)
176                                                 vc_x = 0;
177                                 } else if (vc_y >= 1) {
178                                         beep(880);
179                                         vc_y--;
180                                         vc_x = vc->vc_cols-WIDTH;
181                                 } else
182                                         beep(220);
183                                 break;
184                         case KEY_RIGHT:
185                                 if (vc_x + WIDTH < vc->vc_cols) {
186                                         vc_x += WIDTH;
187                                 } else if (vc_y + 1 < vc->vc_rows) {
188                                         beep(880);
189                                         vc_y++;
190                                         vc_x = 0;
191                                 } else
192                                         beep(220);
193                                 break;
194                         case KEY_DOWN:
195                                 if (vc_y + 1 < vc->vc_rows)
196                                         vc_y++;
197                                 else
198                                         beep(220);
199                                 break;
200                         case KEY_UP:
201                                 if (vc_y >= 1)
202                                         vc_y--;
203                                 else
204                                         beep(220);
205                                 break;
206                         case KEY_HOME:
207                                 vc_follow_cursor(vc);
208                                 break;
209                         case KEY_PAGEUP:
210                                 vc_x = 0;
211                                 vc_y = 0;
212                                 break;
213                         case KEY_PAGEDOWN:
214                                 vc_x = 0;
215                                 vc_y = vc->vc_rows-1;
216                                 break;
217                         default:
218                                 ret = NOTIFY_OK;
219                                 break;
220                         }
221                         if (ret == NOTIFY_STOP)
222                                 vc_refresh(vc);
223                 }
224                 break;
225         case KBD_POST_KEYSYM:
226         {
227                 unsigned char type = KTYP(param->value) - 0xf0;
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         switch (code) {
269         case VT_ALLOCATE:
270                 break;
271         case VT_DEALLOCATE:
272                 break;
273         case VT_WRITE:
274         {
275                 unsigned char c = param->c;
276                 if (vc->vc_num != fg_console)
277                         break;
278                 switch (c) {
279                 case '\b':
280                 case 127:
281                         if (console_cursor > 0) {
282                                 console_cursor--;
283                                 console_buf[console_cursor] = ' ';
284                         }
285                         break;
286                 case '\n':
287                 case '\v':
288                 case '\f':
289                 case '\r':
290                         console_newline = 1;
291                         break;
292                 case '\t':
293                         c = ' ';
294                         fallthrough;
295                 default:
296                         if (c < 32)
297                                 /* Ignore other control sequences */
298                                 break;
299                         if (console_newline) {
300                                 memset(console_buf, 0, sizeof(console_buf));
301                                 console_cursor = 0;
302                                 console_newline = 0;
303                         }
304                         if (console_cursor == WIDTH)
305                                 memmove(console_buf, &console_buf[1],
306                                         (WIDTH-1) * sizeof(*console_buf));
307                         else
308                                 console_cursor++;
309                         console_buf[console_cursor-1] = c;
310                         break;
311                 }
312                 if (console_show)
313                         braille_write(console_buf);
314                 else {
315                         vc_maybe_cursor_moved(vc);
316                         vc_refresh(vc);
317                 }
318                 break;
319         }
320         case VT_UPDATE:
321                 /* Maybe a VT switch, flush */
322                 if (console_show) {
323                         if (vc->vc_num != lastVC) {
324                                 lastVC = vc->vc_num;
325                                 memset(console_buf, 0, sizeof(console_buf));
326                                 console_cursor = 0;
327                                 braille_write(console_buf);
328                         }
329                 } else {
330                         vc_maybe_cursor_moved(vc);
331                         vc_refresh(vc);
332                 }
333                 break;
334         }
335         return NOTIFY_OK;
336 }
337
338 static struct notifier_block vt_notifier_block = {
339         .notifier_call = vt_notifier_call,
340 };
341
342 /*
343  * Called from printk.c when console=brl is given
344  */
345
346 int braille_register_console(struct console *console, int index,
347                 char *console_options, char *braille_options)
348 {
349         int ret;
350
351         if (!console_options)
352                 /* Only support VisioBraille for now */
353                 console_options = "57600o8";
354         if (braille_co)
355                 return -ENODEV;
356         if (console->setup) {
357                 ret = console->setup(console, console_options);
358                 if (ret != 0)
359                         return ret;
360         }
361         console->flags |= CON_ENABLED;
362         console->index = index;
363         braille_co = console;
364         register_keyboard_notifier(&keyboard_notifier_block);
365         register_vt_notifier(&vt_notifier_block);
366         return 1;
367 }
368
369 int braille_unregister_console(struct console *console)
370 {
371         if (braille_co != console)
372                 return -EINVAL;
373         unregister_keyboard_notifier(&keyboard_notifier_block);
374         unregister_vt_notifier(&vt_notifier_block);
375         braille_co = NULL;
376         return 1;
377 }