summary refs log tree commit diff
path: root/drivers/input
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/serio/serio_raw.c54
1 files changed, 29 insertions, 25 deletions
diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
index 830e2fe70a66..4d4cd142bbbb 100644
--- a/drivers/input/serio/serio_raw.c
+++ b/drivers/input/serio/serio_raw.c
@@ -51,7 +51,6 @@ struct serio_raw_client {
 
 static DEFINE_MUTEX(serio_raw_mutex);
 static LIST_HEAD(serio_raw_list);
-static unsigned int serio_raw_no;
 
 /*********************************************************************
  *             Interface with userspace (file operations)            *
@@ -117,14 +116,11 @@ out:
 	return retval;
 }
 
-static void serio_raw_cleanup(struct kref *kref)
+static void serio_raw_free(struct kref *kref)
 {
 	struct serio_raw *serio_raw =
 			container_of(kref, struct serio_raw, kref);
 
-	misc_deregister(&serio_raw->dev);
-	list_del_init(&serio_raw->node);
-
 	put_device(&serio_raw->serio->dev);
 	kfree(serio_raw);
 }
@@ -134,11 +130,14 @@ static int serio_raw_release(struct inode *inode, struct file *file)
 	struct serio_raw_client *client = file->private_data;
 	struct serio_raw *serio_raw = client->serio_raw;
 
-	mutex_lock(&serio_raw_mutex);
+	serio_pause_rx(serio_raw->serio);
+	list_del(&client->node);
+	serio_continue_rx(serio_raw->serio);
 
-	kref_put(&serio_raw->kref, serio_raw_cleanup);
+	kfree(client);
+
+	kref_put(&serio_raw->kref, serio_raw_free);
 
-	mutex_unlock(&serio_raw_mutex);
 	return 0;
 }
 
@@ -281,6 +280,7 @@ static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data,
 
 static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
 {
+	static atomic_t serio_raw_no = ATOMIC_INIT(0);
 	struct serio_raw *serio_raw;
 	int err;
 
@@ -290,10 +290,8 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
 		return -ENOMEM;
 	}
 
-	mutex_lock(&serio_raw_mutex);
-
 	snprintf(serio_raw->name, sizeof(serio_raw->name),
-		 "serio_raw%d", serio_raw_no++);
+		 "serio_raw%ld", (long)atomic_inc_return(&serio_raw_no) - 1);
 	kref_init(&serio_raw->kref);
 	INIT_LIST_HEAD(&serio_raw->client_list);
 	init_waitqueue_head(&serio_raw->wait);
@@ -305,9 +303,14 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto out_free;
+		goto err_free;
+
+	err = mutex_lock_killable(&serio_raw_mutex);
+	if (err)
+		goto err_close;
 
 	list_add_tail(&serio_raw->node, &serio_raw_list);
+	mutex_unlock(&serio_raw_mutex);
 
 	serio_raw->dev.minor = PSMOUSE_MINOR;
 	serio_raw->dev.name = serio_raw->name;
@@ -324,22 +327,20 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
 		dev_err(&serio->dev,
 			"failed to register raw access device for %s\n",
 			serio->phys);
-		goto out_close;
+		goto err_unlink;
 	}
 
 	dev_info(&serio->dev, "raw access enabled on %s (%s, minor %d)\n",
 		 serio->phys, serio_raw->name, serio_raw->dev.minor);
-	goto out;
+	return 0;
 
-out_close:
-	serio_close(serio);
+err_unlink:
 	list_del_init(&serio_raw->node);
-out_free:
+err_close:
+	serio_close(serio);
+err_free:
 	serio_set_drvdata(serio, NULL);
-	put_device(&serio->dev);
-	kfree(serio_raw);
-out:
-	mutex_unlock(&serio_raw_mutex);
+	kref_put(&serio_raw->kref, serio_raw_free);
 	return err;
 }
 
@@ -382,14 +383,17 @@ static void serio_raw_disconnect(struct serio *serio)
 {
 	struct serio_raw *serio_raw = serio_get_drvdata(serio);
 
-	mutex_lock(&serio_raw_mutex);
+	misc_deregister(&serio_raw->dev);
 
-	serio_close(serio);
+	mutex_lock(&serio_raw_mutex);
 	serio_raw->dead = true;
+	list_del_init(&serio_raw->node);
+	mutex_unlock(&serio_raw_mutex);
+
 	serio_raw_hangup(serio_raw);
-	kref_put(&serio_raw->kref, serio_raw_cleanup);
 
-	mutex_unlock(&serio_raw_mutex);
+	serio_close(serio);
+	kref_put(&serio_raw->kref, serio_raw_free);
 
 	serio_set_drvdata(serio, NULL);
 }