[PATCH] to drivers/input/evdev.c to add mixer device "/dev/input/events"

From: Joe Peterson
Date: Mon Aug 15 2005 - 12:29:02 EST


I, and a growing number of others, have been having trouble with using
touch screen devices in Linux, particularly the motorized ones that fit
into car dashboards. The problem is that these devices, which have a
USB touchscreen (often the eGalax brand), turn off when the screen is
retracted, causing Linux to remove and/or disconnect the event device
in /dev/input.

Using udev to assign a persistent symlink to the device was the first
thing I tried, but it does not solve the problem, since X windows does
not see the device when it turns back on, even if it's the same name. I
(and others) have tried hacks like changing virtual terminals, etc. to
get it back, but these are not elegant solutions are are problematic.

Anyway, I finally decided the thing I needed was something like
/dev/input/mice, since it is always there for X to see, even if the
device is off during boot. But the mousedev devices are not the
right data format for use with the touch screens (you need "event"
ones). So I hacked evdev.c and added "/dev/input/events", which is a
mixer as well and catches all events from event0, event1, etc.

This works great, and I think it would be a great addition to the Linux
kernel. I would like to submit this patch, and I hope it can be made a
part of the Linux kernel. However, I would feel more comfortable if
someone (the maintainer?) could look over my changes and verify that
they are done correctly. I had a few open questions that resulted from
the fact that evdev is slightly different than mousedev:

For example, in evdev_open, I had to check evdev->handle.dev for
non-zero before making the input call (evdev_mix has a zero in this
pointer), or of course I would get a seg fault. I also got a lockup
later that I fixed by making other similar checks for a zero
handle.dev, but since these checks were not necessary in mousedev, I
would like to verify that this is the correct way to deal with it.

Another thing is the "grab" concept in evdev. I check for handle.dev to
be non-zero before I let grab get set, and I do not know every aspect of
the grab mechanism, so I hope this is the right thing to do.

Anyway, I hope my change is valuable. I (and others) would love to see
it appear in the official kernel source. I attached the patch.

Thanks, Joe

--- linux-2.6.12/drivers/input/evdev.c.orig 2005-08-15 11:12:15.000000000 -0600
+++ linux-2.6.12/drivers/input/evdev.c 2005-08-15 11:12:11.000000000 -0600
@@ -8,8 +8,17 @@
* the Free Software Foundation.
*/

+/*
+ * Modified 2005-8-12 Joe Peterson
+ *
+ * - Added persistent mixer device "/dev/input/events"
+ * (analogous to mousedev.c's "/dev/input/mice")
+ *
+ */
+
#define EVDEV_MINOR_BASE 64
#define EVDEV_MINORS 32
+#define EVDEV_MIX 31
#define EVDEV_BUFFER_SIZE 64

#include <linux/poll.h>
@@ -42,11 +51,13 @@ struct evdev_list {
struct list_head node;
};

+static struct input_handler evdev_handler;
+
static struct evdev *evdev_table[EVDEV_MINORS];
+static struct evdev evdev_mix;

-static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+static void evdev_handle_event(struct evdev *evdev, unsigned int type, unsigned int code, int value)
{
- struct evdev *evdev = handle->private;
struct evdev_list *list;

if (evdev->grab) {
@@ -74,6 +85,14 @@ static void evdev_event(struct input_han
wake_up_interruptible(&evdev->wait);
}

+static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+ struct evdev *evdev = handle->private;
+
+ evdev_handle_event(evdev, type, code, value);
+ evdev_handle_event(&evdev_mix, type, code, value);
+}
+
static int evdev_fasync(int fd, struct file *file, int on)
{
int retval;
@@ -86,7 +105,10 @@ static int evdev_flush(struct file * fil
{
struct evdev_list *list = file->private_data;
if (!list->evdev->exist) return -ENODEV;
- return input_flush_device(&list->evdev->handle, file);
+ if (list->evdev->handle.dev)
+ return input_flush_device(&list->evdev->handle, file);
+ else
+ return 0;
}

static void evdev_free(struct evdev *evdev)
@@ -95,6 +117,24 @@ static void evdev_free(struct evdev *evd
kfree(evdev);
}

+static int mixdev_release(void)
+{
+ struct input_handle *handle;
+
+ list_for_each_entry(handle, &evdev_handler.h_list, h_node) {
+ struct evdev *evdev = handle->private;
+
+ if (!evdev->open) {
+ if (evdev->exist)
+ input_close_device(&evdev->handle);
+ else
+ evdev_free(evdev);
+ }
+ }
+
+ return 0;
+}
+
static int evdev_release(struct inode * inode, struct file * file)
{
struct evdev_list *list = file->private_data;
@@ -108,10 +148,15 @@ static int evdev_release(struct inode *
list_del(&list->node);

if (!--list->evdev->open) {
- if (list->evdev->exist)
- input_close_device(&list->evdev->handle);
- else
- evdev_free(list->evdev);
+ if (list->evdev->minor == EVDEV_MIX)
+ return mixdev_release();
+
+ if (!evdev_mix.open) {
+ if (list->evdev->exist)
+ input_close_device(&list->evdev->handle);
+ else
+ evdev_free(list->evdev);
+ }
}

kfree(list);
@@ -121,13 +166,16 @@ static int evdev_release(struct inode *
static int evdev_open(struct inode * inode, struct file * file)
{
struct evdev_list *list;
+ struct input_handle *handle;
+ struct evdev *evdev;
int i = iminor(inode) - EVDEV_MINOR_BASE;
int accept_err;

if (i >= EVDEV_MINORS || !evdev_table[i] || !evdev_table[i]->exist)
return -ENODEV;

- if ((accept_err = input_accept_process(&(evdev_table[i]->handle), file)))
+ if (evdev_table[i]->handle.dev)
+ if ((accept_err = input_accept_process(&(evdev_table[i]->handle), file)))
return accept_err;

if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL)))
@@ -138,10 +186,18 @@ static int evdev_open(struct inode * ino
list_add_tail(&list->node, &evdev_table[i]->list);
file->private_data = list;

- if (!list->evdev->open++)
- if (list->evdev->exist)
- input_open_device(&list->evdev->handle);
-
+ if (!list->evdev->open++) {
+ if (list->evdev->minor == EVDEV_MIX) {
+ list_for_each_entry(handle, &evdev_handler.h_list, h_node) {
+ evdev = handle->private;
+ if (!evdev->open && evdev->exist)
+ input_open_device(handle);
+ }
+ } else
+ if (!evdev_mix.open && list->evdev->exist)
+ input_open_device(&list->evdev->handle);
+ }
+
return 0;
}

@@ -157,7 +213,8 @@ static ssize_t evdev_write(struct file *

if (copy_from_user(&event, buffer + retval, sizeof(struct input_event)))
return -EFAULT;
- input_event(list->evdev->handle.dev, event.type, event.code, event.value);
+ if (list->evdev->handle.dev)
+ input_event(list->evdev->handle.dev, event.type, event.code, event.value);
retval += sizeof(struct input_event);
}

@@ -268,20 +325,24 @@ static int evdev_ioctl(struct inode *ino
return 0;

case EVIOCGRAB:
- if (arg) {
+ if (evdev->handle.dev) {
+ if (arg) {
if (evdev->grab)
return -EBUSY;
if (input_grab_device(&evdev->handle))
return -EBUSY;
evdev->grab = list;
return 0;
- } else {
+ } else {
if (evdev->grab != list)
return -EINVAL;
input_release_device(&evdev->handle);
evdev->grab = NULL;
return 0;
- }
+ }
+ } else {
+ return 0;
+ }

default:

@@ -427,6 +488,9 @@ static struct input_handle *evdev_connec
evdev->handle.private = evdev;
sprintf(evdev->name, "event%d", minor);

+ if (evdev_mix.open)
+ input_open_device(&evdev->handle);
+
evdev_table[minor] = evdev;

devfs_mk_cdev(MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
@@ -452,8 +516,11 @@ static void evdev_disconnect(struct inpu
wake_up_interruptible(&evdev->wait);
list_for_each_entry(list, &evdev->list, node)
kill_fasync(&list->fasync, SIGIO, POLL_HUP);
- } else
+ } else {
+ if (evdev_mix.open)
+ input_close_device(handle);
evdev_free(evdev);
+ }
}

static struct input_device_id evdev_ids[] = {
@@ -476,11 +543,28 @@ static struct input_handler evdev_handle
static int __init evdev_init(void)
{
input_register_handler(&evdev_handler);
+
+ memset(&evdev_mix, 0, sizeof(struct evdev));
+ INIT_LIST_HEAD(&evdev_mix.list);
+ init_waitqueue_head(&evdev_mix.wait);
+ evdev_table[EVDEV_MIX] = &evdev_mix;
+ evdev_mix.exist = 1;
+ evdev_mix.minor = EVDEV_MIX;
+
+ devfs_mk_cdev(MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + EVDEV_MIX),
+ S_IFCHR|S_IRUGO|S_IWUSR, "input/events");
+ class_simple_device_add(input_class, MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + EVDEV_MIX),
+ NULL, "events");
+
+ printk(KERN_INFO "events: event device common for all events\n");
+
return 0;
}

static void __exit evdev_exit(void)
{
+ devfs_remove("input/events");
+ class_simple_device_remove(MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + EVDEV_MIX));
input_unregister_handler(&evdev_handler);
}