Keyboard struct lifetime is easy, but the locking is not and is completely ignored by the existing code. Tackle this one head on - Make the kbd_table private so we can run down all direct users - Hoick the relevant ioctl handlers into the keyboard layer - Lock them with the keyboard lock so they don't change mid keypress - Add helpers for things like console stop/start so we isolate the poking around properly - Tweak the braille console so it still builds There are a couple of FIXME locking cases left for ioctls that are so hideous they should be addressed in a later patch. After this patch the kbd_table is private and all the keyboard jiggery pokery is in one place. This update fixes speakup and also a memory leak in the original. Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
		
			
				
	
	
		
			389 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			389 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Minimalistic braille device kernel support.
 | |
|  *
 | |
|  * By default, shows console messages on the braille device.
 | |
|  * Pressing Insert switches to VC browsing.
 | |
|  *
 | |
|  *  Copyright (C) Samuel Thibault <samuel.thibault@ens-lyon.org>
 | |
|  *
 | |
|  * This program is free software ; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation ; either version 2 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY ; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with the program ; if not, write to the Free Software
 | |
|  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | |
|  */
 | |
| 
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/moduleparam.h>
 | |
| #include <linux/console.h>
 | |
| #include <linux/notifier.h>
 | |
| 
 | |
| #include <linux/selection.h>
 | |
| #include <linux/vt_kern.h>
 | |
| #include <linux/consolemap.h>
 | |
| 
 | |
| #include <linux/keyboard.h>
 | |
| #include <linux/kbd_kern.h>
 | |
| #include <linux/input.h>
 | |
| 
 | |
| MODULE_AUTHOR("samuel.thibault@ens-lyon.org");
 | |
| MODULE_DESCRIPTION("braille device");
 | |
| MODULE_LICENSE("GPL");
 | |
| 
 | |
| /*
 | |
|  * Braille device support part.
 | |
|  */
 | |
| 
 | |
| /* Emit various sounds */
 | |
| static bool sound;
 | |
| module_param(sound, bool, 0);
 | |
| MODULE_PARM_DESC(sound, "emit sounds");
 | |
| 
 | |
| static void beep(unsigned int freq)
 | |
| {
 | |
| 	if (sound)
 | |
| 		kd_mksound(freq, HZ/10);
 | |
| }
 | |
| 
 | |
| /* mini console */
 | |
| #define WIDTH 40
 | |
| #define BRAILLE_KEY KEY_INSERT
 | |
| static u16 console_buf[WIDTH];
 | |
| static int console_cursor;
 | |
| 
 | |
| /* mini view of VC */
 | |
| static int vc_x, vc_y, lastvc_x, lastvc_y;
 | |
| 
 | |
| /* show console ? (or show VC) */
 | |
| static int console_show = 1;
 | |
| /* pending newline ? */
 | |
| static int console_newline = 1;
 | |
| static int lastVC = -1;
 | |
| 
 | |
| static struct console *braille_co;
 | |
| 
 | |
| /* Very VisioBraille-specific */
 | |
| static void braille_write(u16 *buf)
 | |
| {
 | |
| 	static u16 lastwrite[WIDTH];
 | |
| 	unsigned char data[1 + 1 + 2*WIDTH + 2 + 1], csum = 0, *c;
 | |
| 	u16 out;
 | |
| 	int i;
 | |
| 
 | |
| 	if (!braille_co)
 | |
| 		return;
 | |
| 
 | |
| 	if (!memcmp(lastwrite, buf, WIDTH * sizeof(*buf)))
 | |
| 		return;
 | |
| 	memcpy(lastwrite, buf, WIDTH * sizeof(*buf));
 | |
| 
 | |
| #define SOH 1
 | |
| #define STX 2
 | |
| #define ETX 2
 | |
| #define EOT 4
 | |
| #define ENQ 5
 | |
| 	data[0] = STX;
 | |
| 	data[1] = '>';
 | |
| 	csum ^= '>';
 | |
| 	c = &data[2];
 | |
| 	for (i = 0; i < WIDTH; i++) {
 | |
| 		out = buf[i];
 | |
| 		if (out >= 0x100)
 | |
| 			out = '?';
 | |
| 		else if (out == 0x00)
 | |
| 			out = ' ';
 | |
| 		csum ^= out;
 | |
| 		if (out <= 0x05) {
 | |
| 			*c++ = SOH;
 | |
| 			out |= 0x40;
 | |
| 		}
 | |
| 		*c++ = out;
 | |
| 	}
 | |
| 
 | |
| 	if (csum <= 0x05) {
 | |
| 		*c++ = SOH;
 | |
| 		csum |= 0x40;
 | |
| 	}
 | |
| 	*c++ = csum;
 | |
| 	*c++ = ETX;
 | |
| 
 | |
| 	braille_co->write(braille_co, data, c - data);
 | |
| }
 | |
| 
 | |
| /* Follow the VC cursor*/
 | |
| static void vc_follow_cursor(struct vc_data *vc)
 | |
| {
 | |
| 	vc_x = vc->vc_x - (vc->vc_x % WIDTH);
 | |
| 	vc_y = vc->vc_y;
 | |
| 	lastvc_x = vc->vc_x;
 | |
| 	lastvc_y = vc->vc_y;
 | |
| }
 | |
| 
 | |
| /* Maybe the VC cursor moved, if so follow it */
 | |
| static void vc_maybe_cursor_moved(struct vc_data *vc)
 | |
| {
 | |
| 	if (vc->vc_x != lastvc_x || vc->vc_y != lastvc_y)
 | |
| 		vc_follow_cursor(vc);
 | |
| }
 | |
| 
 | |
| /* Show portion of VC at vc_x, vc_y */
 | |
| static void vc_refresh(struct vc_data *vc)
 | |
| {
 | |
| 	u16 buf[WIDTH];
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < WIDTH; i++) {
 | |
| 		u16 glyph = screen_glyph(vc,
 | |
| 				2 * (vc_x + i) + vc_y * vc->vc_size_row);
 | |
| 		buf[i] = inverse_translate(vc, glyph, 1);
 | |
| 	}
 | |
| 	braille_write(buf);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Link to keyboard
 | |
|  */
 | |
| 
 | |
| static int keyboard_notifier_call(struct notifier_block *blk,
 | |
| 				  unsigned long code, void *_param)
 | |
| {
 | |
| 	struct keyboard_notifier_param *param = _param;
 | |
| 	struct vc_data *vc = param->vc;
 | |
| 	int ret = NOTIFY_OK;
 | |
| 
 | |
| 	if (!param->down)
 | |
| 		return ret;
 | |
| 
 | |
| 	switch (code) {
 | |
| 	case KBD_KEYCODE:
 | |
| 		if (console_show) {
 | |
| 			if (param->value == BRAILLE_KEY) {
 | |
| 				console_show = 0;
 | |
| 				beep(880);
 | |
| 				vc_maybe_cursor_moved(vc);
 | |
| 				vc_refresh(vc);
 | |
| 				ret = NOTIFY_STOP;
 | |
| 			}
 | |
| 		} else {
 | |
| 			ret = NOTIFY_STOP;
 | |
| 			switch (param->value) {
 | |
| 			case KEY_INSERT:
 | |
| 				beep(440);
 | |
| 				console_show = 1;
 | |
| 				lastVC = -1;
 | |
| 				braille_write(console_buf);
 | |
| 				break;
 | |
| 			case KEY_LEFT:
 | |
| 				if (vc_x > 0) {
 | |
| 					vc_x -= WIDTH;
 | |
| 					if (vc_x < 0)
 | |
| 						vc_x = 0;
 | |
| 				} else if (vc_y >= 1) {
 | |
| 					beep(880);
 | |
| 					vc_y--;
 | |
| 					vc_x = vc->vc_cols-WIDTH;
 | |
| 				} else
 | |
| 					beep(220);
 | |
| 				break;
 | |
| 			case KEY_RIGHT:
 | |
| 				if (vc_x + WIDTH < vc->vc_cols) {
 | |
| 					vc_x += WIDTH;
 | |
| 				} else if (vc_y + 1 < vc->vc_rows) {
 | |
| 					beep(880);
 | |
| 					vc_y++;
 | |
| 					vc_x = 0;
 | |
| 				} else
 | |
| 					beep(220);
 | |
| 				break;
 | |
| 			case KEY_DOWN:
 | |
| 				if (vc_y + 1 < vc->vc_rows)
 | |
| 					vc_y++;
 | |
| 				else
 | |
| 					beep(220);
 | |
| 				break;
 | |
| 			case KEY_UP:
 | |
| 				if (vc_y >= 1)
 | |
| 					vc_y--;
 | |
| 				else
 | |
| 					beep(220);
 | |
| 				break;
 | |
| 			case KEY_HOME:
 | |
| 				vc_follow_cursor(vc);
 | |
| 				break;
 | |
| 			case KEY_PAGEUP:
 | |
| 				vc_x = 0;
 | |
| 				vc_y = 0;
 | |
| 				break;
 | |
| 			case KEY_PAGEDOWN:
 | |
| 				vc_x = 0;
 | |
| 				vc_y = vc->vc_rows-1;
 | |
| 				break;
 | |
| 			default:
 | |
| 				ret = NOTIFY_OK;
 | |
| 				break;
 | |
| 			}
 | |
| 			if (ret == NOTIFY_STOP)
 | |
| 				vc_refresh(vc);
 | |
| 		}
 | |
| 		break;
 | |
| 	case KBD_POST_KEYSYM:
 | |
| 	{
 | |
| 		unsigned char type = KTYP(param->value) - 0xf0;
 | |
| 		if (type == KT_SPEC) {
 | |
| 			unsigned char val = KVAL(param->value);
 | |
| 			int on_off = -1;
 | |
| 
 | |
| 			switch (val) {
 | |
| 			case KVAL(K_CAPS):
 | |
| 				on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
 | |
| 				break;
 | |
| 			case KVAL(K_NUM):
 | |
| 				on_off = vt_get_leds(fg_console, VC_NUMLOCK);
 | |
| 				break;
 | |
| 			case KVAL(K_HOLD):
 | |
| 				on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
 | |
| 				break;
 | |
| 			}
 | |
| 			if (on_off == 1)
 | |
| 				beep(880);
 | |
| 			else if (on_off == 0)
 | |
| 				beep(440);
 | |
| 		}
 | |
| 	}
 | |
| 	case KBD_UNBOUND_KEYCODE:
 | |
| 	case KBD_UNICODE:
 | |
| 	case KBD_KEYSYM:
 | |
| 		/* Unused */
 | |
| 		break;
 | |
| 	}
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static struct notifier_block keyboard_notifier_block = {
 | |
| 	.notifier_call = keyboard_notifier_call,
 | |
| };
 | |
| 
 | |
| static int vt_notifier_call(struct notifier_block *blk,
 | |
| 			    unsigned long code, void *_param)
 | |
| {
 | |
| 	struct vt_notifier_param *param = _param;
 | |
| 	struct vc_data *vc = param->vc;
 | |
| 	switch (code) {
 | |
| 	case VT_ALLOCATE:
 | |
| 		break;
 | |
| 	case VT_DEALLOCATE:
 | |
| 		break;
 | |
| 	case VT_WRITE:
 | |
| 	{
 | |
| 		unsigned char c = param->c;
 | |
| 		if (vc->vc_num != fg_console)
 | |
| 			break;
 | |
| 		switch (c) {
 | |
| 		case '\b':
 | |
| 		case 127:
 | |
| 			if (console_cursor > 0) {
 | |
| 				console_cursor--;
 | |
| 				console_buf[console_cursor] = ' ';
 | |
| 			}
 | |
| 			break;
 | |
| 		case '\n':
 | |
| 		case '\v':
 | |
| 		case '\f':
 | |
| 		case '\r':
 | |
| 			console_newline = 1;
 | |
| 			break;
 | |
| 		case '\t':
 | |
| 			c = ' ';
 | |
| 			/* Fallthrough */
 | |
| 		default:
 | |
| 			if (c < 32)
 | |
| 				/* Ignore other control sequences */
 | |
| 				break;
 | |
| 			if (console_newline) {
 | |
| 				memset(console_buf, 0, sizeof(console_buf));
 | |
| 				console_cursor = 0;
 | |
| 				console_newline = 0;
 | |
| 			}
 | |
| 			if (console_cursor == WIDTH)
 | |
| 				memmove(console_buf, &console_buf[1],
 | |
| 					(WIDTH-1) * sizeof(*console_buf));
 | |
| 			else
 | |
| 				console_cursor++;
 | |
| 			console_buf[console_cursor-1] = c;
 | |
| 			break;
 | |
| 		}
 | |
| 		if (console_show)
 | |
| 			braille_write(console_buf);
 | |
| 		else {
 | |
| 			vc_maybe_cursor_moved(vc);
 | |
| 			vc_refresh(vc);
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
| 	case VT_UPDATE:
 | |
| 		/* Maybe a VT switch, flush */
 | |
| 		if (console_show) {
 | |
| 			if (vc->vc_num != lastVC) {
 | |
| 				lastVC = vc->vc_num;
 | |
| 				memset(console_buf, 0, sizeof(console_buf));
 | |
| 				console_cursor = 0;
 | |
| 				braille_write(console_buf);
 | |
| 			}
 | |
| 		} else {
 | |
| 			vc_maybe_cursor_moved(vc);
 | |
| 			vc_refresh(vc);
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
| 	return NOTIFY_OK;
 | |
| }
 | |
| 
 | |
| static struct notifier_block vt_notifier_block = {
 | |
| 	.notifier_call = vt_notifier_call,
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * Called from printk.c when console=brl is given
 | |
|  */
 | |
| 
 | |
| int braille_register_console(struct console *console, int index,
 | |
| 		char *console_options, char *braille_options)
 | |
| {
 | |
| 	int ret;
 | |
| 	if (!console_options)
 | |
| 		/* Only support VisioBraille for now */
 | |
| 		console_options = "57600o8";
 | |
| 	if (braille_co)
 | |
| 		return -ENODEV;
 | |
| 	if (console->setup) {
 | |
| 		ret = console->setup(console, console_options);
 | |
| 		if (ret != 0)
 | |
| 			return ret;
 | |
| 	}
 | |
| 	console->flags |= CON_ENABLED;
 | |
| 	console->index = index;
 | |
| 	braille_co = console;
 | |
| 	register_keyboard_notifier(&keyboard_notifier_block);
 | |
| 	register_vt_notifier(&vt_notifier_block);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int braille_unregister_console(struct console *console)
 | |
| {
 | |
| 	if (braille_co != console)
 | |
| 		return -EINVAL;
 | |
| 	unregister_keyboard_notifier(&keyboard_notifier_block);
 | |
| 	unregister_vt_notifier(&vt_notifier_block);
 | |
| 	braille_co = NULL;
 | |
| 	return 0;
 | |
| }
 |