18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Minimalistic braille device kernel support.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * By default, shows console messages on the braille device.
68c2ecf20Sopenharmony_ci * Pressing Insert switches to VC browsing.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  Copyright (C) Samuel Thibault <samuel.thibault@ens-lyon.org>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
148c2ecf20Sopenharmony_ci#include <linux/console.h>
158c2ecf20Sopenharmony_ci#include <linux/notifier.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/selection.h>
188c2ecf20Sopenharmony_ci#include <linux/vt_kern.h>
198c2ecf20Sopenharmony_ci#include <linux/consolemap.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/keyboard.h>
228c2ecf20Sopenharmony_ci#include <linux/kbd_kern.h>
238c2ecf20Sopenharmony_ci#include <linux/input.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ciMODULE_AUTHOR("samuel.thibault@ens-lyon.org");
268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("braille device");
278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * Braille device support part.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/* Emit various sounds */
348c2ecf20Sopenharmony_cistatic bool sound;
358c2ecf20Sopenharmony_cimodule_param(sound, bool, 0);
368c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sound, "emit sounds");
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void beep(unsigned int freq)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	if (sound)
418c2ecf20Sopenharmony_ci		kd_mksound(freq, HZ/10);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* mini console */
458c2ecf20Sopenharmony_ci#define WIDTH 40
468c2ecf20Sopenharmony_ci#define BRAILLE_KEY KEY_INSERT
478c2ecf20Sopenharmony_cistatic u16 console_buf[WIDTH];
488c2ecf20Sopenharmony_cistatic int console_cursor;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* mini view of VC */
518c2ecf20Sopenharmony_cistatic int vc_x, vc_y, lastvc_x, lastvc_y;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* show console ? (or show VC) */
548c2ecf20Sopenharmony_cistatic int console_show = 1;
558c2ecf20Sopenharmony_ci/* pending newline ? */
568c2ecf20Sopenharmony_cistatic int console_newline = 1;
578c2ecf20Sopenharmony_cistatic int lastVC = -1;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic struct console *braille_co;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* Very VisioBraille-specific */
628c2ecf20Sopenharmony_cistatic void braille_write(u16 *buf)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	static u16 lastwrite[WIDTH];
658c2ecf20Sopenharmony_ci	unsigned char data[1 + 1 + 2*WIDTH + 2 + 1], csum = 0, *c;
668c2ecf20Sopenharmony_ci	u16 out;
678c2ecf20Sopenharmony_ci	int i;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (!braille_co)
708c2ecf20Sopenharmony_ci		return;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (!memcmp(lastwrite, buf, WIDTH * sizeof(*buf)))
738c2ecf20Sopenharmony_ci		return;
748c2ecf20Sopenharmony_ci	memcpy(lastwrite, buf, WIDTH * sizeof(*buf));
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#define SOH 1
778c2ecf20Sopenharmony_ci#define STX 2
788c2ecf20Sopenharmony_ci#define ETX 2
798c2ecf20Sopenharmony_ci#define EOT 4
808c2ecf20Sopenharmony_ci#define ENQ 5
818c2ecf20Sopenharmony_ci	data[0] = STX;
828c2ecf20Sopenharmony_ci	data[1] = '>';
838c2ecf20Sopenharmony_ci	csum ^= '>';
848c2ecf20Sopenharmony_ci	c = &data[2];
858c2ecf20Sopenharmony_ci	for (i = 0; i < WIDTH; i++) {
868c2ecf20Sopenharmony_ci		out = buf[i];
878c2ecf20Sopenharmony_ci		if (out >= 0x100)
888c2ecf20Sopenharmony_ci			out = '?';
898c2ecf20Sopenharmony_ci		else if (out == 0x00)
908c2ecf20Sopenharmony_ci			out = ' ';
918c2ecf20Sopenharmony_ci		csum ^= out;
928c2ecf20Sopenharmony_ci		if (out <= 0x05) {
938c2ecf20Sopenharmony_ci			*c++ = SOH;
948c2ecf20Sopenharmony_ci			out |= 0x40;
958c2ecf20Sopenharmony_ci		}
968c2ecf20Sopenharmony_ci		*c++ = out;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (csum <= 0x05) {
1008c2ecf20Sopenharmony_ci		*c++ = SOH;
1018c2ecf20Sopenharmony_ci		csum |= 0x40;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci	*c++ = csum;
1048c2ecf20Sopenharmony_ci	*c++ = ETX;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	braille_co->write(braille_co, data, c - data);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/* Follow the VC cursor*/
1108c2ecf20Sopenharmony_cistatic void vc_follow_cursor(struct vc_data *vc)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	vc_x = vc->state.x - (vc->state.x % WIDTH);
1138c2ecf20Sopenharmony_ci	vc_y = vc->state.y;
1148c2ecf20Sopenharmony_ci	lastvc_x = vc->state.x;
1158c2ecf20Sopenharmony_ci	lastvc_y = vc->state.y;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/* Maybe the VC cursor moved, if so follow it */
1198c2ecf20Sopenharmony_cistatic void vc_maybe_cursor_moved(struct vc_data *vc)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	if (vc->state.x != lastvc_x || vc->state.y != lastvc_y)
1228c2ecf20Sopenharmony_ci		vc_follow_cursor(vc);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/* Show portion of VC at vc_x, vc_y */
1268c2ecf20Sopenharmony_cistatic void vc_refresh(struct vc_data *vc)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	u16 buf[WIDTH];
1298c2ecf20Sopenharmony_ci	int i;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	for (i = 0; i < WIDTH; i++) {
1328c2ecf20Sopenharmony_ci		u16 glyph = screen_glyph(vc,
1338c2ecf20Sopenharmony_ci				2 * (vc_x + i) + vc_y * vc->vc_size_row);
1348c2ecf20Sopenharmony_ci		buf[i] = inverse_translate(vc, glyph, 1);
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci	braille_write(buf);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/*
1408c2ecf20Sopenharmony_ci * Link to keyboard
1418c2ecf20Sopenharmony_ci */
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic int keyboard_notifier_call(struct notifier_block *blk,
1448c2ecf20Sopenharmony_ci				  unsigned long code, void *_param)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct keyboard_notifier_param *param = _param;
1478c2ecf20Sopenharmony_ci	struct vc_data *vc = param->vc;
1488c2ecf20Sopenharmony_ci	int ret = NOTIFY_OK;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (!param->down)
1518c2ecf20Sopenharmony_ci		return ret;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	switch (code) {
1548c2ecf20Sopenharmony_ci	case KBD_KEYCODE:
1558c2ecf20Sopenharmony_ci		if (console_show) {
1568c2ecf20Sopenharmony_ci			if (param->value == BRAILLE_KEY) {
1578c2ecf20Sopenharmony_ci				console_show = 0;
1588c2ecf20Sopenharmony_ci				beep(880);
1598c2ecf20Sopenharmony_ci				vc_maybe_cursor_moved(vc);
1608c2ecf20Sopenharmony_ci				vc_refresh(vc);
1618c2ecf20Sopenharmony_ci				ret = NOTIFY_STOP;
1628c2ecf20Sopenharmony_ci			}
1638c2ecf20Sopenharmony_ci		} else {
1648c2ecf20Sopenharmony_ci			ret = NOTIFY_STOP;
1658c2ecf20Sopenharmony_ci			switch (param->value) {
1668c2ecf20Sopenharmony_ci			case KEY_INSERT:
1678c2ecf20Sopenharmony_ci				beep(440);
1688c2ecf20Sopenharmony_ci				console_show = 1;
1698c2ecf20Sopenharmony_ci				lastVC = -1;
1708c2ecf20Sopenharmony_ci				braille_write(console_buf);
1718c2ecf20Sopenharmony_ci				break;
1728c2ecf20Sopenharmony_ci			case KEY_LEFT:
1738c2ecf20Sopenharmony_ci				if (vc_x > 0) {
1748c2ecf20Sopenharmony_ci					vc_x -= WIDTH;
1758c2ecf20Sopenharmony_ci					if (vc_x < 0)
1768c2ecf20Sopenharmony_ci						vc_x = 0;
1778c2ecf20Sopenharmony_ci				} else if (vc_y >= 1) {
1788c2ecf20Sopenharmony_ci					beep(880);
1798c2ecf20Sopenharmony_ci					vc_y--;
1808c2ecf20Sopenharmony_ci					vc_x = vc->vc_cols-WIDTH;
1818c2ecf20Sopenharmony_ci				} else
1828c2ecf20Sopenharmony_ci					beep(220);
1838c2ecf20Sopenharmony_ci				break;
1848c2ecf20Sopenharmony_ci			case KEY_RIGHT:
1858c2ecf20Sopenharmony_ci				if (vc_x + WIDTH < vc->vc_cols) {
1868c2ecf20Sopenharmony_ci					vc_x += WIDTH;
1878c2ecf20Sopenharmony_ci				} else if (vc_y + 1 < vc->vc_rows) {
1888c2ecf20Sopenharmony_ci					beep(880);
1898c2ecf20Sopenharmony_ci					vc_y++;
1908c2ecf20Sopenharmony_ci					vc_x = 0;
1918c2ecf20Sopenharmony_ci				} else
1928c2ecf20Sopenharmony_ci					beep(220);
1938c2ecf20Sopenharmony_ci				break;
1948c2ecf20Sopenharmony_ci			case KEY_DOWN:
1958c2ecf20Sopenharmony_ci				if (vc_y + 1 < vc->vc_rows)
1968c2ecf20Sopenharmony_ci					vc_y++;
1978c2ecf20Sopenharmony_ci				else
1988c2ecf20Sopenharmony_ci					beep(220);
1998c2ecf20Sopenharmony_ci				break;
2008c2ecf20Sopenharmony_ci			case KEY_UP:
2018c2ecf20Sopenharmony_ci				if (vc_y >= 1)
2028c2ecf20Sopenharmony_ci					vc_y--;
2038c2ecf20Sopenharmony_ci				else
2048c2ecf20Sopenharmony_ci					beep(220);
2058c2ecf20Sopenharmony_ci				break;
2068c2ecf20Sopenharmony_ci			case KEY_HOME:
2078c2ecf20Sopenharmony_ci				vc_follow_cursor(vc);
2088c2ecf20Sopenharmony_ci				break;
2098c2ecf20Sopenharmony_ci			case KEY_PAGEUP:
2108c2ecf20Sopenharmony_ci				vc_x = 0;
2118c2ecf20Sopenharmony_ci				vc_y = 0;
2128c2ecf20Sopenharmony_ci				break;
2138c2ecf20Sopenharmony_ci			case KEY_PAGEDOWN:
2148c2ecf20Sopenharmony_ci				vc_x = 0;
2158c2ecf20Sopenharmony_ci				vc_y = vc->vc_rows-1;
2168c2ecf20Sopenharmony_ci				break;
2178c2ecf20Sopenharmony_ci			default:
2188c2ecf20Sopenharmony_ci				ret = NOTIFY_OK;
2198c2ecf20Sopenharmony_ci				break;
2208c2ecf20Sopenharmony_ci			}
2218c2ecf20Sopenharmony_ci			if (ret == NOTIFY_STOP)
2228c2ecf20Sopenharmony_ci				vc_refresh(vc);
2238c2ecf20Sopenharmony_ci		}
2248c2ecf20Sopenharmony_ci		break;
2258c2ecf20Sopenharmony_ci	case KBD_POST_KEYSYM:
2268c2ecf20Sopenharmony_ci	{
2278c2ecf20Sopenharmony_ci		unsigned char type = KTYP(param->value) - 0xf0;
2288c2ecf20Sopenharmony_ci		if (type == KT_SPEC) {
2298c2ecf20Sopenharmony_ci			unsigned char val = KVAL(param->value);
2308c2ecf20Sopenharmony_ci			int on_off = -1;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci			switch (val) {
2338c2ecf20Sopenharmony_ci			case KVAL(K_CAPS):
2348c2ecf20Sopenharmony_ci				on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
2358c2ecf20Sopenharmony_ci				break;
2368c2ecf20Sopenharmony_ci			case KVAL(K_NUM):
2378c2ecf20Sopenharmony_ci				on_off = vt_get_leds(fg_console, VC_NUMLOCK);
2388c2ecf20Sopenharmony_ci				break;
2398c2ecf20Sopenharmony_ci			case KVAL(K_HOLD):
2408c2ecf20Sopenharmony_ci				on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
2418c2ecf20Sopenharmony_ci				break;
2428c2ecf20Sopenharmony_ci			}
2438c2ecf20Sopenharmony_ci			if (on_off == 1)
2448c2ecf20Sopenharmony_ci				beep(880);
2458c2ecf20Sopenharmony_ci			else if (on_off == 0)
2468c2ecf20Sopenharmony_ci				beep(440);
2478c2ecf20Sopenharmony_ci		}
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	case KBD_UNBOUND_KEYCODE:
2508c2ecf20Sopenharmony_ci	case KBD_UNICODE:
2518c2ecf20Sopenharmony_ci	case KBD_KEYSYM:
2528c2ecf20Sopenharmony_ci		/* Unused */
2538c2ecf20Sopenharmony_ci		break;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	return ret;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic struct notifier_block keyboard_notifier_block = {
2598c2ecf20Sopenharmony_ci	.notifier_call = keyboard_notifier_call,
2608c2ecf20Sopenharmony_ci};
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic int vt_notifier_call(struct notifier_block *blk,
2638c2ecf20Sopenharmony_ci			    unsigned long code, void *_param)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct vt_notifier_param *param = _param;
2668c2ecf20Sopenharmony_ci	struct vc_data *vc = param->vc;
2678c2ecf20Sopenharmony_ci	switch (code) {
2688c2ecf20Sopenharmony_ci	case VT_ALLOCATE:
2698c2ecf20Sopenharmony_ci		break;
2708c2ecf20Sopenharmony_ci	case VT_DEALLOCATE:
2718c2ecf20Sopenharmony_ci		break;
2728c2ecf20Sopenharmony_ci	case VT_WRITE:
2738c2ecf20Sopenharmony_ci	{
2748c2ecf20Sopenharmony_ci		unsigned char c = param->c;
2758c2ecf20Sopenharmony_ci		if (vc->vc_num != fg_console)
2768c2ecf20Sopenharmony_ci			break;
2778c2ecf20Sopenharmony_ci		switch (c) {
2788c2ecf20Sopenharmony_ci		case '\b':
2798c2ecf20Sopenharmony_ci		case 127:
2808c2ecf20Sopenharmony_ci			if (console_cursor > 0) {
2818c2ecf20Sopenharmony_ci				console_cursor--;
2828c2ecf20Sopenharmony_ci				console_buf[console_cursor] = ' ';
2838c2ecf20Sopenharmony_ci			}
2848c2ecf20Sopenharmony_ci			break;
2858c2ecf20Sopenharmony_ci		case '\n':
2868c2ecf20Sopenharmony_ci		case '\v':
2878c2ecf20Sopenharmony_ci		case '\f':
2888c2ecf20Sopenharmony_ci		case '\r':
2898c2ecf20Sopenharmony_ci			console_newline = 1;
2908c2ecf20Sopenharmony_ci			break;
2918c2ecf20Sopenharmony_ci		case '\t':
2928c2ecf20Sopenharmony_ci			c = ' ';
2938c2ecf20Sopenharmony_ci			fallthrough;
2948c2ecf20Sopenharmony_ci		default:
2958c2ecf20Sopenharmony_ci			if (c < 32)
2968c2ecf20Sopenharmony_ci				/* Ignore other control sequences */
2978c2ecf20Sopenharmony_ci				break;
2988c2ecf20Sopenharmony_ci			if (console_newline) {
2998c2ecf20Sopenharmony_ci				memset(console_buf, 0, sizeof(console_buf));
3008c2ecf20Sopenharmony_ci				console_cursor = 0;
3018c2ecf20Sopenharmony_ci				console_newline = 0;
3028c2ecf20Sopenharmony_ci			}
3038c2ecf20Sopenharmony_ci			if (console_cursor == WIDTH)
3048c2ecf20Sopenharmony_ci				memmove(console_buf, &console_buf[1],
3058c2ecf20Sopenharmony_ci					(WIDTH-1) * sizeof(*console_buf));
3068c2ecf20Sopenharmony_ci			else
3078c2ecf20Sopenharmony_ci				console_cursor++;
3088c2ecf20Sopenharmony_ci			console_buf[console_cursor-1] = c;
3098c2ecf20Sopenharmony_ci			break;
3108c2ecf20Sopenharmony_ci		}
3118c2ecf20Sopenharmony_ci		if (console_show)
3128c2ecf20Sopenharmony_ci			braille_write(console_buf);
3138c2ecf20Sopenharmony_ci		else {
3148c2ecf20Sopenharmony_ci			vc_maybe_cursor_moved(vc);
3158c2ecf20Sopenharmony_ci			vc_refresh(vc);
3168c2ecf20Sopenharmony_ci		}
3178c2ecf20Sopenharmony_ci		break;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	case VT_UPDATE:
3208c2ecf20Sopenharmony_ci		/* Maybe a VT switch, flush */
3218c2ecf20Sopenharmony_ci		if (console_show) {
3228c2ecf20Sopenharmony_ci			if (vc->vc_num != lastVC) {
3238c2ecf20Sopenharmony_ci				lastVC = vc->vc_num;
3248c2ecf20Sopenharmony_ci				memset(console_buf, 0, sizeof(console_buf));
3258c2ecf20Sopenharmony_ci				console_cursor = 0;
3268c2ecf20Sopenharmony_ci				braille_write(console_buf);
3278c2ecf20Sopenharmony_ci			}
3288c2ecf20Sopenharmony_ci		} else {
3298c2ecf20Sopenharmony_ci			vc_maybe_cursor_moved(vc);
3308c2ecf20Sopenharmony_ci			vc_refresh(vc);
3318c2ecf20Sopenharmony_ci		}
3328c2ecf20Sopenharmony_ci		break;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci	return NOTIFY_OK;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic struct notifier_block vt_notifier_block = {
3388c2ecf20Sopenharmony_ci	.notifier_call = vt_notifier_call,
3398c2ecf20Sopenharmony_ci};
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci/*
3428c2ecf20Sopenharmony_ci * Called from printk.c when console=brl is given
3438c2ecf20Sopenharmony_ci */
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ciint braille_register_console(struct console *console, int index,
3468c2ecf20Sopenharmony_ci		char *console_options, char *braille_options)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	int ret;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (!console_options)
3518c2ecf20Sopenharmony_ci		/* Only support VisioBraille for now */
3528c2ecf20Sopenharmony_ci		console_options = "57600o8";
3538c2ecf20Sopenharmony_ci	if (braille_co)
3548c2ecf20Sopenharmony_ci		return -ENODEV;
3558c2ecf20Sopenharmony_ci	if (console->setup) {
3568c2ecf20Sopenharmony_ci		ret = console->setup(console, console_options);
3578c2ecf20Sopenharmony_ci		if (ret != 0)
3588c2ecf20Sopenharmony_ci			return ret;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci	console->flags |= CON_ENABLED;
3618c2ecf20Sopenharmony_ci	console->index = index;
3628c2ecf20Sopenharmony_ci	braille_co = console;
3638c2ecf20Sopenharmony_ci	register_keyboard_notifier(&keyboard_notifier_block);
3648c2ecf20Sopenharmony_ci	register_vt_notifier(&vt_notifier_block);
3658c2ecf20Sopenharmony_ci	return 1;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ciint braille_unregister_console(struct console *console)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	if (braille_co != console)
3718c2ecf20Sopenharmony_ci		return -EINVAL;
3728c2ecf20Sopenharmony_ci	unregister_keyboard_notifier(&keyboard_notifier_block);
3738c2ecf20Sopenharmony_ci	unregister_vt_notifier(&vt_notifier_block);
3748c2ecf20Sopenharmony_ci	braille_co = NULL;
3758c2ecf20Sopenharmony_ci	return 1;
3768c2ecf20Sopenharmony_ci}
377