brintos

brintos / linux-shallow public Read only

0
0
Text · 7.3 KiB · 06b43b6 Raw
380 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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 <samuel.thibault@ens-lyon.org>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 25MODULE_AUTHOR("samuel.thibault@ens-lyon.org");26MODULE_DESCRIPTION("braille device");27 28/*29 * Braille device support part.30 */31 32/* Emit various sounds */33static bool sound;34module_param(sound, bool, 0);35MODULE_PARM_DESC(sound, "emit sounds");36 37static void beep(unsigned int freq)38{39	if (sound)40		kd_mksound(freq, HZ/10);41}42 43/* mini console */44#define WIDTH 4045#define BRAILLE_KEY KEY_INSERT46static u16 console_buf[WIDTH];47static int console_cursor;48 49/* mini view of VC */50static int vc_x, vc_y, lastvc_x, lastvc_y;51 52/* show console ? (or show VC) */53static int console_show = 1;54/* pending newline ? */55static int console_newline = 1;56static int lastVC = -1;57 58static struct console *braille_co;59 60/* Very VisioBraille-specific */61static void braille_write(u16 *buf)62{63	static u16 lastwrite[WIDTH];64	unsigned char data[1 + 1 + 2*WIDTH + 2 + 1], csum = 0, *c;65	u16 out;66	int i;67 68	if (!braille_co)69		return;70 71	if (!memcmp(lastwrite, buf, WIDTH * sizeof(*buf)))72		return;73	memcpy(lastwrite, buf, WIDTH * sizeof(*buf));74 75#define SOH 176#define STX 277#define ETX 278#define EOT 479#define ENQ 580	data[0] = STX;81	data[1] = '>';82	csum ^= '>';83	c = &data[2];84	for (i = 0; i < WIDTH; i++) {85		out = buf[i];86		if (out >= 0x100)87			out = '?';88		else if (out == 0x00)89			out = ' ';90		csum ^= out;91		if (out <= 0x05) {92			*c++ = SOH;93			out |= 0x40;94		}95		*c++ = out;96	}97 98	if (csum <= 0x05) {99		*c++ = SOH;100		csum |= 0x40;101	}102	*c++ = csum;103	*c++ = ETX;104 105	braille_co->write(braille_co, data, c - data);106}107 108/* Follow the VC cursor*/109static void vc_follow_cursor(struct vc_data *vc)110{111	vc_x = vc->state.x - (vc->state.x % WIDTH);112	vc_y = vc->state.y;113	lastvc_x = vc->state.x;114	lastvc_y = vc->state.y;115}116 117/* Maybe the VC cursor moved, if so follow it */118static void vc_maybe_cursor_moved(struct vc_data *vc)119{120	if (vc->state.x != lastvc_x || vc->state.y != lastvc_y)121		vc_follow_cursor(vc);122}123 124/* Show portion of VC at vc_x, vc_y */125static void vc_refresh(struct vc_data *vc)126{127	u16 buf[WIDTH];128	int i;129 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);134	}135	braille_write(buf);136}137 138/*139 * Link to keyboard140 */141 142static int keyboard_notifier_call(struct notifier_block *blk,143				  unsigned long code, void *_param)144{145	struct keyboard_notifier_param *param = _param;146	struct vc_data *vc = param->vc;147	int ret = NOTIFY_OK;148 149	if (!param->down)150		return ret;151 152	switch (code) {153	case KBD_KEYCODE:154		if (console_show) {155			if (param->value == BRAILLE_KEY) {156				console_show = 0;157				beep(880);158				vc_maybe_cursor_moved(vc);159				vc_refresh(vc);160				ret = NOTIFY_STOP;161			}162		} else {163			ret = NOTIFY_STOP;164			switch (param->value) {165			case KEY_INSERT:166				beep(440);167				console_show = 1;168				lastVC = -1;169				braille_write(console_buf);170				break;171			case KEY_LEFT:172				if (vc_x > 0) {173					vc_x -= WIDTH;174					if (vc_x < 0)175						vc_x = 0;176				} else if (vc_y >= 1) {177					beep(880);178					vc_y--;179					vc_x = vc->vc_cols-WIDTH;180				} else181					beep(220);182				break;183			case KEY_RIGHT:184				if (vc_x + WIDTH < vc->vc_cols) {185					vc_x += WIDTH;186				} else if (vc_y + 1 < vc->vc_rows) {187					beep(880);188					vc_y++;189					vc_x = 0;190				} else191					beep(220);192				break;193			case KEY_DOWN:194				if (vc_y + 1 < vc->vc_rows)195					vc_y++;196				else197					beep(220);198				break;199			case KEY_UP:200				if (vc_y >= 1)201					vc_y--;202				else203					beep(220);204				break;205			case KEY_HOME:206				vc_follow_cursor(vc);207				break;208			case KEY_PAGEUP:209				vc_x = 0;210				vc_y = 0;211				break;212			case KEY_PAGEDOWN:213				vc_x = 0;214				vc_y = vc->vc_rows-1;215				break;216			default:217				ret = NOTIFY_OK;218				break;219			}220			if (ret == NOTIFY_STOP)221				vc_refresh(vc);222		}223		break;224	case KBD_POST_KEYSYM:225	{226		unsigned char type = KTYP(param->value) - 0xf0;227 228		if (type == KT_SPEC) {229			unsigned char val = KVAL(param->value);230			int on_off = -1;231 232			switch (val) {233			case KVAL(K_CAPS):234				on_off = vt_get_leds(fg_console, VC_CAPSLOCK);235				break;236			case KVAL(K_NUM):237				on_off = vt_get_leds(fg_console, VC_NUMLOCK);238				break;239			case KVAL(K_HOLD):240				on_off = vt_get_leds(fg_console, VC_SCROLLOCK);241				break;242			}243			if (on_off == 1)244				beep(880);245			else if (on_off == 0)246				beep(440);247		}248	}249		break;250	case KBD_UNBOUND_KEYCODE:251	case KBD_UNICODE:252	case KBD_KEYSYM:253		/* Unused */254		break;255	}256	return ret;257}258 259static struct notifier_block keyboard_notifier_block = {260	.notifier_call = keyboard_notifier_call,261};262 263static int vt_notifier_call(struct notifier_block *blk,264			    unsigned long code, void *_param)265{266	struct vt_notifier_param *param = _param;267	struct vc_data *vc = param->vc;268 269	switch (code) {270	case VT_ALLOCATE:271		break;272	case VT_DEALLOCATE:273		break;274	case VT_WRITE:275	{276		unsigned char c = param->c;277 278		if (vc->vc_num != fg_console)279			break;280		switch (c) {281		case '\b':282		case 127:283			if (console_cursor > 0) {284				console_cursor--;285				console_buf[console_cursor] = ' ';286			}287			break;288		case '\n':289		case '\v':290		case '\f':291		case '\r':292			console_newline = 1;293			break;294		case '\t':295			c = ' ';296			fallthrough;297		default:298			if (c < 32)299				/* Ignore other control sequences */300				break;301			if (console_newline) {302				memset(console_buf, 0, sizeof(console_buf));303				console_cursor = 0;304				console_newline = 0;305			}306			if (console_cursor == WIDTH)307				memmove(console_buf, &console_buf[1],308					(WIDTH-1) * sizeof(*console_buf));309			else310				console_cursor++;311			console_buf[console_cursor-1] = c;312			break;313		}314		if (console_show)315			braille_write(console_buf);316		else {317			vc_maybe_cursor_moved(vc);318			vc_refresh(vc);319		}320		break;321	}322	case VT_UPDATE:323		/* Maybe a VT switch, flush */324		if (console_show) {325			if (vc->vc_num != lastVC) {326				lastVC = vc->vc_num;327				memset(console_buf, 0, sizeof(console_buf));328				console_cursor = 0;329				braille_write(console_buf);330			}331		} else {332			vc_maybe_cursor_moved(vc);333			vc_refresh(vc);334		}335		break;336	}337	return NOTIFY_OK;338}339 340static struct notifier_block vt_notifier_block = {341	.notifier_call = vt_notifier_call,342};343 344/*345 * Called from printk.c when console=brl is given346 */347 348int braille_register_console(struct console *console, int index,349		char *console_options, char *braille_options)350{351	int ret;352 353	if (!console_options)354		/* Only support VisioBraille for now */355		console_options = "57600o8";356	if (braille_co)357		return -ENODEV;358	if (console->setup) {359		ret = console->setup(console, console_options);360		if (ret != 0)361			return ret;362	}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);368	return 1;369}370 371int braille_unregister_console(struct console *console)372{373	if (braille_co != console)374		return -EINVAL;375	unregister_keyboard_notifier(&keyboard_notifier_block);376	unregister_vt_notifier(&vt_notifier_block);377	braille_co = NULL;378	return 1;379}380