1446 lines · 32.4 KB · C
RawDownload
	cnt = evdev_get_mask_cnt(type);	size = sizeof(unsigned long) * BITS_TO_LONGS(cnt);	xfer_size = min_t(size_t, codes_size, size); 	if (cnt > 0) {		mask = client->evmasks[type];		if (mask) {			error = bits_to_user(mask, cnt - 1,					     xfer_size, codes, compat);			if (error < 0)				return error;		} else {			/* fake mask with all bits set */			for (i = 0; i < xfer_size; i++)				if (put_user(0xffU, (u8 __user *)codes + i))					return -EFAULT;		}	} 	if (xfer_size < codes_size)		if (clear_user(codes + xfer_size, codes_size - xfer_size))			return -EFAULT; 	return 0;} 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;} static long evdev_ioctl_handler(struct file *file, unsigned int cmd,				void __user *p, int compat_mode){	struct evdev_client *client = file->private_data;	struct evdev *evdev = client->evdev;	int retval; 	retval = mutex_lock_interruptible(&evdev->mutex);	if (retval)		return retval; 	if (!evdev->exist || client->revoked) {		retval = -ENODEV;		goto out;	} 	retval = evdev_do_ioctl(file, cmd, p, compat_mode);  out:	mutex_unlock(&evdev->mutex);	return retval;} 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_COMPATstatic 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 const struct file_operations evdev_fops = {	.owner		= THIS_MODULE,	.read		= evdev_read,	.write		= evdev_write,	.poll		= evdev_poll,	.open		= evdev_open,	.release	= evdev_release,	.unlocked_ioctl	= evdev_ioctl,#ifdef CONFIG_COMPAT	.compat_ioctl	= evdev_ioctl_compat,#endif	.fasync		= evdev_fasync,}; /* * Mark device non-existent. This disables writes, ioctls and * prevents new users from opening the device. Already posted * blocking reads will stay, however new ones will fail. */static void evdev_mark_dead(struct evdev *evdev){	mutex_lock(&evdev->mutex);	evdev->exist = false;	mutex_unlock(&evdev->mutex);} static void evdev_cleanup(struct evdev *evdev){	struct input_handle *handle = &evdev->handle; 	evdev_mark_dead(evdev);	evdev_hangup(evdev); 	/* evdev is marked dead so no one else accesses evdev->open */	if (evdev->open) {		input_flush_device(handle, NULL);		input_close_device(handle);	}} /* * Create new evdev device. Note that input core serializes calls * to connect and disconnect. */static int evdev_connect(struct input_handler *handler, struct input_dev *dev,			 const struct input_device_id *id){	struct evdev *evdev;	int minor;	int dev_no;	int error; 	minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);	if (minor < 0) {		error = minor;		pr_err("failed to reserve new minor: %d\n", error);		return error;	} 	evdev = kzalloc_obj(struct evdev);	if (!evdev) {		error = -ENOMEM;		goto err_free_minor;	} 	INIT_LIST_HEAD(&evdev->client_list);	spin_lock_init(&evdev->client_lock);	mutex_init(&evdev->mutex);	evdev->exist = true; 	dev_no = minor;	/* Normalize device number if it falls into legacy range */	if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)		dev_no -= EVDEV_MINOR_BASE;	dev_set_name(&evdev->dev, "event%d", dev_no); 	evdev->handle.dev = input_get_device(dev);	evdev->handle.name = dev_name(&evdev->dev);	evdev->handle.handler = handler;	evdev->handle.private = evdev; 	evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);	evdev->dev.class = &input_class;	evdev->dev.parent = &dev->dev;	evdev->dev.release = evdev_free;	device_initialize(&evdev->dev); 	error = input_register_handle(&evdev->handle);	if (error)		goto err_free_evdev; 	cdev_init(&evdev->cdev, &evdev_fops); 	error = cdev_device_add(&evdev->cdev, &evdev->dev);	if (error)		goto err_cleanup_evdev; 	return 0;  err_cleanup_evdev:	evdev_cleanup(evdev);	input_unregister_handle(&evdev->handle); err_free_evdev:	put_device(&evdev->dev); err_free_minor:	input_free_minor(minor);	return error;} static void evdev_disconnect(struct input_handle *handle){	struct evdev *evdev = handle->private; 	cdev_device_del(&evdev->cdev, &evdev->dev);	evdev_cleanup(evdev);	input_free_minor(MINOR(evdev->dev.devt));	input_unregister_handle(handle);	put_device(&evdev->dev);} static const struct input_device_id evdev_ids[] = {	{		/* Matches all devices */		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,		.evbit = { BIT_MASK(EV_SYN) },	},	{ }	/* Terminating zero entry */}; MODULE_DEVICE_TABLE(input, evdev_ids); static struct input_handler evdev_handler = {	.events		= evdev_events,	.connect	= evdev_connect,	.disconnect	= evdev_disconnect,	.legacy_minors	= true,	.minor		= EVDEV_MINOR_BASE,	.name		= "evdev",	.id_table	= evdev_ids,}; static int __init evdev_init(void){	return input_register_handler(&evdev_handler);} static void __exit evdev_exit(void){	input_unregister_handler(&evdev_handler);} module_init(evdev_init);module_exit(evdev_exit); MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");MODULE_DESCRIPTION("Input driver event char devices");MODULE_LICENSE("GPL");

Lines 1001–1446 of 1446. Previous lines

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t