Commit aa051d36 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus-2022052401' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

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
  ...
parents d8e0f976 07d17217
...@@ -1044,7 +1044,6 @@ F: arch/arm64/boot/dts/amd/amd-seattle-xgbe*.dtsi ...@@ -1044,7 +1044,6 @@ F: arch/arm64/boot/dts/amd/amd-seattle-xgbe*.dtsi
F: drivers/net/ethernet/amd/xgbe/ F: drivers/net/ethernet/amd/xgbe/
AMD SENSOR FUSION HUB DRIVER AMD SENSOR FUSION HUB DRIVER
M: Nehal Shah <nehal-bakulchandra.shah@amd.com>
M: Basavaraj Natikar <basavaraj.natikar@amd.com> M: Basavaraj Natikar <basavaraj.natikar@amd.com>
L: linux-input@vger.kernel.org L: linux-input@vger.kernel.org
S: Maintained S: Maintained
...@@ -8766,6 +8765,14 @@ F: drivers/hid/hid-sensor-* ...@@ -8766,6 +8765,14 @@ F: drivers/hid/hid-sensor-*
F: drivers/iio/*/hid-* F: drivers/iio/*/hid-*
F: include/linux/hid-sensor-* 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 HIGH-RESOLUTION TIMERS, CLOCKEVENTS
M: Thomas Gleixner <tglx@linutronix.de> M: Thomas Gleixner <tglx@linutronix.de>
L: linux-kernel@vger.kernel.org L: linux-kernel@vger.kernel.org
......
...@@ -697,6 +697,14 @@ config HID_MAYFLASH ...@@ -697,6 +697,14 @@ config HID_MAYFLASH
Say Y here if you have HJZ Mayflash PS3 game controller adapters Say Y here if you have HJZ Mayflash PS3 game controller adapters
and want to enable force feedback support. 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 config HID_REDRAGON
tristate "Redragon keyboards" tristate "Redragon keyboards"
depends on HID depends on HID
......
...@@ -77,6 +77,7 @@ obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o ...@@ -77,6 +77,7 @@ obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
obj-$(CONFIG_HID_MALTRON) += hid-maltron.o obj-$(CONFIG_HID_MALTRON) += hid-maltron.o
obj-$(CONFIG_HID_MCP2221) += hid-mcp2221.o obj-$(CONFIG_HID_MCP2221) += hid-mcp2221.o
obj-$(CONFIG_HID_MAYFLASH) += hid-mf.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_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
......
...@@ -141,6 +141,24 @@ u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts) ...@@ -141,6 +141,24 @@ u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
return 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) int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
{ {
struct amd_input_data *in_data = &privdata->in_data; struct amd_input_data *in_data = &privdata->in_data;
...@@ -219,13 +237,27 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) ...@@ -219,13 +237,27 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
(privdata, cl_data->sensor_idx[i], SENSOR_DISABLED); (privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);
if (status != SENSOR_ENABLED) if (status != SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_DISABLED; cl_data->sensor_sts[i] = SENSOR_DISABLED;
dev_dbg(dev, "sid 0x%x status 0x%x\n", dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], cl_data->sensor_sts[i]); cl_data->sensor_idx[i],
get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
goto cleanup; goto cleanup;
} }
} }
dev_dbg(dev, "sid 0x%x status 0x%x\n", dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], cl_data->sensor_sts[i]); 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)); schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
return 0; return 0;
...@@ -257,8 +289,9 @@ int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata) ...@@ -257,8 +289,9 @@ int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)
(privdata, cl_data->sensor_idx[i], SENSOR_DISABLED); (privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);
if (status != SENSOR_ENABLED) if (status != SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_DISABLED; cl_data->sensor_sts[i] = SENSOR_DISABLED;
dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x status 0x%x\n", dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], cl_data->sensor_sts[i]); cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
} }
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include "amd_sfh_hid.h" #include "amd_sfh_hid.h"
#include "amd_sfh_pcie.h"
#define AMD_SFH_RESPONSE_TIMEOUT 1500 #define AMD_SFH_RESPONSE_TIMEOUT 1500
...@@ -120,6 +121,8 @@ static struct hid_ll_driver amdtp_hid_ll_driver = { ...@@ -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) 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 hid_device *hid;
struct amdtp_hid_data *hid_data; struct amdtp_hid_data *hid_data;
int rc; int rc;
...@@ -141,10 +144,12 @@ int amdtp_hid_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data) ...@@ -141,10 +144,12 @@ int amdtp_hid_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data)
hid->driver_data = hid_data; hid->driver_data = hid_data;
cli_data->hid_sensor_hubs[cur_hid_dev] = hid; 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->vendor = AMD_SFH_HID_VENDOR;
hid->product = AMD_SFH_HID_PRODUCT; 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); hid->vendor, hid->product);
rc = hid_add_device(hid); rc = hid_add_device(hid);
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#define AMDSFH_HID_H #define AMDSFH_HID_H
#define MAX_HID_DEVICES 5 #define MAX_HID_DEVICES 5
#define BUS_AMD_AMDTP 0x20
#define AMD_SFH_HID_VENDOR 0x1022 #define AMD_SFH_HID_VENDOR 0x1022
#define AMD_SFH_HID_PRODUCT 0x0001 #define AMD_SFH_HID_PRODUCT 0x0001
......
...@@ -130,6 +130,12 @@ static int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata) ...@@ -130,6 +130,12 @@ static int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata)
return 0; 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) void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
{ {
union sfh_cmd_param cmd_param; union sfh_cmd_param cmd_param;
...@@ -245,6 +251,7 @@ static const struct amd_mp2_ops amd_sfh_ops_v2 = { ...@@ -245,6 +251,7 @@ static const struct amd_mp2_ops amd_sfh_ops_v2 = {
.response = amd_sfh_wait_response_v2, .response = amd_sfh_wait_response_v2,
.clear_intr = amd_sfh_clear_intr_v2, .clear_intr = amd_sfh_clear_intr_v2,
.init_intr = amd_sfh_irq_init_v2, .init_intr = amd_sfh_irq_init_v2,
.discovery_status = amd_sfh_dis_sts_v2,
}; };
static const struct amd_mp2_ops amd_sfh_ops = { static const struct amd_mp2_ops amd_sfh_ops = {
...@@ -346,8 +353,9 @@ static int __maybe_unused amd_mp2_pci_resume(struct device *dev) ...@@ -346,8 +353,9 @@ static int __maybe_unused amd_mp2_pci_resume(struct device *dev)
(mp2, cl_data->sensor_idx[i], SENSOR_ENABLED); (mp2, cl_data->sensor_idx[i], SENSOR_ENABLED);
if (status == SENSOR_ENABLED) if (status == SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_ENABLED; cl_data->sensor_sts[i] = SENSOR_ENABLED;
dev_dbg(dev, "resume sid 0x%x status 0x%x\n", dev_dbg(dev, "suspend sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], cl_data->sensor_sts[i]); 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) ...@@ -371,8 +379,9 @@ static int __maybe_unused amd_mp2_pci_suspend(struct device *dev)
(mp2, cl_data->sensor_idx[i], SENSOR_DISABLED); (mp2, cl_data->sensor_idx[i], SENSOR_DISABLED);
if (status != SENSOR_ENABLED) if (status != SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_DISABLED; cl_data->sensor_sts[i] = SENSOR_DISABLED;
dev_dbg(dev, "suspend sid 0x%x status 0x%x\n", dev_dbg(dev, "suspend sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], cl_data->sensor_sts[i]); cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
} }
} }
......
...@@ -39,6 +39,9 @@ ...@@ -39,6 +39,9 @@
#define AMD_SFH_IDLE_LOOP 200 #define AMD_SFH_IDLE_LOOP 200
#define SENSOR_DISCOVERY_STATUS_MASK GENMASK(5, 3)
#define SENSOR_DISCOVERY_STATUS_SHIFT 3
/* SFH Command register */ /* SFH Command register */
union sfh_cmd_base { union sfh_cmd_base {
u32 ul; u32 ul;
...@@ -135,6 +138,7 @@ int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata); ...@@ -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); 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_suspend(struct amd_mp2_dev *mp2);
void amd_mp2_resume(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 { struct amd_mp2_ops {
void (*start)(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info); void (*start)(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info);
...@@ -143,5 +147,6 @@ struct amd_mp2_ops { ...@@ -143,5 +147,6 @@ struct amd_mp2_ops {
int (*response)(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts); int (*response)(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts);
void (*clear_intr)(struct amd_mp2_dev *privdata); void (*clear_intr)(struct amd_mp2_dev *privdata);
int (*init_intr)(struct amd_mp2_dev *privdata); int (*init_intr)(struct amd_mp2_dev *privdata);
int (*discovery_status)(struct amd_mp2_dev *privdata);
}; };
#endif #endif
...@@ -179,7 +179,7 @@ static const u8 accel3_report_descriptor[] = { ...@@ -179,7 +179,7 @@ static const u8 accel3_report_descriptor[] = {
0xC0 /* HID end collection */ 0xC0 /* HID end collection */
}; };
const u8 gyro3_report_descriptor[] = { static const u8 gyro3_report_descriptor[] = {
0x05, 0x20, /* Usage page */ 0x05, 0x20, /* Usage page */
0x09, 0x76, /* Motion type Gyro3D */ 0x09, 0x76, /* Motion type Gyro3D */
0xA1, 0x00, /* HID Collection (Physical) */ 0xA1, 0x00, /* HID Collection (Physical) */
...@@ -340,7 +340,7 @@ const u8 gyro3_report_descriptor[] = { ...@@ -340,7 +340,7 @@ const u8 gyro3_report_descriptor[] = {
0xC0, /* HID end collection */ 0xC0, /* HID end collection */
}; };
const u8 comp3_report_descriptor[] = { static const u8 comp3_report_descriptor[] = {
0x05, 0x20, /* Usage page */ 0x05, 0x20, /* Usage page */
0x09, 0x83, /* Motion type Orientation compass 3D */ 0x09, 0x83, /* Motion type Orientation compass 3D */
0xA1, 0x00, /* HID Collection (Physical) */ 0xA1, 0x00, /* HID Collection (Physical) */
...@@ -512,7 +512,7 @@ const u8 comp3_report_descriptor[] = { ...@@ -512,7 +512,7 @@ const u8 comp3_report_descriptor[] = {
0xC0 /* HID end collection */ 0xC0 /* HID end collection */
}; };
const u8 als_report_descriptor[] = { static const u8 als_report_descriptor[] = {
0x05, 0x20, /* HID usage page sensor */ 0x05, 0x20, /* HID usage page sensor */
0x09, 0x41, /* HID usage sensor type Ambientlight */ 0x09, 0x41, /* HID usage sensor type Ambientlight */
0xA1, 0x00, /* HID Collection (Physical) */ 0xA1, 0x00, /* HID Collection (Physical) */
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/string.h>
#include "hid-ids.h" #include "hid-ids.h"
...@@ -35,16 +36,17 @@ ...@@ -35,16 +36,17 @@
#define APPLE_NUMLOCK_EMULATION BIT(8) #define APPLE_NUMLOCK_EMULATION BIT(8)
#define APPLE_RDESC_BATTERY BIT(9) #define APPLE_RDESC_BATTERY BIT(9)
#define APPLE_BACKLIGHT_CTL BIT(10) #define APPLE_BACKLIGHT_CTL BIT(10)
#define APPLE_IS_KEYCHRON BIT(11)
#define APPLE_FLAG_FKEY 0x01 #define APPLE_FLAG_FKEY 0x01
#define HID_COUNTRY_INTERNATIONAL_ISO 13 #define HID_COUNTRY_INTERNATIONAL_ISO 13
#define APPLE_BATTERY_TIMEOUT_MS 60000 #define APPLE_BATTERY_TIMEOUT_MS 60000
static unsigned int fnmode = 1; static unsigned int fnmode = 3;
module_param(fnmode, uint, 0644); module_param(fnmode, uint, 0644);
MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, " 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; static int iso_layout = -1;
module_param(iso_layout, int, 0644); module_param(iso_layout, int, 0644);
...@@ -349,6 +351,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, ...@@ -349,6 +351,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
const struct apple_key_translation *trans, *table; const struct apple_key_translation *trans, *table;
bool do_translate; bool do_translate;
u16 code = 0; u16 code = 0;
unsigned int real_fnmode;
u16 fn_keycode = (swap_fn_leftctrl) ? (KEY_LEFTCTRL) : (KEY_FN); 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, ...@@ -359,7 +362,13 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
return 1; 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 || 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_ISO ||
hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS || 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, ...@@ -406,7 +415,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
if (!code) { if (!code) {
if (trans->flags & APPLE_FLAG_FKEY) { if (trans->flags & APPLE_FLAG_FKEY) {
switch (fnmode) { switch (real_fnmode) {
case 1: case 1:
do_translate = !asc->fn_on; do_translate = !asc->fn_on;
break; break;
...@@ -660,6 +669,11 @@ static int apple_input_configured(struct hid_device *hdev, ...@@ -660,6 +669,11 @@ static int apple_input_configured(struct hid_device *hdev,
asc->quirks &= ~APPLE_HAS_FN; 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; return 0;
} }
......
...@@ -347,6 +347,12 @@ static int bigben_probe(struct hid_device *hid, ...@@ -347,6 +347,12 @@ static int bigben_probe(struct hid_device *hid,
bigben->report = list_entry(report_list->next, bigben->report = list_entry(report_list->next,
struct hid_report, list); 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); hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
set_bit(FF_RUMBLE, hidinput->input->ffbit); set_bit(FF_RUMBLE, hidinput->input->ffbit);
......
...@@ -2222,6 +2222,10 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) ...@@ -2222,6 +2222,10 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
case BUS_VIRTUAL: case BUS_VIRTUAL:
bus = "VIRTUAL"; bus = "VIRTUAL";
break; break;
case BUS_INTEL_ISHTP:
case BUS_AMD_SFH:
bus = "SENSOR HUB";
break;
default: default:
bus = "<UNKNOWN>"; bus = "<UNKNOWN>";
} }
......
...@@ -188,7 +188,6 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) ...@@ -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); ret = input_mt_init_slots(input, ELAN_MAX_FINGERS, INPUT_MT_POINTER);
if (ret) { if (ret) {
hid_err(hdev, "Failed to init elan MT slots: %d\n", ret); hid_err(hdev, "Failed to init elan MT slots: %d\n", ret);
input_free_device(input);
return ret; return ret;
} }
...@@ -200,7 +199,6 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) ...@@ -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", hid_err(hdev, "Failed to register elan input device: %d\n",
ret); ret);
input_mt_destroy_slots(input); input_mt_destroy_slots(input);
input_free_device(input);
return ret; return ret;
} }
......
...@@ -761,13 +761,16 @@ ...@@ -761,13 +761,16 @@
#define USB_VENDOR_ID_LENOVO 0x17ef #define USB_VENDOR_ID_LENOVO 0x17ef
#define USB_DEVICE_ID_LENOVO_TPKBD 0x6009 #define USB_DEVICE_ID_LENOVO_TPKBD 0x6009
#define USB_DEVICE_ID_LENOVO_CUSBKBD 0x6047 #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_CBTKBD 0x6048
#define USB_DEVICE_ID_LENOVO_TPIIBTKBD 0x60e1
#define USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL 0x6049 #define USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL 0x6049
#define USB_DEVICE_ID_LENOVO_TP10UBKBD 0x6062 #define USB_DEVICE_ID_LENOVO_TP10UBKBD 0x6062
#define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067 #define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067
#define USB_DEVICE_ID_LENOVO_X1_COVER 0x6085 #define USB_DEVICE_ID_LENOVO_X1_COVER 0x6085
#define USB_DEVICE_ID_LENOVO_X1_TAB 0x60a3 #define USB_DEVICE_ID_LENOVO_X1_TAB 0x60a3
#define USB_DEVICE_ID_LENOVO_X1_TAB3 0x60b5 #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_OPTICAL_USB_MOUSE_600E 0x600e
#define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_608D 0x608d #define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_608D 0x608d
#define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_6019 0x6019 #define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_6019 0x6019
...@@ -868,6 +871,9 @@ ...@@ -868,6 +871,9 @@
#define USB_VENDOR_ID_MCS 0x16d0 #define USB_VENDOR_ID_MCS 0x16d0
#define USB_DEVICE_ID_MCS_GAMEPADBLOCK 0x0bcc #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_VENDOR_ID_MGE 0x0463
#define USB_DEVICE_ID_MGE_UPS 0xffff #define USB_DEVICE_ID_MGE_UPS 0xffff
#define USB_DEVICE_ID_MGE_UPS1 0x0001 #define USB_DEVICE_ID_MGE_UPS1 0x0001
...@@ -1272,6 +1278,7 @@ ...@@ -1272,6 +1278,7 @@
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 0x0075 #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_G640 0x0094
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042 #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_G5 0x0074
#define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071 #define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071
#define USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720 0x0055 #define USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720 0x0055
......
...@@ -33,7 +33,7 @@ static __u8 easypen_i405x_rdesc_fixed[] = { ...@@ -33,7 +33,7 @@ static __u8 easypen_i405x_rdesc_fixed[] = {
0xB1, 0x02, /* Feature (Variable), */ 0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */ 0xC0, /* End Collection, */
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x10, /* Report ID (16), */ 0x85, 0x10, /* Report ID (16), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -91,7 +91,7 @@ static __u8 mousepen_i608x_rdesc_fixed[] = { ...@@ -91,7 +91,7 @@ static __u8 mousepen_i608x_rdesc_fixed[] = {
0xB1, 0x02, /* Feature (Variable), */ 0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */ 0xC0, /* End Collection, */
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x10, /* Report ID (16), */ 0x85, 0x10, /* Report ID (16), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -190,7 +190,7 @@ static __u8 mousepen_i608x_v2_rdesc_fixed[] = { ...@@ -190,7 +190,7 @@ static __u8 mousepen_i608x_v2_rdesc_fixed[] = {
0xB1, 0x02, /* Feature (Variable), */ 0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */ 0xC0, /* End Collection, */
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x10, /* Report ID (16), */ 0x85, 0x10, /* Report ID (16), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -289,7 +289,7 @@ static __u8 easypen_m610x_rdesc_fixed[] = { ...@@ -289,7 +289,7 @@ static __u8 easypen_m610x_rdesc_fixed[] = {
0xB1, 0x02, /* Feature (Variable), */ 0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */ 0xC0, /* End Collection, */
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x10, /* Report ID (16), */ 0x85, 0x10, /* Report ID (16), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -368,7 +368,7 @@ static __u8 pensketch_m912_rdesc_fixed[] = { ...@@ -368,7 +368,7 @@ static __u8 pensketch_m912_rdesc_fixed[] = {
0xB1, 0x02, /* Feature (Variable), */ 0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */ 0xC0, /* End Collection, */
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x10, /* Report ID (16), */ 0x85, 0x10, /* Report ID (16), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -497,7 +497,7 @@ static __u8 easypen_m406xe_rdesc_fixed[] = { ...@@ -497,7 +497,7 @@ static __u8 easypen_m406xe_rdesc_fixed[] = {
0xB1, 0x02, /* Feature (Variable), */ 0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */ 0xC0, /* End Collection, */
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x10, /* Report ID (16), */ 0x85, 0x10, /* Report ID (16), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
......
...@@ -366,7 +366,7 @@ static const struct hidled_config hidled_configs[] = { ...@@ -366,7 +366,7 @@ static const struct hidled_config hidled_configs[] = {
.type = DREAM_CHEEKY, .type = DREAM_CHEEKY,
.name = "Dream Cheeky Webmail Notifier", .name = "Dream Cheeky Webmail Notifier",
.short_name = "dream_cheeky", .short_name = "dream_cheeky",
.max_brightness = 31, .max_brightness = 63,
.num_leds = 1, .num_leds = 1,
.report_size = 9, .report_size = 9,
.report_type = RAW_REQUEST, .report_type = RAW_REQUEST,
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* - ThinkPad USB Keyboard with TrackPoint (tpkbd) * - ThinkPad USB Keyboard with TrackPoint (tpkbd)
* - ThinkPad Compact Bluetooth Keyboard with TrackPoint (cptkbd) * - ThinkPad Compact Bluetooth Keyboard with TrackPoint (cptkbd)
* - ThinkPad Compact USB 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) 2012 Bernhard Seibold
* Copyright (c) 2014 Jamie Lentin <jm@lentin.co.uk> * Copyright (c) 2014 Jamie Lentin <jm@lentin.co.uk>
...@@ -110,6 +111,23 @@ static const __u8 lenovo_pro_dock_need_fixup_collection[] = { ...@@ -110,6 +111,23 @@ static const __u8 lenovo_pro_dock_need_fixup_collection[] = {
0x2a, 0xff, 0xff, /* Usage Maximum (65535) */ 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, static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize) unsigned int *rsize)
{ {
...@@ -126,6 +144,19 @@ static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc, ...@@ -126,6 +144,19 @@ static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
rdesc[152] = 0x00; rdesc[152] = 0x00;
} }
break; 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; return rdesc;
} }
...@@ -217,6 +248,101 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev, ...@@ -217,6 +248,101 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev,
return 0; 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, static int lenovo_input_mapping_scrollpoint(struct hid_device *hdev,
struct hid_input *hi, struct hid_field *field, struct hid_input *hi, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit, int *max) struct hid_usage *usage, unsigned long **bit, int *max)
...@@ -326,6 +452,10 @@ static int lenovo_input_mapping(struct hid_device *hdev, ...@@ -326,6 +452,10 @@ static int lenovo_input_mapping(struct hid_device *hdev,
case USB_DEVICE_ID_LENOVO_CBTKBD: case USB_DEVICE_ID_LENOVO_CBTKBD:
return lenovo_input_mapping_cptkbd(hdev, hi, field, return lenovo_input_mapping_cptkbd(hdev, hi, field,
usage, bit, max); 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_III:
case USB_DEVICE_ID_IBM_SCROLLPOINT_PRO: case USB_DEVICE_ID_IBM_SCROLLPOINT_PRO:
case USB_DEVICE_ID_IBM_SCROLLPOINT_OPTICAL: case USB_DEVICE_ID_IBM_SCROLLPOINT_OPTICAL:
...@@ -357,16 +487,23 @@ static int lenovo_send_cmd_cptkbd(struct hid_device *hdev, ...@@ -357,16 +487,23 @@ static int lenovo_send_cmd_cptkbd(struct hid_device *hdev,
if (!buf) if (!buf)
return -ENOMEM; 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[0] = 0x18;
buf[1] = byte2; buf[1] = byte2;
buf[2] = byte3; buf[2] = byte3;
switch (hdev->product) { switch (hdev->product) {
case USB_DEVICE_ID_LENOVO_CUSBKBD: case USB_DEVICE_ID_LENOVO_CUSBKBD:
case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
ret = hid_hw_raw_request(hdev, 0x13, buf, 3, ret = hid_hw_raw_request(hdev, 0x13, buf, 3,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT); HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
break; break;
case USB_DEVICE_ID_LENOVO_CBTKBD: case USB_DEVICE_ID_LENOVO_CBTKBD:
case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
ret = hid_hw_output_report(hdev, buf, 3); ret = hid_hw_output_report(hdev, buf, 3);
break; break;
default: default:
...@@ -422,6 +559,8 @@ static ssize_t attr_fn_lock_store(struct device *dev, ...@@ -422,6 +559,8 @@ static ssize_t attr_fn_lock_store(struct device *dev,
switch (hdev->product) { switch (hdev->product) {
case USB_DEVICE_ID_LENOVO_CUSBKBD: case USB_DEVICE_ID_LENOVO_CUSBKBD:
case USB_DEVICE_ID_LENOVO_CBTKBD: case USB_DEVICE_ID_LENOVO_CBTKBD:
case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
lenovo_features_set_cptkbd(hdev); lenovo_features_set_cptkbd(hdev);
break; break;
case USB_DEVICE_ID_LENOVO_TP10UBKBD: case USB_DEVICE_ID_LENOVO_TP10UBKBD:
...@@ -556,6 +695,15 @@ static int lenovo_event_cptkbd(struct hid_device *hdev, ...@@ -556,6 +695,15 @@ static int lenovo_event_cptkbd(struct hid_device *hdev,
return 1; 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; return 0;
} }
...@@ -568,6 +716,8 @@ static int lenovo_event(struct hid_device *hdev, struct hid_field *field, ...@@ -568,6 +716,8 @@ static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
switch (hdev->product) { switch (hdev->product) {
case USB_DEVICE_ID_LENOVO_CUSBKBD: case USB_DEVICE_ID_LENOVO_CUSBKBD:
case USB_DEVICE_ID_LENOVO_CBTKBD: 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); return lenovo_event_cptkbd(hdev, field, usage, value);
case USB_DEVICE_ID_LENOVO_TP10UBKBD: case USB_DEVICE_ID_LENOVO_TP10UBKBD:
case USB_DEVICE_ID_LENOVO_X1_TAB: case USB_DEVICE_ID_LENOVO_X1_TAB:
...@@ -960,8 +1110,9 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev) ...@@ -960,8 +1110,9 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev)
struct lenovo_drvdata *cptkbd_data; struct lenovo_drvdata *cptkbd_data;
/* All the custom action happens on the USBMOUSE device for USB */ /* All the custom action happens on the USBMOUSE device for USB */
if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD if (((hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD) ||
&& hdev->type != HID_TYPE_USBMOUSE) { (hdev->product == USB_DEVICE_ID_LENOVO_TPIIUSBKBD)) &&
hdev->type != HID_TYPE_USBMOUSE) {
hid_dbg(hdev, "Ignoring keyboard half of device\n"); hid_dbg(hdev, "Ignoring keyboard half of device\n");
return 0; return 0;
} }
...@@ -977,11 +1128,14 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev) ...@@ -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 * 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 (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD ||
if (ret) hdev->product == USB_DEVICE_ID_LENOVO_CBTKBD) {
hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret); 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 */ /* Switch middle button to native mode */
ret = lenovo_send_cmd_cptkbd(hdev, 0x09, 0x01); ret = lenovo_send_cmd_cptkbd(hdev, 0x09, 0x01);
...@@ -1088,6 +1242,8 @@ static int lenovo_probe(struct hid_device *hdev, ...@@ -1088,6 +1242,8 @@ static int lenovo_probe(struct hid_device *hdev,
break; break;
case USB_DEVICE_ID_LENOVO_CUSBKBD: case USB_DEVICE_ID_LENOVO_CUSBKBD:
case USB_DEVICE_ID_LENOVO_CBTKBD: case USB_DEVICE_ID_LENOVO_CBTKBD:
case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
ret = lenovo_probe_cptkbd(hdev); ret = lenovo_probe_cptkbd(hdev);
break; break;
case USB_DEVICE_ID_LENOVO_TP10UBKBD: case USB_DEVICE_ID_LENOVO_TP10UBKBD:
...@@ -1154,6 +1310,8 @@ static void lenovo_remove(struct hid_device *hdev) ...@@ -1154,6 +1310,8 @@ static void lenovo_remove(struct hid_device *hdev)
break; break;
case USB_DEVICE_ID_LENOVO_CUSBKBD: case USB_DEVICE_ID_LENOVO_CUSBKBD:
case USB_DEVICE_ID_LENOVO_CBTKBD: case USB_DEVICE_ID_LENOVO_CBTKBD:
case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
lenovo_remove_cptkbd(hdev); lenovo_remove_cptkbd(hdev);
break; break;
case USB_DEVICE_ID_LENOVO_TP10UBKBD: case USB_DEVICE_ID_LENOVO_TP10UBKBD:
...@@ -1172,6 +1330,8 @@ static int lenovo_input_configured(struct hid_device *hdev, ...@@ -1172,6 +1330,8 @@ static int lenovo_input_configured(struct hid_device *hdev,
case USB_DEVICE_ID_LENOVO_TPKBD: case USB_DEVICE_ID_LENOVO_TPKBD:
case USB_DEVICE_ID_LENOVO_CUSBKBD: case USB_DEVICE_ID_LENOVO_CUSBKBD:
case USB_DEVICE_ID_LENOVO_CBTKBD: 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)) { if (test_bit(EV_REL, hi->input->evbit)) {
/* set only for trackpoint device */ /* set only for trackpoint device */
__set_bit(INPUT_PROP_POINTER, hi->input->propbit); __set_bit(INPUT_PROP_POINTER, hi->input->propbit);
...@@ -1188,7 +1348,9 @@ static int lenovo_input_configured(struct hid_device *hdev, ...@@ -1188,7 +1348,9 @@ static int lenovo_input_configured(struct hid_device *hdev,
static const struct hid_device_id lenovo_devices[] = { 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_TPKBD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) }, { 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_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_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_III) },
{ HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_PRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_PRO) },
......
// 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");
...@@ -2034,6 +2034,12 @@ static const struct hid_device_id mt_devices[] = { ...@@ -2034,6 +2034,12 @@ static const struct hid_device_id mt_devices[] = {
USB_VENDOR_ID_LENOVO, USB_VENDOR_ID_LENOVO,
USB_DEVICE_ID_LENOVO_X1_TAB3) }, 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 */ /* MosArt panels */
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
MT_USB_DEVICE(USB_VENDOR_ID_ASUS, MT_USB_DEVICE(USB_VENDOR_ID_ASUS,
...@@ -2178,6 +2184,9 @@ static const struct hid_device_id mt_devices[] = { ...@@ -2178,6 +2184,9 @@ static const struct hid_device_id mt_devices[] = {
{ .driver_data = MT_CLS_GOOGLE, { .driver_data = MT_CLS_GOOGLE,
HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE, HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE,
USB_DEVICE_ID_GOOGLE_TOUCH_ROSE) }, 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 */ /* Generic MT device */
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) }, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) },
......
...@@ -81,6 +81,24 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, ...@@ -81,6 +81,24 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
return 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, static int uclogic_input_configured(struct hid_device *hdev,
struct hid_input *hi) struct hid_input *hi)
{ {
...@@ -90,6 +108,8 @@ static int uclogic_input_configured(struct hid_device *hdev, ...@@ -90,6 +108,8 @@ static int uclogic_input_configured(struct hid_device *hdev,
const char *suffix = NULL; const char *suffix = NULL;
struct hid_field *field; struct hid_field *field;
size_t len; size_t len;
size_t i;
const struct uclogic_params_frame *frame;
/* no report associated (HID_QUIRK_MULTI_INPUT not set) */ /* no report associated (HID_QUIRK_MULTI_INPUT not set) */
if (!hi->report) if (!hi->report)
...@@ -104,27 +124,44 @@ static int uclogic_input_configured(struct hid_device *hdev, ...@@ -104,27 +124,44 @@ static int uclogic_input_configured(struct hid_device *hdev,
drvdata->pen_input = hi->input; 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) { if (!suffix) {
case HID_GD_KEYBOARD: field = hi->report->field[0];
suffix = "Keyboard";
break; switch (field->application) {
case HID_GD_MOUSE: case HID_GD_KEYBOARD:
suffix = "Mouse"; suffix = "Keyboard";
break; break;
case HID_GD_KEYPAD: case HID_GD_MOUSE:
suffix = "Pad"; suffix = "Mouse";
break; break;
case HID_DG_PEN: case HID_GD_KEYPAD:
suffix = "Pen"; suffix = "Pad";
break; break;
case HID_CP_CONSUMER_CONTROL: case HID_DG_PEN:
suffix = "Consumer Control"; suffix = "Pen";
break; break;
case HID_GD_SYSTEM_CONTROL: case HID_CP_CONSUMER_CONTROL:
suffix = "System Control"; suffix = "Consumer Control";
break; break;
case HID_GD_SYSTEM_CONTROL:
suffix = "System Control";
break;
}
} }
if (suffix) { if (suffix) {
...@@ -172,8 +209,8 @@ static int uclogic_probe(struct hid_device *hdev, ...@@ -172,8 +209,8 @@ static int uclogic_probe(struct hid_device *hdev,
goto failure; goto failure;
} }
params_initialized = true; params_initialized = true;
hid_dbg(hdev, "parameters:\n" UCLOGIC_PARAMS_FMT_STR, hid_dbg(hdev, "parameters:\n");
UCLOGIC_PARAMS_FMT_ARGS(&drvdata->params)); uclogic_params_hid_dbg(hdev, &drvdata->params);
if (drvdata->params.invalid) { if (drvdata->params.invalid) {
hid_info(hdev, "interface is invalid, ignoring\n"); hid_info(hdev, "interface is invalid, ignoring\n");
rc = -ENODEV; rc = -ENODEV;
...@@ -313,8 +350,15 @@ static int uclogic_raw_event_frame( ...@@ -313,8 +350,15 @@ static int uclogic_raw_event_frame(
/* If need to, and can, set pad device ID for Wacom drivers */ /* If need to, and can, set pad device ID for Wacom drivers */
if (frame->dev_id_byte > 0 && frame->dev_id_byte < size) { 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 need to, and can, read rotary encoder state change */
if (frame->re_lsb > 0 && frame->re_lsb / 8 < size) { if (frame->re_lsb > 0 && frame->re_lsb / 8 < size) {
unsigned int byte = frame->re_lsb / 8; unsigned int byte = frame->re_lsb / 8;
...@@ -341,6 +385,26 @@ static int uclogic_raw_event_frame( ...@@ -341,6 +385,26 @@ static int uclogic_raw_event_frame(
drvdata->re_state = state; 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; return 0;
} }
...@@ -457,6 +521,8 @@ static const struct hid_device_id uclogic_devices[] = { ...@@ -457,6 +521,8 @@ static const struct hid_device_id uclogic_devices[] = {
USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640) }, USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01) }, 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); MODULE_DEVICE_TABLE(hid, uclogic_devices);
...@@ -468,6 +534,7 @@ static struct hid_driver uclogic_driver = { ...@@ -468,6 +534,7 @@ static struct hid_driver uclogic_driver = {
.remove = uclogic_remove, .remove = uclogic_remove,
.report_fixup = uclogic_report_fixup, .report_fixup = uclogic_report_fixup,
.raw_event = uclogic_raw_event, .raw_event = uclogic_raw_event,
.input_mapping = uclogic_input_mapping,
.input_configured = uclogic_input_configured, .input_configured = uclogic_input_configured,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.resume = uclogic_resume, .resume = uclogic_resume,
......
...@@ -29,8 +29,8 @@ ...@@ -29,8 +29,8 @@
* Returns: * Returns:
* The string representing the type, or NULL if the type is unknown. * The string representing the type, or NULL if the type is unknown.
*/ */
const char *uclogic_params_pen_inrange_to_str( static const char *uclogic_params_pen_inrange_to_str(
enum uclogic_params_pen_inrange inrange) enum uclogic_params_pen_inrange inrange)
{ {
switch (inrange) { switch (inrange) {
case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL: case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL:
...@@ -44,6 +44,91 @@ const char *uclogic_params_pen_inrange_to_str( ...@@ -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 * uclogic_params_get_str_desc - retrieve a string descriptor from a HID
* device interface, putting it into a kmalloc-allocated buffer as is, without * 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) ...@@ -253,28 +338,45 @@ static s32 uclogic_params_get_le24(const void *p)
* uclogic_params_pen_init_v2() - initialize tablet interface pen * uclogic_params_pen_init_v2() - initialize tablet interface pen
* input and retrieve its parameters from the device, using v2 protocol. * input and retrieve its parameters from the device, using v2 protocol.
* *
* @pen: Pointer to the pen parameters to initialize (to be * @pen: Pointer to the pen parameters to initialize (to be
* cleaned up with uclogic_params_pen_cleanup()). Not modified in * cleaned up with uclogic_params_pen_cleanup()). Not
* case of error, or if parameters are not found. Cannot be NULL. * modified in case of error, or if parameters are not
* @pfound: Location for a flag which is set to true if the parameters * found. Cannot be NULL.
* were found, and to false if not (e.g. device was * @pfound: Location for a flag which is set to true if the
* incompatible). Not modified in case of error. Cannot be NULL. * parameters were found, and to false if not (e.g.
* @hdev: The HID device of the tablet interface to initialize and get * device was incompatible). Not modified in case of
* parameters from. Cannot be NULL. * 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: * Returns:
* Zero, if successful. A negative errno code on error. * Zero, if successful. A negative errno code on error.
*/ */
static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen, static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
bool *pfound, bool *pfound,
__u8 **pparams_ptr,
size_t *pparams_len,
struct hid_device *hdev) struct hid_device *hdev)
{ {
int rc; int rc;
bool found = false; bool found = false;
/* Buffer for (part of) the string descriptor */ /* Buffer for (part of) the parameter string descriptor */
__u8 *buf = NULL; __u8 *buf = NULL;
/* Descriptor length required */ /* Parameter string descriptor required length */
const int len = 18; 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; s32 resolution;
/* Pen report descriptor template parameters */ /* Pen report descriptor template parameters */
s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; 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, ...@@ -292,7 +394,7 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
* the Windows driver traffic. * the Windows driver traffic.
* NOTE: This enables fully-functional tablet mode. * 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) { if (rc == -EPIPE) {
hid_dbg(hdev, hid_dbg(hdev,
"string descriptor with pen parameters not found, assuming not compatible\n"); "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, ...@@ -300,27 +402,28 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
} else if (rc < 0) { } else if (rc < 0) {
hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
goto cleanup; goto cleanup;
} else if (rc != len) { } else if (rc < params_len_min) {
hid_dbg(hdev, hid_dbg(hdev,
"string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n", "string descriptor with pen parameters is too short (got %d, expected at least %d), assuming not compatible\n",
rc, len); 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; 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, ...@@ -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 / desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
resolution; resolution;
} }
kfree(buf);
buf = NULL;
/* /*
* Generate pen report descriptor * Generate pen report descriptor
...@@ -371,6 +472,13 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen, ...@@ -371,6 +472,13 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
pen->fragmented_hires = true; pen->fragmented_hires = true;
pen->tilt_y_flipped = true; pen->tilt_y_flipped = true;
found = true; found = true;
if (pparams_ptr != NULL) {
*pparams_ptr = buf;
buf = NULL;
}
if (pparams_len != NULL)
*pparams_len = params_len;
finish: finish:
*pfound = found; *pfound = found;
rc = 0; rc = 0;
...@@ -700,6 +808,14 @@ static int uclogic_params_huion_init(struct uclogic_params *params, ...@@ -700,6 +808,14 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
static const char transition_ver[] = "HUION_T153_160607"; static const char transition_ver[] = "HUION_T153_160607";
char *ver_ptr = NULL; char *ver_ptr = NULL;
const size_t ver_len = sizeof(transition_ver) + 1; 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 */ /* Check arguments */
if (params == NULL || hdev == NULL) { if (params == NULL || hdev == NULL) {
...@@ -711,8 +827,13 @@ static int uclogic_params_huion_init(struct uclogic_params *params, ...@@ -711,8 +827,13 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
iface = to_usb_interface(hdev->dev.parent); iface = to_usb_interface(hdev->dev.parent);
bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
/* If it's not a pen interface */ /* If it's a custom keyboard interface */
if (bInterfaceNumber != 0) { 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); uclogic_params_init_invalid(&p);
goto output; goto output;
} }
...@@ -738,29 +859,103 @@ static int uclogic_params_huion_init(struct uclogic_params *params, ...@@ -738,29 +859,103 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
"transition firmware detected, not probing pen v2 parameters\n"); "transition firmware detected, not probing pen v2 parameters\n");
} else { } else {
/* Try to probe v2 pen parameters */ /* 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) { if (rc != 0) {
hid_err(hdev, hid_err(hdev,
"failed probing pen v2 parameters: %d\n", rc); "failed probing pen v2 parameters: %d\n", rc);
goto cleanup; goto cleanup;
} else if (found) { } else if (found) {
hid_dbg(hdev, "pen v2 parameters found\n"); hid_dbg(hdev, "pen v2 parameters found\n");
/* Create v2 frame parameters */ /* Create v2 frame button parameters */
rc = uclogic_params_frame_init_with_desc( rc = uclogic_params_frame_init_with_desc(
&p.frame_list[0], &p.frame_list[0],
uclogic_rdesc_v2_frame_arr, uclogic_rdesc_v2_frame_buttons_arr,
uclogic_rdesc_v2_frame_size, uclogic_rdesc_v2_frame_buttons_size,
UCLOGIC_RDESC_V2_FRAME_ID); UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID);
if (rc != 0) { if (rc != 0) {
hid_err(hdev, hid_err(hdev,
"failed creating v2 frame parameters: %d\n", "failed creating v2 frame button parameters: %d\n",
rc); rc);
goto cleanup; 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].value = 0xe0;
p.pen.subreport_list[0].id = 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; goto output;
} }
hid_dbg(hdev, "pen v2 parameters not found\n"); hid_dbg(hdev, "pen v2 parameters not found\n");
...@@ -801,6 +996,7 @@ static int uclogic_params_huion_init(struct uclogic_params *params, ...@@ -801,6 +996,7 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
memset(&p, 0, sizeof(p)); memset(&p, 0, sizeof(p));
rc = 0; rc = 0;
cleanup: cleanup:
kfree(params_ptr);
kfree(ver_ptr); kfree(ver_ptr);
uclogic_params_cleanup(&p); uclogic_params_cleanup(&p);
return rc; return rc;
...@@ -999,6 +1195,8 @@ int uclogic_params_init(struct uclogic_params *params, ...@@ -999,6 +1195,8 @@ int uclogic_params_init(struct uclogic_params *params,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540): USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540):
case VID_PID(USB_VENDOR_ID_UGEE, case VID_PID(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640): 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, case VID_PID(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720): USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720):
/* If this is the pen interface */ /* If this is the pen interface */
......
...@@ -29,11 +29,6 @@ enum uclogic_params_pen_inrange { ...@@ -29,11 +29,6 @@ enum uclogic_params_pen_inrange {
UCLOGIC_PARAMS_PEN_INRANGE_NONE, 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. * Pen report's subreport data.
*/ */
...@@ -62,8 +57,13 @@ struct uclogic_params_pen_subreport { ...@@ -62,8 +57,13 @@ struct uclogic_params_pen_subreport {
*/ */
struct uclogic_params_pen { struct uclogic_params_pen {
/* /*
* Pointer to report descriptor describing the inputs. * True if pen usage is invalid for this interface and should be
* Allocated with kmalloc. * 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; __u8 *desc_ptr;
/* /*
...@@ -73,8 +73,8 @@ struct uclogic_params_pen { ...@@ -73,8 +73,8 @@ struct uclogic_params_pen {
unsigned int desc_size; unsigned int desc_size;
/* Report ID, if reports should be tweaked, zero if not */ /* Report ID, if reports should be tweaked, zero if not */
unsigned int id; unsigned int id;
/* The list of subreports */ /* The list of subreports, only valid if "id" is not zero */
struct uclogic_params_pen_subreport subreport_list[1]; struct uclogic_params_pen_subreport subreport_list[3];
/* Type of in-range reporting, only valid if "id" is not zero */ /* Type of in-range reporting, only valid if "id" is not zero */
enum uclogic_params_pen_inrange inrange; enum uclogic_params_pen_inrange inrange;
/* /*
...@@ -101,8 +101,8 @@ struct uclogic_params_pen { ...@@ -101,8 +101,8 @@ struct uclogic_params_pen {
*/ */
struct uclogic_params_frame { struct uclogic_params_frame {
/* /*
* Pointer to report descriptor describing the inputs. * Pointer to report descriptor part describing the frame inputs.
* Allocated with kmalloc. * Allocated with kmalloc. NULL if the part is not specified.
*/ */
__u8 *desc_ptr; __u8 *desc_ptr;
/* /*
...@@ -114,6 +114,10 @@ struct uclogic_params_frame { ...@@ -114,6 +114,10 @@ struct uclogic_params_frame {
* Report ID, if reports should be tweaked, zero if not. * Report ID, if reports should be tweaked, zero if not.
*/ */
unsigned int id; 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 * 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 * encoder, in the report. Cannot point to a 2-bit field crossing a
...@@ -123,10 +127,40 @@ struct uclogic_params_frame { ...@@ -123,10 +127,40 @@ struct uclogic_params_frame {
/* /*
* Offset of the Wacom-style device ID byte in the report, to be set * 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 * 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 * if no changes to the report should be made. The ID byte will be set
* not zero. * 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; 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 { ...@@ -156,7 +190,7 @@ struct uclogic_params {
__u8 *desc_ptr; __u8 *desc_ptr;
/* /*
* Size of the common part of the replacement report descriptor. * 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; unsigned int desc_size;
/* /*
...@@ -168,50 +202,13 @@ struct uclogic_params { ...@@ -168,50 +202,13 @@ struct uclogic_params {
* The list of frame control parameters and optional report descriptor * The list of frame control parameters and optional report descriptor
* parts. Only valid, if "invalid" is false. * 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 */ /* Initialize a tablet interface and discover its parameters */
extern int uclogic_params_init(struct uclogic_params *params, extern int uclogic_params_init(struct uclogic_params *params,
struct hid_device *hdev); 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. */ /* Get a replacement report descriptor for a tablet's interface. */
extern int uclogic_params_get_desc(const struct uclogic_params *params, extern int uclogic_params_get_desc(const struct uclogic_params *params,
__u8 **pdesc, __u8 **pdesc,
...@@ -220,4 +217,8 @@ extern int uclogic_params_get_desc(const struct uclogic_params *params, ...@@ -220,4 +217,8 @@ extern int uclogic_params_get_desc(const struct uclogic_params *params,
/* Free resources used by tablet interface's parameters */ /* Free resources used by tablet interface's parameters */
extern void uclogic_params_cleanup(struct uclogic_params *params); 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 */ #endif /* _HID_UCLOGIC_PARAMS_H */
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
/* Fixed WP4030U report descriptor */ /* Fixed WP4030U report descriptor */
__u8 uclogic_rdesc_wp4030u_fixed_arr[] = { __u8 uclogic_rdesc_wp4030u_fixed_arr[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x09, /* Report ID (9), */ 0x85, 0x09, /* Report ID (9), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -66,7 +66,7 @@ const size_t uclogic_rdesc_wp4030u_fixed_size = ...@@ -66,7 +66,7 @@ const size_t uclogic_rdesc_wp4030u_fixed_size =
/* Fixed WP5540U report descriptor */ /* Fixed WP5540U report descriptor */
__u8 uclogic_rdesc_wp5540u_fixed_arr[] = { __u8 uclogic_rdesc_wp5540u_fixed_arr[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x09, /* Report ID (9), */ 0x85, 0x09, /* Report ID (9), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -143,7 +143,7 @@ const size_t uclogic_rdesc_wp5540u_fixed_size = ...@@ -143,7 +143,7 @@ const size_t uclogic_rdesc_wp5540u_fixed_size =
/* Fixed WP8060U report descriptor */ /* Fixed WP8060U report descriptor */
__u8 uclogic_rdesc_wp8060u_fixed_arr[] = { __u8 uclogic_rdesc_wp8060u_fixed_arr[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x09, /* Report ID (9), */ 0x85, 0x09, /* Report ID (9), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -220,7 +220,7 @@ const size_t uclogic_rdesc_wp8060u_fixed_size = ...@@ -220,7 +220,7 @@ const size_t uclogic_rdesc_wp8060u_fixed_size =
/* Fixed WP1062 report descriptor */ /* Fixed WP1062 report descriptor */
__u8 uclogic_rdesc_wp1062_fixed_arr[] = { __u8 uclogic_rdesc_wp1062_fixed_arr[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x09, /* Report ID (9), */ 0x85, 0x09, /* Report ID (9), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -268,7 +268,7 @@ const size_t uclogic_rdesc_wp1062_fixed_size = ...@@ -268,7 +268,7 @@ const size_t uclogic_rdesc_wp1062_fixed_size =
/* Fixed PF1209 report descriptor */ /* Fixed PF1209 report descriptor */
__u8 uclogic_rdesc_pf1209_fixed_arr[] = { __u8 uclogic_rdesc_pf1209_fixed_arr[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x09, /* Report ID (9), */ 0x85, 0x09, /* Report ID (9), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -345,7 +345,7 @@ const size_t uclogic_rdesc_pf1209_fixed_size = ...@@ -345,7 +345,7 @@ const size_t uclogic_rdesc_pf1209_fixed_size =
/* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */ /* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */
__u8 uclogic_rdesc_twhl850_fixed0_arr[] = { __u8 uclogic_rdesc_twhl850_fixed0_arr[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x09, /* Report ID (9), */ 0x85, 0x09, /* Report ID (9), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -457,7 +457,7 @@ const size_t uclogic_rdesc_twhl850_fixed2_size = ...@@ -457,7 +457,7 @@ const size_t uclogic_rdesc_twhl850_fixed2_size =
/* Fixed TWHA60 report descriptor, interface 0 (stylus) */ /* Fixed TWHA60 report descriptor, interface 0 (stylus) */
__u8 uclogic_rdesc_twha60_fixed0_arr[] = { __u8 uclogic_rdesc_twha60_fixed0_arr[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x09, /* Report ID (9), */ 0x85, 0x09, /* Report ID (9), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -534,7 +534,7 @@ const size_t uclogic_rdesc_twha60_fixed1_size = ...@@ -534,7 +534,7 @@ const size_t uclogic_rdesc_twha60_fixed1_size =
/* Fixed report descriptor template for (tweaked) v1 pen reports */ /* Fixed report descriptor template for (tweaked) v1 pen reports */
const __u8 uclogic_rdesc_v1_pen_template_arr[] = { const __u8 uclogic_rdesc_v1_pen_template_arr[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x07, /* Report ID (7), */ 0x85, 0x07, /* Report ID (7), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -588,7 +588,7 @@ const size_t uclogic_rdesc_v1_pen_template_size = ...@@ -588,7 +588,7 @@ const size_t uclogic_rdesc_v1_pen_template_size =
/* Fixed report descriptor template for (tweaked) v2 pen reports */ /* Fixed report descriptor template for (tweaked) v2 pen reports */
const __u8 uclogic_rdesc_v2_pen_template_arr[] = { const __u8 uclogic_rdesc_v2_pen_template_arr[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x08, /* Report ID (8), */ 0x85, 0x08, /* Report ID (8), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
...@@ -652,12 +652,12 @@ const size_t uclogic_rdesc_v2_pen_template_size = ...@@ -652,12 +652,12 @@ const size_t uclogic_rdesc_v2_pen_template_size =
sizeof(uclogic_rdesc_v2_pen_template_arr); 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. * @_id: The report ID to use.
* @_size: Size of the report to pad to, including report ID, bytes. * @_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), */ \ 0x05, 0x01, /* Usage Page (Desktop), */ \
0x09, 0x07, /* Usage (Keypad), */ \ 0x09, 0x07, /* Usage (Keypad), */ \
0xA1, 0x01, /* Collection (Application), */ \ 0xA1, 0x01, /* Collection (Application), */ \
...@@ -700,17 +700,164 @@ const size_t uclogic_rdesc_v2_pen_template_size = ...@@ -700,17 +700,164 @@ const size_t uclogic_rdesc_v2_pen_template_size =
/* Fixed report descriptor for (tweaked) v1 frame reports */ /* Fixed report descriptor for (tweaked) v1 frame reports */
const __u8 uclogic_rdesc_v1_frame_arr[] = { 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 = const size_t uclogic_rdesc_v1_frame_size =
sizeof(uclogic_rdesc_v1_frame_arr); sizeof(uclogic_rdesc_v1_frame_arr);
/* Fixed report descriptor for (tweaked) v2 frame reports */ /* Fixed report descriptor for (tweaked) v2 frame button reports */
const __u8 uclogic_rdesc_v2_frame_arr[] = { const __u8 uclogic_rdesc_v2_frame_buttons_arr[] = {
UCLOGIC_RDESC_FRAME_BYTES(UCLOGIC_RDESC_V2_FRAME_ID, 12) UCLOGIC_RDESC_FRAME_BUTTONS_BYTES(UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID,
12)
}; };
const size_t uclogic_rdesc_v2_frame_size = const size_t uclogic_rdesc_v2_frame_buttons_size =
sizeof(uclogic_rdesc_v2_frame_arr); 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 */ /* Fixed report descriptor for Ugee EX07 frame */
const __u8 uclogic_rdesc_ugee_ex07_frame_arr[] = { const __u8 uclogic_rdesc_ugee_ex07_frame_arr[] = {
......
...@@ -124,12 +124,36 @@ extern const size_t uclogic_rdesc_v2_pen_template_size; ...@@ -124,12 +124,36 @@ extern const size_t uclogic_rdesc_v2_pen_template_size;
extern const __u8 uclogic_rdesc_v1_frame_arr[]; extern const __u8 uclogic_rdesc_v1_frame_arr[];
extern const size_t uclogic_rdesc_v1_frame_size; extern const size_t uclogic_rdesc_v1_frame_size;
/* Report ID for tweaked v2 frame reports */ /* Report ID for tweaked v2 frame button reports */
#define UCLOGIC_RDESC_V2_FRAME_ID 0xf7 #define UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID 0xf7
/* Fixed report descriptor for (tweaked) v2 frame reports */ /* Fixed report descriptor for (tweaked) v2 frame button reports */
extern const __u8 uclogic_rdesc_v2_frame_arr[]; extern const __u8 uclogic_rdesc_v2_frame_buttons_arr[];
extern const size_t uclogic_rdesc_v2_frame_size; 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 */ /* Fixed report descriptor for Ugee EX07 frame */
extern const __u8 uclogic_rdesc_ugee_ex07_frame_arr[]; extern const __u8 uclogic_rdesc_ugee_ex07_frame_arr[];
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
/* Fixed report descriptor of PD1011 signature pad */ /* Fixed report descriptor of PD1011 signature pad */
static __u8 pd1011_rdesc_fixed[] = { static __u8 pd1011_rdesc_fixed[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0x85, 0x02, /* Report ID (2), */ 0x85, 0x02, /* Report ID (2), */
0x09, 0x20, /* Usage (Stylus), */ 0x09, 0x20, /* Usage (Stylus), */
......
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
#define TGL_H_DEVICE_ID 0x43FC #define TGL_H_DEVICE_ID 0x43FC
#define ADL_S_DEVICE_ID 0x7AF8 #define ADL_S_DEVICE_ID 0x7AF8
#define ADL_P_DEVICE_ID 0x51FC #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_A0 0x6
#define REVISION_ID_CHT_Ax_SI 0x0 #define REVISION_ID_CHT_Ax_SI 0x0
......
...@@ -41,6 +41,8 @@ static const struct pci_device_id ish_pci_tbl[] = { ...@@ -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, TGL_H_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_S_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_P_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_N_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, RPL_S_DEVICE_ID)},
{0, } {0, }
}; };
MODULE_DEVICE_TABLE(pci, ish_pci_tbl); MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
......
...@@ -1777,7 +1777,7 @@ static int __wacom_initialize_battery(struct wacom *wacom, ...@@ -1777,7 +1777,7 @@ static int __wacom_initialize_battery(struct wacom *wacom,
bat_desc->get_property = wacom_battery_get_property; bat_desc->get_property = wacom_battery_get_property;
sprintf(battery->bat_name, "wacom_battery_%ld", n); sprintf(battery->bat_name, "wacom_battery_%ld", n);
bat_desc->name = battery->bat_name; 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; bat_desc->use_for_apm = 0;
ps_bat = devm_power_supply_register(dev, bat_desc, &psy_cfg); ps_bat = devm_power_supply_register(dev, bat_desc, &psy_cfg);
......
...@@ -1811,7 +1811,9 @@ int wacom_equivalent_usage(int usage) ...@@ -1811,7 +1811,9 @@ int wacom_equivalent_usage(int usage)
usage == WACOM_HID_WD_TOUCHSTRIP2 || usage == WACOM_HID_WD_TOUCHSTRIP2 ||
usage == WACOM_HID_WD_TOUCHRING || usage == WACOM_HID_WD_TOUCHRING ||
usage == WACOM_HID_WD_TOUCHRINGSTATUS || 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; return usage;
} }
...@@ -2196,8 +2198,11 @@ static void wacom_set_barrel_switch3_usage(struct wacom_wac *wacom_wac) ...@@ -2196,8 +2198,11 @@ static void wacom_set_barrel_switch3_usage(struct wacom_wac *wacom_wac)
if (!(features->quirks & WACOM_QUIRK_AESPEN) && if (!(features->quirks & WACOM_QUIRK_AESPEN) &&
wacom_wac->hid_data.barrelswitch && wacom_wac->hid_data.barrelswitch &&
wacom_wac->hid_data.barrelswitch2 && 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); 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, 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, ...@@ -2261,6 +2266,9 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
features->quirks |= WACOM_QUIRK_TOOLSERIAL; features->quirks |= WACOM_QUIRK_TOOLSERIAL;
wacom_map_usage(input, usage, field, EV_MSC, MSC_SERIAL, 0); wacom_map_usage(input, usage, field, EV_MSC, MSC_SERIAL, 0);
break; break;
case HID_DG_SCANTIME:
wacom_map_usage(input, usage, field, EV_MSC, MSC_TIMESTAMP, 0);
break;
case WACOM_HID_WD_SENSE: case WACOM_HID_WD_SENSE:
features->quirks |= WACOM_QUIRK_SENSE; features->quirks |= WACOM_QUIRK_SENSE;
wacom_map_usage(input, usage, field, EV_KEY, BTN_TOOL_PEN, 0); 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, ...@@ -2274,6 +2282,11 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
input_set_capability(input, EV_KEY, BTN_TOOL_AIRBRUSH); input_set_capability(input, EV_KEY, BTN_TOOL_AIRBRUSH);
wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0); wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0);
break; 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 ...@@ -2390,6 +2403,14 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
case WACOM_HID_WD_REPORT_VALID: case WACOM_HID_WD_REPORT_VALID:
wacom_wac->is_invalid_bt_frame = !value; wacom_wac->is_invalid_bt_frame = !value;
return; 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 /* 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, ...@@ -2442,12 +2463,15 @@ static void wacom_wac_pen_report(struct hid_device *hdev,
if (!delay_pen_events(wacom_wac) && wacom_wac->tool[0]) { if (!delay_pen_events(wacom_wac) && wacom_wac->tool[0]) {
int id = wacom_wac->id[0]; int id = wacom_wac->id[0];
int sw_state = wacom_wac->hid_data.barrelswitch | if (wacom_wac->features.quirks & WACOM_QUIRK_PEN_BUTTON3 &&
(wacom_wac->hid_data.barrelswitch2 << 1); wacom_wac->hid_data.barrelswitch & wacom_wac->hid_data.barrelswitch2) {
wacom_wac->hid_data.barrelswitch = 0;
input_report_key(input, BTN_STYLUS, sw_state == 1); wacom_wac->hid_data.barrelswitch2 = 0;
input_report_key(input, BTN_STYLUS2, sw_state == 2); wacom_wac->hid_data.barrelswitch3 = 1;
input_report_key(input, BTN_STYLUS3, sw_state == 3); }
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 * 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, ...@@ -2529,6 +2553,9 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
field->logical_maximum = 255; field->logical_maximum = 255;
} }
break; break;
case HID_DG_SCANTIME:
wacom_map_usage(input, usage, field, EV_MSC, MSC_TIMESTAMP, 0);
break;
} }
} }
......
...@@ -86,6 +86,7 @@ ...@@ -86,6 +86,7 @@
#define WACOM_QUIRK_AESPEN 0x0004 #define WACOM_QUIRK_AESPEN 0x0004
#define WACOM_QUIRK_BATTERY 0x0008 #define WACOM_QUIRK_BATTERY 0x0008
#define WACOM_QUIRK_TOOLSERIAL 0x0010 #define WACOM_QUIRK_TOOLSERIAL 0x0010
#define WACOM_QUIRK_PEN_BUTTON3 0x0020
/* device types */ /* device types */
#define WACOM_DEVICETYPE_NONE 0x0000 #define WACOM_DEVICETYPE_NONE 0x0000
...@@ -108,6 +109,7 @@ ...@@ -108,6 +109,7 @@
#define WACOM_HID_WD_DIGITIZERFNKEYS (WACOM_HID_UP_WACOMDIGITIZER | 0x39) #define WACOM_HID_WD_DIGITIZERFNKEYS (WACOM_HID_UP_WACOMDIGITIZER | 0x39)
#define WACOM_HID_WD_SERIALNUMBER (WACOM_HID_UP_WACOMDIGITIZER | 0x5b) #define WACOM_HID_WD_SERIALNUMBER (WACOM_HID_UP_WACOMDIGITIZER | 0x5b)
#define WACOM_HID_WD_SERIALHI (WACOM_HID_UP_WACOMDIGITIZER | 0x5c) #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_TOOLTYPE (WACOM_HID_UP_WACOMDIGITIZER | 0x77)
#define WACOM_HID_WD_DISTANCE (WACOM_HID_UP_WACOMDIGITIZER | 0x0132) #define WACOM_HID_WD_DISTANCE (WACOM_HID_UP_WACOMDIGITIZER | 0x0132)
#define WACOM_HID_WD_TOUCHSTRIP (WACOM_HID_UP_WACOMDIGITIZER | 0x0136) #define WACOM_HID_WD_TOUCHSTRIP (WACOM_HID_UP_WACOMDIGITIZER | 0x0136)
...@@ -115,6 +117,7 @@ ...@@ -115,6 +117,7 @@
#define WACOM_HID_WD_TOUCHRING (WACOM_HID_UP_WACOMDIGITIZER | 0x0138) #define WACOM_HID_WD_TOUCHRING (WACOM_HID_UP_WACOMDIGITIZER | 0x0138)
#define WACOM_HID_WD_TOUCHRINGSTATUS (WACOM_HID_UP_WACOMDIGITIZER | 0x0139) #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_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_X (WACOM_HID_UP_WACOMDIGITIZER | 0x0401)
#define WACOM_HID_WD_ACCELEROMETER_Y (WACOM_HID_UP_WACOMDIGITIZER | 0x0402) #define WACOM_HID_WD_ACCELEROMETER_Y (WACOM_HID_UP_WACOMDIGITIZER | 0x0402)
#define WACOM_HID_WD_ACCELEROMETER_Z (WACOM_HID_UP_WACOMDIGITIZER | 0x0403) #define WACOM_HID_WD_ACCELEROMETER_Z (WACOM_HID_UP_WACOMDIGITIZER | 0x0403)
...@@ -300,6 +303,7 @@ struct hid_data { ...@@ -300,6 +303,7 @@ struct hid_data {
bool tipswitch; bool tipswitch;
bool barrelswitch; bool barrelswitch;
bool barrelswitch2; bool barrelswitch2;
bool barrelswitch3;
bool serialhi; bool serialhi;
bool confidence; bool confidence;
int x; int x;
...@@ -320,6 +324,7 @@ struct hid_data { ...@@ -320,6 +324,7 @@ struct hid_data {
int bat_connected; int bat_connected;
int ps_connected; int ps_connected;
bool pad_input_event_flag; bool pad_input_event_flag;
unsigned short sequence_number;
}; };
struct wacom_remote_data { struct wacom_remote_data {
......
...@@ -271,6 +271,7 @@ struct input_mask { ...@@ -271,6 +271,7 @@ struct input_mask {
#define BUS_RMI 0x1D #define BUS_RMI 0x1D
#define BUS_CEC 0x1E #define BUS_CEC 0x1E
#define BUS_INTEL_ISHTP 0x1F #define BUS_INTEL_ISHTP 0x1F
#define BUS_AMD_SFH 0x20
/* /*
* MT_TOOL types * MT_TOOL types
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment