patch-2.3.99-pre4 linux/drivers/usb/evdev.c

Next file: linux/drivers/usb/hid.c
Previous file: linux/drivers/usb/drivers.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.99-pre3/linux/drivers/usb/evdev.c linux/drivers/usb/evdev.c
@@ -29,9 +29,9 @@
  */
 
 #define EVDEV_MINOR_BASE	64
+#define EVDEV_MINORS		32
 #define EVDEV_BUFFER_SIZE	64
 
-#include <linux/miscdevice.h>
 #include <linux/poll.h>
 #include <linux/malloc.h>
 #include <linux/module.h>
@@ -39,11 +39,12 @@
 #include <linux/input.h>
 
 struct evdev {
-	char name[32];
 	int used;
+	int open;
+	int minor;
 	struct input_handle handle;
-	struct miscdevice misc;
 	wait_queue_head_t wait;
+	devfs_handle_t devfs;
 	struct evdev_list *list;
 };
 
@@ -56,8 +57,7 @@
 	struct evdev_list *next;
 };
 
-static unsigned long evdev_miscbits = 0;
-static struct evdev *evdev_base[BITS_PER_LONG];
+static struct evdev *evdev_table[EVDEV_MINORS] = { NULL, /* ... */ };
 
 static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
 {
@@ -99,10 +99,13 @@
 	while (*listptr && (*listptr != list))
 		listptr = &((*listptr)->next);
 	*listptr = (*listptr)->next;
+
+	if (!--list->evdev->open)
+		input_close_device(&list->evdev->handle);	
 	
 	if (!--list->evdev->used) {
-		clear_bit(list->evdev->misc.minor - EVDEV_MINOR_BASE, &evdev_miscbits);
-		misc_deregister(&list->evdev->misc);
+		input_unregister_minor(list->evdev->devfs);
+		evdev_table[list->evdev->minor] = NULL;
 		kfree(list->evdev);
 	}
 
@@ -117,23 +120,28 @@
 	struct evdev_list *list;
 	int i = MINOR(inode->i_rdev) - EVDEV_MINOR_BASE;
 
-	if (i > BITS_PER_LONG || !test_bit(i, &evdev_miscbits))
+	if (i > EVDEV_MINORS || !evdev_table[i])
 		return -ENODEV;
 
-	if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL)))
-		return -ENOMEM;
+	MOD_INC_USE_COUNT;
 
+	if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL))) {
+		MOD_DEC_USE_COUNT;
+		return -ENOMEM;
+	}
 	memset(list, 0, sizeof(struct evdev_list));
 
-	list->evdev = evdev_base[i];
-	list->next = evdev_base[i]->list;
-	evdev_base[i]->list = list;
+	list->evdev = evdev_table[i];
+	list->next = evdev_table[i]->list;
+	evdev_table[i]->list = list;
 
 	file->private_data = list;
 
 	list->evdev->used++;
 
-	MOD_INC_USE_COUNT;
+	if (!list->evdev->open++)
+		input_open_device(&list->evdev->handle);	
+
 	return 0;
 }
 
@@ -193,34 +201,81 @@
 	return 0;
 }
 
+static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct evdev_list *list = file->private_data;
+	struct evdev *evdev = list->evdev;
+	struct input_dev *dev = evdev->handle.dev;
+
+	switch (cmd) {
+
+		case EVIOCGVERSION:
+			return put_user(EV_VERSION, (__u32 *) arg);
+		case EVIOCGID:
+			return copy_to_user(&dev->id, (void *) arg,
+						sizeof(struct input_id)) ? -EFAULT : 0;
+		default:
+
+			if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ)
+				return -EINVAL;
+
+			if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
+
+				long *bits = NULL;
+				int len = 0;
+
+				switch (_IOC_NR(cmd) & EV_MAX) {
+					case      0: bits = dev->evbit;  len = EV_MAX;  break;
+					case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
+					case EV_REL: bits = dev->relbit; len = REL_MAX; break;
+					case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
+					case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
+					case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
+					default: return -EINVAL;
+				}
+				len = NBITS(len) * sizeof(long);
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				return copy_to_user((void *) arg, bits, len) ? -EFAULT : len;
+			}
+
+			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) {
+				int len = strlen(dev->name) + 1;
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				return copy_to_user((char *) arg, dev->name, len) ? -EFAULT : len;
+			}
+	}
+	return -EINVAL;
+}
+
 static struct file_operations evdev_fops = {
 	read:		evdev_read,
 	write:		evdev_write,
 	poll:		evdev_poll,
 	open:		evdev_open,
 	release:	evdev_release,
+	ioctl:		evdev_ioctl,
 	fasync:		evdev_fasync,
 };
 
-static int evdev_connect(struct input_handler *handler, struct input_dev *dev)
+static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev)
 {
 	struct evdev *evdev;
+	int minor;
 
-	if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL)))
-		return -1;
+	for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
+	if (evdev_table[minor]) {
+		printk(KERN_ERR "evdev: no more free evdev devices\n");
+		return NULL;
+	}
 
+	if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL)))
+		return NULL;
 	memset(evdev, 0, sizeof(struct evdev));
 
 	init_waitqueue_head(&evdev->wait);
 
-	evdev->misc.minor = ffz(evdev_miscbits);
-	set_bit(evdev->misc.minor, &evdev_miscbits);
-	evdev_base[evdev->misc.minor] = evdev;
-
-	sprintf(evdev->name, "evdev%d", evdev->misc.minor);
-	evdev->misc.name = evdev->name;
-	evdev->misc.minor += EVDEV_MINOR_BASE;
-	evdev->misc.fops = &evdev_fops;
+	evdev->minor = minor;
+	evdev_table[minor] = evdev;
 
 	evdev->handle.dev = dev;
 	evdev->handle.handler = handler;
@@ -228,24 +283,23 @@
 
 	evdev->used = 1;
 
-	misc_register(&evdev->misc);
-	input_open_device(&evdev->handle);
+	evdev->devfs = input_register_minor("event%d", minor, EVDEV_MINOR_BASE);
 
-	printk("%s: Event device for input%d on misc%d - /dev/input%d\n",
-		evdev->name, dev->number, evdev->misc.minor, evdev->misc.minor - EVDEV_MINOR_BASE);
+	printk("event%d: Event device for input%d\n", minor, dev->number);
 
-	return 0;
+	return &evdev->handle;
 }
 
 static void evdev_disconnect(struct input_handle *handle)
 {
 	struct evdev *evdev = handle->private;
 
-	input_close_device(handle);
+	if (evdev->open)
+		input_close_device(handle);
 
 	if (!--evdev->used) {
-		clear_bit(evdev->misc.minor - EVDEV_MINOR_BASE, &evdev_miscbits);
-		misc_deregister(&evdev->misc);
+		input_unregister_minor(evdev->devfs);
+		evdev_table[evdev->minor] = NULL;
 		kfree(evdev);
 	}
 }
@@ -254,6 +308,8 @@
 	event:		evdev_event,
 	connect:	evdev_connect,
 	disconnect:	evdev_disconnect,
+	fops:		&evdev_fops,
+	minor:		EVDEV_MINOR_BASE,
 };
 
 static int __init evdev_init(void)

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)