|
@@ -492,13 +492,15 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
|
|
|
}
|
|
|
|
|
|
#define OLD_KEY_MAX 0x1ff
|
|
|
-static int handle_eviocgbit(struct input_dev *dev, unsigned int cmd, void __user *p, int compat_mode)
|
|
|
+static int handle_eviocgbit(struct input_dev *dev,
|
|
|
+ unsigned int type, unsigned int size,
|
|
|
+ void __user *p, int compat_mode)
|
|
|
{
|
|
|
static unsigned long keymax_warn_time;
|
|
|
unsigned long *bits;
|
|
|
int len;
|
|
|
|
|
|
- switch (_IOC_NR(cmd) & EV_MAX) {
|
|
|
+ switch (type) {
|
|
|
|
|
|
case 0: bits = dev->evbit; len = EV_MAX; break;
|
|
|
case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
|
|
@@ -517,7 +519,7 @@ static int handle_eviocgbit(struct input_dev *dev, unsigned int cmd, void __user
|
|
|
* EVIOCGBIT(EV_KEY, KEY_MAX) and not realize that 'len'
|
|
|
* should be in bytes, not in bits.
|
|
|
*/
|
|
|
- if ((_IOC_NR(cmd) & EV_MAX) == EV_KEY && _IOC_SIZE(cmd) == OLD_KEY_MAX) {
|
|
|
+ if (type == EV_KEY && size == OLD_KEY_MAX) {
|
|
|
len = OLD_KEY_MAX;
|
|
|
if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000))
|
|
|
printk(KERN_WARNING
|
|
@@ -528,7 +530,7 @@ static int handle_eviocgbit(struct input_dev *dev, unsigned int cmd, void __user
|
|
|
BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long));
|
|
|
}
|
|
|
|
|
|
- return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
|
|
|
+ return bits_to_user(bits, len, size, p, compat_mode);
|
|
|
}
|
|
|
#undef OLD_KEY_MAX
|
|
|
|
|
@@ -542,8 +544,10 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
|
|
struct ff_effect effect;
|
|
|
int __user *ip = (int __user *)p;
|
|
|
unsigned int i, t, u, v;
|
|
|
+ unsigned int size;
|
|
|
int error;
|
|
|
|
|
|
+ /* First we check for fixed-length commands */
|
|
|
switch (cmd) {
|
|
|
|
|
|
case EVIOCGVERSION:
|
|
@@ -610,112 +614,102 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
|
|
return evdev_grab(evdev, client);
|
|
|
else
|
|
|
return evdev_ungrab(evdev, client);
|
|
|
+ }
|
|
|
|
|
|
- default:
|
|
|
-
|
|
|
- if (_IOC_TYPE(cmd) != 'E')
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- if (_IOC_DIR(cmd) == _IOC_READ) {
|
|
|
+ size = _IOC_SIZE(cmd);
|
|
|
|
|
|
- if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
|
|
|
- return handle_eviocgbit(dev, cmd, p, compat_mode);
|
|
|
+ /* Now check variable-length commands */
|
|
|
+#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
|
|
|
|
|
|
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
|
|
|
- return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
|
|
|
- p, compat_mode);
|
|
|
+ switch (EVIOC_MASK_SIZE(cmd)) {
|
|
|
|
|
|
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
|
|
|
- return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
|
|
|
- p, compat_mode);
|
|
|
+ case EVIOCGKEY(0):
|
|
|
+ return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode);
|
|
|
|
|
|
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
|
|
|
- return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
|
|
|
- p, compat_mode);
|
|
|
+ case EVIOCGLED(0):
|
|
|
+ return bits_to_user(dev->led, LED_MAX, size, p, compat_mode);
|
|
|
|
|
|
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
|
|
|
- return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
|
|
|
- p, compat_mode);
|
|
|
+ case EVIOCGSND(0):
|
|
|
+ return bits_to_user(dev->snd, SND_MAX, size, p, compat_mode);
|
|
|
|
|
|
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
|
|
|
- return str_to_user(dev->name, _IOC_SIZE(cmd), p);
|
|
|
+ case EVIOCGSW(0):
|
|
|
+ return bits_to_user(dev->sw, SW_MAX, size, p, compat_mode);
|
|
|
|
|
|
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
|
|
|
- return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
|
|
|
+ case EVIOCGNAME(0):
|
|
|
+ return str_to_user(dev->name, size, p);
|
|
|
|
|
|
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
|
|
|
- return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
|
|
|
+ case EVIOCGPHYS(0):
|
|
|
+ return str_to_user(dev->phys, size, p);
|
|
|
|
|
|
- if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
|
|
|
+ case EVIOCGUNIQ(0):
|
|
|
+ return str_to_user(dev->uniq, size, p);
|
|
|
|
|
|
- t = _IOC_NR(cmd) & ABS_MAX;
|
|
|
+ case EVIOC_MASK_SIZE(EVIOCSFF):
|
|
|
+ if (input_ff_effect_from_user(p, size, &effect))
|
|
|
+ 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];
|
|
|
- abs.resolution = dev->absres[t];
|
|
|
+ error = input_ff_upload(dev, &effect, file);
|
|
|
|
|
|
- if (copy_to_user(p, &abs, min_t(size_t,
|
|
|
- _IOC_SIZE(cmd),
|
|
|
- sizeof(struct input_absinfo))))
|
|
|
- return -EFAULT;
|
|
|
+ if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
|
|
|
+ return -EFAULT;
|
|
|
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ return error;
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ /* Multi-number variable-length handlers */
|
|
|
+ if (_IOC_TYPE(cmd) != 'E')
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- if (_IOC_DIR(cmd) == _IOC_WRITE) {
|
|
|
+ if (_IOC_DIR(cmd) == _IOC_READ) {
|
|
|
|
|
|
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCSFF)) {
|
|
|
+ if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
|
|
|
+ return handle_eviocgbit(dev,
|
|
|
+ _IOC_NR(cmd) & EV_MAX, size,
|
|
|
+ p, compat_mode);
|
|
|
|
|
|
- if (input_ff_effect_from_user(p, _IOC_SIZE(cmd), &effect))
|
|
|
- return -EFAULT;
|
|
|
+ if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
|
|
|
|
|
|
- error = input_ff_upload(dev, &effect, file);
|
|
|
+ t = _IOC_NR(cmd) & ABS_MAX;
|
|
|
+ abs = dev->absinfo[t];
|
|
|
|
|
|
- if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
|
|
|
- return -EFAULT;
|
|
|
+ if (copy_to_user(p, &abs, min_t(size_t,
|
|
|
+ size, sizeof(struct input_absinfo))))
|
|
|
+ return -EFAULT;
|
|
|
|
|
|
- return error;
|
|
|
- }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
|
|
|
+ if (_IOC_DIR(cmd) == _IOC_READ) {
|
|
|
|
|
|
- t = _IOC_NR(cmd) & ABS_MAX;
|
|
|
+ if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
|
|
|
|
|
|
- if (copy_from_user(&abs, p, min_t(size_t,
|
|
|
- _IOC_SIZE(cmd),
|
|
|
- sizeof(struct input_absinfo))))
|
|
|
- return -EFAULT;
|
|
|
+ t = _IOC_NR(cmd) & ABS_MAX;
|
|
|
|
|
|
- /* We can't change number of reserved MT slots */
|
|
|
- if (t == ABS_MT_SLOT)
|
|
|
- return -EINVAL;
|
|
|
+ if (copy_from_user(&abs, p, min_t(size_t,
|
|
|
+ size, sizeof(struct input_absinfo))))
|
|
|
+ return -EFAULT;
|
|
|
|
|
|
- /*
|
|
|
- * Take event lock to ensure that we are not
|
|
|
- * changing device parameters in the middle
|
|
|
- * of event.
|
|
|
- */
|
|
|
- spin_lock_irq(&dev->event_lock);
|
|
|
+ if (size < sizeof(struct input_absinfo))
|
|
|
+ abs.resolution = 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;
|
|
|
- dev->absres[t] = _IOC_SIZE(cmd) < sizeof(struct input_absinfo) ?
|
|
|
- 0 : abs.resolution;
|
|
|
+ /* We can't change number of reserved MT slots */
|
|
|
+ if (t == ABS_MT_SLOT)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- spin_unlock_irq(&dev->event_lock);
|
|
|
+ /*
|
|
|
+ * Take event lock to ensure that we are not
|
|
|
+ * changing device parameters in the middle
|
|
|
+ * of event.
|
|
|
+ */
|
|
|
+ spin_lock_irq(&dev->event_lock);
|
|
|
+ dev->absinfo[t] = abs;
|
|
|
+ spin_unlock_irq(&dev->event_lock);
|
|
|
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ return 0;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|