Input: serio_raw - explicitly mark disconnected ports as dead

Instead of relying on setting serio_raw->serio to NULL upon disconnecting
ports mark them explicitly as "dead". Also take and carry reference to
underlying serio port to make sure it does not go away until we are done
with it.

Reviewed-by: Wanlong Gao <gaowanlong@cn.fujitsu.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
index 64fcefb..30ff963 100644
--- a/drivers/input/serio/serio_raw.c
+++ b/drivers/input/serio/serio_raw.c
@@ -40,6 +40,7 @@
 	wait_queue_head_t wait;
 	struct list_head client_list;
 	struct list_head node;
+	bool dead;
 };
 
 struct serio_raw_client {
@@ -91,7 +92,7 @@
 		goto out;
 	}
 
-	if (!serio_raw->serio) {
+	if (serio_raw->dead) {
 		retval = -ENODEV;
 		goto out;
 	}
@@ -123,6 +124,8 @@
 
 	misc_deregister(&serio_raw->dev);
 	list_del_init(&serio_raw->node);
+
+	put_device(&serio_raw->serio->dev);
 	kfree(serio_raw);
 }
 
@@ -164,19 +167,18 @@
 	char uninitialized_var(c);
 	ssize_t retval = 0;
 
-	if (!serio_raw->serio)
+	if (serio_raw->dead)
 		return -ENODEV;
 
 	if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK))
 		return -EAGAIN;
 
 	retval = wait_event_interruptible(serio_raw->wait,
-					  serio_raw->head != serio_raw->tail ||
-						!serio_raw->serio);
+			serio_raw->head != serio_raw->tail || serio_raw->dead);
 	if (retval)
 		return retval;
 
-	if (!serio_raw->serio)
+	if (serio_raw->dead)
 		return -ENODEV;
 
 	while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) {
@@ -201,7 +203,7 @@
 	if (retval)
 		return retval;
 
-	if (!serio_raw->serio) {
+	if (serio_raw->dead) {
 		retval = -ENODEV;
 		goto out;
 	}
@@ -291,10 +293,12 @@
 	snprintf(serio_raw->name, sizeof(serio_raw->name),
 		 "serio_raw%d", serio_raw_no++);
 	kref_init(&serio_raw->kref);
-	serio_raw->serio = serio;
 	INIT_LIST_HEAD(&serio_raw->client_list);
 	init_waitqueue_head(&serio_raw->wait);
 
+	serio_raw->serio = serio;
+	get_device(&serio->dev);
+
 	serio_set_drvdata(serio, serio_raw);
 
 	err = serio_open(serio, drv);
@@ -330,6 +334,7 @@
 	list_del_init(&serio_raw->node);
 out_free:
 	serio_set_drvdata(serio, NULL);
+	put_device(&serio->dev);
 	kfree(serio_raw);
 out:
 	mutex_unlock(&serio_raw_mutex);
@@ -365,7 +370,7 @@
 	serio_close(serio);
 	serio_set_drvdata(serio, NULL);
 
-	serio_raw->serio = NULL;
+	serio_raw->dead = true;
 	wake_up_interruptible(&serio_raw->wait);
 	kref_put(&serio_raw->kref, serio_raw_cleanup);