1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Minimalistic braille device kernel support.
5 * By default, shows console messages on the braille device.
6 * Pressing Insert switches to VC browsing.
8 * Copyright (C) Samuel Thibault <samuel.thibault@ens-lyon.org>
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>
17 #include <linux/selection.h>
18 #include <linux/vt_kern.h>
19 #include <linux/consolemap.h>
21 #include <linux/keyboard.h>
22 #include <linux/kbd_kern.h>
23 #include <linux/input.h>
25 MODULE_AUTHOR("samuel.thibault@ens-lyon.org");
26 MODULE_DESCRIPTION("braille device");
29 * Braille device support part.
32 /* Emit various sounds */
34 module_param(sound
, bool, 0);
35 MODULE_PARM_DESC(sound
, "emit sounds");
37 static void beep(unsigned int freq
)
40 kd_mksound(freq
, HZ
/10);
45 #define BRAILLE_KEY KEY_INSERT
46 static u16 console_buf
[WIDTH
];
47 static int console_cursor
;
50 static int vc_x
, vc_y
, lastvc_x
, lastvc_y
;
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;
58 static struct console
*braille_co
;
60 /* Very VisioBraille-specific */
61 static void braille_write(u16
*buf
)
63 static u16 lastwrite
[WIDTH
];
64 unsigned char data
[1 + 1 + 2*WIDTH
+ 2 + 1], csum
= 0, *c
;
71 if (!memcmp(lastwrite
, buf
, WIDTH
* sizeof(*buf
)))
73 memcpy(lastwrite
, buf
, WIDTH
* sizeof(*buf
));
84 for (i
= 0; i
< WIDTH
; i
++) {
105 braille_co
->write(braille_co
, data
, c
- data
);
108 /* Follow the VC cursor*/
109 static void vc_follow_cursor(struct vc_data
*vc
)
111 vc_x
= vc
->state
.x
- (vc
->state
.x
% WIDTH
);
113 lastvc_x
= vc
->state
.x
;
114 lastvc_y
= vc
->state
.y
;
117 /* Maybe the VC cursor moved, if so follow it */
118 static void vc_maybe_cursor_moved(struct vc_data
*vc
)
120 if (vc
->state
.x
!= lastvc_x
|| vc
->state
.y
!= lastvc_y
)
121 vc_follow_cursor(vc
);
124 /* Show portion of VC at vc_x, vc_y */
125 static void vc_refresh(struct vc_data
*vc
)
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);
142 static int keyboard_notifier_call(struct notifier_block
*blk
,
143 unsigned long code
, void *_param
)
145 struct keyboard_notifier_param
*param
= _param
;
146 struct vc_data
*vc
= param
->vc
;
155 if (param
->value
== BRAILLE_KEY
) {
158 vc_maybe_cursor_moved(vc
);
164 switch (param
->value
) {
169 braille_write(console_buf
);
176 } else if (vc_y
>= 1) {
179 vc_x
= vc
->vc_cols
-WIDTH
;
184 if (vc_x
+ WIDTH
< vc
->vc_cols
) {
186 } else if (vc_y
+ 1 < vc
->vc_rows
) {
194 if (vc_y
+ 1 < vc
->vc_rows
)
206 vc_follow_cursor(vc
);
214 vc_y
= vc
->vc_rows
-1;
220 if (ret
== NOTIFY_STOP
)
224 case KBD_POST_KEYSYM
:
226 unsigned char type
= KTYP(param
->value
) - 0xf0;
228 if (type
== KT_SPEC
) {
229 unsigned char val
= KVAL(param
->value
);
234 on_off
= vt_get_leds(fg_console
, VC_CAPSLOCK
);
237 on_off
= vt_get_leds(fg_console
, VC_NUMLOCK
);
240 on_off
= vt_get_leds(fg_console
, VC_SCROLLOCK
);
245 else if (on_off
== 0)
250 case KBD_UNBOUND_KEYCODE
:
259 static struct notifier_block keyboard_notifier_block
= {
260 .notifier_call
= keyboard_notifier_call
,
263 static int vt_notifier_call(struct notifier_block
*blk
,
264 unsigned long code
, void *_param
)
266 struct vt_notifier_param
*param
= _param
;
267 struct vc_data
*vc
= param
->vc
;
276 unsigned char c
= param
->c
;
278 if (vc
->vc_num
!= fg_console
)
283 if (console_cursor
> 0) {
285 console_buf
[console_cursor
] = ' ';
299 /* Ignore other control sequences */
301 if (console_newline
) {
302 memset(console_buf
, 0, sizeof(console_buf
));
306 if (console_cursor
== WIDTH
)
307 memmove(console_buf
, &console_buf
[1],
308 (WIDTH
-1) * sizeof(*console_buf
));
311 console_buf
[console_cursor
-1] = c
;
315 braille_write(console_buf
);
317 vc_maybe_cursor_moved(vc
);
323 /* Maybe a VT switch, flush */
325 if (vc
->vc_num
!= lastVC
) {
327 memset(console_buf
, 0, sizeof(console_buf
));
329 braille_write(console_buf
);
332 vc_maybe_cursor_moved(vc
);
340 static struct notifier_block vt_notifier_block
= {
341 .notifier_call
= vt_notifier_call
,
345 * Called from printk.c when console=brl is given
348 int braille_register_console(struct console
*console
, int index
,
349 char *console_options
, char *braille_options
)
353 if (!console_options
)
354 /* Only support VisioBraille for now */
355 console_options
= "57600o8";
358 if (console
->setup
) {
359 ret
= console
->setup(console
, console_options
);
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
);
371 int braille_unregister_console(struct console
*console
)
373 if (braille_co
!= console
)
375 unregister_keyboard_notifier(&keyboard_notifier_block
);
376 unregister_vt_notifier(&vt_notifier_block
);