xref: /openbmc/linux/drivers/input/keyboard/sunkbd.c (revision 2d09ac95)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  Copyright (c) 1999-2001 Vojtech Pavlik
41da177e4SLinus Torvalds  */
51da177e4SLinus Torvalds 
61da177e4SLinus Torvalds /*
71da177e4SLinus Torvalds  * Sun keyboard driver for Linux
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds #include <linux/delay.h>
11d43c36dcSAlexey Dobriyan #include <linux/sched.h>
121da177e4SLinus Torvalds #include <linux/slab.h>
131da177e4SLinus Torvalds #include <linux/module.h>
141da177e4SLinus Torvalds #include <linux/interrupt.h>
151da177e4SLinus Torvalds #include <linux/input.h>
161da177e4SLinus Torvalds #include <linux/serio.h>
171da177e4SLinus Torvalds #include <linux/workqueue.h>
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds #define DRIVER_DESC	"Sun keyboard driver"
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
221da177e4SLinus Torvalds MODULE_DESCRIPTION(DRIVER_DESC);
231da177e4SLinus Torvalds MODULE_LICENSE("GPL");
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds static unsigned char sunkbd_keycode[128] = {
268d9a9ae3SVojtech Pavlik 	  0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112,
271da177e4SLinus Torvalds 	 65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106,  1,  2,  3,
281da177e4SLinus Torvalds 	  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
291da177e4SLinus Torvalds 	116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
301da177e4SLinus Torvalds 	 26, 27,111,127, 71, 72, 73, 74,134,135,107,  0, 29, 30, 31, 32,
311da177e4SLinus Torvalds 	 33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
321da177e4SLinus Torvalds 	104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
331da177e4SLinus Torvalds 	 79, 80, 81,  0,  0,  0,138, 58,125, 57,126,109, 86, 78
341da177e4SLinus Torvalds };
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds #define SUNKBD_CMD_RESET	0x1
371da177e4SLinus Torvalds #define SUNKBD_CMD_BELLON	0x2
381da177e4SLinus Torvalds #define SUNKBD_CMD_BELLOFF	0x3
391da177e4SLinus Torvalds #define SUNKBD_CMD_CLICK	0xa
401da177e4SLinus Torvalds #define SUNKBD_CMD_NOCLICK	0xb
411da177e4SLinus Torvalds #define SUNKBD_CMD_SETLED	0xe
421da177e4SLinus Torvalds #define SUNKBD_CMD_LAYOUT	0xf
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds #define SUNKBD_RET_RESET	0xff
451da177e4SLinus Torvalds #define SUNKBD_RET_ALLUP	0x7f
461da177e4SLinus Torvalds #define SUNKBD_RET_LAYOUT	0xfe
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds #define SUNKBD_LAYOUT_5_MASK	0x20
491da177e4SLinus Torvalds #define SUNKBD_RELEASE		0x80
501da177e4SLinus Torvalds #define SUNKBD_KEY		0x7f
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds /*
531da177e4SLinus Torvalds  * Per-keyboard data.
541da177e4SLinus Torvalds  */
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds struct sunkbd {
577cac9cd9SDmitry Torokhov 	unsigned char keycode[ARRAY_SIZE(sunkbd_keycode)];
583c42f0c3SDmitry Torokhov 	struct input_dev *dev;
591da177e4SLinus Torvalds 	struct serio *serio;
601da177e4SLinus Torvalds 	struct work_struct tq;
611da177e4SLinus Torvalds 	wait_queue_head_t wait;
621da177e4SLinus Torvalds 	char name[64];
631da177e4SLinus Torvalds 	char phys[32];
641da177e4SLinus Torvalds 	char type;
657cac9cd9SDmitry Torokhov 	bool enabled;
661da177e4SLinus Torvalds 	volatile s8 reset;
671da177e4SLinus Torvalds 	volatile s8 layout;
681da177e4SLinus Torvalds };
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds /*
711da177e4SLinus Torvalds  * sunkbd_interrupt() is called by the low level driver when a character
721da177e4SLinus Torvalds  * is received.
731da177e4SLinus Torvalds  */
741da177e4SLinus Torvalds 
sunkbd_interrupt(struct serio * serio,unsigned char data,unsigned int flags)751da177e4SLinus Torvalds static irqreturn_t sunkbd_interrupt(struct serio *serio,
767d12e780SDavid Howells 		unsigned char data, unsigned int flags)
771da177e4SLinus Torvalds {
781da177e4SLinus Torvalds 	struct sunkbd *sunkbd = serio_get_drvdata(serio);
791da177e4SLinus Torvalds 
807cac9cd9SDmitry Torokhov 	if (sunkbd->reset <= -1) {
817cac9cd9SDmitry Torokhov 		/*
827cac9cd9SDmitry Torokhov 		 * If cp[i] is 0xff, sunkbd->reset will stay -1.
837cac9cd9SDmitry Torokhov 		 * The keyboard sends 0xff 0xff 0xID on powerup.
847cac9cd9SDmitry Torokhov 		 */
857cac9cd9SDmitry Torokhov 		sunkbd->reset = data;
861da177e4SLinus Torvalds 		wake_up_interruptible(&sunkbd->wait);
871da177e4SLinus Torvalds 		goto out;
881da177e4SLinus Torvalds 	}
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds 	if (sunkbd->layout == -1) {
911da177e4SLinus Torvalds 		sunkbd->layout = data;
921da177e4SLinus Torvalds 		wake_up_interruptible(&sunkbd->wait);
931da177e4SLinus Torvalds 		goto out;
941da177e4SLinus Torvalds 	}
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds 	switch (data) {
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 	case SUNKBD_RET_RESET:
9977e70d35SDmitry Torokhov 		if (sunkbd->enabled)
1001da177e4SLinus Torvalds 			schedule_work(&sunkbd->tq);
1011da177e4SLinus Torvalds 		sunkbd->reset = -1;
1021da177e4SLinus Torvalds 		break;
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds 	case SUNKBD_RET_LAYOUT:
1051da177e4SLinus Torvalds 		sunkbd->layout = -1;
1061da177e4SLinus Torvalds 		break;
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds 	case SUNKBD_RET_ALLUP: /* All keys released */
1091da177e4SLinus Torvalds 		break;
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds 	default:
1123c42f0c3SDmitry Torokhov 		if (!sunkbd->enabled)
1133c42f0c3SDmitry Torokhov 			break;
1143c42f0c3SDmitry Torokhov 
1151da177e4SLinus Torvalds 		if (sunkbd->keycode[data & SUNKBD_KEY]) {
1167cac9cd9SDmitry Torokhov 			input_report_key(sunkbd->dev,
1177cac9cd9SDmitry Torokhov 					 sunkbd->keycode[data & SUNKBD_KEY],
1187cac9cd9SDmitry Torokhov 					 !(data & SUNKBD_RELEASE));
1193c42f0c3SDmitry Torokhov 			input_sync(sunkbd->dev);
1201da177e4SLinus Torvalds 		} else {
1217cac9cd9SDmitry Torokhov 			printk(KERN_WARNING
1227cac9cd9SDmitry Torokhov 				"sunkbd.c: Unknown key (scancode %#x) %s.\n",
1237cac9cd9SDmitry Torokhov 				data & SUNKBD_KEY,
1247cac9cd9SDmitry Torokhov 				data & SUNKBD_RELEASE ? "released" : "pressed");
1251da177e4SLinus Torvalds 		}
1261da177e4SLinus Torvalds 	}
1271da177e4SLinus Torvalds out:
1281da177e4SLinus Torvalds 	return IRQ_HANDLED;
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds /*
1321da177e4SLinus Torvalds  * sunkbd_event() handles events from the input module.
1331da177e4SLinus Torvalds  */
1341da177e4SLinus Torvalds 
sunkbd_event(struct input_dev * dev,unsigned int type,unsigned int code,int value)1357cac9cd9SDmitry Torokhov static int sunkbd_event(struct input_dev *dev,
1367cac9cd9SDmitry Torokhov 			unsigned int type, unsigned int code, int value)
1371da177e4SLinus Torvalds {
138b356872fSDmitry Torokhov 	struct sunkbd *sunkbd = input_get_drvdata(dev);
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds 	switch (type) {
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 	case EV_LED:
1431da177e4SLinus Torvalds 
144dd0d5443SDmitry Torokhov 		serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
145dd0d5443SDmitry Torokhov 		serio_write(sunkbd->serio,
1467cac9cd9SDmitry Torokhov 			(!!test_bit(LED_CAPSL,   dev->led) << 3) |
1477cac9cd9SDmitry Torokhov 			(!!test_bit(LED_SCROLLL, dev->led) << 2) |
1487cac9cd9SDmitry Torokhov 			(!!test_bit(LED_COMPOSE, dev->led) << 1) |
1497cac9cd9SDmitry Torokhov 			 !!test_bit(LED_NUML,    dev->led));
1501da177e4SLinus Torvalds 		return 0;
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	case EV_SND:
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 		switch (code) {
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds 		case SND_CLICK:
157dd0d5443SDmitry Torokhov 			serio_write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
1581da177e4SLinus Torvalds 			return 0;
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds 		case SND_BELL:
161dd0d5443SDmitry Torokhov 			serio_write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
1621da177e4SLinus Torvalds 			return 0;
1631da177e4SLinus Torvalds 		}
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 		break;
1661da177e4SLinus Torvalds 	}
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds 	return -1;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds /*
1721da177e4SLinus Torvalds  * sunkbd_initialize() checks for a Sun keyboard attached, and determines
1731da177e4SLinus Torvalds  * its type.
1741da177e4SLinus Torvalds  */
1751da177e4SLinus Torvalds 
sunkbd_initialize(struct sunkbd * sunkbd)1761da177e4SLinus Torvalds static int sunkbd_initialize(struct sunkbd *sunkbd)
1771da177e4SLinus Torvalds {
1781da177e4SLinus Torvalds 	sunkbd->reset = -2;
179dd0d5443SDmitry Torokhov 	serio_write(sunkbd->serio, SUNKBD_CMD_RESET);
1801da177e4SLinus Torvalds 	wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
1811da177e4SLinus Torvalds 	if (sunkbd->reset < 0)
1821da177e4SLinus Torvalds 		return -1;
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds 	sunkbd->type = sunkbd->reset;
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	if (sunkbd->type == 4) {	/* Type 4 keyboard */
1871da177e4SLinus Torvalds 		sunkbd->layout = -2;
188dd0d5443SDmitry Torokhov 		serio_write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
1897cac9cd9SDmitry Torokhov 		wait_event_interruptible_timeout(sunkbd->wait,
1907cac9cd9SDmitry Torokhov 						 sunkbd->layout >= 0, HZ / 4);
1917cac9cd9SDmitry Torokhov 		if (sunkbd->layout < 0)
1927cac9cd9SDmitry Torokhov 			return -1;
1937cac9cd9SDmitry Torokhov 		if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK)
1947cac9cd9SDmitry Torokhov 			sunkbd->type = 5;
1951da177e4SLinus Torvalds 	}
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds 	return 0;
1981da177e4SLinus Torvalds }
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds /*
20177e70d35SDmitry Torokhov  * sunkbd_set_leds_beeps() sets leds and beeps to a state the computer remembers
20277e70d35SDmitry Torokhov  * they were in.
2031da177e4SLinus Torvalds  */
2041da177e4SLinus Torvalds 
sunkbd_set_leds_beeps(struct sunkbd * sunkbd)20577e70d35SDmitry Torokhov static void sunkbd_set_leds_beeps(struct sunkbd *sunkbd)
2061da177e4SLinus Torvalds {
207dd0d5443SDmitry Torokhov 	serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
208dd0d5443SDmitry Torokhov 	serio_write(sunkbd->serio,
209dd0d5443SDmitry Torokhov 		(!!test_bit(LED_CAPSL,   sunkbd->dev->led) << 3) |
210dd0d5443SDmitry Torokhov 		(!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
211dd0d5443SDmitry Torokhov 		(!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) |
212dd0d5443SDmitry Torokhov 		 !!test_bit(LED_NUML,    sunkbd->dev->led));
2137cac9cd9SDmitry Torokhov 	serio_write(sunkbd->serio,
2147cac9cd9SDmitry Torokhov 		SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
2157cac9cd9SDmitry Torokhov 	serio_write(sunkbd->serio,
2167cac9cd9SDmitry Torokhov 		SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
2173c42f0c3SDmitry Torokhov }
2183c42f0c3SDmitry Torokhov 
21977e70d35SDmitry Torokhov 
22077e70d35SDmitry Torokhov /*
22177e70d35SDmitry Torokhov  * sunkbd_reinit() wait for the keyboard reset to complete and restores state
22277e70d35SDmitry Torokhov  * of leds and beeps.
22377e70d35SDmitry Torokhov  */
22477e70d35SDmitry Torokhov 
sunkbd_reinit(struct work_struct * work)22577e70d35SDmitry Torokhov static void sunkbd_reinit(struct work_struct *work)
22677e70d35SDmitry Torokhov {
22777e70d35SDmitry Torokhov 	struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
22877e70d35SDmitry Torokhov 
22977e70d35SDmitry Torokhov 	/*
23077e70d35SDmitry Torokhov 	 * It is OK that we check sunkbd->enabled without pausing serio,
23177e70d35SDmitry Torokhov 	 * as we only want to catch true->false transition that will
23277e70d35SDmitry Torokhov 	 * happen once and we will be woken up for it.
23377e70d35SDmitry Torokhov 	 */
23477e70d35SDmitry Torokhov 	wait_event_interruptible_timeout(sunkbd->wait,
23577e70d35SDmitry Torokhov 					 sunkbd->reset >= 0 || !sunkbd->enabled,
23677e70d35SDmitry Torokhov 					 HZ);
23777e70d35SDmitry Torokhov 
23877e70d35SDmitry Torokhov 	if (sunkbd->reset >= 0 && sunkbd->enabled)
23977e70d35SDmitry Torokhov 		sunkbd_set_leds_beeps(sunkbd);
24077e70d35SDmitry Torokhov }
24177e70d35SDmitry Torokhov 
sunkbd_enable(struct sunkbd * sunkbd,bool enable)2427cac9cd9SDmitry Torokhov static void sunkbd_enable(struct sunkbd *sunkbd, bool enable)
2433c42f0c3SDmitry Torokhov {
2443c42f0c3SDmitry Torokhov 	serio_pause_rx(sunkbd->serio);
2459bc83dcfSFabrice Knevez 	sunkbd->enabled = enable;
2463c42f0c3SDmitry Torokhov 	serio_continue_rx(sunkbd->serio);
24777e70d35SDmitry Torokhov 
24877e70d35SDmitry Torokhov 	if (!enable) {
24977e70d35SDmitry Torokhov 		wake_up_interruptible(&sunkbd->wait);
25077e70d35SDmitry Torokhov 		cancel_work_sync(&sunkbd->tq);
25177e70d35SDmitry Torokhov 	}
2521da177e4SLinus Torvalds }
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds /*
2557cac9cd9SDmitry Torokhov  * sunkbd_connect() probes for a Sun keyboard and fills the necessary
2567cac9cd9SDmitry Torokhov  * structures.
2571da177e4SLinus Torvalds  */
2581da177e4SLinus Torvalds 
sunkbd_connect(struct serio * serio,struct serio_driver * drv)2591da177e4SLinus Torvalds static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
2601da177e4SLinus Torvalds {
2611da177e4SLinus Torvalds 	struct sunkbd *sunkbd;
2623c42f0c3SDmitry Torokhov 	struct input_dev *input_dev;
2633c42f0c3SDmitry Torokhov 	int err = -ENOMEM;
2641da177e4SLinus Torvalds 	int i;
2651da177e4SLinus Torvalds 
2663c42f0c3SDmitry Torokhov 	sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
2673c42f0c3SDmitry Torokhov 	input_dev = input_allocate_device();
2683c42f0c3SDmitry Torokhov 	if (!sunkbd || !input_dev)
2692b03b60eSDmitry Torokhov 		goto fail1;
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	sunkbd->serio = serio;
2723c42f0c3SDmitry Torokhov 	sunkbd->dev = input_dev;
2733c42f0c3SDmitry Torokhov 	init_waitqueue_head(&sunkbd->wait);
274c4028958SDavid Howells 	INIT_WORK(&sunkbd->tq, sunkbd_reinit);
2753c42f0c3SDmitry Torokhov 	snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds 	serio_set_drvdata(serio, sunkbd);
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds 	err = serio_open(serio, drv);
2803c42f0c3SDmitry Torokhov 	if (err)
2812b03b60eSDmitry Torokhov 		goto fail2;
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds 	if (sunkbd_initialize(sunkbd) < 0) {
2842b03b60eSDmitry Torokhov 		err = -ENODEV;
2852b03b60eSDmitry Torokhov 		goto fail3;
2861da177e4SLinus Torvalds 	}
2871da177e4SLinus Torvalds 
2887cac9cd9SDmitry Torokhov 	snprintf(sunkbd->name, sizeof(sunkbd->name),
2897cac9cd9SDmitry Torokhov 		 "Sun Type %d keyboard", sunkbd->type);
2901da177e4SLinus Torvalds 	memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
2913c42f0c3SDmitry Torokhov 
2923c42f0c3SDmitry Torokhov 	input_dev->name = sunkbd->name;
2933c42f0c3SDmitry Torokhov 	input_dev->phys = sunkbd->phys;
2943c42f0c3SDmitry Torokhov 	input_dev->id.bustype = BUS_RS232;
2953c42f0c3SDmitry Torokhov 	input_dev->id.vendor  = SERIO_SUNKBD;
2963c42f0c3SDmitry Torokhov 	input_dev->id.product = sunkbd->type;
2973c42f0c3SDmitry Torokhov 	input_dev->id.version = 0x0100;
298469ba4dfSDmitry Torokhov 	input_dev->dev.parent = &serio->dev;
299b356872fSDmitry Torokhov 
300b356872fSDmitry Torokhov 	input_set_drvdata(input_dev, sunkbd);
301b356872fSDmitry Torokhov 
3023c42f0c3SDmitry Torokhov 	input_dev->event = sunkbd_event;
3033c42f0c3SDmitry Torokhov 
3047b19ada2SJiri Slaby 	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
3057b19ada2SJiri Slaby 		BIT_MASK(EV_SND) | BIT_MASK(EV_REP);
3067b19ada2SJiri Slaby 	input_dev->ledbit[0] = BIT_MASK(LED_CAPSL) | BIT_MASK(LED_COMPOSE) |
3077b19ada2SJiri Slaby 		BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_NUML);
3087b19ada2SJiri Slaby 	input_dev->sndbit[0] = BIT_MASK(SND_CLICK) | BIT_MASK(SND_BELL);
3093c42f0c3SDmitry Torokhov 
3103c42f0c3SDmitry Torokhov 	input_dev->keycode = sunkbd->keycode;
3113c42f0c3SDmitry Torokhov 	input_dev->keycodesize = sizeof(unsigned char);
3123c42f0c3SDmitry Torokhov 	input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
3137cac9cd9SDmitry Torokhov 	for (i = 0; i < ARRAY_SIZE(sunkbd_keycode); i++)
3147cac9cd9SDmitry Torokhov 		__set_bit(sunkbd->keycode[i], input_dev->keybit);
3157cac9cd9SDmitry Torokhov 	__clear_bit(KEY_RESERVED, input_dev->keybit);
3161da177e4SLinus Torvalds 
3177cac9cd9SDmitry Torokhov 	sunkbd_enable(sunkbd, true);
3182b03b60eSDmitry Torokhov 
3192b03b60eSDmitry Torokhov 	err = input_register_device(sunkbd->dev);
3202b03b60eSDmitry Torokhov 	if (err)
3212b03b60eSDmitry Torokhov 		goto fail4;
3222b03b60eSDmitry Torokhov 
3231da177e4SLinus Torvalds 	return 0;
3243c42f0c3SDmitry Torokhov 
3257cac9cd9SDmitry Torokhov  fail4:	sunkbd_enable(sunkbd, false);
3262b03b60eSDmitry Torokhov  fail3:	serio_close(serio);
3272b03b60eSDmitry Torokhov  fail2:	serio_set_drvdata(serio, NULL);
3282b03b60eSDmitry Torokhov  fail1:	input_free_device(input_dev);
3293c42f0c3SDmitry Torokhov 	kfree(sunkbd);
3303c42f0c3SDmitry Torokhov 	return err;
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds /*
3341da177e4SLinus Torvalds  * sunkbd_disconnect() unregisters and closes behind us.
3351da177e4SLinus Torvalds  */
3361da177e4SLinus Torvalds 
sunkbd_disconnect(struct serio * serio)3371da177e4SLinus Torvalds static void sunkbd_disconnect(struct serio *serio)
3381da177e4SLinus Torvalds {
3391da177e4SLinus Torvalds 	struct sunkbd *sunkbd = serio_get_drvdata(serio);
3403c42f0c3SDmitry Torokhov 
3417cac9cd9SDmitry Torokhov 	sunkbd_enable(sunkbd, false);
3423c42f0c3SDmitry Torokhov 	input_unregister_device(sunkbd->dev);
3431da177e4SLinus Torvalds 	serio_close(serio);
3441da177e4SLinus Torvalds 	serio_set_drvdata(serio, NULL);
3451da177e4SLinus Torvalds 	kfree(sunkbd);
3461da177e4SLinus Torvalds }
3471da177e4SLinus Torvalds 
3481966e005SArvind Yadav static const struct serio_device_id sunkbd_serio_ids[] = {
3491da177e4SLinus Torvalds 	{
3501da177e4SLinus Torvalds 		.type	= SERIO_RS232,
3511da177e4SLinus Torvalds 		.proto	= SERIO_SUNKBD,
3521da177e4SLinus Torvalds 		.id	= SERIO_ANY,
3531da177e4SLinus Torvalds 		.extra	= SERIO_ANY,
3541da177e4SLinus Torvalds 	},
3551da177e4SLinus Torvalds 	{
3561da177e4SLinus Torvalds 		.type	= SERIO_RS232,
3571da177e4SLinus Torvalds 		.proto	= SERIO_UNKNOWN, /* sunkbd does probe */
3581da177e4SLinus Torvalds 		.id	= SERIO_ANY,
3591da177e4SLinus Torvalds 		.extra	= SERIO_ANY,
3601da177e4SLinus Torvalds 	},
3611da177e4SLinus Torvalds 	{ 0 }
3621da177e4SLinus Torvalds };
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
3651da177e4SLinus Torvalds 
3661da177e4SLinus Torvalds static struct serio_driver sunkbd_drv = {
3671da177e4SLinus Torvalds 	.driver		= {
3681da177e4SLinus Torvalds 		.name	= "sunkbd",
3691da177e4SLinus Torvalds 	},
3701da177e4SLinus Torvalds 	.description	= DRIVER_DESC,
3711da177e4SLinus Torvalds 	.id_table	= sunkbd_serio_ids,
3721da177e4SLinus Torvalds 	.interrupt	= sunkbd_interrupt,
3731da177e4SLinus Torvalds 	.connect	= sunkbd_connect,
3741da177e4SLinus Torvalds 	.disconnect	= sunkbd_disconnect,
3751da177e4SLinus Torvalds };
3761da177e4SLinus Torvalds 
37765ac9f7aSAxel Lin module_serio_driver(sunkbd_drv);
378