Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 13 Jul 2019 21:40:42 +0000 (14:40 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 13 Jul 2019 21:40:42 +0000 (14:40 -0700)
Pull input updates from Dmitry Torokhov:

 - an update to Elan touchpad SMBus driver to fetch device parameters
   (size, resolution) while it is still in PS/2 mode, before switching
   over to SMBus, as in that mode some devices return garbage dimensions

 - update to iforce joystick driver

 - miscellaneous driver fixes

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (48 commits)
  Input: gpio_keys_polled - allow specifying name of input device
  Input: edt-ft5x06 - simplify event reporting code
  Input: max77650-onkey - add MODULE_ALIAS()
  Input: atmel_mxt_ts - fix leak in mxt_update_cfg()
  Input: synaptics - enable SMBUS on T480 thinkpad trackpad
  Input: atmel_mxt_ts - fix -Wunused-const-variable
  Input: joydev - extend absolute mouse detection
  HID: quirks: Refactor ELAN 400 and 401 handling
  Input: elan_i2c - export the device id whitelist
  Input: edt-ft5x06 - use get_unaligned_be16()
  Input: iforce - add the Saitek R440 Force Wheel
  Input: iforce - use unaligned accessors, where appropriate
  Input: iforce - drop couple of temps from transport code
  Input: iforce - drop bus type from iforce structure
  Input: iforce - use DMA-safe buffores for USB transfers
  Input: iforce - allow callers supply data buffer when fetching device IDs
  Input: iforce - only call iforce_process_packet() if initialized
  Input: iforce - signal command completion from transport code
  Input: iforce - do not combine arguments for iforce_process_packet()
  Input: iforce - factor out hat handling when parsing packets
  ...

20 files changed:
1  2 
drivers/hid/hid-quirks.c
drivers/input/joydev.c
drivers/input/joystick/iforce/Kconfig
drivers/input/joystick/iforce/Makefile
drivers/input/joystick/iforce/iforce-ff.c
drivers/input/joystick/iforce/iforce-main.c
drivers/input/joystick/iforce/iforce-packets.c
drivers/input/joystick/iforce/iforce-serio.c
drivers/input/joystick/iforce/iforce-usb.c
drivers/input/joystick/iforce/iforce.h
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/gpio_keys_polled.c
drivers/input/misc/da9063_onkey.c
drivers/input/mouse/elan_i2c_core.c
drivers/input/mouse/elantech.c
drivers/input/mouse/elantech.h
drivers/input/mouse/synaptics.c
drivers/input/rmi4/rmi_f12.c
drivers/input/touchscreen/atmel_mxt_ts.c
drivers/input/touchscreen/eeti_ts.c

diff --combined drivers/hid/hid-quirks.c
index 1549c7a2f04c2ee83cd2f579c9960d189aa1c962,3afb8fa5a1ac23baaa4589c322166766ed76c396..185a577c46f61b0fd7888de55ca9e86e526e9844
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *  HID quirks support for Linux
   *
   */
  
  /*
 - * 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.
   */
  
  #include <linux/hid.h>
  #include <linux/export.h>
  #include <linux/slab.h>
  #include <linux/mutex.h>
+ #include <linux/input/elan-i2c-ids.h>
  
  #include "hid-ids.h"
  
@@@ -42,7 -46,6 +43,7 @@@ static const struct hid_device_id hid_q
        { HID_USB_DEVICE(USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM), HID_QUIRK_NOGET },
        { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_MULTI_TOUCH), HID_QUIRK_MULTI_INPUT },
        { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE), HID_QUIRK_ALWAYS_POLL },
 +      { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE2), HID_QUIRK_ALWAYS_POLL },
        { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS), HID_QUIRK_MULTI_INPUT },
        { HID_USB_DEVICE(USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD), HID_QUIRK_BADPAD },
        { HID_USB_DEVICE(USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK), HID_QUIRK_NOGET },
        { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE), HID_QUIRK_ALWAYS_POLL },
        { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_MOUSE_4D22), HID_QUIRK_ALWAYS_POLL },
        { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4D0F), HID_QUIRK_ALWAYS_POLL },
 +      { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4D65), HID_QUIRK_ALWAYS_POLL },
        { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4E22), HID_QUIRK_ALWAYS_POLL },
        { HID_USB_DEVICE(USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS), HID_QUIRK_NOGET },
        { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001), HID_QUIRK_NOGET },
@@@ -431,6 -433,7 +432,6 @@@ static const struct hid_device_id hid_h
  #if IS_ENABLED(CONFIG_HID_LOGITECH)
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
 -      { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) },
  #endif
  #if IS_ENABLED(CONFIG_HID_LOGITECH_HIDPP)
 -      { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL) },
  #endif
 -#if IS_ENABLED(CONFIG_HID_LOGITECH_DJ)
 -      { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER) },
 -      { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2) },
 -#endif
  #if IS_ENABLED(CONFIG_HID_MAGICMOUSE)
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) },
@@@ -916,6 -924,8 +917,8 @@@ static const struct hid_device_id hid_m
  
  bool hid_ignore(struct hid_device *hdev)
  {
+       int i;
        if (hdev->quirks & HID_QUIRK_NO_IGNORE)
                return false;
        if (hdev->quirks & HID_QUIRK_IGNORE)
                break;
        case USB_VENDOR_ID_ELAN:
                /*
-                * Many Elan devices have a product id of 0x0401 and are handled
-                * by the elan_i2c input driver. But the ACPI HID ELAN0800 dev
-                * is not (and cannot be) handled by that driver ->
-                * Ignore all 0x0401 devs except for the ELAN0800 dev.
+                * Blacklist of everything that gets handled by the elan_i2c
+                * input driver.  This avoids disabling valid touchpads and
+                * other ELAN devices.
                 */
-               if (hdev->product == 0x0401 &&
-                   strncmp(hdev->name, "ELAN0800", 8) != 0)
-                       return true;
-               /* Same with product id 0x0400 */
-               if (hdev->product == 0x0400 &&
-                   strncmp(hdev->name, "QTEC0001", 8) != 0)
-                       return true;
+               if ((hdev->product == 0x0401 || hdev->product == 0x0400))
+                       for (i = 0; strlen(elan_acpi_id[i].id); ++i)
+                               if (!strncmp(hdev->name, elan_acpi_id[i].id,
+                                            strlen(elan_acpi_id[i].id)))
+                                       return true;
                break;
        }
  
diff --combined drivers/input/joydev.c
index ac21c050fdb0e4d121e34c070035b0dbc6d20386,319c641edb938a98faf74465545741c1da35a057..a2b5fbba2d3b3983a875709a7a8b86839949816b
@@@ -1,9 -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
@@@ -275,7 -279,7 +275,7 @@@ static int joydev_open(struct inode *in
                goto err_free_client;
  
        file->private_data = client;
 -      nonseekable_open(inode, file);
 +      stream_open(inode, file);
  
        return 0;
  
@@@ -808,6 -812,7 +808,7 @@@ static bool joydev_dev_is_blacklisted(s
  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);
  
         * considered to be an absolute mouse if the following is
         * true:
         *
-        * 1) Event types are exactly EV_ABS, EV_KEY and EV_SYN.
+        * 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))
+       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);
index 62dcc5b71641cbf2db89a8a6392c2fd6f5a582ac,55f6ae1da6b02120209dc72da3aa3f3d10f41c31..f002fb88f2e75e96616d9e8fb648215c970eec18
@@@ -1,4 -1,3 +1,4 @@@
 +# SPDX-License-Identifier: GPL-2.0-only
  #
  # I-Force driver configuration
  #
@@@ -14,15 -13,15 +14,15 @@@ config JOYSTICK_IFORC
          module will be called iforce.
  
  config JOYSTICK_IFORCE_USB
-       bool "I-Force USB joysticks and wheels"
-       depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || USB=y) && USB
+       tristate "I-Force USB joysticks and wheels"
+       depends on JOYSTICK_IFORCE && USB
        help
          Say Y here if you have an I-Force joystick or steering wheel
          connected to your USB port.
  
  config JOYSTICK_IFORCE_232
-       bool "I-Force Serial joysticks and wheels"
-       depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || SERIO=y) && SERIO
+       tristate "I-Force Serial joysticks and wheels"
+       depends on JOYSTICK_IFORCE && SERIO
        help
          Say Y here if you have an I-Force joystick or steering wheel
          connected to your serial (COM) port.
index fa79a49d7ca10b95bf95ec6703bb837ec20a210e,414075019a4f778f4452c93f9acfd9795d67c7b2..dbbe7c04010dc240fef3fbf70959f7a28c9cb602
@@@ -1,12 -1,10 +1,11 @@@
 +# SPDX-License-Identifier: GPL-2.0-only
  #
  # Makefile for the I-Force driver
  #
  # By Johann Deneux <johann.deneux@gmail.com>
  #
  
- obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o
+ obj-$(CONFIG_JOYSTICK_IFORCE)         += iforce.o
  iforce-y := iforce-ff.o iforce-main.o iforce-packets.o
iforce-$(CONFIG_JOYSTICK_IFORCE_232)  += iforce-serio.o
iforce-$(CONFIG_JOYSTICK_IFORCE_USB)  += iforce-usb.o
obj-$(CONFIG_JOYSTICK_IFORCE_232)     += iforce-serio.o
obj-$(CONFIG_JOYSTICK_IFORCE_USB)     += iforce-usb.o
index 2ed7da7d1f3e1eeead2fc882b8ce6ff68ccbcf69,56973dd97fd6ee6fce8f74c2535143d537df5aaa..4cadebd8b9c46df56dc386a425b7755b5195edf2
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
   *  Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
@@@ -7,6 -6,19 +7,6 @@@
   */
  
  /*
 - * 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.
 - *
 - * This program is distributed in the hope that it will be useful,
 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 - * GNU General Public License for more details.
 - *
 - * You should have received a copy of the GNU General Public License
 - * along with this program; if not, write to the Free Software
 - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
   */
  
  #include "iforce.h"
@@@ -372,12 -384,12 +372,12 @@@ int iforce_upload_periodic(struct iforc
        }
  
        switch (effect->u.periodic.waveform) {
-               case FF_SQUARE:         wave_code = 0x20; break;
-               case FF_TRIANGLE:       wave_code = 0x21; break;
-               case FF_SINE:           wave_code = 0x22; break;
-               case FF_SAW_UP:         wave_code = 0x23; break;
-               case FF_SAW_DOWN:       wave_code = 0x24; break;
-               default:                wave_code = 0x20; break;
+       case FF_SQUARE:         wave_code = 0x20; break;
+       case FF_TRIANGLE:       wave_code = 0x21; break;
+       case FF_SINE:           wave_code = 0x22; break;
+       case FF_SAW_UP:         wave_code = 0x23; break;
+       case FF_SAW_DOWN:       wave_code = 0x24; break;
+       default:                wave_code = 0x20; break;
        }
  
        if (!old || need_core(old, effect)) {
@@@ -476,9 -488,9 +476,9 @@@ int iforce_upload_condition(struct ifor
        int core_err = 0;
  
        switch (effect->type) {
-               case FF_SPRING: type = 0x40; break;
-               case FF_DAMPER: type = 0x41; break;
-               default: return -1;
+       case FF_SPRING: type = 0x40; break;
+       case FF_DAMPER: type = 0x41; break;
+       default: return -1;
        }
  
        if (!old || need_condition_modifier(iforce, old, effect)) {
index 55f5b7bb4cac9284128892670ea626f4d62eb6de,40eb65bfd57e715770c8d26dfbe675eb48d454d7..9a5f90da06ec0b2ae943cfab181acdb4d4dde4fd
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
   *  Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
@@@ -7,12 -6,26 +7,13 @@@
   */
  
  /*
 - * 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.
 - *
 - * This program is distributed in the hope that it will be useful,
 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 - * GNU General Public License for more details.
 - *
 - * You should have received a copy of the GNU General Public License
 - * along with this program; if not, write to the Free Software
 - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
   */
  
+ #include <asm/unaligned.h>
  #include "iforce.h"
  
  MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>");
- MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver");
+ MODULE_DESCRIPTION("Core I-Force joysticks and wheels driver");
  MODULE_LICENSE("GPL");
  
  static signed short btn_joystick[] =
@@@ -55,6 -68,7 +56,7 @@@ static struct iforce_device iforce_devi
        { 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel",   btn_wheel, abs_wheel, ff_iforce }, //?
        { 0x061c, 0xc0a4, "ACT LABS Force RS",                          btn_wheel, abs_wheel, ff_iforce }, //?
        { 0x061c, 0xc084, "ACT LABS Force RS",                          btn_wheel, abs_wheel, ff_iforce },
+       { 0x06a3, 0xff04, "Saitek R440 Force Wheel",                    btn_wheel, abs_wheel, ff_iforce }, //?
        { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback",       btn_wheel, abs_wheel, ff_iforce }, //?
        { 0x06f8, 0x0001, "Guillemot Jet Leader Force Feedback",        btn_joystick, abs_joystick_rudder, ff_iforce },
        { 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel",      btn_wheel, abs_wheel, ff_iforce }, //?
@@@ -120,22 -134,21 +122,21 @@@ static int iforce_upload_effect(struct 
   * Upload the effect
   */
        switch (effect->type) {
+       case FF_PERIODIC:
+               ret = iforce_upload_periodic(iforce, effect, old);
+               break;
  
-               case FF_PERIODIC:
-                       ret = iforce_upload_periodic(iforce, effect, old);
-                       break;
-               case FF_CONSTANT:
-                       ret = iforce_upload_constant(iforce, effect, old);
-                       break;
+       case FF_CONSTANT:
+               ret = iforce_upload_constant(iforce, effect, old);
+               break;
  
-               case FF_SPRING:
-               case FF_DAMPER:
-                       ret = iforce_upload_condition(iforce, effect, old);
-                       break;
+       case FF_SPRING:
+       case FF_DAMPER:
+               ret = iforce_upload_condition(iforce, effect, old);
+               break;
  
-               default:
-                       return -EINVAL;
+       default:
+               return -EINVAL;
        }
  
        if (ret == 0) {
@@@ -173,15 -186,7 +174,7 @@@ static int iforce_open(struct input_de
  {
        struct iforce *iforce = input_get_drvdata(dev);
  
-       switch (iforce->bus) {
- #ifdef CONFIG_JOYSTICK_IFORCE_USB
-               case IFORCE_USB:
-                       iforce->irq->dev = iforce->usbdev;
-                       if (usb_submit_urb(iforce->irq, GFP_KERNEL))
-                               return -EIO;
-                       break;
- #endif
-       }
+       iforce->xport_ops->start_io(iforce);
  
        if (test_bit(EV_FF, dev->evbit)) {
                /* Enable force feedback */
@@@ -214,27 -219,17 +207,17 @@@ static void iforce_close(struct input_d
                        !test_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags));
        }
  
-       switch (iforce->bus) {
- #ifdef CONFIG_JOYSTICK_IFORCE_USB
-       case IFORCE_USB:
-               usb_kill_urb(iforce->irq);
-               usb_kill_urb(iforce->out);
-               usb_kill_urb(iforce->ctrl);
-               break;
- #endif
- #ifdef CONFIG_JOYSTICK_IFORCE_232
-       case IFORCE_232:
-               //TODO: Wait for the last packets to be sent
-               break;
- #endif
-       }
+       iforce->xport_ops->stop_io(iforce);
  }
  
- int iforce_init_device(struct iforce *iforce)
+ int iforce_init_device(struct device *parent, u16 bustype,
+                      struct iforce *iforce)
  {
        struct input_dev *input_dev;
        struct ff_device *ff;
-       unsigned char c[] = "CEOV";
+       u8 c[] = "CEOV";
+       u8 buf[IFORCE_MAX_LENGTH];
+       size_t len;
        int i, error;
        int ff_effects = 0;
  
   * Input device fields.
   */
  
-       switch (iforce->bus) {
- #ifdef CONFIG_JOYSTICK_IFORCE_USB
-       case IFORCE_USB:
-               input_dev->id.bustype = BUS_USB;
-               input_dev->dev.parent = &iforce->usbdev->dev;
-               break;
- #endif
- #ifdef CONFIG_JOYSTICK_IFORCE_232
-       case IFORCE_232:
-               input_dev->id.bustype = BUS_RS232;
-               input_dev->dev.parent = &iforce->serio->dev;
-               break;
- #endif
-       }
+       input_dev->id.bustype = bustype;
+       input_dev->dev.parent = parent;
  
        input_set_drvdata(input_dev, iforce);
  
   */
  
        for (i = 0; i < 20; i++)
-               if (!iforce_get_id_packet(iforce, "O"))
+               if (!iforce_get_id_packet(iforce, 'O', buf, &len))
                        break;
  
        if (i == 20) { /* 5 seconds */
   * Get device info.
   */
  
-       if (!iforce_get_id_packet(iforce, "M"))
-               input_dev->id.vendor = (iforce->edata[2] << 8) | iforce->edata[1];
+       if (!iforce_get_id_packet(iforce, 'M', buf, &len) || len < 3)
+               input_dev->id.vendor = get_unaligned_le16(buf + 1);
        else
                dev_warn(&iforce->dev->dev, "Device does not respond to id packet M\n");
  
-       if (!iforce_get_id_packet(iforce, "P"))
-               input_dev->id.product = (iforce->edata[2] << 8) | iforce->edata[1];
+       if (!iforce_get_id_packet(iforce, 'P', buf, &len) || len < 3)
+               input_dev->id.product = get_unaligned_le16(buf + 1);
        else
                dev_warn(&iforce->dev->dev, "Device does not respond to id packet P\n");
  
-       if (!iforce_get_id_packet(iforce, "B"))
-               iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1];
+       if (!iforce_get_id_packet(iforce, 'B', buf, &len) || len < 3)
+               iforce->device_memory.end = get_unaligned_le16(buf + 1);
        else
                dev_warn(&iforce->dev->dev, "Device does not respond to id packet B\n");
  
-       if (!iforce_get_id_packet(iforce, "N"))
-               ff_effects = iforce->edata[1];
+       if (!iforce_get_id_packet(iforce, 'N', buf, &len) || len < 2)
+               ff_effects = buf[1];
        else
                dev_warn(&iforce->dev->dev, "Device does not respond to id packet N\n");
  
   */
  
        for (i = 0; c[i]; i++)
-               if (!iforce_get_id_packet(iforce, c + i))
-                       iforce_dump_packet(iforce, "info", iforce->ecmd, iforce->edata);
+               if (!iforce_get_id_packet(iforce, c[i], buf, &len))
+                       iforce_dump_packet(iforce, "info",
+                                          (FF_CMD_QUERY & 0xff00) | len, buf);
  
  /*
   * Disable spring, enable force feedback.
                signed short t = iforce->type->abs[i];
  
                switch (t) {
+               case ABS_X:
+               case ABS_Y:
+               case ABS_WHEEL:
+                       input_set_abs_params(input_dev, t, -1920, 1920, 16, 128);
+                       set_bit(t, input_dev->ffbit);
+                       break;
  
-                       case ABS_X:
-                       case ABS_Y:
-                       case ABS_WHEEL:
-                               input_set_abs_params(input_dev, t, -1920, 1920, 16, 128);
-                               set_bit(t, input_dev->ffbit);
-                               break;
-                       case ABS_THROTTLE:
-                       case ABS_GAS:
-                       case ABS_BRAKE:
-                               input_set_abs_params(input_dev, t, 0, 255, 0, 0);
-                               break;
-                       case ABS_RUDDER:
-                               input_set_abs_params(input_dev, t, -128, 127, 0, 0);
-                               break;
+               case ABS_THROTTLE:
+               case ABS_GAS:
+               case ABS_BRAKE:
+                       input_set_abs_params(input_dev, t, 0, 255, 0, 0);
+                       break;
  
-                       case ABS_HAT0X:
-                       case ABS_HAT0Y:
-                       case ABS_HAT1X:
-                       case ABS_HAT1Y:
+               case ABS_RUDDER:
+                       input_set_abs_params(input_dev, t, -128, 127, 0, 0);
+                       break;
  
-                               input_set_abs_params(input_dev, t, -1, 1, 0, 0);
-                               break;
+               case ABS_HAT0X:
+               case ABS_HAT0Y:
+               case ABS_HAT1X:
+               case ABS_HAT1Y:
+                       input_set_abs_params(input_dev, t, -1, 1, 0, 0);
+                       break;
                }
        }
  
   fail:        input_free_device(input_dev);
        return error;
  }
- static int __init iforce_init(void)
- {
-       int err = 0;
- #ifdef CONFIG_JOYSTICK_IFORCE_USB
-       err = usb_register(&iforce_usb_driver);
-       if (err)
-               return err;
- #endif
- #ifdef CONFIG_JOYSTICK_IFORCE_232
-       err = serio_register_driver(&iforce_serio_drv);
- #ifdef CONFIG_JOYSTICK_IFORCE_USB
-       if (err)
-               usb_deregister(&iforce_usb_driver);
- #endif
- #endif
-       return err;
- }
- static void __exit iforce_exit(void)
- {
- #ifdef CONFIG_JOYSTICK_IFORCE_USB
-       usb_deregister(&iforce_usb_driver);
- #endif
- #ifdef CONFIG_JOYSTICK_IFORCE_232
-       serio_unregister_driver(&iforce_serio_drv);
- #endif
- }
- module_init(iforce_init);
- module_exit(iforce_exit);
+ EXPORT_SYMBOL(iforce_init_device);
index 42cd9730e4cc7efa7b7a6a4a66a245c8157fb5a4,76c4475740ab6f8368fe06cd846a16ba90b92d8a..b313e38b2c3a83436a514a7a241ec84d174e1052
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
   *  Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
@@@ -7,8 -6,22 +7,9 @@@
   */
  
  /*
 - * 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.
 - *
 - * This program is distributed in the hope that it will be useful,
 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 - * GNU General Public License for more details.
 - *
 - * You should have received a copy of the GNU General Public License
 - * along with this program; if not, write to the Free Software
 - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
   */
  
+ #include <asm/unaligned.h>
  #include "iforce.h"
  
  static struct {
@@@ -79,27 -92,12 +80,12 @@@ int iforce_send_packet(struct iforce *i
  /*
   * If necessary, start the transmission
   */
-       switch (iforce->bus) {
+       if (empty)
+               iforce->xport_ops->xmit(iforce);
  
- #ifdef CONFIG_JOYSTICK_IFORCE_232
-               case IFORCE_232:
-               if (empty)
-                       iforce_serial_xmit(iforce);
-               break;
- #endif
- #ifdef CONFIG_JOYSTICK_IFORCE_USB
-               case IFORCE_USB:
-               if (iforce->usbdev && empty &&
-                       !test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
-                       iforce_usb_xmit(iforce);
-               }
-               break;
- #endif
-       }
        return 0;
  }
+ EXPORT_SYMBOL(iforce_send_packet);
  
  /* Start or stop an effect */
  int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value)
@@@ -133,157 -131,96 +119,96 @@@ static int mark_core_as_ready(struct if
        return -1;
  }
  
void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data)
static void iforce_report_hats_buttons(struct iforce *iforce, u8 *data)
  {
        struct input_dev *dev = iforce->dev;
        int i;
-       static int being_used = 0;
  
-       if (being_used)
-               dev_warn(&iforce->dev->dev,
-                        "re-entrant call to iforce_process %d\n", being_used);
-       being_used++;
- #ifdef CONFIG_JOYSTICK_IFORCE_232
-       if (HI(iforce->expect_packet) == HI(cmd)) {
-               iforce->expect_packet = 0;
-               iforce->ecmd = cmd;
-               memcpy(iforce->edata, data, IFORCE_MAX_LENGTH);
-       }
- #endif
-       wake_up(&iforce->wait);
+       input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x);
+       input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y);
  
-       if (!iforce->type) {
-               being_used--;
-               return;
-       }
-       switch (HI(cmd)) {
-               case 0x01:      /* joystick position data */
-               case 0x03:      /* wheel position data */
-                       if (HI(cmd) == 1) {
-                               input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0]));
-                               input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2]));
-                               input_report_abs(dev, ABS_THROTTLE, 255 - data[4]);
-                               if (LO(cmd) >= 8 && test_bit(ABS_RUDDER ,dev->absbit))
-                                       input_report_abs(dev, ABS_RUDDER, (__s8)data[7]);
-                       } else {
-                               input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0]));
-                               input_report_abs(dev, ABS_GAS,   255 - data[2]);
-                               input_report_abs(dev, ABS_BRAKE, 255 - data[3]);
-                       }
+       for (i = 0; iforce->type->btn[i] >= 0; i++)
+               input_report_key(dev, iforce->type->btn[i],
+                                data[(i >> 3) + 5] & (1 << (i & 7)));
  
-                       input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x);
-                       input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y);
-                       for (i = 0; iforce->type->btn[i] >= 0; i++)
-                               input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7)));
-                       /* If there are untouched bits left, interpret them as the second hat */
-                       if (i <= 8) {
-                               int btns = data[6];
-                               if (test_bit(ABS_HAT1X, dev->absbit)) {
-                                       if (btns & 8) input_report_abs(dev, ABS_HAT1X, -1);
-                                       else if (btns & 2) input_report_abs(dev, ABS_HAT1X, 1);
-                                       else input_report_abs(dev, ABS_HAT1X, 0);
-                               }
-                               if (test_bit(ABS_HAT1Y, dev->absbit)) {
-                                       if (btns & 1) input_report_abs(dev, ABS_HAT1Y, -1);
-                                       else if (btns & 4) input_report_abs(dev, ABS_HAT1Y, 1);
-                                       else input_report_abs(dev, ABS_HAT1Y, 0);
-                               }
-                       }
+       /* If there are untouched bits left, interpret them as the second hat */
+       if (i <= 8) {
+               u8 btns = data[6];
  
-                       input_sync(dev);
-                       break;
-               case 0x02:      /* status report */
-                       input_report_key(dev, BTN_DEAD, data[0] & 0x02);
-                       input_sync(dev);
+               if (test_bit(ABS_HAT1X, dev->absbit)) {
+                       if (btns & BIT(3))
+                               input_report_abs(dev, ABS_HAT1X, -1);
+                       else if (btns & BIT(1))
+                               input_report_abs(dev, ABS_HAT1X, 1);
+                       else
+                               input_report_abs(dev, ABS_HAT1X, 0);
+               }
  
-                       /* Check if an effect was just started or stopped */
-                       i = data[1] & 0x7f;
-                       if (data[1] & 0x80) {
-                               if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
-                                       /* Report play event */
-                                       input_report_ff_status(dev, i, FF_STATUS_PLAYING);
-                               }
-                       } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
-                               /* Report stop event */
-                               input_report_ff_status(dev, i, FF_STATUS_STOPPED);
-                       }
-                       if (LO(cmd) > 3) {
-                               int j;
-                               for (j = 3; j < LO(cmd); j += 2)
-                                       mark_core_as_ready(iforce, data[j] | (data[j+1]<<8));
-                       }
-                       break;
+               if (test_bit(ABS_HAT1Y, dev->absbit)) {
+                       if (btns & BIT(0))
+                               input_report_abs(dev, ABS_HAT1Y, -1);
+                       else if (btns & BIT(2))
+                               input_report_abs(dev, ABS_HAT1Y, 1);
+                       else
+                               input_report_abs(dev, ABS_HAT1Y, 0);
+               }
        }
-       being_used--;
  }
  
- int iforce_get_id_packet(struct iforce *iforce, char *packet)
+ void iforce_process_packet(struct iforce *iforce,
+                          u8 packet_id, u8 *data, size_t len)
  {
-       switch (iforce->bus) {
+       struct input_dev *dev = iforce->dev;
+       int i, j;
  
-       case IFORCE_USB: {
- #ifdef CONFIG_JOYSTICK_IFORCE_USB
-               int status;
+       switch (packet_id) {
  
-               iforce->cr.bRequest = packet[0];
-               iforce->ctrl->dev = iforce->usbdev;
+       case 0x01:      /* joystick position data */
+               input_report_abs(dev, ABS_X,
+                                (__s16) get_unaligned_le16(data));
+               input_report_abs(dev, ABS_Y,
+                                (__s16) get_unaligned_le16(data + 2));
+               input_report_abs(dev, ABS_THROTTLE, 255 - data[4]);
  
-               status = usb_submit_urb(iforce->ctrl, GFP_KERNEL);
-               if (status) {
-                       dev_err(&iforce->intf->dev,
-                               "usb_submit_urb failed %d\n", status);
-                       return -1;
-               }
+               if (len >= 8 && test_bit(ABS_RUDDER ,dev->absbit))
+                       input_report_abs(dev, ABS_RUDDER, (__s8)data[7]);
  
-               wait_event_interruptible_timeout(iforce->wait,
-                       iforce->ctrl->status != -EINPROGRESS, HZ);
+               iforce_report_hats_buttons(iforce, data);
  
-               if (iforce->ctrl->status) {
-                       dev_dbg(&iforce->intf->dev,
-                               "iforce->ctrl->status = %d\n",
-                               iforce->ctrl->status);
-                       usb_unlink_urb(iforce->ctrl);
-                       return -1;
-               }
- #else
-               printk(KERN_DEBUG "iforce_get_id_packet: iforce->bus = USB!\n");
- #endif
-               }
+               input_sync(dev);
                break;
  
-       case IFORCE_232:
+       case 0x03:      /* wheel position data */
+               input_report_abs(dev, ABS_WHEEL,
+                                (__s16) get_unaligned_le16(data));
+               input_report_abs(dev, ABS_GAS,   255 - data[2]);
+               input_report_abs(dev, ABS_BRAKE, 255 - data[3]);
  
- #ifdef CONFIG_JOYSTICK_IFORCE_232
-               iforce->expect_packet = FF_CMD_QUERY;
-               iforce_send_packet(iforce, FF_CMD_QUERY, packet);
+               iforce_report_hats_buttons(iforce, data);
  
-               wait_event_interruptible_timeout(iforce->wait,
-                       !iforce->expect_packet, HZ);
+               input_sync(dev);
+               break;
+       case 0x02:      /* status report */
+               input_report_key(dev, BTN_DEAD, data[0] & 0x02);
+               input_sync(dev);
  
-               if (iforce->expect_packet) {
-                       iforce->expect_packet = 0;
-                       return -1;
+               /* Check if an effect was just started or stopped */
+               i = data[1] & 0x7f;
+               if (data[1] & 0x80) {
+                       if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
+                               /* Report play event */
+                               input_report_ff_status(dev, i, FF_STATUS_PLAYING);
+                       }
+               } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
+                       /* Report stop event */
+                       input_report_ff_status(dev, i, FF_STATUS_STOPPED);
                }
- #else
-               dev_err(&iforce->dev->dev,
-                       "iforce_get_id_packet: iforce->bus = SERIO!\n");
- #endif
-               break;
  
-       default:
-               dev_err(&iforce->dev->dev,
-                       "iforce_get_id_packet: iforce->bus = %d\n",
-                       iforce->bus);
+               for (j = 3; j < len; j += 2)
+                       mark_core_as_ready(iforce, get_unaligned_le16(data + j));
                break;
        }
-       return -(iforce->edata[0] != packet[0]);
  }
+ EXPORT_SYMBOL(iforce_process_packet);
index 65a4fe26324fe752b7fc55100876cbb947ae9744,e7692a38591e981c3ca43bdfb32d175cf64f1397..bbe31e0b759fcfd032bf195eba6c7f8b7605f03f
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *  Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
   *  Copyright (c) 2001, 2007 Johann Deneux <johann.deneux@gmail.com>
@@@ -7,12 -6,41 +7,28 @@@
   */
  
  /*
 - * 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.
 - *
 - * This program is distributed in the hope that it will be useful,
 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 - * GNU General Public License for more details.
 - *
 - * You should have received a copy of the GNU General Public License
 - * along with this program; if not, write to the Free Software
 - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
   */
  
+ #include <linux/serio.h>
  #include "iforce.h"
  
- void iforce_serial_xmit(struct iforce *iforce)
+ struct iforce_serio {
+       struct iforce iforce;
+       struct serio *serio;
+       int idx, pkt, len, id;
+       u8 csum;
+       u8 expect_packet;
+       u8 cmd_response[IFORCE_MAX_LENGTH];
+       u8 cmd_response_len;
+       u8 data_in[IFORCE_MAX_LENGTH];
+ };
+ static void iforce_serio_xmit(struct iforce *iforce)
  {
+       struct iforce_serio *iforce_serio = container_of(iforce,
+                                                        struct iforce_serio,
+                                                        iforce);
        unsigned char cs;
        int i;
        unsigned long flags;
@@@ -33,19 -61,20 +49,20 @@@ again
  
        cs = 0x2b;
  
-       serio_write(iforce->serio, 0x2b);
+       serio_write(iforce_serio->serio, 0x2b);
  
-       serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
+       serio_write(iforce_serio->serio, iforce->xmit.buf[iforce->xmit.tail]);
        cs ^= iforce->xmit.buf[iforce->xmit.tail];
        XMIT_INC(iforce->xmit.tail, 1);
  
        for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) {
-               serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
+               serio_write(iforce_serio->serio,
+                           iforce->xmit.buf[iforce->xmit.tail]);
                cs ^= iforce->xmit.buf[iforce->xmit.tail];
                XMIT_INC(iforce->xmit.tail, 1);
        }
  
-       serio_write(iforce->serio, cs);
+       serio_write(iforce_serio->serio, cs);
  
        if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags))
                goto again;
        spin_unlock_irqrestore(&iforce->xmit_lock, flags);
  }
  
+ static int iforce_serio_get_id(struct iforce *iforce, u8 id,
+                              u8 *response_data, size_t *response_len)
+ {
+       struct iforce_serio *iforce_serio = container_of(iforce,
+                                                        struct iforce_serio,
+                                                        iforce);
+       iforce_serio->expect_packet = HI(FF_CMD_QUERY);
+       iforce_serio->cmd_response_len = 0;
+       iforce_send_packet(iforce, FF_CMD_QUERY, &id);
+       wait_event_interruptible_timeout(iforce->wait,
+                                        !iforce_serio->expect_packet, HZ);
+       if (iforce_serio->expect_packet) {
+               iforce_serio->expect_packet = 0;
+               return -ETIMEDOUT;
+       }
+       if (iforce_serio->cmd_response[0] != id)
+               return -EIO;
+       memcpy(response_data, iforce_serio->cmd_response,
+              iforce_serio->cmd_response_len);
+       *response_len = iforce_serio->cmd_response_len;
+       return 0;
+ }
+ static int iforce_serio_start_io(struct iforce *iforce)
+ {
+       /* No special handling required */
+       return 0;
+ }
+ static void iforce_serio_stop_io(struct iforce *iforce)
+ {
+       //TODO: Wait for the last packets to be sent
+ }
+ static const struct iforce_xport_ops iforce_serio_xport_ops = {
+       .xmit           = iforce_serio_xmit,
+       .get_id         = iforce_serio_get_id,
+       .start_io       = iforce_serio_start_io,
+       .stop_io        = iforce_serio_stop_io,
+ };
  static void iforce_serio_write_wakeup(struct serio *serio)
  {
        struct iforce *iforce = serio_get_drvdata(serio);
  
-       iforce_serial_xmit(iforce);
+       iforce_serio_xmit(iforce);
  }
  
  static irqreturn_t iforce_serio_irq(struct serio *serio,
-               unsigned char data, unsigned int flags)
+                                   unsigned char data, unsigned int flags)
  {
-       struct iforce *iforce = serio_get_drvdata(serio);
+       struct iforce_serio *iforce_serio = serio_get_drvdata(serio);
+       struct iforce *iforce = &iforce_serio->iforce;
  
-       if (!iforce->pkt) {
+       if (!iforce_serio->pkt) {
                if (data == 0x2b)
-                       iforce->pkt = 1;
+                       iforce_serio->pkt = 1;
                goto out;
        }
  
-       if (!iforce->id) {
+       if (!iforce_serio->id) {
                if (data > 3 && data != 0xff)
-                       iforce->pkt = 0;
+                       iforce_serio->pkt = 0;
                else
-                       iforce->id = data;
+                       iforce_serio->id = data;
                goto out;
        }
  
-       if (!iforce->len) {
+       if (!iforce_serio->len) {
                if (data > IFORCE_MAX_LENGTH) {
-                       iforce->pkt = 0;
-                       iforce->id = 0;
+                       iforce_serio->pkt = 0;
+                       iforce_serio->id = 0;
                } else {
-                       iforce->len = data;
+                       iforce_serio->len = data;
                }
                goto out;
        }
  
-       if (iforce->idx < iforce->len) {
-               iforce->csum += iforce->data[iforce->idx++] = data;
+       if (iforce_serio->idx < iforce_serio->len) {
+               iforce_serio->data_in[iforce_serio->idx++] = data;
+               iforce_serio->csum += data;
                goto out;
        }
  
-       if (iforce->idx == iforce->len) {
-               iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data);
-               iforce->pkt = 0;
-               iforce->id  = 0;
-               iforce->len = 0;
-               iforce->idx = 0;
-               iforce->csum = 0;
+       if (iforce_serio->idx == iforce_serio->len) {
+               /* Handle command completion */
+               if (iforce_serio->expect_packet == iforce_serio->id) {
+                       iforce_serio->expect_packet = 0;
+                       memcpy(iforce_serio->cmd_response,
+                              iforce_serio->data_in, IFORCE_MAX_LENGTH);
+                       iforce_serio->cmd_response_len = iforce_serio->len;
+                       /* Signal that command is done */
+                       wake_up(&iforce->wait);
+               } else if (likely(iforce->type)) {
+                       iforce_process_packet(iforce, iforce_serio->id,
+                                             iforce_serio->data_in,
+                                             iforce_serio->len);
+               }
+               iforce_serio->pkt = 0;
+               iforce_serio->id  = 0;
+               iforce_serio->len = 0;
+               iforce_serio->idx = 0;
+               iforce_serio->csum = 0;
        }
  out:
        return IRQ_HANDLED;
  
  static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv)
  {
-       struct iforce *iforce;
+       struct iforce_serio *iforce_serio;
        int err;
  
-       iforce = kzalloc(sizeof(struct iforce), GFP_KERNEL);
-       if (!iforce)
+       iforce_serio = kzalloc(sizeof(*iforce_serio), GFP_KERNEL);
+       if (!iforce_serio)
                return -ENOMEM;
  
-       iforce->bus = IFORCE_232;
-       iforce->serio = serio;
+       iforce_serio->iforce.xport_ops = &iforce_serio_xport_ops;
  
-       serio_set_drvdata(serio, iforce);
+       iforce_serio->serio = serio;
+       serio_set_drvdata(serio, iforce_serio);
  
        err = serio_open(serio, drv);
        if (err)
                goto fail1;
  
-       err = iforce_init_device(iforce);
+       err = iforce_init_device(&serio->dev, BUS_RS232, &iforce_serio->iforce);
        if (err)
                goto fail2;
  
  
   fail2:       serio_close(serio);
   fail1:       serio_set_drvdata(serio, NULL);
-       kfree(iforce);
+       kfree(iforce_serio);
        return err;
  }
  
  static void iforce_serio_disconnect(struct serio *serio)
  {
-       struct iforce *iforce = serio_get_drvdata(serio);
+       struct iforce_serio *iforce_serio = serio_get_drvdata(serio);
  
-       input_unregister_device(iforce->dev);
+       input_unregister_device(iforce_serio->iforce.dev);
        serio_close(serio);
        serio_set_drvdata(serio, NULL);
-       kfree(iforce);
+       kfree(iforce_serio);
  }
  
  static const struct serio_device_id iforce_serio_ids[] = {
@@@ -171,3 -264,9 +252,9 @@@ struct serio_driver iforce_serio_drv = 
        .connect        = iforce_serio_connect,
        .disconnect     = iforce_serio_disconnect,
  };
+ module_serio_driver(iforce_serio_drv);
+ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>");
+ MODULE_DESCRIPTION("RS232 I-Force joysticks and wheels driver");
+ MODULE_LICENSE("GPL");
index f1569ae8381bcb8f5a9e044e4f905c09056367af,75a2b0ea37b4589b9ff011ce9b82ead93391af52..ade376bfb79f5b5430db68741c6fd9068a0007c1
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
   /*
   *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
   *  Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
@@@ -7,12 -6,39 +7,26 @@@
   */
  
  /*
 - * 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.
 - *
 - * This program is distributed in the hope that it will be useful,
 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 - * GNU General Public License for more details.
 - *
 - * You should have received a copy of the GNU General Public License
 - * along with this program; if not, write to the Free Software
 - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
   */
  
+ #include <linux/usb.h>
  #include "iforce.h"
  
- void iforce_usb_xmit(struct iforce *iforce)
+ struct iforce_usb {
+       struct iforce iforce;
+       struct usb_device *usbdev;
+       struct usb_interface *intf;
+       struct urb *irq, *out;
+       u8 data_in[IFORCE_MAX_LENGTH] ____cacheline_aligned;
+       u8 data_out[IFORCE_MAX_LENGTH] ____cacheline_aligned;
+ };
+ static void __iforce_usb_xmit(struct iforce *iforce)
  {
+       struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
+                                                    iforce);
        int n, c;
        unsigned long flags;
  
                return;
        }
  
-       ((char *)iforce->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];
+       ((char *)iforce_usb->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];
        XMIT_INC(iforce->xmit.tail, 1);
        n = iforce->xmit.buf[iforce->xmit.tail];
        XMIT_INC(iforce->xmit.tail, 1);
  
-       iforce->out->transfer_buffer_length = n + 1;
-       iforce->out->dev = iforce->usbdev;
+       iforce_usb->out->transfer_buffer_length = n + 1;
+       iforce_usb->out->dev = iforce_usb->usbdev;
  
        /* Copy rest of data then */
        c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE);
        if (n < c) c=n;
  
-       memcpy(iforce->out->transfer_buffer + 1,
+       memcpy(iforce_usb->out->transfer_buffer + 1,
               &iforce->xmit.buf[iforce->xmit.tail],
               c);
        if (n != c) {
-               memcpy(iforce->out->transfer_buffer + 1 + c,
+               memcpy(iforce_usb->out->transfer_buffer + 1 + c,
                       &iforce->xmit.buf[0],
                       n-c);
        }
        XMIT_INC(iforce->xmit.tail, n);
  
-       if ( (n=usb_submit_urb(iforce->out, GFP_ATOMIC)) ) {
+       if ( (n=usb_submit_urb(iforce_usb->out, GFP_ATOMIC)) ) {
                clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
-               dev_warn(&iforce->intf->dev, "usb_submit_urb failed %d\n", n);
+               dev_warn(&iforce_usb->intf->dev,
+                        "usb_submit_urb failed %d\n", n);
        }
  
        /* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended.
        spin_unlock_irqrestore(&iforce->xmit_lock, flags);
  }
  
+ static void iforce_usb_xmit(struct iforce *iforce)
+ {
+       if (!test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags))
+               __iforce_usb_xmit(iforce);
+ }
+ static int iforce_usb_get_id(struct iforce *iforce, u8 id,
+                            u8 *response_data, size_t *response_len)
+ {
+       struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
+                                                    iforce);
+       u8 *buf;
+       int status;
+       buf = kmalloc(IFORCE_MAX_LENGTH, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       status = usb_control_msg(iforce_usb->usbdev,
+                                usb_rcvctrlpipe(iforce_usb->usbdev, 0),
+                                id,
+                                USB_TYPE_VENDOR | USB_DIR_IN |
+                                       USB_RECIP_INTERFACE,
+                                0, 0, buf, IFORCE_MAX_LENGTH, HZ);
+       if (status < 0) {
+               dev_err(&iforce_usb->intf->dev,
+                       "usb_submit_urb failed: %d\n", status);
+       } else if (buf[0] != id) {
+               status = -EIO;
+       } else {
+               memcpy(response_data, buf, status);
+               *response_len = status;
+               status = 0;
+       }
+       kfree(buf);
+       return status;
+ }
+ static int iforce_usb_start_io(struct iforce *iforce)
+ {
+       struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
+                                                    iforce);
+       if (usb_submit_urb(iforce_usb->irq, GFP_KERNEL))
+               return -EIO;
+       return 0;
+ }
+ static void iforce_usb_stop_io(struct iforce *iforce)
+ {
+       struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
+                                                    iforce);
+       usb_kill_urb(iforce_usb->irq);
+       usb_kill_urb(iforce_usb->out);
+ }
+ static const struct iforce_xport_ops iforce_usb_xport_ops = {
+       .xmit           = iforce_usb_xmit,
+       .get_id         = iforce_usb_get_id,
+       .start_io       = iforce_usb_start_io,
+       .stop_io        = iforce_usb_stop_io,
+ };
  static void iforce_usb_irq(struct urb *urb)
  {
-       struct iforce *iforce = urb->context;
-       struct device *dev = &iforce->intf->dev;
+       struct iforce_usb *iforce_usb = urb->context;
+       struct iforce *iforce = &iforce_usb->iforce;
+       struct device *dev = &iforce_usb->intf->dev;
        int status;
  
        switch (urb->status) {
                goto exit;
        }
  
-       iforce_process_packet(iforce,
-               (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1);
+       iforce_process_packet(iforce, iforce_usb->data_in[0],
+                             iforce_usb->data_in + 1, urb->actual_length - 1);
  
  exit:
-       status = usb_submit_urb (urb, GFP_ATOMIC);
+       status = usb_submit_urb(urb, GFP_ATOMIC);
        if (status)
                dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
                        __func__, status);
  
  static void iforce_usb_out(struct urb *urb)
  {
-       struct iforce *iforce = urb->context;
+       struct iforce_usb *iforce_usb = urb->context;
+       struct iforce *iforce = &iforce_usb->iforce;
  
        if (urb->status) {
                clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
-               dev_dbg(&iforce->intf->dev, "urb->status %d, exiting\n",
+               dev_dbg(&iforce_usb->intf->dev, "urb->status %d, exiting\n",
                        urb->status);
                return;
        }
  
-       iforce_usb_xmit(iforce);
+       __iforce_usb_xmit(iforce);
  
        wake_up(&iforce->wait);
  }
  
- static void iforce_usb_ctrl(struct urb *urb)
- {
-       struct iforce *iforce = urb->context;
-       if (urb->status) return;
-       iforce->ecmd = 0xff00 | urb->actual_length;
-       wake_up(&iforce->wait);
- }
  static int iforce_usb_probe(struct usb_interface *intf,
                                const struct usb_device_id *id)
  {
        struct usb_device *dev = interface_to_usbdev(intf);
        struct usb_host_interface *interface;
        struct usb_endpoint_descriptor *epirq, *epout;
-       struct iforce *iforce;
+       struct iforce_usb *iforce_usb;
        int err = -ENOMEM;
  
        interface = intf->cur_altsetting;
        epirq = &interface->endpoint[0].desc;
        epout = &interface->endpoint[1].desc;
  
-       if (!(iforce = kzalloc(sizeof(struct iforce) + 32, GFP_KERNEL)))
-               goto fail;
-       if (!(iforce->irq = usb_alloc_urb(0, GFP_KERNEL)))
+       iforce_usb = kzalloc(sizeof(*iforce_usb), GFP_KERNEL);
+       if (!iforce_usb)
                goto fail;
  
-       if (!(iforce->out = usb_alloc_urb(0, GFP_KERNEL)))
+       iforce_usb->irq = usb_alloc_urb(0, GFP_KERNEL);
+       if (!iforce_usb->irq)
                goto fail;
  
-       if (!(iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL)))
+       iforce_usb->out = usb_alloc_urb(0, GFP_KERNEL);
+       if (!iforce_usb->out)
                goto fail;
  
-       iforce->bus = IFORCE_USB;
-       iforce->usbdev = dev;
-       iforce->intf = intf;
-       iforce->cr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
-       iforce->cr.wIndex = 0;
-       iforce->cr.wLength = cpu_to_le16(16);
+       iforce_usb->iforce.xport_ops = &iforce_usb_xport_ops;
  
-       usb_fill_int_urb(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),
-                       iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);
+       iforce_usb->usbdev = dev;
+       iforce_usb->intf = intf;
  
-       usb_fill_int_urb(iforce->out, dev, usb_sndintpipe(dev, epout->bEndpointAddress),
-                       iforce + 1, 32, iforce_usb_out, iforce, epout->bInterval);
+       usb_fill_int_urb(iforce_usb->irq, dev,
+                        usb_rcvintpipe(dev, epirq->bEndpointAddress),
+                        iforce_usb->data_in, sizeof(iforce_usb->data_in),
+                        iforce_usb_irq, iforce_usb, epirq->bInterval);
  
-       usb_fill_control_urb(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),
-                       (void*) &iforce->cr, iforce->edata, 16, iforce_usb_ctrl, iforce);
+       usb_fill_int_urb(iforce_usb->out, dev,
+                        usb_sndintpipe(dev, epout->bEndpointAddress),
+                        iforce_usb->data_out, sizeof(iforce_usb->data_out),
+                        iforce_usb_out, iforce_usb, epout->bInterval);
  
-       err = iforce_init_device(iforce);
+       err = iforce_init_device(&intf->dev, BUS_USB, &iforce_usb->iforce);
        if (err)
                goto fail;
  
-       usb_set_intfdata(intf, iforce);
+       usb_set_intfdata(intf, iforce_usb);
        return 0;
  
  fail:
-       if (iforce) {
-               usb_free_urb(iforce->irq);
-               usb_free_urb(iforce->out);
-               usb_free_urb(iforce->ctrl);
-               kfree(iforce);
+       if (iforce_usb) {
+               usb_free_urb(iforce_usb->irq);
+               usb_free_urb(iforce_usb->out);
+               kfree(iforce_usb);
        }
  
        return err;
  
  static void iforce_usb_disconnect(struct usb_interface *intf)
  {
-       struct iforce *iforce = usb_get_intfdata(intf);
+       struct iforce_usb *iforce_usb = usb_get_intfdata(intf);
  
        usb_set_intfdata(intf, NULL);
  
-       input_unregister_device(iforce->dev);
+       input_unregister_device(iforce_usb->iforce.dev);
  
-       usb_free_urb(iforce->irq);
-       usb_free_urb(iforce->out);
-       usb_free_urb(iforce->ctrl);
+       usb_free_urb(iforce_usb->irq);
+       usb_free_urb(iforce_usb->out);
  
-       kfree(iforce);
+       kfree(iforce_usb);
  }
  
  static const struct usb_device_id iforce_usb_ids[] = {
        { USB_DEVICE(0x05ef, 0x8888) },         /* AVB Top Shot FFB Racing Wheel */
        { USB_DEVICE(0x061c, 0xc0a4) },         /* ACT LABS Force RS */
        { USB_DEVICE(0x061c, 0xc084) },         /* ACT LABS Force RS */
+       { USB_DEVICE(0x06a3, 0xff04) },         /* Saitek R440 Force Wheel */
        { USB_DEVICE(0x06f8, 0x0001) },         /* Guillemot Race Leader Force Feedback */
        { USB_DEVICE(0x06f8, 0x0003) },         /* Guillemot Jet Leader Force Feedback */
        { USB_DEVICE(0x06f8, 0x0004) },         /* Guillemot Force Feedback Racing Wheel */
@@@ -217,3 -301,9 +289,9 @@@ struct usb_driver iforce_usb_driver = 
        .disconnect =   iforce_usb_disconnect,
        .id_table =     iforce_usb_ids,
  };
+ module_usb_driver(iforce_usb_driver);
+ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>");
+ MODULE_DESCRIPTION("USB I-Force joysticks and wheels driver");
+ MODULE_LICENSE("GPL");
index f1681706f5266803f98a62e1258e03bcc91a1262,3e91cc381488a97027fa3087ad6cfa083620b6d3..9cfa460466aa783fb11f76e1b3838fd9a5fa6eb4
@@@ -1,4 -1,3 +1,4 @@@
 +/* SPDX-License-Identifier: GPL-2.0-or-later */
  /*
   *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
   *  Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
@@@ -7,6 -6,19 +7,6 @@@
   */
  
  /*
 - * 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.
 - *
 - * This program is distributed in the hope that it will be useful,
 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 - * GNU General Public License for more details.
 - *
 - * You should have received a copy of the GNU General Public License
 - * along with this program; if not, write to the Free Software
 - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
   */
  
  #include <linux/kernel.h>
@@@ -14,8 -26,6 +14,6 @@@
  #include <linux/input.h>
  #include <linux/module.h>
  #include <linux/spinlock.h>
- #include <linux/usb.h>
- #include <linux/serio.h>
  #include <linux/circ_buf.h>
  #include <linux/mutex.h>
  
  
  #define IFORCE_MAX_LENGTH     16
  
- /* iforce::bus */
- #define IFORCE_232    1
- #define IFORCE_USB    2
  #define IFORCE_EFFECTS_MAX    32
  
  /* Each force feedback effect is made of one core effect, which can be
@@@ -81,27 -87,21 +75,21 @@@ struct iforce_device 
        signed short *ff;
  };
  
+ struct iforce;
+ struct iforce_xport_ops {
+       void (*xmit)(struct iforce *iforce);
+       int (*get_id)(struct iforce *iforce, u8 id,
+                     u8 *response_data, size_t *response_len);
+       int (*start_io)(struct iforce *iforce);
+       void (*stop_io)(struct iforce *iforce);
+ };
  struct iforce {
        struct input_dev *dev;          /* Input device interface */
        struct iforce_device *type;
-       int bus;
-       unsigned char data[IFORCE_MAX_LENGTH];
-       unsigned char edata[IFORCE_MAX_LENGTH];
-       u16 ecmd;
-       u16 expect_packet;
- #ifdef CONFIG_JOYSTICK_IFORCE_232
-       struct serio *serio;            /* RS232 transfer */
-       int idx, pkt, len, id;
-       unsigned char csum;
- #endif
- #ifdef CONFIG_JOYSTICK_IFORCE_USB
-       struct usb_device *usbdev;      /* USB transfer */
-       struct usb_interface *intf;
-       struct urb *irq, *out, *ctrl;
-       struct usb_ctrlrequest cr;
- #endif
+       const struct iforce_xport_ops *xport_ops;
        spinlock_t xmit_lock;
        /* Buffer used for asynchronous sending of bytes to the device */
        struct circ_buf xmit;
  /* Encode a time value */
  #define TIME_SCALE(a) (a)
  
+ static inline int iforce_get_id_packet(struct iforce *iforce, u8 id,
+                                      u8 *response_data, size_t *response_len)
+ {
+       return iforce->xport_ops->get_id(iforce, id,
+                                        response_data, response_len);
+ }
  
  /* Public functions */
- /* iforce-serio.c */
- void iforce_serial_xmit(struct iforce *iforce);
- /* iforce-usb.c */
- void iforce_usb_xmit(struct iforce *iforce);
  /* iforce-main.c */
- int iforce_init_device(struct iforce *iforce);
+ int iforce_init_device(struct device *parent, u16 bustype,
+                      struct iforce *iforce);
  
  /* iforce-packets.c */
  int iforce_control_playback(struct iforce*, u16 id, unsigned int);
- void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data);
+ void iforce_process_packet(struct iforce *iforce,
+                          u8 packet_id, u8 *data, size_t len);
  int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data);
  void iforce_dump_packet(struct iforce *iforce, char *msg, u16 cmd, unsigned char *data);
- int iforce_get_id_packet(struct iforce *iforce, char *packet);
  
  /* iforce-ff.c */
  int iforce_upload_periodic(struct iforce *, struct ff_effect *, struct ff_effect *);
index a23c23979a2e1ab792e28e51a0bace4345e1d563,c186c2552b04b6017c384a0289ee49602457bb80..03f4d152f6b7a298ff6ce7390c55e0958dcd664b
@@@ -1,9 -1,12 +1,9 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   * Driver for keys on GPIO lines capable of generating interrupts.
   *
   * Copyright 2005 Phil Blundell
   * Copyright 2010, 2011 David Jander <david@protonic.nl>
 - *
 - * This program is free software; you can redistribute it and/or modify
 - * it under the terms of the GNU General Public License version 2 as
 - * published by the Free Software Foundation.
   */
  
  #include <linux/module.h>
@@@ -771,7 -774,6 +771,6 @@@ static int gpio_keys_probe(struct platf
        struct fwnode_handle *child = NULL;
        struct gpio_keys_drvdata *ddata;
        struct input_dev *input;
-       size_t size;
        int i, error;
        int wakeup = 0;
  
                        return PTR_ERR(pdata);
        }
  
-       size = sizeof(struct gpio_keys_drvdata) +
-                       pdata->nbuttons * sizeof(struct gpio_button_data);
-       ddata = devm_kzalloc(dev, size, GFP_KERNEL);
+       ddata = devm_kzalloc(dev, struct_size(ddata, data, pdata->nbuttons),
+                            GFP_KERNEL);
        if (!ddata) {
                dev_err(dev, "failed to allocate state\n");
                return -ENOMEM;
index 1eafe6b848ba51f5cb04763aa9ff5cc90e0ab42a,033655ab5ed0812ce5c5229b4891850104125f5a..465eecfa6b3f0b95476db3810a998fca91ba51eb
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   *  Driver for buttons on GPIO lines not capable of generating interrupts
   *
@@@ -10,6 -9,10 +10,6 @@@
   *
   *  also was based on: /drivers/input/keyboard/gpio_keys.c
   *    Copyright 2005 Phil Blundell
 - *
 - *  This program is free software; you can redistribute it and/or modify
 - *  it under the terms of the GNU General Public License version 2 as
 - *  published by the Free Software Foundation.
   */
  
  #include <linux/kernel.h>
@@@ -165,6 -168,8 +165,8 @@@ gpio_keys_polled_get_devtree_pdata(stru
        pdata->rep = device_property_present(dev, "autorepeat");
        device_property_read_u32(dev, "poll-interval", &pdata->poll_interval);
  
+       device_property_read_string(dev, "label", &pdata->name);
        device_for_each_child_node(dev, child) {
                if (fwnode_property_read_u32(child, "linux,code",
                                             &button->code)) {
@@@ -232,7 -237,6 +234,6 @@@ static int gpio_keys_polled_probe(struc
        struct gpio_keys_polled_dev *bdev;
        struct input_polled_dev *poll_dev;
        struct input_dev *input;
-       size_t size;
        int error;
        int i;
  
                return -EINVAL;
        }
  
-       size = sizeof(struct gpio_keys_polled_dev) +
-                       pdata->nbuttons * sizeof(struct gpio_keys_button_data);
-       bdev = devm_kzalloc(dev, size, GFP_KERNEL);
+       bdev = devm_kzalloc(dev, struct_size(bdev, data, pdata->nbuttons),
+                           GFP_KERNEL);
        if (!bdev) {
                dev_err(dev, "no memory for private data\n");
                return -ENOMEM;
  
        input = poll_dev->input;
  
-       input->name = pdev->name;
+       input->name = pdata->name ?: pdev->name;
        input->phys = DRV_NAME"/input0";
  
        input->id.bustype = BUS_HOST;
index 9d39679372c5ae350b6ace3c65d788f271e3daf2,c06e067bd62768fcc4d159dace8cfd387f467137..fd355cf593979ea5eab2f9dca547d9f24d5aced8
@@@ -1,4 -1,4 +1,4 @@@
 -// SPDX-License-Identifier: GPL-2.0+
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /*
   * OnKey device driver for DA9063, DA9062 and DA9061 PMICs
   * Copyright (C) 2015  Dialog Semiconductor Ltd.
@@@ -13,7 -13,6 +13,6 @@@
  #include <linux/regmap.h>
  #include <linux/of.h>
  #include <linux/mfd/da9063/core.h>
- #include <linux/mfd/da9063/pdata.h>
  #include <linux/mfd/da9063/registers.h>
  #include <linux/mfd/da9062/core.h>
  #include <linux/mfd/da9062/registers.h>
@@@ -192,8 -191,6 +191,6 @@@ static void da9063_cancel_poll(void *da
  
  static int da9063_onkey_probe(struct platform_device *pdev)
  {
-       struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent);
-       struct da9063_pdata *pdata = dev_get_platdata(da9063->dev);
        struct da9063_onkey *onkey;
        const struct of_device_id *match;
        int irq;
                return -ENXIO;
        }
  
-       if (pdata)
-               onkey->key_power = pdata->key_power;
-       else
-               onkey->key_power =
-                       !of_property_read_bool(pdev->dev.of_node,
-                                              "dlg,disable-key-power");
+       onkey->key_power = !of_property_read_bool(pdev->dev.of_node,
+                                                 "dlg,disable-key-power");
  
        onkey->input = devm_input_allocate_device(&pdev->dev);
        if (!onkey->input) {
index 420efaab3860f2f4fcc96bc47566d14f146b3b4a,e2c824abd19c42969cbb082ce028eac8e160e001..d9b103a81a798e4a22a224504c7f7b445d6e68a7
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   * Elan I2C/SMBus Touchpad driver
   *
   * copyright (c) 2011-2012 Cypress Semiconductor, Inc.
   * copyright (c) 2011-2012 Google, Inc.
   *
 - * This program is free software; you can redistribute it and/or modify it
 - * under the terms of the GNU General Public License version 2 as published
 - * by the Free Software Foundation.
 - *
   * Trademarks are the property of their respective owners.
   */
  
@@@ -34,6 -37,7 +34,7 @@@
  #include <linux/completion.h>
  #include <linux/of.h>
  #include <linux/property.h>
+ #include <linux/input/elan-i2c-ids.h>
  #include <linux/regulator/consumer.h>
  #include <asm/unaligned.h>
  
@@@ -96,6 -100,7 +97,7 @@@ struct elan_tp_data 
        u8                      max_baseline;
        bool                    baseline_ready;
        u8                      clickpad;
+       bool                    middle_button;
  };
  
  static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
@@@ -363,27 -368,62 +365,62 @@@ static unsigned int elan_convert_resolu
  
  static int elan_query_device_parameters(struct elan_tp_data *data)
  {
+       struct i2c_client *client = data->client;
        unsigned int x_traces, y_traces;
+       u32 x_mm, y_mm;
        u8 hw_x_res, hw_y_res;
        int error;
  
-       error = data->ops->get_max(data->client, &data->max_x, &data->max_y);
-       if (error)
-               return error;
-       error = data->ops->get_num_traces(data->client, &x_traces, &y_traces);
-       if (error)
-               return error;
+       if (device_property_read_u32(&client->dev,
+                                    "touchscreen-size-x", &data->max_x) ||
+           device_property_read_u32(&client->dev,
+                                    "touchscreen-size-y", &data->max_y)) {
+               error = data->ops->get_max(data->client,
+                                          &data->max_x,
+                                          &data->max_y);
+               if (error)
+                       return error;
+       } else {
+               /* size is the maximum + 1 */
+               --data->max_x;
+               --data->max_y;
+       }
  
+       if (device_property_read_u32(&client->dev,
+                                    "elan,x_traces",
+                                    &x_traces) ||
+           device_property_read_u32(&client->dev,
+                                    "elan,y_traces",
+                                    &y_traces)) {
+               error = data->ops->get_num_traces(data->client,
+                                                 &x_traces, &y_traces);
+               if (error)
+                       return error;
+       }
        data->width_x = data->max_x / x_traces;
        data->width_y = data->max_y / y_traces;
  
-       error = data->ops->get_resolution(data->client, &hw_x_res, &hw_y_res);
-       if (error)
-               return error;
+       if (device_property_read_u32(&client->dev,
+                                    "touchscreen-x-mm", &x_mm) ||
+           device_property_read_u32(&client->dev,
+                                    "touchscreen-y-mm", &y_mm)) {
+               error = data->ops->get_resolution(data->client,
+                                                 &hw_x_res, &hw_y_res);
+               if (error)
+                       return error;
+               data->x_res = elan_convert_resolution(hw_x_res);
+               data->y_res = elan_convert_resolution(hw_y_res);
+       } else {
+               data->x_res = (data->max_x + 1) / x_mm;
+               data->y_res = (data->max_y + 1) / y_mm;
+       }
+       if (device_property_read_bool(&client->dev, "elan,clickpad"))
+               data->clickpad = 1;
  
-       data->x_res = elan_convert_resolution(hw_x_res);
-       data->y_res = elan_convert_resolution(hw_y_res);
+       if (device_property_read_bool(&client->dev, "elan,middle-button"))
+               data->middle_button = true;
  
        return 0;
  }
@@@ -923,8 -963,9 +960,9 @@@ static void elan_report_absolute(struc
                        finger_data += ETP_FINGER_DATA_LEN;
        }
  
-       input_report_key(input, BTN_LEFT, tp_info & 0x01);
-       input_report_key(input, BTN_RIGHT, tp_info & 0x02);
+       input_report_key(input, BTN_LEFT,   tp_info & BIT(0));
+       input_report_key(input, BTN_MIDDLE, tp_info & BIT(2));
+       input_report_key(input, BTN_RIGHT,  tp_info & BIT(1));
        input_report_abs(input, ABS_DISTANCE, hover_event != 0);
        input_mt_report_pointer_emulation(input, true);
        input_sync(input);
@@@ -1058,10 -1099,13 +1096,13 @@@ static int elan_setup_input_device(stru
  
        __set_bit(EV_ABS, input->evbit);
        __set_bit(INPUT_PROP_POINTER, input->propbit);
-       if (data->clickpad)
+       if (data->clickpad) {
                __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
-       else
+       } else {
                __set_bit(BTN_RIGHT, input->keybit);
+               if (data->middle_button)
+                       __set_bit(BTN_MIDDLE, input->keybit);
+       }
        __set_bit(BTN_LEFT, input->keybit);
  
        /* Set up ST parameters */
@@@ -1332,55 -1376,6 +1373,6 @@@ static const struct i2c_device_id elan_
  MODULE_DEVICE_TABLE(i2c, elan_id);
  
  #ifdef CONFIG_ACPI
- static const struct acpi_device_id elan_acpi_id[] = {
-       { "ELAN0000", 0 },
-       { "ELAN0100", 0 },
-       { "ELAN0600", 0 },
-       { "ELAN0601", 0 },
-       { "ELAN0602", 0 },
-       { "ELAN0603", 0 },
-       { "ELAN0604", 0 },
-       { "ELAN0605", 0 },
-       { "ELAN0606", 0 },
-       { "ELAN0607", 0 },
-       { "ELAN0608", 0 },
-       { "ELAN0609", 0 },
-       { "ELAN060B", 0 },
-       { "ELAN060C", 0 },
-       { "ELAN060F", 0 },
-       { "ELAN0610", 0 },
-       { "ELAN0611", 0 },
-       { "ELAN0612", 0 },
-       { "ELAN0615", 0 },
-       { "ELAN0616", 0 },
-       { "ELAN0617", 0 },
-       { "ELAN0618", 0 },
-       { "ELAN0619", 0 },
-       { "ELAN061A", 0 },
-       { "ELAN061B", 0 },
-       { "ELAN061C", 0 },
-       { "ELAN061D", 0 },
-       { "ELAN061E", 0 },
-       { "ELAN061F", 0 },
-       { "ELAN0620", 0 },
-       { "ELAN0621", 0 },
-       { "ELAN0622", 0 },
-       { "ELAN0623", 0 },
-       { "ELAN0624", 0 },
-       { "ELAN0625", 0 },
-       { "ELAN0626", 0 },
-       { "ELAN0627", 0 },
-       { "ELAN0628", 0 },
-       { "ELAN0629", 0 },
-       { "ELAN062A", 0 },
-       { "ELAN062B", 0 },
-       { "ELAN062C", 0 },
-       { "ELAN062D", 0 },
-       { "ELAN0631", 0 },
-       { "ELAN0632", 0 },
-       { "ELAN1000", 0 },
-       { }
- };
  MODULE_DEVICE_TABLE(acpi, elan_acpi_id);
  #endif
  
index a4345052abd257f5611d2de0b7c8e20013f50e5a,ea1ee0f44a650e180060079c981e2184144f6459..2d8434b7b62381a85abdb28daba2391fdd117e40
@@@ -1,9 -1,12 +1,9 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   * Elantech Touchpad driver (v6)
   *
   * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net>
   *
 - * This program is free software; you can redistribute it and/or modify it
 - * under the terms of the GNU General Public License version 2 as published
 - * by the Free Software Foundation.
 - *
   * Trademarks are the property of their respective owners.
   */
  
@@@ -226,6 -229,52 +226,52 @@@ static void elantech_packet_dump(struc
                       psmouse->pktsize, psmouse->packet);
  }
  
+ /*
+  * Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in
+  * fw_version for this is based on the following fw_version & caps table:
+  *
+  * Laptop-model:           fw_version:     caps:           buttons:
+  * Acer S3                 0x461f00        10, 13, 0e      clickpad
+  * Acer S7-392             0x581f01        50, 17, 0d      clickpad
+  * Acer V5-131             0x461f02        01, 16, 0c      clickpad
+  * Acer V5-551             0x461f00        ?               clickpad
+  * Asus K53SV              0x450f01        78, 15, 0c      2 hw buttons
+  * Asus G46VW              0x460f02        00, 18, 0c      2 hw buttons
+  * Asus G750JX             0x360f00        00, 16, 0c      2 hw buttons
+  * Asus TP500LN            0x381f17        10, 14, 0e      clickpad
+  * Asus X750JN             0x381f17        10, 14, 0e      clickpad
+  * Asus UX31               0x361f00        20, 15, 0e      clickpad
+  * Asus UX32VD             0x361f02        00, 15, 0e      clickpad
+  * Avatar AVIU-145A2       0x361f00        ?               clickpad
+  * Fujitsu CELSIUS H760    0x570f02        40, 14, 0c      3 hw buttons (**)
+  * Fujitsu CELSIUS H780    0x5d0f02        41, 16, 0d      3 hw buttons (**)
+  * Fujitsu LIFEBOOK E544   0x470f00        d0, 12, 09      2 hw buttons
+  * Fujitsu LIFEBOOK E546   0x470f00        50, 12, 09      2 hw buttons
+  * Fujitsu LIFEBOOK E547   0x470f00        50, 12, 09      2 hw buttons
+  * Fujitsu LIFEBOOK E554   0x570f01        40, 14, 0c      2 hw buttons
+  * Fujitsu LIFEBOOK E557   0x570f01        40, 14, 0c      2 hw buttons
+  * Fujitsu T725            0x470f01        05, 12, 09      2 hw buttons
+  * Fujitsu H730            0x570f00        c0, 14, 0c      3 hw buttons (**)
+  * Gigabyte U2442          0x450f01        58, 17, 0c      2 hw buttons
+  * Lenovo L430             0x350f02        b9, 15, 0c      2 hw buttons (*)
+  * Lenovo L530             0x350f02        b9, 15, 0c      2 hw buttons (*)
+  * Samsung NF210           0x150b00        78, 14, 0a      2 hw buttons
+  * Samsung NP770Z5E        0x575f01        10, 15, 0f      clickpad
+  * Samsung NP700Z5B        0x361f06        21, 15, 0f      clickpad
+  * Samsung NP900X3E-A02    0x575f03        ?               clickpad
+  * Samsung NP-QX410        0x851b00        19, 14, 0c      clickpad
+  * Samsung RC512           0x450f00        08, 15, 0c      2 hw buttons
+  * Samsung RF710           0x450f00        ?               2 hw buttons
+  * System76 Pangolin       0x250f01        ?               2 hw buttons
+  * (*) + 3 trackpoint buttons
+  * (**) + 0 trackpoint buttons
+  * Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps
+  */
+ static inline int elantech_is_buttonpad(struct elantech_device_info *info)
+ {
+       return info->fw_version & 0x001000;
+ }
  /*
   * Interpret complete data packets and report absolute mode input events for
   * hardware version 1. (4 byte packets)
@@@ -523,7 -572,7 +569,7 @@@ static void elantech_report_absolute_v3
        input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
  
        /* For clickpads map both buttons to BTN_LEFT */
-       if (etd->info.fw_version & 0x001000)
+       if (elantech_is_buttonpad(&etd->info))
                input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
        else
                psmouse_report_standard_buttons(dev, packet[0]);
@@@ -541,7 -590,7 +587,7 @@@ static void elantech_input_sync_v4(stru
        unsigned char *packet = psmouse->packet;
  
        /* For clickpads map both buttons to BTN_LEFT */
-       if (etd->info.fw_version & 0x001000)
+       if (elantech_is_buttonpad(&etd->info))
                input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
        else
                psmouse_report_standard_buttons(dev, packet[0]);
@@@ -991,88 -1040,6 +1037,6 @@@ static int elantech_set_absolute_mode(s
        return rc;
  }
  
- static int elantech_set_range(struct psmouse *psmouse,
-                             unsigned int *x_min, unsigned int *y_min,
-                             unsigned int *x_max, unsigned int *y_max,
-                             unsigned int *width)
- {
-       struct elantech_data *etd = psmouse->private;
-       struct elantech_device_info *info = &etd->info;
-       unsigned char param[3];
-       unsigned char traces;
-       switch (info->hw_version) {
-       case 1:
-               *x_min = ETP_XMIN_V1;
-               *y_min = ETP_YMIN_V1;
-               *x_max = ETP_XMAX_V1;
-               *y_max = ETP_YMAX_V1;
-               break;
-       case 2:
-               if (info->fw_version == 0x020800 ||
-                   info->fw_version == 0x020b00 ||
-                   info->fw_version == 0x020030) {
-                       *x_min = ETP_XMIN_V2;
-                       *y_min = ETP_YMIN_V2;
-                       *x_max = ETP_XMAX_V2;
-                       *y_max = ETP_YMAX_V2;
-               } else {
-                       int i;
-                       int fixed_dpi;
-                       i = (info->fw_version > 0x020800 &&
-                            info->fw_version < 0x020900) ? 1 : 2;
-                       if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
-                               return -1;
-                       fixed_dpi = param[1] & 0x10;
-                       if (((info->fw_version >> 16) == 0x14) && fixed_dpi) {
-                               if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
-                                       return -1;
-                               *x_max = (info->capabilities[1] - i) * param[1] / 2;
-                               *y_max = (info->capabilities[2] - i) * param[2] / 2;
-                       } else if (info->fw_version == 0x040216) {
-                               *x_max = 819;
-                               *y_max = 405;
-                       } else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) {
-                               *x_max = 900;
-                               *y_max = 500;
-                       } else {
-                               *x_max = (info->capabilities[1] - i) * 64;
-                               *y_max = (info->capabilities[2] - i) * 64;
-                       }
-               }
-               break;
-       case 3:
-               if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
-                       return -1;
-               *x_max = (0x0f & param[0]) << 8 | param[1];
-               *y_max = (0xf0 & param[0]) << 4 | param[2];
-               break;
-       case 4:
-               if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
-                       return -1;
-               *x_max = (0x0f & param[0]) << 8 | param[1];
-               *y_max = (0xf0 & param[0]) << 4 | param[2];
-               traces = info->capabilities[1];
-               if ((traces < 2) || (traces > *x_max))
-                       return -1;
-               *width = *x_max / (traces - 1);
-               break;
-       }
-       return 0;
- }
  /*
   * (value from firmware) * 10 + 790 = dpi
   * we also have to convert dpi to dots/mm (*10/254 to avoid floating point)
@@@ -1099,53 -1066,12 +1063,12 @@@ static int elantech_get_resolution_v4(s
        return 0;
  }
  
- /*
-  * Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in
-  * fw_version for this is based on the following fw_version & caps table:
-  *
-  * Laptop-model:           fw_version:     caps:           buttons:
-  * Acer S3                 0x461f00        10, 13, 0e      clickpad
-  * Acer S7-392             0x581f01        50, 17, 0d      clickpad
-  * Acer V5-131             0x461f02        01, 16, 0c      clickpad
-  * Acer V5-551             0x461f00        ?               clickpad
-  * Asus K53SV              0x450f01        78, 15, 0c      2 hw buttons
-  * Asus G46VW              0x460f02        00, 18, 0c      2 hw buttons
-  * Asus G750JX             0x360f00        00, 16, 0c      2 hw buttons
-  * Asus TP500LN            0x381f17        10, 14, 0e      clickpad
-  * Asus X750JN             0x381f17        10, 14, 0e      clickpad
-  * Asus UX31               0x361f00        20, 15, 0e      clickpad
-  * Asus UX32VD             0x361f02        00, 15, 0e      clickpad
-  * Avatar AVIU-145A2       0x361f00        ?               clickpad
-  * Fujitsu CELSIUS H760    0x570f02        40, 14, 0c      3 hw buttons (**)
-  * Fujitsu CELSIUS H780    0x5d0f02        41, 16, 0d      3 hw buttons (**)
-  * Fujitsu LIFEBOOK E544   0x470f00        d0, 12, 09      2 hw buttons
-  * Fujitsu LIFEBOOK E546   0x470f00        50, 12, 09      2 hw buttons
-  * Fujitsu LIFEBOOK E547   0x470f00        50, 12, 09      2 hw buttons
-  * Fujitsu LIFEBOOK E554   0x570f01        40, 14, 0c      2 hw buttons
-  * Fujitsu LIFEBOOK E557   0x570f01        40, 14, 0c      2 hw buttons
-  * Fujitsu T725            0x470f01        05, 12, 09      2 hw buttons
-  * Fujitsu H730            0x570f00        c0, 14, 0c      3 hw buttons (**)
-  * Gigabyte U2442          0x450f01        58, 17, 0c      2 hw buttons
-  * Lenovo L430             0x350f02        b9, 15, 0c      2 hw buttons (*)
-  * Lenovo L530             0x350f02        b9, 15, 0c      2 hw buttons (*)
-  * Samsung NF210           0x150b00        78, 14, 0a      2 hw buttons
-  * Samsung NP770Z5E        0x575f01        10, 15, 0f      clickpad
-  * Samsung NP700Z5B        0x361f06        21, 15, 0f      clickpad
-  * Samsung NP900X3E-A02    0x575f03        ?               clickpad
-  * Samsung NP-QX410        0x851b00        19, 14, 0c      clickpad
-  * Samsung RC512           0x450f00        08, 15, 0c      2 hw buttons
-  * Samsung RF710           0x450f00        ?               2 hw buttons
-  * System76 Pangolin       0x250f01        ?               2 hw buttons
-  * (*) + 3 trackpoint buttons
-  * (**) + 0 trackpoint buttons
-  * Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps
-  */
  static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
  {
        struct input_dev *dev = psmouse->dev;
        struct elantech_data *etd = psmouse->private;
  
-       if (etd->info.fw_version & 0x001000) {
+       if (elantech_is_buttonpad(&etd->info)) {
                __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
                __clear_bit(BTN_RIGHT, dev->keybit);
        }
@@@ -1181,16 -1107,6 +1104,6 @@@ static const struct dmi_system_id elant
        { }
  };
  
- static const char * const middle_button_pnp_ids[] = {
-       "LEN2131", /* ThinkPad P52 w/ NFC */
-       "LEN2132", /* ThinkPad P52 */
-       "LEN2133", /* ThinkPad P72 w/ NFC */
-       "LEN2134", /* ThinkPad P72 */
-       "LEN0407",
-       "LEN0408",
-       NULL
- };
  /*
   * Set the appropriate event bits for the input subsystem
   */
@@@ -1199,10 -1115,9 +1112,9 @@@ static int elantech_set_input_params(st
        struct input_dev *dev = psmouse->dev;
        struct elantech_data *etd = psmouse->private;
        struct elantech_device_info *info = &etd->info;
-       unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0;
-       if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width))
-               return -1;
+       unsigned int x_min = info->x_min, y_min = info->y_min,
+                    x_max = info->x_max, y_max = info->y_max,
+                    width = info->width;
  
        __set_bit(INPUT_PROP_POINTER, dev->propbit);
        __set_bit(EV_KEY, dev->evbit);
        __clear_bit(EV_REL, dev->evbit);
  
        __set_bit(BTN_LEFT, dev->keybit);
-       if (dmi_check_system(elantech_dmi_has_middle_button) ||
-                       psmouse_matches_pnp_id(psmouse, middle_button_pnp_ids))
+       if (info->has_middle_button)
                __set_bit(BTN_MIDDLE, dev->keybit);
        __set_bit(BTN_RIGHT, dev->keybit);
  
@@@ -1686,6 -1600,7 +1597,7 @@@ static int elantech_query_info(struct p
                               struct elantech_device_info *info)
  {
        unsigned char param[3];
+       unsigned char traces;
  
        memset(info, 0, sizeof(*info));
  
                }
        }
  
+       /* query range information */
+       switch (info->hw_version) {
+       case 1:
+               info->x_min = ETP_XMIN_V1;
+               info->y_min = ETP_YMIN_V1;
+               info->x_max = ETP_XMAX_V1;
+               info->y_max = ETP_YMAX_V1;
+               break;
+       case 2:
+               if (info->fw_version == 0x020800 ||
+                   info->fw_version == 0x020b00 ||
+                   info->fw_version == 0x020030) {
+                       info->x_min = ETP_XMIN_V2;
+                       info->y_min = ETP_YMIN_V2;
+                       info->x_max = ETP_XMAX_V2;
+                       info->y_max = ETP_YMAX_V2;
+               } else {
+                       int i;
+                       int fixed_dpi;
+                       i = (info->fw_version > 0x020800 &&
+                            info->fw_version < 0x020900) ? 1 : 2;
+                       if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+                               return -EINVAL;
+                       fixed_dpi = param[1] & 0x10;
+                       if (((info->fw_version >> 16) == 0x14) && fixed_dpi) {
+                               if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
+                                       return -EINVAL;
+                               info->x_max = (info->capabilities[1] - i) * param[1] / 2;
+                               info->y_max = (info->capabilities[2] - i) * param[2] / 2;
+                       } else if (info->fw_version == 0x040216) {
+                               info->x_max = 819;
+                               info->y_max = 405;
+                       } else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) {
+                               info->x_max = 900;
+                               info->y_max = 500;
+                       } else {
+                               info->x_max = (info->capabilities[1] - i) * 64;
+                               info->y_max = (info->capabilities[2] - i) * 64;
+                       }
+               }
+               break;
+       case 3:
+               if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+                       return -EINVAL;
+               info->x_max = (0x0f & param[0]) << 8 | param[1];
+               info->y_max = (0xf0 & param[0]) << 4 | param[2];
+               break;
+       case 4:
+               if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+                       return -EINVAL;
+               info->x_max = (0x0f & param[0]) << 8 | param[1];
+               info->y_max = (0xf0 & param[0]) << 4 | param[2];
+               traces = info->capabilities[1];
+               if ((traces < 2) || (traces > info->x_max))
+                       return -EINVAL;
+               info->width = info->x_max / (traces - 1);
+               /* column number of traces */
+               info->x_traces = traces;
+               /* row number of traces */
+               traces = info->capabilities[2];
+               if ((traces >= 2) && (traces <= info->y_max))
+                       info->y_traces = traces;
+               break;
+       }
+       /* check for the middle button: DMI matching or new v4 firmwares */
+       info->has_middle_button = dmi_check_system(elantech_dmi_has_middle_button) ||
+                                 (ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version) &&
+                                  !elantech_is_buttonpad(info));
        return 0;
  }
  
@@@ -1780,10 -1779,6 +1776,6 @@@ static const char * const i2c_blacklist
         * These are known to not be working properly as bits are missing
         * in elan_i2c.
         */
-       "LEN2131", /* ThinkPad P52 w/ NFC */
-       "LEN2132", /* ThinkPad P52 */
-       "LEN2133", /* ThinkPad P72 w/ NFC */
-       "LEN2134", /* ThinkPad P72 */
        NULL
  };
  
@@@ -1791,17 -1786,45 +1783,45 @@@ static int elantech_create_smbus(struc
                                 struct elantech_device_info *info,
                                 bool leave_breadcrumbs)
  {
-       const struct property_entry i2c_properties[] = {
-               PROPERTY_ENTRY_BOOL("elan,trackpoint"),
-               { },
-       };
+       struct property_entry i2c_props[11] = {};
        struct i2c_board_info smbus_board = {
                I2C_BOARD_INFO("elan_i2c", 0x15),
                .flags = I2C_CLIENT_HOST_NOTIFY,
        };
+       unsigned int idx = 0;
+       smbus_board.properties = i2c_props;
+       i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-x",
+                                                  info->x_max + 1);
+       i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-y",
+                                                  info->y_max + 1);
+       i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-min-x",
+                                                  info->x_min);
+       i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-min-y",
+                                                  info->y_min);
+       if (info->x_res)
+               i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-x-mm",
+                                                     (info->x_max + 1) / info->x_res);
+       if (info->y_res)
+               i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-y-mm",
+                                                     (info->y_max + 1) / info->y_res);
  
        if (info->has_trackpoint)
-               smbus_board.properties = i2c_properties;
+               i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,trackpoint");
+       if (info->has_middle_button)
+               i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,middle-button");
+       if (info->x_traces)
+               i2c_props[idx++] = PROPERTY_ENTRY_U32("elan,x_traces",
+                                                     info->x_traces);
+       if (info->y_traces)
+               i2c_props[idx++] = PROPERTY_ENTRY_U32("elan,y_traces",
+                                                     info->y_traces);
+       if (elantech_is_buttonpad(info))
+               i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,clickpad");
  
        return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false,
                                  leave_breadcrumbs);
index 12ba5af931453d1ccfeba36fb0056dc845099de4,a7eaa62af6a06f43d1a5138a57b02c0fb6853fc3..46343998522b047cfa9b0e531a935a1dc5a0ace2
@@@ -1,9 -1,12 +1,9 @@@
 +/* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * Elantech Touchpad driver (v6)
   *
   * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net>
   *
 - * This program is free software; you can redistribute it and/or modify it
 - * under the terms of the GNU General Public License version 2 as published
 - * by the Free Software Foundation.
 - *
   * Trademarks are the property of their respective owners.
   */
  
@@@ -141,8 -144,15 +141,15 @@@ struct elantech_device_info 
        unsigned char debug;
        unsigned char hw_version;
        unsigned int fw_version;
+       unsigned int x_min;
+       unsigned int y_min;
+       unsigned int x_max;
+       unsigned int y_max;
        unsigned int x_res;
        unsigned int y_res;
+       unsigned int x_traces;
+       unsigned int y_traces;
+       unsigned int width;
        unsigned int bus;
        bool paritycheck;
        bool jumpy_cursor;
        bool crc_enabled;
        bool set_hw_resolution;
        bool has_trackpoint;
+       bool has_middle_button;
        int (*send_cmd)(struct psmouse *psmouse, unsigned char c,
                        unsigned char *param);
  };
index b8ec301025b7c7a88b17dff84925234f97d4077d,68fd8232d44cf9387e19589fb5152969b9bca1ff..1080c0c498154a789f89c42e4854c1307eb0d4b1
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   * Synaptics TouchPad PS/2 mouse driver
   *
   *   Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com>
   *     code for the special synaptics commands (from the tpconfig-source)
   *
 - * This program is free software; you can redistribute it and/or modify it
 - * under the terms of the GNU General Public License version 2 as published by
 - * the Free Software Foundation.
 - *
   * Trademarks are the property of their respective owners.
   */
  
@@@ -173,6 -176,7 +173,7 @@@ static const char * const smbus_pnp_ids
        "LEN0072", /* X1 Carbon Gen 5 (2017) - Elan/ALPS trackpoint */
        "LEN0073", /* X1 Carbon G5 (Elantech) */
        "LEN0092", /* X1 Carbon 6 */
+       "LEN0093", /* T480 */
        "LEN0096", /* X280 */
        "LEN0097", /* X280 -> ALPS trackpoint */
        "LEN200f", /* T450s */
index bb14369e34a78cf8a4d321384167fc58db9e6adf,3b7d7b940cab73844c79c7e8d36cb60d8e066950..d20a5d6780d1d40b209f40da0c9342493aef9ed3
@@@ -1,6 -1,9 +1,6 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   * Copyright (c) 2012-2016 Synaptics Incorporated
 - *
 - * This program is free software; you can redistribute it and/or modify it
 - * under the terms of the GNU General Public License version 2 as published by
 - * the Free Software Foundation.
   */
  #include <linux/input.h>
  #include <linux/input/mt.h>
@@@ -70,7 -73,6 +70,6 @@@ static int rmi_f12_read_sensor_tuning(s
        int pitch_y = 0;
        int rx_receivers = 0;
        int tx_receivers = 0;
-       int sensor_flags = 0;
  
        item = rmi_get_register_desc_item(&f12->control_reg_desc, 8);
        if (!item) {
                offset += 2;
        }
  
-       if (rmi_register_desc_has_subpacket(item, 4)) {
-               sensor_flags = buf[offset];
+       /* Skip over sensor flags */
+       if (rmi_register_desc_has_subpacket(item, 4))
                offset += 1;
-       }
  
        sensor->x_mm = (pitch_x * rx_receivers) >> 12;
        sensor->y_mm = (pitch_y * tx_receivers) >> 12;
index 19378f200c635706a3f8388a31a8b81a94281792,6f4f9da0515cc7b601fb8355db7be2bc44add676..4a5f482cf1af5550e6f504b11c3bdf44d4319b67
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /*
   * Atmel maXTouch Touchscreen driver
   *
@@@ -8,6 -7,12 +8,6 @@@
   * Copyright (C) 2016 Zodiac Inflight Innovations
   *
   * Author: Joonyoung Shim <jy0922.shim@samsung.com>
 - *
 - * 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.
 - *
   */
  
  #include <linux/acpi.h>
@@@ -256,16 -261,6 +256,6 @@@ enum v4l_dbg_inputs 
        MXT_V4L_INPUT_MAX,
  };
  
- static const struct v4l2_file_operations mxt_video_fops = {
-       .owner = THIS_MODULE,
-       .open = v4l2_fh_open,
-       .release = vb2_fop_release,
-       .unlocked_ioctl = video_ioctl2,
-       .read = vb2_fop_read,
-       .mmap = vb2_fop_mmap,
-       .poll = vb2_fop_poll,
- };
  enum mxt_suspend_mode {
        MXT_SUSPEND_DEEP_SLEEP  = 0,
        MXT_SUSPEND_T9_CTRL     = 1,
@@@ -1521,7 -1516,8 +1511,8 @@@ static int mxt_update_cfg(struct mxt_da
                } else if (config_crc == data->config_crc) {
                        dev_dbg(dev, "Config CRC 0x%06X: OK\n",
                                 data->config_crc);
-                       return 0;
+                       ret = 0;
+                       goto release_raw;
                } else {
                        dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
                                 data->config_crc, config_crc);
@@@ -2218,6 -2214,16 +2209,16 @@@ recheck
  }
  
  #ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37
+ static const struct v4l2_file_operations mxt_video_fops = {
+       .owner = THIS_MODULE,
+       .open = v4l2_fh_open,
+       .release = vb2_fop_release,
+       .unlocked_ioctl = video_ioctl2,
+       .read = vb2_fop_read,
+       .mmap = vb2_fop_mmap,
+       .poll = vb2_fop_poll,
+ };
  static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x,
                               unsigned int y)
  {
index c6b85ba7f991fe5b43ec1d4b901df92fca31d817,b508d2693c0e28f4a7ac9c64f2e7f2d768e5bad1..2e1404cd09ec9f62491f082d9598b8d1c71e1ac8
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /*
   * Touch Screen driver for EETI's I2C connected touch screen panels
   *   Copyright (c) 2009,2018 Daniel Mack <daniel@zonque.org>
@@@ -9,6 -8,20 +9,6 @@@
   * Based on migor_ts.c
   *   Copyright (c) 2008 Magnus Damm
   *   Copyright (c) 2007 Ujjwal Pande <ujjwal@kenati.com>
 - *
 - * This file 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.
 - *
 - * This file is distributed in the hope that it will be useful,
 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - *  General Public License for more details.
 - *
 - * You should have received a copy of the GNU General Public
 - * License along with this library; if not, write to the Free Software
 - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  #include <linux/module.h>
@@@ -28,6 -41,7 +28,7 @@@ struct eeti_ts 
        struct input_dev *input;
        struct gpio_desc *attn_gpio;
        struct touchscreen_properties props;
+       struct mutex mutex;
        bool running;
  };
  
@@@ -62,42 -76,80 +63,80 @@@ static void eeti_ts_report_event(struc
        input_sync(eeti->input);
  }
  
+ static int eeti_ts_read(struct eeti_ts *eeti)
+ {
+       int len, error;
+       char buf[6];
+       len = i2c_master_recv(eeti->client, buf, sizeof(buf));
+       if (len != sizeof(buf)) {
+               error = len < 0 ? len : -EIO;
+               dev_err(&eeti->client->dev,
+                       "failed to read touchscreen data: %d\n",
+                       error);
+               return error;
+       }
+       /* Motion packet */
+       if (buf[0] & 0x80)
+               eeti_ts_report_event(eeti, buf);
+       return 0;
+ }
  static irqreturn_t eeti_ts_isr(int irq, void *dev_id)
  {
        struct eeti_ts *eeti = dev_id;
-       int len;
        int error;
-       char buf[6];
+       mutex_lock(&eeti->mutex);
  
        do {
-               len = i2c_master_recv(eeti->client, buf, sizeof(buf));
-               if (len != sizeof(buf)) {
-                       error = len < 0 ? len : -EIO;
-                       dev_err(&eeti->client->dev,
-                               "failed to read touchscreen data: %d\n",
-                               error);
+               /*
+                * If we have attention GPIO, trust it. Otherwise we'll read
+                * once and exit. We assume that in this case we are using
+                * level triggered interrupt and it will get raised again
+                * if/when there is more data.
+                */
+               if (eeti->attn_gpio &&
+                   !gpiod_get_value_cansleep(eeti->attn_gpio)) {
                        break;
                }
  
-               if (buf[0] & 0x80) {
-                       /* Motion packet */
-                       eeti_ts_report_event(eeti, buf);
-               }
-       } while (eeti->running &&
-                eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio));
+               error = eeti_ts_read(eeti);
+               if (error)
+                       break;
+       } while (eeti->running && eeti->attn_gpio);
  
+       mutex_unlock(&eeti->mutex);
        return IRQ_HANDLED;
  }
  
  static void eeti_ts_start(struct eeti_ts *eeti)
  {
+       mutex_lock(&eeti->mutex);
        eeti->running = true;
-       wmb();
        enable_irq(eeti->client->irq);
+       /*
+        * Kick the controller in case we are using edge interrupt and
+        * we missed our edge while interrupt was disabled. We expect
+        * the attention GPIO to be wired in this case.
+        */
+       if (eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio))
+               eeti_ts_read(eeti);
+       mutex_unlock(&eeti->mutex);
  }
  
  static void eeti_ts_stop(struct eeti_ts *eeti)
  {
+       /*
+        * Not locking here, just setting a flag and expect that the
+        * interrupt thread will notice the flag eventually.
+        */
        eeti->running = false;
        wmb();
        disable_irq(eeti->client->irq);
@@@ -140,6 -192,8 +179,8 @@@ static int eeti_ts_probe(struct i2c_cli
                return -ENOMEM;
        }
  
+       mutex_init(&eeti->mutex);
        input = devm_input_allocate_device(dev);
        if (!input) {
                dev_err(dev, "Failed to allocate input device.\n");