xref: /openbmc/linux/drivers/input/keyboard/sunkbd.c (revision 31e67366)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  Copyright (c) 1999-2001 Vojtech Pavlik
4  */
5 
6 /*
7  * Sun keyboard driver for Linux
8  */
9 
10 /*
11  */
12 
13 #include <linux/delay.h>
14 #include <linux/sched.h>
15 #include <linux/slab.h>
16 #include <linux/module.h>
17 #include <linux/interrupt.h>
18 #include <linux/input.h>
19 #include <linux/serio.h>
20 #include <linux/workqueue.h>
21 
22 #define DRIVER_DESC	"Sun keyboard driver"
23 
24 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
25 MODULE_DESCRIPTION(DRIVER_DESC);
26 MODULE_LICENSE("GPL");
27 
28 static unsigned char sunkbd_keycode[128] = {
29 	  0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112,
30 	 65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106,  1,  2,  3,
31 	  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
32 	116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
33 	 26, 27,111,127, 71, 72, 73, 74,134,135,107,  0, 29, 30, 31, 32,
34 	 33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
35 	104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
36 	 79, 80, 81,  0,  0,  0,138, 58,125, 57,126,109, 86, 78
37 };
38 
39 #define SUNKBD_CMD_RESET	0x1
40 #define SUNKBD_CMD_BELLON	0x2
41 #define SUNKBD_CMD_BELLOFF	0x3
42 #define SUNKBD_CMD_CLICK	0xa
43 #define SUNKBD_CMD_NOCLICK	0xb
44 #define SUNKBD_CMD_SETLED	0xe
45 #define SUNKBD_CMD_LAYOUT	0xf
46 
47 #define SUNKBD_RET_RESET	0xff
48 #define SUNKBD_RET_ALLUP	0x7f
49 #define SUNKBD_RET_LAYOUT	0xfe
50 
51 #define SUNKBD_LAYOUT_5_MASK	0x20
52 #define SUNKBD_RELEASE		0x80
53 #define SUNKBD_KEY		0x7f
54 
55 /*
56  * Per-keyboard data.
57  */
58 
59 struct sunkbd {
60 	unsigned char keycode[ARRAY_SIZE(sunkbd_keycode)];
61 	struct input_dev *dev;
62 	struct serio *serio;
63 	struct work_struct tq;
64 	wait_queue_head_t wait;
65 	char name[64];
66 	char phys[32];
67 	char type;
68 	bool enabled;
69 	volatile s8 reset;
70 	volatile s8 layout;
71 };
72 
73 /*
74  * sunkbd_interrupt() is called by the low level driver when a character
75  * is received.
76  */
77 
78 static irqreturn_t sunkbd_interrupt(struct serio *serio,
79 		unsigned char data, unsigned int flags)
80 {
81 	struct sunkbd *sunkbd = serio_get_drvdata(serio);
82 
83 	if (sunkbd->reset <= -1) {
84 		/*
85 		 * If cp[i] is 0xff, sunkbd->reset will stay -1.
86 		 * The keyboard sends 0xff 0xff 0xID on powerup.
87 		 */
88 		sunkbd->reset = data;
89 		wake_up_interruptible(&sunkbd->wait);
90 		goto out;
91 	}
92 
93 	if (sunkbd->layout == -1) {
94 		sunkbd->layout = data;
95 		wake_up_interruptible(&sunkbd->wait);
96 		goto out;
97 	}
98 
99 	switch (data) {
100 
101 	case SUNKBD_RET_RESET:
102 		if (sunkbd->enabled)
103 			schedule_work(&sunkbd->tq);
104 		sunkbd->reset = -1;
105 		break;
106 
107 	case SUNKBD_RET_LAYOUT:
108 		sunkbd->layout = -1;
109 		break;
110 
111 	case SUNKBD_RET_ALLUP: /* All keys released */
112 		break;
113 
114 	default:
115 		if (!sunkbd->enabled)
116 			break;
117 
118 		if (sunkbd->keycode[data & SUNKBD_KEY]) {
119 			input_report_key(sunkbd->dev,
120 					 sunkbd->keycode[data & SUNKBD_KEY],
121 					 !(data & SUNKBD_RELEASE));
122 			input_sync(sunkbd->dev);
123 		} else {
124 			printk(KERN_WARNING
125 				"sunkbd.c: Unknown key (scancode %#x) %s.\n",
126 				data & SUNKBD_KEY,
127 				data & SUNKBD_RELEASE ? "released" : "pressed");
128 		}
129 	}
130 out:
131 	return IRQ_HANDLED;
132 }
133 
134 /*
135  * sunkbd_event() handles events from the input module.
136  */
137 
138 static int sunkbd_event(struct input_dev *dev,
139 			unsigned int type, unsigned int code, int value)
140 {
141 	struct sunkbd *sunkbd = input_get_drvdata(dev);
142 
143 	switch (type) {
144 
145 	case EV_LED:
146 
147 		serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
148 		serio_write(sunkbd->serio,
149 			(!!test_bit(LED_CAPSL,   dev->led) << 3) |
150 			(!!test_bit(LED_SCROLLL, dev->led) << 2) |
151 			(!!test_bit(LED_COMPOSE, dev->led) << 1) |
152 			 !!test_bit(LED_NUML,    dev->led));
153 		return 0;
154 
155 	case EV_SND:
156 
157 		switch (code) {
158 
159 		case SND_CLICK:
160 			serio_write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
161 			return 0;
162 
163 		case SND_BELL:
164 			serio_write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
165 			return 0;
166 		}
167 
168 		break;
169 	}
170 
171 	return -1;
172 }
173 
174 /*
175  * sunkbd_initialize() checks for a Sun keyboard attached, and determines
176  * its type.
177  */
178 
179 static int sunkbd_initialize(struct sunkbd *sunkbd)
180 {
181 	sunkbd->reset = -2;
182 	serio_write(sunkbd->serio, SUNKBD_CMD_RESET);
183 	wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
184 	if (sunkbd->reset < 0)
185 		return -1;
186 
187 	sunkbd->type = sunkbd->reset;
188 
189 	if (sunkbd->type == 4) {	/* Type 4 keyboard */
190 		sunkbd->layout = -2;
191 		serio_write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
192 		wait_event_interruptible_timeout(sunkbd->wait,
193 						 sunkbd->layout >= 0, HZ / 4);
194 		if (sunkbd->layout < 0)
195 			return -1;
196 		if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK)
197 			sunkbd->type = 5;
198 	}
199 
200 	return 0;
201 }
202 
203 /*
204  * sunkbd_set_leds_beeps() sets leds and beeps to a state the computer remembers
205  * they were in.
206  */
207 
208 static void sunkbd_set_leds_beeps(struct sunkbd *sunkbd)
209 {
210 	serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
211 	serio_write(sunkbd->serio,
212 		(!!test_bit(LED_CAPSL,   sunkbd->dev->led) << 3) |
213 		(!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
214 		(!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) |
215 		 !!test_bit(LED_NUML,    sunkbd->dev->led));
216 	serio_write(sunkbd->serio,
217 		SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
218 	serio_write(sunkbd->serio,
219 		SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
220 }
221 
222 
223 /*
224  * sunkbd_reinit() wait for the keyboard reset to complete and restores state
225  * of leds and beeps.
226  */
227 
228 static void sunkbd_reinit(struct work_struct *work)
229 {
230 	struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
231 
232 	/*
233 	 * It is OK that we check sunkbd->enabled without pausing serio,
234 	 * as we only want to catch true->false transition that will
235 	 * happen once and we will be woken up for it.
236 	 */
237 	wait_event_interruptible_timeout(sunkbd->wait,
238 					 sunkbd->reset >= 0 || !sunkbd->enabled,
239 					 HZ);
240 
241 	if (sunkbd->reset >= 0 && sunkbd->enabled)
242 		sunkbd_set_leds_beeps(sunkbd);
243 }
244 
245 static void sunkbd_enable(struct sunkbd *sunkbd, bool enable)
246 {
247 	serio_pause_rx(sunkbd->serio);
248 	sunkbd->enabled = enable;
249 	serio_continue_rx(sunkbd->serio);
250 
251 	if (!enable) {
252 		wake_up_interruptible(&sunkbd->wait);
253 		cancel_work_sync(&sunkbd->tq);
254 	}
255 }
256 
257 /*
258  * sunkbd_connect() probes for a Sun keyboard and fills the necessary
259  * structures.
260  */
261 
262 static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
263 {
264 	struct sunkbd *sunkbd;
265 	struct input_dev *input_dev;
266 	int err = -ENOMEM;
267 	int i;
268 
269 	sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
270 	input_dev = input_allocate_device();
271 	if (!sunkbd || !input_dev)
272 		goto fail1;
273 
274 	sunkbd->serio = serio;
275 	sunkbd->dev = input_dev;
276 	init_waitqueue_head(&sunkbd->wait);
277 	INIT_WORK(&sunkbd->tq, sunkbd_reinit);
278 	snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
279 
280 	serio_set_drvdata(serio, sunkbd);
281 
282 	err = serio_open(serio, drv);
283 	if (err)
284 		goto fail2;
285 
286 	if (sunkbd_initialize(sunkbd) < 0) {
287 		err = -ENODEV;
288 		goto fail3;
289 	}
290 
291 	snprintf(sunkbd->name, sizeof(sunkbd->name),
292 		 "Sun Type %d keyboard", sunkbd->type);
293 	memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
294 
295 	input_dev->name = sunkbd->name;
296 	input_dev->phys = sunkbd->phys;
297 	input_dev->id.bustype = BUS_RS232;
298 	input_dev->id.vendor  = SERIO_SUNKBD;
299 	input_dev->id.product = sunkbd->type;
300 	input_dev->id.version = 0x0100;
301 	input_dev->dev.parent = &serio->dev;
302 
303 	input_set_drvdata(input_dev, sunkbd);
304 
305 	input_dev->event = sunkbd_event;
306 
307 	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
308 		BIT_MASK(EV_SND) | BIT_MASK(EV_REP);
309 	input_dev->ledbit[0] = BIT_MASK(LED_CAPSL) | BIT_MASK(LED_COMPOSE) |
310 		BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_NUML);
311 	input_dev->sndbit[0] = BIT_MASK(SND_CLICK) | BIT_MASK(SND_BELL);
312 
313 	input_dev->keycode = sunkbd->keycode;
314 	input_dev->keycodesize = sizeof(unsigned char);
315 	input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
316 	for (i = 0; i < ARRAY_SIZE(sunkbd_keycode); i++)
317 		__set_bit(sunkbd->keycode[i], input_dev->keybit);
318 	__clear_bit(KEY_RESERVED, input_dev->keybit);
319 
320 	sunkbd_enable(sunkbd, true);
321 
322 	err = input_register_device(sunkbd->dev);
323 	if (err)
324 		goto fail4;
325 
326 	return 0;
327 
328  fail4:	sunkbd_enable(sunkbd, false);
329  fail3:	serio_close(serio);
330  fail2:	serio_set_drvdata(serio, NULL);
331  fail1:	input_free_device(input_dev);
332 	kfree(sunkbd);
333 	return err;
334 }
335 
336 /*
337  * sunkbd_disconnect() unregisters and closes behind us.
338  */
339 
340 static void sunkbd_disconnect(struct serio *serio)
341 {
342 	struct sunkbd *sunkbd = serio_get_drvdata(serio);
343 
344 	sunkbd_enable(sunkbd, false);
345 	input_unregister_device(sunkbd->dev);
346 	serio_close(serio);
347 	serio_set_drvdata(serio, NULL);
348 	kfree(sunkbd);
349 }
350 
351 static const struct serio_device_id sunkbd_serio_ids[] = {
352 	{
353 		.type	= SERIO_RS232,
354 		.proto	= SERIO_SUNKBD,
355 		.id	= SERIO_ANY,
356 		.extra	= SERIO_ANY,
357 	},
358 	{
359 		.type	= SERIO_RS232,
360 		.proto	= SERIO_UNKNOWN, /* sunkbd does probe */
361 		.id	= SERIO_ANY,
362 		.extra	= SERIO_ANY,
363 	},
364 	{ 0 }
365 };
366 
367 MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
368 
369 static struct serio_driver sunkbd_drv = {
370 	.driver		= {
371 		.name	= "sunkbd",
372 	},
373 	.description	= DRIVER_DESC,
374 	.id_table	= sunkbd_serio_ids,
375 	.interrupt	= sunkbd_interrupt,
376 	.connect	= sunkbd_connect,
377 	.disconnect	= sunkbd_disconnect,
378 };
379 
380 module_serio_driver(sunkbd_drv);
381