+static void tsdev_free(struct device *dev)
+{
+ struct tsdev *tsdev = container_of(dev, struct tsdev, dev);
+
+ kfree(tsdev);
+}
+
+static void tsdev_attach_client(struct tsdev *tsdev, struct tsdev_client *client)
+{
+ spin_lock(&tsdev->client_lock);
+ list_add_tail_rcu(&client->node, &tsdev->client_list);
+ spin_unlock(&tsdev->client_lock);
+ synchronize_sched();
+}
+
+static void tsdev_detach_client(struct tsdev *tsdev, struct tsdev_client *client)
+{
+ spin_lock(&tsdev->client_lock);
+ list_del_rcu(&client->node);
+ spin_unlock(&tsdev->client_lock);
+ synchronize_sched();
+}
+
+static int tsdev_open_device(struct tsdev *tsdev)
+{
+ int retval;
+
+ retval = mutex_lock_interruptible(&tsdev->mutex);
+ if (retval)
+ return retval;
+
+ if (!tsdev->exist)
+ retval = -ENODEV;
+ else if (!tsdev->open++)
+ retval = input_open_device(&tsdev->handle);
+
+ mutex_unlock(&tsdev->mutex);
+ return retval;
+}
+
+static void tsdev_close_device(struct tsdev *tsdev)
+{
+ mutex_lock(&tsdev->mutex);
+
+ if (tsdev->exist && !--tsdev->open)
+ input_close_device(&tsdev->handle);
+
+ mutex_unlock(&tsdev->mutex);
+}
+
+/*
+ * Wake up users waiting for IO so they can disconnect from
+ * dead device.
+ */
+static void tsdev_hangup(struct tsdev *tsdev)
+{
+ struct tsdev_client *client;
+
+ spin_lock(&tsdev->client_lock);
+ list_for_each_entry(client, &tsdev->client_list, node)
+ kill_fasync(&client->fasync, SIGIO, POLL_HUP);
+ spin_unlock(&tsdev->client_lock);
+
+ wake_up_interruptible(&tsdev->wait);
+}
+
+static int tsdev_release(struct inode *inode, struct file *file)
+{
+ struct tsdev_client *client = file->private_data;
+ struct tsdev *tsdev = client->tsdev;
+
+ tsdev_fasync(-1, file, 0);
+ tsdev_detach_client(tsdev, client);
+ kfree(client);
+
+ tsdev_close_device(tsdev);
+ put_device(&tsdev->dev);
+
+ return 0;
+}
+