forked from Minki/linux
Input: evdev - rearrange ioctl handling
Split ioctl handling into 3 separate sections: fixed-length ioctls, variable-length ioctls and multi-number variable length handlers. This reduces identation and makes the code a bit clearer. Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
This commit is contained in:
parent
d31b2865a4
commit
448cd1664a
@ -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,101 +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:
|
||||
size = _IOC_SIZE(cmd);
|
||||
|
||||
if (_IOC_TYPE(cmd) != 'E')
|
||||
return -EINVAL;
|
||||
/* Now check variable-length commands */
|
||||
#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
|
||||
|
||||
if (_IOC_DIR(cmd) == _IOC_READ) {
|
||||
switch (EVIOC_MASK_SIZE(cmd)) {
|
||||
|
||||
if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
|
||||
return handle_eviocgbit(dev, 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(EVIOCGKEY(0)))
|
||||
return bits_to_user(dev->key, KEY_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(EVIOCGLED(0)))
|
||||
return bits_to_user(dev->led, LED_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(EVIOCGSND(0)))
|
||||
return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
|
||||
p, compat_mode);
|
||||
case EVIOCGSW(0):
|
||||
return bits_to_user(dev->sw, SW_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 EVIOCGNAME(0):
|
||||
return str_to_user(dev->name, size, p);
|
||||
|
||||
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
|
||||
return str_to_user(dev->name, _IOC_SIZE(cmd), p);
|
||||
case EVIOCGPHYS(0):
|
||||
return str_to_user(dev->phys, size, p);
|
||||
|
||||
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
|
||||
return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
|
||||
case EVIOCGUNIQ(0):
|
||||
return str_to_user(dev->uniq, size, p);
|
||||
|
||||
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
|
||||
return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
|
||||
case EVIOC_MASK_SIZE(EVIOCSFF):
|
||||
if (input_ff_effect_from_user(p, size, &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,
|
||||
_IOC_SIZE(cmd),
|
||||
sizeof(struct input_absinfo))))
|
||||
return -EFAULT;
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* Multi-number variable-length handlers */
|
||||
if (_IOC_TYPE(cmd) != 'E')
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
if (_IOC_DIR(cmd) == _IOC_READ) {
|
||||
|
||||
if (_IOC_DIR(cmd) == _IOC_WRITE) {
|
||||
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 (_IOC_NR(cmd) == _IOC_NR(EVIOCSFF)) {
|
||||
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
|
||||
|
||||
if (input_ff_effect_from_user(p, _IOC_SIZE(cmd), &effect))
|
||||
return -EFAULT;
|
||||
t = _IOC_NR(cmd) & ABS_MAX;
|
||||
abs = dev->absinfo[t];
|
||||
|
||||
error = input_ff_upload(dev, &effect, file);
|
||||
if (copy_to_user(p, &abs, min_t(size_t,
|
||||
size, sizeof(struct input_absinfo))))
|
||||
return -EFAULT;
|
||||
|
||||
if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
|
||||
return -EFAULT;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
|
||||
|
||||
t = _IOC_NR(cmd) & ABS_MAX;
|
||||
|
||||
if (copy_from_user(&abs, p, min_t(size_t,
|
||||
_IOC_SIZE(cmd),
|
||||
sizeof(struct input_absinfo))))
|
||||
return -EFAULT;
|
||||
|
||||
if (_IOC_SIZE(cmd) < sizeof(struct input_absinfo))
|
||||
abs.resolution = 0;
|
||||
|
||||
/* We can't change number of reserved MT slots */
|
||||
if (t == ABS_MT_SLOT)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (_IOC_DIR(cmd) == _IOC_READ) {
|
||||
|
||||
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
|
||||
|
||||
t = _IOC_NR(cmd) & ABS_MAX;
|
||||
|
||||
if (copy_from_user(&abs, p, min_t(size_t,
|
||||
size, sizeof(struct input_absinfo))))
|
||||
return -EFAULT;
|
||||
|
||||
if (size < sizeof(struct input_absinfo))
|
||||
abs.resolution = 0;
|
||||
|
||||
/* We can't change number of reserved MT slots */
|
||||
if (t == ABS_MT_SLOT)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* 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 -EINVAL;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user