xref: /linux/drivers/input/keyboard/sunkbd.c (revision 77e70d351db7de07a46ac49b87a6c3c7a60fca7e)
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 /*
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds #include <linux/delay.h>
14d43c36dcSAlexey Dobriyan #include <linux/sched.h>
151da177e4SLinus Torvalds #include <linux/slab.h>
161da177e4SLinus Torvalds #include <linux/module.h>
171da177e4SLinus Torvalds #include <linux/interrupt.h>
181da177e4SLinus Torvalds #include <linux/input.h>
191da177e4SLinus Torvalds #include <linux/serio.h>
201da177e4SLinus Torvalds #include <linux/workqueue.h>
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #define DRIVER_DESC	"Sun keyboard driver"
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
251da177e4SLinus Torvalds MODULE_DESCRIPTION(DRIVER_DESC);
261da177e4SLinus Torvalds MODULE_LICENSE("GPL");
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds static unsigned char sunkbd_keycode[128] = {
298d9a9ae3SVojtech Pavlik 	  0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112,
301da177e4SLinus Torvalds 	 65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106,  1,  2,  3,
311da177e4SLinus Torvalds 	  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
321da177e4SLinus Torvalds 	116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
331da177e4SLinus Torvalds 	 26, 27,111,127, 71, 72, 73, 74,134,135,107,  0, 29, 30, 31, 32,
341da177e4SLinus Torvalds 	 33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
351da177e4SLinus Torvalds 	104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
361da177e4SLinus Torvalds 	 79, 80, 81,  0,  0,  0,138, 58,125, 57,126,109, 86, 78
371da177e4SLinus Torvalds };
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds #define SUNKBD_CMD_RESET	0x1
401da177e4SLinus Torvalds #define SUNKBD_CMD_BELLON	0x2
411da177e4SLinus Torvalds #define SUNKBD_CMD_BELLOFF	0x3
421da177e4SLinus Torvalds #define SUNKBD_CMD_CLICK	0xa
431da177e4SLinus Torvalds #define SUNKBD_CMD_NOCLICK	0xb
441da177e4SLinus Torvalds #define SUNKBD_CMD_SETLED	0xe
451da177e4SLinus Torvalds #define SUNKBD_CMD_LAYOUT	0xf
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds #define SUNKBD_RET_RESET	0xff
481da177e4SLinus Torvalds #define SUNKBD_RET_ALLUP	0x7f
491da177e4SLinus Torvalds #define SUNKBD_RET_LAYOUT	0xfe
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds #define SUNKBD_LAYOUT_5_MASK	0x20
521da177e4SLinus Torvalds #define SUNKBD_RELEASE		0x80
531da177e4SLinus Torvalds #define SUNKBD_KEY		0x7f
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds /*
561da177e4SLinus Torvalds  * Per-keyboard data.
571da177e4SLinus Torvalds  */
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds struct sunkbd {
607cac9cd9SDmitry Torokhov 	unsigned char keycode[ARRAY_SIZE(sunkbd_keycode)];
613c42f0c3SDmitry Torokhov 	struct input_dev *dev;
621da177e4SLinus Torvalds 	struct serio *serio;
631da177e4SLinus Torvalds 	struct work_struct tq;
641da177e4SLinus Torvalds 	wait_queue_head_t wait;
651da177e4SLinus Torvalds 	char name[64];
661da177e4SLinus Torvalds 	char phys[32];
671da177e4SLinus Torvalds 	char type;
687cac9cd9SDmitry Torokhov 	bool enabled;
691da177e4SLinus Torvalds 	volatile s8 reset;
701da177e4SLinus Torvalds 	volatile s8 layout;
711da177e4SLinus Torvalds };
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds /*
741da177e4SLinus Torvalds  * sunkbd_interrupt() is called by the low level driver when a character
751da177e4SLinus Torvalds  * is received.
761da177e4SLinus Torvalds  */
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds static irqreturn_t sunkbd_interrupt(struct serio *serio,
797d12e780SDavid Howells 		unsigned char data, unsigned int flags)
801da177e4SLinus Torvalds {
811da177e4SLinus Torvalds 	struct sunkbd *sunkbd = serio_get_drvdata(serio);
821da177e4SLinus Torvalds 
837cac9cd9SDmitry Torokhov 	if (sunkbd->reset <= -1) {
847cac9cd9SDmitry Torokhov 		/*
857cac9cd9SDmitry Torokhov 		 * If cp[i] is 0xff, sunkbd->reset will stay -1.
867cac9cd9SDmitry Torokhov 		 * The keyboard sends 0xff 0xff 0xID on powerup.
877cac9cd9SDmitry Torokhov 		 */
887cac9cd9SDmitry Torokhov 		sunkbd->reset = data;
891da177e4SLinus Torvalds 		wake_up_interruptible(&sunkbd->wait);
901da177e4SLinus Torvalds 		goto out;
911da177e4SLinus Torvalds 	}
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds 	if (sunkbd->layout == -1) {
941da177e4SLinus Torvalds 		sunkbd->layout = data;
951da177e4SLinus Torvalds 		wake_up_interruptible(&sunkbd->wait);
961da177e4SLinus Torvalds 		goto out;
971da177e4SLinus Torvalds 	}
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds 	switch (data) {
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	case SUNKBD_RET_RESET:
102*77e70d35SDmitry Torokhov 		if (sunkbd->enabled)
1031da177e4SLinus Torvalds 			schedule_work(&sunkbd->tq);
1041da177e4SLinus Torvalds 		sunkbd->reset = -1;
1051da177e4SLinus Torvalds 		break;
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds 	case SUNKBD_RET_LAYOUT:
1081da177e4SLinus Torvalds 		sunkbd->layout = -1;
1091da177e4SLinus Torvalds 		break;
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds 	case SUNKBD_RET_ALLUP: /* All keys released */
1121da177e4SLinus Torvalds 		break;
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	default:
1153c42f0c3SDmitry Torokhov 		if (!sunkbd->enabled)
1163c42f0c3SDmitry Torokhov 			break;
1173c42f0c3SDmitry Torokhov 
1181da177e4SLinus Torvalds 		if (sunkbd->keycode[data & SUNKBD_KEY]) {
1197cac9cd9SDmitry Torokhov 			input_report_key(sunkbd->dev,
1207cac9cd9SDmitry Torokhov 					 sunkbd->keycode[data & SUNKBD_KEY],
1217cac9cd9SDmitry Torokhov 					 !(data & SUNKBD_RELEASE));
1223c42f0c3SDmitry Torokhov 			input_sync(sunkbd->dev);
1231da177e4SLinus Torvalds 		} else {
1247cac9cd9SDmitry Torokhov 			printk(KERN_WARNING
1257cac9cd9SDmitry Torokhov 				"sunkbd.c: Unknown key (scancode %#x) %s.\n",
1267cac9cd9SDmitry Torokhov 				data & SUNKBD_KEY,
1277cac9cd9SDmitry Torokhov 				data & SUNKBD_RELEASE ? "released" : "pressed");
1281da177e4SLinus Torvalds 		}
1291da177e4SLinus Torvalds 	}
1301da177e4SLinus Torvalds out:
1311da177e4SLinus Torvalds 	return IRQ_HANDLED;
1321da177e4SLinus Torvalds }
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds /*
1351da177e4SLinus Torvalds  * sunkbd_event() handles events from the input module.
1361da177e4SLinus Torvalds  */
1371da177e4SLinus Torvalds 
1387cac9cd9SDmitry Torokhov static int sunkbd_event(struct input_dev *dev,
1397cac9cd9SDmitry Torokhov 			unsigned int type, unsigned int code, int value)
1401da177e4SLinus Torvalds {
141b356872fSDmitry Torokhov 	struct sunkbd *sunkbd = input_get_drvdata(dev);
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	switch (type) {
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds 	case EV_LED:
1461da177e4SLinus Torvalds 
147dd0d5443SDmitry Torokhov 		serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
148dd0d5443SDmitry Torokhov 		serio_write(sunkbd->serio,
1497cac9cd9SDmitry Torokhov 			(!!test_bit(LED_CAPSL,   dev->led) << 3) |
1507cac9cd9SDmitry Torokhov 			(!!test_bit(LED_SCROLLL, dev->led) << 2) |
1517cac9cd9SDmitry Torokhov 			(!!test_bit(LED_COMPOSE, dev->led) << 1) |
1527cac9cd9SDmitry Torokhov 			 !!test_bit(LED_NUML,    dev->led));
1531da177e4SLinus Torvalds 		return 0;
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 	case EV_SND:
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 		switch (code) {
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 		case SND_CLICK:
160dd0d5443SDmitry Torokhov 			serio_write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
1611da177e4SLinus Torvalds 			return 0;
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 		case SND_BELL:
164dd0d5443SDmitry Torokhov 			serio_write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
1651da177e4SLinus Torvalds 			return 0;
1661da177e4SLinus Torvalds 		}
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds 		break;
1691da177e4SLinus Torvalds 	}
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds 	return -1;
1721da177e4SLinus Torvalds }
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds /*
1751da177e4SLinus Torvalds  * sunkbd_initialize() checks for a Sun keyboard attached, and determines
1761da177e4SLinus Torvalds  * its type.
1771da177e4SLinus Torvalds  */
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds static int sunkbd_initialize(struct sunkbd *sunkbd)
1801da177e4SLinus Torvalds {
1811da177e4SLinus Torvalds 	sunkbd->reset = -2;
182dd0d5443SDmitry Torokhov 	serio_write(sunkbd->serio, SUNKBD_CMD_RESET);
1831da177e4SLinus Torvalds 	wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
1841da177e4SLinus Torvalds 	if (sunkbd->reset < 0)
1851da177e4SLinus Torvalds 		return -1;
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 	sunkbd->type = sunkbd->reset;
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds 	if (sunkbd->type == 4) {	/* Type 4 keyboard */
1901da177e4SLinus Torvalds 		sunkbd->layout = -2;
191dd0d5443SDmitry Torokhov 		serio_write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
1927cac9cd9SDmitry Torokhov 		wait_event_interruptible_timeout(sunkbd->wait,
1937cac9cd9SDmitry Torokhov 						 sunkbd->layout >= 0, HZ / 4);
1947cac9cd9SDmitry Torokhov 		if (sunkbd->layout < 0)
1957cac9cd9SDmitry Torokhov 			return -1;
1967cac9cd9SDmitry Torokhov 		if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK)
1977cac9cd9SDmitry Torokhov 			sunkbd->type = 5;
1981da177e4SLinus Torvalds 	}
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds 	return 0;
2011da177e4SLinus Torvalds }
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds /*
204*77e70d35SDmitry Torokhov  * sunkbd_set_leds_beeps() sets leds and beeps to a state the computer remembers
205*77e70d35SDmitry Torokhov  * they were in.
2061da177e4SLinus Torvalds  */
2071da177e4SLinus Torvalds 
208*77e70d35SDmitry Torokhov static void sunkbd_set_leds_beeps(struct sunkbd *sunkbd)
2091da177e4SLinus Torvalds {
210dd0d5443SDmitry Torokhov 	serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
211dd0d5443SDmitry Torokhov 	serio_write(sunkbd->serio,
212dd0d5443SDmitry Torokhov 		(!!test_bit(LED_CAPSL,   sunkbd->dev->led) << 3) |
213dd0d5443SDmitry Torokhov 		(!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
214dd0d5443SDmitry Torokhov 		(!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) |
215dd0d5443SDmitry Torokhov 		 !!test_bit(LED_NUML,    sunkbd->dev->led));
2167cac9cd9SDmitry Torokhov 	serio_write(sunkbd->serio,
2177cac9cd9SDmitry Torokhov 		SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
2187cac9cd9SDmitry Torokhov 	serio_write(sunkbd->serio,
2197cac9cd9SDmitry Torokhov 		SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
2203c42f0c3SDmitry Torokhov }
2213c42f0c3SDmitry Torokhov 
222*77e70d35SDmitry Torokhov 
223*77e70d35SDmitry Torokhov /*
224*77e70d35SDmitry Torokhov  * sunkbd_reinit() wait for the keyboard reset to complete and restores state
225*77e70d35SDmitry Torokhov  * of leds and beeps.
226*77e70d35SDmitry Torokhov  */
227*77e70d35SDmitry Torokhov 
228*77e70d35SDmitry Torokhov static void sunkbd_reinit(struct work_struct *work)
229*77e70d35SDmitry Torokhov {
230*77e70d35SDmitry Torokhov 	struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
231*77e70d35SDmitry Torokhov 
232*77e70d35SDmitry Torokhov 	/*
233*77e70d35SDmitry Torokhov 	 * It is OK that we check sunkbd->enabled without pausing serio,
234*77e70d35SDmitry Torokhov 	 * as we only want to catch true->false transition that will
235*77e70d35SDmitry Torokhov 	 * happen once and we will be woken up for it.
236*77e70d35SDmitry Torokhov 	 */
237*77e70d35SDmitry Torokhov 	wait_event_interruptible_timeout(sunkbd->wait,
238*77e70d35SDmitry Torokhov 					 sunkbd->reset >= 0 || !sunkbd->enabled,
239*77e70d35SDmitry Torokhov 					 HZ);
240*77e70d35SDmitry Torokhov 
241*77e70d35SDmitry Torokhov 	if (sunkbd->reset >= 0 && sunkbd->enabled)
242*77e70d35SDmitry Torokhov 		sunkbd_set_leds_beeps(sunkbd);
243*77e70d35SDmitry Torokhov }
244*77e70d35SDmitry Torokhov 
2457cac9cd9SDmitry Torokhov static void sunkbd_enable(struct sunkbd *sunkbd, bool enable)
2463c42f0c3SDmitry Torokhov {
2473c42f0c3SDmitry Torokhov 	serio_pause_rx(sunkbd->serio);
2489bc83dcfSFabrice Knevez 	sunkbd->enabled = enable;
2493c42f0c3SDmitry Torokhov 	serio_continue_rx(sunkbd->serio);
250*77e70d35SDmitry Torokhov 
251*77e70d35SDmitry Torokhov 	if (!enable) {
252*77e70d35SDmitry Torokhov 		wake_up_interruptible(&sunkbd->wait);
253*77e70d35SDmitry Torokhov 		cancel_work_sync(&sunkbd->tq);
254*77e70d35SDmitry Torokhov 	}
2551da177e4SLinus Torvalds }
2561da177e4SLinus Torvalds 
2571da177e4SLinus Torvalds /*
2587cac9cd9SDmitry Torokhov  * sunkbd_connect() probes for a Sun keyboard and fills the necessary
2597cac9cd9SDmitry Torokhov  * structures.
2601da177e4SLinus Torvalds  */
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
2631da177e4SLinus Torvalds {
2641da177e4SLinus Torvalds 	struct sunkbd *sunkbd;
2653c42f0c3SDmitry Torokhov 	struct input_dev *input_dev;
2663c42f0c3SDmitry Torokhov 	int err = -ENOMEM;
2671da177e4SLinus Torvalds 	int i;
2681da177e4SLinus Torvalds 
2693c42f0c3SDmitry Torokhov 	sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
2703c42f0c3SDmitry Torokhov 	input_dev = input_allocate_device();
2713c42f0c3SDmitry Torokhov 	if (!sunkbd || !input_dev)
2722b03b60eSDmitry Torokhov 		goto fail1;
2731da177e4SLinus Torvalds 
2741da177e4SLinus Torvalds 	sunkbd->serio = serio;
2753c42f0c3SDmitry Torokhov 	sunkbd->dev = input_dev;
2763c42f0c3SDmitry Torokhov 	init_waitqueue_head(&sunkbd->wait);
277c4028958SDavid Howells 	INIT_WORK(&sunkbd->tq, sunkbd_reinit);
2783c42f0c3SDmitry Torokhov 	snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds 	serio_set_drvdata(serio, sunkbd);
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 	err = serio_open(serio, drv);
2833c42f0c3SDmitry Torokhov 	if (err)
2842b03b60eSDmitry Torokhov 		goto fail2;
2851da177e4SLinus Torvalds 
2861da177e4SLinus Torvalds 	if (sunkbd_initialize(sunkbd) < 0) {
2872b03b60eSDmitry Torokhov 		err = -ENODEV;
2882b03b60eSDmitry Torokhov 		goto fail3;
2891da177e4SLinus Torvalds 	}
2901da177e4SLinus Torvalds 
2917cac9cd9SDmitry Torokhov 	snprintf(sunkbd->name, sizeof(sunkbd->name),
2927cac9cd9SDmitry Torokhov 		 "Sun Type %d keyboard", sunkbd->type);
2931da177e4SLinus Torvalds 	memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
2943c42f0c3SDmitry Torokhov 
2953c42f0c3SDmitry Torokhov 	input_dev->name = sunkbd->name;
2963c42f0c3SDmitry Torokhov 	input_dev->phys = sunkbd->phys;
2973c42f0c3SDmitry Torokhov 	input_dev->id.bustype = BUS_RS232;
2983c42f0c3SDmitry Torokhov 	input_dev->id.vendor  = SERIO_SUNKBD;
2993c42f0c3SDmitry Torokhov 	input_dev->id.product = sunkbd->type;
3003c42f0c3SDmitry Torokhov 	input_dev->id.version = 0x0100;
301469ba4dfSDmitry Torokhov 	input_dev->dev.parent = &serio->dev;
302b356872fSDmitry Torokhov 
303b356872fSDmitry Torokhov 	input_set_drvdata(input_dev, sunkbd);
304b356872fSDmitry Torokhov 
3053c42f0c3SDmitry Torokhov 	input_dev->event = sunkbd_event;
3063c42f0c3SDmitry Torokhov 
3077b19ada2SJiri Slaby 	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
3087b19ada2SJiri Slaby 		BIT_MASK(EV_SND) | BIT_MASK(EV_REP);
3097b19ada2SJiri Slaby 	input_dev->ledbit[0] = BIT_MASK(LED_CAPSL) | BIT_MASK(LED_COMPOSE) |
3107b19ada2SJiri Slaby 		BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_NUML);
3117b19ada2SJiri Slaby 	input_dev->sndbit[0] = BIT_MASK(SND_CLICK) | BIT_MASK(SND_BELL);
3123c42f0c3SDmitry Torokhov 
3133c42f0c3SDmitry Torokhov 	input_dev->keycode = sunkbd->keycode;
3143c42f0c3SDmitry Torokhov 	input_dev->keycodesize = sizeof(unsigned char);
3153c42f0c3SDmitry Torokhov 	input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
3167cac9cd9SDmitry Torokhov 	for (i = 0; i < ARRAY_SIZE(sunkbd_keycode); i++)
3177cac9cd9SDmitry Torokhov 		__set_bit(sunkbd->keycode[i], input_dev->keybit);
3187cac9cd9SDmitry Torokhov 	__clear_bit(KEY_RESERVED, input_dev->keybit);
3191da177e4SLinus Torvalds 
3207cac9cd9SDmitry Torokhov 	sunkbd_enable(sunkbd, true);
3212b03b60eSDmitry Torokhov 
3222b03b60eSDmitry Torokhov 	err = input_register_device(sunkbd->dev);
3232b03b60eSDmitry Torokhov 	if (err)
3242b03b60eSDmitry Torokhov 		goto fail4;
3252b03b60eSDmitry Torokhov 
3261da177e4SLinus Torvalds 	return 0;
3273c42f0c3SDmitry Torokhov 
3287cac9cd9SDmitry Torokhov  fail4:	sunkbd_enable(sunkbd, false);
3292b03b60eSDmitry Torokhov  fail3:	serio_close(serio);
3302b03b60eSDmitry Torokhov  fail2:	serio_set_drvdata(serio, NULL);
3312b03b60eSDmitry Torokhov  fail1:	input_free_device(input_dev);
3323c42f0c3SDmitry Torokhov 	kfree(sunkbd);
3333c42f0c3SDmitry Torokhov 	return err;
3341da177e4SLinus Torvalds }
3351da177e4SLinus Torvalds 
3361da177e4SLinus Torvalds /*
3371da177e4SLinus Torvalds  * sunkbd_disconnect() unregisters and closes behind us.
3381da177e4SLinus Torvalds  */
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds static void sunkbd_disconnect(struct serio *serio)
3411da177e4SLinus Torvalds {
3421da177e4SLinus Torvalds 	struct sunkbd *sunkbd = serio_get_drvdata(serio);
3433c42f0c3SDmitry Torokhov 
3447cac9cd9SDmitry Torokhov 	sunkbd_enable(sunkbd, false);
3453c42f0c3SDmitry Torokhov 	input_unregister_device(sunkbd->dev);
3461da177e4SLinus Torvalds 	serio_close(serio);
3471da177e4SLinus Torvalds 	serio_set_drvdata(serio, NULL);
3481da177e4SLinus Torvalds 	kfree(sunkbd);
3491da177e4SLinus Torvalds }
3501da177e4SLinus Torvalds 
3511966e005SArvind Yadav static const struct serio_device_id sunkbd_serio_ids[] = {
3521da177e4SLinus Torvalds 	{
3531da177e4SLinus Torvalds 		.type	= SERIO_RS232,
3541da177e4SLinus Torvalds 		.proto	= SERIO_SUNKBD,
3551da177e4SLinus Torvalds 		.id	= SERIO_ANY,
3561da177e4SLinus Torvalds 		.extra	= SERIO_ANY,
3571da177e4SLinus Torvalds 	},
3581da177e4SLinus Torvalds 	{
3591da177e4SLinus Torvalds 		.type	= SERIO_RS232,
3601da177e4SLinus Torvalds 		.proto	= SERIO_UNKNOWN, /* sunkbd does probe */
3611da177e4SLinus Torvalds 		.id	= SERIO_ANY,
3621da177e4SLinus Torvalds 		.extra	= SERIO_ANY,
3631da177e4SLinus Torvalds 	},
3641da177e4SLinus Torvalds 	{ 0 }
3651da177e4SLinus Torvalds };
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds static struct serio_driver sunkbd_drv = {
3701da177e4SLinus Torvalds 	.driver		= {
3711da177e4SLinus Torvalds 		.name	= "sunkbd",
3721da177e4SLinus Torvalds 	},
3731da177e4SLinus Torvalds 	.description	= DRIVER_DESC,
3741da177e4SLinus Torvalds 	.id_table	= sunkbd_serio_ids,
3751da177e4SLinus Torvalds 	.interrupt	= sunkbd_interrupt,
3761da177e4SLinus Torvalds 	.connect	= sunkbd_connect,
3771da177e4SLinus Torvalds 	.disconnect	= sunkbd_disconnect,
3781da177e4SLinus Torvalds };
3791da177e4SLinus Torvalds 
38065ac9f7aSAxel Lin module_serio_driver(sunkbd_drv);
381