in evdev.c [1029:1254]
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_dev *dev = evdev->handle.dev;
struct input_absinfo abs;
struct input_mask mask;
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:
return put_user(EV_VERSION, ip);
case EVIOCGID:
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_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
return 0;
case EVIOCRMFF:
return input_ff_erase(dev, (int)(unsigned long) p, file);
case EVIOCGEFFECTS:
i = test_bit(EV_FF, dev->evbit) ?
dev->ff->max_effects : 0;
if (put_user(i, ip))
return -EFAULT;
return 0;
case EVIOCGRAB:
if (p)
return evdev_grab(evdev, client);
else
return evdev_ungrab(evdev, client);
case EVIOCREVOKE:
if (p)
return -EINVAL;
else
return evdev_revoke(evdev, client, file);
case EVIOCGMASK: {
void __user *codes_ptr;
if (copy_from_user(&mask, p, sizeof(mask)))
return -EFAULT;
codes_ptr = (void __user *)(unsigned long)mask.codes_ptr;
return evdev_get_mask(client,
mask.type, codes_ptr, mask.codes_size,
compat_mode);
}
case EVIOCSMASK: {
const void __user *codes_ptr;
if (copy_from_user(&mask, p, sizeof(mask)))
return -EFAULT;
codes_ptr = (const void __user *)(unsigned long)mask.codes_ptr;
return evdev_set_mask(client,
mask.type, codes_ptr, mask.codes_size,
compat_mode);
}
case EVIOCSCLOCKID:
if (copy_from_user(&i, p, sizeof(unsigned int)))
return -EFAULT;
return evdev_set_clk_type(client, i);
case EVIOCGKEYCODE:
return evdev_handle_get_keycode(dev, p);
case EVIOCSKEYCODE:
return evdev_handle_set_keycode(dev, p);
case EVIOCGKEYCODE_V2:
return evdev_handle_get_keycode_v2(dev, p);
case EVIOCSKEYCODE_V2:
return evdev_handle_set_keycode_v2(dev, p);
}
size = _IOC_SIZE(cmd);
/* Now check variable-length commands */
#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
switch (EVIOC_MASK_SIZE(cmd)) {
case EVIOCGPROP(0):
return bits_to_user(dev->propbit, INPUT_PROP_MAX,
size, p, compat_mode);
case EVIOCGMTSLOTS(0):
return evdev_handle_mt_request(dev, size, ip);
case EVIOCGKEY(0):
return evdev_handle_get_val(client, dev, EV_KEY, dev->key,
KEY_MAX, size, p, compat_mode);
case EVIOCGLED(0):
return evdev_handle_get_val(client, dev, EV_LED, dev->led,
LED_MAX, size, p, compat_mode);
case EVIOCGSND(0):
return evdev_handle_get_val(client, dev, EV_SND, dev->snd,
SND_MAX, size, p, compat_mode);
case EVIOCGSW(0):
return evdev_handle_get_val(client, dev, EV_SW, dev->sw,
SW_MAX, size, p, compat_mode);
case EVIOCGNAME(0):
return str_to_user(dev->name, size, p);
case EVIOCGPHYS(0):
return str_to_user(dev->phys, size, p);
case EVIOCGUNIQ(0):
return str_to_user(dev->uniq, size, p);
case EVIOC_MASK_SIZE(EVIOCSFF):
if (input_ff_effect_from_user(p, size, &effect))
return -EFAULT;
error = input_ff_upload(dev, &effect, file);
if (error)
return error;
if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
return -EFAULT;
return 0;
}
/* Multi-number variable-length handlers */
if (_IOC_TYPE(cmd) != 'E')
return -EINVAL;
if (_IOC_DIR(cmd) == _IOC_READ) {
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) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
if (!dev->absinfo)
return -EINVAL;
t = _IOC_NR(cmd) & ABS_MAX;
abs = dev->absinfo[t];
if (copy_to_user(p, &abs, min_t(size_t,
size, sizeof(struct input_absinfo))))
return -EFAULT;
return 0;
}
}
if (_IOC_DIR(cmd) == _IOC_WRITE) {
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
if (!dev->absinfo)
return -EINVAL;
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;
}