summaryrefslogtreecommitdiffstats
path: root/drivers/usb/evdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/evdev.c')
-rw-r--r--drivers/usb/evdev.c122
1 files changed, 89 insertions, 33 deletions
diff --git a/drivers/usb/evdev.c b/drivers/usb/evdev.c
index 9cca9fdf6..1d42f7df0 100644
--- a/drivers/usb/evdev.c
+++ b/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 {
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 @@ static int evdev_release(struct inode * inode, struct file * file)
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 @@ static int evdev_open(struct inode * inode, struct file * file)
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 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait)
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 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev)
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 @@ static struct input_handler evdev_handler = {
event: evdev_event,
connect: evdev_connect,
disconnect: evdev_disconnect,
+ fops: &evdev_fops,
+ minor: EVDEV_MINOR_BASE,
};
static int __init evdev_init(void)