]> Git Repo - J-linux.git/blob - drivers/accessibility/braille/braille_console.c
Merge remote-tracking branch 'spi/for-5.14' into spi-linus
[J-linux.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 <[email protected]>
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("[email protected]");
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
229                 if (type == KT_SPEC) {
230                         unsigned char val = KVAL(param->value);
231                         int on_off = -1;
232
233                         switch (val) {
234                         case KVAL(K_CAPS):
235                                 on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
236                                 break;
237                         case KVAL(K_NUM):
238                                 on_off = vt_get_leds(fg_console, VC_NUMLOCK);
239                                 break;
240                         case KVAL(K_HOLD):
241                                 on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
242                                 break;
243                         }
244                         if (on_off == 1)
245                                 beep(880);
246                         else if (on_off == 0)
247                                 beep(440);
248                 }
249         }
250                 break;
251         case KBD_UNBOUND_KEYCODE:
252         case KBD_UNICODE:
253         case KBD_KEYSYM:
254                 /* Unused */
255                 break;
256         }
257         return ret;
258 }
259
260 static struct notifier_block keyboard_notifier_block = {
261         .notifier_call = keyboard_notifier_call,
262 };
263
264 static int vt_notifier_call(struct notifier_block *blk,
265                             unsigned long code, void *_param)
266 {
267         struct vt_notifier_param *param = _param;
268         struct vc_data *vc = param->vc;
269
270         switch (code) {
271         case VT_ALLOCATE:
272                 break;
273         case VT_DEALLOCATE:
274                 break;
275         case VT_WRITE:
276         {
277                 unsigned char c = param->c;
278
279                 if (vc->vc_num != fg_console)
280                         break;
281                 switch (c) {
282                 case '\b':
283                 case 127:
284                         if (console_cursor > 0) {
285                                 console_cursor--;
286                                 console_buf[console_cursor] = ' ';
287                         }
288                         break;
289                 case '\n':
290                 case '\v':
291                 case '\f':
292                 case '\r':
293                         console_newline = 1;
294                         break;
295                 case '\t':
296                         c = ' ';
297                         fallthrough;
298                 default:
299                         if (c < 32)
300                                 /* Ignore other control sequences */
301                                 break;
302                         if (console_newline) {
303                                 memset(console_buf, 0, sizeof(console_buf));
304                                 console_cursor = 0;
305                                 console_newline = 0;
306                         }
307                         if (console_cursor == WIDTH)
308                                 memmove(console_buf, &console_buf[1],
309                                         (WIDTH-1) * sizeof(*console_buf));
310                         else
311                                 console_cursor++;
312                         console_buf[console_cursor-1] = c;
313                         break;
314                 }
315                 if (console_show)
316                         braille_write(console_buf);
317                 else {
318                         vc_maybe_cursor_moved(vc);
319                         vc_refresh(vc);
320                 }
321                 break;
322         }
323         case VT_UPDATE:
324                 /* Maybe a VT switch, flush */
325                 if (console_show) {
326                         if (vc->vc_num != lastVC) {
327                                 lastVC = vc->vc_num;
328                                 memset(console_buf, 0, sizeof(console_buf));
329                                 console_cursor = 0;
330                                 braille_write(console_buf);
331                         }
332                 } else {
333                         vc_maybe_cursor_moved(vc);
334                         vc_refresh(vc);
335                 }
336                 break;
337         }
338         return NOTIFY_OK;
339 }
340
341 static struct notifier_block vt_notifier_block = {
342         .notifier_call = vt_notifier_call,
343 };
344
345 /*
346  * Called from printk.c when console=brl is given
347  */
348
349 int braille_register_console(struct console *console, int index,
350                 char *console_options, char *braille_options)
351 {
352         int ret;
353
354         if (!console_options)
355                 /* Only support VisioBraille for now */
356                 console_options = "57600o8";
357         if (braille_co)
358                 return -ENODEV;
359         if (console->setup) {
360                 ret = console->setup(console, console_options);
361                 if (ret != 0)
362                         return ret;
363         }
364         console->flags |= CON_ENABLED;
365         console->index = index;
366         braille_co = console;
367         register_keyboard_notifier(&keyboard_notifier_block);
368         register_vt_notifier(&vt_notifier_block);
369         return 1;
370 }
371
372 int braille_unregister_console(struct console *console)
373 {
374         if (braille_co != console)
375                 return -EINVAL;
376         unregister_keyboard_notifier(&keyboard_notifier_block);
377         unregister_vt_notifier(&vt_notifier_block);
378         braille_co = NULL;
379         return 1;
380 }
This page took 0.054637 seconds and 4 git commands to generate.