981 lines · 22.6 KB · C
RawDownload
	len = min(len, sizeof(joydev->keypam)); 	/* Validate the map. */	keypam = kmalloc(len, GFP_KERNEL);	if (!keypam)		return -ENOMEM; 	if (copy_from_user(keypam, argp, len)) {		retval = -EFAULT;		goto out;	} 	for (i = 0; i < joydev->nkey; i++) {		if (keypam[i] > KEY_MAX || keypam[i] < BTN_MISC) {			retval = -EINVAL;			goto out;		}	} 	memcpy(joydev->keypam, keypam, len); 	for (i = 0; i < joydev->nkey; i++)		joydev->keymap[keypam[i] - BTN_MISC] = i;  out:	kfree(keypam);	return retval;}  static int joydev_ioctl_common(struct joydev *joydev,				unsigned int cmd, void __user *argp){	struct input_dev *dev = joydev->handle.dev;	size_t len;	int i;	const char *name; 	/* Process fixed-sized commands. */	switch (cmd) { 	case JS_SET_CAL:		return copy_from_user(&joydev->glue.JS_CORR, argp,				sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; 	case JS_GET_CAL:		return copy_to_user(argp, &joydev->glue.JS_CORR,				sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; 	case JS_SET_TIMEOUT:		return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); 	case JS_GET_TIMEOUT:		return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); 	case JSIOCGVERSION:		return put_user(JS_VERSION, (__u32 __user *) argp); 	case JSIOCGAXES:		return put_user(joydev->nabs, (__u8 __user *) argp); 	case JSIOCGBUTTONS:		return put_user(joydev->nkey, (__u8 __user *) argp); 	case JSIOCSCORR:		if (copy_from_user(joydev->corr, argp,			      sizeof(joydev->corr[0]) * joydev->nabs))			return -EFAULT; 		for (i = 0; i < joydev->nabs; i++) {			int val = input_abs_get_val(dev, joydev->abspam[i]);			joydev->abs[i] = joydev_correct(val, &joydev->corr[i]);		}		return 0; 	case JSIOCGCORR:		return copy_to_user(argp, joydev->corr,			sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0; 	} 	/*	 * Process variable-sized commands (the axis and button map commands	 * are considered variable-sized to decouple them from the values of	 * ABS_MAX and KEY_MAX).	 */	switch (cmd & ~IOCSIZE_MASK) { 	case (JSIOCSAXMAP & ~IOCSIZE_MASK):		return joydev_handle_JSIOCSAXMAP(joydev, argp, _IOC_SIZE(cmd)); 	case (JSIOCGAXMAP & ~IOCSIZE_MASK):		len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->abspam));		return copy_to_user(argp, joydev->abspam, len) ? -EFAULT : len; 	case (JSIOCSBTNMAP & ~IOCSIZE_MASK):		return joydev_handle_JSIOCSBTNMAP(joydev, argp, _IOC_SIZE(cmd)); 	case (JSIOCGBTNMAP & ~IOCSIZE_MASK):		len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->keypam));		return copy_to_user(argp, joydev->keypam, len) ? -EFAULT : len; 	case JSIOCGNAME(0):		name = dev->name;		if (!name)			return 0; 		len = min_t(size_t, _IOC_SIZE(cmd), strlen(name) + 1);		return copy_to_user(argp, name, len) ? -EFAULT : len;	} 	return -EINVAL;} #ifdef CONFIG_COMPATstatic long joydev_compat_ioctl(struct file *file,				unsigned int cmd, unsigned long arg){	struct joydev_client *client = file->private_data;	struct joydev *joydev = client->joydev;	void __user *argp = (void __user *)arg;	s32 tmp32;	struct JS_DATA_SAVE_TYPE_32 ds32;	int retval; 	retval = mutex_lock_interruptible(&joydev->mutex);	if (retval)		return retval; 	if (!joydev->exist) {		retval = -ENODEV;		goto out;	} 	switch (cmd) { 	case JS_SET_TIMELIMIT:		retval = get_user(tmp32, (s32 __user *) arg);		if (retval == 0)			joydev->glue.JS_TIMELIMIT = tmp32;		break; 	case JS_GET_TIMELIMIT:		tmp32 = joydev->glue.JS_TIMELIMIT;		retval = put_user(tmp32, (s32 __user *) arg);		break; 	case JS_SET_ALL:		retval = copy_from_user(&ds32, argp,					sizeof(ds32)) ? -EFAULT : 0;		if (retval == 0) {			joydev->glue.JS_TIMEOUT    = ds32.JS_TIMEOUT;			joydev->glue.BUSY          = ds32.BUSY;			joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME;			joydev->glue.JS_TIMELIMIT  = ds32.JS_TIMELIMIT;			joydev->glue.JS_SAVE       = ds32.JS_SAVE;			joydev->glue.JS_CORR       = ds32.JS_CORR;		}		break; 	case JS_GET_ALL:		ds32.JS_TIMEOUT    = joydev->glue.JS_TIMEOUT;		ds32.BUSY          = joydev->glue.BUSY;		ds32.JS_EXPIRETIME = joydev->glue.JS_EXPIRETIME;		ds32.JS_TIMELIMIT  = joydev->glue.JS_TIMELIMIT;		ds32.JS_SAVE       = joydev->glue.JS_SAVE;		ds32.JS_CORR       = joydev->glue.JS_CORR; 		retval = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0;		break; 	default:		retval = joydev_ioctl_common(joydev, cmd, argp);		break;	}  out:	mutex_unlock(&joydev->mutex);	return retval;}#endif /* CONFIG_COMPAT */ static long joydev_ioctl(struct file *file,			 unsigned int cmd, unsigned long arg){	struct joydev_client *client = file->private_data;	struct joydev *joydev = client->joydev;	void __user *argp = (void __user *)arg;	int retval; 	retval = mutex_lock_interruptible(&joydev->mutex);	if (retval)		return retval; 	if (!joydev->exist) {		retval = -ENODEV;		goto out;	} 	switch (cmd) { 	case JS_SET_TIMELIMIT:		retval = get_user(joydev->glue.JS_TIMELIMIT,				  (long __user *) arg);		break; 	case JS_GET_TIMELIMIT:		retval = put_user(joydev->glue.JS_TIMELIMIT,				  (long __user *) arg);		break; 	case JS_SET_ALL:		retval = copy_from_user(&joydev->glue, argp,					sizeof(joydev->glue)) ? -EFAULT: 0;		break; 	case JS_GET_ALL:		retval = copy_to_user(argp, &joydev->glue,				      sizeof(joydev->glue)) ? -EFAULT : 0;		break; 	default:		retval = joydev_ioctl_common(joydev, cmd, argp);		break;	} out:	mutex_unlock(&joydev->mutex);	return retval;} static const struct file_operations joydev_fops = {	.owner		= THIS_MODULE,	.read		= joydev_read,	.poll		= joydev_poll,	.open		= joydev_open,	.release	= joydev_release,	.unlocked_ioctl	= joydev_ioctl,#ifdef CONFIG_COMPAT	.compat_ioctl	= joydev_compat_ioctl,#endif	.fasync		= joydev_fasync,	.llseek		= no_llseek,}; static int joydev_install_chrdev(struct joydev *joydev){	joydev_table[joydev->minor] = joydev;	return 0;} static void joydev_remove_chrdev(struct joydev *joydev){	mutex_lock(&joydev_table_mutex);	joydev_table[joydev->minor] = NULL;	mutex_unlock(&joydev_table_mutex);} /* * 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 joydev_mark_dead(struct joydev *joydev){	mutex_lock(&joydev->mutex);	joydev->exist = false;	mutex_unlock(&joydev->mutex);} static void joydev_cleanup(struct joydev *joydev){	struct input_handle *handle = &joydev->handle; 	joydev_mark_dead(joydev);	joydev_hangup(joydev);	joydev_remove_chrdev(joydev); 	/* joydev is marked dead so no one else accesses joydev->open */	if (joydev->open)		input_close_device(handle);}  static bool joydev_match(struct input_handler *handler, struct input_dev *dev){	/* Avoid touchpads and touchscreens */	if (test_bit(EV_KEY, dev->evbit) && test_bit(BTN_TOUCH, dev->keybit))		return false; 	/* Avoid tablets, digitisers and similar devices */	if (test_bit(EV_KEY, dev->evbit) && test_bit(BTN_DIGI, dev->keybit))		return false; 	return true;} static int joydev_connect(struct input_handler *handler, struct input_dev *dev,			  const struct input_device_id *id){	struct joydev *joydev;	int i, j, t, minor;	int error; 	for (minor = 0; minor < JOYDEV_MINORS; minor++)		if (!joydev_table[minor])			break; 	if (minor == JOYDEV_MINORS) {		pr_err("no more free joydev devices\n");		return -ENFILE;	} 	joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL);	if (!joydev)		return -ENOMEM; 	INIT_LIST_HEAD(&joydev->client_list);	spin_lock_init(&joydev->client_lock);	mutex_init(&joydev->mutex);	init_waitqueue_head(&joydev->wait); 	dev_set_name(&joydev->dev, "js%d", minor);	joydev->exist = true;	joydev->minor = minor; 	joydev->handle.dev = input_get_device(dev);	joydev->handle.name = dev_name(&joydev->dev);	joydev->handle.handler = handler;	joydev->handle.private = joydev; 	for (i = 0; i < ABS_CNT; i++)		if (test_bit(i, dev->absbit)) {			joydev->absmap[i] = joydev->nabs;			joydev->abspam[joydev->nabs] = i;			joydev->nabs++;		} 	for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC + 1; i++)		if (test_bit(i + BTN_MISC, dev->keybit)) {			joydev->keymap[i] = joydev->nkey;			joydev->keypam[joydev->nkey] = i + BTN_MISC;			joydev->nkey++;		} 	for (i = 0; i < BTN_JOYSTICK - BTN_MISC; i++)		if (test_bit(i + BTN_MISC, dev->keybit)) {			joydev->keymap[i] = joydev->nkey;			joydev->keypam[joydev->nkey] = i + BTN_MISC;			joydev->nkey++;		} 	for (i = 0; i < joydev->nabs; i++) {		j = joydev->abspam[i];		if (input_abs_get_max(dev, j) == input_abs_get_min(dev, j)) {			joydev->corr[i].type = JS_CORR_NONE;			joydev->abs[i] = input_abs_get_val(dev, j);			continue;		}		joydev->corr[i].type = JS_CORR_BROKEN;		joydev->corr[i].prec = input_abs_get_fuzz(dev, j); 		t = (input_abs_get_max(dev, j) + input_abs_get_min(dev, j)) / 2;		joydev->corr[i].coef[0] = t - input_abs_get_flat(dev, j);		joydev->corr[i].coef[1] = t + input_abs_get_flat(dev, j); 		t = (input_abs_get_max(dev, j) - input_abs_get_min(dev, j)) / 2			- 2 * input_abs_get_flat(dev, j);		if (t) {			joydev->corr[i].coef[2] = (1 << 29) / t;			joydev->corr[i].coef[3] = (1 << 29) / t; 			joydev->abs[i] =				joydev_correct(input_abs_get_val(dev, j),					       joydev->corr + i);		}	} 	joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor);	joydev->dev.class = &input_class;	joydev->dev.parent = &dev->dev;	joydev->dev.release = joydev_free;	device_initialize(&joydev->dev); 	error = input_register_handle(&joydev->handle);	if (error)		goto err_free_joydev; 	error = joydev_install_chrdev(joydev);	if (error)		goto err_unregister_handle; 	error = device_add(&joydev->dev);	if (error)		goto err_cleanup_joydev; 	return 0;  err_cleanup_joydev:	joydev_cleanup(joydev); err_unregister_handle:	input_unregister_handle(&joydev->handle); err_free_joydev:	put_device(&joydev->dev);	return error;} static void joydev_disconnect(struct input_handle *handle){	struct joydev *joydev = handle->private; 	device_del(&joydev->dev);	joydev_cleanup(joydev);	input_unregister_handle(handle);	put_device(&joydev->dev);} static const struct input_device_id joydev_ids[] = {	{		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |				INPUT_DEVICE_ID_MATCH_ABSBIT,		.evbit = { BIT_MASK(EV_ABS) },		.absbit = { BIT_MASK(ABS_X) },	},	{		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |				INPUT_DEVICE_ID_MATCH_ABSBIT,		.evbit = { BIT_MASK(EV_ABS) },		.absbit = { BIT_MASK(ABS_WHEEL) },	},	{		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |				INPUT_DEVICE_ID_MATCH_ABSBIT,		.evbit = { BIT_MASK(EV_ABS) },		.absbit = { BIT_MASK(ABS_THROTTLE) },	},	{		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |				INPUT_DEVICE_ID_MATCH_KEYBIT,		.evbit = { BIT_MASK(EV_KEY) },		.keybit = {[BIT_WORD(BTN_JOYSTICK)] = BIT_MASK(BTN_JOYSTICK) },	},	{		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |				INPUT_DEVICE_ID_MATCH_KEYBIT,		.evbit = { BIT_MASK(EV_KEY) },		.keybit = { [BIT_WORD(BTN_GAMEPAD)] = BIT_MASK(BTN_GAMEPAD) },	},	{		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |				INPUT_DEVICE_ID_MATCH_KEYBIT,		.evbit = { BIT_MASK(EV_KEY) },		.keybit = { [BIT_WORD(BTN_TRIGGER_HAPPY)] = BIT_MASK(BTN_TRIGGER_HAPPY) },	},	{ }	/* Terminating entry */}; MODULE_DEVICE_TABLE(input, joydev_ids); static struct input_handler joydev_handler = {	.event		= joydev_event,	.match		= joydev_match,	.connect	= joydev_connect,	.disconnect	= joydev_disconnect,	.fops		= &joydev_fops,	.minor		= JOYDEV_MINOR_BASE,	.name		= "joydev",	.id_table	= joydev_ids,}; static int __init joydev_init(void){	return input_register_handler(&joydev_handler);} static void __exit joydev_exit(void){	input_unregister_handler(&joydev_handler);} module_init(joydev_init);module_exit(joydev_exit);

Lines 501–981 of 981. 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