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 }
393