Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / input / evdev.c
index 17552a2..ba325f1 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/major.h>
 #include <linux/smp_lock.h>
 #include <linux/device.h>
-#include <linux/devfs_fs_kernel.h>
+#include <linux/compat.h>
 
 struct evdev {
        int exist;
@@ -130,9 +130,8 @@ static int evdev_open(struct inode * inode, struct file * file)
        if ((accept_err = input_accept_process(&(evdev_table[i]->handle), file)))
                return accept_err;
 
-       if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL)))
+       if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL)))
                return -ENOMEM;
-       memset(list, 0, sizeof(struct evdev_list));
 
        list->evdev = evdev_table[i];
        list_add_tail(&list->node, &evdev_table[i]->list);
@@ -145,20 +144,119 @@ static int evdev_open(struct inode * inode, struct file * file)
        return 0;
 }
 
+#ifdef CONFIG_COMPAT
+
+struct input_event_compat {
+       struct compat_timeval time;
+       __u16 type;
+       __u16 code;
+       __s32 value;
+};
+
+/* Note to the author of this code: did it ever occur to
+   you why the ifdefs are needed? Think about it again. -AK */
+#ifdef CONFIG_X86_64
+#  define COMPAT_TEST is_compat_task()
+#elif defined(CONFIG_IA64)
+#  define COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current))
+#elif defined(CONFIG_S390)
+#  define COMPAT_TEST test_thread_flag(TIF_31BIT)
+#elif defined(CONFIG_MIPS)
+#  define COMPAT_TEST (current->thread.mflags & MF_32BIT_ADDR)
+#else
+#  define COMPAT_TEST test_thread_flag(TIF_32BIT)
+#endif
+
+static inline size_t evdev_event_size(void)
+{
+       return COMPAT_TEST ?
+               sizeof(struct input_event_compat) : sizeof(struct input_event);
+}
+
+static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
+{
+       if (COMPAT_TEST) {
+               struct input_event_compat compat_event;
+
+               if (copy_from_user(&compat_event, buffer, sizeof(struct input_event_compat)))
+                       return -EFAULT;
+
+               event->time.tv_sec = compat_event.time.tv_sec;
+               event->time.tv_usec = compat_event.time.tv_usec;
+               event->type = compat_event.type;
+               event->code = compat_event.code;
+               event->value = compat_event.value;
+
+       } else {
+               if (copy_from_user(event, buffer, sizeof(struct input_event)))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
+{
+       if (COMPAT_TEST) {
+               struct input_event_compat compat_event;
+
+               compat_event.time.tv_sec = event->time.tv_sec;
+               compat_event.time.tv_usec = event->time.tv_usec;
+               compat_event.type = event->type;
+               compat_event.code = event->code;
+               compat_event.value = event->value;
+
+               if (copy_to_user(buffer, &compat_event, sizeof(struct input_event_compat)))
+                       return -EFAULT;
+
+       } else {
+               if (copy_to_user(buffer, event, sizeof(struct input_event)))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+#else
+
+static inline size_t evdev_event_size(void)
+{
+       return sizeof(struct input_event);
+}
+
+static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
+{
+       if (copy_from_user(event, buffer, sizeof(struct input_event)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
+{
+       if (copy_to_user(buffer, event, sizeof(struct input_event)))
+               return -EFAULT;
+
+       return 0;
+}
+
+#endif /* CONFIG_COMPAT */
+
 static ssize_t evdev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
 {
        struct evdev_list *list = file->private_data;
        struct input_event event;
        int retval = 0;
 
-       if (!list->evdev->exist) return -ENODEV;
+       if (!list->evdev->exist)
+               return -ENODEV;
 
        while (retval < count) {
 
-               if (copy_from_user(&event, buffer + retval, sizeof(struct input_event)))
+               if (evdev_event_from_user(buffer + retval, &event))
                        return -EFAULT;
                input_event(list->evdev->handle.dev, event.type, event.code, event.value);
-               retval += sizeof(struct input_event);
+               retval += evdev_event_size();
        }
 
        return retval;
@@ -169,7 +267,7 @@ static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count
        struct evdev_list *list = file->private_data;
        int retval;
 
-       if (count < sizeof(struct input_event))
+       if (count < evdev_event_size())
                return -EINVAL;
 
        if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK))
@@ -184,11 +282,15 @@ static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count
        if (!list->evdev->exist)
                return -ENODEV;
 
-       while (list->head != list->tail && retval + sizeof(struct input_event) <= count) {
-               if (copy_to_user(buffer + retval, list->buffer + list->tail,
-                        sizeof(struct input_event))) return -EFAULT;
+       while (list->head != list->tail && retval + evdev_event_size() <= count) {
+
+               struct input_event *event = (struct input_event *) list->buffer + list->tail;
+
+               if (evdev_event_to_user(buffer + retval, event))
+                       return -EFAULT;
+
                list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
-               retval += sizeof(struct input_event);
+               retval += evdev_event_size();
        }
 
        return retval;
@@ -203,17 +305,95 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait)
                (list->evdev->exist ? 0 : (POLLHUP | POLLERR));
 }
 
-static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+#ifdef CONFIG_COMPAT
+
+#define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8)
+#define NBITS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1)
+
+#ifdef __BIG_ENDIAN
+static int bits_to_user(unsigned long *bits, unsigned int maxbit,
+                       unsigned int maxlen, void __user *p, int compat)
+{
+       int len, i;
+
+       if (compat) {
+               len = NBITS_COMPAT(maxbit) * sizeof(compat_long_t);
+               if (len < maxlen)
+                       len = maxlen;
+
+               for (i = 0; i < len / sizeof(compat_long_t); i++)
+                       if (copy_to_user((compat_long_t __user *) p + i,
+                                        (compat_long_t *) bits +
+                                               i + 1 - ((i % 2) << 1),
+                                        sizeof(compat_long_t)))
+                               return -EFAULT;
+       } else {
+               len = NBITS(maxbit) * sizeof(long);
+               if (len > maxlen)
+                       len = maxlen;
+
+               if (copy_to_user(p, bits, len))
+                       return -EFAULT;
+       }
+
+       return len;
+}
+#else
+static int bits_to_user(unsigned long *bits, unsigned int maxbit,
+                       unsigned int maxlen, void __user *p, int compat)
+{
+       int len = compat ?
+                       NBITS_COMPAT(maxbit) * sizeof(compat_long_t) :
+                       NBITS(maxbit) * sizeof(long);
+
+       if (len > maxlen)
+               len = maxlen;
+
+       return copy_to_user(p, bits, len) ? -EFAULT : len;
+}
+#endif /* __BIG_ENDIAN */
+
+#else
+
+static int bits_to_user(unsigned long *bits, unsigned int maxbit,
+                       unsigned int maxlen, void __user *p, int compat)
+{
+       int len = NBITS(maxbit) * sizeof(long);
+
+       if (len > maxlen)
+               len = maxlen;
+
+       return copy_to_user(p, bits, len) ? -EFAULT : len;
+}
+
+#endif /* CONFIG_COMPAT */
+
+static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
+{
+       int len;
+
+       if (!str)
+               return -ENOENT;
+
+       len = strlen(str) + 1;
+       if (len > maxlen)
+               len = maxlen;
+
+       return copy_to_user(p, str, len) ? -EFAULT : len;
+}
+
+static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
+                               void __user *p, int compat_mode)
 {
        struct evdev_list *list = file->private_data;
        struct evdev *evdev = list->evdev;
        struct input_dev *dev = evdev->handle.dev;
        struct input_absinfo abs;
-       void __user *p = (void __user *)arg;
-       int __user *ip = (int __user *)arg;
+       int __user *ip = (int __user *)p;
        int i, t, u, v;
 
-       if (!evdev->exist) return -ENODEV;
+       if (!evdev->exist)
+               return -ENODEV;
 
        switch (cmd) {
 
@@ -221,25 +401,60 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
                        return put_user(EV_VERSION, ip);
 
                case EVIOCGID:
-                       return copy_to_user(p, &dev->id, sizeof(struct input_id)) ? -EFAULT : 0;
+                       if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
+                               return -EFAULT;
+                       return 0;
+
+               case EVIOCGREP:
+                       if (!test_bit(EV_REP, dev->evbit))
+                               return -ENOSYS;
+                       if (put_user(dev->rep[REP_DELAY], ip))
+                               return -EFAULT;
+                       if (put_user(dev->rep[REP_PERIOD], ip + 1))
+                               return -EFAULT;
+                       return 0;
+
+               case EVIOCSREP:
+                       if (!test_bit(EV_REP, dev->evbit))
+                               return -ENOSYS;
+                       if (get_user(u, ip))
+                               return -EFAULT;
+                       if (get_user(v, ip + 1))
+                               return -EFAULT;
+
+                       input_event(dev, EV_REP, REP_DELAY, u);
+                       input_event(dev, EV_REP, REP_PERIOD, v);
+
+                       return 0;
 
                case EVIOCGKEYCODE:
-                       if (get_user(t, ip)) return -EFAULT;
-                       if (t < 0 || t >= dev->keycodemax || !dev->keycodesize) return -EINVAL;
-                       if (put_user(INPUT_KEYCODE(dev, t), ip + 1)) return -EFAULT;
+                       if (get_user(t, ip))
+                               return -EFAULT;
+                       if (t < 0 || t >= dev->keycodemax || !dev->keycodesize)
+                               return -EINVAL;
+                       if (put_user(INPUT_KEYCODE(dev, t), ip + 1))
+                               return -EFAULT;
                        return 0;
 
                case EVIOCSKEYCODE:
-                       if (get_user(t, ip)) return -EFAULT;
-                       if (t < 0 || t >= dev->keycodemax || !dev->keycodesize) return -EINVAL;
-                       if (get_user(v, ip + 1)) return -EFAULT;
-                       if (v < 0 || v > KEY_MAX) return -EINVAL;
+                       if (get_user(t, ip))
+                               return -EFAULT;
+                       if (t < 0 || t >= dev->keycodemax || !dev->keycodesize)
+                               return -EINVAL;
+                       if (get_user(v, ip + 1))
+                               return -EFAULT;
+                       if (v < 0 || v > KEY_MAX)
+                               return -EINVAL;
+                       if (dev->keycodesize < sizeof(v) && (v >> (dev->keycodesize * 8)))
+                               return -EINVAL;
+
                        u = SET_INPUT_KEYCODE(dev, t, v);
                        clear_bit(u, dev->keybit);
                        set_bit(v, dev->keybit);
                        for (i = 0; i < dev->keycodemax; i++)
-                               if (INPUT_KEYCODE(dev,i) == u)
+                               if (INPUT_KEYCODE(dev, i) == u)
                                        set_bit(u, dev->keybit);
+
                        return 0;
 
                case EVIOCSFF:
@@ -250,17 +465,17 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
                                if (copy_from_user(&effect, p, sizeof(effect)))
                                        return -EFAULT;
                                err = dev->upload_effect(dev, &effect);
-                               if (put_user(effect.id, &(((struct ff_effect __user *)arg)->id)))
+                               if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
                                        return -EFAULT;
                                return err;
-                       }
-                       else return -ENOSYS;
+                       } else
+                               return -ENOSYS;
 
                case EVIOCRMFF:
-                       if (dev->erase_effect) {
-                               return dev->erase_effect(dev, (int)arg);
-                       }
-                       else return -ENOSYS;
+                       if (!dev->erase_effect)
+                               return -ENOSYS;
+
+                       return dev->erase_effect(dev, (int)(unsigned long) p);
 
                case EVIOCGEFFECTS:
                        if (put_user(dev->ff_effects_max, ip))
@@ -268,7 +483,7 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
                        return 0;
 
                case EVIOCGRAB:
-                       if (arg) {
+                       if (p) {
                                if (evdev->grab)
                                        return -EBUSY;
                                if (input_grab_device(&evdev->handle))
@@ -285,110 +500,108 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 
                default:
 
-                       if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ)
+                       if (_IOC_TYPE(cmd) != 'E')
                                return -EINVAL;
 
-                       if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
-
-                               long *bits;
-                               int len;
-
-                               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_MSC: bits = dev->mscbit; len = MSC_MAX; break;
-                                       case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
-                                       case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
-                                       case EV_FF:  bits = dev->ffbit;  len = FF_MAX;  break;
-                                       default: return -EINVAL;
+                       if (_IOC_DIR(cmd) == _IOC_READ) {
+
+                               if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
+
+                                       long *bits;
+                                       int len;
+
+                                       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_MSC: bits = dev->mscbit; len = MSC_MAX; break;
+                                               case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
+                                               case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
+                                               case EV_FF:  bits = dev->ffbit;  len = FF_MAX;  break;
+                                               case EV_SW:  bits = dev->swbit;  len = SW_MAX;  break;
+                                               default: return -EINVAL;
+                                       }
+                                       return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
                                }
-                               len = NBITS(len) * sizeof(long);
-                               if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
-                               return copy_to_user(p, bits, len) ? -EFAULT : len;
-                       }
 
-                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) {
-                               int len;
-                               len = NBITS(KEY_MAX) * sizeof(long);
-                               if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
-                               return copy_to_user(p, dev->key, len) ? -EFAULT : len;
-                       }
+                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
+                                       return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
+                                                           p, compat_mode);
 
-                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) {
-                               int len;
-                               len = NBITS(LED_MAX) * sizeof(long);
-                               if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
-                               return copy_to_user(p, dev->led, len) ? -EFAULT : len;
-                       }
+                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
+                                       return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
+                                                           p, compat_mode);
 
-                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) {
-                               int len;
-                               len = NBITS(SND_MAX) * sizeof(long);
-                               if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
-                               return copy_to_user(p, dev->snd, len) ? -EFAULT : len;
-                       }
+                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
+                                       return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
+                                                           p, compat_mode);
 
-                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) {
-                               int len;
-                               if (!dev->name) return -ENOENT;
-                               len = strlen(dev->name) + 1;
-                               if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
-                               return copy_to_user(p, dev->name, len) ? -EFAULT : len;
-                       }
+                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
+                                       return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
+                                                           p, compat_mode);
 
-                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) {
-                               int len;
-                               if (!dev->phys) return -ENOENT;
-                               len = strlen(dev->phys) + 1;
-                               if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
-                               return copy_to_user(p, dev->phys, len) ? -EFAULT : len;
-                       }
+                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
+                                       return str_to_user(dev->name, _IOC_SIZE(cmd), p);
 
-                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) {
-                               int len;
-                               if (!dev->uniq) return -ENOENT;
-                               len = strlen(dev->uniq) + 1;
-                               if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
-                               return copy_to_user(p, dev->uniq, len) ? -EFAULT : len;
-                       }
+                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
+                                       return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
 
-                       if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
+                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
+                                       return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
 
-                               int t = _IOC_NR(cmd) & ABS_MAX;
+                               if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
 
-                               abs.value = dev->abs[t];
-                               abs.minimum = dev->absmin[t];
-                               abs.maximum = dev->absmax[t];
-                               abs.fuzz = dev->absfuzz[t];
-                               abs.flat = dev->absflat[t];
+                                       int t = _IOC_NR(cmd) & ABS_MAX;
 
-                               if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
-                                       return -EFAULT;
+                                       abs.value = dev->abs[t];
+                                       abs.minimum = dev->absmin[t];
+                                       abs.maximum = dev->absmax[t];
+                                       abs.fuzz = dev->absfuzz[t];
+                                       abs.flat = dev->absflat[t];
+
+                                       if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
+                                               return -EFAULT;
+
+                                       return 0;
+                               }
 
-                               return 0;
                        }
 
-                       if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
+                       if (_IOC_DIR(cmd) == _IOC_WRITE) {
 
-                               int t = _IOC_NR(cmd) & ABS_MAX;
+                               if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
 
-                               if (copy_from_user(&abs, p, sizeof(struct input_absinfo)))
-                                       return -EFAULT;
+                                       int t = _IOC_NR(cmd) & ABS_MAX;
 
-                               dev->abs[t] = abs.value;
-                               dev->absmin[t] = abs.minimum;
-                               dev->absmax[t] = abs.maximum;
-                               dev->absfuzz[t] = abs.fuzz;
-                               dev->absflat[t] = abs.flat;
+                                       if (copy_from_user(&abs, p, sizeof(struct input_absinfo)))
+                                               return -EFAULT;
 
-                               return 0;
+                                       dev->abs[t] = abs.value;
+                                       dev->absmin[t] = abs.minimum;
+                                       dev->absmax[t] = abs.maximum;
+                                       dev->absfuzz[t] = abs.fuzz;
+                                       dev->absflat[t] = abs.flat;
+
+                                       return 0;
+                               }
                        }
        }
        return -EINVAL;
 }
 
+static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
+}
+
+#ifdef CONFIG_COMPAT
+static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
+}
+#endif
+
 static struct file_operations evdev_fops = {
        .owner =        THIS_MODULE,
        .read =         evdev_read,
@@ -396,7 +609,10 @@ static struct file_operations evdev_fops = {
        .poll =         evdev_poll,
        .open =         evdev_open,
        .release =      evdev_release,
-       .ioctl =        evdev_ioctl,
+       .unlocked_ioctl = evdev_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = evdev_ioctl_compat,
+#endif
        .fasync =       evdev_fasync,
        .flush =        evdev_flush
 };
@@ -404,6 +620,7 @@ static struct file_operations evdev_fops = {
 static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
 {
        struct evdev *evdev;
+       struct class_device *cdev;
        int minor;
 
        for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
@@ -412,9 +629,8 @@ static struct input_handle *evdev_connect(struct input_handler *handler, struct
                return NULL;
        }
 
-       if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL)))
+       if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)))
                return NULL;
-       memset(evdev, 0, sizeof(struct evdev));
 
        INIT_LIST_HEAD(&evdev->list);
        init_waitqueue_head(&evdev->wait);
@@ -429,11 +645,13 @@ static struct input_handle *evdev_connect(struct input_handler *handler, struct
 
        evdev_table[minor] = evdev;
 
-       devfs_mk_cdev(MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
-                       S_IFCHR|S_IRUGO|S_IWUSR, "input/event%d", minor);
-       class_simple_device_add(input_class,
-                               MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
-                               dev->dev, "event%d", minor);
+       cdev = class_device_create(&input_class, &dev->cdev,
+                       MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
+                       dev->cdev.dev, evdev->name);
+
+       /* temporary symlink to keep userspace happy */
+       sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj,
+                         evdev->name);
 
        return &evdev->handle;
 }
@@ -443,8 +661,9 @@ static void evdev_disconnect(struct input_handle *handle)
        struct evdev *evdev = handle->private;
        struct evdev_list *list;
 
-       class_simple_device_remove(MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + evdev->minor));
-       devfs_remove("input/event%d", evdev->minor);
+       sysfs_remove_link(&input_class.subsys.kset.kobj, evdev->name);
+       class_device_destroy(&input_class,
+                       MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + evdev->minor));
        evdev->exist = 0;
 
        if (evdev->open) {