18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright (C) 2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci/*
78c2ecf20Sopenharmony_ci * LK keyboard driver for Linux, based on sunkbd.c (C) by Vojtech Pavlik
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/*
118c2ecf20Sopenharmony_ci * DEC LK201 and LK401 keyboard driver for Linux (primary for DECstations
128c2ecf20Sopenharmony_ci * and VAXstations, but can also be used on any standard RS232 with an
138c2ecf20Sopenharmony_ci * adaptor).
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * DISCLAIMER: This works for _me_. If you break anything by using the
168c2ecf20Sopenharmony_ci * information given below, I will _not_ be liable!
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * RJ10 pinout:		To DE9:		Or DB25:
198c2ecf20Sopenharmony_ci *	1 - RxD <---->	Pin 3 (TxD) <->	Pin 2 (TxD)
208c2ecf20Sopenharmony_ci *	2 - GND <---->	Pin 5 (GND) <->	Pin 7 (GND)
218c2ecf20Sopenharmony_ci *	4 - TxD <---->	Pin 2 (RxD) <->	Pin 3 (RxD)
228c2ecf20Sopenharmony_ci *	3 - +12V (from HDD drive connector), DON'T connect to DE9 or DB25!!!
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * Pin numbers for DE9 and DB25 are noted on the plug (quite small:). For
258c2ecf20Sopenharmony_ci * RJ10, it's like this:
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci *      __=__	Hold the plug in front of you, cable downwards,
288c2ecf20Sopenharmony_ci *     /___/|	nose is hidden behind the plug. Now, pin 1 is at
298c2ecf20Sopenharmony_ci *    |1234||	the left side, pin 4 at the right and 2 and 3 are
308c2ecf20Sopenharmony_ci *    |IIII||	in between, of course:)
318c2ecf20Sopenharmony_ci *    |    ||
328c2ecf20Sopenharmony_ci *    |____|/
338c2ecf20Sopenharmony_ci *      ||	So the adaptor consists of three connected cables
348c2ecf20Sopenharmony_ci *      ||	for data transmission (RxD and TxD) and signal ground.
358c2ecf20Sopenharmony_ci *		Additionally, you have to get +12V from somewhere.
368c2ecf20Sopenharmony_ci * Most easily, you'll get that from a floppy or HDD power connector.
378c2ecf20Sopenharmony_ci * It's the yellow cable there (black is ground and red is +5V).
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * The keyboard and all the commands it understands are documented in
408c2ecf20Sopenharmony_ci * "VCB02 Video Subsystem - Technical Manual", EK-104AA-TM-001. This
418c2ecf20Sopenharmony_ci * document is LK201 specific, but LK401 is mostly compatible. It comes
428c2ecf20Sopenharmony_ci * up in LK201 mode and doesn't report any of the additional keys it
438c2ecf20Sopenharmony_ci * has. These need to be switched on with the LK_CMD_ENABLE_LK401
448c2ecf20Sopenharmony_ci * command. You'll find this document (scanned .pdf file) on MANX,
458c2ecf20Sopenharmony_ci * a search engine specific to DEC documentation. Try
468c2ecf20Sopenharmony_ci * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/*
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#include <linux/delay.h>
538c2ecf20Sopenharmony_ci#include <linux/slab.h>
548c2ecf20Sopenharmony_ci#include <linux/module.h>
558c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
568c2ecf20Sopenharmony_ci#include <linux/input.h>
578c2ecf20Sopenharmony_ci#include <linux/serio.h>
588c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define DRIVER_DESC	"LK keyboard driver"
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
638c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/*
678c2ecf20Sopenharmony_ci * Known parameters:
688c2ecf20Sopenharmony_ci *	bell_volume
698c2ecf20Sopenharmony_ci *	keyclick_volume
708c2ecf20Sopenharmony_ci *	ctrlclick_volume
718c2ecf20Sopenharmony_ci *
728c2ecf20Sopenharmony_ci * Please notice that there's not yet an API to set these at runtime.
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_cistatic int bell_volume = 100; /* % */
758c2ecf20Sopenharmony_cimodule_param(bell_volume, int, 0);
768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bell_volume, "Bell volume (in %). default is 100%");
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic int keyclick_volume = 100; /* % */
798c2ecf20Sopenharmony_cimodule_param(keyclick_volume, int, 0);
808c2ecf20Sopenharmony_ciMODULE_PARM_DESC(keyclick_volume, "Keyclick volume (in %), default is 100%");
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int ctrlclick_volume = 100; /* % */
838c2ecf20Sopenharmony_cimodule_param(ctrlclick_volume, int, 0);
848c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ctrlclick_volume, "Ctrlclick volume (in %), default is 100%");
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int lk201_compose_is_alt;
878c2ecf20Sopenharmony_cimodule_param(lk201_compose_is_alt, int, 0);
888c2ecf20Sopenharmony_ciMODULE_PARM_DESC(lk201_compose_is_alt,
898c2ecf20Sopenharmony_ci		 "If set non-zero, LK201' Compose key will act as an Alt key");
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#undef LKKBD_DEBUG
948c2ecf20Sopenharmony_ci#ifdef LKKBD_DEBUG
958c2ecf20Sopenharmony_ci#define DBG(x...) printk(x)
968c2ecf20Sopenharmony_ci#else
978c2ecf20Sopenharmony_ci#define DBG(x...) do {} while (0)
988c2ecf20Sopenharmony_ci#endif
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/* LED control */
1018c2ecf20Sopenharmony_ci#define LK_LED_WAIT		0x81
1028c2ecf20Sopenharmony_ci#define LK_LED_COMPOSE		0x82
1038c2ecf20Sopenharmony_ci#define LK_LED_SHIFTLOCK	0x84
1048c2ecf20Sopenharmony_ci#define LK_LED_SCROLLLOCK	0x88
1058c2ecf20Sopenharmony_ci#define LK_CMD_LED_ON		0x13
1068c2ecf20Sopenharmony_ci#define LK_CMD_LED_OFF		0x11
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/* Mode control */
1098c2ecf20Sopenharmony_ci#define LK_MODE_DOWN		0x80
1108c2ecf20Sopenharmony_ci#define LK_MODE_AUTODOWN	0x82
1118c2ecf20Sopenharmony_ci#define LK_MODE_UPDOWN		0x86
1128c2ecf20Sopenharmony_ci#define LK_CMD_SET_MODE(mode, div)	((mode) | ((div) << 3))
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* Misc commands */
1158c2ecf20Sopenharmony_ci#define LK_CMD_ENABLE_KEYCLICK	0x1b
1168c2ecf20Sopenharmony_ci#define LK_CMD_DISABLE_KEYCLICK	0x99
1178c2ecf20Sopenharmony_ci#define LK_CMD_DISABLE_BELL	0xa1
1188c2ecf20Sopenharmony_ci#define LK_CMD_SOUND_BELL	0xa7
1198c2ecf20Sopenharmony_ci#define LK_CMD_ENABLE_BELL	0x23
1208c2ecf20Sopenharmony_ci#define LK_CMD_DISABLE_CTRCLICK	0xb9
1218c2ecf20Sopenharmony_ci#define LK_CMD_ENABLE_CTRCLICK	0xbb
1228c2ecf20Sopenharmony_ci#define LK_CMD_SET_DEFAULTS	0xd3
1238c2ecf20Sopenharmony_ci#define LK_CMD_POWERCYCLE_RESET	0xfd
1248c2ecf20Sopenharmony_ci#define LK_CMD_ENABLE_LK401	0xe9
1258c2ecf20Sopenharmony_ci#define LK_CMD_REQUEST_ID	0xab
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci/* Misc responses from keyboard */
1288c2ecf20Sopenharmony_ci#define LK_STUCK_KEY		0x3d
1298c2ecf20Sopenharmony_ci#define LK_SELFTEST_FAILED	0x3e
1308c2ecf20Sopenharmony_ci#define LK_ALL_KEYS_UP		0xb3
1318c2ecf20Sopenharmony_ci#define LK_METRONOME		0xb4
1328c2ecf20Sopenharmony_ci#define LK_OUTPUT_ERROR		0xb5
1338c2ecf20Sopenharmony_ci#define LK_INPUT_ERROR		0xb6
1348c2ecf20Sopenharmony_ci#define LK_KBD_LOCKED		0xb7
1358c2ecf20Sopenharmony_ci#define LK_KBD_TEST_MODE_ACK	0xb8
1368c2ecf20Sopenharmony_ci#define LK_PREFIX_KEY_DOWN	0xb9
1378c2ecf20Sopenharmony_ci#define LK_MODE_CHANGE_ACK	0xba
1388c2ecf20Sopenharmony_ci#define LK_RESPONSE_RESERVED	0xbb
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci#define LK_NUM_KEYCODES		256
1418c2ecf20Sopenharmony_ci#define LK_NUM_IGNORE_BYTES	6
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic unsigned short lkkbd_keycode[LK_NUM_KEYCODES] = {
1448c2ecf20Sopenharmony_ci	[0x56] = KEY_F1,
1458c2ecf20Sopenharmony_ci	[0x57] = KEY_F2,
1468c2ecf20Sopenharmony_ci	[0x58] = KEY_F3,
1478c2ecf20Sopenharmony_ci	[0x59] = KEY_F4,
1488c2ecf20Sopenharmony_ci	[0x5a] = KEY_F5,
1498c2ecf20Sopenharmony_ci	[0x64] = KEY_F6,
1508c2ecf20Sopenharmony_ci	[0x65] = KEY_F7,
1518c2ecf20Sopenharmony_ci	[0x66] = KEY_F8,
1528c2ecf20Sopenharmony_ci	[0x67] = KEY_F9,
1538c2ecf20Sopenharmony_ci	[0x68] = KEY_F10,
1548c2ecf20Sopenharmony_ci	[0x71] = KEY_F11,
1558c2ecf20Sopenharmony_ci	[0x72] = KEY_F12,
1568c2ecf20Sopenharmony_ci	[0x73] = KEY_F13,
1578c2ecf20Sopenharmony_ci	[0x74] = KEY_F14,
1588c2ecf20Sopenharmony_ci	[0x7c] = KEY_F15,
1598c2ecf20Sopenharmony_ci	[0x7d] = KEY_F16,
1608c2ecf20Sopenharmony_ci	[0x80] = KEY_F17,
1618c2ecf20Sopenharmony_ci	[0x81] = KEY_F18,
1628c2ecf20Sopenharmony_ci	[0x82] = KEY_F19,
1638c2ecf20Sopenharmony_ci	[0x83] = KEY_F20,
1648c2ecf20Sopenharmony_ci	[0x8a] = KEY_FIND,
1658c2ecf20Sopenharmony_ci	[0x8b] = KEY_INSERT,
1668c2ecf20Sopenharmony_ci	[0x8c] = KEY_DELETE,
1678c2ecf20Sopenharmony_ci	[0x8d] = KEY_SELECT,
1688c2ecf20Sopenharmony_ci	[0x8e] = KEY_PAGEUP,
1698c2ecf20Sopenharmony_ci	[0x8f] = KEY_PAGEDOWN,
1708c2ecf20Sopenharmony_ci	[0x92] = KEY_KP0,
1718c2ecf20Sopenharmony_ci	[0x94] = KEY_KPDOT,
1728c2ecf20Sopenharmony_ci	[0x95] = KEY_KPENTER,
1738c2ecf20Sopenharmony_ci	[0x96] = KEY_KP1,
1748c2ecf20Sopenharmony_ci	[0x97] = KEY_KP2,
1758c2ecf20Sopenharmony_ci	[0x98] = KEY_KP3,
1768c2ecf20Sopenharmony_ci	[0x99] = KEY_KP4,
1778c2ecf20Sopenharmony_ci	[0x9a] = KEY_KP5,
1788c2ecf20Sopenharmony_ci	[0x9b] = KEY_KP6,
1798c2ecf20Sopenharmony_ci	[0x9c] = KEY_KPCOMMA,
1808c2ecf20Sopenharmony_ci	[0x9d] = KEY_KP7,
1818c2ecf20Sopenharmony_ci	[0x9e] = KEY_KP8,
1828c2ecf20Sopenharmony_ci	[0x9f] = KEY_KP9,
1838c2ecf20Sopenharmony_ci	[0xa0] = KEY_KPMINUS,
1848c2ecf20Sopenharmony_ci	[0xa1] = KEY_PROG1,
1858c2ecf20Sopenharmony_ci	[0xa2] = KEY_PROG2,
1868c2ecf20Sopenharmony_ci	[0xa3] = KEY_PROG3,
1878c2ecf20Sopenharmony_ci	[0xa4] = KEY_PROG4,
1888c2ecf20Sopenharmony_ci	[0xa7] = KEY_LEFT,
1898c2ecf20Sopenharmony_ci	[0xa8] = KEY_RIGHT,
1908c2ecf20Sopenharmony_ci	[0xa9] = KEY_DOWN,
1918c2ecf20Sopenharmony_ci	[0xaa] = KEY_UP,
1928c2ecf20Sopenharmony_ci	[0xab] = KEY_RIGHTSHIFT,
1938c2ecf20Sopenharmony_ci	[0xac] = KEY_LEFTALT,
1948c2ecf20Sopenharmony_ci	[0xad] = KEY_COMPOSE, /* Right Compose, that is. */
1958c2ecf20Sopenharmony_ci	[0xae] = KEY_LEFTSHIFT, /* Same as KEY_RIGHTSHIFT on LK201 */
1968c2ecf20Sopenharmony_ci	[0xaf] = KEY_LEFTCTRL,
1978c2ecf20Sopenharmony_ci	[0xb0] = KEY_CAPSLOCK,
1988c2ecf20Sopenharmony_ci	[0xb1] = KEY_COMPOSE, /* Left Compose, that is. */
1998c2ecf20Sopenharmony_ci	[0xb2] = KEY_RIGHTALT,
2008c2ecf20Sopenharmony_ci	[0xbc] = KEY_BACKSPACE,
2018c2ecf20Sopenharmony_ci	[0xbd] = KEY_ENTER,
2028c2ecf20Sopenharmony_ci	[0xbe] = KEY_TAB,
2038c2ecf20Sopenharmony_ci	[0xbf] = KEY_ESC,
2048c2ecf20Sopenharmony_ci	[0xc0] = KEY_1,
2058c2ecf20Sopenharmony_ci	[0xc1] = KEY_Q,
2068c2ecf20Sopenharmony_ci	[0xc2] = KEY_A,
2078c2ecf20Sopenharmony_ci	[0xc3] = KEY_Z,
2088c2ecf20Sopenharmony_ci	[0xc5] = KEY_2,
2098c2ecf20Sopenharmony_ci	[0xc6] = KEY_W,
2108c2ecf20Sopenharmony_ci	[0xc7] = KEY_S,
2118c2ecf20Sopenharmony_ci	[0xc8] = KEY_X,
2128c2ecf20Sopenharmony_ci	[0xc9] = KEY_102ND,
2138c2ecf20Sopenharmony_ci	[0xcb] = KEY_3,
2148c2ecf20Sopenharmony_ci	[0xcc] = KEY_E,
2158c2ecf20Sopenharmony_ci	[0xcd] = KEY_D,
2168c2ecf20Sopenharmony_ci	[0xce] = KEY_C,
2178c2ecf20Sopenharmony_ci	[0xd0] = KEY_4,
2188c2ecf20Sopenharmony_ci	[0xd1] = KEY_R,
2198c2ecf20Sopenharmony_ci	[0xd2] = KEY_F,
2208c2ecf20Sopenharmony_ci	[0xd3] = KEY_V,
2218c2ecf20Sopenharmony_ci	[0xd4] = KEY_SPACE,
2228c2ecf20Sopenharmony_ci	[0xd6] = KEY_5,
2238c2ecf20Sopenharmony_ci	[0xd7] = KEY_T,
2248c2ecf20Sopenharmony_ci	[0xd8] = KEY_G,
2258c2ecf20Sopenharmony_ci	[0xd9] = KEY_B,
2268c2ecf20Sopenharmony_ci	[0xdb] = KEY_6,
2278c2ecf20Sopenharmony_ci	[0xdc] = KEY_Y,
2288c2ecf20Sopenharmony_ci	[0xdd] = KEY_H,
2298c2ecf20Sopenharmony_ci	[0xde] = KEY_N,
2308c2ecf20Sopenharmony_ci	[0xe0] = KEY_7,
2318c2ecf20Sopenharmony_ci	[0xe1] = KEY_U,
2328c2ecf20Sopenharmony_ci	[0xe2] = KEY_J,
2338c2ecf20Sopenharmony_ci	[0xe3] = KEY_M,
2348c2ecf20Sopenharmony_ci	[0xe5] = KEY_8,
2358c2ecf20Sopenharmony_ci	[0xe6] = KEY_I,
2368c2ecf20Sopenharmony_ci	[0xe7] = KEY_K,
2378c2ecf20Sopenharmony_ci	[0xe8] = KEY_COMMA,
2388c2ecf20Sopenharmony_ci	[0xea] = KEY_9,
2398c2ecf20Sopenharmony_ci	[0xeb] = KEY_O,
2408c2ecf20Sopenharmony_ci	[0xec] = KEY_L,
2418c2ecf20Sopenharmony_ci	[0xed] = KEY_DOT,
2428c2ecf20Sopenharmony_ci	[0xef] = KEY_0,
2438c2ecf20Sopenharmony_ci	[0xf0] = KEY_P,
2448c2ecf20Sopenharmony_ci	[0xf2] = KEY_SEMICOLON,
2458c2ecf20Sopenharmony_ci	[0xf3] = KEY_SLASH,
2468c2ecf20Sopenharmony_ci	[0xf5] = KEY_EQUAL,
2478c2ecf20Sopenharmony_ci	[0xf6] = KEY_RIGHTBRACE,
2488c2ecf20Sopenharmony_ci	[0xf7] = KEY_BACKSLASH,
2498c2ecf20Sopenharmony_ci	[0xf9] = KEY_MINUS,
2508c2ecf20Sopenharmony_ci	[0xfa] = KEY_LEFTBRACE,
2518c2ecf20Sopenharmony_ci	[0xfb] = KEY_APOSTROPHE,
2528c2ecf20Sopenharmony_ci};
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci#define CHECK_LED(LK, VAR_ON, VAR_OFF, LED, BITS) do {		\
2558c2ecf20Sopenharmony_ci	if (test_bit(LED, (LK)->dev->led))			\
2568c2ecf20Sopenharmony_ci		VAR_ON |= BITS;					\
2578c2ecf20Sopenharmony_ci	else							\
2588c2ecf20Sopenharmony_ci		VAR_OFF |= BITS;				\
2598c2ecf20Sopenharmony_ci	} while (0)
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci/*
2628c2ecf20Sopenharmony_ci * Per-keyboard data
2638c2ecf20Sopenharmony_ci */
2648c2ecf20Sopenharmony_cistruct lkkbd {
2658c2ecf20Sopenharmony_ci	unsigned short keycode[LK_NUM_KEYCODES];
2668c2ecf20Sopenharmony_ci	int ignore_bytes;
2678c2ecf20Sopenharmony_ci	unsigned char id[LK_NUM_IGNORE_BYTES];
2688c2ecf20Sopenharmony_ci	struct input_dev *dev;
2698c2ecf20Sopenharmony_ci	struct serio *serio;
2708c2ecf20Sopenharmony_ci	struct work_struct tq;
2718c2ecf20Sopenharmony_ci	char name[64];
2728c2ecf20Sopenharmony_ci	char phys[32];
2738c2ecf20Sopenharmony_ci	char type;
2748c2ecf20Sopenharmony_ci	int bell_volume;
2758c2ecf20Sopenharmony_ci	int keyclick_volume;
2768c2ecf20Sopenharmony_ci	int ctrlclick_volume;
2778c2ecf20Sopenharmony_ci};
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci#ifdef LKKBD_DEBUG
2808c2ecf20Sopenharmony_ci/*
2818c2ecf20Sopenharmony_ci * Responses from the keyboard and mapping back to their names.
2828c2ecf20Sopenharmony_ci */
2838c2ecf20Sopenharmony_cistatic struct {
2848c2ecf20Sopenharmony_ci	unsigned char value;
2858c2ecf20Sopenharmony_ci	unsigned char *name;
2868c2ecf20Sopenharmony_ci} lk_response[] = {
2878c2ecf20Sopenharmony_ci#define RESPONSE(x) { .value = (x), .name = #x, }
2888c2ecf20Sopenharmony_ci	RESPONSE(LK_STUCK_KEY),
2898c2ecf20Sopenharmony_ci	RESPONSE(LK_SELFTEST_FAILED),
2908c2ecf20Sopenharmony_ci	RESPONSE(LK_ALL_KEYS_UP),
2918c2ecf20Sopenharmony_ci	RESPONSE(LK_METRONOME),
2928c2ecf20Sopenharmony_ci	RESPONSE(LK_OUTPUT_ERROR),
2938c2ecf20Sopenharmony_ci	RESPONSE(LK_INPUT_ERROR),
2948c2ecf20Sopenharmony_ci	RESPONSE(LK_KBD_LOCKED),
2958c2ecf20Sopenharmony_ci	RESPONSE(LK_KBD_TEST_MODE_ACK),
2968c2ecf20Sopenharmony_ci	RESPONSE(LK_PREFIX_KEY_DOWN),
2978c2ecf20Sopenharmony_ci	RESPONSE(LK_MODE_CHANGE_ACK),
2988c2ecf20Sopenharmony_ci	RESPONSE(LK_RESPONSE_RESERVED),
2998c2ecf20Sopenharmony_ci#undef RESPONSE
3008c2ecf20Sopenharmony_ci};
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic unsigned char *response_name(unsigned char value)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	int i;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(lk_response); i++)
3078c2ecf20Sopenharmony_ci		if (lk_response[i].value == value)
3088c2ecf20Sopenharmony_ci			return lk_response[i].name;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return "<unknown>";
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci#endif /* LKKBD_DEBUG */
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci/*
3158c2ecf20Sopenharmony_ci * Calculate volume parameter byte for a given volume.
3168c2ecf20Sopenharmony_ci */
3178c2ecf20Sopenharmony_cistatic unsigned char volume_to_hw(int volume_percent)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	unsigned char ret = 0;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (volume_percent < 0)
3228c2ecf20Sopenharmony_ci		volume_percent = 0;
3238c2ecf20Sopenharmony_ci	if (volume_percent > 100)
3248c2ecf20Sopenharmony_ci		volume_percent = 100;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (volume_percent >= 0)
3278c2ecf20Sopenharmony_ci		ret = 7;
3288c2ecf20Sopenharmony_ci	if (volume_percent >= 13)	/* 12.5 */
3298c2ecf20Sopenharmony_ci		ret = 6;
3308c2ecf20Sopenharmony_ci	if (volume_percent >= 25)
3318c2ecf20Sopenharmony_ci		ret = 5;
3328c2ecf20Sopenharmony_ci	if (volume_percent >= 38)	/* 37.5 */
3338c2ecf20Sopenharmony_ci		ret = 4;
3348c2ecf20Sopenharmony_ci	if (volume_percent >= 50)
3358c2ecf20Sopenharmony_ci		ret = 3;
3368c2ecf20Sopenharmony_ci	if (volume_percent >= 63)	/* 62.5 */
3378c2ecf20Sopenharmony_ci		ret = 2;		/* This is the default volume */
3388c2ecf20Sopenharmony_ci	if (volume_percent >= 75)
3398c2ecf20Sopenharmony_ci		ret = 1;
3408c2ecf20Sopenharmony_ci	if (volume_percent >= 88)	/* 87.5 */
3418c2ecf20Sopenharmony_ci		ret = 0;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	ret |= 0x80;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	return ret;
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic void lkkbd_detection_done(struct lkkbd *lk)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	int i;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/*
3538c2ecf20Sopenharmony_ci	 * Reset setting for Compose key. Let Compose be KEY_COMPOSE.
3548c2ecf20Sopenharmony_ci	 */
3558c2ecf20Sopenharmony_ci	lk->keycode[0xb1] = KEY_COMPOSE;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	/*
3588c2ecf20Sopenharmony_ci	 * Print keyboard name and modify Compose=Alt on user's request.
3598c2ecf20Sopenharmony_ci	 */
3608c2ecf20Sopenharmony_ci	switch (lk->id[4]) {
3618c2ecf20Sopenharmony_ci	case 1:
3628c2ecf20Sopenharmony_ci		strlcpy(lk->name, "DEC LK201 keyboard", sizeof(lk->name));
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		if (lk201_compose_is_alt)
3658c2ecf20Sopenharmony_ci			lk->keycode[0xb1] = KEY_LEFTALT;
3668c2ecf20Sopenharmony_ci		break;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	case 2:
3698c2ecf20Sopenharmony_ci		strlcpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name));
3708c2ecf20Sopenharmony_ci		break;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	default:
3738c2ecf20Sopenharmony_ci		strlcpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name));
3748c2ecf20Sopenharmony_ci		printk(KERN_ERR
3758c2ecf20Sopenharmony_ci			"lkkbd: keyboard on %s is unknown, please report to "
3768c2ecf20Sopenharmony_ci			"Jan-Benedict Glaw <jbglaw@lug-owl.de>\n", lk->phys);
3778c2ecf20Sopenharmony_ci		printk(KERN_ERR "lkkbd: keyboard ID'ed as:");
3788c2ecf20Sopenharmony_ci		for (i = 0; i < LK_NUM_IGNORE_BYTES; i++)
3798c2ecf20Sopenharmony_ci			printk(" 0x%02x", lk->id[i]);
3808c2ecf20Sopenharmony_ci		printk("\n");
3818c2ecf20Sopenharmony_ci		break;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	printk(KERN_INFO "lkkbd: keyboard on %s identified as: %s\n",
3858c2ecf20Sopenharmony_ci		lk->phys, lk->name);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	/*
3888c2ecf20Sopenharmony_ci	 * Report errors during keyboard boot-up.
3898c2ecf20Sopenharmony_ci	 */
3908c2ecf20Sopenharmony_ci	switch (lk->id[2]) {
3918c2ecf20Sopenharmony_ci	case 0x00:
3928c2ecf20Sopenharmony_ci		/* All okay */
3938c2ecf20Sopenharmony_ci		break;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	case LK_STUCK_KEY:
3968c2ecf20Sopenharmony_ci		printk(KERN_ERR "lkkbd: Stuck key on keyboard at %s\n",
3978c2ecf20Sopenharmony_ci			lk->phys);
3988c2ecf20Sopenharmony_ci		break;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	case LK_SELFTEST_FAILED:
4018c2ecf20Sopenharmony_ci		printk(KERN_ERR
4028c2ecf20Sopenharmony_ci			"lkkbd: Selftest failed on keyboard at %s, "
4038c2ecf20Sopenharmony_ci			"keyboard may not work properly\n", lk->phys);
4048c2ecf20Sopenharmony_ci		break;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	default:
4078c2ecf20Sopenharmony_ci		printk(KERN_ERR
4088c2ecf20Sopenharmony_ci			"lkkbd: Unknown error %02x on keyboard at %s\n",
4098c2ecf20Sopenharmony_ci			lk->id[2], lk->phys);
4108c2ecf20Sopenharmony_ci		break;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	/*
4148c2ecf20Sopenharmony_ci	 * Try to hint user if there's a stuck key.
4158c2ecf20Sopenharmony_ci	 */
4168c2ecf20Sopenharmony_ci	if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0)
4178c2ecf20Sopenharmony_ci		printk(KERN_ERR
4188c2ecf20Sopenharmony_ci			"Scancode of stuck key is 0x%02x, keycode is 0x%04x\n",
4198c2ecf20Sopenharmony_ci			lk->id[3], lk->keycode[lk->id[3]]);
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci/*
4238c2ecf20Sopenharmony_ci * lkkbd_interrupt() is called by the low level driver when a character
4248c2ecf20Sopenharmony_ci * is received.
4258c2ecf20Sopenharmony_ci */
4268c2ecf20Sopenharmony_cistatic irqreturn_t lkkbd_interrupt(struct serio *serio,
4278c2ecf20Sopenharmony_ci				   unsigned char data, unsigned int flags)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	struct lkkbd *lk = serio_get_drvdata(serio);
4308c2ecf20Sopenharmony_ci	struct input_dev *input_dev = lk->dev;
4318c2ecf20Sopenharmony_ci	unsigned int keycode;
4328c2ecf20Sopenharmony_ci	int i;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	DBG(KERN_INFO "Got byte 0x%02x\n", data);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	if (lk->ignore_bytes > 0) {
4378c2ecf20Sopenharmony_ci		DBG(KERN_INFO "Ignoring a byte on %s\n", lk->name);
4388c2ecf20Sopenharmony_ci		lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci		if (lk->ignore_bytes == 0)
4418c2ecf20Sopenharmony_ci			lkkbd_detection_done(lk);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	switch (data) {
4478c2ecf20Sopenharmony_ci	case LK_ALL_KEYS_UP:
4488c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(lkkbd_keycode); i++)
4498c2ecf20Sopenharmony_ci			input_report_key(input_dev, lk->keycode[i], 0);
4508c2ecf20Sopenharmony_ci		input_sync(input_dev);
4518c2ecf20Sopenharmony_ci		break;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	case 0x01:
4548c2ecf20Sopenharmony_ci		DBG(KERN_INFO "Got 0x01, scheduling re-initialization\n");
4558c2ecf20Sopenharmony_ci		lk->ignore_bytes = LK_NUM_IGNORE_BYTES;
4568c2ecf20Sopenharmony_ci		lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
4578c2ecf20Sopenharmony_ci		schedule_work(&lk->tq);
4588c2ecf20Sopenharmony_ci		break;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	case LK_METRONOME:
4618c2ecf20Sopenharmony_ci	case LK_OUTPUT_ERROR:
4628c2ecf20Sopenharmony_ci	case LK_INPUT_ERROR:
4638c2ecf20Sopenharmony_ci	case LK_KBD_LOCKED:
4648c2ecf20Sopenharmony_ci	case LK_KBD_TEST_MODE_ACK:
4658c2ecf20Sopenharmony_ci	case LK_PREFIX_KEY_DOWN:
4668c2ecf20Sopenharmony_ci	case LK_MODE_CHANGE_ACK:
4678c2ecf20Sopenharmony_ci	case LK_RESPONSE_RESERVED:
4688c2ecf20Sopenharmony_ci		DBG(KERN_INFO "Got %s and don't know how to handle...\n",
4698c2ecf20Sopenharmony_ci			response_name(data));
4708c2ecf20Sopenharmony_ci		break;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	default:
4738c2ecf20Sopenharmony_ci		keycode = lk->keycode[data];
4748c2ecf20Sopenharmony_ci		if (keycode != KEY_RESERVED) {
4758c2ecf20Sopenharmony_ci			input_report_key(input_dev, keycode,
4768c2ecf20Sopenharmony_ci					 !test_bit(keycode, input_dev->key));
4778c2ecf20Sopenharmony_ci			input_sync(input_dev);
4788c2ecf20Sopenharmony_ci		} else {
4798c2ecf20Sopenharmony_ci			printk(KERN_WARNING
4808c2ecf20Sopenharmony_ci				"%s: Unknown key with scancode 0x%02x on %s.\n",
4818c2ecf20Sopenharmony_ci				__FILE__, data, lk->name);
4828c2ecf20Sopenharmony_ci		}
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic void lkkbd_toggle_leds(struct lkkbd *lk)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	struct serio *serio = lk->serio;
4918c2ecf20Sopenharmony_ci	unsigned char leds_on = 0;
4928c2ecf20Sopenharmony_ci	unsigned char leds_off = 0;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	CHECK_LED(lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
4958c2ecf20Sopenharmony_ci	CHECK_LED(lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE);
4968c2ecf20Sopenharmony_ci	CHECK_LED(lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
4978c2ecf20Sopenharmony_ci	CHECK_LED(lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
4988c2ecf20Sopenharmony_ci	if (leds_on != 0) {
4998c2ecf20Sopenharmony_ci		serio_write(serio, LK_CMD_LED_ON);
5008c2ecf20Sopenharmony_ci		serio_write(serio, leds_on);
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci	if (leds_off != 0) {
5038c2ecf20Sopenharmony_ci		serio_write(serio, LK_CMD_LED_OFF);
5048c2ecf20Sopenharmony_ci		serio_write(serio, leds_off);
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic void lkkbd_toggle_keyclick(struct lkkbd *lk, bool on)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	struct serio *serio = lk->serio;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if (on) {
5138c2ecf20Sopenharmony_ci		DBG("%s: Activating key clicks\n", __func__);
5148c2ecf20Sopenharmony_ci		serio_write(serio, LK_CMD_ENABLE_KEYCLICK);
5158c2ecf20Sopenharmony_ci		serio_write(serio, volume_to_hw(lk->keyclick_volume));
5168c2ecf20Sopenharmony_ci		serio_write(serio, LK_CMD_ENABLE_CTRCLICK);
5178c2ecf20Sopenharmony_ci		serio_write(serio, volume_to_hw(lk->ctrlclick_volume));
5188c2ecf20Sopenharmony_ci	} else {
5198c2ecf20Sopenharmony_ci		DBG("%s: Deactivating key clicks\n", __func__);
5208c2ecf20Sopenharmony_ci		serio_write(serio, LK_CMD_DISABLE_KEYCLICK);
5218c2ecf20Sopenharmony_ci		serio_write(serio, LK_CMD_DISABLE_CTRCLICK);
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci/*
5278c2ecf20Sopenharmony_ci * lkkbd_event() handles events from the input module.
5288c2ecf20Sopenharmony_ci */
5298c2ecf20Sopenharmony_cistatic int lkkbd_event(struct input_dev *dev,
5308c2ecf20Sopenharmony_ci			unsigned int type, unsigned int code, int value)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	struct lkkbd *lk = input_get_drvdata(dev);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	switch (type) {
5358c2ecf20Sopenharmony_ci	case EV_LED:
5368c2ecf20Sopenharmony_ci		lkkbd_toggle_leds(lk);
5378c2ecf20Sopenharmony_ci		return 0;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	case EV_SND:
5408c2ecf20Sopenharmony_ci		switch (code) {
5418c2ecf20Sopenharmony_ci		case SND_CLICK:
5428c2ecf20Sopenharmony_ci			lkkbd_toggle_keyclick(lk, value);
5438c2ecf20Sopenharmony_ci			return 0;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci		case SND_BELL:
5468c2ecf20Sopenharmony_ci			if (value != 0)
5478c2ecf20Sopenharmony_ci				serio_write(lk->serio, LK_CMD_SOUND_BELL);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci			return 0;
5508c2ecf20Sopenharmony_ci		}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci		break;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	default:
5558c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s(): Got unknown type %d, code %d, value %d\n",
5568c2ecf20Sopenharmony_ci			__func__, type, code, value);
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	return -1;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci/*
5638c2ecf20Sopenharmony_ci * lkkbd_reinit() sets leds and beeps to a state the computer remembers they
5648c2ecf20Sopenharmony_ci * were in.
5658c2ecf20Sopenharmony_ci */
5668c2ecf20Sopenharmony_cistatic void lkkbd_reinit(struct work_struct *work)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct lkkbd *lk = container_of(work, struct lkkbd, tq);
5698c2ecf20Sopenharmony_ci	int division;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	/* Ask for ID */
5728c2ecf20Sopenharmony_ci	serio_write(lk->serio, LK_CMD_REQUEST_ID);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	/* Reset parameters */
5758c2ecf20Sopenharmony_ci	serio_write(lk->serio, LK_CMD_SET_DEFAULTS);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	/* Set LEDs */
5788c2ecf20Sopenharmony_ci	lkkbd_toggle_leds(lk);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	/*
5818c2ecf20Sopenharmony_ci	 * Try to activate extended LK401 mode. This command will
5828c2ecf20Sopenharmony_ci	 * only work with a LK401 keyboard and grants access to
5838c2ecf20Sopenharmony_ci	 * LAlt, RAlt, RCompose and RShift.
5848c2ecf20Sopenharmony_ci	 */
5858c2ecf20Sopenharmony_ci	serio_write(lk->serio, LK_CMD_ENABLE_LK401);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	/* Set all keys to UPDOWN mode */
5888c2ecf20Sopenharmony_ci	for (division = 1; division <= 14; division++)
5898c2ecf20Sopenharmony_ci		serio_write(lk->serio,
5908c2ecf20Sopenharmony_ci			    LK_CMD_SET_MODE(LK_MODE_UPDOWN, division));
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	/* Enable bell and set volume */
5938c2ecf20Sopenharmony_ci	serio_write(lk->serio, LK_CMD_ENABLE_BELL);
5948c2ecf20Sopenharmony_ci	serio_write(lk->serio, volume_to_hw(lk->bell_volume));
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	/* Enable/disable keyclick (and possibly set volume) */
5978c2ecf20Sopenharmony_ci	lkkbd_toggle_keyclick(lk, test_bit(SND_CLICK, lk->dev->snd));
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	/* Sound the bell if needed */
6008c2ecf20Sopenharmony_ci	if (test_bit(SND_BELL, lk->dev->snd))
6018c2ecf20Sopenharmony_ci		serio_write(lk->serio, LK_CMD_SOUND_BELL);
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci/*
6058c2ecf20Sopenharmony_ci * lkkbd_connect() probes for a LK keyboard and fills the necessary structures.
6068c2ecf20Sopenharmony_ci */
6078c2ecf20Sopenharmony_cistatic int lkkbd_connect(struct serio *serio, struct serio_driver *drv)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	struct lkkbd *lk;
6108c2ecf20Sopenharmony_ci	struct input_dev *input_dev;
6118c2ecf20Sopenharmony_ci	int i;
6128c2ecf20Sopenharmony_ci	int err;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	lk = kzalloc(sizeof(struct lkkbd), GFP_KERNEL);
6158c2ecf20Sopenharmony_ci	input_dev = input_allocate_device();
6168c2ecf20Sopenharmony_ci	if (!lk || !input_dev) {
6178c2ecf20Sopenharmony_ci		err = -ENOMEM;
6188c2ecf20Sopenharmony_ci		goto fail1;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	lk->serio = serio;
6228c2ecf20Sopenharmony_ci	lk->dev = input_dev;
6238c2ecf20Sopenharmony_ci	INIT_WORK(&lk->tq, lkkbd_reinit);
6248c2ecf20Sopenharmony_ci	lk->bell_volume = bell_volume;
6258c2ecf20Sopenharmony_ci	lk->keyclick_volume = keyclick_volume;
6268c2ecf20Sopenharmony_ci	lk->ctrlclick_volume = ctrlclick_volume;
6278c2ecf20Sopenharmony_ci	memcpy(lk->keycode, lkkbd_keycode, sizeof(lk->keycode));
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	strlcpy(lk->name, "DEC LK keyboard", sizeof(lk->name));
6308c2ecf20Sopenharmony_ci	snprintf(lk->phys, sizeof(lk->phys), "%s/input0", serio->phys);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	input_dev->name = lk->name;
6338c2ecf20Sopenharmony_ci	input_dev->phys = lk->phys;
6348c2ecf20Sopenharmony_ci	input_dev->id.bustype = BUS_RS232;
6358c2ecf20Sopenharmony_ci	input_dev->id.vendor = SERIO_LKKBD;
6368c2ecf20Sopenharmony_ci	input_dev->id.product = 0;
6378c2ecf20Sopenharmony_ci	input_dev->id.version = 0x0100;
6388c2ecf20Sopenharmony_ci	input_dev->dev.parent = &serio->dev;
6398c2ecf20Sopenharmony_ci	input_dev->event = lkkbd_event;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	input_set_drvdata(input_dev, lk);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	__set_bit(EV_KEY, input_dev->evbit);
6448c2ecf20Sopenharmony_ci	__set_bit(EV_LED, input_dev->evbit);
6458c2ecf20Sopenharmony_ci	__set_bit(EV_SND, input_dev->evbit);
6468c2ecf20Sopenharmony_ci	__set_bit(EV_REP, input_dev->evbit);
6478c2ecf20Sopenharmony_ci	__set_bit(LED_CAPSL, input_dev->ledbit);
6488c2ecf20Sopenharmony_ci	__set_bit(LED_SLEEP, input_dev->ledbit);
6498c2ecf20Sopenharmony_ci	__set_bit(LED_COMPOSE, input_dev->ledbit);
6508c2ecf20Sopenharmony_ci	__set_bit(LED_SCROLLL, input_dev->ledbit);
6518c2ecf20Sopenharmony_ci	__set_bit(SND_BELL, input_dev->sndbit);
6528c2ecf20Sopenharmony_ci	__set_bit(SND_CLICK, input_dev->sndbit);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	input_dev->keycode = lk->keycode;
6558c2ecf20Sopenharmony_ci	input_dev->keycodesize = sizeof(lk->keycode[0]);
6568c2ecf20Sopenharmony_ci	input_dev->keycodemax = ARRAY_SIZE(lk->keycode);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	for (i = 0; i < LK_NUM_KEYCODES; i++)
6598c2ecf20Sopenharmony_ci		__set_bit(lk->keycode[i], input_dev->keybit);
6608c2ecf20Sopenharmony_ci	__clear_bit(KEY_RESERVED, input_dev->keybit);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	serio_set_drvdata(serio, lk);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	err = serio_open(serio, drv);
6658c2ecf20Sopenharmony_ci	if (err)
6668c2ecf20Sopenharmony_ci		goto fail2;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	err = input_register_device(lk->dev);
6698c2ecf20Sopenharmony_ci	if (err)
6708c2ecf20Sopenharmony_ci		goto fail3;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	serio_write(lk->serio, LK_CMD_POWERCYCLE_RESET);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	return 0;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci fail3:	serio_close(serio);
6778c2ecf20Sopenharmony_ci fail2:	serio_set_drvdata(serio, NULL);
6788c2ecf20Sopenharmony_ci fail1:	input_free_device(input_dev);
6798c2ecf20Sopenharmony_ci	kfree(lk);
6808c2ecf20Sopenharmony_ci	return err;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci/*
6848c2ecf20Sopenharmony_ci * lkkbd_disconnect() unregisters and closes behind us.
6858c2ecf20Sopenharmony_ci */
6868c2ecf20Sopenharmony_cistatic void lkkbd_disconnect(struct serio *serio)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	struct lkkbd *lk = serio_get_drvdata(serio);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	input_get_device(lk->dev);
6918c2ecf20Sopenharmony_ci	input_unregister_device(lk->dev);
6928c2ecf20Sopenharmony_ci	serio_close(serio);
6938c2ecf20Sopenharmony_ci	serio_set_drvdata(serio, NULL);
6948c2ecf20Sopenharmony_ci	input_put_device(lk->dev);
6958c2ecf20Sopenharmony_ci	kfree(lk);
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic const struct serio_device_id lkkbd_serio_ids[] = {
6998c2ecf20Sopenharmony_ci	{
7008c2ecf20Sopenharmony_ci		.type	= SERIO_RS232,
7018c2ecf20Sopenharmony_ci		.proto	= SERIO_LKKBD,
7028c2ecf20Sopenharmony_ci		.id	= SERIO_ANY,
7038c2ecf20Sopenharmony_ci		.extra	= SERIO_ANY,
7048c2ecf20Sopenharmony_ci	},
7058c2ecf20Sopenharmony_ci	{ 0 }
7068c2ecf20Sopenharmony_ci};
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(serio, lkkbd_serio_ids);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_cistatic struct serio_driver lkkbd_drv = {
7118c2ecf20Sopenharmony_ci	.driver		= {
7128c2ecf20Sopenharmony_ci		.name	= "lkkbd",
7138c2ecf20Sopenharmony_ci	},
7148c2ecf20Sopenharmony_ci	.description	= DRIVER_DESC,
7158c2ecf20Sopenharmony_ci	.id_table	= lkkbd_serio_ids,
7168c2ecf20Sopenharmony_ci	.connect	= lkkbd_connect,
7178c2ecf20Sopenharmony_ci	.disconnect	= lkkbd_disconnect,
7188c2ecf20Sopenharmony_ci	.interrupt	= lkkbd_interrupt,
7198c2ecf20Sopenharmony_ci};
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_cimodule_serio_driver(lkkbd_drv);
722