Merge tag 'for-linus-2022052401' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 24 May 2022 22:21:15 +0000 (15:21 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 24 May 2022 22:21:15 +0000 (15:21 -0700)
Pull HID updates from Jiri Kosina:

 - support for pens with 3 buttons with Wacom driver (Joshua Dickens)

 - support for HID_DG_SCANTIME to report the timestamp for pen and touch
   events in Wacom driver (Joshua Dickens)

 - support for sensor discovery in amd-sfh driver (Basavaraj Natikar)

 - support for wider variety of Huion tablets ported from DIGImend
   project (José Expósito, Nikolai Kondrashov)

 - new device IDs and other assorted small code cleanups

* tag 'for-linus-2022052401' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (44 commits)
  HID: apple: Properly handle function keys on Keychron keyboards
  HID: uclogic: Switch to Digitizer usage for styluses
  HID: uclogic: Add pen support for XP-PEN Star 06
  HID: uclogic: Differentiate touch ring and touch strip
  HID: uclogic: Always shift touch reports to zero
  HID: uclogic: Do not focus on touch ring only
  HID: uclogic: Return raw parameters from v2 pen init
  HID: uclogic: Move param printing to a function
  HID: core: Display "SENSOR HUB" for sensor hub bus string in hid_info
  HID: amd_sfh: Move bus declaration outside of amd-sfh
  HID: amd_sfh: Add physical location to HID device
  HID: amd_sfh: Modify the hid name
  HID: amd_sfh: Modify the bus name
  HID: amd_sfh: Add sensor name by index for debug info
  HID: amd_sfh: Add support for sensor discovery
  HID: bigben: fix slab-out-of-bounds Write in bigben_probe
  Hid: wacom: Fix kernel test robot warning
  HID: uclogic: Disable pen usage for Huion keyboard interfaces
  HID: uclogic: Support disabling pen usage
  HID: uclogic: Pass keyboard reports as is
  ...

31 files changed:
MAINTAINERS
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/amd-sfh-hid/amd_sfh_client.c
drivers/hid/amd-sfh-hid/amd_sfh_hid.c
drivers/hid/amd-sfh-hid/amd_sfh_hid.h
drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h
drivers/hid/hid-apple.c
drivers/hid/hid-bigbenff.c
drivers/hid/hid-core.c
drivers/hid/hid-elan.c
drivers/hid/hid-ids.h
drivers/hid/hid-kye.c
drivers/hid/hid-led.c
drivers/hid/hid-lenovo.c
drivers/hid/hid-megaworld.c [new file with mode: 0644]
drivers/hid/hid-multitouch.c
drivers/hid/hid-uclogic-core.c
drivers/hid/hid-uclogic-params.c
drivers/hid/hid-uclogic-params.h
drivers/hid/hid-uclogic-rdesc.c
drivers/hid/hid-uclogic-rdesc.h
drivers/hid/hid-viewsonic.c
drivers/hid/intel-ish-hid/ipc/hw-ish.h
drivers/hid/intel-ish-hid/ipc/pci-ish.c
drivers/hid/wacom_sys.c
drivers/hid/wacom_wac.c
drivers/hid/wacom_wac.h
include/uapi/linux/input.h

index 4ec3ddf..e7b0bfc 100644 (file)
@@ -1044,7 +1044,6 @@ F:        arch/arm64/boot/dts/amd/amd-seattle-xgbe*.dtsi
 F:     drivers/net/ethernet/amd/xgbe/
 
 AMD SENSOR FUSION HUB DRIVER
-M:     Nehal Shah <nehal-bakulchandra.shah@amd.com>
 M:     Basavaraj Natikar <basavaraj.natikar@amd.com>
 L:     linux-input@vger.kernel.org
 S:     Maintained
@@ -8766,6 +8765,14 @@ F:       drivers/hid/hid-sensor-*
 F:     drivers/iio/*/hid-*
 F:     include/linux/hid-sensor-*
 
+HID WACOM DRIVER
+M:     Ping Cheng <ping.cheng@wacom.com>
+M:     Jason Gerecke  <jason.gerecke@wacom.com>
+L:     linux-input@vger.kernel.org
+S:     Maintained
+F:     drivers/hid/wacom.h
+F:     drivers/hid/wacom_*
+
 HIGH-RESOLUTION TIMERS, CLOCKEVENTS
 M:     Thomas Gleixner <tglx@linutronix.de>
 L:     linux-kernel@vger.kernel.org
index a95a7cb..70da593 100644 (file)
@@ -697,6 +697,14 @@ config HID_MAYFLASH
        Say Y here if you have HJZ Mayflash PS3 game controller adapters
        and want to enable force feedback support.
 
+config HID_MEGAWORLD_FF
+       tristate "Mega World based game controller force feedback support"
+       depends on USB_HID
+       select INPUT_FF_MEMLESS
+       help
+       Say Y here if you have a Mega World based game controller and want
+       to have force feedback support for it.
+
 config HID_REDRAGON
        tristate "Redragon keyboards"
        depends on HID
index 345ac55..cac2cbe 100644 (file)
@@ -77,6 +77,7 @@ obj-$(CONFIG_HID_MAGICMOUSE)  += hid-magicmouse.o
 obj-$(CONFIG_HID_MALTRON)      += hid-maltron.o
 obj-$(CONFIG_HID_MCP2221)      += hid-mcp2221.o
 obj-$(CONFIG_HID_MAYFLASH)     += hid-mf.o
+obj-$(CONFIG_HID_MEGAWORLD_FF) += hid-megaworld.o
 obj-$(CONFIG_HID_MICROSOFT)    += hid-microsoft.o
 obj-$(CONFIG_HID_MONTEREY)     += hid-monterey.o
 obj-$(CONFIG_HID_MULTITOUCH)   += hid-multitouch.o
index c5de0ec..0f770a2 100644 (file)
@@ -141,6 +141,24 @@ u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
        return sensor_sts;
 }
 
+const char *get_sensor_name(int idx)
+{
+       switch (idx) {
+       case accel_idx:
+               return "accelerometer";
+       case gyro_idx:
+               return "gyroscope";
+       case mag_idx:
+               return "magnetometer";
+       case als_idx:
+               return "ALS";
+       case HPD_IDX:
+               return "HPD";
+       default:
+               return "unknown sensor type";
+       }
+}
+
 int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
 {
        struct amd_input_data *in_data = &privdata->in_data;
@@ -219,13 +237,27 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
                                        (privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);
                                if (status != SENSOR_ENABLED)
                                        cl_data->sensor_sts[i] = SENSOR_DISABLED;
-                               dev_dbg(dev, "sid 0x%x status 0x%x\n",
-                                       cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
+                               dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
+                                       cl_data->sensor_idx[i],
+                                       get_sensor_name(cl_data->sensor_idx[i]),
+                                       cl_data->sensor_sts[i]);
                                goto cleanup;
                        }
                }
-               dev_dbg(dev, "sid 0x%x status 0x%x\n",
-                       cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
+               dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
+                       cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
+                       cl_data->sensor_sts[i]);
+       }
+       if (privdata->mp2_ops->discovery_status &&
+           privdata->mp2_ops->discovery_status(privdata) == 0) {
+               amd_sfh_hid_client_deinit(privdata);
+               for (i = 0; i < cl_data->num_hid_devices; i++) {
+                       devm_kfree(dev, cl_data->feature_report[i]);
+                       devm_kfree(dev, in_data->input_report[i]);
+                       devm_kfree(dev, cl_data->report_descr[i]);
+               }
+               dev_warn(dev, "Failed to discover, sensors not enabled\n");
+               return -EOPNOTSUPP;
        }
        schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
        return 0;
@@ -257,8 +289,9 @@ int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)
                                        (privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);
                        if (status != SENSOR_ENABLED)
                                cl_data->sensor_sts[i] = SENSOR_DISABLED;
-                       dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x status 0x%x\n",
-                               cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
+                       dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x (%s) status 0x%x\n",
+                               cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
+                               cl_data->sensor_sts[i]);
                }
        }
 
index 2bf97b6..1089134 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/sched.h>
 
 #include "amd_sfh_hid.h"
+#include "amd_sfh_pcie.h"
 
 #define AMD_SFH_RESPONSE_TIMEOUT       1500
 
@@ -120,6 +121,8 @@ static struct hid_ll_driver amdtp_hid_ll_driver = {
 
 int amdtp_hid_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data)
 {
+       struct amd_mp2_dev *mp2 = container_of(cli_data->in_data, struct amd_mp2_dev, in_data);
+       struct device *dev = &mp2->pdev->dev;
        struct hid_device *hid;
        struct amdtp_hid_data *hid_data;
        int rc;
@@ -141,10 +144,12 @@ int amdtp_hid_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data)
 
        hid->driver_data = hid_data;
        cli_data->hid_sensor_hubs[cur_hid_dev] = hid;
-       hid->bus = BUS_AMD_AMDTP;
+       strscpy(hid->phys, dev->driver ? dev->driver->name : dev_name(dev),
+               sizeof(hid->phys));
+       hid->bus = BUS_AMD_SFH;
        hid->vendor = AMD_SFH_HID_VENDOR;
        hid->product = AMD_SFH_HID_PRODUCT;
-       snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-amdtp",
+       snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-amdsfh",
                 hid->vendor, hid->product);
 
        rc = hid_add_device(hid);
index c60abd3..ad264db 100644 (file)
@@ -12,7 +12,6 @@
 #define AMDSFH_HID_H
 
 #define MAX_HID_DEVICES                5
-#define BUS_AMD_AMDTP          0x20
 #define AMD_SFH_HID_VENDOR     0x1022
 #define AMD_SFH_HID_PRODUCT    0x0001
 
index 6b5fd90..dadc491 100644 (file)
@@ -130,6 +130,12 @@ static int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata)
        return 0;
 }
 
+static int amd_sfh_dis_sts_v2(struct amd_mp2_dev *privdata)
+{
+       return (readl(privdata->mmio + AMD_P2C_MSG(1)) &
+                     SENSOR_DISCOVERY_STATUS_MASK) >> SENSOR_DISCOVERY_STATUS_SHIFT;
+}
+
 void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
 {
        union sfh_cmd_param cmd_param;
@@ -245,6 +251,7 @@ static const struct amd_mp2_ops amd_sfh_ops_v2 = {
        .response = amd_sfh_wait_response_v2,
        .clear_intr = amd_sfh_clear_intr_v2,
        .init_intr = amd_sfh_irq_init_v2,
+       .discovery_status = amd_sfh_dis_sts_v2,
 };
 
 static const struct amd_mp2_ops amd_sfh_ops = {
@@ -346,8 +353,9 @@ static int __maybe_unused amd_mp2_pci_resume(struct device *dev)
                                        (mp2, cl_data->sensor_idx[i], SENSOR_ENABLED);
                        if (status == SENSOR_ENABLED)
                                cl_data->sensor_sts[i] = SENSOR_ENABLED;
-                       dev_dbg(dev, "resume sid 0x%x status 0x%x\n",
-                               cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
+                       dev_dbg(dev, "suspend sid 0x%x (%s) status 0x%x\n",
+                               cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
+                               cl_data->sensor_sts[i]);
                }
        }
 
@@ -371,8 +379,9 @@ static int __maybe_unused amd_mp2_pci_suspend(struct device *dev)
                                        (mp2, cl_data->sensor_idx[i], SENSOR_DISABLED);
                        if (status != SENSOR_ENABLED)
                                cl_data->sensor_sts[i] = SENSOR_DISABLED;
-                       dev_dbg(dev, "suspend sid 0x%x status 0x%x\n",
-                               cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
+                       dev_dbg(dev, "suspend sid 0x%x (%s) status 0x%x\n",
+                               cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
+                               cl_data->sensor_sts[i]);
                }
        }
 
index 97b9986..8c76052 100644 (file)
@@ -39,6 +39,9 @@
 
 #define AMD_SFH_IDLE_LOOP      200
 
+#define SENSOR_DISCOVERY_STATUS_MASK           GENMASK(5, 3)
+#define SENSOR_DISCOVERY_STATUS_SHIFT          3
+
 /* SFH Command register */
 union sfh_cmd_base {
        u32 ul;
@@ -135,6 +138,7 @@ int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata);
 u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts);
 void amd_mp2_suspend(struct amd_mp2_dev *mp2);
 void amd_mp2_resume(struct amd_mp2_dev *mp2);
+const char *get_sensor_name(int idx);
 
 struct amd_mp2_ops {
         void (*start)(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info);
@@ -143,5 +147,6 @@ struct amd_mp2_ops {
         int (*response)(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts);
         void (*clear_intr)(struct amd_mp2_dev *privdata);
         int (*init_intr)(struct amd_mp2_dev *privdata);
+        int (*discovery_status)(struct amd_mp2_dev *privdata);
 };
 #endif
index 8d97ca0..697f279 100644 (file)
@@ -179,7 +179,7 @@ static const u8 accel3_report_descriptor[] = {
 0xC0                   /* HID end collection */
 };
 
-const u8 gyro3_report_descriptor[] = {
+static const u8 gyro3_report_descriptor[] = {
 0x05, 0x20,            /* Usage page */
 0x09, 0x76,            /* Motion type Gyro3D */
 0xA1, 0x00,            /* HID Collection (Physical) */
@@ -340,7 +340,7 @@ const u8 gyro3_report_descriptor[] = {
 0xC0,                  /* HID end collection */
 };
 
-const u8 comp3_report_descriptor[] = {
+static const u8 comp3_report_descriptor[] = {
 0x05, 0x20,            /* Usage page */
 0x09, 0x83,            /* Motion type Orientation compass 3D */
 0xA1, 0x00,            /* HID Collection (Physical) */
@@ -512,7 +512,7 @@ const u8 comp3_report_descriptor[] = {
 0xC0                           /* HID end collection */
 };
 
-const u8 als_report_descriptor[] = {
+static const u8 als_report_descriptor[] = {
 0x05, 0x20,    /* HID usage page sensor */
 0x09, 0x41,    /* HID usage sensor type Ambientlight  */
 0xA1, 0x00,    /* HID Collection (Physical) */
index 0cf35ca..42a5689 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/timer.h>
+#include <linux/string.h>
 
 #include "hid-ids.h"
 
 #define APPLE_NUMLOCK_EMULATION        BIT(8)
 #define APPLE_RDESC_BATTERY    BIT(9)
 #define APPLE_BACKLIGHT_CTL    BIT(10)
+#define APPLE_IS_KEYCHRON      BIT(11)
 
 #define APPLE_FLAG_FKEY                0x01
 
 #define HID_COUNTRY_INTERNATIONAL_ISO  13
 #define APPLE_BATTERY_TIMEOUT_MS       60000
 
-static unsigned int fnmode = 1;
+static unsigned int fnmode = 3;
 module_param(fnmode, uint, 0644);
 MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, "
-               "[1] = fkeyslast, 2 = fkeysfirst)");
+               "1 = fkeyslast, 2 = fkeysfirst, [3] = auto)");
 
 static int iso_layout = -1;
 module_param(iso_layout, int, 0644);
@@ -349,6 +351,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
        const struct apple_key_translation *trans, *table;
        bool do_translate;
        u16 code = 0;
+       unsigned int real_fnmode;
 
        u16 fn_keycode = (swap_fn_leftctrl) ? (KEY_LEFTCTRL) : (KEY_FN);
 
@@ -359,7 +362,13 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
                return 1;
        }
 
-       if (fnmode) {
+       if (fnmode == 3) {
+               real_fnmode = (asc->quirks & APPLE_IS_KEYCHRON) ? 2 : 1;
+       } else {
+               real_fnmode = fnmode;
+       }
+
+       if (real_fnmode) {
                if (hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI ||
                    hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO ||
                    hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS ||
@@ -406,7 +415,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
 
                        if (!code) {
                                if (trans->flags & APPLE_FLAG_FKEY) {
-                                       switch (fnmode) {
+                                       switch (real_fnmode) {
                                        case 1:
                                                do_translate = !asc->fn_on;
                                                break;
@@ -660,6 +669,11 @@ static int apple_input_configured(struct hid_device *hdev,
                asc->quirks &= ~APPLE_HAS_FN;
        }
 
+       if (strncmp(hdev->name, "Keychron", 8) == 0) {
+               hid_info(hdev, "Keychron keyboard detected; function keys will default to fnmode=2 behavior\n");
+               asc->quirks |= APPLE_IS_KEYCHRON;
+       }
+
        return 0;
 }
 
index 74ad8bf..e8c5e3a 100644 (file)
@@ -347,6 +347,12 @@ static int bigben_probe(struct hid_device *hid,
        bigben->report = list_entry(report_list->next,
                struct hid_report, list);
 
+       if (list_empty(&hid->inputs)) {
+               hid_err(hid, "no inputs found\n");
+               error = -ENODEV;
+               goto error_hw_stop;
+       }
+
        hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
        set_bit(FF_RUMBLE, hidinput->input->ffbit);
 
index db92579..00154a1 100644 (file)
@@ -2222,6 +2222,10 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
        case BUS_VIRTUAL:
                bus = "VIRTUAL";
                break;
+       case BUS_INTEL_ISHTP:
+       case BUS_AMD_SFH:
+               bus = "SENSOR HUB";
+               break;
        default:
                bus = "<UNKNOWN>";
        }
index 3091355..8e4a552 100644 (file)
@@ -188,7 +188,6 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi)
        ret = input_mt_init_slots(input, ELAN_MAX_FINGERS, INPUT_MT_POINTER);
        if (ret) {
                hid_err(hdev, "Failed to init elan MT slots: %d\n", ret);
-               input_free_device(input);
                return ret;
        }
 
@@ -200,7 +199,6 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi)
                hid_err(hdev, "Failed to register elan input device: %d\n",
                        ret);
                input_mt_destroy_slots(input);
-               input_free_device(input);
                return ret;
        }
 
index 053853a..d9eb676 100644 (file)
 #define USB_VENDOR_ID_LENOVO           0x17ef
 #define USB_DEVICE_ID_LENOVO_TPKBD     0x6009
 #define USB_DEVICE_ID_LENOVO_CUSBKBD   0x6047
+#define USB_DEVICE_ID_LENOVO_TPIIUSBKBD        0x60ee
 #define USB_DEVICE_ID_LENOVO_CBTKBD    0x6048
+#define USB_DEVICE_ID_LENOVO_TPIIBTKBD 0x60e1
 #define USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL       0x6049
 #define USB_DEVICE_ID_LENOVO_TP10UBKBD 0x6062
 #define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067
 #define USB_DEVICE_ID_LENOVO_X1_COVER  0x6085
 #define USB_DEVICE_ID_LENOVO_X1_TAB    0x60a3
 #define USB_DEVICE_ID_LENOVO_X1_TAB3   0x60b5
+#define USB_DEVICE_ID_LENOVO_X12_TAB   0x60fe
 #define USB_DEVICE_ID_LENOVO_OPTICAL_USB_MOUSE_600E    0x600e
 #define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_608D     0x608d
 #define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_6019     0x6019
 #define USB_VENDOR_ID_MCS              0x16d0
 #define USB_DEVICE_ID_MCS_GAMEPADBLOCK 0x0bcc
 
+#define USB_VENDOR_MEGAWORLD           0x07b5
+#define USB_DEVICE_ID_MEGAWORLD_GAMEPAD        0x0312
+
 #define USB_VENDOR_ID_MGE              0x0463
 #define USB_DEVICE_ID_MGE_UPS          0xffff
 #define USB_DEVICE_ID_MGE_UPS1         0x0001
 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540   0x0075
 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640   0x0094
 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042
+#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06 0x0078
 #define USB_DEVICE_ID_UGEE_TABLET_G5           0x0074
 #define USB_DEVICE_ID_UGEE_TABLET_EX07S                0x0071
 #define USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720        0x0055
index f466163..da90313 100644 (file)
@@ -33,7 +33,7 @@ static __u8 easypen_i405x_rdesc_fixed[] = {
        0xB1, 0x02,       /*    Feature (Variable),           */
        0xC0,             /*  End Collection,                 */
        0x05, 0x0D,       /*  Usage Page (Digitizer),         */
-       0x09, 0x02,       /*  Usage (Pen),                    */
+       0x09, 0x01,       /*  Usage (Digitizer),              */
        0xA1, 0x01,       /*  Collection (Application),       */
        0x85, 0x10,       /*    Report ID (16),               */
        0x09, 0x20,       /*    Usage (Stylus),               */
@@ -91,7 +91,7 @@ static __u8 mousepen_i608x_rdesc_fixed[] = {
        0xB1, 0x02,       /*    Feature (Variable),           */
        0xC0,             /*  End Collection,                 */
        0x05, 0x0D,       /*  Usage Page (Digitizer),         */
-       0x09, 0x02,       /*  Usage (Pen),                    */
+       0x09, 0x01,       /*  Usage (Digitizer),              */
        0xA1, 0x01,       /*  Collection (Application),       */
        0x85, 0x10,       /*    Report ID (16),               */
        0x09, 0x20,       /*    Usage (Stylus),               */
@@ -190,7 +190,7 @@ static __u8 mousepen_i608x_v2_rdesc_fixed[] = {
        0xB1, 0x02,                   /*    Feature (Variable),           */
        0xC0,                         /*  End Collection,                 */
        0x05, 0x0D,                   /*  Usage Page (Digitizer),         */
-       0x09, 0x02,                   /*  Usage (Pen),                    */
+       0x09, 0x01,                   /*  Usage (Digitizer),              */
        0xA1, 0x01,                   /*  Collection (Application),       */
        0x85, 0x10,                   /*    Report ID (16),               */
        0x09, 0x20,                   /*    Usage (Stylus),               */
@@ -289,7 +289,7 @@ static __u8 easypen_m610x_rdesc_fixed[] = {
        0xB1, 0x02,                   /*    Feature (Variable),           */
        0xC0,                         /*  End Collection,                 */
        0x05, 0x0D,                   /*  Usage Page (Digitizer),         */
-       0x09, 0x02,                   /*  Usage (Pen),                    */
+       0x09, 0x01,                   /*  Usage (Digitizer),              */
        0xA1, 0x01,                   /*  Collection (Application),       */
        0x85, 0x10,                   /*    Report ID (16),               */
        0x09, 0x20,                   /*    Usage (Stylus),               */
@@ -368,7 +368,7 @@ static __u8 pensketch_m912_rdesc_fixed[] = {
        0xB1, 0x02,                   /*    Feature (Variable),           */
        0xC0,                         /*  End Collection,                 */
        0x05, 0x0D,                   /*  Usage Page (Digitizer),         */
-       0x09, 0x02,                   /*  Usage (Pen),                    */
+       0x09, 0x01,                   /*  Usage (Digitizer),              */
        0xA1, 0x01,                   /*  Collection (Application),       */
        0x85, 0x10,                   /*    Report ID (16),               */
        0x09, 0x20,                   /*    Usage (Stylus),               */
@@ -497,7 +497,7 @@ static __u8 easypen_m406xe_rdesc_fixed[] = {
        0xB1, 0x02,         /*      Feature (Variable),             */
        0xC0,               /*  End Collection,                     */
        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
-       0x09, 0x02,         /*  Usage (Pen),                        */
+       0x09, 0x01,         /*  Usage (Digitizer),                  */
        0xA1, 0x01,         /*  Collection (Application),           */
        0x85, 0x10,         /*      Report ID (16),                 */
        0x09, 0x20,         /*      Usage (Stylus),                 */
index c2c66ce..7d82f8d 100644 (file)
@@ -366,7 +366,7 @@ static const struct hidled_config hidled_configs[] = {
                .type = DREAM_CHEEKY,
                .name = "Dream Cheeky Webmail Notifier",
                .short_name = "dream_cheeky",
-               .max_brightness = 31,
+               .max_brightness = 63,
                .num_leds = 1,
                .report_size = 9,
                .report_type = RAW_REQUEST,
index 93b1f93..9dabd63 100644 (file)
@@ -4,6 +4,7 @@
  *  - ThinkPad USB Keyboard with TrackPoint (tpkbd)
  *  - ThinkPad Compact Bluetooth Keyboard with TrackPoint (cptkbd)
  *  - ThinkPad Compact USB Keyboard with TrackPoint (cptkbd)
+ *  - ThinkPad TrackPoint Keyboard II USB/Bluetooth (cptkbd/tpIIkbd)
  *
  *  Copyright (c) 2012 Bernhard Seibold
  *  Copyright (c) 2014 Jamie Lentin <jm@lentin.co.uk>
@@ -110,6 +111,23 @@ static const __u8 lenovo_pro_dock_need_fixup_collection[] = {
        0x2a, 0xff, 0xff,       /*  Usage Maximum (65535)               */
 };
 
+/* Broken ThinkPad TrackPoint II collection (Bluetooth mode) */
+static const __u8 lenovo_tpIIbtkbd_need_fixup_collection[] = {
+       0x06, 0x00, 0xFF,       /* Usage Page (Vendor Defined 0xFF00) */
+       0x09, 0x01,             /* Usage (0x01) */
+       0xA1, 0x01,             /* Collection (Application) */
+       0x85, 0x05,             /*   Report ID (5) */
+       0x1A, 0xF1, 0x00,       /*   Usage Minimum (0xF1) */
+       0x2A, 0xFC, 0x00,       /*   Usage Maximum (0xFC) */
+       0x15, 0x00,             /*   Logical Minimum (0) */
+       0x25, 0x01,             /*   Logical Maximum (1) */
+       0x75, 0x01,             /*   Report Size (1) */
+       0x95, 0x0D,             /*   Report Count (13) */
+       0x81, 0x02,             /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
+       0x95, 0x03,             /*   Report Count (3) */
+       0x81, 0x01,             /*   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
+};
+
 static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                unsigned int *rsize)
 {
@@ -126,6 +144,19 @@ static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                        rdesc[152] = 0x00;
                }
                break;
+       case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
+               if (*rsize >= 263 &&
+                   memcmp(&rdesc[234], lenovo_tpIIbtkbd_need_fixup_collection,
+                         sizeof(lenovo_tpIIbtkbd_need_fixup_collection)) == 0) {
+                       rdesc[244] = 0x00; /* usage minimum = 0x00 */
+                       rdesc[247] = 0xff; /* usage maximum = 0xff */
+                       rdesc[252] = 0xff; /* logical maximum = 0xff */
+                       rdesc[254] = 0x08; /* report size = 0x08 */
+                       rdesc[256] = 0x01; /* report count = 0x01 */
+                       rdesc[258] = 0x00; /* input = 0x00 */
+                       rdesc[260] = 0x01; /* report count (2) = 0x01 */
+               }
+               break;
        }
        return rdesc;
 }
@@ -217,6 +248,101 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev,
        return 0;
 }
 
+static int lenovo_input_mapping_tpIIkbd(struct hid_device *hdev,
+               struct hid_input *hi, struct hid_field *field,
+               struct hid_usage *usage, unsigned long **bit, int *max)
+{
+       /*
+        * 0xff0a0000 = USB, HID_UP_MSVENDOR = BT.
+        *
+        * In BT mode, there are two HID_UP_MSVENDOR pages.
+        * Use only the page that contains report ID == 5.
+        */
+       if (((usage->hid & HID_USAGE_PAGE) == 0xff0a0000 ||
+           (usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR) &&
+           field->report->id == 5) {
+               switch (usage->hid & HID_USAGE) {
+               case 0x00bb: /* Fn-F4: Mic mute */
+                       map_key_clear(LENOVO_KEY_MICMUTE);
+                       return 1;
+               case 0x00c3: /* Fn-F5: Brightness down */
+                       map_key_clear(KEY_BRIGHTNESSDOWN);
+                       return 1;
+               case 0x00c4: /* Fn-F6: Brightness up */
+                       map_key_clear(KEY_BRIGHTNESSUP);
+                       return 1;
+               case 0x00c1: /* Fn-F8: Notification center */
+                       map_key_clear(KEY_NOTIFICATION_CENTER);
+                       return 1;
+               case 0x00bc: /* Fn-F9: Control panel */
+                       map_key_clear(KEY_CONFIG);
+                       return 1;
+               case 0x00b6: /* Fn-F10: Bluetooth */
+                       map_key_clear(KEY_BLUETOOTH);
+                       return 1;
+               case 0x00b7: /* Fn-F11: Keyboard config */
+                       map_key_clear(KEY_KEYBOARD);
+                       return 1;
+               case 0x00b8: /* Fn-F12: User function */
+                       map_key_clear(KEY_PROG1);
+                       return 1;
+               case 0x00b9: /* Fn-PrtSc: Snipping tool */
+                       map_key_clear(KEY_SELECTIVE_SCREENSHOT);
+                       return 1;
+               case 0x00b5: /* Fn-Esc: Fn-lock toggle */
+                       map_key_clear(KEY_FN_ESC);
+                       return 1;
+               }
+       }
+
+       if ((usage->hid & HID_USAGE_PAGE) == 0xffa00000) {
+               switch (usage->hid & HID_USAGE) {
+               case 0x00fb: /* Middle mouse (in native USB mode) */
+                       map_key_clear(BTN_MIDDLE);
+                       return 1;
+               }
+       }
+
+       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR &&
+           field->report->id == 21) {
+               switch (usage->hid & HID_USAGE) {
+               case 0x0004: /* Middle mouse (in native Bluetooth mode) */
+                       map_key_clear(BTN_MIDDLE);
+                       return 1;
+               }
+       }
+
+       /* Compatibility middle/wheel mappings should be ignored */
+       if (usage->hid == HID_GD_WHEEL)
+               return -1;
+       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON &&
+                       (usage->hid & HID_USAGE) == 0x003)
+               return -1;
+       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER &&
+                       (usage->hid & HID_USAGE) == 0x238)
+               return -1;
+
+       /* Map wheel emulation reports: 0xff10 */
+       if ((usage->hid & HID_USAGE_PAGE) == 0xff100000) {
+               field->flags |= HID_MAIN_ITEM_RELATIVE | HID_MAIN_ITEM_VARIABLE;
+               field->logical_minimum = -127;
+               field->logical_maximum = 127;
+
+               switch (usage->hid & HID_USAGE) {
+               case 0x0000:
+                       hid_map_usage(hi, usage, bit, max, EV_REL, REL_HWHEEL);
+                       return 1;
+               case 0x0001:
+                       hid_map_usage(hi, usage, bit, max, EV_REL, REL_WHEEL);
+                       return 1;
+               default:
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
 static int lenovo_input_mapping_scrollpoint(struct hid_device *hdev,
                struct hid_input *hi, struct hid_field *field,
                struct hid_usage *usage, unsigned long **bit, int *max)
@@ -326,6 +452,10 @@ static int lenovo_input_mapping(struct hid_device *hdev,
        case USB_DEVICE_ID_LENOVO_CBTKBD:
                return lenovo_input_mapping_cptkbd(hdev, hi, field,
                                                        usage, bit, max);
+       case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
+       case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
+               return lenovo_input_mapping_tpIIkbd(hdev, hi, field,
+                                                       usage, bit, max);
        case USB_DEVICE_ID_IBM_SCROLLPOINT_III:
        case USB_DEVICE_ID_IBM_SCROLLPOINT_PRO:
        case USB_DEVICE_ID_IBM_SCROLLPOINT_OPTICAL:
@@ -357,16 +487,23 @@ static int lenovo_send_cmd_cptkbd(struct hid_device *hdev,
        if (!buf)
                return -ENOMEM;
 
+       /*
+        * Feature report 0x13 is used for USB,
+        * output report 0x18 is used for Bluetooth.
+        * buf[0] is ignored by hid_hw_raw_request.
+        */
        buf[0] = 0x18;
        buf[1] = byte2;
        buf[2] = byte3;
 
        switch (hdev->product) {
        case USB_DEVICE_ID_LENOVO_CUSBKBD:
+       case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
                ret = hid_hw_raw_request(hdev, 0x13, buf, 3,
                                        HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
                break;
        case USB_DEVICE_ID_LENOVO_CBTKBD:
+       case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
                ret = hid_hw_output_report(hdev, buf, 3);
                break;
        default:
@@ -422,6 +559,8 @@ static ssize_t attr_fn_lock_store(struct device *dev,
        switch (hdev->product) {
        case USB_DEVICE_ID_LENOVO_CUSBKBD:
        case USB_DEVICE_ID_LENOVO_CBTKBD:
+       case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
+       case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
                lenovo_features_set_cptkbd(hdev);
                break;
        case USB_DEVICE_ID_LENOVO_TP10UBKBD:
@@ -556,6 +695,15 @@ static int lenovo_event_cptkbd(struct hid_device *hdev,
                return 1;
        }
 
+       if (usage->type == EV_KEY && usage->code == KEY_FN_ESC && value == 1) {
+               /*
+                * The user has toggled the Fn-lock state. Toggle our own
+                * cached value of it and sync our value to the keyboard to
+                * ensure things are in sync (the syncing should be a no-op).
+                */
+               cptkbd_data->fn_lock = !cptkbd_data->fn_lock;
+       }
+
        return 0;
 }
 
@@ -568,6 +716,8 @@ static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
        switch (hdev->product) {
        case USB_DEVICE_ID_LENOVO_CUSBKBD:
        case USB_DEVICE_ID_LENOVO_CBTKBD:
+       case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
+       case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
                return lenovo_event_cptkbd(hdev, field, usage, value);
        case USB_DEVICE_ID_LENOVO_TP10UBKBD:
        case USB_DEVICE_ID_LENOVO_X1_TAB:
@@ -960,8 +1110,9 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev)
        struct lenovo_drvdata *cptkbd_data;
 
        /* All the custom action happens on the USBMOUSE device for USB */
-       if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD
-                       && hdev->type != HID_TYPE_USBMOUSE) {
+       if (((hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD) ||
+           (hdev->product == USB_DEVICE_ID_LENOVO_TPIIUSBKBD)) &&
+           hdev->type != HID_TYPE_USBMOUSE) {
                hid_dbg(hdev, "Ignoring keyboard half of device\n");
                return 0;
        }
@@ -977,11 +1128,14 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev)
 
        /*
         * Tell the keyboard a driver understands it, and turn F7, F9, F11 into
-        * regular keys
+        * regular keys (Compact only)
         */
-       ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03);
-       if (ret)
-               hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret);
+       if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD ||
+           hdev->product == USB_DEVICE_ID_LENOVO_CBTKBD) {
+               ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03);
+               if (ret)
+                       hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret);
+       }
 
        /* Switch middle button to native mode */
        ret = lenovo_send_cmd_cptkbd(hdev, 0x09, 0x01);
@@ -1088,6 +1242,8 @@ static int lenovo_probe(struct hid_device *hdev,
                break;
        case USB_DEVICE_ID_LENOVO_CUSBKBD:
        case USB_DEVICE_ID_LENOVO_CBTKBD:
+       case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
+       case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
                ret = lenovo_probe_cptkbd(hdev);
                break;
        case USB_DEVICE_ID_LENOVO_TP10UBKBD:
@@ -1154,6 +1310,8 @@ static void lenovo_remove(struct hid_device *hdev)
                break;
        case USB_DEVICE_ID_LENOVO_CUSBKBD:
        case USB_DEVICE_ID_LENOVO_CBTKBD:
+       case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
+       case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
                lenovo_remove_cptkbd(hdev);
                break;
        case USB_DEVICE_ID_LENOVO_TP10UBKBD:
@@ -1172,6 +1330,8 @@ static int lenovo_input_configured(struct hid_device *hdev,
                case USB_DEVICE_ID_LENOVO_TPKBD:
                case USB_DEVICE_ID_LENOVO_CUSBKBD:
                case USB_DEVICE_ID_LENOVO_CBTKBD:
+               case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
+               case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
                        if (test_bit(EV_REL, hi->input->evbit)) {
                                /* set only for trackpoint device */
                                __set_bit(INPUT_PROP_POINTER, hi->input->propbit);
@@ -1188,7 +1348,9 @@ static int lenovo_input_configured(struct hid_device *hdev,
 static const struct hid_device_id lenovo_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPIIUSBKBD) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPIIBTKBD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
        { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_III) },
        { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_PRO) },
diff --git a/drivers/hid/hid-megaworld.c b/drivers/hid/hid-megaworld.c
new file mode 100644 (file)
index 0000000..5996578
--- /dev/null
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Vibration support for Mega World controllers
+ *
+ * Copyright 2022 Frank Zago
+ *
+ * Derived from hid-zpff.c:
+ *   Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com>
+ */
+
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "hid-ids.h"
+
+struct mwctrl_device {
+       struct hid_report *report;
+       s32 *weak;
+       s32 *strong;
+};
+
+static int mwctrl_play(struct input_dev *dev, void *data,
+                      struct ff_effect *effect)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+       struct mwctrl_device *mwctrl = data;
+
+       *mwctrl->strong = effect->u.rumble.strong_magnitude >> 8;
+       *mwctrl->weak = effect->u.rumble.weak_magnitude >> 8;
+
+       hid_hw_request(hid, mwctrl->report, HID_REQ_SET_REPORT);
+
+       return 0;
+}
+
+static int mwctrl_init(struct hid_device *hid)
+{
+       struct mwctrl_device *mwctrl;
+       struct hid_report *report;
+       struct hid_input *hidinput;
+       struct input_dev *dev;
+       int error;
+       int i;
+
+       if (list_empty(&hid->inputs)) {
+               hid_err(hid, "no inputs found\n");
+               return -ENODEV;
+       }
+       hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+       dev = hidinput->input;
+
+       for (i = 0; i < 4; i++) {
+               report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1);
+               if (!report)
+                       return -ENODEV;
+       }
+
+       mwctrl = kzalloc(sizeof(struct mwctrl_device), GFP_KERNEL);
+       if (!mwctrl)
+               return -ENOMEM;
+
+       set_bit(FF_RUMBLE, dev->ffbit);
+
+       error = input_ff_create_memless(dev, mwctrl, mwctrl_play);
+       if (error) {
+               kfree(mwctrl);
+               return error;
+       }
+
+       mwctrl->report = report;
+
+       /* Field 0 is always 2, and field 1 is always 0. The original
+        * windows driver has a 5 bytes command, where the 5th byte is
+        * a repeat of the 3rd byte, however the device has only 4
+        * fields. It could be a bug in the driver, or there is a
+        * different device that needs it.
+        */
+       report->field[0]->value[0] = 0x02;
+
+       mwctrl->strong = &report->field[2]->value[0];
+       mwctrl->weak = &report->field[3]->value[0];
+
+       return 0;
+}
+
+static int mwctrl_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "parse failed\n");
+               return ret;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+       if (ret) {
+               hid_err(hdev, "hw start failed\n");
+               return ret;
+       }
+
+       ret = mwctrl_init(hdev);
+       if (ret)
+               hid_hw_stop(hdev);
+
+       return ret;
+}
+
+static const struct hid_device_id mwctrl_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_MEGAWORLD,
+                        USB_DEVICE_ID_MEGAWORLD_GAMEPAD) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, mwctrl_devices);
+
+static struct hid_driver mwctrl_driver = {
+       .name = "megaworld",
+       .id_table = mwctrl_devices,
+       .probe = mwctrl_probe,
+};
+module_hid_driver(mwctrl_driver);
+
+MODULE_LICENSE("GPL");
index 99eabfb..6bb3890 100644 (file)
@@ -2034,6 +2034,12 @@ static const struct hid_device_id mt_devices[] = {
                           USB_VENDOR_ID_LENOVO,
                           USB_DEVICE_ID_LENOVO_X1_TAB3) },
 
+       /* Lenovo X12 TAB Gen 1 */
+       { .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT,
+               HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8,
+                          USB_VENDOR_ID_LENOVO,
+                          USB_DEVICE_ID_LENOVO_X12_TAB) },
+
        /* MosArt panels */
        { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
                MT_USB_DEVICE(USB_VENDOR_ID_ASUS,
@@ -2178,6 +2184,9 @@ static const struct hid_device_id mt_devices[] = {
        { .driver_data = MT_CLS_GOOGLE,
                HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE,
                        USB_DEVICE_ID_GOOGLE_TOUCH_ROSE) },
+       { .driver_data = MT_CLS_GOOGLE,
+               HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, USB_VENDOR_ID_GOOGLE,
+                       USB_DEVICE_ID_GOOGLE_WHISKERS) },
 
        /* Generic MT device */
        { HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) },
index 05147f2..c0fe66e 100644 (file)
@@ -81,6 +81,24 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
        return rdesc;
 }
 
+static int uclogic_input_mapping(struct hid_device *hdev,
+                                struct hid_input *hi,
+                                struct hid_field *field,
+                                struct hid_usage *usage,
+                                unsigned long **bit,
+                                int *max)
+{
+       struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
+       struct uclogic_params *params = &drvdata->params;
+
+       /* Discard invalid pen usages */
+       if (params->pen.usage_invalid && (field->application == HID_DG_PEN))
+               return -1;
+
+       /* Let hid-core decide what to do */
+       return 0;
+}
+
 static int uclogic_input_configured(struct hid_device *hdev,
                struct hid_input *hi)
 {
@@ -90,6 +108,8 @@ static int uclogic_input_configured(struct hid_device *hdev,
        const char *suffix = NULL;
        struct hid_field *field;
        size_t len;
+       size_t i;
+       const struct uclogic_params_frame *frame;
 
        /* no report associated (HID_QUIRK_MULTI_INPUT not set) */
        if (!hi->report)
@@ -104,27 +124,44 @@ static int uclogic_input_configured(struct hid_device *hdev,
                drvdata->pen_input = hi->input;
        }
 
-       field = hi->report->field[0];
+       /* If it's one of the frame devices */
+       for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
+               frame = &params->frame_list[i];
+               if (hi->report->id == frame->id) {
+                       /* Assign custom suffix, if any */
+                       suffix = frame->suffix;
+                       /*
+                        * Disable EV_MSC reports for touch ring interfaces to
+                        * make the Wacom driver pickup touch ring extents
+                        */
+                       if (frame->touch_byte > 0)
+                               __clear_bit(EV_MSC, hi->input->evbit);
+               }
+       }
 
-       switch (field->application) {
-       case HID_GD_KEYBOARD:
-               suffix = "Keyboard";
-               break;
-       case HID_GD_MOUSE:
-               suffix = "Mouse";
-               break;
-       case HID_GD_KEYPAD:
-               suffix = "Pad";
-               break;
-       case HID_DG_PEN:
-               suffix = "Pen";
-               break;
-       case HID_CP_CONSUMER_CONTROL:
-               suffix = "Consumer Control";
-               break;
-       case HID_GD_SYSTEM_CONTROL:
-               suffix = "System Control";
-               break;
+       if (!suffix) {
+               field = hi->report->field[0];
+
+               switch (field->application) {
+               case HID_GD_KEYBOARD:
+                       suffix = "Keyboard";
+                       break;
+               case HID_GD_MOUSE:
+                       suffix = "Mouse";
+                       break;
+               case HID_GD_KEYPAD:
+                       suffix = "Pad";
+                       break;
+               case HID_DG_PEN:
+                       suffix = "Pen";
+                       break;
+               case HID_CP_CONSUMER_CONTROL:
+                       suffix = "Consumer Control";
+                       break;
+               case HID_GD_SYSTEM_CONTROL:
+                       suffix = "System Control";
+                       break;
+               }
        }
 
        if (suffix) {
@@ -172,8 +209,8 @@ static int uclogic_probe(struct hid_device *hdev,
                goto failure;
        }
        params_initialized = true;
-       hid_dbg(hdev, "parameters:\n" UCLOGIC_PARAMS_FMT_STR,
-               UCLOGIC_PARAMS_FMT_ARGS(&drvdata->params));
+       hid_dbg(hdev, "parameters:\n");
+       uclogic_params_hid_dbg(hdev, &drvdata->params);
        if (drvdata->params.invalid) {
                hid_info(hdev, "interface is invalid, ignoring\n");
                rc = -ENODEV;
@@ -313,8 +350,15 @@ static int uclogic_raw_event_frame(
 
        /* If need to, and can, set pad device ID for Wacom drivers */
        if (frame->dev_id_byte > 0 && frame->dev_id_byte < size) {
-               data[frame->dev_id_byte] = 0xf;
+               /* If we also have a touch ring and the finger left it */
+               if (frame->touch_byte > 0 && frame->touch_byte < size &&
+                   data[frame->touch_byte] == 0) {
+                       data[frame->dev_id_byte] = 0;
+               } else {
+                       data[frame->dev_id_byte] = 0xf;
+               }
        }
+
        /* If need to, and can, read rotary encoder state change */
        if (frame->re_lsb > 0 && frame->re_lsb / 8 < size) {
                unsigned int byte = frame->re_lsb / 8;
@@ -341,6 +385,26 @@ static int uclogic_raw_event_frame(
                drvdata->re_state = state;
        }
 
+       /* If need to, and can, transform the touch ring reports */
+       if (frame->touch_byte > 0 && frame->touch_byte < size) {
+               __s8 value = data[frame->touch_byte];
+
+               if (value != 0) {
+                       if (frame->touch_flip_at != 0) {
+                               value = frame->touch_flip_at - value;
+                               if (value <= 0)
+                                       value = frame->touch_max + value;
+                       }
+                       data[frame->touch_byte] = value - 1;
+               }
+       }
+
+       /* If need to, and can, transform the bitmap dial reports */
+       if (frame->bitmap_dial_byte > 0 && frame->bitmap_dial_byte < size) {
+               if (data[frame->bitmap_dial_byte] == 2)
+                       data[frame->bitmap_dial_byte] = -1;
+       }
+
        return 0;
 }
 
@@ -457,6 +521,8 @@ static const struct hid_device_id uclogic_devices[] = {
                                USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
                                USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
+                               USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) },
        { }
 };
 MODULE_DEVICE_TABLE(hid, uclogic_devices);
@@ -468,6 +534,7 @@ static struct hid_driver uclogic_driver = {
        .remove = uclogic_remove,
        .report_fixup = uclogic_report_fixup,
        .raw_event = uclogic_raw_event,
+       .input_mapping = uclogic_input_mapping,
        .input_configured = uclogic_input_configured,
 #ifdef CONFIG_PM
        .resume           = uclogic_resume,
index 5f50ceb..db838f1 100644 (file)
@@ -29,8 +29,8 @@
  * Returns:
  *     The string representing the type, or NULL if the type is unknown.
  */
-const char *uclogic_params_pen_inrange_to_str(
-                       enum uclogic_params_pen_inrange inrange)
+static const char *uclogic_params_pen_inrange_to_str(
+                               enum uclogic_params_pen_inrange inrange)
 {
        switch (inrange) {
        case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL:
@@ -44,6 +44,91 @@ const char *uclogic_params_pen_inrange_to_str(
        }
 }
 
+/**
+ * Dump tablet interface pen parameters with hid_dbg(), indented with one tab.
+ *
+ * @hdev:      The HID device the pen parameters describe.
+ * @pen:       The pen parameters to dump.
+ */
+static void uclogic_params_pen_hid_dbg(const struct hid_device *hdev,
+                                       const struct uclogic_params_pen *pen)
+{
+       size_t i;
+
+       hid_dbg(hdev, "\t.usage_invalid = %s\n",
+               (pen->usage_invalid ? "true" : "false"));
+       hid_dbg(hdev, "\t.desc_ptr = %p\n", pen->desc_ptr);
+       hid_dbg(hdev, "\t.desc_size = %u\n", pen->desc_size);
+       hid_dbg(hdev, "\t.id = %u\n", pen->id);
+       hid_dbg(hdev, "\t.subreport_list = {\n");
+       for (i = 0; i < ARRAY_SIZE(pen->subreport_list); i++) {
+               hid_dbg(hdev, "\t\t{0x%02hhx, %hhu}%s\n",
+                       pen->subreport_list[i].value,
+                       pen->subreport_list[i].id,
+                       i < (ARRAY_SIZE(pen->subreport_list) - 1) ? "," : "");
+       }
+       hid_dbg(hdev, "\t}\n");
+       hid_dbg(hdev, "\t.inrange = %s\n",
+               uclogic_params_pen_inrange_to_str(pen->inrange));
+       hid_dbg(hdev, "\t.fragmented_hires = %s\n",
+               (pen->fragmented_hires ? "true" : "false"));
+       hid_dbg(hdev, "\t.tilt_y_flipped = %s\n",
+               (pen->tilt_y_flipped ? "true" : "false"));
+}
+
+/**
+ * Dump tablet interface frame parameters with hid_dbg(), indented with two
+ * tabs.
+ *
+ * @hdev:      The HID device the pen parameters describe.
+ * @frame:     The frame parameters to dump.
+ */
+static void uclogic_params_frame_hid_dbg(
+                               const struct hid_device *hdev,
+                               const struct uclogic_params_frame *frame)
+{
+       hid_dbg(hdev, "\t\t.desc_ptr = %p\n", frame->desc_ptr);
+       hid_dbg(hdev, "\t\t.desc_size = %u\n", frame->desc_size);
+       hid_dbg(hdev, "\t\t.id = %u\n", frame->id);
+       hid_dbg(hdev, "\t\t.suffix = %s\n", frame->suffix);
+       hid_dbg(hdev, "\t\t.re_lsb = %u\n", frame->re_lsb);
+       hid_dbg(hdev, "\t\t.dev_id_byte = %u\n", frame->dev_id_byte);
+       hid_dbg(hdev, "\t\t.touch_byte = %u\n", frame->touch_byte);
+       hid_dbg(hdev, "\t\t.touch_max = %hhd\n", frame->touch_max);
+       hid_dbg(hdev, "\t\t.touch_flip_at = %hhd\n",
+               frame->touch_flip_at);
+       hid_dbg(hdev, "\t\t.bitmap_dial_byte = %u\n",
+               frame->bitmap_dial_byte);
+}
+
+/**
+ * Dump tablet interface parameters with hid_dbg().
+ *
+ * @hdev:      The HID device the parameters describe.
+ * @params:    The parameters to dump.
+ */
+void uclogic_params_hid_dbg(const struct hid_device *hdev,
+                               const struct uclogic_params *params)
+{
+       size_t i;
+
+       hid_dbg(hdev, ".invalid = %s\n",
+               params->invalid ? "true" : "false");
+       hid_dbg(hdev, ".desc_ptr = %p\n", params->desc_ptr);
+       hid_dbg(hdev, ".desc_size = %u\n", params->desc_size);
+       hid_dbg(hdev, ".pen = {\n");
+       uclogic_params_pen_hid_dbg(hdev, &params->pen);
+       hid_dbg(hdev, "\t}\n");
+       hid_dbg(hdev, ".frame_list = {\n");
+       for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
+               hid_dbg(hdev, "\t{\n");
+               uclogic_params_frame_hid_dbg(hdev, &params->frame_list[i]);
+               hid_dbg(hdev, "\t}%s\n",
+                       i < (ARRAY_SIZE(params->frame_list) - 1) ? "," : "");
+       }
+       hid_dbg(hdev, "}\n");
+}
+
 /**
  * uclogic_params_get_str_desc - retrieve a string descriptor from a HID
  * device interface, putting it into a kmalloc-allocated buffer as is, without
@@ -253,28 +338,45 @@ static s32 uclogic_params_get_le24(const void *p)
  * uclogic_params_pen_init_v2() - initialize tablet interface pen
  * input and retrieve its parameters from the device, using v2 protocol.
  *
- * @pen:       Pointer to the pen parameters to initialize (to be
- *             cleaned up with uclogic_params_pen_cleanup()). Not modified in
- *             case of error, or if parameters are not found. Cannot be NULL.
- * @pfound:    Location for a flag which is set to true if the parameters
- *             were found, and to false if not (e.g. device was
- *             incompatible). Not modified in case of error. Cannot be NULL.
- * @hdev:      The HID device of the tablet interface to initialize and get
- *             parameters from. Cannot be NULL.
+ * @pen:               Pointer to the pen parameters to initialize (to be
+ *                     cleaned up with uclogic_params_pen_cleanup()). Not
+ *                     modified in case of error, or if parameters are not
+ *                     found. Cannot be NULL.
+ * @pfound:            Location for a flag which is set to true if the
+ *                     parameters were found, and to false if not (e.g.
+ *                     device was incompatible). Not modified in case of
+ *                     error. Cannot be NULL.
+ * @pparams_ptr:       Location for a kmalloc'ed pointer to the retrieved raw
+ *                     parameters, which could be used to identify the tablet
+ *                     to some extent. Should be freed with kfree after use.
+ *                     NULL, if not needed. Not modified in case of error.
+ *                     Only set if *pfound is set to true.
+ * @pparams_len:       Location for the length of the retrieved raw
+ *                     parameters. NULL, if not needed. Not modified in case
+ *                     of error. Only set if *pfound is set to true.
+ * @hdev:              The HID device of the tablet interface to initialize
+ *                     and get parameters from. Cannot be NULL.
  *
  * Returns:
  *     Zero, if successful. A negative errno code on error.
  */
 static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
                                        bool *pfound,
+                                       __u8 **pparams_ptr,
+                                       size_t *pparams_len,
                                        struct hid_device *hdev)
 {
        int rc;
        bool found = false;
-       /* Buffer for (part of) the string descriptor */
+       /* Buffer for (part of) the parameter string descriptor */
        __u8 *buf = NULL;
-       /* Descriptor length required */
-       const int len = 18;
+       /* Parameter string descriptor required length */
+       const int params_len_min = 18;
+       /* Parameter string descriptor accepted length */
+       const int params_len_max = 32;
+       /* Parameter string descriptor received length */
+       int params_len;
+       size_t i;
        s32 resolution;
        /* Pen report descriptor template parameters */
        s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
@@ -292,7 +394,7 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
         * the Windows driver traffic.
         * NOTE: This enables fully-functional tablet mode.
         */
-       rc = uclogic_params_get_str_desc(&buf, hdev, 200, len);
+       rc = uclogic_params_get_str_desc(&buf, hdev, 200, params_len_max);
        if (rc == -EPIPE) {
                hid_dbg(hdev,
                        "string descriptor with pen parameters not found, assuming not compatible\n");
@@ -300,27 +402,28 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
        } else if (rc < 0) {
                hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
                goto cleanup;
-       } else if (rc != len) {
+       } else if (rc < params_len_min) {
                hid_dbg(hdev,
-                       "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",
-                       rc, len);
+                       "string descriptor with pen parameters is too short (got %d, expected at least %d), assuming not compatible\n",
+                       rc, params_len_min);
+               goto finish;
+       }
+
+       params_len = rc;
+
+       /*
+        * Check it's not just a catch-all UTF-16LE-encoded ASCII
+        * string (such as the model name) some tablets put into all
+        * unknown string descriptors.
+        */
+       for (i = 2;
+            i < params_len &&
+               (buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0);
+            i += 2);
+       if (i >= params_len) {
+               hid_dbg(hdev,
+                       "string descriptor with pen parameters seems to contain only text, assuming not compatible\n");
                goto finish;
-       } else {
-               size_t i;
-               /*
-                * Check it's not just a catch-all UTF-16LE-encoded ASCII
-                * string (such as the model name) some tablets put into all
-                * unknown string descriptors.
-                */
-               for (i = 2;
-                    i < len &&
-                       (buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0);
-                    i += 2);
-               if (i >= len) {
-                       hid_dbg(hdev,
-                               "string descriptor with pen parameters seems to contain only text, assuming not compatible\n");
-                       goto finish;
-               }
        }
 
        /*
@@ -344,8 +447,6 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
                        desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
                        resolution;
        }
-       kfree(buf);
-       buf = NULL;
 
        /*
         * Generate pen report descriptor
@@ -371,6 +472,13 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
        pen->fragmented_hires = true;
        pen->tilt_y_flipped = true;
        found = true;
+       if (pparams_ptr != NULL) {
+               *pparams_ptr = buf;
+               buf = NULL;
+       }
+       if (pparams_len != NULL)
+               *pparams_len = params_len;
+
 finish:
        *pfound = found;
        rc = 0;
@@ -700,6 +808,14 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
        static const char transition_ver[] = "HUION_T153_160607";
        char *ver_ptr = NULL;
        const size_t ver_len = sizeof(transition_ver) + 1;
+       __u8 *params_ptr = NULL;
+       size_t params_len = 0;
+       /* Parameters string descriptor of a model with touch ring (HS610) */
+       const __u8 touch_ring_model_params_buf[] = {
+               0x13, 0x03, 0x70, 0xC6, 0x00, 0x06, 0x7C, 0x00,
+               0xFF, 0x1F, 0xD8, 0x13, 0x03, 0x0D, 0x10, 0x01,
+               0x04, 0x3C, 0x3E
+       };
 
        /* Check arguments */
        if (params == NULL || hdev == NULL) {
@@ -711,8 +827,13 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
        iface = to_usb_interface(hdev->dev.parent);
        bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
 
-       /* If it's not a pen interface */
-       if (bInterfaceNumber != 0) {
+       /* If it's a custom keyboard interface */
+       if (bInterfaceNumber == 1) {
+               /* Keep everything intact, but mark pen usage invalid */
+               p.pen.usage_invalid = true;
+               goto output;
+       /* Else, if it's not a pen interface */
+       } else if (bInterfaceNumber != 0) {
                uclogic_params_init_invalid(&p);
                goto output;
        }
@@ -738,29 +859,103 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
                        "transition firmware detected, not probing pen v2 parameters\n");
        } else {
                /* Try to probe v2 pen parameters */
-               rc = uclogic_params_pen_init_v2(&p.pen, &found, hdev);
+               rc = uclogic_params_pen_init_v2(&p.pen, &found,
+                                               &params_ptr, &params_len,
+                                               hdev);
                if (rc != 0) {
                        hid_err(hdev,
                                "failed probing pen v2 parameters: %d\n", rc);
                        goto cleanup;
                } else if (found) {
                        hid_dbg(hdev, "pen v2 parameters found\n");
-                       /* Create v2 frame parameters */
+                       /* Create v2 frame button parameters */
                        rc = uclogic_params_frame_init_with_desc(
                                        &p.frame_list[0],
-                                       uclogic_rdesc_v2_frame_arr,
-                                       uclogic_rdesc_v2_frame_size,
-                                       UCLOGIC_RDESC_V2_FRAME_ID);
+                                       uclogic_rdesc_v2_frame_buttons_arr,
+                                       uclogic_rdesc_v2_frame_buttons_size,
+                                       UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID);
                        if (rc != 0) {
                                hid_err(hdev,
-                                       "failed creating v2 frame parameters: %d\n",
+                                       "failed creating v2 frame button parameters: %d\n",
                                        rc);
                                goto cleanup;
                        }
-                       /* Link frame button subreports from pen reports */
+
+                       /* Link from pen sub-report */
                        p.pen.subreport_list[0].value = 0xe0;
                        p.pen.subreport_list[0].id =
-                               UCLOGIC_RDESC_V2_FRAME_ID;
+                               UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID;
+
+                       /* If this is the model with touch ring */
+                       if (params_ptr != NULL &&
+                           params_len == sizeof(touch_ring_model_params_buf) &&
+                           memcmp(params_ptr, touch_ring_model_params_buf,
+                                  params_len) == 0) {
+                               /* Create touch ring parameters */
+                               rc = uclogic_params_frame_init_with_desc(
+                                       &p.frame_list[1],
+                                       uclogic_rdesc_v2_frame_touch_ring_arr,
+                                       uclogic_rdesc_v2_frame_touch_ring_size,
+                                       UCLOGIC_RDESC_V2_FRAME_TOUCH_ID);
+                               if (rc != 0) {
+                                       hid_err(hdev,
+                                               "failed creating v2 frame touch ring parameters: %d\n",
+                                               rc);
+                                       goto cleanup;
+                               }
+                               p.frame_list[1].suffix = "Touch Ring";
+                               p.frame_list[1].dev_id_byte =
+                                       UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE;
+                               p.frame_list[1].touch_byte = 5;
+                               p.frame_list[1].touch_max = 12;
+                               p.frame_list[1].touch_flip_at = 7;
+                       } else {
+                               /* Create touch strip parameters */
+                               rc = uclogic_params_frame_init_with_desc(
+                                       &p.frame_list[1],
+                                       uclogic_rdesc_v2_frame_touch_strip_arr,
+                                       uclogic_rdesc_v2_frame_touch_strip_size,
+                                       UCLOGIC_RDESC_V2_FRAME_TOUCH_ID);
+                               if (rc != 0) {
+                                       hid_err(hdev,
+                                               "failed creating v2 frame touch strip parameters: %d\n",
+                                               rc);
+                                       goto cleanup;
+                               }
+                               p.frame_list[1].suffix = "Touch Strip";
+                               p.frame_list[1].dev_id_byte =
+                                       UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE;
+                               p.frame_list[1].touch_byte = 5;
+                               p.frame_list[1].touch_max = 8;
+                       }
+
+                       /* Link from pen sub-report */
+                       p.pen.subreport_list[1].value = 0xf0;
+                       p.pen.subreport_list[1].id =
+                               UCLOGIC_RDESC_V2_FRAME_TOUCH_ID;
+
+                       /* Create v2 frame dial parameters */
+                       rc = uclogic_params_frame_init_with_desc(
+                                       &p.frame_list[2],
+                                       uclogic_rdesc_v2_frame_dial_arr,
+                                       uclogic_rdesc_v2_frame_dial_size,
+                                       UCLOGIC_RDESC_V2_FRAME_DIAL_ID);
+                       if (rc != 0) {
+                               hid_err(hdev,
+                                       "failed creating v2 frame dial parameters: %d\n",
+                                       rc);
+                               goto cleanup;
+                       }
+                       p.frame_list[2].suffix = "Dial";
+                       p.frame_list[2].dev_id_byte =
+                               UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE;
+                       p.frame_list[2].bitmap_dial_byte = 5;
+
+                       /* Link from pen sub-report */
+                       p.pen.subreport_list[2].value = 0xf1;
+                       p.pen.subreport_list[2].id =
+                               UCLOGIC_RDESC_V2_FRAME_DIAL_ID;
+
                        goto output;
                }
                hid_dbg(hdev, "pen v2 parameters not found\n");
@@ -801,6 +996,7 @@ output:
        memset(&p, 0, sizeof(p));
        rc = 0;
 cleanup:
+       kfree(params_ptr);
        kfree(ver_ptr);
        uclogic_params_cleanup(&p);
        return rc;
@@ -999,6 +1195,8 @@ int uclogic_params_init(struct uclogic_params *params,
                     USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540):
        case VID_PID(USB_VENDOR_ID_UGEE,
                     USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640):
+       case VID_PID(USB_VENDOR_ID_UGEE,
+                    USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06):
        case VID_PID(USB_VENDOR_ID_UGEE,
                     USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720):
                /* If this is the pen interface */
index 86f616d..5bef8da 100644 (file)
@@ -29,11 +29,6 @@ enum uclogic_params_pen_inrange {
        UCLOGIC_PARAMS_PEN_INRANGE_NONE,
 };
 
-/* Convert a pen in-range reporting type to a string */
-extern const char *uclogic_params_pen_inrange_to_str(
-                       enum uclogic_params_pen_inrange inrange);
-
-
 /*
  * Pen report's subreport data.
  */
@@ -62,8 +57,13 @@ struct uclogic_params_pen_subreport {
  */
 struct uclogic_params_pen {
        /*
-        * Pointer to report descriptor describing the inputs.
-        * Allocated with kmalloc.
+        * True if pen usage is invalid for this interface and should be
+        * ignored, false otherwise.
+        */
+       bool usage_invalid;
+       /*
+        * Pointer to report descriptor part describing the pen inputs.
+        * Allocated with kmalloc. NULL if the part is not specified.
         */
        __u8 *desc_ptr;
        /*
@@ -73,8 +73,8 @@ struct uclogic_params_pen {
        unsigned int desc_size;
        /* Report ID, if reports should be tweaked, zero if not */
        unsigned int id;
-       /* The list of subreports */
-       struct uclogic_params_pen_subreport subreport_list[1];
+       /* The list of subreports, only valid if "id" is not zero */
+       struct uclogic_params_pen_subreport subreport_list[3];
        /* Type of in-range reporting, only valid if "id" is not zero */
        enum uclogic_params_pen_inrange inrange;
        /*
@@ -101,8 +101,8 @@ struct uclogic_params_pen {
  */
 struct uclogic_params_frame {
        /*
-        * Pointer to report descriptor describing the inputs.
-        * Allocated with kmalloc.
+        * Pointer to report descriptor part describing the frame inputs.
+        * Allocated with kmalloc. NULL if the part is not specified.
         */
        __u8 *desc_ptr;
        /*
@@ -114,6 +114,10 @@ struct uclogic_params_frame {
         * Report ID, if reports should be tweaked, zero if not.
         */
        unsigned int id;
+       /*
+        * The suffix to add to the input device name, if not NULL.
+        */
+       const char *suffix;
        /*
         * Number of the least-significant bit of the 2-bit state of a rotary
         * encoder, in the report. Cannot point to a 2-bit field crossing a
@@ -123,10 +127,40 @@ struct uclogic_params_frame {
        /*
         * Offset of the Wacom-style device ID byte in the report, to be set
         * to pad device ID (0xf), for compatibility with Wacom drivers. Zero
-        * if no changes to the report should be made. Only valid if "id" is
-        * not zero.
+        * if no changes to the report should be made. The ID byte will be set
+        * to zero whenever the byte pointed by "touch_byte" is zero, if
+        * the latter is valid. Only valid if "id" is not zero.
         */
        unsigned int dev_id_byte;
+       /*
+        * Offset of the touch ring/strip state byte, in the report.
+        * Zero if not present. If dev_id_byte is also valid and non-zero,
+        * then the device ID byte will be cleared when the byte pointed to by
+        * this offset is zero. Only valid if "id" is not zero.
+        */
+       unsigned int touch_byte;
+       /*
+        * The value to anchor the reversed touch ring/strip reports at.
+        * I.e. one, if the reports should be flipped without offset.
+        * Zero if no reversal should be done.
+        * Only valid if "touch_byte" is valid and not zero.
+        */
+       __s8 touch_flip_at;
+       /*
+        * Maximum value of the touch ring/strip report around which the value
+        * should be wrapped when flipping according to "touch_flip_at".
+        * The minimum valid value is considered to be one, with zero being
+        * out-of-proximity (finger lift) value.
+        * Only valid if "touch_flip_at" is valid and not zero.
+        */
+       __s8 touch_max;
+       /*
+        * Offset of the bitmap dial byte, in the report. Zero if not present.
+        * Only valid if "id" is not zero. A bitmap dial sends reports with a
+        * dedicated bit per direction: 1 means clockwise rotation, 2 means
+        * counterclockwise, as opposed to the normal 1 and -1.
+        */
+       unsigned int bitmap_dial_byte;
 };
 
 /*
@@ -156,7 +190,7 @@ struct uclogic_params {
        __u8 *desc_ptr;
        /*
         * Size of the common part of the replacement report descriptor.
-        * Only valid, if "desc_ptr" is not NULL.
+        * Only valid, if "desc_ptr" is valid and not NULL.
         */
        unsigned int desc_size;
        /*
@@ -168,50 +202,13 @@ struct uclogic_params {
         * The list of frame control parameters and optional report descriptor
         * parts. Only valid, if "invalid" is false.
         */
-       struct uclogic_params_frame frame_list[1];
+       struct uclogic_params_frame frame_list[3];
 };
 
 /* Initialize a tablet interface and discover its parameters */
 extern int uclogic_params_init(struct uclogic_params *params,
                                struct hid_device *hdev);
 
-/* Tablet interface parameters *printf format string */
-#define UCLOGIC_PARAMS_FMT_STR \
-               ".invalid = %s\n"                               \
-               ".desc_ptr = %p\n"                              \
-               ".desc_size = %u\n"                             \
-               ".pen.desc_ptr = %p\n"                          \
-               ".pen.desc_size = %u\n"                         \
-               ".pen.id = %u\n"                                \
-               ".pen.subreport_list[0] = {0x%02hhx, %hhu}\n"   \
-               ".pen.inrange = %s\n"                           \
-               ".pen.fragmented_hires = %s\n"                  \
-               ".pen.tilt_y_flipped = %s\n"                    \
-               ".frame_list[0].desc_ptr = %p\n"                \
-               ".frame_list[0].desc_size = %u\n"               \
-               ".frame_list[0].id = %u\n"                      \
-               ".frame_list[0].re_lsb = %u\n"                  \
-               ".frame_list[0].dev_id_byte = %u\n"
-
-/* Tablet interface parameters *printf format arguments */
-#define UCLOGIC_PARAMS_FMT_ARGS(_params) \
-               ((_params)->invalid ? "true" : "false"),                    \
-               (_params)->desc_ptr,                                        \
-               (_params)->desc_size,                                       \
-               (_params)->pen.desc_ptr,                                    \
-               (_params)->pen.desc_size,                                   \
-               (_params)->pen.id,                                          \
-               (_params)->pen.subreport_list[0].value,                     \
-               (_params)->pen.subreport_list[0].id,                        \
-               uclogic_params_pen_inrange_to_str((_params)->pen.inrange),  \
-               ((_params)->pen.fragmented_hires ? "true" : "false"),       \
-               ((_params)->pen.tilt_y_flipped ? "true" : "false"),         \
-               (_params)->frame_list[0].desc_ptr,                          \
-               (_params)->frame_list[0].desc_size,                         \
-               (_params)->frame_list[0].id,                                \
-               (_params)->frame_list[0].re_lsb,                            \
-               (_params)->frame_list[0].dev_id_byte
-
 /* Get a replacement report descriptor for a tablet's interface. */
 extern int uclogic_params_get_desc(const struct uclogic_params *params,
                                        __u8 **pdesc,
@@ -220,4 +217,8 @@ extern int uclogic_params_get_desc(const struct uclogic_params *params,
 /* Free resources used by tablet interface's parameters */
 extern void uclogic_params_cleanup(struct uclogic_params *params);
 
+/* Dump tablet interface parameters with hid_dbg() */
+extern void uclogic_params_hid_dbg(const struct hid_device *hdev,
+                                       const struct uclogic_params *params);
+
 #endif /* _HID_UCLOGIC_PARAMS_H */
index 04644d9..13f9ce7 100644 (file)
@@ -21,7 +21,7 @@
 /* Fixed WP4030U report descriptor */
 __u8 uclogic_rdesc_wp4030u_fixed_arr[] = {
        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
-       0x09, 0x02,         /*  Usage (Pen),                        */
+       0x09, 0x01,         /*  Usage (Digitizer),                  */
        0xA1, 0x01,         /*  Collection (Application),           */
        0x85, 0x09,         /*      Report ID (9),                  */
        0x09, 0x20,         /*      Usage (Stylus),                 */
@@ -66,7 +66,7 @@ const size_t uclogic_rdesc_wp4030u_fixed_size =
 /* Fixed WP5540U report descriptor */
 __u8 uclogic_rdesc_wp5540u_fixed_arr[] = {
        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
-       0x09, 0x02,         /*  Usage (Pen),                        */
+       0x09, 0x01,         /*  Usage (Digitizer),                  */
        0xA1, 0x01,         /*  Collection (Application),           */
        0x85, 0x09,         /*      Report ID (9),                  */
        0x09, 0x20,         /*      Usage (Stylus),                 */
@@ -143,7 +143,7 @@ const size_t uclogic_rdesc_wp5540u_fixed_size =
 /* Fixed WP8060U report descriptor */
 __u8 uclogic_rdesc_wp8060u_fixed_arr[] = {
        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
-       0x09, 0x02,         /*  Usage (Pen),                        */
+       0x09, 0x01,         /*  Usage (Digitizer),                  */
        0xA1, 0x01,         /*  Collection (Application),           */
        0x85, 0x09,         /*      Report ID (9),                  */
        0x09, 0x20,         /*      Usage (Stylus),                 */
@@ -220,7 +220,7 @@ const size_t uclogic_rdesc_wp8060u_fixed_size =
 /* Fixed WP1062 report descriptor */
 __u8 uclogic_rdesc_wp1062_fixed_arr[] = {
        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
-       0x09, 0x02,         /*  Usage (Pen),                        */
+       0x09, 0x01,         /*  Usage (Digitizer),                  */
        0xA1, 0x01,         /*  Collection (Application),           */
        0x85, 0x09,         /*      Report ID (9),                  */
        0x09, 0x20,         /*      Usage (Stylus),                 */
@@ -268,7 +268,7 @@ const size_t uclogic_rdesc_wp1062_fixed_size =
 /* Fixed PF1209 report descriptor */
 __u8 uclogic_rdesc_pf1209_fixed_arr[] = {
        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
-       0x09, 0x02,         /*  Usage (Pen),                        */
+       0x09, 0x01,         /*  Usage (Digitizer),                  */
        0xA1, 0x01,         /*  Collection (Application),           */
        0x85, 0x09,         /*      Report ID (9),                  */
        0x09, 0x20,         /*      Usage (Stylus),                 */
@@ -345,7 +345,7 @@ const size_t uclogic_rdesc_pf1209_fixed_size =
 /* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */
 __u8 uclogic_rdesc_twhl850_fixed0_arr[] = {
        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
-       0x09, 0x02,         /*  Usage (Pen),                        */
+       0x09, 0x01,         /*  Usage (Digitizer),                  */
        0xA1, 0x01,         /*  Collection (Application),           */
        0x85, 0x09,         /*      Report ID (9),                  */
        0x09, 0x20,         /*      Usage (Stylus),                 */
@@ -457,7 +457,7 @@ const size_t uclogic_rdesc_twhl850_fixed2_size =
 /* Fixed TWHA60 report descriptor, interface 0 (stylus) */
 __u8 uclogic_rdesc_twha60_fixed0_arr[] = {
        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
-       0x09, 0x02,         /*  Usage (Pen),                        */
+       0x09, 0x01,         /*  Usage (Digitizer),                  */
        0xA1, 0x01,         /*  Collection (Application),           */
        0x85, 0x09,         /*      Report ID (9),                  */
        0x09, 0x20,         /*      Usage (Stylus),                 */
@@ -534,7 +534,7 @@ const size_t uclogic_rdesc_twha60_fixed1_size =
 /* Fixed report descriptor template for (tweaked) v1 pen reports */
 const __u8 uclogic_rdesc_v1_pen_template_arr[] = {
        0x05, 0x0D,             /*  Usage Page (Digitizer),                 */
-       0x09, 0x02,             /*  Usage (Pen),                            */
+       0x09, 0x01,             /*  Usage (Digitizer),                      */
        0xA1, 0x01,             /*  Collection (Application),               */
        0x85, 0x07,             /*      Report ID (7),                      */
        0x09, 0x20,             /*      Usage (Stylus),                     */
@@ -588,7 +588,7 @@ const size_t uclogic_rdesc_v1_pen_template_size =
 /* Fixed report descriptor template for (tweaked) v2 pen reports */
 const __u8 uclogic_rdesc_v2_pen_template_arr[] = {
        0x05, 0x0D,             /*  Usage Page (Digitizer),                 */
-       0x09, 0x02,             /*  Usage (Pen),                            */
+       0x09, 0x01,             /*  Usage (Digitizer),                      */
        0xA1, 0x01,             /*  Collection (Application),               */
        0x85, 0x08,             /*      Report ID (8),                      */
        0x09, 0x20,             /*      Usage (Stylus),                     */
@@ -652,12 +652,12 @@ const size_t uclogic_rdesc_v2_pen_template_size =
                        sizeof(uclogic_rdesc_v2_pen_template_arr);
 
 /*
- * Expand to the contents of a generic frame report descriptor.
+ * Expand to the contents of a generic frame buttons report descriptor.
  *
  * @_id:       The report ID to use.
  * @_size:     Size of the report to pad to, including report ID, bytes.
  */
-#define UCLOGIC_RDESC_FRAME_BYTES(_id, _size) \
+#define UCLOGIC_RDESC_FRAME_BUTTONS_BYTES(_id, _size) \
        0x05, 0x01,     /*  Usage Page (Desktop),               */ \
        0x09, 0x07,     /*  Usage (Keypad),                     */ \
        0xA1, 0x01,     /*  Collection (Application),           */ \
@@ -700,17 +700,164 @@ const size_t uclogic_rdesc_v2_pen_template_size =
 
 /* Fixed report descriptor for (tweaked) v1 frame reports */
 const __u8 uclogic_rdesc_v1_frame_arr[] = {
-       UCLOGIC_RDESC_FRAME_BYTES(UCLOGIC_RDESC_V1_FRAME_ID, 8)
+       UCLOGIC_RDESC_FRAME_BUTTONS_BYTES(UCLOGIC_RDESC_V1_FRAME_ID, 8)
 };
 const size_t uclogic_rdesc_v1_frame_size =
                        sizeof(uclogic_rdesc_v1_frame_arr);
 
-/* Fixed report descriptor for (tweaked) v2 frame reports */
-const __u8 uclogic_rdesc_v2_frame_arr[] = {
-       UCLOGIC_RDESC_FRAME_BYTES(UCLOGIC_RDESC_V2_FRAME_ID, 12)
+/* Fixed report descriptor for (tweaked) v2 frame button reports */
+const __u8 uclogic_rdesc_v2_frame_buttons_arr[] = {
+       UCLOGIC_RDESC_FRAME_BUTTONS_BYTES(UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID,
+                                         12)
 };
-const size_t uclogic_rdesc_v2_frame_size =
-                       sizeof(uclogic_rdesc_v2_frame_arr);
+const size_t uclogic_rdesc_v2_frame_buttons_size =
+                       sizeof(uclogic_rdesc_v2_frame_buttons_arr);
+
+/* Fixed report descriptor for (tweaked) v2 frame touch ring reports */
+const __u8 uclogic_rdesc_v2_frame_touch_ring_arr[] = {
+       0x05, 0x01,         /*  Usage Page (Desktop),               */
+       0x09, 0x07,         /*  Usage (Keypad),                     */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, UCLOGIC_RDESC_V2_FRAME_TOUCH_ID,
+                           /*      Report ID (TOUCH_ID),           */
+       0x14,               /*      Logical Minimum (0),            */
+       0x05, 0x0D,         /*      Usage Page (Digitizer),         */
+       0x09, 0x39,         /*      Usage (Tablet Function Keys),   */
+       0xA0,               /*      Collection (Physical),          */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x05, 0x09,         /*          Usage Page (Button),        */
+       0x09, 0x01,         /*          Usage (01h),                */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x07,         /*          Report Count (7),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x02,         /*          Report Count (2),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x05, 0x0D,         /*          Usage Page (Digitizer),     */
+       0x0A, 0xFF, 0xFF,   /*          Usage (FFFFh),              */
+       0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x09, 0x38,         /*          Usage (Wheel),              */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x15, 0x00,         /*          Logical Minimum (0),        */
+       0x25, 0x0B,         /*          Logical Maximum (11),       */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x95, 0x02,         /*          Report Count (2),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x2E,         /*          Report Count (46),          */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0                /*  End Collection                      */
+};
+const size_t uclogic_rdesc_v2_frame_touch_ring_size =
+                       sizeof(uclogic_rdesc_v2_frame_touch_ring_arr);
+
+/* Fixed report descriptor for (tweaked) v2 frame touch strip reports */
+const __u8 uclogic_rdesc_v2_frame_touch_strip_arr[] = {
+       0x05, 0x01,         /*  Usage Page (Desktop),               */
+       0x09, 0x07,         /*  Usage (Keypad),                     */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, UCLOGIC_RDESC_V2_FRAME_TOUCH_ID,
+                           /*      Report ID (TOUCH_ID),           */
+       0x14,               /*      Logical Minimum (0),            */
+       0x05, 0x0D,         /*      Usage Page (Digitizer),         */
+       0x09, 0x39,         /*      Usage (Tablet Function Keys),   */
+       0xA0,               /*      Collection (Physical),          */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x05, 0x09,         /*          Usage Page (Button),        */
+       0x09, 0x01,         /*          Usage (01h),                */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x07,         /*          Report Count (7),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x02,         /*          Report Count (2),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x05, 0x0D,         /*          Usage Page (Digitizer),     */
+       0x0A, 0xFF, 0xFF,   /*          Usage (FFFFh),              */
+       0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x09, 0x38,         /*          Usage (Wheel),              */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x15, 0x00,         /*          Logical Minimum (0),        */
+       0x25, 0x07,         /*          Logical Maximum (7),        */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x95, 0x02,         /*          Report Count (2),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x2E,         /*          Report Count (46),          */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0                /*  End Collection                      */
+};
+const size_t uclogic_rdesc_v2_frame_touch_strip_size =
+                       sizeof(uclogic_rdesc_v2_frame_touch_strip_arr);
+
+/* Fixed report descriptor for (tweaked) v2 frame dial reports */
+const __u8 uclogic_rdesc_v2_frame_dial_arr[] = {
+       0x05, 0x01,         /*  Usage Page (Desktop),               */
+       0x09, 0x07,         /*  Usage (Keypad),                     */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, UCLOGIC_RDESC_V2_FRAME_DIAL_ID,
+                           /*      Report ID (DIAL_ID),            */
+       0x14,               /*      Logical Minimum (0),            */
+       0x05, 0x0D,         /*      Usage Page (Digitizer),         */
+       0x09, 0x39,         /*      Usage (Tablet Function Keys),   */
+       0xA0,               /*      Collection (Physical),          */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x05, 0x09,         /*          Usage Page (Button),        */
+       0x09, 0x01,         /*          Usage (01h),                */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x06,         /*          Report Count (6),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x02,         /*          Report Count (2),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x05, 0x0D,         /*          Usage Page (Digitizer),     */
+       0x0A, 0xFF, 0xFF,   /*          Usage (FFFFh),              */
+       0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x09, 0x38,         /*          Usage (Wheel),              */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x15, 0xFF,         /*          Logical Minimum (-1),       */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x81, 0x06,         /*          Input (Variable, Relative), */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x95, 0x02,         /*          Report Count (2),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x2E,         /*          Report Count (46),          */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0                /*  End Collection                      */
+};
+const size_t uclogic_rdesc_v2_frame_dial_size =
+                       sizeof(uclogic_rdesc_v2_frame_dial_arr);
 
 /* Fixed report descriptor for Ugee EX07 frame */
 const __u8 uclogic_rdesc_ugee_ex07_frame_arr[] = {
index 3d904c2..0c6e95e 100644 (file)
@@ -124,12 +124,36 @@ extern const size_t uclogic_rdesc_v2_pen_template_size;
 extern const __u8 uclogic_rdesc_v1_frame_arr[];
 extern const size_t uclogic_rdesc_v1_frame_size;
 
-/* Report ID for tweaked v2 frame reports */
-#define UCLOGIC_RDESC_V2_FRAME_ID 0xf7
+/* Report ID for tweaked v2 frame button reports */
+#define UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID 0xf7
 
-/* Fixed report descriptor for (tweaked) v2 frame reports */
-extern const __u8 uclogic_rdesc_v2_frame_arr[];
-extern const size_t uclogic_rdesc_v2_frame_size;
+/* Fixed report descriptor for (tweaked) v2 frame button reports */
+extern const __u8 uclogic_rdesc_v2_frame_buttons_arr[];
+extern const size_t uclogic_rdesc_v2_frame_buttons_size;
+
+/* Report ID for tweaked v2 frame touch ring/strip reports */
+#define UCLOGIC_RDESC_V2_FRAME_TOUCH_ID 0xf8
+
+/* Fixed report descriptor for (tweaked) v2 frame touch ring reports */
+extern const __u8 uclogic_rdesc_v2_frame_touch_ring_arr[];
+extern const size_t uclogic_rdesc_v2_frame_touch_ring_size;
+
+/* Fixed report descriptor for (tweaked) v2 frame touch strip reports */
+extern const __u8 uclogic_rdesc_v2_frame_touch_strip_arr[];
+extern const size_t uclogic_rdesc_v2_frame_touch_strip_size;
+
+/* Device ID byte offset in v2 frame touch ring/strip reports */
+#define UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE       0x4
+
+/* Report ID for tweaked v2 frame dial reports */
+#define UCLOGIC_RDESC_V2_FRAME_DIAL_ID 0xf9
+
+/* Fixed report descriptor for (tweaked) v2 frame dial reports */
+extern const __u8 uclogic_rdesc_v2_frame_dial_arr[];
+extern const size_t uclogic_rdesc_v2_frame_dial_size;
+
+/* Device ID byte offset in v2 frame dial reports */
+#define UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE        0x4
 
 /* Fixed report descriptor for Ugee EX07 frame */
 extern const __u8 uclogic_rdesc_ugee_ex07_frame_arr[];
index df60c8f..8024b1d 100644 (file)
@@ -24,7 +24,7 @@
 /* Fixed report descriptor of PD1011 signature pad */
 static __u8 pd1011_rdesc_fixed[] = {
        0x05, 0x0D,             /*  Usage Page (Digitizer),             */
-       0x09, 0x02,             /*  Usage (Pen),                        */
+       0x09, 0x01,             /*  Usage (Digitizer),                  */
        0xA1, 0x01,             /*  Collection (Application),           */
        0x85, 0x02,             /*      Report ID (2),                  */
        0x09, 0x20,             /*      Usage (Stylus),                 */
index 07e3cbc..e600dbf 100644 (file)
@@ -30,6 +30,8 @@
 #define TGL_H_DEVICE_ID                0x43FC
 #define ADL_S_DEVICE_ID                0x7AF8
 #define ADL_P_DEVICE_ID                0x51FC
+#define ADL_N_DEVICE_ID                0x54FC
+#define RPL_S_DEVICE_ID                0x7A78
 
 #define        REVISION_ID_CHT_A0      0x6
 #define        REVISION_ID_CHT_Ax_SI   0x0
index 8e9d945..2c67ec1 100644 (file)
@@ -41,6 +41,8 @@ static const struct pci_device_id ish_pci_tbl[] = {
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_H_DEVICE_ID)},
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_S_DEVICE_ID)},
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_P_DEVICE_ID)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_N_DEVICE_ID)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, RPL_S_DEVICE_ID)},
        {0, }
 };
 MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
index 066c567..620fe74 100644 (file)
@@ -1777,7 +1777,7 @@ static int __wacom_initialize_battery(struct wacom *wacom,
        bat_desc->get_property = wacom_battery_get_property;
        sprintf(battery->bat_name, "wacom_battery_%ld", n);
        bat_desc->name = battery->bat_name;
-       bat_desc->type = POWER_SUPPLY_TYPE_USB;
+       bat_desc->type = POWER_SUPPLY_TYPE_BATTERY;
        bat_desc->use_for_apm = 0;
 
        ps_bat = devm_power_supply_register(dev, bat_desc, &psy_cfg);
index a7176fc..9470c2b 100644 (file)
@@ -1811,7 +1811,9 @@ int wacom_equivalent_usage(int usage)
                    usage == WACOM_HID_WD_TOUCHSTRIP2 ||
                    usage == WACOM_HID_WD_TOUCHRING ||
                    usage == WACOM_HID_WD_TOUCHRINGSTATUS ||
-                   usage == WACOM_HID_WD_REPORT_VALID) {
+                   usage == WACOM_HID_WD_REPORT_VALID ||
+                   usage == WACOM_HID_WD_BARRELSWITCH3 ||
+                   usage == WACOM_HID_WD_SEQUENCENUMBER) {
                        return usage;
                }
 
@@ -2196,8 +2198,11 @@ static void wacom_set_barrel_switch3_usage(struct wacom_wac *wacom_wac)
        if (!(features->quirks & WACOM_QUIRK_AESPEN) &&
            wacom_wac->hid_data.barrelswitch &&
            wacom_wac->hid_data.barrelswitch2 &&
-           wacom_wac->hid_data.serialhi)
+           wacom_wac->hid_data.serialhi &&
+           !wacom_wac->hid_data.barrelswitch3) {
                input_set_capability(input, EV_KEY, BTN_STYLUS3);
+               features->quirks |= WACOM_QUIRK_PEN_BUTTON3;
+       }
 }
 
 static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
@@ -2261,6 +2266,9 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
                features->quirks |= WACOM_QUIRK_TOOLSERIAL;
                wacom_map_usage(input, usage, field, EV_MSC, MSC_SERIAL, 0);
                break;
+       case HID_DG_SCANTIME:
+               wacom_map_usage(input, usage, field, EV_MSC, MSC_TIMESTAMP, 0);
+               break;
        case WACOM_HID_WD_SENSE:
                features->quirks |= WACOM_QUIRK_SENSE;
                wacom_map_usage(input, usage, field, EV_KEY, BTN_TOOL_PEN, 0);
@@ -2274,6 +2282,11 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
                input_set_capability(input, EV_KEY, BTN_TOOL_AIRBRUSH);
                wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0);
                break;
+       case WACOM_HID_WD_BARRELSWITCH3:
+               wacom_wac->hid_data.barrelswitch3 = true;
+               wacom_map_usage(input, usage, field, EV_KEY, BTN_STYLUS3, 0);
+               features->quirks &= ~WACOM_QUIRK_PEN_BUTTON3;
+               break;
        }
 }
 
@@ -2390,6 +2403,14 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
        case WACOM_HID_WD_REPORT_VALID:
                wacom_wac->is_invalid_bt_frame = !value;
                return;
+       case WACOM_HID_WD_BARRELSWITCH3:
+               wacom_wac->hid_data.barrelswitch3 = value;
+               return;
+       case WACOM_HID_WD_SEQUENCENUMBER:
+               if (wacom_wac->hid_data.sequence_number != value)
+                       hid_warn(hdev, "Dropped %hu packets", (unsigned short)(value - wacom_wac->hid_data.sequence_number));
+               wacom_wac->hid_data.sequence_number = value + 1;
+               return;
        }
 
        /* send pen events only when touch is up or forced out
@@ -2442,12 +2463,15 @@ static void wacom_wac_pen_report(struct hid_device *hdev,
 
        if (!delay_pen_events(wacom_wac) && wacom_wac->tool[0]) {
                int id = wacom_wac->id[0];
-               int sw_state = wacom_wac->hid_data.barrelswitch |
-                              (wacom_wac->hid_data.barrelswitch2 << 1);
-
-               input_report_key(input, BTN_STYLUS, sw_state == 1);
-               input_report_key(input, BTN_STYLUS2, sw_state == 2);
-               input_report_key(input, BTN_STYLUS3, sw_state == 3);
+               if (wacom_wac->features.quirks & WACOM_QUIRK_PEN_BUTTON3 &&
+                   wacom_wac->hid_data.barrelswitch & wacom_wac->hid_data.barrelswitch2) {
+                       wacom_wac->hid_data.barrelswitch = 0;
+                       wacom_wac->hid_data.barrelswitch2 = 0;
+                       wacom_wac->hid_data.barrelswitch3 = 1;
+               }
+               input_report_key(input, BTN_STYLUS, wacom_wac->hid_data.barrelswitch);
+               input_report_key(input, BTN_STYLUS2, wacom_wac->hid_data.barrelswitch2);
+               input_report_key(input, BTN_STYLUS3, wacom_wac->hid_data.barrelswitch3);
 
                /*
                 * Non-USI EMR tools should have their IDs mangled to
@@ -2529,6 +2553,9 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
                        field->logical_maximum = 255;
                }
                break;
+       case HID_DG_SCANTIME:
+               wacom_map_usage(input, usage, field, EV_MSC, MSC_TIMESTAMP, 0);
+               break;
        }
 }
 
index 466b62c..fef1538 100644 (file)
@@ -86,6 +86,7 @@
 #define WACOM_QUIRK_AESPEN             0x0004
 #define WACOM_QUIRK_BATTERY            0x0008
 #define WACOM_QUIRK_TOOLSERIAL         0x0010
+#define WACOM_QUIRK_PEN_BUTTON3        0x0020
 
 /* device types */
 #define WACOM_DEVICETYPE_NONE           0x0000
 #define WACOM_HID_WD_DIGITIZERFNKEYS    (WACOM_HID_UP_WACOMDIGITIZER | 0x39)
 #define WACOM_HID_WD_SERIALNUMBER       (WACOM_HID_UP_WACOMDIGITIZER | 0x5b)
 #define WACOM_HID_WD_SERIALHI           (WACOM_HID_UP_WACOMDIGITIZER | 0x5c)
+#define WACOM_HID_WD_BARRELSWITCH3      (WACOM_HID_UP_WACOMDIGITIZER | 0x5d)
 #define WACOM_HID_WD_TOOLTYPE           (WACOM_HID_UP_WACOMDIGITIZER | 0x77)
 #define WACOM_HID_WD_DISTANCE           (WACOM_HID_UP_WACOMDIGITIZER | 0x0132)
 #define WACOM_HID_WD_TOUCHSTRIP         (WACOM_HID_UP_WACOMDIGITIZER | 0x0136)
 #define WACOM_HID_WD_TOUCHRING          (WACOM_HID_UP_WACOMDIGITIZER | 0x0138)
 #define WACOM_HID_WD_TOUCHRINGSTATUS    (WACOM_HID_UP_WACOMDIGITIZER | 0x0139)
 #define WACOM_HID_WD_REPORT_VALID       (WACOM_HID_UP_WACOMDIGITIZER | 0x01d0)
+#define WACOM_HID_WD_SEQUENCENUMBER     (WACOM_HID_UP_WACOMDIGITIZER | 0x0220)
 #define WACOM_HID_WD_ACCELEROMETER_X    (WACOM_HID_UP_WACOMDIGITIZER | 0x0401)
 #define WACOM_HID_WD_ACCELEROMETER_Y    (WACOM_HID_UP_WACOMDIGITIZER | 0x0402)
 #define WACOM_HID_WD_ACCELEROMETER_Z    (WACOM_HID_UP_WACOMDIGITIZER | 0x0403)
@@ -300,6 +303,7 @@ struct hid_data {
        bool tipswitch;
        bool barrelswitch;
        bool barrelswitch2;
+       bool barrelswitch3;
        bool serialhi;
        bool confidence;
        int x;
@@ -320,6 +324,7 @@ struct hid_data {
        int bat_connected;
        int ps_connected;
        bool pad_input_event_flag;
+       unsigned short sequence_number;
 };
 
 struct wacom_remote_data {
index ee31274..ef4257a 100644 (file)
@@ -271,6 +271,7 @@ struct input_mask {
 #define BUS_RMI                        0x1D
 #define BUS_CEC                        0x1E
 #define BUS_INTEL_ISHTP                0x1F
+#define BUS_AMD_SFH            0x20
 
 /*
  * MT_TOOL types