diff options
Diffstat (limited to 'drivers/input/joydev.c')
| -rw-r--r-- | drivers/input/joydev.c | 253 |
1 files changed, 197 insertions, 56 deletions
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index f362883c94e3..ba2b17288bcd 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -1,13 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Joystick device driver for the input driver suite. * * Copyright (c) 1999-2002 Vojtech Pavlik * Copyright (c) 1999 Colin Van Dyke - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -22,7 +18,6 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/mm.h> -#include <linux/miscdevice.h> #include <linux/module.h> #include <linux/poll.h> #include <linux/init.h> @@ -31,7 +26,6 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Joystick device interfaces"); -MODULE_SUPPORTED_DEVICE("input/js"); MODULE_LICENSE("GPL"); #define JOYDEV_MINOR_BASE 0 @@ -88,7 +82,7 @@ static int joydev_correct(int value, struct js_corr *corr) return 0; } - return value < -32767 ? -32767 : (value > 32767 ? 32767 : value); + return clamp(value, -32767, 32767); } static void joydev_pass_event(struct joydev_client *client, @@ -188,6 +182,17 @@ static void joydev_detach_client(struct joydev *joydev, synchronize_rcu(); } +static void joydev_refresh_state(struct joydev *joydev) +{ + struct input_dev *dev = joydev->handle.dev; + int i, val; + + for (i = 0; i < joydev->nabs; i++) { + val = input_abs_get_val(dev, joydev->abspam[i]); + joydev->abs[i] = joydev_correct(val, &joydev->corr[i]); + } +} + static int joydev_open_device(struct joydev *joydev) { int retval; @@ -202,6 +207,8 @@ static int joydev_open_device(struct joydev *joydev) retval = input_open_device(&joydev->handle); if (retval) joydev->open--; + else + joydev_refresh_state(joydev); } mutex_unlock(&joydev->mutex); @@ -267,7 +274,7 @@ static int joydev_open(struct inode *inode, struct file *file) goto err_free_client; file->private_data = client; - nonseekable_open(inode, file); + stream_open(inode, file); return 0; @@ -424,14 +431,14 @@ static ssize_t joydev_read(struct file *file, char __user *buf, } /* No kernel lock - fine */ -static unsigned int joydev_poll(struct file *file, poll_table *wait) +static __poll_t joydev_poll(struct file *file, poll_table *wait) { struct joydev_client *client = file->private_data; struct joydev *joydev = client->joydev; poll_wait(file, &joydev->wait, wait); - return (joydev_data_pending(client) ? (POLLIN | POLLRDNORM) : 0) | - (joydev->exist ? 0 : (POLLHUP | POLLERR)); + return (joydev_data_pending(client) ? (EPOLLIN | EPOLLRDNORM) : 0) | + (joydev->exist ? 0 : (EPOLLHUP | EPOLLERR)); } static int joydev_handle_JSIOCSAXMAP(struct joydev *joydev, @@ -444,16 +451,11 @@ static int joydev_handle_JSIOCSAXMAP(struct joydev *joydev, len = min(len, sizeof(joydev->abspam)); /* Validate the map. */ - abspam = kmalloc(len, GFP_KERNEL); - if (!abspam) - return -ENOMEM; + abspam = memdup_user(argp, len); + if (IS_ERR(abspam)) + return PTR_ERR(abspam); - if (copy_from_user(abspam, argp, len)) { - retval = -EFAULT; - goto out; - } - - for (i = 0; i < joydev->nabs; i++) { + for (i = 0; i < len && i < joydev->nabs; i++) { if (abspam[i] > ABS_MAX) { retval = -EINVAL; goto out; @@ -477,19 +479,17 @@ static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev, int i; int retval = 0; + if (len % sizeof(*keypam)) + return -EINVAL; + 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; - } + keypam = memdup_user(argp, len); + if (IS_ERR(keypam)) + return PTR_ERR(keypam); - for (i = 0; i < joydev->nkey; i++) { + for (i = 0; i < (len / 2) && i < joydev->nkey; i++) { if (keypam[i] > KEY_MAX || keypam[i] < BTN_MISC) { retval = -EINVAL; goto out; @@ -499,7 +499,7 @@ static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev, memcpy(joydev->keypam, keypam, len); for (i = 0; i < joydev->nkey; i++) - joydev->keymap[keypam[i] - BTN_MISC] = i; + joydev->keymap[joydev->keypam[i] - BTN_MISC] = i; out: kfree(keypam); @@ -718,7 +718,6 @@ static const struct file_operations joydev_fops = { .compat_ioctl = joydev_compat_ioctl, #endif .fasync = joydev_fasync, - .llseek = no_llseek, }; /* @@ -740,22 +739,169 @@ static void joydev_cleanup(struct joydev *joydev) joydev_mark_dead(joydev); joydev_hangup(joydev); - cdev_del(&joydev->cdev); - /* joydev is marked dead so no one else accesses joydev->open */ if (joydev->open) input_close_device(handle); } +/* + * These codes are copied from hid-ids.h, unfortunately there is no common + * usb_ids/bt_ids.h header. + */ +#define USB_VENDOR_ID_SONY 0x054c +#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 +#define USB_DEVICE_ID_SONY_PS4_CONTROLLER 0x05c4 +#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 0x09cc +#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE 0x0ba0 + +#define USB_VENDOR_ID_THQ 0x20d6 +#define USB_DEVICE_ID_THQ_PS3_UDRAW 0xcb17 + +#define USB_VENDOR_ID_NINTENDO 0x057e +#define USB_DEVICE_ID_NINTENDO_JOYCONL 0x2006 +#define USB_DEVICE_ID_NINTENDO_JOYCONR 0x2007 +#define USB_DEVICE_ID_NINTENDO_PROCON 0x2009 +#define USB_DEVICE_ID_NINTENDO_CHRGGRIP 0x200E + +#define ACCEL_DEV(vnd, prd) \ + { \ + .flags = INPUT_DEVICE_ID_MATCH_VENDOR | \ + INPUT_DEVICE_ID_MATCH_PRODUCT | \ + INPUT_DEVICE_ID_MATCH_PROPBIT, \ + .vendor = (vnd), \ + .product = (prd), \ + .propbit = { BIT_MASK(INPUT_PROP_ACCELEROMETER) }, \ + } + +static const struct input_device_id joydev_blacklist[] = { + /* Avoid touchpads and touchscreens */ + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + }, + /* Avoid tablets, digitisers and similar devices */ + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(BTN_DIGI)] = BIT_MASK(BTN_DIGI) }, + }, + /* Disable accelerometers on composite devices */ + ACCEL_DEV(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER), + ACCEL_DEV(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), + ACCEL_DEV(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2), + ACCEL_DEV(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE), + ACCEL_DEV(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW), + ACCEL_DEV(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_PROCON), + ACCEL_DEV(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_CHRGGRIP), + ACCEL_DEV(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_JOYCONL), + ACCEL_DEV(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_JOYCONR), + { /* sentinel */ } +}; + +static bool joydev_dev_is_blacklisted(struct input_dev *dev) +{ + const struct input_device_id *id; + + for (id = joydev_blacklist; id->flags; id++) { + if (input_match_device_id(dev, id)) { + dev_dbg(&dev->dev, + "joydev: blacklisting '%s'\n", dev->name); + return true; + } + } + + return false; +} + +static bool joydev_dev_is_absolute_mouse(struct input_dev *dev) +{ + DECLARE_BITMAP(jd_scratch, KEY_CNT); + bool ev_match = false; + + BUILD_BUG_ON(ABS_CNT > KEY_CNT || EV_CNT > KEY_CNT); + + /* + * Virtualization (VMware, etc) and remote management (HP + * ILO2) solutions use absolute coordinates for their virtual + * pointing devices so that there is one-to-one relationship + * between pointer position on the host screen and virtual + * guest screen, and so their mice use ABS_X, ABS_Y and 3 + * primary button events. This clashes with what joydev + * considers to be joysticks (a device with at minimum ABS_X + * axis). + * + * Here we are trying to separate absolute mice from + * joysticks. A device is, for joystick detection purposes, + * considered to be an absolute mouse if the following is + * true: + * + * 1) Event types are exactly + * EV_ABS, EV_KEY and EV_SYN + * or + * EV_ABS, EV_KEY, EV_SYN and EV_MSC + * or + * EV_ABS, EV_KEY, EV_SYN, EV_MSC and EV_REL. + * 2) Absolute events are exactly ABS_X and ABS_Y. + * 3) Keys are exactly BTN_LEFT, BTN_RIGHT and BTN_MIDDLE. + * 4) Device is not on "Amiga" bus. + */ + + bitmap_zero(jd_scratch, EV_CNT); + /* VMware VMMouse, HP ILO2 */ + __set_bit(EV_ABS, jd_scratch); + __set_bit(EV_KEY, jd_scratch); + __set_bit(EV_SYN, jd_scratch); + if (bitmap_equal(jd_scratch, dev->evbit, EV_CNT)) + ev_match = true; + + /* HP ILO2, AMI BMC firmware */ + __set_bit(EV_MSC, jd_scratch); + if (bitmap_equal(jd_scratch, dev->evbit, EV_CNT)) + ev_match = true; + + /* VMware Virtual USB Mouse, QEMU USB Tablet, ATEN BMC firmware */ + __set_bit(EV_REL, jd_scratch); + if (bitmap_equal(jd_scratch, dev->evbit, EV_CNT)) + ev_match = true; + + if (!ev_match) + return false; + + bitmap_zero(jd_scratch, ABS_CNT); + __set_bit(ABS_X, jd_scratch); + __set_bit(ABS_Y, jd_scratch); + if (!bitmap_equal(dev->absbit, jd_scratch, ABS_CNT)) + return false; + + bitmap_zero(jd_scratch, KEY_CNT); + __set_bit(BTN_LEFT, jd_scratch); + __set_bit(BTN_RIGHT, jd_scratch); + __set_bit(BTN_MIDDLE, jd_scratch); + + if (!bitmap_equal(dev->keybit, jd_scratch, KEY_CNT)) + return false; + + /* + * Amiga joystick (amijoy) historically uses left/middle/right + * button events. + */ + if (dev->id.bustype == BUS_AMIGA) + return false; + + return true; +} 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)) + /* Disable blacklisted devices */ + if (joydev_dev_is_blacklisted(dev)) return false; - /* Avoid tablets, digitisers and similar devices */ - if (test_bit(EV_KEY, dev->evbit) && test_bit(BTN_DIGI, dev->keybit)) + /* Avoid absolute mice */ + if (joydev_dev_is_absolute_mouse(dev)) return false; return true; @@ -798,12 +944,11 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *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_each_set_bit(i, dev->absbit, ABS_CNT) { + 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)) { @@ -823,7 +968,6 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, 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; @@ -838,10 +982,6 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, 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); } } @@ -856,12 +996,8 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, goto err_free_joydev; cdev_init(&joydev->cdev, &joydev_fops); - joydev->cdev.kobj.parent = &joydev->dev.kobj; - error = cdev_add(&joydev->cdev, joydev->dev.devt, 1); - if (error) - goto err_unregister_handle; - error = device_add(&joydev->dev); + error = cdev_device_add(&joydev->cdev, &joydev->dev); if (error) goto err_cleanup_joydev; @@ -869,7 +1005,6 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, err_cleanup_joydev: joydev_cleanup(joydev); - err_unregister_handle: input_unregister_handle(&joydev->handle); err_free_joydev: put_device(&joydev->dev); @@ -882,7 +1017,7 @@ static void joydev_disconnect(struct input_handle *handle) { struct joydev *joydev = handle->private; - device_del(&joydev->dev); + cdev_device_del(&joydev->cdev, &joydev->dev); joydev_cleanup(joydev); input_free_minor(MINOR(joydev->dev.devt)); input_unregister_handle(handle); @@ -900,6 +1035,12 @@ 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_Z) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) }, .absbit = { BIT_MASK(ABS_WHEEL) }, }, { |
