Commit 34da76dc authored by Linus Torvalds's avatar Linus Torvalds

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

Pull HID updates from Jiri Kosina:

 - import a bunch of HID selftests from out-of-tree hid-tools project
   (Benjamin Tissoires)

 - drastically reducing Bluetooth disconnects on hid-nintendo driven
   devices (Daniel J. Ogorchock)

 - lazy initialization of battery interfaces in wacom driver (Jason
   Gerecke)

 - generic support for all Kye tablets (David Yang)

 - proper rumble queue overrun handling in hid-nintendo (Daniel J.
   Ogorchock)

 - support for ADC measurement in logitech-hidpp driver (Bastien Nocera)

 - reset GPIO support in i2c-hid (Hans de Goede)

 - improved handling of generic "Digitizer" usage (Jason Gerecke)

 - support for KEY_CAMERA_FOCUS (Feng Qi)

 - quirks for Apple Geyser 3 and Apple Geyser 4 (Alex Henrie)

 - assorted functional fixes and device ID additions

* tag 'for-linus-2023042601' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (54 commits)
  HID: amd_sfh: Fix max supported HID devices
  HID: wacom: generic: Set battery quirk only when we see battery data
  HID: wacom: Lazy-init batteries
  HID: Ignore battery for ELAN touchscreen on ROG Flow X13 GV301RA
  HID: asus: explicitly include linux/leds.h
  HID: lg-g15: explicitly include linux/leds.h
  HID: steelseries: explicitly include linux/leds.h
  HID: apple: Set the tilde quirk flag on the Geyser 3
  HID: apple: explicitly include linux/leds.h
  HID: mcp2221: fix get and get_direction for gpio
  HID: mcp2221: fix report layout for gpio get
  HID: wacom: Set a default resolution for older tablets
  HID: i2c-hid-of: Add reset GPIO support to i2c-hid-of
  HID: i2c-hid-of: Allow using i2c-hid-of on non OF platforms
  HID: i2c-hid-of: Consistenly use dev local variable in probe()
  HID: kye: Fix rdesc for kye tablets
  HID: amd_sfh: Support for additional light sensor
  HID: amd_sfh: Handle "no sensors" enabled for SFH1.1
  HID: amd_sfh: Increase sensor command timeout for SFH1.1
  HID: amd_sfh: Correct the stop all command
  ...
parents 725a345b c3a6ef33
......@@ -166,6 +166,23 @@ Description:
The file will be present for all speeds of USB devices, and will
always read "no" for USB 1.1 and USB 2.0 devices.
What: /sys/bus/usb/devices/<INTERFACE>/wireless_status
Date: February 2023
Contact: Bastien Nocera <hadess@hadess.net>
Description:
Some USB devices use a USB receiver dongle to communicate
wirelessly with their device using proprietary protocols. This
attribute allows user-space to know whether the device is
connected to its receiver dongle, and, for example, consider
the device to be absent when choosing whether to show the
device's battery, show a headset in a list of outputs, or show
an on-screen keyboard if the only wireless keyboard is
turned off.
This attribute is not to be used to replace protocol specific
statuses available in WWAN, WLAN/Wi-Fi, Bluetooth, etc.
If the device does not use a receiver dongle with a wireless
device, then this attribute will not exist.
What: /sys/bus/usb/devices/.../<hub_interface>/port<X>
Date: August 2012
Contact: Lan Tianyu <tianyu.lan@intel.com>
......
......@@ -147,6 +147,7 @@ static const char *get_sensor_name(int idx)
case mag_idx:
return "magnetometer";
case als_idx:
case ACS_IDX: /* ambient color sensor */
return "ALS";
case HPD_IDX:
return "HPD";
......
......@@ -11,7 +11,7 @@
#ifndef AMDSFH_HID_H
#define AMDSFH_HID_H
#define MAX_HID_DEVICES 5
#define MAX_HID_DEVICES 6
#define AMD_SFH_HID_VENDOR 0x1022
#define AMD_SFH_HID_PRODUCT 0x0001
......
......@@ -29,6 +29,7 @@
#define MAGNO_EN BIT(2)
#define HPD_EN BIT(16)
#define ALS_EN BIT(19)
#define ACS_EN BIT(22)
static int sensor_mask_override = -1;
module_param_named(sensor_mask, sensor_mask_override, int, 0444);
......@@ -233,6 +234,9 @@ int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id)
if (HPD_EN & activestatus)
sensor_id[num_of_sensors++] = HPD_IDX;
if (ACS_EN & activestatus)
sensor_id[num_of_sensors++] = ACS_IDX;
return num_of_sensors;
}
......@@ -367,6 +371,14 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
return devm_add_action_or_reset(&pdev->dev, privdata->mp2_ops->remove, privdata);
}
static void amd_sfh_shutdown(struct pci_dev *pdev)
{
struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev);
if (mp2 && mp2->mp2_ops)
mp2->mp2_ops->stop_all(mp2);
}
static int __maybe_unused amd_mp2_pci_resume(struct device *dev)
{
struct amd_mp2_dev *mp2 = dev_get_drvdata(dev);
......@@ -401,6 +413,7 @@ static struct pci_driver amd_mp2_pci_driver = {
.id_table = amd_mp2_pci_tbl,
.probe = amd_mp2_pci_probe,
.driver.pm = &amd_mp2_pm_ops,
.shutdown = amd_sfh_shutdown,
};
module_pci_driver(amd_mp2_pci_driver);
......
......@@ -23,6 +23,7 @@
#define V2_STATUS 0x2
#define HPD_IDX 16
#define ACS_IDX 22
#define SENSOR_DISCOVERY_STATUS_MASK GENMASK(5, 3)
#define SENSOR_DISCOVERY_STATUS_SHIFT 3
......
......@@ -48,6 +48,7 @@ static int get_report_descriptor(int sensor_idx, u8 *rep_desc)
sizeof(comp3_report_descriptor));
break;
case als_idx: /* ambient light sensor */
case ACS_IDX: /* ambient color sensor */
memset(rep_desc, 0, sizeof(als_report_descriptor));
memcpy(rep_desc, als_report_descriptor,
sizeof(als_report_descriptor));
......@@ -97,6 +98,7 @@ static u32 get_descr_sz(int sensor_idx, int descriptor_name)
}
break;
case als_idx:
case ACS_IDX: /* ambient color sensor */
switch (descriptor_name) {
case descr_size:
return sizeof(als_report_descriptor);
......@@ -174,6 +176,7 @@ static u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report)
report_size = sizeof(magno_feature);
break;
case als_idx: /* ambient light sensor */
case ACS_IDX: /* ambient color sensor */
get_common_features(&als_feature.common_property, report_id);
als_feature.als_change_sesnitivity = HID_DEFAULT_SENSITIVITY;
als_feature.als_sensitivity_min = HID_DEFAULT_MIN_VALUE;
......@@ -245,6 +248,7 @@ static u8 get_input_report(u8 current_index, int sensor_idx, int report_id,
report_size = sizeof(magno_input);
break;
case als_idx: /* Als */
case ACS_IDX: /* ambient color sensor */
get_common_inputs(&als_input.common_property, report_id);
/* For ALS ,V2 Platforms uses C2P_MSG5 register instead of DRAM access method */
if (supported_input == V2_STATUS)
......
......@@ -218,7 +218,7 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id,
OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data));
get_common_inputs(&als_input.common_property, report_id);
als_input.illuminance_value = als_data.lux;
als_input.illuminance_value = float_to_int(als_data.lux);
report_size = sizeof(als_input);
memcpy(input_report, &als_input, sizeof(als_input));
break;
......
......@@ -112,6 +112,7 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
cl_data->num_hid_devices = amd_sfh_get_sensor_num(privdata, &cl_data->sensor_idx[0]);
if (cl_data->num_hid_devices == 0)
return -ENODEV;
cl_data->is_any_sensor_enabled = false;
INIT_DELAYED_WORK(&cl_data->work, amd_sfh_work);
INIT_DELAYED_WORK(&cl_data->work_buffer, amd_sfh_work_buffer);
......@@ -170,6 +171,7 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
status = (status == 0) ? SENSOR_ENABLED : SENSOR_DISABLED;
if (status == SENSOR_ENABLED) {
cl_data->is_any_sensor_enabled = true;
cl_data->sensor_sts[i] = SENSOR_ENABLED;
rc = amdtp_hid_probe(i, cl_data);
if (rc) {
......@@ -186,12 +188,21 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
cl_data->sensor_sts[i]);
goto cleanup;
}
} else {
cl_data->sensor_sts[i] = SENSOR_DISABLED;
}
dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
}
if (!cl_data->is_any_sensor_enabled) {
dev_warn(dev, "Failed to discover, sensors not enabled is %d\n",
cl_data->is_any_sensor_enabled);
rc = -EOPNOTSUPP;
goto cleanup;
}
schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
return 0;
......
......@@ -16,11 +16,11 @@ static int amd_sfh_wait_response(struct amd_mp2_dev *mp2, u8 sid, u32 cmd_id)
{
struct sfh_cmd_response cmd_resp;
/* Get response with status within a max of 1600 ms timeout */
/* Get response with status within a max of 10000 ms timeout */
if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG(0), cmd_resp.resp,
(cmd_resp.response.response == 0 &&
cmd_resp.response.cmd_id == cmd_id && (sid == 0xff ||
cmd_resp.response.sensor_id == sid)), 500, 1600000))
cmd_resp.response.sensor_id == sid)), 500, 10000000))
return cmd_resp.response.response;
return -1;
......@@ -33,6 +33,7 @@ static void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor
cmd_base.ul = 0;
cmd_base.cmd.cmd_id = ENABLE_SENSOR;
cmd_base.cmd.intr_disable = 0;
cmd_base.cmd.sub_cmd_value = 1;
cmd_base.cmd.sensor_id = info.sensor_idx;
writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG(0));
......@@ -45,6 +46,7 @@ static void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx)
cmd_base.ul = 0;
cmd_base.cmd.cmd_id = DISABLE_SENSOR;
cmd_base.cmd.intr_disable = 0;
cmd_base.cmd.sub_cmd_value = 1;
cmd_base.cmd.sensor_id = sensor_idx;
writeq(0x0, privdata->mmio + AMD_C2P_MSG(1));
......@@ -56,8 +58,10 @@ static void amd_stop_all_sensor(struct amd_mp2_dev *privdata)
struct sfh_cmd_base cmd_base;
cmd_base.ul = 0;
cmd_base.cmd.cmd_id = STOP_ALL_SENSORS;
cmd_base.cmd.cmd_id = DISABLE_SENSOR;
cmd_base.cmd.intr_disable = 0;
/* 0xf indicates all sensors */
cmd_base.cmd.sensor_id = 0xf;
writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG(0));
}
......
......@@ -33,9 +33,9 @@ struct sfh_cmd_base {
struct {
u32 sensor_id : 4;
u32 cmd_id : 4;
u32 sub_cmd_id : 6;
u32 length : 12;
u32 rsvd : 5;
u32 sub_cmd_id : 8;
u32 sub_cmd_value : 12;
u32 rsvd : 3;
u32 intr_disable : 1;
} cmd;
};
......@@ -133,7 +133,7 @@ struct sfh_mag_data {
struct sfh_als_data {
struct sfh_common_data commondata;
u16 lux;
u32 lux;
};
struct hpd_status {
......
......@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/leds.h>
#include "hid-ids.h"
......@@ -875,14 +876,16 @@ static const struct hid_device_id apple_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
APPLE_RDESC_JIS },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
APPLE_RDESC_JIS },
......@@ -901,7 +904,8 @@ static const struct hid_device_id apple_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
APPLE_RDESC_JIS },
......@@ -942,31 +946,31 @@ static const struct hid_device_id apple_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
.driver_data = APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO),
.driver_data = APPLE_HAS_FN },
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS),
.driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI),
.driver_data = APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO),
.driver_data = APPLE_HAS_FN },
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS),
.driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI),
.driver_data = APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ISO),
.driver_data = APPLE_HAS_FN },
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_JIS),
.driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI),
.driver_data = APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_ISO),
.driver_data = APPLE_HAS_FN },
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4_JIS),
.driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI),
.driver_data = APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO),
.driver_data = APPLE_HAS_FN },
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS),
.driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI),
......
......@@ -30,6 +30,7 @@
#include <linux/input/mt.h>
#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
#include <linux/power_supply.h>
#include <linux/leds.h>
#include "hid-ids.h"
......
......@@ -415,6 +415,7 @@
#define I2C_DEVICE_ID_HP_SPECTRE_X360_15 0x2817
#define I2C_DEVICE_ID_HP_SPECTRE_X360_13_AW0020NG 0x29DF
#define I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN 0x2BC8
#define I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN 0x2C82
#define USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN 0x2544
#define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706
#define I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN 0x261A
......@@ -721,12 +722,19 @@
#define USB_DEVICE_ID_GENIUS_MANTICORE 0x0153
#define USB_DEVICE_ID_GENIUS_GX_IMPERATOR 0x4018
#define USB_DEVICE_ID_KYE_GPEN_560 0x5003
#define USB_DEVICE_ID_KYE_EASYPEN_M406 0x5005
#define USB_DEVICE_ID_KYE_EASYPEN_M506 0x500F
#define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010
#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011
#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2 0x501a
#define USB_DEVICE_ID_KYE_EASYPEN_M406W 0x5012
#define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013
#define USB_DEVICE_ID_KYE_EASYPEN_340 0x5014
#define USB_DEVICE_ID_KYE_PENSKETCH_M912 0x5015
#define USB_DEVICE_ID_KYE_MOUSEPEN_M508WX 0x5016
#define USB_DEVICE_ID_KYE_MOUSEPEN_M508X 0x5017
#define USB_DEVICE_ID_KYE_EASYPEN_M406XE 0x5019
#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2 0x501A
#define USB_DEVICE_ID_KYE_PENSKETCH_T609A 0x501B
#define USB_VENDOR_ID_LABTEC 0x1020
#define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006
......
......@@ -372,6 +372,8 @@ static const struct hid_device_id hid_battery_quirks[] = {
HID_BATTERY_QUIRK_IGNORE },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN),
......@@ -1267,6 +1269,16 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
return;
}
goto unknown;
case HID_UP_CAMERA:
switch (usage->hid & HID_USAGE) {
case 0x020:
map_key_clear(KEY_CAMERA_FOCUS); break;
case 0x021:
map_key_clear(KEY_CAMERA); break;
default:
goto ignore;
}
break;
case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */
set_bit(EV_REP, input->evbit);
......
......@@ -5,329 +5,176 @@
* Copyright (c) 2009 Jiri Kosina
* Copyright (c) 2009 Tomas Hanak
* Copyright (c) 2012 Nikolai Kondrashov
* Copyright (c) 2023 David Yang
*/
/*
*/
#include <asm-generic/unaligned.h>
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
/* Original EasyPen i405X report descriptor size */
#define EASYPEN_I405X_RDESC_ORIG_SIZE 476
/* Data gathered from Database/VID0458_PID????/Vista/TBoard/default.xml in ioTablet driver
*
* TODO:
* - Add battery and sleep support for EasyPen M406W and MousePen M508WX
* - Investigate ScrollZ.MiceFMT buttons of EasyPen M406
*/
/* Fixed EasyPen i405X report descriptor */
static __u8 easypen_i405x_rdesc_fixed[] = {
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x01, /* Usage (01h), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x05, /* Report ID (5), */
0x09, 0x01, /* Usage (01h), */
0x15, 0x80, /* Logical Minimum (-128), */
0x25, 0x7F, /* Logical Maximum (127), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x07, /* Report Count (7), */
0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x01, /* Usage (Digitizer), */
static const __u8 easypen_m406_control_rdesc[] = {
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x10, /* Report ID (16), */
0x09, 0x20, /* Usage (Stylus), */
0xA0, /* Collection (Physical), */
0x85, 0x12, /* Report ID (18), */
0x0A, 0x45, 0x02, /* Usage (AC Rotate), */
0x09, 0x40, /* Usage (Menu), */
0x0A, 0x2F, 0x02, /* Usage (AC Zoom), */
0x0A, 0x46, 0x02, /* Usage (AC Resize), */
0x0A, 0x1A, 0x02, /* Usage (AC Undo), */
0x0A, 0x6A, 0x02, /* Usage (AC Delete), */
0x0A, 0x24, 0x02, /* Usage (AC Back), */
0x0A, 0x25, 0x02, /* Usage (AC Forward), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x09, 0x42, /* Usage (Tip Switch), */
0x09, 0x44, /* Usage (Barrel Switch), */
0x09, 0x46, /* Usage (Tablet Pick), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x03, /* Input (Constant, Variable), */
0x09, 0x32, /* Usage (In Range), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x02, /* Input (Variable), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x46, 0x7C, 0x15, /* Physical Maximum (5500), */
0x26, 0x00, 0x37, /* Logical Maximum (14080), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0xA0, 0x0F, /* Physical Maximum (4000), */
0x26, 0x00, 0x28, /* Logical Maximum (10240), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x09, 0x30, /* Usage (Tip Pressure), */
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
0x95, 0x08, /* Report Count (8), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0x95, 0x30, /* Report Count (48), */
0x81, 0x01, /* Input (Constant), */
0xC0 /* End Collection */
};
/* Original MousePen i608X report descriptor size */
#define MOUSEPEN_I608X_RDESC_ORIG_SIZE 476
/* Fixed MousePen i608X report descriptor */
static __u8 mousepen_i608x_rdesc_fixed[] = {
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x01, /* Usage (01h), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x05, /* Report ID (5), */
0x09, 0x01, /* Usage (01h), */
0x15, 0x80, /* Logical Minimum (-128), */
0x25, 0x7F, /* Logical Maximum (127), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x07, /* Report Count (7), */
0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x01, /* Usage (Digitizer), */
static const __u8 easypen_m506_control_rdesc[] = {
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x10, /* Report ID (16), */
0x09, 0x20, /* Usage (Stylus), */
0xA0, /* Collection (Physical), */
0x85, 0x12, /* Report ID (18), */
0x0A, 0x6A, 0x02, /* Usage (AC Delete), */
0x0A, 0x1A, 0x02, /* Usage (AC Undo), */
0x0A, 0x2D, 0x02, /* Usage (AC Zoom In), */
0x0A, 0x2E, 0x02, /* Usage (AC Zoom Out), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x09, 0x42, /* Usage (Tip Switch), */
0x09, 0x44, /* Usage (Barrel Switch), */
0x09, 0x46, /* Usage (Tablet Pick), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x03, /* Input (Constant, Variable), */
0x09, 0x32, /* Usage (In Range), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x02, /* Input (Variable), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x46, 0x40, 0x1F, /* Physical Maximum (8000), */
0x26, 0x00, 0x50, /* Logical Maximum (20480), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0x70, 0x17, /* Physical Maximum (6000), */
0x26, 0x00, 0x3C, /* Logical Maximum (15360), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x09, 0x30, /* Usage (Tip Pressure), */
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xC0, /* End Collection, */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x02, /* Usage (Mouse), */
0x95, 0x34, /* Report Count (52), */
0x81, 0x01, /* Input (Constant), */
0xC0 /* End Collection */
};
static const __u8 easypen_m406w_control_rdesc[] = {
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x11, /* Report ID (17), */
0x09, 0x01, /* Usage (Pointer), */
0xA0, /* Collection (Physical), */
0x85, 0x12, /* Report ID (18), */
0x0A, 0x6A, 0x02, /* Usage (AC Delete), */
0x0A, 0x1A, 0x02, /* Usage (AC Undo), */
0x0A, 0x01, 0x02, /* Usage (AC New), */
0x09, 0x40, /* Usage (Menu), */
0x14, /* Logical Minimum (0), */
0xA4, /* Push, */
0x05, 0x09, /* Usage Page (Button), */
0x75, 0x01, /* Report Size (1), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x03, /* Usage Maximum (03h), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x05, /* Report Count (5), */
0x81, 0x01, /* Input (Constant), */
0xB4, /* Pop, */
0x95, 0x01, /* Report Count (1), */
0xA4, /* Push, */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x34, /* Physical Minimum (0), */
0x75, 0x10, /* Report Size (16), */
0x09, 0x30, /* Usage (X), */
0x46, 0x40, 0x1F, /* Physical Maximum (8000), */
0x26, 0x00, 0x50, /* Logical Maximum (20480), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0x70, 0x17, /* Physical Maximum (6000), */
0x26, 0x00, 0x3C, /* Logical Maximum (15360), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x75, 0x08, /* Report Size (8), */
0x09, 0x38, /* Usage (Wheel), */
0x15, 0xFF, /* Logical Minimum (-1), */
0x25, 0x01, /* Logical Maximum (1), */
0x81, 0x06, /* Input (Variable, Relative), */
0x95, 0x34, /* Report Count (52), */
0x81, 0x01, /* Input (Constant), */
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
/* Original MousePen i608X v2 report descriptor size */
#define MOUSEPEN_I608X_V2_RDESC_ORIG_SIZE 482
/* Fixed MousePen i608X v2 report descriptor */
static __u8 mousepen_i608x_v2_rdesc_fixed[] = {
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x01, /* Usage (01h), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x05, /* Report ID (5), */
0x09, 0x01, /* Usage (01h), */
0x15, 0x80, /* Logical Minimum (-128), */
0x25, 0x7F, /* Logical Maximum (127), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x07, /* Report Count (7), */
0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x01, /* Usage (Digitizer), */
static const __u8 easypen_m610x_control_rdesc[] = {
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x10, /* Report ID (16), */
0x09, 0x20, /* Usage (Stylus), */
0xA0, /* Collection (Physical), */
0x85, 0x12, /* Report ID (18), */
0x0A, 0x1A, 0x02, /* Usage (AC Undo), */
0x0A, 0x79, 0x02, /* Usage (AC Redo Or Repeat), */
0x0A, 0x2D, 0x02, /* Usage (AC Zoom In), */
0x0A, 0x2E, 0x02, /* Usage (AC Zoom Out), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x09, 0x42, /* Usage (Tip Switch), */
0x09, 0x44, /* Usage (Barrel Switch), */
0x09, 0x46, /* Usage (Tablet Pick), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x03, /* Input (Constant, Variable), */
0x09, 0x32, /* Usage (In Range), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x02, /* Input (Variable), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x46, 0x40, 0x1F, /* Physical Maximum (8000), */
0x27, 0x00, 0xA0, 0x00, 0x00, /* Logical Maximum (40960), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0x70, 0x17, /* Physical Maximum (6000), */
0x26, 0x00, 0x78, /* Logical Maximum (30720), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x09, 0x30, /* Usage (Tip Pressure), */
0x26, 0xFF, 0x07, /* Logical Maximum (2047), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xC0, /* End Collection, */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x02, /* Usage (Mouse), */
0x95, 0x34, /* Report Count (52), */
0x81, 0x01, /* Input (Constant), */
0xC0 /* End Collection */
};
static const __u8 pensketch_m912_control_rdesc[] = {
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x11, /* Report ID (17), */
0x09, 0x01, /* Usage (Pointer), */
0xA0, /* Collection (Physical), */
0x85, 0x12, /* Report ID (18), */
0x14, /* Logical Minimum (0), */
0xA4, /* Push, */
0x05, 0x09, /* Usage Page (Button), */
0x75, 0x01, /* Report Size (1), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x03, /* Usage Maximum (03h), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x05, /* Report Count (5), */
0x81, 0x01, /* Input (Constant), */
0xB4, /* Pop, */
0x95, 0x01, /* Report Count (1), */
0xA4, /* Push, */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x34, /* Physical Minimum (0), */
0x75, 0x10, /* Report Size (16), */
0x09, 0x30, /* Usage (X), */
0x46, 0x40, 0x1F, /* Physical Maximum (8000), */
0x27, 0x00, 0xA0, 0x00, 0x00, /* Logical Maximum (40960), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0x70, 0x17, /* Physical Maximum (6000), */
0x26, 0x00, 0x78, /* Logical Maximum (30720), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x08, /* Report Count (8), */
0x05, 0x0C, /* Usage Page (Consumer), */
0x0A, 0x6A, 0x02, /* Usage (AC Delete), */
0x0A, 0x1A, 0x02, /* Usage (AC Undo), */
0x0A, 0x01, 0x02, /* Usage (AC New), */
0x0A, 0x2F, 0x02, /* Usage (AC Zoom), */
0x0A, 0x25, 0x02, /* Usage (AC Forward), */
0x0A, 0x24, 0x02, /* Usage (AC Back), */
0x0A, 0x2D, 0x02, /* Usage (AC Zoom In), */
0x0A, 0x2E, 0x02, /* Usage (AC Zoom Out), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x75, 0x08, /* Report Size (8), */
0x09, 0x38, /* Usage (Wheel), */
0x15, 0xFF, /* Logical Minimum (-1), */
0x25, 0x01, /* Logical Maximum (1), */
0x81, 0x06, /* Input (Variable, Relative), */
0x81, 0x01, /* Input (Constant), */
0xC0, /* End Collection, */
0x95, 0x30, /* Report Count (48), */
0x81, 0x03, /* Input (Constant, Variable), */
0xC0 /* End Collection */
};
/* Original EasyPen M610X report descriptor size */
#define EASYPEN_M610X_RDESC_ORIG_SIZE 476
/* Fixed EasyPen M610X report descriptor */
static __u8 easypen_m610x_rdesc_fixed[] = {
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x01, /* Usage (01h), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x05, /* Report ID (5), */
0x09, 0x01, /* Usage (01h), */
0x15, 0x80, /* Logical Minimum (-128), */
0x25, 0x7F, /* Logical Maximum (127), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x07, /* Report Count (7), */
0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x01, /* Usage (Digitizer), */
static const __u8 mousepen_m508wx_control_rdesc[] = {
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x10, /* Report ID (16), */
0x09, 0x20, /* Usage (Stylus), */
0xA0, /* Collection (Physical), */
0x85, 0x12, /* Report ID (18), */
0x0A, 0x1A, 0x02, /* Usage (AC Undo), */
0x0A, 0x6A, 0x02, /* Usage (AC Delete), */
0x0A, 0x2D, 0x02, /* Usage (AC Zoom In), */
0x0A, 0x2E, 0x02, /* Usage (AC Zoom Out), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x09, 0x42, /* Usage (Tip Switch), */
0x09, 0x44, /* Usage (Barrel Switch), */
0x09, 0x46, /* Usage (Tablet Pick), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x03, /* Input (Constant, Variable), */
0x09, 0x32, /* Usage (In Range), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x02, /* Input (Variable), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x46, 0x10, 0x27, /* Physical Maximum (10000), */
0x27, 0x00, 0xA0, 0x00, 0x00, /* Logical Maximum (40960), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0x6A, 0x18, /* Physical Maximum (6250), */
0x26, 0x00, 0x64, /* Logical Maximum (25600), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x09, 0x30, /* Usage (Tip Pressure), */
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
0x95, 0x34, /* Report Count (52), */
0x81, 0x01, /* Input (Constant), */
0xC0 /* End Collection */
};
static const __u8 mousepen_m508x_control_rdesc[] = {
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x12, /* Report ID (18), */
0x0A, 0x01, 0x02, /* Usage (AC New), */
0x09, 0x40, /* Usage (Menu), */
0x0A, 0x6A, 0x02, /* Usage (AC Delete), */
0x0A, 0x1A, 0x02, /* Usage (AC Undo), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xC0, /* End Collection, */
0x81, 0x01, /* Input (Constant), */
0x15, 0xFF, /* Logical Minimum (-1), */
0x95, 0x10, /* Report Count (16), */
0x81, 0x01, /* Input (Constant), */
0x0A, 0x35, 0x02, /* Usage (AC Scroll), */
0x0A, 0x2F, 0x02, /* Usage (AC Zoom), */
0x0A, 0x38, 0x02, /* Usage (AC Pan), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x06, /* Input (Variable, Relative), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x01, /* Input (Constant), */
0xC0 /* End Collection */
};
static const __u8 easypen_m406xe_control_rdesc[] = {
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
......@@ -336,30 +183,38 @@ static __u8 easypen_m610x_rdesc_fixed[] = {
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x04, /* Report Count (4), */
0x0A, 0x1A, 0x02, /* Usage (AC Undo), */
0x0A, 0x79, 0x02, /* Usage (AC Redo Or Repeat), */
0x0A, 0x1A, 0x02, /* Usage (AC Undo), */
0x0A, 0x2D, 0x02, /* Usage (AC Zoom In), */
0x0A, 0x2E, 0x02, /* Usage (AC Zoom Out), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x01, /* Report Count (1), */
0x75, 0x14, /* Report Size (20), */
0x81, 0x03, /* Input (Constant, Variable), */
0x75, 0x20, /* Report Size (32), */
0x95, 0x34, /* Report Count (52), */
0x81, 0x03, /* Input (Constant, Variable), */
0xC0 /* End Collection */
};
static const __u8 pensketch_t609a_control_rdesc[] = {
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x12, /* Report ID (18), */
0x0A, 0x6A, 0x02, /* Usage (AC Delete), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x08, /* Report Count (8), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x37, /* Report Count (55), */
0x81, 0x01, /* Input (Constant), */
0xC0 /* End Collection */
};
/* Original PenSketch M912 report descriptor size */
#define PENSKETCH_M912_RDESC_ORIG_SIZE 482
/* Fixed PenSketch M912 report descriptor */
static __u8 pensketch_m912_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x08, /* Usage (00h), */
/* Fix indexes in kye_tablet_fixup if you change this */
static const __u8 kye_tablet_rdesc[] = {
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x01, /* Usage (01h), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x05, /* Report ID (5), */
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x01, /* Usage (01h), */
0x15, 0x81, /* Logical Minimum (-127), */
0x25, 0x7F, /* Logical Maximum (127), */
......@@ -382,30 +237,29 @@ static __u8 pensketch_m912_rdesc_fixed[] = {
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x03, /* Input (Constant, Variable), */
0x81, 0x01, /* Input (Constant), */
0x09, 0x32, /* Usage (In Range), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x02, /* Input (Variable), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x14, /* Logical Minimum (0), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x27, 0x00, 0xF0, 0x00, 0x00, /* Logical Maximum (61440), */
0x46, 0xE0, 0x2E, /* Physical Maximum (12000), */
0x27, 0xFF, 0x7F, 0x00, 0x00, /* Logical Maximum (32767), */
0x34, /* Physical Minimum (0), */
0x47, 0x00, 0x00, 0x00, 0x00, /* Physical Maximum (0), */
0x65, 0x11, /* Unit (Centimeter), */
0x55, 0x00, /* Unit Exponent (0), */
0x75, 0x10, /* Report Size (16), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x27, 0x00, 0xB4, 0x00, 0x00, /* Logical Maximum (46080), */
0x46, 0x28, 0x23, /* Physical Maximum (9000), */
0x27, 0xFF, 0x7F, 0x00, 0x00, /* Logical Maximum (32767), */
0x47, 0x00, 0x00, 0x00, 0x00, /* Physical Maximum (0), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x30, /* Usage (Tip Pressure), */
0x14, /* Logical Minimum (0), */
0x26, 0xFF, 0x07, /* Logical Maximum (2047), */
0x27, 0xFF, 0x07, 0x00, 0x00, /* Logical Maximum (2047), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xC0, /* End Collection, */
......@@ -416,146 +270,98 @@ static __u8 pensketch_m912_rdesc_fixed[] = {
0x09, 0x21, /* Usage (Puck), */
0xA0, /* Collection (Physical), */
0x05, 0x09, /* Usage Page (Button), */
0x75, 0x01, /* Report Size (1), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x03, /* Usage Maximum (03h), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x01, /* Input (Constant), */
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x32, /* Usage (In Range), */
0x95, 0x01, /* Report Count (1), */
0x0B, 0x32, 0x00, 0x0D, 0x00, /* Usage (Digitizer In Range), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x81, 0x02, /* Input (Variable), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x14, /* Logical Minimum (0), */
0x34, /* Physical Minimum (0), */
0xA4, /* Push, */
0x09, 0x30, /* Usage (X), */
0x27, 0x00, 0xF0, 0x00, 0x00, /* Logical Maximum (61440), */
0x46, 0xE0, 0x2E, /* Physical Maximum (12000), */
0x27, 0xFF, 0x7F, 0x00, 0x00, /* Logical Maximum (32767), */
0x34, /* Physical Minimum (0), */
0x47, 0x00, 0x00, 0x00, 0x00, /* Physical Maximum (0), */
0x65, 0x11, /* Unit (Centimeter), */
0x55, 0x00, /* Unit Exponent (0), */
0x75, 0x10, /* Report Size (16), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x27, 0x00, 0xB4, 0x00, 0x00, /* Logical Maximum (46080), */
0x46, 0x28, 0x23, /* Physical Maximum (9000), */
0x27, 0xFF, 0x7F, 0x00, 0x00, /* Logical Maximum (32767), */
0x47, 0x00, 0x00, 0x00, 0x00, /* Physical Maximum (0), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x09, 0x38, /* Usage (Wheel), */
0x15, 0xFF, /* Logical Minimum (-1), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x01, /* Report Count (1), */
0x15, 0xFF, /* Logical Minimum (-1), */
0x25, 0x01, /* Logical Maximum (1), */
0x34, /* Physical Minimum (0), */
0x44, /* Physical Maximum (0), */
0x81, 0x06, /* Input (Variable, Relative), */
0xB4, /* Pop, */
0xC0, /* End Collection, */
0x81, 0x01, /* Input (Constant), */
0xC0, /* End Collection, */
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x12, /* Report ID (18), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x08, /* Report Count (8), */
0x05, 0x0C, /* Usage Page (Consumer), */
0x0A, 0x6A, 0x02, /* Usage (AC Delete), */
0x0A, 0x1A, 0x02, /* Usage (AC Undo), */
0x0A, 0x01, 0x02, /* Usage (AC New), */
0x0A, 0x2F, 0x02, /* Usage (AC Zoom), */
0x0A, 0x25, 0x02, /* Usage (AC Forward), */
0x0A, 0x24, 0x02, /* Usage (AC Back), */
0x0A, 0x2D, 0x02, /* Usage (AC Zoom In), */
0x0A, 0x2E, 0x02, /* Usage (AC Zoom Out), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x30, /* Report Count (48), */
0x81, 0x03, /* Input (Constant, Variable), */
0xC0 /* End Collection */
};
/* Original EasyPen M406XE report descriptor size */
#define EASYPEN_M406XE_RDESC_ORIG_SIZE 476
/* Fixed EasyPen M406XE report descriptor */
static __u8 easypen_m406xe_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x01, /* Usage (01h), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x05, /* Report ID (5), */
0x09, 0x01, /* Usage (01h), */
0x15, 0x80, /* Logical Minimum (-128), */
0x25, 0x7F, /* Logical Maximum (127), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x07, /* Report Count (7), */
0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x01, /* Usage (Digitizer), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x10, /* Report ID (16), */
0x09, 0x20, /* Usage (Stylus), */
0xA0, /* Collection (Physical), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x09, 0x42, /* Usage (Tip Switch), */
0x09, 0x44, /* Usage (Barrel Switch), */
0x09, 0x46, /* Usage (Tablet Pick), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x03, /* Input (Constant, Variable), */
0x09, 0x32, /* Usage (In Range), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x02, /* Input (Variable), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x46, 0x70, 0x17, /* Physical Maximum (6000), */
0x26, 0x00, 0x3C, /* Logical Maximum (15360), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0xA0, 0x0F, /* Physical Maximum (4000), */
0x26, 0x00, 0x28, /* Logical Maximum (10240), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x09, 0x30, /* Usage (Tip Pressure), */
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xC0, /* End Collection */
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x12, /* Report ID (18), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x04, /* Report Count (4), */
0x0A, 0x79, 0x02, /* Usage (AC Redo Or Repeat), */
0x0A, 0x1A, 0x02, /* Usage (AC Undo), */
0x0A, 0x2D, 0x02, /* Usage (AC Zoom In), */
0x0A, 0x2E, 0x02, /* Usage (AC Zoom Out), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x34, /* Report Count (52), */
0x81, 0x03, /* Input (Constant, Variable), */
0xC0 /* End Collection */
static const struct kye_tablet_info {
__u32 product;
__s32 x_logical_maximum;
__s32 y_logical_maximum;
__s32 pressure_logical_maximum;
__s32 x_physical_maximum;
__s32 y_physical_maximum;
__s8 unit_exponent;
__s8 unit;
bool has_punk;
unsigned int control_rsize;
const __u8 *control_rdesc;
} kye_tablets_info[] = {
{USB_DEVICE_ID_KYE_EASYPEN_M406, /* 0x5005 */
15360, 10240, 1023, 6, 4, 0, 0x13, false,
sizeof(easypen_m406_control_rdesc), easypen_m406_control_rdesc},
{USB_DEVICE_ID_KYE_EASYPEN_M506, /* 0x500F */
24576, 20480, 1023, 6, 5, 0, 0x13, false,
sizeof(easypen_m506_control_rdesc), easypen_m506_control_rdesc},
{USB_DEVICE_ID_KYE_EASYPEN_I405X, /* 0x5010 */
14080, 10240, 1023, 55, 40, -1, 0x13, false},
{USB_DEVICE_ID_KYE_MOUSEPEN_I608X, /* 0x5011 */
20480, 15360, 2047, 8, 6, 0, 0x13, true},
{USB_DEVICE_ID_KYE_EASYPEN_M406W, /* 0x5012 */
15360, 10240, 1023, 6, 4, 0, 0x13, false,
sizeof(easypen_m406w_control_rdesc), easypen_m406w_control_rdesc},
{USB_DEVICE_ID_KYE_EASYPEN_M610X, /* 0x5013 */
40960, 25600, 1023, 1000, 625, -2, 0x13, false,
sizeof(easypen_m610x_control_rdesc), easypen_m610x_control_rdesc},
{USB_DEVICE_ID_KYE_EASYPEN_340, /* 0x5014 */
10240, 7680, 1023, 4, 3, 0, 0x13, false},
{USB_DEVICE_ID_KYE_PENSKETCH_M912, /* 0x5015 */
61440, 46080, 2047, 12, 9, 0, 0x13, true,
sizeof(pensketch_m912_control_rdesc), pensketch_m912_control_rdesc},
{USB_DEVICE_ID_KYE_MOUSEPEN_M508WX, /* 0x5016 */
40960, 25600, 2047, 8, 5, 0, 0x13, true,
sizeof(mousepen_m508wx_control_rdesc), mousepen_m508wx_control_rdesc},
{USB_DEVICE_ID_KYE_MOUSEPEN_M508X, /* 0x5017 */
40960, 25600, 2047, 8, 5, 0, 0x13, true,
sizeof(mousepen_m508x_control_rdesc), mousepen_m508x_control_rdesc},
{USB_DEVICE_ID_KYE_EASYPEN_M406XE, /* 0x5019 */
15360, 10240, 1023, 6, 4, 0, 0x13, false,
sizeof(easypen_m406xe_control_rdesc), easypen_m406xe_control_rdesc},
{USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2, /* 0x501A */
40960, 30720, 2047, 8, 6, 0, 0x13, true},
{USB_DEVICE_ID_KYE_PENSKETCH_T609A, /* 0x501B */
43520, 28160, 1023, 85, 55, -1, 0x13, false,
sizeof(pensketch_t609a_control_rdesc), pensketch_t609a_control_rdesc},
{}
};
static __u8 *kye_consumer_control_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize, int offset, const char *device_name) {
unsigned int *rsize, int offset, const char *device_name)
{
/*
* the fixup that need to be done:
* - change Usage Maximum in the Consumer Control
......@@ -574,6 +380,79 @@ static __u8 *kye_consumer_control_fixup(struct hid_device *hdev, __u8 *rdesc,
return rdesc;
}
/*
* Fix tablet descriptor of so-called "DataFormat 2".
*
* Though we may achieve a usable descriptor from original vendor-defined one,
* some problems exist:
* - Their Logical Maximum never exceed 32767 (7F FF), though device do report
* values greater than that;
* - Physical Maximums are arbitrarily filled (always equal to Logical
* Maximum);
* - Detail for control buttons are not provided (a vendor-defined Usage Page
* with fixed content).
*
* Thus we use a pre-defined parameter table rather than digging it from
* original descriptor.
*
* We may as well write a fallback routine for unrecognized kye tablet, but it's
* clear kye are unlikely to produce new models in the foreseeable future, so we
* simply enumerate all possible models.
*/
static __u8 *kye_tablet_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize)
{
const struct kye_tablet_info *info;
unsigned int newsize;
if (*rsize < sizeof(kye_tablet_rdesc)) {
hid_warn(hdev,
"tablet report size too small, or kye_tablet_rdesc unexpectedly large\n");
return rdesc;
}
for (info = kye_tablets_info; info->product; info++) {
if (hdev->product == info->product)
break;
}
if (!info->product) {
hid_err(hdev, "tablet unknown, someone forget to add kye_tablet_info entry?\n");
return rdesc;
}
newsize = info->has_punk ? sizeof(kye_tablet_rdesc) : 112;
memcpy(rdesc, kye_tablet_rdesc, newsize);
put_unaligned_le32(info->x_logical_maximum, rdesc + 66);
put_unaligned_le32(info->x_physical_maximum, rdesc + 72);
rdesc[77] = info->unit;
rdesc[79] = info->unit_exponent;
put_unaligned_le32(info->y_logical_maximum, rdesc + 87);
put_unaligned_le32(info->y_physical_maximum, rdesc + 92);
put_unaligned_le32(info->pressure_logical_maximum, rdesc + 104);
if (info->has_punk) {
put_unaligned_le32(info->x_logical_maximum, rdesc + 156);
put_unaligned_le32(info->x_physical_maximum, rdesc + 162);
rdesc[167] = info->unit;
rdesc[169] = info->unit_exponent;
put_unaligned_le32(info->y_logical_maximum, rdesc + 177);
put_unaligned_le32(info->y_physical_maximum, rdesc + 182);
}
if (info->control_rsize) {
if (newsize + info->control_rsize > *rsize)
hid_err(hdev, "control rdesc unexpectedly large");
else {
memcpy(rdesc + newsize, info->control_rdesc, info->control_rsize);
newsize += info->control_rsize;
}
}
*rsize = newsize;
return rdesc;
}
static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
......@@ -602,66 +481,37 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
rdesc[74] = 0x08;
}
break;
case USB_DEVICE_ID_KYE_EASYPEN_I405X:
if (*rsize == EASYPEN_I405X_RDESC_ORIG_SIZE) {
rdesc = easypen_i405x_rdesc_fixed;
*rsize = sizeof(easypen_i405x_rdesc_fixed);
}
break;
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X:
if (*rsize == MOUSEPEN_I608X_RDESC_ORIG_SIZE) {
rdesc = mousepen_i608x_rdesc_fixed;
*rsize = sizeof(mousepen_i608x_rdesc_fixed);
}
break;
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2:
if (*rsize == MOUSEPEN_I608X_V2_RDESC_ORIG_SIZE) {
rdesc = mousepen_i608x_v2_rdesc_fixed;
*rsize = sizeof(mousepen_i608x_v2_rdesc_fixed);
}
break;
case USB_DEVICE_ID_KYE_EASYPEN_M610X:
if (*rsize == EASYPEN_M610X_RDESC_ORIG_SIZE) {
rdesc = easypen_m610x_rdesc_fixed;
*rsize = sizeof(easypen_m610x_rdesc_fixed);
}
break;
case USB_DEVICE_ID_KYE_EASYPEN_M406XE:
if (*rsize == EASYPEN_M406XE_RDESC_ORIG_SIZE) {
rdesc = easypen_m406xe_rdesc_fixed;
*rsize = sizeof(easypen_m406xe_rdesc_fixed);
}
break;
case USB_DEVICE_ID_KYE_PENSKETCH_M912:
if (*rsize == PENSKETCH_M912_RDESC_ORIG_SIZE) {
rdesc = pensketch_m912_rdesc_fixed;
*rsize = sizeof(pensketch_m912_rdesc_fixed);
}
break;
case USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE:
rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 104,
"Genius Gila Gaming Mouse");
break;
case USB_DEVICE_ID_GENIUS_MANTICORE:
rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 104,
"Genius Manticore Keyboard");
break;
case USB_DEVICE_ID_GENIUS_GX_IMPERATOR:
rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 83,
"Genius Gx Imperator Keyboard");
break;
case USB_DEVICE_ID_GENIUS_MANTICORE:
rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 104,
"Genius Manticore Keyboard");
case USB_DEVICE_ID_KYE_EASYPEN_M406:
case USB_DEVICE_ID_KYE_EASYPEN_M506:
case USB_DEVICE_ID_KYE_EASYPEN_I405X:
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X:
case USB_DEVICE_ID_KYE_EASYPEN_M406W:
case USB_DEVICE_ID_KYE_EASYPEN_M610X:
case USB_DEVICE_ID_KYE_EASYPEN_340:
case USB_DEVICE_ID_KYE_PENSKETCH_M912:
case USB_DEVICE_ID_KYE_MOUSEPEN_M508WX:
case USB_DEVICE_ID_KYE_MOUSEPEN_M508X:
case USB_DEVICE_ID_KYE_EASYPEN_M406XE:
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2:
case USB_DEVICE_ID_KYE_PENSKETCH_T609A:
rdesc = kye_tablet_fixup(hdev, rdesc, rsize);
break;
}
return rdesc;
}
/**
* kye_tablet_enable() - Enable fully-functional tablet mode by setting a special feature report.
*
* @hdev: HID device
*
* The specific report ID and data were discovered by sniffing the
* Windows driver traffic.
*/
static int kye_tablet_enable(struct hid_device *hdev)
{
struct list_head *list;
......@@ -688,6 +538,15 @@ static int kye_tablet_enable(struct hid_device *hdev)
value = report->field[0]->value;
/*
* The code is for DataFormat 2 of config xml. They have no obvious
* meaning (at least not configurable in Windows driver) except enabling
* fully-functional tablet mode (absolute positioning). Otherwise, the
* tablet acts like a relative mouse.
*
* Though there're magic codes for DataFormat 3 and 4, no devices use
* these DataFormats.
*/
value[0] = 0x12;
value[1] = 0x10;
value[2] = 0x11;
......@@ -717,26 +576,33 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
switch (id->product) {
case USB_DEVICE_ID_GENIUS_MANTICORE:
/*
* The manticore keyboard needs to have all the interfaces
* opened at least once to be fully functional.
*/
if (hid_hw_open(hdev))
hid_hw_close(hdev);
break;
case USB_DEVICE_ID_KYE_EASYPEN_M406:
case USB_DEVICE_ID_KYE_EASYPEN_M506:
case USB_DEVICE_ID_KYE_EASYPEN_I405X:
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X:
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2:
case USB_DEVICE_ID_KYE_EASYPEN_M406W:
case USB_DEVICE_ID_KYE_EASYPEN_M610X:
case USB_DEVICE_ID_KYE_EASYPEN_M406XE:
case USB_DEVICE_ID_KYE_EASYPEN_340:
case USB_DEVICE_ID_KYE_PENSKETCH_M912:
case USB_DEVICE_ID_KYE_MOUSEPEN_M508WX:
case USB_DEVICE_ID_KYE_MOUSEPEN_M508X:
case USB_DEVICE_ID_KYE_EASYPEN_M406XE:
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2:
case USB_DEVICE_ID_KYE_PENSKETCH_T609A:
ret = kye_tablet_enable(hdev);
if (ret) {
hid_err(hdev, "tablet enabling failed\n");
goto enabling_err;
}
break;
case USB_DEVICE_ID_GENIUS_MANTICORE:
/*
* The manticore keyboard needs to have all the interfaces
* opened at least once to be fully functional.
*/
if (hid_hw_open(hdev))
hid_hw_close(hdev);
break;
}
return 0;
......@@ -748,24 +614,38 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id)
static const struct hid_device_id kye_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_GENIUS_MANTICORE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_GENIUS_GX_IMPERATOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_EASYPEN_M406) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_EASYPEN_M506) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_EASYPEN_I405X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_MOUSEPEN_I608X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) },
USB_DEVICE_ID_KYE_EASYPEN_M406W) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_EASYPEN_M610X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_EASYPEN_M406XE) },
USB_DEVICE_ID_KYE_EASYPEN_340) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) },
USB_DEVICE_ID_KYE_PENSKETCH_M912) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_GENIUS_GX_IMPERATOR) },
USB_DEVICE_ID_KYE_MOUSEPEN_M508WX) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_GENIUS_MANTICORE) },
USB_DEVICE_ID_KYE_MOUSEPEN_M508X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_PENSKETCH_M912) },
USB_DEVICE_ID_KYE_EASYPEN_M406XE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_PENSKETCH_T609A) },
{ }
};
MODULE_DEVICE_TABLE(hid, kye_devices);
......
......@@ -7,6 +7,7 @@
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/sched.h>
......
......@@ -74,6 +74,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(27)
#define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(28)
#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(29)
#define HIDPP_QUIRK_WIRELESS_STATUS BIT(30)
/* These are just aliases for now */
#define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
......@@ -94,6 +95,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL BIT(7)
#define HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL BIT(8)
#define HIDPP_CAPABILITY_HIDPP10_FAST_SCROLL BIT(9)
#define HIDPP_CAPABILITY_ADC_MEASUREMENT BIT(10)
#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
......@@ -145,6 +147,7 @@ struct hidpp_battery {
u8 feature_index;
u8 solar_feature_index;
u8 voltage_feature_index;
u8 adc_measurement_feature_index;
struct power_supply_desc desc;
struct power_supply *ps;
char name[64];
......@@ -471,6 +474,26 @@ static void hidpp_prefix_name(char **name, int name_length)
*name = new_name;
}
/*
* Updates the USB wireless_status based on whether the headset
* is turned on and reachable.
*/
static void hidpp_update_usb_wireless_status(struct hidpp_device *hidpp)
{
struct hid_device *hdev = hidpp->hid_dev;
struct usb_interface *intf;
if (!(hidpp->quirks & HIDPP_QUIRK_WIRELESS_STATUS))
return;
if (!hid_is_usb(hdev))
return;
intf = to_usb_interface(hdev->dev.parent);
usb_set_wireless_status(intf, hidpp->battery.online ?
USB_WIRELESS_STATUS_CONNECTED :
USB_WIRELESS_STATUS_DISCONNECTED);
}
/**
* hidpp_scroll_counter_handle_scroll() - Send high- and low-resolution scroll
* events given a high-resolution wheel
......@@ -853,8 +876,7 @@ static int hidpp_unifying_init(struct hidpp_device *hidpp)
if (ret)
return ret;
snprintf(hdev->uniq, sizeof(hdev->uniq), "%04x-%4phD",
hdev->product, &serial);
snprintf(hdev->uniq, sizeof(hdev->uniq), "%4phD", &serial);
dbg_hid("HID++ Unifying: Got serial: %s\n", hdev->uniq);
name = hidpp_unifying_get_name(hidpp);
......@@ -947,6 +969,54 @@ static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp)
return 0;
}
/* -------------------------------------------------------------------------- */
/* 0x0003: Device Information */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_DEVICE_INFORMATION 0x0003
#define CMD_GET_DEVICE_INFO 0x00
static int hidpp_get_serial(struct hidpp_device *hidpp, u32 *serial)
{
struct hidpp_report response;
u8 feature_type;
u8 feature_index;
int ret;
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_DEVICE_INFORMATION,
&feature_index,
&feature_type);
if (ret)
return ret;
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
CMD_GET_DEVICE_INFO,
NULL, 0, &response);
if (ret)
return ret;
/* See hidpp_unifying_get_serial() */
*serial = *((u32 *)&response.rap.params[1]);
return 0;
}
static int hidpp_serial_init(struct hidpp_device *hidpp)
{
struct hid_device *hdev = hidpp->hid_dev;
u32 serial;
int ret;
ret = hidpp_get_serial(hidpp, &serial);
if (ret)
return ret;
snprintf(hdev->uniq, sizeof(hdev->uniq), "%4phD", &serial);
dbg_hid("HID++ DeviceInformation: Got serial: %s\n", hdev->uniq);
return 0;
}
/* -------------------------------------------------------------------------- */
/* 0x0005: GetDeviceNameType */
/* -------------------------------------------------------------------------- */
......@@ -1357,7 +1427,7 @@ static int hidpp20_map_battery_capacity(struct hid_device *hid_dev, int voltage)
* there are a few devices that use different battery technology.
*/
static const int voltages[] = {
static const int voltages[100] = {
4186, 4156, 4143, 4133, 4122, 4113, 4103, 4094, 4086, 4075,
4067, 4059, 4051, 4043, 4035, 4027, 4019, 4011, 4003, 3997,
3989, 3983, 3976, 3969, 3961, 3955, 3949, 3942, 3935, 3929,
......@@ -1372,8 +1442,6 @@ static int hidpp20_map_battery_capacity(struct hid_device *hid_dev, int voltage)
int i;
BUILD_BUG_ON(ARRAY_SIZE(voltages) != 100);
if (unlikely(voltage < 3500 || voltage >= 5000))
hid_warn_once(hid_dev,
"%s: possibly using the wrong voltage curve\n",
......@@ -1745,6 +1813,164 @@ static int hidpp_set_wireless_feature_index(struct hidpp_device *hidpp)
return ret;
}
/* -------------------------------------------------------------------------- */
/* 0x1f20: ADC measurement */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_ADC_MEASUREMENT 0x1f20
#define CMD_ADC_MEASUREMENT_GET_ADC_MEASUREMENT 0x00
#define EVENT_ADC_MEASUREMENT_STATUS_BROADCAST 0x00
static int hidpp20_map_adc_measurement_1f20_capacity(struct hid_device *hid_dev, int voltage)
{
/* NB: This voltage curve doesn't necessarily map perfectly to all
* devices that implement the ADC_MEASUREMENT feature. This is because
* there are a few devices that use different battery technology.
*
* Adapted from:
* https://github.com/Sapd/HeadsetControl/blob/acd972be0468e039b93aae81221f20a54d2d60f7/src/devices/logitech_g633_g933_935.c#L44-L52
*/
static const int voltages[100] = {
4030, 4024, 4018, 4011, 4003, 3994, 3985, 3975, 3963, 3951,
3937, 3922, 3907, 3893, 3880, 3868, 3857, 3846, 3837, 3828,
3820, 3812, 3805, 3798, 3791, 3785, 3779, 3773, 3768, 3762,
3757, 3752, 3747, 3742, 3738, 3733, 3729, 3724, 3720, 3716,
3712, 3708, 3704, 3700, 3696, 3692, 3688, 3685, 3681, 3677,
3674, 3670, 3667, 3663, 3660, 3657, 3653, 3650, 3646, 3643,
3640, 3637, 3633, 3630, 3627, 3624, 3620, 3617, 3614, 3611,
3608, 3604, 3601, 3598, 3595, 3592, 3589, 3585, 3582, 3579,
3576, 3573, 3569, 3566, 3563, 3560, 3556, 3553, 3550, 3546,
3543, 3539, 3536, 3532, 3529, 3525, 3499, 3466, 3433, 3399,
};
int i;
if (voltage == 0)
return 0;
if (unlikely(voltage < 3400 || voltage >= 5000))
hid_warn_once(hid_dev,
"%s: possibly using the wrong voltage curve\n",
__func__);
for (i = 0; i < ARRAY_SIZE(voltages); i++) {
if (voltage >= voltages[i])
return ARRAY_SIZE(voltages) - i;
}
return 0;
}
static int hidpp20_map_adc_measurement_1f20(u8 data[3], int *voltage)
{
int status;
u8 flags;
flags = data[2];
switch (flags) {
case 0x01:
status = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case 0x03:
status = POWER_SUPPLY_STATUS_CHARGING;
break;
case 0x07:
status = POWER_SUPPLY_STATUS_FULL;
break;
case 0x0F:
default:
status = POWER_SUPPLY_STATUS_UNKNOWN;
break;
}
*voltage = get_unaligned_be16(data);
dbg_hid("Parsed 1f20 data as flag 0x%02x voltage %dmV\n",
flags, *voltage);
return status;
}
/* Return value is whether the device is online */
static bool hidpp20_get_adc_measurement_1f20(struct hidpp_device *hidpp,
u8 feature_index,
int *status, int *voltage)
{
struct hidpp_report response;
int ret;
u8 *params = (u8 *)response.fap.params;
*status = POWER_SUPPLY_STATUS_UNKNOWN;
*voltage = 0;
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
CMD_ADC_MEASUREMENT_GET_ADC_MEASUREMENT,
NULL, 0, &response);
if (ret > 0) {
hid_dbg(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
__func__, ret);
return false;
}
*status = hidpp20_map_adc_measurement_1f20(params, voltage);
return true;
}
static int hidpp20_query_adc_measurement_info_1f20(struct hidpp_device *hidpp)
{
u8 feature_type;
if (hidpp->battery.adc_measurement_feature_index == 0xff) {
int ret;
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_ADC_MEASUREMENT,
&hidpp->battery.adc_measurement_feature_index,
&feature_type);
if (ret)
return ret;
hidpp->capabilities |= HIDPP_CAPABILITY_ADC_MEASUREMENT;
}
hidpp->battery.online = hidpp20_get_adc_measurement_1f20(hidpp,
hidpp->battery.adc_measurement_feature_index,
&hidpp->battery.status,
&hidpp->battery.voltage);
hidpp->battery.capacity = hidpp20_map_adc_measurement_1f20_capacity(hidpp->hid_dev,
hidpp->battery.voltage);
hidpp_update_usb_wireless_status(hidpp);
return 0;
}
static int hidpp20_adc_measurement_event_1f20(struct hidpp_device *hidpp,
u8 *data, int size)
{
struct hidpp_report *report = (struct hidpp_report *)data;
int status, voltage;
if (report->fap.feature_index != hidpp->battery.adc_measurement_feature_index ||
report->fap.funcindex_clientid != EVENT_ADC_MEASUREMENT_STATUS_BROADCAST)
return 0;
status = hidpp20_map_adc_measurement_1f20(report->fap.params, &voltage);
hidpp->battery.online = status != POWER_SUPPLY_STATUS_UNKNOWN;
if (voltage != hidpp->battery.voltage || status != hidpp->battery.status) {
hidpp->battery.status = status;
hidpp->battery.voltage = voltage;
hidpp->battery.capacity = hidpp20_map_adc_measurement_1f20_capacity(hidpp->hid_dev, voltage);
if (hidpp->battery.ps)
power_supply_changed(hidpp->battery.ps);
hidpp_update_usb_wireless_status(hidpp);
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* 0x2120: Hi-resolution scrolling */
/* -------------------------------------------------------------------------- */
......@@ -3663,6 +3889,9 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
ret = hidpp20_battery_voltage_event(hidpp, data, size);
if (ret != 0)
return ret;
ret = hidpp20_adc_measurement_event_1f20(hidpp, data, size);
if (ret != 0)
return ret;
}
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
......@@ -3786,6 +4015,7 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
hidpp->battery.feature_index = 0xff;
hidpp->battery.solar_feature_index = 0xff;
hidpp->battery.voltage_feature_index = 0xff;
hidpp->battery.adc_measurement_feature_index = 0xff;
if (hidpp->protocol_major >= 2) {
if (hidpp->quirks & HIDPP_QUIRK_CLASS_K750)
......@@ -3799,6 +4029,8 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
ret = hidpp20_query_battery_info_1004(hidpp);
if (ret)
ret = hidpp20_query_battery_voltage_info(hidpp);
if (ret)
ret = hidpp20_query_adc_measurement_info_1f20(hidpp);
}
if (ret)
......@@ -3828,7 +4060,8 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE ||
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_PERCENTAGE ||
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE ||
hidpp->capabilities & HIDPP_CAPABILITY_ADC_MEASUREMENT)
battery_props[num_battery_props++] =
POWER_SUPPLY_PROP_CAPACITY;
......@@ -3836,7 +4069,8 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
battery_props[num_battery_props++] =
POWER_SUPPLY_PROP_CAPACITY_LEVEL;
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE ||
hidpp->capabilities & HIDPP_CAPABILITY_ADC_MEASUREMENT)
battery_props[num_battery_props++] =
POWER_SUPPLY_PROP_VOLTAGE_NOW;
......@@ -4009,6 +4243,8 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
hidpp20_query_battery_voltage_info(hidpp);
else if (hidpp->capabilities & HIDPP_CAPABILITY_UNIFIED_BATTERY)
hidpp20_query_battery_info_1004(hidpp);
else if (hidpp->capabilities & HIDPP_CAPABILITY_ADC_MEASUREMENT)
hidpp20_query_adc_measurement_info_1f20(hidpp);
else
hidpp20_query_battery_info_1000(hidpp);
}
......@@ -4210,6 +4446,8 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (hidpp->quirks & HIDPP_QUIRK_UNIFYING)
hidpp_unifying_init(hidpp);
else if (hid_is_usb(hidpp->hid_dev))
hidpp_serial_init(hidpp);
connected = hidpp_root_get_protocol_version(hidpp) == 0;
atomic_set(&hidpp->connected, connected);
......@@ -4379,6 +4617,10 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* Logitech G Pro Gaming Mouse over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) },
{ /* G935 Gaming Headset */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0x0a87),
.driver_data = HIDPP_QUIRK_WIRELESS_STATUS },
{ /* MX5000 keyboard over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb305),
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
......
......@@ -79,8 +79,8 @@ struct mcp_get_gpio {
u8 cmd;
u8 dummy;
struct {
u8 direction;
u8 value;
u8 direction;
} gpio[MCP_NGPIO];
} __packed;
......@@ -594,7 +594,7 @@ static int mcp_gpio_get(struct gpio_chip *gc,
mcp->txbuf[0] = MCP2221_GPIO_GET;
mcp->gp_idx = offsetof(struct mcp_get_gpio, gpio[offset].value);
mcp->gp_idx = offsetof(struct mcp_get_gpio, gpio[offset]);
mutex_lock(&mcp->lock);
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
......@@ -675,7 +675,7 @@ static int mcp_gpio_get_direction(struct gpio_chip *gc,
mcp->txbuf[0] = MCP2221_GPIO_GET;
mcp->gp_idx = offsetof(struct mcp_get_gpio, gpio[offset].direction);
mcp->gp_idx = offsetof(struct mcp_get_gpio, gpio[offset]);
mutex_lock(&mcp->lock);
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
......
......@@ -433,7 +433,9 @@ struct joycon_ctlr {
u8 usb_ack_match;
u8 subcmd_ack_match;
bool received_input_report;
unsigned int last_input_report_msecs;
unsigned int last_subcmd_sent_msecs;
unsigned int consecutive_valid_report_deltas;
/* factory calibration data */
struct joycon_stick_cal left_stick_cal_x;
......@@ -543,19 +545,54 @@ static void joycon_wait_for_input_report(struct joycon_ctlr *ctlr)
* Sending subcommands and/or rumble data at too high a rate can cause bluetooth
* controller disconnections.
*/
#define JC_INPUT_REPORT_MIN_DELTA 8
#define JC_INPUT_REPORT_MAX_DELTA 17
#define JC_SUBCMD_TX_OFFSET_MS 4
#define JC_SUBCMD_VALID_DELTA_REQ 3
#define JC_SUBCMD_RATE_MAX_ATTEMPTS 500
#define JC_SUBCMD_RATE_LIMITER_USB_MS 20
#define JC_SUBCMD_RATE_LIMITER_BT_MS 60
#define JC_SUBCMD_RATE_LIMITER_MS(ctlr) ((ctlr)->hdev->bus == BUS_USB ? JC_SUBCMD_RATE_LIMITER_USB_MS : JC_SUBCMD_RATE_LIMITER_BT_MS)
static void joycon_enforce_subcmd_rate(struct joycon_ctlr *ctlr)
{
static const unsigned int max_subcmd_rate_ms = 25;
unsigned int current_ms = jiffies_to_msecs(jiffies);
unsigned int delta_ms = current_ms - ctlr->last_subcmd_sent_msecs;
unsigned int current_ms;
unsigned long subcmd_delta;
int consecutive_valid_deltas = 0;
int attempts = 0;
unsigned long flags;
if (unlikely(ctlr->ctlr_state != JOYCON_CTLR_STATE_READ))
return;
while (delta_ms < max_subcmd_rate_ms &&
ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) {
do {
joycon_wait_for_input_report(ctlr);
current_ms = jiffies_to_msecs(jiffies);
delta_ms = current_ms - ctlr->last_subcmd_sent_msecs;
subcmd_delta = current_ms - ctlr->last_subcmd_sent_msecs;
spin_lock_irqsave(&ctlr->lock, flags);
consecutive_valid_deltas = ctlr->consecutive_valid_report_deltas;
spin_unlock_irqrestore(&ctlr->lock, flags);
attempts++;
} while ((consecutive_valid_deltas < JC_SUBCMD_VALID_DELTA_REQ ||
subcmd_delta < JC_SUBCMD_RATE_LIMITER_MS(ctlr)) &&
ctlr->ctlr_state == JOYCON_CTLR_STATE_READ &&
attempts < JC_SUBCMD_RATE_MAX_ATTEMPTS);
if (attempts >= JC_SUBCMD_RATE_MAX_ATTEMPTS) {
hid_warn(ctlr->hdev, "%s: exceeded max attempts", __func__);
return;
}
ctlr->last_subcmd_sent_msecs = current_ms;
/*
* Wait a short time after receiving an input report before
* transmitting. This should reduce odds of a TX coinciding with an RX.
* Minimizing concurrent BT traffic with the controller seems to lower
* the rate of disconnections.
*/
msleep(JC_SUBCMD_TX_OFFSET_MS);
}
static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len,
......@@ -1223,6 +1260,7 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr,
u8 tmp;
u32 btns;
unsigned long msecs = jiffies_to_msecs(jiffies);
unsigned long report_delta_ms = msecs - ctlr->last_input_report_msecs;
spin_lock_irqsave(&ctlr->lock, flags);
if (IS_ENABLED(CONFIG_NINTENDO_FF) && rep->vibrator_report &&
......@@ -1364,6 +1402,31 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr,
input_sync(dev);
spin_lock_irqsave(&ctlr->lock, flags);
ctlr->last_input_report_msecs = msecs;
/*
* Was this input report a reasonable time delta compared to the prior
* report? We use this information to decide when a safe time is to send
* rumble packets or subcommand packets.
*/
if (report_delta_ms >= JC_INPUT_REPORT_MIN_DELTA &&
report_delta_ms <= JC_INPUT_REPORT_MAX_DELTA) {
if (ctlr->consecutive_valid_report_deltas < JC_SUBCMD_VALID_DELTA_REQ)
ctlr->consecutive_valid_report_deltas++;
} else {
ctlr->consecutive_valid_report_deltas = 0;
}
/*
* Our consecutive valid report tracking is only relevant for
* bluetooth-connected controllers. For USB devices, we're beholden to
* USB's underlying polling rate anyway. Always set to the consecutive
* delta requirement.
*/
if (ctlr->hdev->bus == BUS_USB)
ctlr->consecutive_valid_report_deltas = JC_SUBCMD_VALID_DELTA_REQ;
spin_unlock_irqrestore(&ctlr->lock, flags);
/*
* Immediately after receiving a report is the most reliable time to
* send a subcommand to the controller. Wake any subcommand senders
......@@ -1527,6 +1590,7 @@ static int joycon_set_rumble(struct joycon_ctlr *ctlr, u16 amp_r, u16 amp_l,
u16 freq_l_low;
u16 freq_l_high;
unsigned long flags;
int next_rq_head;
spin_lock_irqsave(&ctlr->lock, flags);
freq_r_low = ctlr->rumble_rl_freq;
......@@ -1547,8 +1611,21 @@ static int joycon_set_rumble(struct joycon_ctlr *ctlr, u16 amp_r, u16 amp_l,
joycon_encode_rumble(data, freq_l_low, freq_l_high, amp);
spin_lock_irqsave(&ctlr->lock, flags);
if (++ctlr->rumble_queue_head >= JC_RUMBLE_QUEUE_SIZE)
ctlr->rumble_queue_head = 0;
next_rq_head = ctlr->rumble_queue_head + 1;
if (next_rq_head >= JC_RUMBLE_QUEUE_SIZE)
next_rq_head = 0;
/* Did we overrun the circular buffer?
* If so, be sure we keep the latest intended rumble state.
*/
if (next_rq_head == ctlr->rumble_queue_tail) {
hid_dbg(ctlr->hdev, "rumble queue is full");
/* overwrite the prior value at the end of the circular buf */
next_rq_head = ctlr->rumble_queue_head;
}
ctlr->rumble_queue_head = next_rq_head;
memcpy(ctlr->rumble_data[ctlr->rumble_queue_head], data,
JC_RUMBLE_DATA_SIZE);
......@@ -2128,7 +2205,7 @@ static int nintendo_hid_probe(struct hid_device *hdev,
ctlr->hdev = hdev;
ctlr->ctlr_state = JOYCON_CTLR_STATE_INIT;
ctlr->rumble_queue_head = JC_RUMBLE_QUEUE_SIZE - 1;
ctlr->rumble_queue_head = 0;
ctlr->rumble_queue_tail = 0;
hid_set_drvdata(hdev, ctlr);
mutex_init(&ctlr->output_mutex);
......
......@@ -104,12 +104,20 @@ static const struct hid_device_id hid_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_1f4a), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6680), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_INNOMEDIA, USB_DEVICE_ID_INNEX_GENESIS_ATARI), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M406), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M506), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M406W), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_340), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_M508WX), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_M508X), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M406XE), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_T609A), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_OPTICAL_USB_MOUSE_600E), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_608D), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_6019), HID_QUIRK_ALWAYS_POLL },
......
......@@ -11,6 +11,7 @@
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/leds.h>
#include "hid-ids.h"
......
......@@ -23,12 +23,14 @@ config I2C_HID_ACPI
config I2C_HID_OF
tristate "HID over I2C transport layer Open Firmware driver"
depends on OF
# No "depends on OF" because this can also be used for manually
# (board-file) instantiated "hid-over-i2c" type i2c-clients.
select I2C_HID_CORE
help
Say Y here if you use a keyboard, a touchpad, a touchscreen, or any
other HID based devices which is connected to your computer via I2C.
This driver supports Open Firmware (Device Tree)-based systems.
This driver supports Open Firmware (Device Tree)-based systems as
well as binding to manually (board-file) instantiated i2c-hid-clients.
If unsure, say N.
......
......@@ -21,6 +21,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/hid.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
......@@ -35,8 +36,10 @@ struct i2c_hid_of {
struct i2chid_ops ops;
struct i2c_client *client;
struct gpio_desc *reset_gpio;
struct regulator_bulk_data supplies[2];
int post_power_delay_ms;
int post_reset_delay_ms;
};
static int i2c_hid_of_power_up(struct i2chid_ops *ops)
......@@ -55,6 +58,10 @@ static int i2c_hid_of_power_up(struct i2chid_ops *ops)
if (ihid_of->post_power_delay_ms)
msleep(ihid_of->post_power_delay_ms);
gpiod_set_value_cansleep(ihid_of->reset_gpio, 0);
if (ihid_of->post_reset_delay_ms)
msleep(ihid_of->post_reset_delay_ms);
return 0;
}
......@@ -62,6 +69,7 @@ static void i2c_hid_of_power_down(struct i2chid_ops *ops)
{
struct i2c_hid_of *ihid_of = container_of(ops, struct i2c_hid_of, ops);
gpiod_set_value_cansleep(ihid_of->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(ihid_of->supplies),
ihid_of->supplies);
}
......@@ -75,33 +83,43 @@ static int i2c_hid_of_probe(struct i2c_client *client)
int ret;
u32 val;
ihid_of = devm_kzalloc(&client->dev, sizeof(*ihid_of), GFP_KERNEL);
ihid_of = devm_kzalloc(dev, sizeof(*ihid_of), GFP_KERNEL);
if (!ihid_of)
return -ENOMEM;
ihid_of->ops.power_up = i2c_hid_of_power_up;
ihid_of->ops.power_down = i2c_hid_of_power_down;
ret = of_property_read_u32(dev->of_node, "hid-descr-addr", &val);
ret = device_property_read_u32(dev, "hid-descr-addr", &val);
if (ret) {
dev_err(&client->dev, "HID register address not provided\n");
dev_err(dev, "HID register address not provided\n");
return -ENODEV;
}
if (val >> 16) {
dev_err(&client->dev, "Bad HID register address: 0x%08x\n",
val);
dev_err(dev, "Bad HID register address: 0x%08x\n", val);
return -EINVAL;
}
hid_descriptor_address = val;
if (!device_property_read_u32(&client->dev, "post-power-on-delay-ms",
&val))
if (!device_property_read_u32(dev, "post-power-on-delay-ms", &val))
ihid_of->post_power_delay_ms = val;
/*
* Note this is a kernel internal device-property set by x86 platform code,
* this MUST not be used in devicetree files without first adding it to
* the DT bindings.
*/
if (!device_property_read_u32(dev, "post-reset-deassert-delay-ms", &val))
ihid_of->post_reset_delay_ms = val;
/* Start out with reset asserted */
ihid_of->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ihid_of->reset_gpio))
return PTR_ERR(ihid_of->reset_gpio);
ihid_of->supplies[0].supply = "vdd";
ihid_of->supplies[1].supply = "vddl";
ret = devm_regulator_bulk_get(&client->dev,
ARRAY_SIZE(ihid_of->supplies),
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ihid_of->supplies),
ihid_of->supplies);
if (ret)
return ret;
......@@ -116,11 +134,13 @@ static int i2c_hid_of_probe(struct i2c_client *client)
hid_descriptor_address, quirks);
}
#ifdef CONFIG_OF
static const struct of_device_id i2c_hid_of_match[] = {
{ .compatible = "hid-over-i2c" },
{},
};
MODULE_DEVICE_TABLE(of, i2c_hid_of_match);
#endif
static const struct i2c_device_id i2c_hid_of_id_table[] = {
{ "hid", 0 },
......
......@@ -2372,13 +2372,6 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
if (error)
goto fail;
if (!(features->device_type & WACOM_DEVICETYPE_WL_MONITOR) &&
(features->quirks & WACOM_QUIRK_BATTERY)) {
error = wacom_initialize_battery(wacom);
if (error)
goto fail;
}
error = wacom_register_inputs(wacom);
if (error)
goto fail;
......@@ -2509,9 +2502,6 @@ static void wacom_wireless_work(struct work_struct *work)
strscpy(wacom_wac->name, wacom_wac1->name,
sizeof(wacom_wac->name));
error = wacom_initialize_battery(wacom);
if (error)
goto fail;
}
return;
......
......@@ -113,6 +113,11 @@ static void wacom_notify_battery(struct wacom_wac *wacom_wac,
bool bat_connected, bool ps_connected)
{
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
bool bat_initialized = wacom->battery.battery;
bool has_quirk = wacom_wac->features.quirks & WACOM_QUIRK_BATTERY;
if (bat_initialized != has_quirk)
wacom_schedule_work(wacom_wac, WACOM_WORKER_BATTERY);
__wacom_notify_battery(&wacom->battery, bat_status, bat_capacity,
bat_charging, bat_connected, ps_connected);
......@@ -1308,6 +1313,9 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
struct input_dev *pen_input = wacom->pen_input;
unsigned char *data = wacom->data;
int number_of_valid_frames = 0;
int time_interval = 15000000;
ktime_t time_packet_received = ktime_get();
int i;
if (wacom->features.type == INTUOSP2_BT ||
......@@ -1328,12 +1336,30 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
wacom->id[0] |= (wacom->serial[0] >> 32) & 0xFFFFF;
}
/* number of valid frames */
for (i = 0; i < pen_frames; i++) {
unsigned char *frame = &data[i*pen_frame_len + 1];
bool valid = frame[0] & 0x80;
if (valid)
number_of_valid_frames++;
}
if (number_of_valid_frames) {
if (wacom->hid_data.time_delayed)
time_interval = ktime_get() - wacom->hid_data.time_delayed;
time_interval /= number_of_valid_frames;
wacom->hid_data.time_delayed = time_packet_received;
}
for (i = 0; i < number_of_valid_frames; i++) {
unsigned char *frame = &data[i*pen_frame_len + 1];
bool valid = frame[0] & 0x80;
bool prox = frame[0] & 0x40;
bool range = frame[0] & 0x20;
bool invert = frame[0] & 0x10;
int frames_number_reversed = number_of_valid_frames - i - 1;
int event_timestamp = time_packet_received - frames_number_reversed * time_interval;
if (!valid)
continue;
......@@ -1346,6 +1372,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
wacom->tool[0] = 0;
wacom->id[0] = 0;
wacom->serial[0] = 0;
wacom->hid_data.time_delayed = 0;
return;
}
......@@ -1382,6 +1409,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
get_unaligned_le16(&frame[11]));
}
}
if (wacom->tool[0]) {
input_report_abs(pen_input, ABS_PRESSURE, get_unaligned_le16(&frame[5]));
if (wacom->features.type == INTUOSP2_BT ||
......@@ -1405,6 +1433,9 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
wacom->shared->stylus_in_proximity = prox;
/* add timestamp to unpack the frames */
input_set_timestamp(pen_input, event_timestamp);
input_sync(pen_input);
}
}
......@@ -1895,6 +1926,7 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage,
int fmax = field->logical_maximum;
unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid);
int resolution_code = code;
int resolution = hidinput_calc_abs_res(field, resolution_code);
if (equivalent_usage == HID_DG_TWIST) {
resolution_code = ABS_RZ;
......@@ -1915,8 +1947,15 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage,
switch (type) {
case EV_ABS:
input_set_abs_params(input, code, fmin, fmax, fuzz, 0);
input_abs_set_res(input, code,
hidinput_calc_abs_res(field, resolution_code));
/* older tablet may miss physical usage */
if ((code == ABS_X || code == ABS_Y) && !resolution) {
resolution = WACOM_INTUOS_RES;
hid_warn(input,
"Wacom usage (%d) missing resolution \n",
code);
}
input_abs_set_res(input, code, resolution);
break;
case EV_KEY:
case EV_MSC:
......@@ -1929,18 +1968,7 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage,
static void wacom_wac_battery_usage_mapping(struct hid_device *hdev,
struct hid_field *field, struct hid_usage *usage)
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct wacom_features *features = &wacom_wac->features;
unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
switch (equivalent_usage) {
case HID_DG_BATTERYSTRENGTH:
case WACOM_HID_WD_BATTERY_LEVEL:
case WACOM_HID_WD_BATTERY_CHARGING:
features->quirks |= WACOM_QUIRK_BATTERY;
break;
}
return;
}
static void wacom_wac_battery_event(struct hid_device *hdev, struct hid_field *field,
......@@ -1961,18 +1989,21 @@ static void wacom_wac_battery_event(struct hid_device *hdev, struct hid_field *f
wacom_wac->hid_data.bat_connected = 1;
wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO;
}
wacom_wac->features.quirks |= WACOM_QUIRK_BATTERY;
break;
case WACOM_HID_WD_BATTERY_LEVEL:
value = value * 100 / (field->logical_maximum - field->logical_minimum);
wacom_wac->hid_data.battery_capacity = value;
wacom_wac->hid_data.bat_connected = 1;
wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO;
wacom_wac->features.quirks |= WACOM_QUIRK_BATTERY;
break;
case WACOM_HID_WD_BATTERY_CHARGING:
wacom_wac->hid_data.bat_charging = value;
wacom_wac->hid_data.ps_connected = value;
wacom_wac->hid_data.bat_connected = 1;
wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO;
wacom_wac->features.quirks |= WACOM_QUIRK_BATTERY;
break;
}
}
......@@ -1988,9 +2019,7 @@ static void wacom_wac_battery_report(struct hid_device *hdev,
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct wacom_features *features = &wacom_wac->features;
if (features->quirks & WACOM_QUIRK_BATTERY) {
int status = wacom_wac->hid_data.bat_status;
int capacity = wacom_wac->hid_data.battery_capacity;
bool charging = wacom_wac->hid_data.bat_charging;
......@@ -1999,7 +2028,6 @@ static void wacom_wac_battery_report(struct hid_device *hdev,
wacom_notify_battery(wacom_wac, status, capacity, charging,
connected, powered);
}
}
static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
......@@ -3365,19 +3393,13 @@ static int wacom_status_irq(struct wacom_wac *wacom_wac, size_t len)
int battery = (data[8] & 0x3f) * 100 / 31;
bool charging = !!(data[8] & 0x80);
features->quirks |= WACOM_QUIRK_BATTERY;
wacom_notify_battery(wacom_wac, WACOM_POWER_SUPPLY_STATUS_AUTO,
battery, charging, battery || charging, 1);
if (!wacom->battery.battery &&
!(features->quirks & WACOM_QUIRK_BATTERY)) {
features->quirks |= WACOM_QUIRK_BATTERY;
wacom_schedule_work(wacom_wac, WACOM_WORKER_BATTERY);
}
}
else if ((features->quirks & WACOM_QUIRK_BATTERY) &&
wacom->battery.battery) {
features->quirks &= ~WACOM_QUIRK_BATTERY;
wacom_schedule_work(wacom_wac, WACOM_WORKER_BATTERY);
wacom_notify_battery(wacom_wac, POWER_SUPPLY_STATUS_UNKNOWN, 0, 0, 0, 0);
}
return 0;
......
......@@ -324,6 +324,7 @@ struct hid_data {
int ps_connected;
bool pad_input_event_flag;
unsigned short sequence_number;
int time_delayed;
};
struct wacom_remote_data {
......
......@@ -1908,6 +1908,45 @@ static void __usb_queue_reset_device(struct work_struct *ws)
usb_put_intf(iface); /* Undo _get_ in usb_queue_reset_device() */
}
/*
* Internal function to set the wireless_status sysfs attribute
* See usb_set_wireless_status() for more details
*/
static void __usb_wireless_status_intf(struct work_struct *ws)
{
struct usb_interface *iface =
container_of(ws, struct usb_interface, wireless_status_work);
device_lock(iface->dev.parent);
if (iface->sysfs_files_created)
usb_update_wireless_status_attr(iface);
device_unlock(iface->dev.parent);
usb_put_intf(iface); /* Undo _get_ in usb_set_wireless_status() */
}
/**
* usb_set_wireless_status - sets the wireless_status struct member
* @iface: the interface to modify
* @status: the new wireless status
*
* Set the wireless_status struct member to the new value, and emit
* sysfs changes as necessary.
*
* Returns: 0 on success, -EALREADY if already set.
*/
int usb_set_wireless_status(struct usb_interface *iface,
enum usb_wireless_status status)
{
if (iface->wireless_status == status)
return -EALREADY;
usb_get_intf(iface);
iface->wireless_status = status;
schedule_work(&iface->wireless_status_work);
return 0;
}
EXPORT_SYMBOL_GPL(usb_set_wireless_status);
/*
* usb_set_configuration - Makes a particular device setting be current
......@@ -2100,6 +2139,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
intf->dev.type = &usb_if_device_type;
intf->dev.groups = usb_interface_groups;
INIT_WORK(&intf->reset_ws, __usb_queue_reset_device);
INIT_WORK(&intf->wireless_status_work, __usb_wireless_status_intf);
intf->minor = -1;
device_initialize(&intf->dev);
pm_runtime_no_callbacks(&intf->dev);
......
......@@ -1227,9 +1227,59 @@ static const struct attribute_group intf_assoc_attr_grp = {
.is_visible = intf_assoc_attrs_are_visible,
};
static ssize_t wireless_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_interface *intf;
intf = to_usb_interface(dev);
if (intf->wireless_status == USB_WIRELESS_STATUS_DISCONNECTED)
return sysfs_emit(buf, "%s\n", "disconnected");
return sysfs_emit(buf, "%s\n", "connected");
}
static DEVICE_ATTR_RO(wireless_status);
static struct attribute *intf_wireless_status_attrs[] = {
&dev_attr_wireless_status.attr,
NULL
};
static umode_t intf_wireless_status_attr_is_visible(struct kobject *kobj,
struct attribute *a, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct usb_interface *intf = to_usb_interface(dev);
if (a != &dev_attr_wireless_status.attr ||
intf->wireless_status != USB_WIRELESS_STATUS_NA)
return a->mode;
return 0;
}
static const struct attribute_group intf_wireless_status_attr_grp = {
.attrs = intf_wireless_status_attrs,
.is_visible = intf_wireless_status_attr_is_visible,
};
int usb_update_wireless_status_attr(struct usb_interface *intf)
{
struct device *dev = &intf->dev;
int ret;
ret = sysfs_update_group(&dev->kobj, &intf_wireless_status_attr_grp);
if (ret < 0)
return ret;
sysfs_notify(&dev->kobj, NULL, "wireless_status");
kobject_uevent(&dev->kobj, KOBJ_CHANGE);
return 0;
}
const struct attribute_group *usb_interface_groups[] = {
&intf_attr_grp,
&intf_assoc_attr_grp,
&intf_wireless_status_attr_grp,
NULL
};
......
......@@ -15,6 +15,7 @@ extern int usb_create_sysfs_dev_files(struct usb_device *dev);
extern void usb_remove_sysfs_dev_files(struct usb_device *dev);
extern void usb_create_sysfs_intf_files(struct usb_interface *intf);
extern void usb_remove_sysfs_intf_files(struct usb_interface *intf);
extern int usb_update_wireless_status_attr(struct usb_interface *intf);
extern int usb_create_ep_devs(struct device *parent,
struct usb_host_endpoint *endpoint,
struct usb_device *udev);
......
......@@ -156,6 +156,7 @@ struct hid_item {
#define HID_UP_DIGITIZER 0x000d0000
#define HID_UP_PID 0x000f0000
#define HID_UP_BATTERY 0x00850000
#define HID_UP_CAMERA 0x00900000
#define HID_UP_HPVENDOR 0xff7f0000
#define HID_UP_HPVENDOR2 0xff010000
#define HID_UP_MSVENDOR 0xff000000
......@@ -873,7 +874,7 @@ extern bool hid_is_usb(const struct hid_device *hdev);
/* We ignore a few input applications that are not widely used */
#define IS_INPUT_APPLICATION(a) \
(((a >= HID_UP_GENDESK) && (a <= HID_GD_MULTIAXIS)) \
|| ((a >= HID_DG_PEN) && (a <= HID_DG_WHITEBOARD)) \
|| ((a >= HID_DG_DIGITIZER) && (a <= HID_DG_WHITEBOARD)) \
|| (a == HID_GD_SYSTEM_CONTROL) || (a == HID_CP_CONSUMER_CONTROL) \
|| (a == HID_GD_WIRELESS_RADIO_CTLS))
......
......@@ -170,6 +170,12 @@ usb_find_last_int_out_endpoint(struct usb_host_interface *alt,
return usb_find_common_endpoints_reverse(alt, NULL, NULL, NULL, int_out);
}
enum usb_wireless_status {
USB_WIRELESS_STATUS_NA = 0,
USB_WIRELESS_STATUS_DISCONNECTED,
USB_WIRELESS_STATUS_CONNECTED,
};
/**
* struct usb_interface - what usb device drivers talk to
* @altsetting: array of interface structures, one for each alternate
......@@ -197,6 +203,10 @@ usb_find_last_int_out_endpoint(struct usb_host_interface *alt,
* following a reset or suspend operation it doesn't support.
* @authorized: This allows to (de)authorize individual interfaces instead
* a whole device in contrast to the device authorization.
* @wireless_status: if the USB device uses a receiver/emitter combo, whether
* the emitter is connected.
* @wireless_status_work: Used for scheduling wireless status changes
* from atomic context.
* @dev: driver model's view of this device
* @usb_dev: if an interface is bound to the USB major, this will point
* to the sysfs representation for that device.
......@@ -253,6 +263,8 @@ struct usb_interface {
unsigned needs_binding:1; /* needs delayed unbind/rebind */
unsigned resetting_device:1; /* true: bandwidth alloc after reset */
unsigned authorized:1; /* used for interface authorization */
enum usb_wireless_status wireless_status;
struct work_struct wireless_status_work;
struct device dev; /* interface specific device info */
struct device *usb_dev;
......@@ -887,6 +899,10 @@ static inline int usb_interface_claimed(struct usb_interface *iface)
extern void usb_driver_release_interface(struct usb_driver *driver,
struct usb_interface *iface);
int usb_set_wireless_status(struct usb_interface *iface,
enum usb_wireless_status status);
const struct usb_device_id *usb_match_id(struct usb_interface *interface,
const struct usb_device_id *id);
extern int usb_match_one_id(struct usb_interface *interface,
......
......@@ -5,6 +5,18 @@ include ../../../build/Build.include
include ../../../scripts/Makefile.arch
include ../../../scripts/Makefile.include
TEST_PROGS := hid-core.sh
TEST_PROGS += hid-apple.sh
TEST_PROGS += hid-gamepad.sh
TEST_PROGS += hid-ite.sh
TEST_PROGS += hid-keyboard.sh
TEST_PROGS += hid-mouse.sh
TEST_PROGS += hid-multitouch.sh
TEST_PROGS += hid-sony.sh
TEST_PROGS += hid-tablet.sh
TEST_PROGS += hid-usb_crash.sh
TEST_PROGS += hid-wacom.sh
CXX ?= $(CROSS_COMPILE)g++
HOSTPKG_CONFIG := pkg-config
......
......@@ -20,3 +20,14 @@ CONFIG_HID=y
CONFIG_HID_BPF=y
CONFIG_INPUT_EVDEV=y
CONFIG_UHID=y
CONFIG_LEDS_CLASS_MULTICOLOR=y
CONFIG_USB=y
CONFIG_USB_HID=y
CONFIG_HID_APPLE=y
CONFIG_HID_ITE=y
CONFIG_HID_MULTITOUCH=y
CONFIG_HID_PLAYSTATION=y
CONFIG_PLAYSTATION_FF=y
CONFIG_HID_SONY=y
CONFIG_SONY_FF=y
CONFIG_HID_WACOM=y
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Runs tests for the HID subsystem
export TARGET=test_apple_keyboard.py
bash ./run-hid-tools-tests.sh
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Runs tests for the HID subsystem
export TARGET=test_hid_core.py
bash ./run-hid-tools-tests.sh
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Runs tests for the HID subsystem
export TARGET=test_gamepad.py
bash ./run-hid-tools-tests.sh
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Runs tests for the HID subsystem
export TARGET=test_ite_keyboard.py
bash ./run-hid-tools-tests.sh
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Runs tests for the HID subsystem
export TARGET=test_keyboard.py
bash ./run-hid-tools-tests.sh
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Runs tests for the HID subsystem
export TARGET=test_mouse.py
bash ./run-hid-tools-tests.sh
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Runs tests for the HID subsystem
export TARGET=test_multitouch.py
bash ./run-hid-tools-tests.sh
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Runs tests for the HID subsystem
export TARGET=test_sony.py
bash ./run-hid-tools-tests.sh
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Runs tests for the HID subsystem
export TARGET=test_tablet.py
bash ./run-hid-tools-tests.sh
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Runs tests for the HID subsystem
export TARGET=test_usb_crash.py
bash ./run-hid-tools-tests.sh
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Runs tests for the HID subsystem
export TARGET=test_wacom_generic.py
bash ./run-hid-tools-tests.sh
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Runs tests for the HID subsystem
if ! command -v python3 > /dev/null 2>&1; then
echo "hid-tools: [SKIP] python3 not installed"
exit 77
fi
if ! python3 -c "import pytest" > /dev/null 2>&1; then
echo "hid: [SKIP/ pytest module not installed"
exit 77
fi
if ! python3 -c "import pytest_tap" > /dev/null 2>&1; then
echo "hid: [SKIP/ pytest_tap module not installed"
exit 77
fi
if ! python3 -c "import hidtools" > /dev/null 2>&1; then
echo "hid: [SKIP/ hid-tools module not installed"
exit 77
fi
TARGET=${TARGET:=.}
echo TAP version 13
python3 -u -m pytest $PYTEST_XDIST ./tests/$TARGET --tap-stream --udevd
# HID tests can be long, so give a little bit more time
# to them
timeout=200
# SPDX-License-Identifier: GPL-2.0
# Just to make sphinx-apidoc document this directory
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2017 Red Hat, Inc.
import libevdev
import os
import pytest
import time
import logging
from hidtools.device.base_device import BaseDevice, EvdevMatch, SysfsFile
from pathlib import Path
from typing import Final
logger = logging.getLogger("hidtools.test.base")
# application to matches
application_matches: Final = {
# pyright: ignore
"Accelerometer": EvdevMatch(
req_properties=[
libevdev.INPUT_PROP_ACCELEROMETER,
]
),
"Game Pad": EvdevMatch( # in systemd, this is a lot more complex, but that will do
requires=[
libevdev.EV_ABS.ABS_X,
libevdev.EV_ABS.ABS_Y,
libevdev.EV_ABS.ABS_RX,
libevdev.EV_ABS.ABS_RY,
libevdev.EV_KEY.BTN_START,
],
excl_properties=[
libevdev.INPUT_PROP_ACCELEROMETER,
],
),
"Joystick": EvdevMatch( # in systemd, this is a lot more complex, but that will do
requires=[
libevdev.EV_ABS.ABS_RX,
libevdev.EV_ABS.ABS_RY,
libevdev.EV_KEY.BTN_START,
],
excl_properties=[
libevdev.INPUT_PROP_ACCELEROMETER,
],
),
"Key": EvdevMatch(
requires=[
libevdev.EV_KEY.KEY_A,
],
excl_properties=[
libevdev.INPUT_PROP_ACCELEROMETER,
libevdev.INPUT_PROP_DIRECT,
libevdev.INPUT_PROP_POINTER,
],
),
"Mouse": EvdevMatch(
requires=[
libevdev.EV_REL.REL_X,
libevdev.EV_REL.REL_Y,
libevdev.EV_KEY.BTN_LEFT,
],
excl_properties=[
libevdev.INPUT_PROP_ACCELEROMETER,
],
),
"Pad": EvdevMatch(
requires=[
libevdev.EV_KEY.BTN_0,
],
excludes=[
libevdev.EV_KEY.BTN_TOOL_PEN,
libevdev.EV_KEY.BTN_TOUCH,
libevdev.EV_ABS.ABS_DISTANCE,
],
excl_properties=[
libevdev.INPUT_PROP_ACCELEROMETER,
],
),
"Pen": EvdevMatch(
requires=[
libevdev.EV_KEY.BTN_STYLUS,
libevdev.EV_ABS.ABS_X,
libevdev.EV_ABS.ABS_Y,
],
excl_properties=[
libevdev.INPUT_PROP_ACCELEROMETER,
],
),
"Stylus": EvdevMatch(
requires=[
libevdev.EV_KEY.BTN_STYLUS,
libevdev.EV_ABS.ABS_X,
libevdev.EV_ABS.ABS_Y,
],
excl_properties=[
libevdev.INPUT_PROP_ACCELEROMETER,
],
),
"Touch Pad": EvdevMatch(
requires=[
libevdev.EV_KEY.BTN_LEFT,
libevdev.EV_ABS.ABS_X,
libevdev.EV_ABS.ABS_Y,
],
excludes=[libevdev.EV_KEY.BTN_TOOL_PEN, libevdev.EV_KEY.BTN_STYLUS],
req_properties=[
libevdev.INPUT_PROP_POINTER,
],
excl_properties=[
libevdev.INPUT_PROP_ACCELEROMETER,
],
),
"Touch Screen": EvdevMatch(
requires=[
libevdev.EV_KEY.BTN_TOUCH,
libevdev.EV_ABS.ABS_X,
libevdev.EV_ABS.ABS_Y,
],
excludes=[libevdev.EV_KEY.BTN_TOOL_PEN, libevdev.EV_KEY.BTN_STYLUS],
req_properties=[
libevdev.INPUT_PROP_DIRECT,
],
excl_properties=[
libevdev.INPUT_PROP_ACCELEROMETER,
],
),
}
class UHIDTestDevice(BaseDevice):
def __init__(self, name, application, rdesc_str=None, rdesc=None, input_info=None):
super().__init__(name, application, rdesc_str, rdesc, input_info)
self.application_matches = application_matches
if name is None:
name = f"uhid test {self.__class__.__name__}"
if not name.startswith("uhid test "):
name = "uhid test " + self.name
self.name = name
class BaseTestCase:
class TestUhid(object):
syn_event = libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT) # type: ignore
key_event = libevdev.InputEvent(libevdev.EV_KEY) # type: ignore
abs_event = libevdev.InputEvent(libevdev.EV_ABS) # type: ignore
rel_event = libevdev.InputEvent(libevdev.EV_REL) # type: ignore
msc_event = libevdev.InputEvent(libevdev.EV_MSC.MSC_SCAN) # type: ignore
# List of kernel modules to load before starting the test
# if any module is not available (not compiled), the test will skip.
# Each element is a tuple '(kernel driver name, kernel module)',
# for example ("playstation", "hid-playstation")
kernel_modules = []
def assertInputEventsIn(self, expected_events, effective_events):
effective_events = effective_events.copy()
for ev in expected_events:
assert ev in effective_events
effective_events.remove(ev)
return effective_events
def assertInputEvents(self, expected_events, effective_events):
remaining = self.assertInputEventsIn(expected_events, effective_events)
assert remaining == []
@classmethod
def debug_reports(cls, reports, uhdev=None, events=None):
data = [" ".join([f"{v:02x}" for v in r]) for r in reports]
if uhdev is not None:
human_data = [
uhdev.parsed_rdesc.format_report(r, split_lines=True)
for r in reports
]
try:
human_data = [
f'\n\t {" " * h.index("/")}'.join(h.split("\n"))
for h in human_data
]
except ValueError:
# '/' not found: not a numbered report
human_data = ["\n\t ".join(h.split("\n")) for h in human_data]
data = [f"{d}\n\t ====> {h}" for d, h in zip(data, human_data)]
reports = data
if len(reports) == 1:
print("sending 1 report:")
else:
print(f"sending {len(reports)} reports:")
for report in reports:
print("\t", report)
if events is not None:
print("events received:", events)
def create_device(self):
raise Exception("please reimplement me in subclasses")
def _load_kernel_module(self, kernel_driver, kernel_module):
sysfs_path = Path("/sys/bus/hid/drivers")
if kernel_driver is not None:
sysfs_path /= kernel_driver
else:
# special case for when testing all available modules:
# we don't know beforehand the name of the module from modinfo
sysfs_path = Path("/sys/module") / kernel_module.replace("-", "_")
if not sysfs_path.exists():
import subprocess
ret = subprocess.run(["/usr/sbin/modprobe", kernel_module])
if ret.returncode != 0:
pytest.skip(
f"module {kernel_module} could not be loaded, skipping the test"
)
@pytest.fixture()
def load_kernel_module(self):
for kernel_driver, kernel_module in self.kernel_modules:
self._load_kernel_module(kernel_driver, kernel_module)
yield
@pytest.fixture()
def new_uhdev(self, load_kernel_module):
return self.create_device()
def assertName(self, uhdev):
evdev = uhdev.get_evdev()
assert uhdev.name in evdev.name
@pytest.fixture(autouse=True)
def context(self, new_uhdev, request):
try:
with HIDTestUdevRule.instance():
with new_uhdev as self.uhdev:
skip_cond = request.node.get_closest_marker("skip_if_uhdev")
if skip_cond:
test, message, *rest = skip_cond.args
if test(self.uhdev):
pytest.skip(message)
self.uhdev.create_kernel_device()
now = time.time()
while not self.uhdev.is_ready() and time.time() - now < 5:
self.uhdev.dispatch(1)
if self.uhdev.get_evdev() is None:
logger.warning(
f"available list of input nodes: (default application is '{self.uhdev.application}')"
)
logger.warning(self.uhdev.input_nodes)
yield
self.uhdev = None
except PermissionError:
pytest.skip("Insufficient permissions, run me as root")
@pytest.fixture(autouse=True)
def check_taint(self):
# we are abusing SysfsFile here, it's in /proc, but meh
taint_file = SysfsFile("/proc/sys/kernel/tainted")
taint = taint_file.int_value
yield
assert taint_file.int_value == taint
def test_creation(self):
"""Make sure the device gets processed by the kernel and creates
the expected application input node.
If this fail, there is something wrong in the device report
descriptors."""
uhdev = self.uhdev
assert uhdev is not None
assert uhdev.get_evdev() is not None
self.assertName(uhdev)
assert len(uhdev.next_sync_events()) == 0
assert uhdev.get_evdev() is not None
class HIDTestUdevRule(object):
_instance = None
"""
A context-manager compatible class that sets up our udev rules file and
deletes it on context exit.
This class is tailored to our test setup: it only sets up the udev rule
on the **second** context and it cleans it up again on the last context
removed. This matches the expected pytest setup: we enter a context for
the session once, then once for each test (the first of which will
trigger the udev rule) and once the last test exited and the session
exited, we clean up after ourselves.
"""
def __init__(self):
self.refs = 0
self.rulesfile = None
def __enter__(self):
self.refs += 1
if self.refs == 2 and self.rulesfile is None:
self.create_udev_rule()
self.reload_udev_rules()
def __exit__(self, exc_type, exc_value, traceback):
self.refs -= 1
if self.refs == 0 and self.rulesfile:
os.remove(self.rulesfile.name)
self.reload_udev_rules()
def reload_udev_rules(self):
import subprocess
subprocess.run("udevadm control --reload-rules".split())
subprocess.run("systemd-hwdb update".split())
def create_udev_rule(self):
import tempfile
os.makedirs("/run/udev/rules.d", exist_ok=True)
with tempfile.NamedTemporaryFile(
prefix="91-uhid-test-device-REMOVEME-",
suffix=".rules",
mode="w+",
dir="/run/udev/rules.d",
delete=False,
) as f:
f.write(
'KERNELS=="*input*", ATTRS{name}=="*uhid test *", ENV{LIBINPUT_IGNORE_DEVICE}="1"\n'
)
f.write(
'KERNELS=="*input*", ATTRS{name}=="*uhid test * System Multi Axis", ENV{ID_INPUT_TOUCHSCREEN}="", ENV{ID_INPUT_SYSTEM_MULTIAXIS}="1"\n'
)
self.rulesfile = f
@classmethod
def instance(cls):
if not cls._instance:
cls._instance = HIDTestUdevRule()
return cls._instance
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2017 Red Hat, Inc.
import platform
import pytest
import re
import resource
import subprocess
from .base import HIDTestUdevRule
from pathlib import Path
# See the comment in HIDTestUdevRule, this doesn't set up but it will clean
# up once the last test exited.
@pytest.fixture(autouse=True, scope="session")
def udev_rules_session_setup():
with HIDTestUdevRule.instance():
yield
@pytest.fixture(autouse=True, scope="session")
def setup_rlimit():
resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
@pytest.fixture(autouse=True, scope="session")
def start_udevd(pytestconfig):
if pytestconfig.getoption("udevd"):
import subprocess
with subprocess.Popen("/usr/lib/systemd/systemd-udevd") as proc:
yield
proc.kill()
else:
yield
def pytest_configure(config):
config.addinivalue_line(
"markers",
"skip_if_uhdev(condition, message): mark test to skip if the condition on the uhdev device is met",
)
# Generate the list of modules and modaliases
# for the tests that need to be parametrized with those
def pytest_generate_tests(metafunc):
if "usbVidPid" in metafunc.fixturenames:
modules = (
Path("/lib/modules/")
/ platform.uname().release
/ "kernel"
/ "drivers"
/ "hid"
)
modalias_re = re.compile(r"alias:\s+hid:b0003g.*v([0-9a-fA-F]+)p([0-9a-fA-F]+)")
params = []
ids = []
for module in modules.glob("*.ko"):
p = subprocess.run(
["modinfo", module], capture_output=True, check=True, encoding="utf-8"
)
for line in p.stdout.split("\n"):
m = modalias_re.match(line)
if m is not None:
vid, pid = m.groups()
vid = int(vid, 16)
pid = int(pid, 16)
params.append([module.name.replace(".ko", ""), vid, pid])
ids.append(f"{module.name} {vid:04x}:{pid:04x}")
metafunc.parametrize("usbVidPid", params, ids=ids)
def pytest_addoption(parser):
parser.addoption("--udevd", action="store_true", default=False)
# SPDX-License-Identifier: GPL-2.0
# fmt: off
wacom_pth660_v145 = [
0x05, 0x01, # . Usage Page (Desktop),
0x09, 0x02, # . Usage (Mouse),
0xA1, 0x01, # . Collection (Application),
0x85, 0x01, # . Report ID (1),
0x09, 0x01, # . Usage (Pointer),
0xA1, 0x00, # . Collection (Physical),
0x05, 0x09, # . Usage Page (Button),
0x19, 0x01, # . Usage Minimum (01h),
0x29, 0x03, # . Usage Maximum (03h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x03, # . Report Count (3),
0x81, 0x02, # . Input (Variable),
0x75, 0x01, # . Report Size (1),
0x95, 0x05, # . Report Count (5),
0x81, 0x03, # . Input (Constant, Variable),
0x05, 0x01, # . Usage Page (Desktop),
0x09, 0x30, # . Usage (X),
0x09, 0x31, # . Usage (Y),
0x15, 0x81, # . Logical Minimum (-127),
0x25, 0x7F, # . Logical Maximum (127),
0x75, 0x08, # . Report Size (8),
0x95, 0x02, # . Report Count (2),
0x81, 0x06, # . Input (Variable, Relative),
0xC0, # . End Collection,
0xC0, # . End Collection,
0x06, 0x0D, 0xFF, # . Usage Page (FF0Dh),
0x09, 0x01, # . Usage (01h),
0xA1, 0x01, # . Collection (Application),
0x85, 0x10, # . Report ID (16),
0x09, 0x20, # . Usage (20h),
0xA1, 0x00, # . Collection (Physical),
0x09, 0x42, # . Usage (42h),
0x09, 0x44, # . Usage (44h),
0x09, 0x5A, # . Usage (5Ah),
0x09, 0x45, # . Usage (45h),
0x09, 0x3C, # . Usage (3Ch),
0x09, 0x32, # . Usage (32h),
0x09, 0x36, # . Usage (36h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x07, # . Report Count (7),
0x81, 0x02, # . Input (Variable),
0x95, 0x01, # . Report Count (1),
0x81, 0x03, # . Input (Constant, Variable),
0x0A, 0x30, 0x01, # . Usage (0130h),
0x65, 0x11, # . Unit (Centimeter),
0x55, 0x0D, # . Unit Exponent (13),
0x35, 0x00, # . Physical Minimum (0),
0x47, 0x80, 0x57, 0x00, 0x00, # . Physical Maximum (22400),
0x15, 0x00, # . Logical Minimum (0),
0x27, 0x00, 0xAF, 0x00, 0x00, # . Logical Maximum (44800),
0x75, 0x18, # . Report Size (24),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x0A, 0x31, 0x01, # . Usage (0131h),
0x47, 0xD0, 0x39, 0x00, 0x00, # . Physical Maximum (14800),
0x27, 0xA0, 0x73, 0x00, 0x00, # . Logical Maximum (29600),
0x81, 0x02, # . Input (Variable),
0x09, 0x30, # . Usage (30h),
0x55, 0x00, # . Unit Exponent (0),
0x65, 0x00, # . Unit,
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x1F, # . Logical Maximum (8191), # !!! Errata: Missing Physical Max = 0
0x75, 0x10, # . Report Size (16),
0x81, 0x02, # . Input (Variable),
0x09, 0x3D, # . Usage (3Dh),
0x09, 0x3E, # . Usage (3Eh),
0x65, 0x14, # . Unit (Degrees),
0x55, 0x00, # . Unit Exponent (0),
0x35, 0xC0, # . Physical Minimum (-64),
0x45, 0x3F, # . Physical Maximum (63),
0x15, 0xC0, # . Logical Minimum (-64),
0x25, 0x3F, # . Logical Maximum (63),
0x75, 0x08, # . Report Size (8),
0x95, 0x02, # . Report Count (2),
0x81, 0x02, # . Input (Variable),
0x09, 0x41, # . Usage (41h),
0x65, 0x14, # . Unit (Degrees),
0x55, 0x00, # . Unit Exponent (0),
0x36, 0x4C, 0xFF, # . Physical Minimum (-180),
0x46, 0xB3, 0x00, # . Physical Maximum (179),
0x16, 0x7C, 0xFC, # . Logical Minimum (-900),
0x26, 0x83, 0x03, # . Logical Maximum (899),
0x75, 0x10, # . Report Size (16),
0x95, 0x01, # . Report Count (1),
0x81, 0x0A, # . Input (Variable, Wrap),
0x0A, 0x03, 0x0D, # . Usage (0D03h),
0x65, 0x00, # . Unit,
0x55, 0x00, # . Unit Exponent (0),
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x07, # . Logical Maximum (2047), # !!! Errata: Missing Physical Min/Max = 0
0x75, 0x10, # . Report Size (16),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x0A, 0x32, 0x01, # . Usage (0132h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x3F, # . Logical Maximum (63),
0x75, 0x08, # . Report Size (8),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x09, 0x5B, # . Usage (5Bh),
0x09, 0x5C, # . Usage (5Ch),
0x17, 0x00, 0x00, 0x00, 0x80, # . Logical Minimum (-2147483648),
0x27, 0xFF, 0xFF, 0xFF, 0x7F, # . Logical Maximum (2147483647),
0x75, 0x20, # . Report Size (32),
0x95, 0x02, # . Report Count (2),
0x81, 0x02, # . Input (Variable),
0x09, 0x77, # . Usage (77h),
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x0F, # . Logical Maximum (4095),
0x75, 0x10, # . Report Size (16),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0xC0, # . End Collection,
0x85, 0x11, # . Report ID (17),
0x09, 0x39, # . Usage (39h),
0xA1, 0x00, # . Collection (Physical),
0x1A, 0x10, 0x09, # . Usage Minimum (0910h),
0x2A, 0x17, 0x09, # . Usage Maximum (0917h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x08, # . Report Count (8),
0x81, 0x02, # . Input (Variable),
0x1A, 0x40, 0x09, # . Usage Minimum (0940h),
0x2A, 0x47, 0x09, # . Usage Maximum (0947h),
0x81, 0x02, # . Input (Variable),
0x0A, 0x95, 0x09, # . Usage (0995h),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x95, 0x07, # . Report Count (7),
0x81, 0x03, # . Input (Constant, Variable),
0x0A, 0x38, 0x01, # . Usage (0138h),
0x65, 0x14, # . Unit (Degrees),
0x55, 0x00, # . Unit Exponent (0),
0x35, 0x00, # . Physical Minimum (0),
0x46, 0x67, 0x01, # . Physical Maximum (359),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x47, # . Logical Maximum (71),
0x75, 0x07, # . Report Size (7),
0x95, 0x01, # . Report Count (1),
0x81, 0x0A, # . Input (Variable, Wrap),
0x0A, 0x39, 0x01, # . Usage (0139h),
0x65, 0x00, # . Unit,
0x55, 0x00, # . Unit Exponent (0),
0x25, 0x01, # . Logical Maximum (1), # !!! Errata: Missing Physical Max = 0
0x75, 0x01, # . Report Size (1),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x75, 0x08, # . Report Size (8),
0x95, 0x04, # . Report Count (4),
0x81, 0x03, # . Input (Constant, Variable),
0xC0, # . End Collection,
0x85, 0x13, # . Report ID (19),
0x0A, 0x13, 0x10, # . Usage (1013h),
0xA1, 0x00, # . Collection (Physical),
0x0A, 0x3B, 0x04, # . Usage (043Bh),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x64, # . Logical Maximum (100),
0x75, 0x07, # . Report Size (7),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x0A, 0x04, 0x04, # . Usage (0404h),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x81, 0x02, # . Input (Variable),
0x0A, 0x52, 0x04, # . Usage (0452h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x95, 0x06, # . Report Count (6),
0x81, 0x03, # . Input (Constant, Variable),
0x0A, 0x54, 0x04, # . Usage (0454h),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x75, 0x08, # . Report Size (8),
0x95, 0x06, # . Report Count (6),
0x81, 0x03, # . Input (Constant, Variable),
0xC0, # . End Collection,
0x09, 0x0E, # . Usage (0Eh),
0xA1, 0x02, # . Collection (Logical),
0x15, 0x00, # . Logical Minimum (0),
0x85, 0x02, # . Report ID (2),
0x09, 0x01, # . Usage (01h),
0x75, 0x08, # . Report Size (8),
0x25, 0x01, # . Logical Maximum (1),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x03, # . Report ID (3),
0x0A, 0x03, 0x10, # . Usage (1003h),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x04, # . Report ID (4),
0x0A, 0x04, 0x10, # . Usage (1004h),
0x25, 0x01, # . Logical Maximum (1),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x07, # . Report ID (7),
0x0A, 0x09, 0x10, # . Usage (1009h),
0x25, 0x02, # . Logical Maximum (2),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x95, 0x01, # . Report Count (1),
0xB1, 0x03, # . Feature (Constant, Variable),
0x0A, 0x07, 0x10, # . Usage (1007h),
0x09, 0x00, # . Usage (00h),
0x0A, 0x08, 0x10, # . Usage (1008h),
0x09, 0x00, # . Usage (00h),
0x09, 0x00, # . Usage (00h),
0x09, 0x00, # . Usage (00h),
0x27, 0xFF, 0xFF, 0x00, 0x00, # . Logical Maximum (65535),
0x75, 0x10, # . Report Size (16),
0x95, 0x06, # . Report Count (6),
0xB1, 0x02, # . Feature (Variable),
0x75, 0x08, # . Report Size (8),
0x95, 0x01, # . Report Count (1),
0xB1, 0x03, # . Feature (Constant, Variable),
0x85, 0x0C, # . Report ID (12),
0x0A, 0x30, 0x0D, # . Usage (0D30h),
0x0A, 0x31, 0x0D, # . Usage (0D31h),
0x0A, 0x32, 0x0D, # . Usage (0D32h),
0x0A, 0x33, 0x0D, # . Usage (0D33h), # !!! Errata: Missing Non-zero Physical Max
0x65, 0x11, # . Unit (Centimeter),
0x55, 0x0D, # . Unit Exponent (13),
0x75, 0x10, # . Report Size (16),
0x95, 0x04, # . Report Count (4),
0xB1, 0x03, # . Feature (Constant, Variable),
0x85, 0x0D, # . Report ID (13),
0x65, 0x00, # . Unit,
0x55, 0x00, # . Unit Exponent (0),
0x0A, 0x0D, 0x10, # . Usage (100Dh),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x08, # . Report Size (8),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x14, # . Report ID (20),
0x0A, 0x14, 0x10, # . Usage (1014h),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x95, 0x0D, # . Report Count (13),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x31, # . Report ID (49),
0x0A, 0x31, 0x10, # . Usage (1031h),
0x25, 0x64, # . Logical Maximum (100),
0x95, 0x05, # . Report Count (5),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x32, # . Report ID (50),
0x0A, 0x31, 0x10, # . Usage (1031h),
0x25, 0x64, # . Logical Maximum (100),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x0A, 0x32, 0x10, # . Usage (1032h),
0x25, 0x03, # . Logical Maximum (3),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x34, # . Report ID (52),
0x0A, 0x34, 0x10, # . Usage (1034h),
0x25, 0x01, # . Logical Maximum (1),
0x95, 0x04, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x35, # . Report ID (53),
0x0A, 0x35, 0x10, # . Usage (1035h),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x95, 0x0A, # . Report Count (10),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x36, # . Report ID (54),
0x0A, 0x35, 0x10, # . Usage (1035h),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x96, 0x01, 0x01, # . Report Count (257),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xCC, # . Report ID (204),
0x0A, 0xCC, 0x10, # . Usage (10CCh),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x95, 0x02, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0xC0, # . End Collection,
0x0A, 0xAC, 0x10, # . Usage (10ACh),
0xA1, 0x02, # . Collection (Logical),
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x75, 0x08, # . Report Size (8),
0x85, 0xAC, # . Report ID (172),
0x09, 0x00, # . Usage (00h),
0x95, 0xBF, # . Report Count (191),
0x81, 0x02, # . Input (Variable),
0x85, 0x33, # . Report ID (51),
0x09, 0x00, # . Usage (00h),
0x95, 0x12, # . Report Count (18),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x64, # . Report ID (100),
0x09, 0x00, # . Usage (00h),
0x95, 0x0C, # . Report Count (12),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x15, # . Report ID (21),
0x09, 0x00, # . Usage (00h),
0x95, 0x0E, # . Report Count (14),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x12, # . Report ID (18),
0x09, 0x00, # . Usage (00h),
0x95, 0x04, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x16, # . Report ID (22),
0x09, 0x00, # . Usage (00h),
0x95, 0x0E, # . Report Count (14),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x40, # . Report ID (64),
0x09, 0x00, # . Usage (00h),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x41, # . Report ID (65),
0x09, 0x00, # . Usage (00h),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x42, # . Report ID (66),
0x09, 0x00, # . Usage (00h),
0x95, 0x04, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x43, # . Report ID (67),
0x09, 0x00, # . Usage (00h),
0x95, 0x0D, # . Report Count (13),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x44, # . Report ID (68),
0x09, 0x00, # . Usage (00h),
0x95, 0x3F, # . Report Count (63),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x45, # . Report ID (69),
0x09, 0x00, # . Usage (00h),
0x95, 0x20, # . Report Count (32),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x60, # . Report ID (96),
0x09, 0x00, # . Usage (00h),
0x95, 0x3F, # . Report Count (63),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x61, # . Report ID (97),
0x09, 0x00, # . Usage (00h),
0x95, 0x3E, # . Report Count (62),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x62, # . Report ID (98),
0x09, 0x00, # . Usage (00h),
0x95, 0x3E, # . Report Count (62),
0xB1, 0x02, # . Feature (Variable),
0xC0, # . End Collection,
0x85, 0xD0, # . Report ID (208),
0x09, 0x01, # . Usage (01h),
0x96, 0x08, 0x00, # . Report Count (8),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD1, # . Report ID (209),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x01, # . Report Count (260),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD2, # . Report ID (210),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x01, # . Report Count (260),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD3, # . Report ID (211),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD4, # . Report ID (212),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD5, # . Report ID (213),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD6, # . Report ID (214),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD7, # . Report ID (215),
0x09, 0x01, # . Usage (01h),
0x96, 0x08, 0x00, # . Report Count (8),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD8, # . Report ID (216),
0x09, 0x01, # . Usage (01h),
0x96, 0x0C, 0x00, # . Report Count (12),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD9, # . Report ID (217),
0x09, 0x01, # . Usage (01h),
0x96, 0x00, 0x0A, # . Report Count (2560),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDA, # . Report ID (218),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x04, # . Report Count (1028),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDB, # . Report ID (219),
0x09, 0x01, # . Usage (01h),
0x96, 0x06, 0x00, # . Report Count (6),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDC, # . Report ID (220),
0x09, 0x01, # . Usage (01h),
0x96, 0x02, 0x00, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDD, # . Report ID (221),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDE, # . Report ID (222),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDF, # . Report ID (223),
0x09, 0x01, # . Usage (01h),
0x96, 0x22, 0x00, # . Report Count (34),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE0, # . Report ID (224),
0x09, 0x01, # . Usage (01h),
0x96, 0x01, 0x00, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE1, # . Report ID (225),
0x09, 0x01, # . Usage (01h),
0x96, 0x02, 0x00, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE2, # . Report ID (226),
0x09, 0x01, # . Usage (01h),
0x96, 0x02, 0x00, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE3, # . Report ID (227),
0x09, 0x01, # . Usage (01h),
0x96, 0x02, 0x00, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE4, # . Report ID (228),
0x09, 0x01, # . Usage (01h),
0x96, 0xFF, 0x01, # . Report Count (511),
0xB1, 0x02, # . Feature (Variable),
0xC0 # . End Collection
]
# fmt: on
# Report ID (20), Usage (1014h), Report Count (13) -> 15
wacom_pth660_v150 = wacom_pth660_v145.copy()
wacom_pth660_v150[0x2CB] = 0x0F
# fmt: off
wacom_pth860_v145 = [
0x05, 0x01, # . Usage Page (Desktop),
0x09, 0x02, # . Usage (Mouse),
0xA1, 0x01, # . Collection (Application),
0x85, 0x01, # . Report ID (1),
0x09, 0x01, # . Usage (Pointer),
0xA1, 0x00, # . Collection (Physical),
0x05, 0x09, # . Usage Page (Button),
0x19, 0x01, # . Usage Minimum (01h),
0x29, 0x03, # . Usage Maximum (03h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x03, # . Report Count (3),
0x81, 0x02, # . Input (Variable),
0x95, 0x05, # . Report Count (5),
0x81, 0x03, # . Input (Constant, Variable),
0x05, 0x01, # . Usage Page (Desktop),
0x09, 0x30, # . Usage (X),
0x09, 0x31, # . Usage (Y),
0x15, 0x80, # . Logical Minimum (-128),
0x25, 0x7F, # . Logical Maximum (127),
0x75, 0x08, # . Report Size (8),
0x95, 0x02, # . Report Count (2),
0x81, 0x06, # . Input (Variable, Relative),
0xC0, # . End Collection,
0xC0, # . End Collection,
0x06, 0x0D, 0xFF, # . Usage Page (FF0Dh),
0x09, 0x01, # . Usage (01h),
0xA1, 0x01, # . Collection (Application),
0x85, 0x10, # . Report ID (16),
0x09, 0x20, # . Usage (20h),
0xA1, 0x00, # . Collection (Physical),
0x09, 0x42, # . Usage (42h),
0x09, 0x44, # . Usage (44h),
0x09, 0x5A, # . Usage (5Ah),
0x09, 0x45, # . Usage (45h),
0x09, 0x3C, # . Usage (3Ch),
0x09, 0x32, # . Usage (32h),
0x09, 0x36, # . Usage (36h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x07, # . Report Count (7),
0x81, 0x02, # . Input (Variable),
0x95, 0x01, # . Report Count (1),
0x81, 0x03, # . Input (Constant, Variable),
0x0A, 0x30, 0x01, # . Usage (0130h),
0x65, 0x11, # . Unit (Centimeter),
0x55, 0x0D, # . Unit Exponent (13),
0x35, 0x00, # . Physical Minimum (0),
0x47, 0x7C, 0x79, 0x00, 0x00, # . Physical Maximum (31100),
0x15, 0x00, # . Logical Minimum (0),
0x27, 0xF8, 0xF2, 0x00, 0x00, # . Logical Maximum (62200),
0x75, 0x18, # . Report Size (24),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x0A, 0x31, 0x01, # . Usage (0131h),
0x47, 0x60, 0x54, 0x00, 0x00, # . Physical Maximum (21600),
0x27, 0xC0, 0xA8, 0x00, 0x00, # . Logical Maximum (43200),
0x81, 0x02, # . Input (Variable),
0x09, 0x30, # . Usage (30h), # !!! Errata: Missing Physical Max = 0
0x55, 0x00, # . Unit Exponent (0),
0x65, 0x00, # . Unit,
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x1F, # . Logical Maximum (8191),
0x75, 0x10, # . Report Size (16),
0x81, 0x02, # . Input (Variable),
0x09, 0x3D, # . Usage (3Dh),
0x09, 0x3E, # . Usage (3Eh),
0x65, 0x14, # . Unit (Degrees),
0x55, 0x00, # . Unit Exponent (0),
0x35, 0xC0, # . Physical Minimum (-64),
0x45, 0x3F, # . Physical Maximum (63),
0x15, 0xC0, # . Logical Minimum (-64),
0x25, 0x3F, # . Logical Maximum (63),
0x75, 0x08, # . Report Size (8),
0x95, 0x02, # . Report Count (2),
0x81, 0x02, # . Input (Variable),
0x09, 0x41, # . Usage (41h),
0x65, 0x14, # . Unit (Degrees),
0x55, 0x00, # . Unit Exponent (0),
0x36, 0x4C, 0xFF, # . Physical Minimum (-180),
0x46, 0xB3, 0x00, # . Physical Maximum (179),
0x16, 0x7C, 0xFC, # . Logical Minimum (-900),
0x26, 0x83, 0x03, # . Logical Maximum (899),
0x75, 0x10, # . Report Size (16),
0x95, 0x01, # . Report Count (1),
0x81, 0x0A, # . Input (Variable, Wrap),
0x0A, 0x03, 0x0D, # . Usage (0D03h),
0x65, 0x00, # . Unit,
0x55, 0x00, # . Unit Exponent (0),
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x07, # . Logical Maximum (2047), # !!! Errata: Missing Physical Min/Max = 0
0x75, 0x10, # . Report Size (16),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x0A, 0x32, 0x01, # . Usage (0132h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x3F, # . Logical Maximum (63),
0x75, 0x08, # . Report Size (8),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x09, 0x5B, # . Usage (5Bh),
0x09, 0x5C, # . Usage (5Ch),
0x17, 0x00, 0x00, 0x00, 0x80, # . Logical Minimum (-2147483648),
0x27, 0xFF, 0xFF, 0xFF, 0x7F, # . Logical Maximum (2147483647),
0x75, 0x20, # . Report Size (32),
0x95, 0x02, # . Report Count (2),
0x81, 0x02, # . Input (Variable),
0x09, 0x77, # . Usage (77h),
0x16, 0x00, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x0F, # . Logical Maximum (4095),
0x75, 0x10, # . Report Size (16),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0xC0, # . End Collection,
0x85, 0x11, # . Report ID (17),
0x09, 0x39, # . Usage (39h),
0xA1, 0x00, # . Collection (Physical),
0x1A, 0x10, 0x09, # . Usage Minimum (0910h),
0x2A, 0x17, 0x09, # . Usage Maximum (0917h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x08, # . Report Count (8),
0x81, 0x02, # . Input (Variable),
0x1A, 0x40, 0x09, # . Usage Minimum (0940h),
0x2A, 0x47, 0x09, # . Usage Maximum (0947h),
0x81, 0x02, # . Input (Variable),
0x0A, 0x95, 0x09, # . Usage (0995h),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x95, 0x07, # . Report Count (7),
0x81, 0x03, # . Input (Constant, Variable),
0x0A, 0x38, 0x01, # . Usage (0138h),
0x65, 0x14, # . Unit (Degrees),
0x55, 0x00, # . Unit Exponent (0),
0x35, 0x00, # . Physical Minimum (0),
0x46, 0x67, 0x01, # . Physical Maximum (359),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x47, # . Logical Maximum (71),
0x75, 0x07, # . Report Size (7),
0x95, 0x01, # . Report Count (1),
0x81, 0x0A, # . Input (Variable, Wrap),
0x0A, 0x39, 0x01, # . Usage (0139h),
0x65, 0x00, # . Unit,
0x55, 0x00, # . Unit Exponent (0),
0x25, 0x01, # . Logical Maximum (1), # !!! Errata: Missing Physical Max = 0
0x75, 0x01, # . Report Size (1),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x75, 0x08, # . Report Size (8),
0x95, 0x04, # . Report Count (4),
0x81, 0x03, # . Input (Constant, Variable),
0xC0, # . End Collection,
0x85, 0x13, # . Report ID (19),
0x0A, 0x13, 0x10, # . Usage (1013h),
0xA1, 0x00, # . Collection (Physical),
0x0A, 0x3B, 0x04, # . Usage (043Bh),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x64, # . Logical Maximum (100),
0x75, 0x07, # . Report Size (7),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x0A, 0x04, 0x04, # . Usage (0404h),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x81, 0x02, # . Input (Variable),
0x0A, 0x52, 0x04, # . Usage (0452h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x95, 0x06, # . Report Count (6),
0x81, 0x03, # . Input (Constant, Variable),
0x0A, 0x54, 0x04, # . Usage (0454h),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x75, 0x08, # . Report Size (8),
0x95, 0x06, # . Report Count (6),
0x81, 0x03, # . Input (Constant, Variable),
0xC0, # . End Collection,
0x09, 0x0E, # . Usage (0Eh),
0xA1, 0x02, # . Collection (Logical),
0x15, 0x00, # . Logical Minimum (0),
0x85, 0x02, # . Report ID (2),
0x09, 0x01, # . Usage (01h),
0x75, 0x08, # . Report Size (8),
0x25, 0x01, # . Logical Maximum (1),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x03, # . Report ID (3),
0x0A, 0x03, 0x10, # . Usage (1003h),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x04, # . Report ID (4),
0x0A, 0x04, 0x10, # . Usage (1004h),
0x25, 0x01, # . Logical Maximum (1),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x07, # . Report ID (7),
0x0A, 0x09, 0x10, # . Usage (1009h),
0x25, 0x02, # . Logical Maximum (2),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x95, 0x01, # . Report Count (1),
0xB1, 0x03, # . Feature (Constant, Variable),
0x0A, 0x07, 0x10, # . Usage (1007h),
0x09, 0x00, # . Usage (00h),
0x0A, 0x08, 0x10, # . Usage (1008h),
0x09, 0x00, # . Usage (00h),
0x09, 0x00, # . Usage (00h),
0x09, 0x00, # . Usage (00h),
0x27, 0xFF, 0xFF, 0x00, 0x00, # . Logical Maximum (65535),
0x75, 0x10, # . Report Size (16),
0x95, 0x06, # . Report Count (6),
0xB1, 0x02, # . Feature (Variable),
0x75, 0x08, # . Report Size (8),
0x95, 0x01, # . Report Count (1),
0xB1, 0x03, # . Feature (Constant, Variable),
0x85, 0x0C, # . Report ID (12),
0x0A, 0x30, 0x0D, # . Usage (0D30h),
0x0A, 0x31, 0x0D, # . Usage (0D31h),
0x0A, 0x32, 0x0D, # . Usage (0D32h),
0x0A, 0x33, 0x0D, # . Usage (0D33h), # !!! Errata: Missing Non-zero Physical Max
0x65, 0x11, # . Unit (Centimeter),
0x55, 0x0D, # . Unit Exponent (13),
0x75, 0x10, # . Report Size (16),
0x95, 0x04, # . Report Count (4),
0xB1, 0x03, # . Feature (Constant, Variable),
0x85, 0x0D, # . Report ID (13),
0x65, 0x00, # . Unit,
0x55, 0x00, # . Unit Exponent (0),
0x0A, 0x0D, 0x10, # . Usage (100Dh),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x08, # . Report Size (8),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x14, # . Report ID (20),
0x0A, 0x14, 0x10, # . Usage (1014h),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x95, 0x0D, # . Report Count (13),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x31, # . Report ID (49),
0x0A, 0x31, 0x10, # . Usage (1031h),
0x25, 0x64, # . Logical Maximum (100),
0x95, 0x05, # . Report Count (5),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x32, # . Report ID (50),
0x0A, 0x31, 0x10, # . Usage (1031h),
0x25, 0x64, # . Logical Maximum (100),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x0A, 0x32, 0x10, # . Usage (1032h),
0x25, 0x03, # . Logical Maximum (3),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x34, # . Report ID (52),
0x0A, 0x34, 0x10, # . Usage (1034h),
0x25, 0x01, # . Logical Maximum (1),
0x95, 0x04, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x35, # . Report ID (53),
0x0A, 0x35, 0x10, # . Usage (1035h),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x95, 0x0A, # . Report Count (10),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x36, # . Report ID (54),
0x0A, 0x35, 0x10, # . Usage (1035h),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x96, 0x01, 0x01, # . Report Count (257),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xCC, # . Report ID (204),
0x0A, 0xCC, 0x10, # . Usage (10CCh),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x95, 0x02, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0xC0, # . End Collection,
0x0A, 0xAC, 0x10, # . Usage (10ACh),
0xA1, 0x02, # . Collection (Logical),
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x75, 0x08, # . Report Size (8),
0x85, 0xAC, # . Report ID (172),
0x09, 0x00, # . Usage (00h),
0x95, 0xBF, # . Report Count (191),
0x81, 0x02, # . Input (Variable),
0x85, 0x33, # . Report ID (51),
0x09, 0x00, # . Usage (00h),
0x95, 0x12, # . Report Count (18),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x64, # . Report ID (100),
0x09, 0x00, # . Usage (00h),
0x95, 0x0C, # . Report Count (12),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x15, # . Report ID (21),
0x09, 0x00, # . Usage (00h),
0x95, 0x0E, # . Report Count (14),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x12, # . Report ID (18),
0x09, 0x00, # . Usage (00h),
0x95, 0x04, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x16, # . Report ID (22),
0x09, 0x00, # . Usage (00h),
0x95, 0x0E, # . Report Count (14),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x40, # . Report ID (64),
0x09, 0x00, # . Usage (00h),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x41, # . Report ID (65),
0x09, 0x00, # . Usage (00h),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x42, # . Report ID (66),
0x09, 0x00, # . Usage (00h),
0x95, 0x04, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x43, # . Report ID (67),
0x09, 0x00, # . Usage (00h),
0x95, 0x0D, # . Report Count (13),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x44, # . Report ID (68),
0x09, 0x00, # . Usage (00h),
0x95, 0x3F, # . Report Count (63),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x45, # . Report ID (69),
0x09, 0x00, # . Usage (00h),
0x95, 0x20, # . Report Count (32),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x60, # . Report ID (96),
0x09, 0x00, # . Usage (00h),
0x95, 0x3F, # . Report Count (63),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x61, # . Report ID (97),
0x09, 0x00, # . Usage (00h),
0x95, 0x3E, # . Report Count (62),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x62, # . Report ID (98),
0x09, 0x00, # . Usage (00h),
0x95, 0x3E, # . Report Count (62),
0xB1, 0x02, # . Feature (Variable),
0xC0, # . End Collection,
0x85, 0xD0, # . Report ID (208),
0x09, 0x01, # . Usage (01h),
0x96, 0x08, 0x00, # . Report Count (8),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD1, # . Report ID (209),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x01, # . Report Count (260),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD2, # . Report ID (210),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x01, # . Report Count (260),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD3, # . Report ID (211),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD4, # . Report ID (212),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD5, # . Report ID (213),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD6, # . Report ID (214),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD7, # . Report ID (215),
0x09, 0x01, # . Usage (01h),
0x96, 0x08, 0x00, # . Report Count (8),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD8, # . Report ID (216),
0x09, 0x01, # . Usage (01h),
0x96, 0x0C, 0x00, # . Report Count (12),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD9, # . Report ID (217),
0x09, 0x01, # . Usage (01h),
0x96, 0x00, 0x0A, # . Report Count (2560),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDA, # . Report ID (218),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x04, # . Report Count (1028),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDB, # . Report ID (219),
0x09, 0x01, # . Usage (01h),
0x96, 0x06, 0x00, # . Report Count (6),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDC, # . Report ID (220),
0x09, 0x01, # . Usage (01h),
0x96, 0x02, 0x00, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDD, # . Report ID (221),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDE, # . Report ID (222),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDF, # . Report ID (223),
0x09, 0x01, # . Usage (01h),
0x96, 0x22, 0x00, # . Report Count (34),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE0, # . Report ID (224),
0x09, 0x01, # . Usage (01h),
0x96, 0x01, 0x00, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE1, # . Report ID (225),
0x09, 0x01, # . Usage (01h),
0x96, 0x02, 0x00, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE2, # . Report ID (226),
0x09, 0x01, # . Usage (01h),
0x96, 0x02, 0x00, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE3, # . Report ID (227),
0x09, 0x01, # . Usage (01h),
0x96, 0x02, 0x00, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE4, # . Report ID (228),
0x09, 0x01, # . Usage (01h),
0x96, 0xFF, 0x01, # . Report Count (511),
0xB1, 0x02, # . Feature (Variable),
0xC0 # . End Collection
]
# fmt: on
# Report ID (20), Usage (1014h), Report Count (13) -> 15
wacom_pth860_v150 = wacom_pth860_v145.copy()
wacom_pth860_v150[0x2CA] = 0x0F
# fmt: off
wacom_pth460_v105 = [
0x06, 0x0D, 0xFF, # . Usage Page (FF0Dh),
0x09, 0x01, # . Usage (01h),
0xA1, 0x01, # . Collection (Application),
0x85, 0x10, # . Report ID (16),
0x09, 0x20, # . Usage (20h),
0x35, 0x00, # . Physical Minimum (0),
0x45, 0x00, # . Physical Maximum (0),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0xA1, 0x00, # . Collection (Physical),
0x09, 0x42, # . Usage (42h),
0x09, 0x44, # . Usage (44h),
0x09, 0x5A, # . Usage (5Ah),
0x09, 0x45, # . Usage (45h),
0x09, 0x3C, # . Usage (3Ch),
0x09, 0x32, # . Usage (32h),
0x09, 0x36, # . Usage (36h),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x07, # . Report Count (7),
0x81, 0x02, # . Input (Variable),
0x95, 0x01, # . Report Count (1),
0x81, 0x03, # . Input (Constant, Variable),
0x0A, 0x30, 0x01, # . Usage (0130h),
0x65, 0x11, # . Unit (Centimeter),
0x55, 0x0D, # . Unit Exponent (13),
0x47, 0x58, 0x3E, 0x00, 0x00, # . Physical Maximum (15960),
0x27, 0xB0, 0x7C, 0x00, 0x00, # . Logical Maximum (31920),
0x75, 0x18, # . Report Size (24),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x0A, 0x31, 0x01, # . Usage (0131h),
0x47, 0xF7, 0x26, 0x00, 0x00, # . Physical Maximum (9975),
0x27, 0xEE, 0x4D, 0x00, 0x00, # . Logical Maximum (19950),
0x81, 0x02, # . Input (Variable),
0x09, 0x30, # . Usage (30h),
0x55, 0x00, # . Unit Exponent (0),
0x65, 0x00, # . Unit,
0x26, 0xFF, 0x1F, # . Logical Maximum (8191), # !!! Errata: Missing Physical Max = 0
0x75, 0x10, # . Report Size (16),
0x81, 0x02, # . Input (Variable),
0x09, 0x3D, # . Usage (3Dh),
0x09, 0x3E, # . Usage (3Eh),
0x65, 0x14, # . Unit (Degrees),
0x55, 0x00, # . Unit Exponent (0),
0x35, 0xC0, # . Physical Minimum (-64),
0x45, 0x3F, # . Physical Maximum (63),
0x15, 0xC0, # . Logical Minimum (-64),
0x25, 0x3F, # . Logical Maximum (63),
0x75, 0x08, # . Report Size (8),
0x95, 0x02, # . Report Count (2),
0x81, 0x02, # . Input (Variable),
0x09, 0x41, # . Usage (41h),
0x65, 0x14, # . Unit (Degrees),
0x55, 0x00, # . Unit Exponent (0),
0x36, 0x4C, 0xFF, # . Physical Minimum (-180),
0x46, 0xB3, 0x00, # . Physical Maximum (179),
0x16, 0x7C, 0xFC, # . Logical Minimum (-900),
0x26, 0x83, 0x03, # . Logical Maximum (899),
0x75, 0x10, # . Report Size (16),
0x95, 0x01, # . Report Count (1),
0x81, 0x0A, # . Input (Variable, Wrap),
0x0A, 0x03, 0x0D, # . Usage (0D03h),
0x65, 0x00, # . Unit,
0x55, 0x00, # . Unit Exponent (0),
0x35, 0x00, # . Physical Minimum (0),
0x45, 0x00, # . Physical Maximum (0),
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x07, # . Logical Maximum (2047),
0x81, 0x02, # . Input (Variable),
0x0A, 0x32, 0x01, # . Usage (0132h),
0x25, 0x3F, # . Logical Maximum (63),
0x75, 0x08, # . Report Size (8),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x09, 0x5B, # . Usage (5Bh),
0x09, 0x5C, # . Usage (5Ch),
0x17, 0x00, 0x00, 0x00, 0x80, # . Logical Minimum (-2147483648),
0x27, 0xFF, 0xFF, 0xFF, 0x7F, # . Logical Maximum (2147483647),
0x75, 0x20, # . Report Size (32),
0x95, 0x02, # . Report Count (2),
0x81, 0x02, # . Input (Variable),
0x09, 0x77, # . Usage (77h),
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x0F, # . Logical Maximum (4095),
0x75, 0x10, # . Report Size (16),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0xC0, # . End Collection,
0x85, 0x11, # . Report ID (17),
0x65, 0x00, # . Unit,
0x55, 0x00, # . Unit Exponent (0),
0x35, 0x00, # . Physical Minimum (0),
0x45, 0x00, # . Physical Maximum (0),
0x09, 0x39, # . Usage (39h),
0xA1, 0x00, # . Collection (Physical),
0x09, 0x39, # . Usage (39h),
0xA1, 0x00, # . Collection (Physical),
0x35, 0x00, # . Physical Minimum (0),
0x45, 0x00, # . Physical Maximum (0),
0x15, 0x00, # . Logical Minimum (0),
0x1A, 0x10, 0x09, # . Usage Minimum (0910h),
0x2A, 0x15, 0x09, # . Usage Maximum (0915h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x06, # . Report Count (6),
0x81, 0x02, # . Input (Variable),
0x95, 0x02, # . Report Count (2),
0x81, 0x03, # . Input (Constant, Variable),
0xC0, # . End Collection,
0x75, 0x08, # . Report Size (8),
0x95, 0x01, # . Report Count (1),
0x81, 0x03, # . Input (Constant, Variable),
0x09, 0x39, # . Usage (39h),
0xA1, 0x00, # . Collection (Physical),
0x35, 0x00, # . Physical Minimum (0),
0x45, 0x00, # . Physical Maximum (0),
0x0A, 0x95, 0x09, # . Usage (0995h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x95, 0x07, # . Report Count (7),
0x81, 0x03, # . Input (Constant, Variable),
0xC0, # . End Collection,
0x09, 0x39, # . Usage (39h),
0xA1, 0x00, # . Collection (Physical),
0x35, 0x00, # . Physical Minimum (0),
0x15, 0x00, # . Logical Minimum (0),
0x0A, 0x38, 0x01, # . Usage (0138h),
0x65, 0x14, # . Unit (Degrees),
0x55, 0x00, # . Unit Exponent (0),
0x35, 0x00, # . Physical Minimum (0),
0x46, 0x67, 0x01, # . Physical Maximum (359),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x47, # . Logical Maximum (71),
0x75, 0x07, # . Report Size (7),
0x95, 0x01, # . Report Count (1),
0x81, 0x4A, # . Input (Variable, Wrap, Null State),
0x0A, 0x39, 0x01, # . Usage (0139h),
0x65, 0x00, # . Unit,
0x55, 0x00, # . Unit Exponent (0),
0x45, 0x00, # . Physical Maximum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0xC0, # . End Collection,
0x75, 0x08, # . Report Size (8),
0x95, 0x04, # . Report Count (4),
0x81, 0x03, # . Input (Constant, Variable),
0xC0, # . End Collection,
0x85, 0x13, # . Report ID (19),
0x65, 0x00, # . Unit,
0x55, 0x00, # . Unit Exponent (0),
0x35, 0x00, # . Physical Minimum (0),
0x45, 0x00, # . Physical Maximum (0),
0x0A, 0x13, 0x10, # . Usage (1013h),
0xA1, 0x00, # . Collection (Physical),
0x0A, 0x13, 0x10, # . Usage (1013h),
0xA1, 0x00, # . Collection (Physical),
0x35, 0x00, # . Physical Minimum (0),
0x45, 0x00, # . Physical Maximum (0),
0x15, 0x00, # . Logical Minimum (0),
0x0A, 0x3B, 0x04, # . Usage (043Bh),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x64, # . Logical Maximum (100),
0x75, 0x07, # . Report Size (7),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x0A, 0x04, 0x04, # . Usage (0404h),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x81, 0x02, # . Input (Variable),
0xC0, # . End Collection,
0x0A, 0x13, 0x10, # . Usage (1013h),
0xA1, 0x00, # . Collection (Physical),
0x35, 0x00, # . Physical Minimum (0),
0x45, 0x00, # . Physical Maximum (0),
0x0A, 0x52, 0x04, # . Usage (0452h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x0A, 0x41, 0x04, # . Usage (0441h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x07, # . Logical Maximum (7),
0x75, 0x03, # . Report Size (3),
0x95, 0x02, # . Report Count (2),
0x81, 0x02, # . Input (Variable),
0x0A, 0x54, 0x04, # . Usage (0454h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0xC0, # . End Collection,
0x0A, 0x13, 0x10, # . Usage (1013h),
0xA1, 0x00, # . Collection (Physical),
0x35, 0x00, # . Physical Minimum (0),
0x45, 0x00, # . Physical Maximum (0),
0x15, 0x00, # . Logical Minimum (0),
0x0A, 0x3C, 0x04, # . Usage (043Ch),
0x55, 0x00, # . Unit Exponent (0),
0x65, 0x00, # . Unit,
0x15, 0xFB, # . Logical Minimum (-5),
0x25, 0x32, # . Logical Maximum (50),
0x75, 0x08, # . Report Size (8),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0xC0, # . End Collection,
0x0A, 0x13, 0x10, # . Usage (1013h),
0xA1, 0x00, # . Collection (Physical),
0x35, 0x00, # . Physical Minimum (0),
0x45, 0x00, # . Physical Maximum (0),
0x15, 0x00, # . Logical Minimum (0),
0x0A, 0x3D, 0x04, # . Usage (043Dh),
0x55, 0x00, # . Unit Exponent (0),
0x65, 0x00, # . Unit,
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x0F, # . Logical Maximum (4095),
0x75, 0x10, # . Report Size (16),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0xC0, # . End Collection,
0x75, 0x08, # . Report Size (8),
0x95, 0x03, # . Report Count (3),
0x81, 0x03, # . Input (Constant, Variable),
0xC0, # . End Collection,
0x09, 0x0E, # . Usage (0Eh),
0xA1, 0x02, # . Collection (Logical),
0x85, 0x02, # . Report ID (2),
0x0A, 0x02, 0x10, # . Usage (1002h),
0x15, 0x02, # . Logical Minimum (2),
0x25, 0x02, # . Logical Maximum (2),
0x75, 0x08, # . Report Size (8),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x03, # . Report ID (3),
0x0A, 0x03, 0x10, # . Usage (1003h),
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x04, # . Report ID (4),
0x0A, 0x04, 0x10, # . Usage (1004h),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x07, # . Report ID (7),
0x0A, 0x09, 0x10, # . Usage (1009h),
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0xB1, 0x03, # . Feature (Constant, Variable),
0x0A, 0x07, 0x10, # . Usage (1007h),
0x09, 0x00, # . Usage (00h),
0x0A, 0x08, 0x10, # . Usage (1008h),
0x09, 0x00, # . Usage (00h),
0x09, 0x00, # . Usage (00h),
0x09, 0x00, # . Usage (00h),
0x27, 0xFF, 0xFF, 0x00, 0x00, # . Logical Maximum (65535),
0x75, 0x10, # . Report Size (16),
0x95, 0x06, # . Report Count (6),
0xB1, 0x02, # . Feature (Variable),
0x09, 0x00, # . Usage (00h),
0x25, 0x00, # . Logical Maximum (0),
0x75, 0x08, # . Report Size (8),
0x95, 0x01, # . Report Count (1),
0xB1, 0x03, # . Feature (Constant, Variable),
0x85, 0x0C, # . Report ID (12),
0x0A, 0x30, 0x0D, # . Usage (0D30h),
0x0A, 0x31, 0x0D, # . Usage (0D31h),
0x0A, 0x32, 0x0D, # . Usage (0D32h),
0x0A, 0x33, 0x0D, # . Usage (0D33h),
0x65, 0x11, # . Unit (Centimeter),
0x55, 0x0D, # . Unit Exponent (13),
0x35, 0x00, # . Physical Minimum (0),
0x46, 0xC8, 0x00, # . Physical Maximum (200),
0x15, 0x00, # . Logical Minimum (0),
0x26, 0x90, 0x01, # . Logical Maximum (400),
0x75, 0x10, # . Report Size (16),
0x95, 0x04, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x0D, # . Report ID (13),
0x0A, 0x0D, 0x10, # . Usage (100Dh),
0x65, 0x00, # . Unit,
0x55, 0x00, # . Unit Exponent (0),
0x45, 0x00, # . Physical Maximum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x08, # . Report Size (8),
0x95, 0x01, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x14, # . Report ID (20),
0x0A, 0x14, 0x10, # . Usage (1014h),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x95, 0x0D, # . Report Count (13),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xCC, # . Report ID (204),
0x0A, 0xCC, 0x10, # . Usage (10CCh),
0x95, 0x02, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0xC0, # . End Collection,
0x09, 0x0E, # . Usage (0Eh),
0xA1, 0x02, # . Collection (Logical),
0x85, 0x31, # . Report ID (49),
0x0A, 0x31, 0x10, # . Usage (1031h),
0x25, 0x64, # . Logical Maximum (100),
0x95, 0x03, # . Report Count (3),
0xB1, 0x02, # . Feature (Variable),
0x95, 0x02, # . Report Count (2),
0xB1, 0x03, # . Feature (Constant, Variable),
0xC0, # . End Collection,
0x0A, 0xAC, 0x10, # . Usage (10ACh),
0xA1, 0x02, # . Collection (Logical),
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x00, # . Logical Maximum (255),
0x75, 0x08, # . Report Size (8),
0x85, 0xAC, # . Report ID (172),
0x09, 0x00, # . Usage (00h),
0x96, 0xBF, 0x00, # . Report Count (191),
0x81, 0x02, # . Input (Variable),
0x85, 0x15, # . Report ID (21),
0x09, 0x00, # . Usage (00h),
0x95, 0x0E, # . Report Count (14),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x33, # . Report ID (51),
0x09, 0x00, # . Usage (00h),
0x95, 0x12, # . Report Count (18),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x44, # . Report ID (68),
0x09, 0x00, # . Usage (00h),
0x95, 0x04, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x45, # . Report ID (69),
0x09, 0x00, # . Usage (00h),
0x95, 0x20, # . Report Count (32),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x60, # . Report ID (96),
0x09, 0x00, # . Usage (00h),
0x95, 0x3F, # . Report Count (63),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x61, # . Report ID (97),
0x09, 0x00, # . Usage (00h),
0x95, 0x3E, # . Report Count (62),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x62, # . Report ID (98),
0x09, 0x00, # . Usage (00h),
0x95, 0x3E, # . Report Count (62),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x65, # . Report ID (101),
0x09, 0x00, # . Usage (00h),
0x95, 0x04, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x66, # . Report ID (102),
0x09, 0x00, # . Usage (00h),
0x95, 0x04, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x67, # . Report ID (103),
0x09, 0x00, # . Usage (00h),
0x95, 0x04, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x68, # . Report ID (104),
0x09, 0x00, # . Usage (00h),
0x95, 0x11, # . Report Count (17),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x6F, # . Report ID (111),
0x09, 0x00, # . Usage (00h),
0x95, 0x3E, # . Report Count (62),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xCD, # . Report ID (205),
0x09, 0x00, # . Usage (00h),
0x95, 0x02, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x16, # . Report ID (22),
0x09, 0x00, # . Usage (00h),
0x95, 0x0E, # . Report Count (14),
0xB1, 0x02, # . Feature (Variable),
0x85, 0x35, # . Report ID (53),
0x09, 0x00, # . Usage (00h),
0x95, 0x0A, # . Report Count (10),
0xB1, 0x02, # . Feature (Variable),
0xC0, # . End Collection,
0x85, 0xD1, # . Report ID (209),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x01, # . Report Count (260),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD2, # . Report ID (210),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x01, # . Report Count (260),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD3, # . Report ID (211),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD4, # . Report ID (212),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD5, # . Report ID (213),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD6, # . Report ID (214),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD7, # . Report ID (215),
0x09, 0x01, # . Usage (01h),
0x96, 0x08, 0x00, # . Report Count (8),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD8, # . Report ID (216),
0x09, 0x01, # . Usage (01h),
0x96, 0x0C, 0x00, # . Report Count (12),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xD9, # . Report ID (217),
0x09, 0x01, # . Usage (01h),
0x96, 0x00, 0x0A, # . Report Count (2560),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDA, # . Report ID (218),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x04, # . Report Count (1028),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDB, # . Report ID (219),
0x09, 0x01, # . Usage (01h),
0x96, 0x06, 0x00, # . Report Count (6),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDC, # . Report ID (220),
0x09, 0x01, # . Usage (01h),
0x96, 0x02, 0x00, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDD, # . Report ID (221),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDE, # . Report ID (222),
0x09, 0x01, # . Usage (01h),
0x96, 0x04, 0x00, # . Report Count (4),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xDF, # . Report ID (223),
0x09, 0x01, # . Usage (01h),
0x96, 0x22, 0x00, # . Report Count (34),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE0, # . Report ID (224),
0x09, 0x01, # . Usage (01h),
0x96, 0x01, 0x00, # . Report Count (1),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE1, # . Report ID (225),
0x09, 0x01, # . Usage (01h),
0x96, 0x02, 0x00, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE2, # . Report ID (226),
0x09, 0x01, # . Usage (01h),
0x96, 0x02, 0x00, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE3, # . Report ID (227),
0x09, 0x01, # . Usage (01h),
0x96, 0x02, 0x00, # . Report Count (2),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xE4, # . Report ID (228),
0x09, 0x01, # . Usage (01h),
0x96, 0xFF, 0x01, # . Report Count (511),
0xB1, 0x02, # . Feature (Variable),
0x85, 0xCB, # . Report ID (203),
0x09, 0x01, # . Usage (01h),
0x96, 0x1F, 0x00, # . Report Count (31),
0xB1, 0x02, # . Feature (Variable),
0xC0 # . End Collection
]
# fmt: on
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2019 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2019 Red Hat, Inc.
#
from .test_keyboard import ArrayKeyboard, TestArrayKeyboard
from hidtools.util import BusType
import libevdev
import logging
logger = logging.getLogger("hidtools.test.apple-keyboard")
KERNEL_MODULE = ("apple", "hid-apple")
class KbdData(object):
pass
class AppleKeyboard(ArrayKeyboard):
# fmt: off
report_descriptor = [
0x05, 0x01, # Usage Page (Generic Desktop)
0x09, 0x06, # Usage (Keyboard)
0xa1, 0x01, # Collection (Application)
0x85, 0x01, # .Report ID (1)
0x05, 0x07, # .Usage Page (Keyboard)
0x19, 0xe0, # .Usage Minimum (224)
0x29, 0xe7, # .Usage Maximum (231)
0x15, 0x00, # .Logical Minimum (0)
0x25, 0x01, # .Logical Maximum (1)
0x75, 0x01, # .Report Size (1)
0x95, 0x08, # .Report Count (8)
0x81, 0x02, # .Input (Data,Var,Abs)
0x75, 0x08, # .Report Size (8)
0x95, 0x01, # .Report Count (1)
0x81, 0x01, # .Input (Cnst,Arr,Abs)
0x75, 0x01, # .Report Size (1)
0x95, 0x05, # .Report Count (5)
0x05, 0x08, # .Usage Page (LEDs)
0x19, 0x01, # .Usage Minimum (1)
0x29, 0x05, # .Usage Maximum (5)
0x91, 0x02, # .Output (Data,Var,Abs)
0x75, 0x03, # .Report Size (3)
0x95, 0x01, # .Report Count (1)
0x91, 0x01, # .Output (Cnst,Arr,Abs)
0x75, 0x08, # .Report Size (8)
0x95, 0x06, # .Report Count (6)
0x15, 0x00, # .Logical Minimum (0)
0x26, 0xff, 0x00, # .Logical Maximum (255)
0x05, 0x07, # .Usage Page (Keyboard)
0x19, 0x00, # .Usage Minimum (0)
0x2a, 0xff, 0x00, # .Usage Maximum (255)
0x81, 0x00, # .Input (Data,Arr,Abs)
0xc0, # End Collection
0x05, 0x0c, # Usage Page (Consumer Devices)
0x09, 0x01, # Usage (Consumer Control)
0xa1, 0x01, # Collection (Application)
0x85, 0x47, # .Report ID (71)
0x05, 0x01, # .Usage Page (Generic Desktop)
0x09, 0x06, # .Usage (Keyboard)
0xa1, 0x02, # .Collection (Logical)
0x05, 0x06, # ..Usage Page (Generic Device Controls)
0x09, 0x20, # ..Usage (Battery Strength)
0x15, 0x00, # ..Logical Minimum (0)
0x26, 0xff, 0x00, # ..Logical Maximum (255)
0x75, 0x08, # ..Report Size (8)
0x95, 0x01, # ..Report Count (1)
0x81, 0x02, # ..Input (Data,Var,Abs)
0xc0, # .End Collection
0xc0, # End Collection
0x05, 0x0c, # Usage Page (Consumer Devices)
0x09, 0x01, # Usage (Consumer Control)
0xa1, 0x01, # Collection (Application)
0x85, 0x11, # .Report ID (17)
0x15, 0x00, # .Logical Minimum (0)
0x25, 0x01, # .Logical Maximum (1)
0x75, 0x01, # .Report Size (1)
0x95, 0x03, # .Report Count (3)
0x81, 0x01, # .Input (Cnst,Arr,Abs)
0x75, 0x01, # .Report Size (1)
0x95, 0x01, # .Report Count (1)
0x05, 0x0c, # .Usage Page (Consumer Devices)
0x09, 0xb8, # .Usage (Eject)
0x81, 0x02, # .Input (Data,Var,Abs)
0x06, 0xff, 0x00, # .Usage Page (Vendor Usage Page 0xff)
0x09, 0x03, # .Usage (Vendor Usage 0x03)
0x81, 0x02, # .Input (Data,Var,Abs)
0x75, 0x01, # .Report Size (1)
0x95, 0x03, # .Report Count (3)
0x81, 0x01, # .Input (Cnst,Arr,Abs)
0x05, 0x0c, # .Usage Page (Consumer Devices)
0x85, 0x12, # .Report ID (18)
0x15, 0x00, # .Logical Minimum (0)
0x25, 0x01, # .Logical Maximum (1)
0x75, 0x01, # .Report Size (1)
0x95, 0x01, # .Report Count (1)
0x09, 0xcd, # .Usage (Play/Pause)
0x81, 0x02, # .Input (Data,Var,Abs)
0x09, 0xb3, # .Usage (Fast Forward)
0x81, 0x02, # .Input (Data,Var,Abs)
0x09, 0xb4, # .Usage (Rewind)
0x81, 0x02, # .Input (Data,Var,Abs)
0x09, 0xb5, # .Usage (Scan Next Track)
0x81, 0x02, # .Input (Data,Var,Abs)
0x09, 0xb6, # .Usage (Scan Previous Track)
0x81, 0x02, # .Input (Data,Var,Abs)
0x81, 0x01, # .Input (Cnst,Arr,Abs)
0x81, 0x01, # .Input (Cnst,Arr,Abs)
0x81, 0x01, # .Input (Cnst,Arr,Abs)
0x85, 0x13, # .Report ID (19)
0x15, 0x00, # .Logical Minimum (0)
0x25, 0x01, # .Logical Maximum (1)
0x75, 0x01, # .Report Size (1)
0x95, 0x01, # .Report Count (1)
0x06, 0x01, 0xff, # .Usage Page (Vendor Usage Page 0xff01)
0x09, 0x0a, # .Usage (Vendor Usage 0x0a)
0x81, 0x02, # .Input (Data,Var,Abs)
0x06, 0x01, 0xff, # .Usage Page (Vendor Usage Page 0xff01)
0x09, 0x0c, # .Usage (Vendor Usage 0x0c)
0x81, 0x22, # .Input (Data,Var,Abs,NoPref)
0x75, 0x01, # .Report Size (1)
0x95, 0x06, # .Report Count (6)
0x81, 0x01, # .Input (Cnst,Arr,Abs)
0x85, 0x09, # .Report ID (9)
0x09, 0x0b, # .Usage (Vendor Usage 0x0b)
0x75, 0x08, # .Report Size (8)
0x95, 0x01, # .Report Count (1)
0xb1, 0x02, # .Feature (Data,Var,Abs)
0x75, 0x08, # .Report Size (8)
0x95, 0x02, # .Report Count (2)
0xb1, 0x01, # .Feature (Cnst,Arr,Abs)
0xc0, # End Collection
]
# fmt: on
def __init__(
self,
rdesc=report_descriptor,
name="Apple Wireless Keyboard",
input_info=(BusType.BLUETOOTH, 0x05AC, 0x0256),
):
super().__init__(rdesc, name, input_info)
self.default_reportID = 1
def send_fn_state(self, state):
data = KbdData()
setattr(data, "0xff0003", state)
r = self.create_report(data, reportID=17)
self.call_input_event(r)
return [r]
class TestAppleKeyboard(TestArrayKeyboard):
kernel_modules = [KERNEL_MODULE]
def create_device(self):
return AppleKeyboard()
def test_single_function_key(self):
"""check for function key reliability."""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
syn_event = self.syn_event
r = uhdev.event(["F4"])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0
r = uhdev.event([])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 0
def test_single_fn_function_key(self):
"""check for function key reliability with the fn key."""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
syn_event = self.syn_event
r = uhdev.send_fn_state(1)
r.extend(uhdev.event(["F4"]))
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_F4] == 1
r = uhdev.event([])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
r = uhdev.send_fn_state(0)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
def test_single_fn_function_key_release_first(self):
"""check for function key reliability with the fn key."""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
syn_event = self.syn_event
r = uhdev.send_fn_state(1)
r.extend(uhdev.event(["F4"]))
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_F4] == 1
r = uhdev.send_fn_state(0)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
r = uhdev.event([])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
def test_single_fn_function_key_inverted(self):
"""check for function key reliability with the fn key."""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
syn_event = self.syn_event
r = uhdev.event(["F4"])
r.extend(uhdev.send_fn_state(1))
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1
r = uhdev.event([])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 0
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
r = uhdev.send_fn_state(0)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
def test_multiple_fn_function_key_release_first(self):
"""check for function key reliability with the fn key."""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
syn_event = self.syn_event
r = uhdev.send_fn_state(1)
r.extend(uhdev.event(["F4"]))
r.extend(uhdev.event(["F4", "F6"]))
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_F4] == 1
assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
r = uhdev.event(["F6"])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F4, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
r = uhdev.send_fn_state(0)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0
r = uhdev.event([])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0
def test_multiple_fn_function_key_release_between(self):
"""check for function key reliability with the fn key."""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
syn_event = self.syn_event
# press F4
r = uhdev.event(["F4"])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1
assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0
assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0
# press Fn key
r = uhdev.send_fn_state(1)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1
assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0
assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
# keep F4 and press F6
r = uhdev.event(["F4", "F6"])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1
assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1
assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
# keep F4 and F6
r = uhdev.event(["F4", "F6"])
expected = []
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 1
assert evdev.value[libevdev.EV_KEY.KEY_F6] == 1
assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
# release Fn key and all keys
r = uhdev.send_fn_state(0)
r.extend(uhdev.event([]))
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_ALL_APPLICATIONS, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_F6, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_F4] == 0
assert evdev.value[libevdev.EV_KEY.KEY_ALL_APPLICATIONS] == 0
assert evdev.value[libevdev.EV_KEY.KEY_F6] == 0
assert evdev.value[libevdev.EV_KEY.KEY_KBDILLUMUP] == 0
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0
def test_single_pageup_key_release_first(self):
"""check for function key reliability with the [page] up key."""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
syn_event = self.syn_event
r = uhdev.send_fn_state(1)
r.extend(uhdev.event(["UpArrow"]))
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_PAGEUP, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_PAGEUP] == 1
assert evdev.value[libevdev.EV_KEY.KEY_UP] == 0
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 1
r = uhdev.send_fn_state(0)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_FN, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_PAGEUP] == 1
assert evdev.value[libevdev.EV_KEY.KEY_UP] == 0
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0
r = uhdev.event([])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_PAGEUP, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_PAGEUP] == 0
assert evdev.value[libevdev.EV_KEY.KEY_UP] == 0
assert evdev.value[libevdev.EV_KEY.KEY_FN] == 0
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2019 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2019 Red Hat, Inc.
#
from . import base
import libevdev
import pytest
from hidtools.device.base_gamepad import AsusGamepad, SaitekGamepad
import logging
logger = logging.getLogger("hidtools.test.gamepad")
class BaseTest:
class TestGamepad(base.BaseTestCase.TestUhid):
@pytest.fixture(autouse=True)
def send_initial_state(self):
"""send an empty report to initialize the axes"""
uhdev = self.uhdev
r = uhdev.event()
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
def assert_button(self, button):
uhdev = self.uhdev
evdev = uhdev.get_evdev()
syn_event = self.syn_event
buttons = {}
key = libevdev.evbit(uhdev.buttons_map[button])
buttons[button] = True
r = uhdev.event(buttons=buttons)
expected_event = libevdev.InputEvent(key, 1)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn((syn_event, expected_event), events)
assert evdev.value[key] == 1
buttons[button] = False
r = uhdev.event(buttons=buttons)
expected_event = libevdev.InputEvent(key, 0)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn((syn_event, expected_event), events)
assert evdev.value[key] == 0
def test_buttons(self):
"""check for button reliability."""
uhdev = self.uhdev
for b in uhdev.buttons:
self.assert_button(b)
def test_dual_buttons(self):
"""check for button reliability when pressing 2 buttons"""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
syn_event = self.syn_event
# can change intended b1 b2 values
b1 = uhdev.buttons[0]
key1 = libevdev.evbit(uhdev.buttons_map[b1])
b2 = uhdev.buttons[1]
key2 = libevdev.evbit(uhdev.buttons_map[b2])
buttons = {b1: True, b2: True}
r = uhdev.event(buttons=buttons)
expected_event0 = libevdev.InputEvent(key1, 1)
expected_event1 = libevdev.InputEvent(key2, 1)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(
(syn_event, expected_event0, expected_event1), events
)
assert evdev.value[key1] == 1
assert evdev.value[key2] == 1
buttons = {b1: False, b2: None}
r = uhdev.event(buttons=buttons)
expected_event = libevdev.InputEvent(key1, 0)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn((syn_event, expected_event), events)
assert evdev.value[key1] == 0
assert evdev.value[key2] == 1
buttons = {b1: None, b2: False}
r = uhdev.event(buttons=buttons)
expected_event = libevdev.InputEvent(key2, 0)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn((syn_event, expected_event), events)
assert evdev.value[key1] == 0
assert evdev.value[key2] == 0
def _get_libevdev_abs_events(self, which):
"""Returns which ABS_* evdev axes are expected for the given stick"""
abs_map = self.uhdev.axes_map[which]
x = abs_map["x"].evdev
y = abs_map["y"].evdev
assert x
assert y
return x, y
def _test_joystick_press(self, which, data):
uhdev = self.uhdev
libevdev_axes = self._get_libevdev_abs_events(which)
r = None
if which == "left_stick":
r = uhdev.event(left=data)
else:
r = uhdev.event(right=data)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
for i, d in enumerate(data):
if d is not None and d != 127:
assert libevdev.InputEvent(libevdev_axes[i], d) in events
else:
assert libevdev.InputEvent(libevdev_axes[i]) not in events
def test_left_joystick_press_left(self):
"""check for the left joystick reliability"""
self._test_joystick_press("left_stick", (63, None))
self._test_joystick_press("left_stick", (0, 127))
def test_left_joystick_press_right(self):
"""check for the left joystick reliability"""
self._test_joystick_press("left_stick", (191, 127))
self._test_joystick_press("left_stick", (255, None))
def test_left_joystick_press_up(self):
"""check for the left joystick reliability"""
self._test_joystick_press("left_stick", (None, 63))
self._test_joystick_press("left_stick", (127, 0))
def test_left_joystick_press_down(self):
"""check for the left joystick reliability"""
self._test_joystick_press("left_stick", (127, 191))
self._test_joystick_press("left_stick", (None, 255))
def test_right_joystick_press_left(self):
"""check for the right joystick reliability"""
self._test_joystick_press("right_stick", (63, None))
self._test_joystick_press("right_stick", (0, 127))
def test_right_joystick_press_right(self):
"""check for the right joystick reliability"""
self._test_joystick_press("right_stick", (191, 127))
self._test_joystick_press("right_stick", (255, None))
def test_right_joystick_press_up(self):
"""check for the right joystick reliability"""
self._test_joystick_press("right_stick", (None, 63))
self._test_joystick_press("right_stick", (127, 0))
def test_right_joystick_press_down(self):
"""check for the right joystick reliability"""
self._test_joystick_press("right_stick", (127, 191))
self._test_joystick_press("right_stick", (None, 255))
@pytest.mark.skip_if_uhdev(
lambda uhdev: "Hat switch" not in uhdev.fields,
"Device not compatible, missing Hat switch usage",
)
@pytest.mark.parametrize(
"hat_value,expected_evdev,evdev_value",
[
(0, "ABS_HAT0Y", -1),
(2, "ABS_HAT0X", 1),
(4, "ABS_HAT0Y", 1),
(6, "ABS_HAT0X", -1),
],
)
def test_hat_switch(self, hat_value, expected_evdev, evdev_value):
uhdev = self.uhdev
r = uhdev.event(hat_switch=hat_value)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
assert (
libevdev.InputEvent(
libevdev.evbit("EV_ABS", expected_evdev), evdev_value
)
in events
)
class TestSaitekGamepad(BaseTest.TestGamepad):
def create_device(self):
return SaitekGamepad()
class TestAsusGamepad(BaseTest.TestGamepad):
def create_device(self):
return AsusGamepad()
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2017 Red Hat, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This is for generic devices
from . import base
import logging
logger = logging.getLogger("hidtools.test.hid")
class TestCollectionOverflow(base.BaseTestCase.TestUhid):
"""
Test class to test re-allocation of the HID collection stack in
hid-core.c.
"""
def create_device(self):
# fmt: off
report_descriptor = [
0x05, 0x01, # .Usage Page (Generic Desktop)
0x09, 0x02, # .Usage (Mouse)
0xa1, 0x01, # .Collection (Application)
0x09, 0x02, # ..Usage (Mouse)
0xa1, 0x02, # ..Collection (Logical)
0x09, 0x01, # ...Usage (Pointer)
0xa1, 0x00, # ...Collection (Physical)
0x05, 0x09, # ....Usage Page (Button)
0x19, 0x01, # ....Usage Minimum (1)
0x29, 0x03, # ....Usage Maximum (3)
0x15, 0x00, # ....Logical Minimum (0)
0x25, 0x01, # ....Logical Maximum (1)
0x75, 0x01, # ....Report Size (1)
0x95, 0x03, # ....Report Count (3)
0x81, 0x02, # ....Input (Data,Var,Abs)
0x75, 0x05, # ....Report Size (5)
0x95, 0x01, # ....Report Count (1)
0x81, 0x03, # ....Input (Cnst,Var,Abs)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0xa1, 0x02, # ....Collection (Logical)
0x09, 0x01, # .....Usage (Pointer)
0x05, 0x01, # .....Usage Page (Generic Desktop)
0x09, 0x30, # .....Usage (X)
0x09, 0x31, # .....Usage (Y)
0x15, 0x81, # .....Logical Minimum (-127)
0x25, 0x7f, # .....Logical Maximum (127)
0x75, 0x08, # .....Report Size (8)
0x95, 0x02, # .....Report Count (2)
0x81, 0x06, # .....Input (Data,Var,Rel)
0xa1, 0x02, # ...Collection (Logical)
0x85, 0x12, # ....Report ID (18)
0x09, 0x48, # ....Usage (Resolution Multiplier)
0x95, 0x01, # ....Report Count (1)
0x75, 0x02, # ....Report Size (2)
0x15, 0x00, # ....Logical Minimum (0)
0x25, 0x01, # ....Logical Maximum (1)
0x35, 0x01, # ....Physical Minimum (1)
0x45, 0x0c, # ....Physical Maximum (12)
0xb1, 0x02, # ....Feature (Data,Var,Abs)
0x85, 0x1a, # ....Report ID (26)
0x09, 0x38, # ....Usage (Wheel)
0x35, 0x00, # ....Physical Minimum (0)
0x45, 0x00, # ....Physical Maximum (0)
0x95, 0x01, # ....Report Count (1)
0x75, 0x10, # ....Report Size (16)
0x16, 0x01, 0x80, # ....Logical Minimum (-32767)
0x26, 0xff, 0x7f, # ....Logical Maximum (32767)
0x81, 0x06, # ....Input (Data,Var,Rel)
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ...End Collection
0xc0, # ..End Collection
0xc0, # .End Collection
]
# fmt: on
return base.UHIDTestDevice(
name=None, rdesc=report_descriptor, application="Mouse"
)
def test_rdesc(self):
"""
This test can only check for negatives. If the kernel crashes, you
know why. If this test passes, either the bug isn't present or just
didn't get triggered. No way to know.
For an explanation, see kernel patch
HID: core: replace the collection tree pointers with indices
"""
pass
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2020 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2020 Red Hat, Inc.
#
from .test_keyboard import ArrayKeyboard, TestArrayKeyboard
from hidtools.util import BusType
import libevdev
import logging
logger = logging.getLogger("hidtools.test.ite-keyboard")
KERNEL_MODULE = ("itetech", "hid_ite")
class KbdData(object):
pass
# The ITE keyboards have an issue regarding the Wifi key:
# nothing comes in when pressing the key, but we get a null
# event on the key release.
# This test covers this case.
class ITEKeyboard(ArrayKeyboard):
# fmt: off
report_descriptor = [
0x06, 0x85, 0xff, # Usage Page (Vendor Usage Page 0xff85)
0x09, 0x95, # Usage (Vendor Usage 0x95) 3
0xa1, 0x01, # Collection (Application) 5
0x85, 0x5a, # .Report ID (90) 7
0x09, 0x01, # .Usage (Vendor Usage 0x01) 9
0x15, 0x00, # .Logical Minimum (0) 11
0x26, 0xff, 0x00, # .Logical Maximum (255) 13
0x75, 0x08, # .Report Size (8) 16
0x95, 0x10, # .Report Count (16) 18
0xb1, 0x00, # .Feature (Data,Arr,Abs) 20
0xc0, # End Collection 22
0x05, 0x01, # Usage Page (Generic Desktop) 23
0x09, 0x06, # Usage (Keyboard) 25
0xa1, 0x01, # Collection (Application) 27
0x85, 0x01, # .Report ID (1) 29
0x75, 0x01, # .Report Size (1) 31
0x95, 0x08, # .Report Count (8) 33
0x05, 0x07, # .Usage Page (Keyboard) 35
0x19, 0xe0, # .Usage Minimum (224) 37
0x29, 0xe7, # .Usage Maximum (231) 39
0x15, 0x00, # .Logical Minimum (0) 41
0x25, 0x01, # .Logical Maximum (1) 43
0x81, 0x02, # .Input (Data,Var,Abs) 45
0x95, 0x01, # .Report Count (1) 47
0x75, 0x08, # .Report Size (8) 49
0x81, 0x03, # .Input (Cnst,Var,Abs) 51
0x95, 0x05, # .Report Count (5) 53
0x75, 0x01, # .Report Size (1) 55
0x05, 0x08, # .Usage Page (LEDs) 57
0x19, 0x01, # .Usage Minimum (1) 59
0x29, 0x05, # .Usage Maximum (5) 61
0x91, 0x02, # .Output (Data,Var,Abs) 63
0x95, 0x01, # .Report Count (1) 65
0x75, 0x03, # .Report Size (3) 67
0x91, 0x03, # .Output (Cnst,Var,Abs) 69
0x95, 0x06, # .Report Count (6) 71
0x75, 0x08, # .Report Size (8) 73
0x15, 0x00, # .Logical Minimum (0) 75
0x26, 0xff, 0x00, # .Logical Maximum (255) 77
0x05, 0x07, # .Usage Page (Keyboard) 80
0x19, 0x00, # .Usage Minimum (0) 82
0x2a, 0xff, 0x00, # .Usage Maximum (255) 84
0x81, 0x00, # .Input (Data,Arr,Abs) 87
0xc0, # End Collection 89
0x05, 0x0c, # Usage Page (Consumer Devices) 90
0x09, 0x01, # Usage (Consumer Control) 92
0xa1, 0x01, # Collection (Application) 94
0x85, 0x02, # .Report ID (2) 96
0x19, 0x00, # .Usage Minimum (0) 98
0x2a, 0x3c, 0x02, # .Usage Maximum (572) 100
0x15, 0x00, # .Logical Minimum (0) 103
0x26, 0x3c, 0x02, # .Logical Maximum (572) 105
0x75, 0x10, # .Report Size (16) 108
0x95, 0x01, # .Report Count (1) 110
0x81, 0x00, # .Input (Data,Arr,Abs) 112
0xc0, # End Collection 114
0x05, 0x01, # Usage Page (Generic Desktop) 115
0x09, 0x0c, # Usage (Wireless Radio Controls) 117
0xa1, 0x01, # Collection (Application) 119
0x85, 0x03, # .Report ID (3) 121
0x15, 0x00, # .Logical Minimum (0) 123
0x25, 0x01, # .Logical Maximum (1) 125
0x09, 0xc6, # .Usage (Wireless Radio Button) 127
0x95, 0x01, # .Report Count (1) 129
0x75, 0x01, # .Report Size (1) 131
0x81, 0x06, # .Input (Data,Var,Rel) 133
0x75, 0x07, # .Report Size (7) 135
0x81, 0x03, # .Input (Cnst,Var,Abs) 137
0xc0, # End Collection 139
0x05, 0x88, # Usage Page (Vendor Usage Page 0x88) 140
0x09, 0x01, # Usage (Vendor Usage 0x01) 142
0xa1, 0x01, # Collection (Application) 144
0x85, 0x04, # .Report ID (4) 146
0x19, 0x00, # .Usage Minimum (0) 148
0x2a, 0xff, 0xff, # .Usage Maximum (65535) 150
0x15, 0x00, # .Logical Minimum (0) 153
0x26, 0xff, 0xff, # .Logical Maximum (65535) 155
0x75, 0x08, # .Report Size (8) 158
0x95, 0x02, # .Report Count (2) 160
0x81, 0x02, # .Input (Data,Var,Abs) 162
0xc0, # End Collection 164
0x05, 0x01, # Usage Page (Generic Desktop) 165
0x09, 0x80, # Usage (System Control) 167
0xa1, 0x01, # Collection (Application) 169
0x85, 0x05, # .Report ID (5) 171
0x19, 0x81, # .Usage Minimum (129) 173
0x29, 0x83, # .Usage Maximum (131) 175
0x15, 0x00, # .Logical Minimum (0) 177
0x25, 0x01, # .Logical Maximum (1) 179
0x95, 0x08, # .Report Count (8) 181
0x75, 0x01, # .Report Size (1) 183
0x81, 0x02, # .Input (Data,Var,Abs) 185
0xc0, # End Collection 187
]
# fmt: on
def __init__(
self,
rdesc=report_descriptor,
name=None,
input_info=(BusType.USB, 0x06CB, 0x2968),
):
super().__init__(rdesc, name, input_info)
def event(self, keys, reportID=None, application=None):
application = application or "Keyboard"
return super().event(keys, reportID, application)
class TestITEKeyboard(TestArrayKeyboard):
kernel_modules = [KERNEL_MODULE]
def create_device(self):
return ITEKeyboard()
def test_wifi_key(self):
uhdev = self.uhdev
syn_event = self.syn_event
# the following sends a 'release' event on the Wifi key.
# the kernel is supposed to translate this into Wifi key
# down and up
r = [0x03, 0x00]
uhdev.call_input_event(r)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_RFKILL, 1))
events = uhdev.next_sync_events()
self.debug_reports([r], uhdev, events)
self.assertInputEventsIn(expected, events)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_RFKILL, 0))
# the kernel sends the two down/up key events in a batch, no need to
# call events = uhdev.next_sync_events()
self.debug_reports([], uhdev, events)
self.assertInputEventsIn(expected, events)
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2018 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2018 Red Hat, Inc.
#
from . import base
import hidtools.hid
import libevdev
import logging
logger = logging.getLogger("hidtools.test.keyboard")
class InvalidHIDCommunication(Exception):
pass
class KeyboardData(object):
pass
class BaseKeyboard(base.UHIDTestDevice):
def __init__(self, rdesc, name=None, input_info=None):
assert rdesc is not None
super().__init__(name, "Key", input_info=input_info, rdesc=rdesc)
self.keystates = {}
def _update_key_state(self, keys):
"""
Update the internal state of keys with the new state given.
:param key: a tuple of chars for the currently pressed keys.
"""
# First remove the already released keys
unused_keys = [k for k, v in self.keystates.items() if not v]
for key in unused_keys:
del self.keystates[key]
# self.keystates contains now the list of currently pressed keys,
# release them...
for key in self.keystates.keys():
self.keystates[key] = False
# ...and press those that are in parameter
for key in keys:
self.keystates[key] = True
def _create_report_data(self):
keyboard = KeyboardData()
for key, value in self.keystates.items():
key = key.replace(" ", "").lower()
setattr(keyboard, key, value)
return keyboard
def create_array_report(self, keys, reportID=None, application=None):
"""
Return an input report for this device.
:param keys: a tuple of chars for the pressed keys. The class maintains
the list of currently pressed keys, so to release a key, the caller
needs to call again this function without the key in this tuple.
:param reportID: the numeric report ID for this report, if needed
"""
self._update_key_state(keys)
reportID = reportID or self.default_reportID
keyboard = self._create_report_data()
return self.create_report(keyboard, reportID=reportID, application=application)
def event(self, keys, reportID=None, application=None):
"""
Send an input event on the default report ID.
:param keys: a tuple of chars for the pressed keys. The class maintains
the list of currently pressed keys, so to release a key, the caller
needs to call again this function without the key in this tuple.
"""
r = self.create_array_report(keys, reportID, application)
self.call_input_event(r)
return [r]
class PlainKeyboard(BaseKeyboard):
# fmt: off
report_descriptor = [
0x05, 0x01, # Usage Page (Generic Desktop)
0x09, 0x06, # Usage (Keyboard)
0xa1, 0x01, # Collection (Application)
0x85, 0x01, # .Report ID (1)
0x05, 0x07, # .Usage Page (Keyboard)
0x19, 0xe0, # .Usage Minimum (224)
0x29, 0xe7, # .Usage Maximum (231)
0x15, 0x00, # .Logical Minimum (0)
0x25, 0x01, # .Logical Maximum (1)
0x75, 0x01, # .Report Size (1)
0x95, 0x08, # .Report Count (8)
0x81, 0x02, # .Input (Data,Var,Abs)
0x19, 0x00, # .Usage Minimum (0)
0x29, 0x97, # .Usage Maximum (151)
0x15, 0x00, # .Logical Minimum (0)
0x25, 0x01, # .Logical Maximum (1)
0x75, 0x01, # .Report Size (1)
0x95, 0x98, # .Report Count (152)
0x81, 0x02, # .Input (Data,Var,Abs)
0xc0, # End Collection
]
# fmt: on
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
super().__init__(rdesc, name, input_info)
self.default_reportID = 1
class ArrayKeyboard(BaseKeyboard):
# fmt: off
report_descriptor = [
0x05, 0x01, # Usage Page (Generic Desktop)
0x09, 0x06, # Usage (Keyboard)
0xa1, 0x01, # Collection (Application)
0x05, 0x07, # .Usage Page (Keyboard)
0x19, 0xe0, # .Usage Minimum (224)
0x29, 0xe7, # .Usage Maximum (231)
0x15, 0x00, # .Logical Minimum (0)
0x25, 0x01, # .Logical Maximum (1)
0x75, 0x01, # .Report Size (1)
0x95, 0x08, # .Report Count (8)
0x81, 0x02, # .Input (Data,Var,Abs)
0x95, 0x06, # .Report Count (6)
0x75, 0x08, # .Report Size (8)
0x15, 0x00, # .Logical Minimum (0)
0x26, 0xa4, 0x00, # .Logical Maximum (164)
0x05, 0x07, # .Usage Page (Keyboard)
0x19, 0x00, # .Usage Minimum (0)
0x29, 0xa4, # .Usage Maximum (164)
0x81, 0x00, # .Input (Data,Arr,Abs)
0xc0, # End Collection
]
# fmt: on
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
super().__init__(rdesc, name, input_info)
def _create_report_data(self):
data = KeyboardData()
array = []
hut = hidtools.hut.HUT
# strip modifiers from the array
for k, v in self.keystates.items():
# we ignore depressed keys
if not v:
continue
usage = hut[0x07].from_name[k].usage
if usage >= 224 and usage <= 231:
# modifier
setattr(data, k.lower(), 1)
else:
array.append(k)
# if array length is bigger than 6, report ErrorRollOver
if len(array) > 6:
array = ["ErrorRollOver"] * 6
data.keyboard = array
return data
class LEDKeyboard(ArrayKeyboard):
# fmt: off
report_descriptor = [
0x05, 0x01, # Usage Page (Generic Desktop)
0x09, 0x06, # Usage (Keyboard)
0xa1, 0x01, # Collection (Application)
0x05, 0x07, # .Usage Page (Keyboard)
0x19, 0xe0, # .Usage Minimum (224)
0x29, 0xe7, # .Usage Maximum (231)
0x15, 0x00, # .Logical Minimum (0)
0x25, 0x01, # .Logical Maximum (1)
0x75, 0x01, # .Report Size (1)
0x95, 0x08, # .Report Count (8)
0x81, 0x02, # .Input (Data,Var,Abs)
0x95, 0x01, # .Report Count (1)
0x75, 0x08, # .Report Size (8)
0x81, 0x01, # .Input (Cnst,Arr,Abs)
0x95, 0x05, # .Report Count (5)
0x75, 0x01, # .Report Size (1)
0x05, 0x08, # .Usage Page (LEDs)
0x19, 0x01, # .Usage Minimum (1)
0x29, 0x05, # .Usage Maximum (5)
0x91, 0x02, # .Output (Data,Var,Abs)
0x95, 0x01, # .Report Count (1)
0x75, 0x03, # .Report Size (3)
0x91, 0x01, # .Output (Cnst,Arr,Abs)
0x95, 0x06, # .Report Count (6)
0x75, 0x08, # .Report Size (8)
0x15, 0x00, # .Logical Minimum (0)
0x26, 0xa4, 0x00, # .Logical Maximum (164)
0x05, 0x07, # .Usage Page (Keyboard)
0x19, 0x00, # .Usage Minimum (0)
0x29, 0xa4, # .Usage Maximum (164)
0x81, 0x00, # .Input (Data,Arr,Abs)
0xc0, # End Collection
]
# fmt: on
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
super().__init__(rdesc, name, input_info)
# Some Primax manufactured keyboards set the Usage Page after having defined
# some local Usages. It relies on the fact that the specification states that
# Usages are to be concatenated with Usage Pages upon finding a Main item (see
# 6.2.2.8). This test covers this case.
class PrimaxKeyboard(ArrayKeyboard):
# fmt: off
report_descriptor = [
0x05, 0x01, # Usage Page (Generic Desktop)
0x09, 0x06, # Usage (Keyboard)
0xA1, 0x01, # Collection (Application)
0x05, 0x07, # .Usage Page (Keyboard)
0x19, 0xE0, # .Usage Minimum (224)
0x29, 0xE7, # .Usage Maximum (231)
0x15, 0x00, # .Logical Minimum (0)
0x25, 0x01, # .Logical Maximum (1)
0x75, 0x01, # .Report Size (1)
0x95, 0x08, # .Report Count (8)
0x81, 0x02, # .Input (Data,Var,Abs)
0x75, 0x08, # .Report Size (8)
0x95, 0x01, # .Report Count (1)
0x81, 0x01, # .Input (Data,Var,Abs)
0x05, 0x08, # .Usage Page (LEDs)
0x19, 0x01, # .Usage Minimum (1)
0x29, 0x03, # .Usage Maximum (3)
0x75, 0x01, # .Report Size (1)
0x95, 0x03, # .Report Count (3)
0x91, 0x02, # .Output (Data,Var,Abs)
0x95, 0x01, # .Report Count (1)
0x75, 0x05, # .Report Size (5)
0x91, 0x01, # .Output (Constant)
0x15, 0x00, # .Logical Minimum (0)
0x26, 0xFF, 0x00, # .Logical Maximum (255)
0x19, 0x00, # .Usage Minimum (0)
0x2A, 0xFF, 0x00, # .Usage Maximum (255)
0x05, 0x07, # .Usage Page (Keyboard)
0x75, 0x08, # .Report Size (8)
0x95, 0x06, # .Report Count (6)
0x81, 0x00, # .Input (Data,Arr,Abs)
0xC0, # End Collection
]
# fmt: on
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
super().__init__(rdesc, name, input_info)
class BaseTest:
class TestKeyboard(base.BaseTestCase.TestUhid):
def test_single_key(self):
"""check for key reliability."""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
syn_event = self.syn_event
r = uhdev.event(["a and A"])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_A] == 1
r = uhdev.event([])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_A] == 0
def test_two_keys(self):
uhdev = self.uhdev
evdev = uhdev.get_evdev()
syn_event = self.syn_event
r = uhdev.event(["a and A", "q and Q"])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_A] == 1
r = uhdev.event([])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_A] == 0
assert evdev.value[libevdev.EV_KEY.KEY_Q] == 0
r = uhdev.event(["c and C"])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_C] == 1
r = uhdev.event(["c and C", "Spacebar"])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_KEY.KEY_C) not in events
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_C] == 1
assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1
r = uhdev.event(["Spacebar"])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE) not in events
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_C] == 0
assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1
r = uhdev.event([])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 0
def test_modifiers(self):
# ctrl-alt-del would be very nice :)
uhdev = self.uhdev
syn_event = self.syn_event
r = uhdev.event(["LeftControl", "LeftShift", "= and +"])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTCTRL, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTSHIFT, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_EQUAL, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
class TestPlainKeyboard(BaseTest.TestKeyboard):
def create_device(self):
return PlainKeyboard()
def test_10_keys(self):
uhdev = self.uhdev
syn_event = self.syn_event
r = uhdev.event(
[
"1 and !",
"2 and @",
"3 and #",
"4 and $",
"5 and %",
"6 and ^",
"7 and &",
"8 and *",
"9 and (",
"0 and )",
]
)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
r = uhdev.event([])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
class TestArrayKeyboard(BaseTest.TestKeyboard):
def create_device(self):
return ArrayKeyboard()
def test_10_keys(self):
uhdev = self.uhdev
syn_event = self.syn_event
r = uhdev.event(
[
"1 and !",
"2 and @",
"3 and #",
"4 and $",
"5 and %",
"6 and ^",
]
)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
# ErrRollOver
r = uhdev.event(
[
"1 and !",
"2 and @",
"3 and #",
"4 and $",
"5 and %",
"6 and ^",
"7 and &",
"8 and *",
"9 and (",
"0 and )",
]
)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
assert len(events) == 0
r = uhdev.event([])
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0))
expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(expected, events)
class TestLEDKeyboard(BaseTest.TestKeyboard):
def create_device(self):
return LEDKeyboard()
class TestPrimaxKeyboard(BaseTest.TestKeyboard):
def create_device(self):
return PrimaxKeyboard()
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2017 Red Hat, Inc.
#
from . import base
import hidtools.hid
from hidtools.util import BusType
import libevdev
import logging
import pytest
logger = logging.getLogger("hidtools.test.mouse")
# workaround https://gitlab.freedesktop.org/libevdev/python-libevdev/issues/6
try:
libevdev.EV_REL.REL_WHEEL_HI_RES
except AttributeError:
libevdev.EV_REL.REL_WHEEL_HI_RES = libevdev.EV_REL.REL_0B
libevdev.EV_REL.REL_HWHEEL_HI_RES = libevdev.EV_REL.REL_0C
class InvalidHIDCommunication(Exception):
pass
class MouseData(object):
pass
class BaseMouse(base.UHIDTestDevice):
def __init__(self, rdesc, name=None, input_info=None):
assert rdesc is not None
super().__init__(name, "Mouse", input_info=input_info, rdesc=rdesc)
self.left = False
self.right = False
self.middle = False
def create_report(self, x, y, buttons=None, wheels=None, reportID=None):
"""
Return an input report for this device.
:param x: relative x
:param y: relative y
:param buttons: a (l, r, m) tuple of bools for the button states,
where ``None`` is "leave unchanged"
:param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for
the two wheels
:param reportID: the numeric report ID for this report, if needed
"""
if buttons is not None:
l, r, m = buttons
if l is not None:
self.left = l
if r is not None:
self.right = r
if m is not None:
self.middle = m
left = self.left
right = self.right
middle = self.middle
# Note: the BaseMouse doesn't actually have a wheel but the
# create_report magic only fills in those fields exist, so let's
# make this generic here.
wheel, acpan = 0, 0
if wheels is not None:
if isinstance(wheels, tuple):
wheel = wheels[0]
acpan = wheels[1]
else:
wheel = wheels
reportID = reportID or self.default_reportID
mouse = MouseData()
mouse.b1 = int(left)
mouse.b2 = int(right)
mouse.b3 = int(middle)
mouse.x = x
mouse.y = y
mouse.wheel = wheel
mouse.acpan = acpan
return super().create_report(mouse, reportID=reportID)
def event(self, x, y, buttons=None, wheels=None):
"""
Send an input event on the default report ID.
:param x: relative x
:param y: relative y
:param buttons: a (l, r, m) tuple of bools for the button states,
where ``None`` is "leave unchanged"
:param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for
the two wheels
"""
r = self.create_report(x, y, buttons, wheels)
self.call_input_event(r)
return [r]
class ButtonMouse(BaseMouse):
# fmt: off
report_descriptor = [
0x05, 0x01, # .Usage Page (Generic Desktop) 0
0x09, 0x02, # .Usage (Mouse) 2
0xa1, 0x01, # .Collection (Application) 4
0x09, 0x02, # ..Usage (Mouse) 6
0xa1, 0x02, # ..Collection (Logical) 8
0x09, 0x01, # ...Usage (Pointer) 10
0xa1, 0x00, # ...Collection (Physical) 12
0x05, 0x09, # ....Usage Page (Button) 14
0x19, 0x01, # ....Usage Minimum (1) 16
0x29, 0x03, # ....Usage Maximum (3) 18
0x15, 0x00, # ....Logical Minimum (0) 20
0x25, 0x01, # ....Logical Maximum (1) 22
0x75, 0x01, # ....Report Size (1) 24
0x95, 0x03, # ....Report Count (3) 26
0x81, 0x02, # ....Input (Data,Var,Abs) 28
0x75, 0x05, # ....Report Size (5) 30
0x95, 0x01, # ....Report Count (1) 32
0x81, 0x03, # ....Input (Cnst,Var,Abs) 34
0x05, 0x01, # ....Usage Page (Generic Desktop) 36
0x09, 0x30, # ....Usage (X) 38
0x09, 0x31, # ....Usage (Y) 40
0x15, 0x81, # ....Logical Minimum (-127) 42
0x25, 0x7f, # ....Logical Maximum (127) 44
0x75, 0x08, # ....Report Size (8) 46
0x95, 0x02, # ....Report Count (2) 48
0x81, 0x06, # ....Input (Data,Var,Rel) 50
0xc0, # ...End Collection 52
0xc0, # ..End Collection 53
0xc0, # .End Collection 54
]
# fmt: on
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
super().__init__(rdesc, name, input_info)
def fake_report(self, x, y, buttons):
if buttons is not None:
left, right, middle = buttons
if left is None:
left = self.left
if right is None:
right = self.right
if middle is None:
middle = self.middle
else:
left = self.left
right = self.right
middle = self.middle
button_mask = sum(1 << i for i, b in enumerate([left, right, middle]) if b)
x = max(-127, min(127, x))
y = max(-127, min(127, y))
x = hidtools.util.to_twos_comp(x, 8)
y = hidtools.util.to_twos_comp(y, 8)
return [button_mask, x, y]
class WheelMouse(ButtonMouse):
# fmt: off
report_descriptor = [
0x05, 0x01, # Usage Page (Generic Desktop) 0
0x09, 0x02, # Usage (Mouse) 2
0xa1, 0x01, # Collection (Application) 4
0x05, 0x09, # .Usage Page (Button) 6
0x19, 0x01, # .Usage Minimum (1) 8
0x29, 0x03, # .Usage Maximum (3) 10
0x15, 0x00, # .Logical Minimum (0) 12
0x25, 0x01, # .Logical Maximum (1) 14
0x95, 0x03, # .Report Count (3) 16
0x75, 0x01, # .Report Size (1) 18
0x81, 0x02, # .Input (Data,Var,Abs) 20
0x95, 0x01, # .Report Count (1) 22
0x75, 0x05, # .Report Size (5) 24
0x81, 0x03, # .Input (Cnst,Var,Abs) 26
0x05, 0x01, # .Usage Page (Generic Desktop) 28
0x09, 0x01, # .Usage (Pointer) 30
0xa1, 0x00, # .Collection (Physical) 32
0x09, 0x30, # ..Usage (X) 34
0x09, 0x31, # ..Usage (Y) 36
0x15, 0x81, # ..Logical Minimum (-127) 38
0x25, 0x7f, # ..Logical Maximum (127) 40
0x75, 0x08, # ..Report Size (8) 42
0x95, 0x02, # ..Report Count (2) 44
0x81, 0x06, # ..Input (Data,Var,Rel) 46
0xc0, # .End Collection 48
0x09, 0x38, # .Usage (Wheel) 49
0x15, 0x81, # .Logical Minimum (-127) 51
0x25, 0x7f, # .Logical Maximum (127) 53
0x75, 0x08, # .Report Size (8) 55
0x95, 0x01, # .Report Count (1) 57
0x81, 0x06, # .Input (Data,Var,Rel) 59
0xc0, # End Collection 61
]
# fmt: on
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
super().__init__(rdesc, name, input_info)
self.wheel_multiplier = 1
class TwoWheelMouse(WheelMouse):
# fmt: off
report_descriptor = [
0x05, 0x01, # Usage Page (Generic Desktop) 0
0x09, 0x02, # Usage (Mouse) 2
0xa1, 0x01, # Collection (Application) 4
0x09, 0x01, # .Usage (Pointer) 6
0xa1, 0x00, # .Collection (Physical) 8
0x05, 0x09, # ..Usage Page (Button) 10
0x19, 0x01, # ..Usage Minimum (1) 12
0x29, 0x10, # ..Usage Maximum (16) 14
0x15, 0x00, # ..Logical Minimum (0) 16
0x25, 0x01, # ..Logical Maximum (1) 18
0x95, 0x10, # ..Report Count (16) 20
0x75, 0x01, # ..Report Size (1) 22
0x81, 0x02, # ..Input (Data,Var,Abs) 24
0x05, 0x01, # ..Usage Page (Generic Desktop) 26
0x16, 0x01, 0x80, # ..Logical Minimum (-32767) 28
0x26, 0xff, 0x7f, # ..Logical Maximum (32767) 31
0x75, 0x10, # ..Report Size (16) 34
0x95, 0x02, # ..Report Count (2) 36
0x09, 0x30, # ..Usage (X) 38
0x09, 0x31, # ..Usage (Y) 40
0x81, 0x06, # ..Input (Data,Var,Rel) 42
0x15, 0x81, # ..Logical Minimum (-127) 44
0x25, 0x7f, # ..Logical Maximum (127) 46
0x75, 0x08, # ..Report Size (8) 48
0x95, 0x01, # ..Report Count (1) 50
0x09, 0x38, # ..Usage (Wheel) 52
0x81, 0x06, # ..Input (Data,Var,Rel) 54
0x05, 0x0c, # ..Usage Page (Consumer Devices) 56
0x0a, 0x38, 0x02, # ..Usage (AC Pan) 58
0x95, 0x01, # ..Report Count (1) 61
0x81, 0x06, # ..Input (Data,Var,Rel) 63
0xc0, # .End Collection 65
0xc0, # End Collection 66
]
# fmt: on
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
super().__init__(rdesc, name, input_info)
self.hwheel_multiplier = 1
class MIDongleMIWirelessMouse(TwoWheelMouse):
# fmt: off
report_descriptor = [
0x05, 0x01, # Usage Page (Generic Desktop)
0x09, 0x02, # Usage (Mouse)
0xa1, 0x01, # Collection (Application)
0x85, 0x01, # .Report ID (1)
0x09, 0x01, # .Usage (Pointer)
0xa1, 0x00, # .Collection (Physical)
0x95, 0x05, # ..Report Count (5)
0x75, 0x01, # ..Report Size (1)
0x05, 0x09, # ..Usage Page (Button)
0x19, 0x01, # ..Usage Minimum (1)
0x29, 0x05, # ..Usage Maximum (5)
0x15, 0x00, # ..Logical Minimum (0)
0x25, 0x01, # ..Logical Maximum (1)
0x81, 0x02, # ..Input (Data,Var,Abs)
0x95, 0x01, # ..Report Count (1)
0x75, 0x03, # ..Report Size (3)
0x81, 0x01, # ..Input (Cnst,Arr,Abs)
0x75, 0x08, # ..Report Size (8)
0x95, 0x01, # ..Report Count (1)
0x05, 0x01, # ..Usage Page (Generic Desktop)
0x09, 0x38, # ..Usage (Wheel)
0x15, 0x81, # ..Logical Minimum (-127)
0x25, 0x7f, # ..Logical Maximum (127)
0x81, 0x06, # ..Input (Data,Var,Rel)
0x05, 0x0c, # ..Usage Page (Consumer Devices)
0x0a, 0x38, 0x02, # ..Usage (AC Pan)
0x95, 0x01, # ..Report Count (1)
0x81, 0x06, # ..Input (Data,Var,Rel)
0xc0, # .End Collection
0x85, 0x02, # .Report ID (2)
0x09, 0x01, # .Usage (Consumer Control)
0xa1, 0x00, # .Collection (Physical)
0x75, 0x0c, # ..Report Size (12)
0x95, 0x02, # ..Report Count (2)
0x05, 0x01, # ..Usage Page (Generic Desktop)
0x09, 0x30, # ..Usage (X)
0x09, 0x31, # ..Usage (Y)
0x16, 0x01, 0xf8, # ..Logical Minimum (-2047)
0x26, 0xff, 0x07, # ..Logical Maximum (2047)
0x81, 0x06, # ..Input (Data,Var,Rel)
0xc0, # .End Collection
0xc0, # End Collection
0x05, 0x0c, # Usage Page (Consumer Devices)
0x09, 0x01, # Usage (Consumer Control)
0xa1, 0x01, # Collection (Application)
0x85, 0x03, # .Report ID (3)
0x15, 0x00, # .Logical Minimum (0)
0x25, 0x01, # .Logical Maximum (1)
0x75, 0x01, # .Report Size (1)
0x95, 0x01, # .Report Count (1)
0x09, 0xcd, # .Usage (Play/Pause)
0x81, 0x06, # .Input (Data,Var,Rel)
0x0a, 0x83, 0x01, # .Usage (AL Consumer Control Config)
0x81, 0x06, # .Input (Data,Var,Rel)
0x09, 0xb5, # .Usage (Scan Next Track)
0x81, 0x06, # .Input (Data,Var,Rel)
0x09, 0xb6, # .Usage (Scan Previous Track)
0x81, 0x06, # .Input (Data,Var,Rel)
0x09, 0xea, # .Usage (Volume Down)
0x81, 0x06, # .Input (Data,Var,Rel)
0x09, 0xe9, # .Usage (Volume Up)
0x81, 0x06, # .Input (Data,Var,Rel)
0x0a, 0x25, 0x02, # .Usage (AC Forward)
0x81, 0x06, # .Input (Data,Var,Rel)
0x0a, 0x24, 0x02, # .Usage (AC Back)
0x81, 0x06, # .Input (Data,Var,Rel)
0xc0, # End Collection
]
# fmt: on
device_input_info = (BusType.USB, 0x2717, 0x003B)
device_name = "uhid test MI Dongle MI Wireless Mouse"
def __init__(
self, rdesc=report_descriptor, name=device_name, input_info=device_input_info
):
super().__init__(rdesc, name, input_info)
def event(self, x, y, buttons=None, wheels=None):
# this mouse spreads the relative pointer and the mouse buttons
# onto 2 distinct reports
rs = []
r = self.create_report(x, y, buttons, wheels, reportID=1)
self.call_input_event(r)
rs.append(r)
r = self.create_report(x, y, buttons, reportID=2)
self.call_input_event(r)
rs.append(r)
return rs
class ResolutionMultiplierMouse(TwoWheelMouse):
# fmt: off
report_descriptor = [
0x05, 0x01, # Usage Page (Generic Desktop) 83
0x09, 0x02, # Usage (Mouse) 85
0xa1, 0x01, # Collection (Application) 87
0x05, 0x01, # .Usage Page (Generic Desktop) 89
0x09, 0x02, # .Usage (Mouse) 91
0xa1, 0x02, # .Collection (Logical) 93
0x85, 0x11, # ..Report ID (17) 95
0x09, 0x01, # ..Usage (Pointer) 97
0xa1, 0x00, # ..Collection (Physical) 99
0x05, 0x09, # ...Usage Page (Button) 101
0x19, 0x01, # ...Usage Minimum (1) 103
0x29, 0x03, # ...Usage Maximum (3) 105
0x95, 0x03, # ...Report Count (3) 107
0x75, 0x01, # ...Report Size (1) 109
0x25, 0x01, # ...Logical Maximum (1) 111
0x81, 0x02, # ...Input (Data,Var,Abs) 113
0x95, 0x01, # ...Report Count (1) 115
0x81, 0x01, # ...Input (Cnst,Arr,Abs) 117
0x09, 0x05, # ...Usage (Vendor Usage 0x05) 119
0x81, 0x02, # ...Input (Data,Var,Abs) 121
0x95, 0x03, # ...Report Count (3) 123
0x81, 0x01, # ...Input (Cnst,Arr,Abs) 125
0x05, 0x01, # ...Usage Page (Generic Desktop) 127
0x09, 0x30, # ...Usage (X) 129
0x09, 0x31, # ...Usage (Y) 131
0x95, 0x02, # ...Report Count (2) 133
0x75, 0x08, # ...Report Size (8) 135
0x15, 0x81, # ...Logical Minimum (-127) 137
0x25, 0x7f, # ...Logical Maximum (127) 139
0x81, 0x06, # ...Input (Data,Var,Rel) 141
0xa1, 0x02, # ...Collection (Logical) 143
0x85, 0x12, # ....Report ID (18) 145
0x09, 0x48, # ....Usage (Resolution Multiplier) 147
0x95, 0x01, # ....Report Count (1) 149
0x75, 0x02, # ....Report Size (2) 151
0x15, 0x00, # ....Logical Minimum (0) 153
0x25, 0x01, # ....Logical Maximum (1) 155
0x35, 0x01, # ....Physical Minimum (1) 157
0x45, 0x04, # ....Physical Maximum (4) 159
0xb1, 0x02, # ....Feature (Data,Var,Abs) 161
0x35, 0x00, # ....Physical Minimum (0) 163
0x45, 0x00, # ....Physical Maximum (0) 165
0x75, 0x06, # ....Report Size (6) 167
0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 169
0x85, 0x11, # ....Report ID (17) 171
0x09, 0x38, # ....Usage (Wheel) 173
0x15, 0x81, # ....Logical Minimum (-127) 175
0x25, 0x7f, # ....Logical Maximum (127) 177
0x75, 0x08, # ....Report Size (8) 179
0x81, 0x06, # ....Input (Data,Var,Rel) 181
0xc0, # ...End Collection 183
0x05, 0x0c, # ...Usage Page (Consumer Devices) 184
0x75, 0x08, # ...Report Size (8) 186
0x0a, 0x38, 0x02, # ...Usage (AC Pan) 188
0x81, 0x06, # ...Input (Data,Var,Rel) 191
0xc0, # ..End Collection 193
0xc0, # .End Collection 194
0xc0, # End Collection 195
]
# fmt: on
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
super().__init__(rdesc, name, input_info)
self.default_reportID = 0x11
# Feature Report 12, multiplier Feature value must be set to 0b01,
# i.e. 1. We should extract that from the descriptor instead
# of hardcoding it here, but meanwhile this will do.
self.set_feature_report = [0x12, 0x1]
def set_report(self, req, rnum, rtype, data):
if rtype != self.UHID_FEATURE_REPORT:
raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")
if rnum != 0x12:
raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")
if data != self.set_feature_report:
raise InvalidHIDCommunication(
f"Unexpected data: {data}, expected {self.set_feature_report}"
)
self.wheel_multiplier = 4
return 0
class BadResolutionMultiplierMouse(ResolutionMultiplierMouse):
def set_report(self, req, rnum, rtype, data):
super().set_report(req, rnum, rtype, data)
self.wheel_multiplier = 1
self.hwheel_multiplier = 1
return 32 # EPIPE
class ResolutionMultiplierHWheelMouse(TwoWheelMouse):
# fmt: off
report_descriptor = [
0x05, 0x01, # Usage Page (Generic Desktop) 0
0x09, 0x02, # Usage (Mouse) 2
0xa1, 0x01, # Collection (Application) 4
0x05, 0x01, # .Usage Page (Generic Desktop) 6
0x09, 0x02, # .Usage (Mouse) 8
0xa1, 0x02, # .Collection (Logical) 10
0x85, 0x1a, # ..Report ID (26) 12
0x09, 0x01, # ..Usage (Pointer) 14
0xa1, 0x00, # ..Collection (Physical) 16
0x05, 0x09, # ...Usage Page (Button) 18
0x19, 0x01, # ...Usage Minimum (1) 20
0x29, 0x05, # ...Usage Maximum (5) 22
0x95, 0x05, # ...Report Count (5) 24
0x75, 0x01, # ...Report Size (1) 26
0x15, 0x00, # ...Logical Minimum (0) 28
0x25, 0x01, # ...Logical Maximum (1) 30
0x81, 0x02, # ...Input (Data,Var,Abs) 32
0x75, 0x03, # ...Report Size (3) 34
0x95, 0x01, # ...Report Count (1) 36
0x81, 0x01, # ...Input (Cnst,Arr,Abs) 38
0x05, 0x01, # ...Usage Page (Generic Desktop) 40
0x09, 0x30, # ...Usage (X) 42
0x09, 0x31, # ...Usage (Y) 44
0x95, 0x02, # ...Report Count (2) 46
0x75, 0x10, # ...Report Size (16) 48
0x16, 0x01, 0x80, # ...Logical Minimum (-32767) 50
0x26, 0xff, 0x7f, # ...Logical Maximum (32767) 53
0x81, 0x06, # ...Input (Data,Var,Rel) 56
0xa1, 0x02, # ...Collection (Logical) 58
0x85, 0x12, # ....Report ID (18) 60
0x09, 0x48, # ....Usage (Resolution Multiplier) 62
0x95, 0x01, # ....Report Count (1) 64
0x75, 0x02, # ....Report Size (2) 66
0x15, 0x00, # ....Logical Minimum (0) 68
0x25, 0x01, # ....Logical Maximum (1) 70
0x35, 0x01, # ....Physical Minimum (1) 72
0x45, 0x0c, # ....Physical Maximum (12) 74
0xb1, 0x02, # ....Feature (Data,Var,Abs) 76
0x85, 0x1a, # ....Report ID (26) 78
0x09, 0x38, # ....Usage (Wheel) 80
0x35, 0x00, # ....Physical Minimum (0) 82
0x45, 0x00, # ....Physical Maximum (0) 84
0x95, 0x01, # ....Report Count (1) 86
0x75, 0x10, # ....Report Size (16) 88
0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 90
0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 93
0x81, 0x06, # ....Input (Data,Var,Rel) 96
0xc0, # ...End Collection 98
0xa1, 0x02, # ...Collection (Logical) 99
0x85, 0x12, # ....Report ID (18) 101
0x09, 0x48, # ....Usage (Resolution Multiplier) 103
0x75, 0x02, # ....Report Size (2) 105
0x15, 0x00, # ....Logical Minimum (0) 107
0x25, 0x01, # ....Logical Maximum (1) 109
0x35, 0x01, # ....Physical Minimum (1) 111
0x45, 0x0c, # ....Physical Maximum (12) 113
0xb1, 0x02, # ....Feature (Data,Var,Abs) 115
0x35, 0x00, # ....Physical Minimum (0) 117
0x45, 0x00, # ....Physical Maximum (0) 119
0x75, 0x04, # ....Report Size (4) 121
0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 123
0x85, 0x1a, # ....Report ID (26) 125
0x05, 0x0c, # ....Usage Page (Consumer Devices) 127
0x95, 0x01, # ....Report Count (1) 129
0x75, 0x10, # ....Report Size (16) 131
0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 133
0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 136
0x0a, 0x38, 0x02, # ....Usage (AC Pan) 139
0x81, 0x06, # ....Input (Data,Var,Rel) 142
0xc0, # ...End Collection 144
0xc0, # ..End Collection 145
0xc0, # .End Collection 146
0xc0, # End Collection 147
]
# fmt: on
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
super().__init__(rdesc, name, input_info)
self.default_reportID = 0x1A
# Feature Report 12, multiplier Feature value must be set to 0b0101,
# i.e. 5. We should extract that from the descriptor instead
# of hardcoding it here, but meanwhile this will do.
self.set_feature_report = [0x12, 0x5]
def set_report(self, req, rnum, rtype, data):
super().set_report(req, rnum, rtype, data)
self.wheel_multiplier = 12
self.hwheel_multiplier = 12
return 0
class BaseTest:
class TestMouse(base.BaseTestCase.TestUhid):
def test_buttons(self):
"""check for button reliability."""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
syn_event = self.syn_event
r = uhdev.event(0, 0, (None, True, None))
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn((syn_event, expected_event), events)
assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
r = uhdev.event(0, 0, (None, False, None))
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn((syn_event, expected_event), events)
assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0
r = uhdev.event(0, 0, (None, None, True))
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 1)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn((syn_event, expected_event), events)
assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 1
r = uhdev.event(0, 0, (None, None, False))
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 0)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn((syn_event, expected_event), events)
assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 0
r = uhdev.event(0, 0, (True, None, None))
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn((syn_event, expected_event), events)
assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1
r = uhdev.event(0, 0, (False, None, None))
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn((syn_event, expected_event), events)
assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
r = uhdev.event(0, 0, (True, True, None))
expected_event0 = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)
expected_event1 = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn(
(syn_event, expected_event0, expected_event1), events
)
assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1
r = uhdev.event(0, 0, (False, None, None))
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn((syn_event, expected_event), events)
assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
r = uhdev.event(0, 0, (None, False, None))
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEventsIn((syn_event, expected_event), events)
assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0
assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
def test_relative(self):
"""Check for relative events."""
uhdev = self.uhdev
syn_event = self.syn_event
r = uhdev.event(0, -1)
expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_Y, -1)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents((syn_event, expected_event), events)
r = uhdev.event(1, 0)
expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_X, 1)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents((syn_event, expected_event), events)
r = uhdev.event(-1, 2)
expected_event0 = libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)
expected_event1 = libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(
(syn_event, expected_event0, expected_event1), events
)
class TestSimpleMouse(BaseTest.TestMouse):
def create_device(self):
return ButtonMouse()
def test_rdesc(self):
"""Check that the testsuite actually manages to format the
reports according to the report descriptors.
No kernel device is used here"""
uhdev = self.uhdev
event = (0, 0, (None, None, None))
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
event = (0, 0, (None, True, None))
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
event = (0, 0, (True, True, None))
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
event = (0, 0, (False, False, False))
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
event = (1, 0, (True, False, True))
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
event = (-1, 0, (True, False, True))
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
event = (-5, 5, (True, False, True))
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
event = (-127, 127, (True, False, True))
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
event = (0, -128, (True, False, True))
with pytest.raises(hidtools.hid.RangeError):
uhdev.create_report(*event)
class TestWheelMouse(BaseTest.TestMouse):
def create_device(self):
return WheelMouse()
def is_wheel_highres(self, uhdev):
evdev = uhdev.get_evdev()
assert evdev.has(libevdev.EV_REL.REL_WHEEL)
return evdev.has(libevdev.EV_REL.REL_WHEEL_HI_RES)
def test_wheel(self):
uhdev = self.uhdev
# check if the kernel is high res wheel compatible
high_res_wheel = self.is_wheel_highres(uhdev)
syn_event = self.syn_event
# The Resolution Multiplier is applied to the HID reports, so we
# need to pre-multiply too.
mult = uhdev.wheel_multiplier
r = uhdev.event(0, 0, wheels=1 * mult)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))
if high_res_wheel:
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
r = uhdev.event(0, 0, wheels=-1 * mult)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -1))
if high_res_wheel:
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
r = uhdev.event(-1, 2, wheels=3 * mult)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 3))
if high_res_wheel:
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 360))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
class TestTwoWheelMouse(TestWheelMouse):
def create_device(self):
return TwoWheelMouse()
def is_hwheel_highres(self, uhdev):
evdev = uhdev.get_evdev()
assert evdev.has(libevdev.EV_REL.REL_HWHEEL)
return evdev.has(libevdev.EV_REL.REL_HWHEEL_HI_RES)
def test_ac_pan(self):
uhdev = self.uhdev
# check if the kernel is high res wheel compatible
high_res_wheel = self.is_wheel_highres(uhdev)
high_res_hwheel = self.is_hwheel_highres(uhdev)
assert high_res_wheel == high_res_hwheel
syn_event = self.syn_event
# The Resolution Multiplier is applied to the HID reports, so we
# need to pre-multiply too.
hmult = uhdev.hwheel_multiplier
vmult = uhdev.wheel_multiplier
r = uhdev.event(0, 0, wheels=(0, 1 * hmult))
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))
if high_res_hwheel:
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
r = uhdev.event(0, 0, wheels=(0, -1 * hmult))
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, -1))
if high_res_hwheel:
expected.append(
libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120)
)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
r = uhdev.event(-1, 2, wheels=(0, 3 * hmult))
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 3))
if high_res_hwheel:
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 360))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
r = uhdev.event(-1, 2, wheels=(-3 * vmult, 4 * hmult))
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -3))
if high_res_wheel:
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -360))
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 4))
if high_res_wheel:
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 480))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
class TestResolutionMultiplierMouse(TestTwoWheelMouse):
def create_device(self):
return ResolutionMultiplierMouse()
def is_wheel_highres(self, uhdev):
high_res = super().is_wheel_highres(uhdev)
if not high_res:
# the kernel doesn't seem to support the high res wheel mice,
# make sure we haven't triggered the feature
assert uhdev.wheel_multiplier == 1
return high_res
def test_resolution_multiplier_wheel(self):
uhdev = self.uhdev
if not self.is_wheel_highres(uhdev):
pytest.skip("Kernel not compatible, we can not trigger the conditions")
assert uhdev.wheel_multiplier > 1
assert 120 % uhdev.wheel_multiplier == 0
def test_wheel_with_multiplier(self):
uhdev = self.uhdev
if not self.is_wheel_highres(uhdev):
pytest.skip("Kernel not compatible, we can not trigger the conditions")
assert uhdev.wheel_multiplier > 1
syn_event = self.syn_event
mult = uhdev.wheel_multiplier
r = uhdev.event(0, 0, wheels=1)
expected = [syn_event]
expected.append(
libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)
)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
r = uhdev.event(0, 0, wheels=-1)
expected = [syn_event]
expected.append(
libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120 / mult)
)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))
expected.append(
libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)
)
for _ in range(mult - 1):
r = uhdev.event(1, -2, wheels=1)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
r = uhdev.event(1, -2, wheels=1)
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
class TestBadResolutionMultiplierMouse(TestTwoWheelMouse):
def create_device(self):
return BadResolutionMultiplierMouse()
def is_wheel_highres(self, uhdev):
high_res = super().is_wheel_highres(uhdev)
assert uhdev.wheel_multiplier == 1
return high_res
def test_resolution_multiplier_wheel(self):
uhdev = self.uhdev
assert uhdev.wheel_multiplier == 1
class TestResolutionMultiplierHWheelMouse(TestResolutionMultiplierMouse):
def create_device(self):
return ResolutionMultiplierHWheelMouse()
def is_hwheel_highres(self, uhdev):
high_res = super().is_hwheel_highres(uhdev)
if not high_res:
# the kernel doesn't seem to support the high res wheel mice,
# make sure we haven't triggered the feature
assert uhdev.hwheel_multiplier == 1
return high_res
def test_resolution_multiplier_ac_pan(self):
uhdev = self.uhdev
if not self.is_hwheel_highres(uhdev):
pytest.skip("Kernel not compatible, we can not trigger the conditions")
assert uhdev.hwheel_multiplier > 1
assert 120 % uhdev.hwheel_multiplier == 0
def test_ac_pan_with_multiplier(self):
uhdev = self.uhdev
if not self.is_hwheel_highres(uhdev):
pytest.skip("Kernel not compatible, we can not trigger the conditions")
assert uhdev.hwheel_multiplier > 1
syn_event = self.syn_event
hmult = uhdev.hwheel_multiplier
r = uhdev.event(0, 0, wheels=(0, 1))
expected = [syn_event]
expected.append(
libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)
)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
r = uhdev.event(0, 0, wheels=(0, -1))
expected = [syn_event]
expected.append(
libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120 / hmult)
)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
expected = [syn_event]
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))
expected.append(
libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)
)
for _ in range(hmult - 1):
r = uhdev.event(1, -2, wheels=(0, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
r = uhdev.event(1, -2, wheels=(0, 1))
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
self.assertInputEvents(expected, events)
class TestMiMouse(TestWheelMouse):
def create_device(self):
return MIDongleMIWirelessMouse()
def assertInputEvents(self, expected_events, effective_events):
# Buttons and x/y are spread over two HID reports, so we can get two
# event frames for this device.
remaining = self.assertInputEventsIn(expected_events, effective_events)
try:
remaining.remove(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT, 0))
except ValueError:
# If there's no SYN_REPORT in the list, continue and let the
# assert below print out the real error
pass
assert remaining == []
This source diff could not be displayed because it is too large. You can view the blob instead.
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2020 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2020 Red Hat, Inc.
#
from .base import application_matches
from .test_gamepad import BaseTest
from hidtools.device.sony_gamepad import (
PS3Controller,
PS4ControllerBluetooth,
PS4ControllerUSB,
PS5ControllerBluetooth,
PS5ControllerUSB,
PSTouchPoint,
)
from hidtools.util import BusType
import libevdev
import logging
import pytest
logger = logging.getLogger("hidtools.test.sony")
PS3_MODULE = ("sony", "hid_sony")
PS4_MODULE = ("playstation", "hid_playstation")
PS5_MODULE = ("playstation", "hid_playstation")
class SonyBaseTest:
class SonyTest(BaseTest.TestGamepad):
pass
class SonyPS4ControllerTest(SonyTest):
kernel_modules = [PS4_MODULE]
def test_accelerometer(self):
uhdev = self.uhdev
evdev = uhdev.get_evdev("Accelerometer")
for x in range(-32000, 32000, 4000):
r = uhdev.event(accel=(x, None, None))
events = uhdev.next_sync_events("Accelerometer")
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_X) in events
value = evdev.value[libevdev.EV_ABS.ABS_X]
# Check against range due to small loss in precision due
# to inverse calibration, followed by calibration by hid-sony.
assert x - 1 <= value <= x + 1
for y in range(-32000, 32000, 4000):
r = uhdev.event(accel=(None, y, None))
events = uhdev.next_sync_events("Accelerometer")
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Y) in events
value = evdev.value[libevdev.EV_ABS.ABS_Y]
assert y - 1 <= value <= y + 1
for z in range(-32000, 32000, 4000):
r = uhdev.event(accel=(None, None, z))
events = uhdev.next_sync_events("Accelerometer")
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Z) in events
value = evdev.value[libevdev.EV_ABS.ABS_Z]
assert z - 1 <= value <= z + 1
def test_gyroscope(self):
uhdev = self.uhdev
evdev = uhdev.get_evdev("Accelerometer")
for rx in range(-2000000, 2000000, 200000):
r = uhdev.event(gyro=(rx, None, None))
events = uhdev.next_sync_events("Accelerometer")
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RX) in events
value = evdev.value[libevdev.EV_ABS.ABS_RX]
# Sensor internal value is 16-bit, but calibrated is 22-bit, so
# 6-bit (64) difference, so allow a range of +/- 64.
assert rx - 64 <= value <= rx + 64
for ry in range(-2000000, 2000000, 200000):
r = uhdev.event(gyro=(None, ry, None))
events = uhdev.next_sync_events("Accelerometer")
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RY) in events
value = evdev.value[libevdev.EV_ABS.ABS_RY]
assert ry - 64 <= value <= ry + 64
for rz in range(-2000000, 2000000, 200000):
r = uhdev.event(gyro=(None, None, rz))
events = uhdev.next_sync_events("Accelerometer")
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RZ) in events
value = evdev.value[libevdev.EV_ABS.ABS_RZ]
assert rz - 64 <= value <= rz + 64
def test_battery(self):
uhdev = self.uhdev
assert uhdev.power_supply_class is not None
# DS4 capacity levels are in increments of 10.
# Battery is never below 5%.
for i in range(5, 105, 10):
uhdev.battery.capacity = i
uhdev.event()
assert uhdev.power_supply_class.capacity == i
# Discharging tests only make sense for BlueTooth.
if uhdev.bus == BusType.BLUETOOTH:
uhdev.battery.cable_connected = False
uhdev.battery.capacity = 45
uhdev.event()
assert uhdev.power_supply_class.status == "Discharging"
uhdev.battery.cable_connected = True
uhdev.battery.capacity = 5
uhdev.event()
assert uhdev.power_supply_class.status == "Charging"
uhdev.battery.capacity = 100
uhdev.event()
assert uhdev.power_supply_class.status == "Charging"
uhdev.battery.full = True
uhdev.event()
assert uhdev.power_supply_class.status == "Full"
def test_mt_single_touch(self):
"""send a single touch in the first slot of the device,
and release it."""
uhdev = self.uhdev
evdev = uhdev.get_evdev("Touch Pad")
t0 = PSTouchPoint(1, 50, 100)
r = uhdev.event(touch=[t0])
events = uhdev.next_sync_events("Touch Pad")
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
t0.tipswitch = False
r = uhdev.event(touch=[t0])
events = uhdev.next_sync_events("Touch Pad")
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
def test_mt_dual_touch(self):
"""Send 2 touches in the first 2 slots.
Make sure the kernel sees this as a dual touch.
Release and check
Note: PTP will send here BTN_DOUBLETAP emulation"""
uhdev = self.uhdev
evdev = uhdev.get_evdev("Touch Pad")
t0 = PSTouchPoint(1, 50, 100)
t1 = PSTouchPoint(2, 150, 200)
r = uhdev.event(touch=[t0])
events = uhdev.next_sync_events("Touch Pad")
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events
assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
r = uhdev.event(touch=[t0, t1])
events = uhdev.next_sync_events("Touch Pad")
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH) not in events
assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1
assert (
libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X, 5) not in events
)
assert (
libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y, 10) not in events
)
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_X] == 150
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 200
t0.tipswitch = False
r = uhdev.event(touch=[t0, t1])
events = uhdev.next_sync_events("Touch Pad")
self.debug_reports(r, uhdev, events)
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X) not in events
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y) not in events
t1.tipswitch = False
r = uhdev.event(touch=[t1])
events = uhdev.next_sync_events("Touch Pad")
self.debug_reports(r, uhdev, events)
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
class TestPS3Controller(SonyBaseTest.SonyTest):
kernel_modules = [PS3_MODULE]
def create_device(self):
controller = PS3Controller()
controller.application_matches = application_matches
return controller
@pytest.fixture(autouse=True)
def start_controller(self):
# emulate a 'PS' button press to tell the kernel we are ready to accept events
self.assert_button(17)
# drain any remaining udev events
while self.uhdev.dispatch(10):
pass
def test_led(self):
for k, v in self.uhdev.led_classes.items():
# the kernel might have set a LED for us
logger.info(f"{k}: {v.brightness}")
idx = int(k[-1]) - 1
assert self.uhdev.hw_leds.get_led(idx)[0] == bool(v.brightness)
v.brightness = 0
self.uhdev.dispatch(10)
assert self.uhdev.hw_leds.get_led(idx)[0] is False
v.brightness = v.max_brightness
self.uhdev.dispatch(10)
assert self.uhdev.hw_leds.get_led(idx)[0]
class CalibratedPS4Controller(object):
# DS4 reports uncalibrated sensor data. Calibration coefficients
# can be retrieved using a feature report (0x2 USB / 0x5 BT).
# The values below are the processed calibration values for the
# DS4s matching the feature reports of PS4ControllerBluetooth/USB
# as dumped from hid-sony 'ds4_get_calibration_data'.
#
# Note we duplicate those values here in case the kernel changes them
# so we can have tests passing even if hid-tools doesn't have the
# correct values.
accelerometer_calibration_data = {
"x": {"bias": -73, "numer": 16384, "denom": 16472},
"y": {"bias": -352, "numer": 16384, "denom": 16344},
"z": {"bias": 81, "numer": 16384, "denom": 16319},
}
gyroscope_calibration_data = {
"x": {"bias": 0, "numer": 1105920, "denom": 17827},
"y": {"bias": 0, "numer": 1105920, "denom": 17777},
"z": {"bias": 0, "numer": 1105920, "denom": 17748},
}
class CalibratedPS4ControllerBluetooth(CalibratedPS4Controller, PS4ControllerBluetooth):
pass
class TestPS4ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest):
def create_device(self):
controller = CalibratedPS4ControllerBluetooth()
controller.application_matches = application_matches
return controller
class CalibratedPS4ControllerUSB(CalibratedPS4Controller, PS4ControllerUSB):
pass
class TestPS4ControllerUSB(SonyBaseTest.SonyPS4ControllerTest):
def create_device(self):
controller = CalibratedPS4ControllerUSB()
controller.application_matches = application_matches
return controller
class CalibratedPS5Controller(object):
# DualSense reports uncalibrated sensor data. Calibration coefficients
# can be retrieved using feature report 0x09.
# The values below are the processed calibration values for the
# DualSene matching the feature reports of PS5ControllerBluetooth/USB
# as dumped from hid-playstation 'dualsense_get_calibration_data'.
#
# Note we duplicate those values here in case the kernel changes them
# so we can have tests passing even if hid-tools doesn't have the
# correct values.
accelerometer_calibration_data = {
"x": {"bias": 0, "numer": 16384, "denom": 16374},
"y": {"bias": -114, "numer": 16384, "denom": 16362},
"z": {"bias": 2, "numer": 16384, "denom": 16395},
}
gyroscope_calibration_data = {
"x": {"bias": 0, "numer": 1105920, "denom": 17727},
"y": {"bias": 0, "numer": 1105920, "denom": 17728},
"z": {"bias": 0, "numer": 1105920, "denom": 17769},
}
class CalibratedPS5ControllerBluetooth(CalibratedPS5Controller, PS5ControllerBluetooth):
pass
class TestPS5ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest):
kernel_modules = [PS5_MODULE]
def create_device(self):
controller = CalibratedPS5ControllerBluetooth()
controller.application_matches = application_matches
return controller
class CalibratedPS5ControllerUSB(CalibratedPS5Controller, PS5ControllerUSB):
pass
class TestPS5ControllerUSB(SonyBaseTest.SonyPS4ControllerTest):
kernel_modules = [PS5_MODULE]
def create_device(self):
controller = CalibratedPS5ControllerUSB()
controller.application_matches = application_matches
return controller
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2021 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2021 Red Hat, Inc.
#
from . import base
import copy
from enum import Enum
from hidtools.util import BusType
import libevdev
import logging
import pytest
from typing import Dict, Tuple
logger = logging.getLogger("hidtools.test.tablet")
class PenState(Enum):
"""Pen states according to Microsoft reference:
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
"""
PEN_IS_OUT_OF_RANGE = (False, None)
PEN_IS_IN_RANGE = (False, libevdev.EV_KEY.BTN_TOOL_PEN)
PEN_IS_IN_CONTACT = (True, libevdev.EV_KEY.BTN_TOOL_PEN)
PEN_IS_IN_RANGE_WITH_ERASING_INTENT = (False, libevdev.EV_KEY.BTN_TOOL_RUBBER)
PEN_IS_ERASING = (True, libevdev.EV_KEY.BTN_TOOL_RUBBER)
def __init__(self, touch, tool):
self.touch = touch
self.tool = tool
@classmethod
def from_evdev(cls, evdev) -> "PenState":
touch = bool(evdev.value[libevdev.EV_KEY.BTN_TOUCH])
tool = None
if (
evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER]
and not evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN]
):
tool = libevdev.EV_KEY.BTN_TOOL_RUBBER
elif (
evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN]
and not evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER]
):
tool = libevdev.EV_KEY.BTN_TOOL_PEN
elif (
evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN]
or evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER]
):
raise ValueError("2 tools are not allowed")
return cls((touch, tool))
def apply(self, events) -> "PenState":
if libevdev.EV_SYN.SYN_REPORT in events:
raise ValueError("EV_SYN is in the event sequence")
touch = self.touch
touch_found = False
tool = self.tool
tool_found = False
for ev in events:
if ev == libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH):
if touch_found:
raise ValueError(f"duplicated BTN_TOUCH in {events}")
touch_found = True
touch = bool(ev.value)
elif ev in (
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN),
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_RUBBER),
):
if tool_found:
raise ValueError(f"duplicated BTN_TOOL_* in {events}")
tool_found = True
if ev.value:
tool = ev.code
else:
tool = None
new_state = PenState((touch, tool))
assert (
new_state in self.valid_transitions()
), f"moving from {self} to {new_state} is forbidden"
return new_state
def valid_transitions(self) -> Tuple["PenState", ...]:
"""Following the state machine in the URL above, with a couple of addition
for skipping the in-range state, due to historical reasons.
Note that those transitions are from the evdev point of view, not HID"""
if self == PenState.PEN_IS_OUT_OF_RANGE:
return (
PenState.PEN_IS_OUT_OF_RANGE,
PenState.PEN_IS_IN_RANGE,
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
PenState.PEN_IS_IN_CONTACT,
PenState.PEN_IS_ERASING,
)
if self == PenState.PEN_IS_IN_RANGE:
return (
PenState.PEN_IS_IN_RANGE,
PenState.PEN_IS_OUT_OF_RANGE,
PenState.PEN_IS_IN_CONTACT,
)
if self == PenState.PEN_IS_IN_CONTACT:
return (
PenState.PEN_IS_IN_CONTACT,
PenState.PEN_IS_IN_RANGE,
PenState.PEN_IS_OUT_OF_RANGE,
)
if self == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT:
return (
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
PenState.PEN_IS_OUT_OF_RANGE,
PenState.PEN_IS_ERASING,
)
if self == PenState.PEN_IS_ERASING:
return (
PenState.PEN_IS_ERASING,
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
PenState.PEN_IS_OUT_OF_RANGE,
)
return tuple()
class Data(object):
pass
class Pen(object):
def __init__(self, x, y):
self.x = x
self.y = y
self.tipswitch = False
self.tippressure = 15
self.azimuth = 0
self.inrange = False
self.width = 10
self.height = 10
self.barrelswitch = False
self.invert = False
self.eraser = False
self.x_tilt = 0
self.y_tilt = 0
self.twist = 0
self._old_values = None
self.current_state = None
def _restore(self):
if self._old_values is not None:
for i in [
"x",
"y",
"tippressure",
"azimuth",
"width",
"height",
"twist",
"x_tilt",
"y_tilt",
]:
setattr(self, i, getattr(self._old_values, i))
def move_to(self, state):
# fill in the previous values
if self.current_state == PenState.PEN_IS_OUT_OF_RANGE:
self._restore()
print(f"\n *** pen is moving to {state} ***")
if state == PenState.PEN_IS_OUT_OF_RANGE:
self._old_values = copy.copy(self)
self.x = 0
self.y = 0
self.tipswitch = False
self.tippressure = 0
self.azimuth = 0
self.inrange = False
self.width = 0
self.height = 0
self.invert = False
self.eraser = False
self.x_tilt = 0
self.y_tilt = 0
self.twist = 0
elif state == PenState.PEN_IS_IN_RANGE:
self.tipswitch = False
self.inrange = True
self.invert = False
self.eraser = False
elif state == PenState.PEN_IS_IN_CONTACT:
self.tipswitch = True
self.inrange = True
self.invert = False
self.eraser = False
elif state == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT:
self.tipswitch = False
self.inrange = True
self.invert = True
self.eraser = False
elif state == PenState.PEN_IS_ERASING:
self.tipswitch = False
self.inrange = True
self.invert = True
self.eraser = True
self.current_state = state
def __assert_axis(self, evdev, axis, value):
if (
axis == libevdev.EV_KEY.BTN_TOOL_RUBBER
and evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] is None
):
return
assert (
evdev.value[axis] == value
), f"assert evdev.value[{axis}] ({evdev.value[axis]}) != {value}"
def assert_expected_input_events(self, evdev):
assert evdev.value[libevdev.EV_ABS.ABS_X] == self.x
assert evdev.value[libevdev.EV_ABS.ABS_Y] == self.y
assert self.current_state == PenState.from_evdev(evdev)
@staticmethod
def legal_transitions() -> Dict[str, Tuple[PenState, ...]]:
"""This is the first half of the Windows Pen Implementation state machine:
we don't have Invert nor Erase bits, so just move in/out-of-range or proximity.
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
"""
return {
"in-range": (PenState.PEN_IS_IN_RANGE,),
"in-range -> out-of-range": (
PenState.PEN_IS_IN_RANGE,
PenState.PEN_IS_OUT_OF_RANGE,
),
"in-range -> touch": (PenState.PEN_IS_IN_RANGE, PenState.PEN_IS_IN_CONTACT),
"in-range -> touch -> release": (
PenState.PEN_IS_IN_RANGE,
PenState.PEN_IS_IN_CONTACT,
PenState.PEN_IS_IN_RANGE,
),
"in-range -> touch -> release -> out-of-range": (
PenState.PEN_IS_IN_RANGE,
PenState.PEN_IS_IN_CONTACT,
PenState.PEN_IS_IN_RANGE,
PenState.PEN_IS_OUT_OF_RANGE,
),
}
@staticmethod
def legal_transitions_with_invert() -> Dict[str, Tuple[PenState, ...]]:
"""This is the second half of the Windows Pen Implementation state machine:
we now have Invert and Erase bits, so move in/out or proximity with the intend
to erase.
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
"""
return {
"hover-erasing": (PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,),
"hover-erasing -> out-of-range": (
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
PenState.PEN_IS_OUT_OF_RANGE,
),
"hover-erasing -> erase": (
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
PenState.PEN_IS_ERASING,
),
"hover-erasing -> erase -> release": (
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
PenState.PEN_IS_ERASING,
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
),
"hover-erasing -> erase -> release -> out-of-range": (
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
PenState.PEN_IS_ERASING,
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
PenState.PEN_IS_OUT_OF_RANGE,
),
"hover-erasing -> in-range": (
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
PenState.PEN_IS_IN_RANGE,
),
"in-range -> hover-erasing": (
PenState.PEN_IS_IN_RANGE,
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
),
}
@staticmethod
def tolerated_transitions() -> Dict[str, Tuple[PenState, ...]]:
"""This is not adhering to the Windows Pen Implementation state machine
but we should expect the kernel to behave properly, mostly for historical
reasons."""
return {
"direct-in-contact": (PenState.PEN_IS_IN_CONTACT,),
"direct-in-contact -> out-of-range": (
PenState.PEN_IS_IN_CONTACT,
PenState.PEN_IS_OUT_OF_RANGE,
),
}
@staticmethod
def tolerated_transitions_with_invert() -> Dict[str, Tuple[PenState, ...]]:
"""This is the second half of the Windows Pen Implementation state machine:
we now have Invert and Erase bits, so move in/out or proximity with the intend
to erase.
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
"""
return {
"direct-erase": (PenState.PEN_IS_ERASING,),
"direct-erase -> out-of-range": (
PenState.PEN_IS_ERASING,
PenState.PEN_IS_OUT_OF_RANGE,
),
}
@staticmethod
def broken_transitions() -> Dict[str, Tuple[PenState, ...]]:
"""Those tests are definitely not part of the Windows specification.
However, a half broken device might export those transitions.
For example, a pen that has the eraser button might wobble between
touching and erasing if the tablet doesn't enforce the Windows
state machine."""
return {
"in-range -> touch -> erase -> hover-erase": (
PenState.PEN_IS_IN_RANGE,
PenState.PEN_IS_IN_CONTACT,
PenState.PEN_IS_ERASING,
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
),
"in-range -> erase -> hover-erase": (
PenState.PEN_IS_IN_RANGE,
PenState.PEN_IS_ERASING,
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
),
"hover-erase -> erase -> touch -> in-range": (
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
PenState.PEN_IS_ERASING,
PenState.PEN_IS_IN_CONTACT,
PenState.PEN_IS_IN_RANGE,
),
"hover-erase -> touch -> in-range": (
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
PenState.PEN_IS_IN_CONTACT,
PenState.PEN_IS_IN_RANGE,
),
"touch -> erase -> touch -> erase": (
PenState.PEN_IS_IN_CONTACT,
PenState.PEN_IS_ERASING,
PenState.PEN_IS_IN_CONTACT,
PenState.PEN_IS_ERASING,
),
}
class PenDigitizer(base.UHIDTestDevice):
def __init__(
self,
name,
rdesc_str=None,
rdesc=None,
application="Pen",
physical="Stylus",
input_info=(BusType.USB, 1, 2),
evdev_name_suffix=None,
):
super().__init__(name, application, rdesc_str, rdesc, input_info)
self.physical = physical
self.cur_application = application
if evdev_name_suffix is not None:
self.name += evdev_name_suffix
self.fields = []
for r in self.parsed_rdesc.input_reports.values():
if r.application_name == self.application:
physicals = [f.physical_name for f in r]
if self.physical not in physicals and None not in physicals:
continue
self.fields = [f.usage_name for f in r]
def event(self, pen):
rs = []
r = self.create_report(application=self.cur_application, data=pen)
self.call_input_event(r)
rs.append(r)
return rs
def get_report(self, req, rnum, rtype):
if rtype != self.UHID_FEATURE_REPORT:
return (1, [])
rdesc = None
for v in self.parsed_rdesc.feature_reports.values():
if v.report_ID == rnum:
rdesc = v
if rdesc is None:
return (1, [])
return (1, [])
def set_report(self, req, rnum, rtype, data):
if rtype != self.UHID_FEATURE_REPORT:
return 1
rdesc = None
for v in self.parsed_rdesc.feature_reports.values():
if v.report_ID == rnum:
rdesc = v
if rdesc is None:
return 1
return 1
class BaseTest:
class TestTablet(base.BaseTestCase.TestUhid):
def create_device(self):
raise Exception("please reimplement me in subclasses")
def post(self, uhdev, pen):
r = uhdev.event(pen)
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
return events
def validate_transitions(self, from_state, pen, evdev, events):
# check that the final state is correct
pen.assert_expected_input_events(evdev)
# check that the transitions are valid
sync_events = []
while libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT) in events:
# split the first EV_SYN from the list
idx = events.index(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT))
sync_events = events[:idx]
events = events[idx + 1 :]
# now check for a valid transition
from_state = from_state.apply(sync_events)
if events:
from_state = from_state.apply(sync_events)
def _test_states(self, state_list, scribble):
"""Internal method to test against a list of
transition between states.
state_list is a list of PenState objects
scribble is a boolean which tells if we need
to wobble a little the X,Y coordinates of the pen
between each state transition."""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
cur_state = PenState.PEN_IS_OUT_OF_RANGE
p = Pen(50, 60)
p.move_to(PenState.PEN_IS_OUT_OF_RANGE)
events = self.post(uhdev, p)
self.validate_transitions(cur_state, p, evdev, events)
cur_state = p.current_state
for state in state_list:
if scribble and cur_state != PenState.PEN_IS_OUT_OF_RANGE:
p.x += 1
p.y -= 1
events = self.post(uhdev, p)
self.validate_transitions(cur_state, p, evdev, events)
assert len(events) >= 3 # X, Y, SYN
p.move_to(state)
if scribble and state != PenState.PEN_IS_OUT_OF_RANGE:
p.x += 1
p.y -= 1
events = self.post(uhdev, p)
self.validate_transitions(cur_state, p, evdev, events)
cur_state = p.current_state
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
@pytest.mark.parametrize(
"state_list",
[pytest.param(v, id=k) for k, v in Pen.legal_transitions().items()],
)
def test_valid_pen_states(self, state_list, scribble):
"""This is the first half of the Windows Pen Implementation state machine:
we don't have Invert nor Erase bits, so just move in/out-of-range or proximity.
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
"""
self._test_states(state_list, scribble)
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
@pytest.mark.parametrize(
"state_list",
[pytest.param(v, id=k) for k, v in Pen.tolerated_transitions().items()],
)
def test_tolerated_pen_states(self, state_list, scribble):
"""This is not adhering to the Windows Pen Implementation state machine
but we should expect the kernel to behave properly, mostly for historical
reasons."""
self._test_states(state_list, scribble)
@pytest.mark.skip_if_uhdev(
lambda uhdev: "Invert" not in uhdev.fields,
"Device not compatible, missing Invert usage",
)
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
@pytest.mark.parametrize(
"state_list",
[
pytest.param(v, id=k)
for k, v in Pen.legal_transitions_with_invert().items()
],
)
def test_valid_invert_pen_states(self, state_list, scribble):
"""This is the second half of the Windows Pen Implementation state machine:
we now have Invert and Erase bits, so move in/out or proximity with the intend
to erase.
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
"""
self._test_states(state_list, scribble)
@pytest.mark.skip_if_uhdev(
lambda uhdev: "Invert" not in uhdev.fields,
"Device not compatible, missing Invert usage",
)
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
@pytest.mark.parametrize(
"state_list",
[
pytest.param(v, id=k)
for k, v in Pen.tolerated_transitions_with_invert().items()
],
)
def test_tolerated_invert_pen_states(self, state_list, scribble):
"""This is the second half of the Windows Pen Implementation state machine:
we now have Invert and Erase bits, so move in/out or proximity with the intend
to erase.
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
"""
self._test_states(state_list, scribble)
@pytest.mark.skip_if_uhdev(
lambda uhdev: "Invert" not in uhdev.fields,
"Device not compatible, missing Invert usage",
)
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
@pytest.mark.parametrize(
"state_list",
[pytest.param(v, id=k) for k, v in Pen.broken_transitions().items()],
)
def test_tolerated_broken_pen_states(self, state_list, scribble):
"""Those tests are definitely not part of the Windows specification.
However, a half broken device might export those transitions.
For example, a pen that has the eraser button might wobble between
touching and erasing if the tablet doesn't enforce the Windows
state machine."""
self._test_states(state_list, scribble)
@pytest.mark.skip_if_uhdev(
lambda uhdev: "Barrel Switch" not in uhdev.fields,
"Device not compatible, missing Barrel Switch usage",
)
def test_primary_button(self):
"""Primary button (stylus) pressed, reports as pressed even while hovering.
Actual reporting from the device: hid=TIPSWITCH,BARRELSWITCH,INRANGE (code=TOUCH,STYLUS,PEN):
{ 0, 0, 1 } <- hover
{ 0, 1, 1 } <- primary button pressed
{ 0, 1, 1 } <- liftoff
{ 0, 0, 0 } <- leaves
"""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
p = Pen(50, 60)
p.inrange = True
events = self.post(uhdev, p)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1) in events
assert evdev.value[libevdev.EV_ABS.ABS_X] == 50
assert evdev.value[libevdev.EV_ABS.ABS_Y] == 60
assert not evdev.value[libevdev.EV_KEY.BTN_STYLUS]
p.barrelswitch = True
events = self.post(uhdev, p)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 1) in events
p.x += 1
p.y -= 1
events = self.post(uhdev, p)
assert len(events) == 3 # X, Y, SYN
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 51) in events
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 59) in events
p.barrelswitch = False
events = self.post(uhdev, p)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 0) in events
p.inrange = False
events = self.post(uhdev, p)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 0) in events
@pytest.mark.skip_if_uhdev(
lambda uhdev: "Barrel Switch" not in uhdev.fields,
"Device not compatible, missing Barrel Switch usage",
)
def test_contact_primary_button(self):
"""Primary button (stylus) pressed, reports as pressed even while hovering.
Actual reporting from the device: hid=TIPSWITCH,BARRELSWITCH,INRANGE (code=TOUCH,STYLUS,PEN):
{ 0, 0, 1 } <- hover
{ 0, 1, 1 } <- primary button pressed
{ 1, 1, 1 } <- touch-down
{ 1, 1, 1 } <- still touch, scribble on the screen
{ 0, 1, 1 } <- liftoff
{ 0, 0, 0 } <- leaves
"""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
p = Pen(50, 60)
p.inrange = True
events = self.post(uhdev, p)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1) in events
assert evdev.value[libevdev.EV_ABS.ABS_X] == 50
assert evdev.value[libevdev.EV_ABS.ABS_Y] == 60
assert not evdev.value[libevdev.EV_KEY.BTN_STYLUS]
p.barrelswitch = True
events = self.post(uhdev, p)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 1) in events
p.tipswitch = True
events = self.post(uhdev, p)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events
assert evdev.value[libevdev.EV_KEY.BTN_STYLUS]
p.x += 1
p.y -= 1
events = self.post(uhdev, p)
assert len(events) == 3 # X, Y, SYN
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 51) in events
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 59) in events
p.tipswitch = False
events = self.post(uhdev, p)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events
p.barrelswitch = False
p.inrange = False
events = self.post(uhdev, p)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 0) in events
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 0) in events
class GXTP_pen(PenDigitizer):
def event(self, pen):
if not hasattr(self, "prev_tip_state"):
self.prev_tip_state = False
internal_pen = copy.copy(pen)
# bug in the controller: when the pen touches the
# surface, in-range stays to 1, but when
# the pen moves in-range gets reverted to 0
if pen.tipswitch and self.prev_tip_state:
internal_pen.inrange = False
self.prev_tip_state = pen.tipswitch
# another bug in the controller: when the pen is
# inverted, invert is set to 1, but as soon as
# the pen touches the surface, eraser is correctly
# set to 1 but invert is released
if pen.eraser:
internal_pen.invert = False
return super().event(internal_pen)
class USIPen(PenDigitizer):
pass
################################################################################
#
# Windows 7 compatible devices
#
################################################################################
# class TestEgalax_capacitive_0eef_7224(BaseTest.TestTablet):
# def create_device(self):
# return PenDigitizer('uhid test egalax-capacitive_0eef_7224',
# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 34 49 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 37 29 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 34 49 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 37 29 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0',
# input_info=(BusType.USB, 0x0eef, 0x7224),
# evdev_name_suffix=' Touchscreen')
#
#
# class TestEgalax_capacitive_0eef_72fa(BaseTest.TestTablet):
# def create_device(self):
# return PenDigitizer('uhid test egalax-capacitive_0eef_72fa',
# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 72 22 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 87 13 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 72 22 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 87 13 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0',
# input_info=(BusType.USB, 0x0eef, 0x72fa),
# evdev_name_suffix=' Touchscreen')
#
#
# class TestEgalax_capacitive_0eef_7336(BaseTest.TestTablet):
# def create_device(self):
# return PenDigitizer('uhid test egalax-capacitive_0eef_7336',
# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 c1 20 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 c2 18 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 c1 20 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 c2 18 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0',
# input_info=(BusType.USB, 0x0eef, 0x7336),
# evdev_name_suffix=' Touchscreen')
#
#
# class TestEgalax_capacitive_0eef_7337(BaseTest.TestTablet):
# def create_device(self):
# return PenDigitizer('uhid test egalax-capacitive_0eef_7337',
# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 ae 17 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 c3 0e 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 ae 17 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 c3 0e 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0',
# input_info=(BusType.USB, 0x0eef, 0x7337),
# evdev_name_suffix=' Touchscreen')
#
#
# class TestEgalax_capacitive_0eef_7349(BaseTest.TestTablet):
# def create_device(self):
# return PenDigitizer('uhid test egalax-capacitive_0eef_7349',
# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 34 49 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 37 29 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 34 49 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 37 29 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0',
# input_info=(BusType.USB, 0x0eef, 0x7349),
# evdev_name_suffix=' Touchscreen')
#
#
# class TestEgalax_capacitive_0eef_73f4(BaseTest.TestTablet):
# def create_device(self):
# return PenDigitizer('uhid test egalax-capacitive_0eef_73f4',
# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 96 4e 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 23 2c 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 96 4e 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 23 2c 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0',
# input_info=(BusType.USB, 0x0eef, 0x73f4),
# evdev_name_suffix=' Touchscreen')
#
# bogus: BTN_TOOL_PEN is not emitted
# class TestIrtouch_6615_0070(BaseTest.TestTablet):
# def create_device(self):
# return PenDigitizer('uhid test irtouch_6615_0070',
# rdesc='05 01 09 02 a1 01 85 10 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 06 81 03 05 01 09 30 09 31 15 00 26 ff 7f 75 10 95 02 81 02 c0 c0 05 0d 09 04 a1 01 85 30 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 c0 05 0d 09 54 15 00 26 02 00 75 08 95 01 81 02 85 03 09 55 15 00 26 ff 00 75 08 95 01 b1 02 c0 05 0d 09 0e a1 01 85 02 09 52 09 53 15 00 26 ff 00 75 08 95 02 b1 02 c0 05 0d 09 02 a1 01 85 20 09 20 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 85 01 06 00 ff 09 01 75 08 95 01 b1 02 c0 c0',
# input_info=(BusType.USB, 0x6615, 0x0070))
class TestNexio_1870_0100(BaseTest.TestTablet):
def create_device(self):
return PenDigitizer(
"uhid test nexio_1870_0100",
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 02 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 40 81 00 19 01 29 40 91 00 c0",
input_info=(BusType.USB, 0x1870, 0x0100),
)
class TestNexio_1870_010d(BaseTest.TestTablet):
def create_device(self):
return PenDigitizer(
"uhid test nexio_1870_010d",
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 06 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 3e 81 00 19 01 29 40 91 00 c0",
input_info=(BusType.USB, 0x1870, 0x010D),
)
class TestNexio_1870_0119(BaseTest.TestTablet):
def create_device(self):
return PenDigitizer(
"uhid test nexio_1870_0119",
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 06 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 3e 81 00 19 01 29 40 91 00 c0",
input_info=(BusType.USB, 0x1870, 0x0119),
)
################################################################################
#
# Windows 8 compatible devices
#
################################################################################
# bogus: application is 'undefined'
# class Testatmel_03eb_8409(BaseTest.TestTablet):
# def create_device(self):
# return PenDigitizer('uhid test atmel_03eb_8409', rdesc='05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 15 00 25 1f 75 05 09 54 95 01 81 02 75 03 25 01 95 01 81 03 75 08 85 02 09 55 25 10 b1 02 06 00 ff 85 05 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 00 a1 01 85 03 09 20 a1 00 15 00 25 01 75 01 95 01 09 42 81 02 09 44 81 02 09 45 81 02 81 03 09 32 81 02 95 03 81 03 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 46 18 06 26 77 0f 09 31 81 02 05 0d 09 30 15 01 26 ff 00 75 08 95 01 81 02 c0 c0')
class Testatmel_03eb_840b(BaseTest.TestTablet):
def create_device(self):
return PenDigitizer(
"uhid test atmel_03eb_840b",
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 15 00 25 1f 75 05 09 54 95 01 81 02 75 03 25 01 95 01 81 03 75 08 85 02 09 55 25 10 b1 02 06 00 ff 85 05 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 02 a1 01 85 03 09 20 a1 00 15 00 25 01 75 01 95 01 09 42 81 02 09 44 81 02 09 45 81 02 81 03 09 32 81 02 95 03 81 03 05 01 55 0e 65 11 35 00 75 10 95 02 46 00 0a 26 ff 0f 09 30 81 02 46 a0 05 26 ff 0f 09 31 81 02 05 0d 09 30 15 01 26 ff 00 75 08 95 01 81 02 c0 c0",
)
class Testn_trig_1b96_0c01(BaseTest.TestTablet):
def create_device(self):
return PenDigitizer(
"uhid test n_trig_1b96_0c01",
rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0",
)
class Testn_trig_1b96_0c03(BaseTest.TestTablet):
def create_device(self):
return PenDigitizer(
"uhid test n_trig_1b96_0c03",
rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0",
)
class Testn_trig_1b96_0f00(BaseTest.TestTablet):
def create_device(self):
return PenDigitizer(
"uhid test n_trig_1b96_0f00",
rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0",
)
class Testn_trig_1b96_0f04(BaseTest.TestTablet):
def create_device(self):
return PenDigitizer(
"uhid test n_trig_1b96_0f04",
rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0",
)
class Testn_trig_1b96_1000(BaseTest.TestTablet):
def create_device(self):
return PenDigitizer(
"uhid test n_trig_1b96_1000",
rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0",
)
class TestGXTP_27c6_0113(BaseTest.TestTablet):
def create_device(self):
return GXTP_pen(
"uhid test GXTP_27c6_0113",
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 55 0e 65 11 35 00 15 00 09 42 25 01 75 01 95 01 81 02 95 07 81 01 95 01 75 08 09 51 81 02 75 10 05 01 26 00 14 46 1f 07 09 30 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 95 07 81 01 95 01 75 08 09 51 81 02 75 10 05 01 26 00 14 46 1f 07 09 30 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 95 07 81 01 95 01 75 08 09 51 81 02 75 10 05 01 26 00 14 46 1f 07 09 30 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 01 75 08 09 51 95 01 81 02 05 01 26 00 14 75 10 55 0e 65 11 09 30 35 00 46 1f 07 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 01 75 08 09 51 95 01 81 02 05 01 26 00 14 75 10 55 0e 65 11 09 30 35 00 46 1f 07 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 54 15 00 25 7f 75 08 95 01 81 02 85 02 09 55 95 01 25 0a b1 02 85 03 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 02 a1 01 85 08 09 20 a1 00 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 04 81 02 95 01 81 03 09 32 81 02 95 02 81 03 95 01 75 08 09 51 81 02 05 01 09 30 75 10 95 01 a4 55 0e 65 11 35 00 26 00 14 46 1f 07 81 42 09 31 26 80 0c 46 77 04 81 42 b4 05 0d 09 30 26 ff 0f 81 02 09 3d 65 14 55 0e 36 d8 dc 46 28 23 16 d8 dc 26 28 23 81 02 09 3e 81 02 c0 c0 06 f0 ff 09 01 a1 01 85 0e 09 01 15 00 25 ff 75 08 95 40 91 02 09 01 15 00 25 ff 75 08 95 40 81 02 c0 05 01 09 06 a1 01 85 04 05 07 09 e3 15 00 25 01 75 01 95 01 81 02 95 07 81 03 c0",
)
################################################################################
#
# Windows 8 compatible devices with USI Pen
#
################################################################################
class TestElan_04f3_2A49(BaseTest.TestTablet):
def create_device(self):
return USIPen(
"uhid test Elan_04f3_2A49",
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 54 25 7f 96 01 00 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 16 00 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 16 00 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 20 09 01 91 02 c0 06 00 ff 09 01 a1 01 85 06 09 03 75 08 95 12 91 02 09 04 75 08 95 03 b1 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0 05 0d 09 02 a1 01 85 07 35 00 09 20 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0f 65 11 46 26 01 26 1c 48 81 42 09 31 46 a6 00 26 bc 2f 81 42 b4 05 0d 09 30 26 00 10 81 02 75 08 95 01 09 3b 25 64 81 42 09 38 15 00 25 02 81 02 09 5c 26 ff 00 81 02 09 5e 81 02 09 70 a1 02 15 01 25 06 09 72 09 73 09 74 09 75 09 76 09 77 81 20 09 5b 25 ff 75 40 81 02 c0 06 00 ff 75 08 95 02 09 01 81 02 c0 05 0d 85 60 09 81 a1 02 09 38 75 08 95 01 15 00 25 02 81 02 09 81 15 01 25 04 09 82 09 83 09 84 09 85 81 20 c0 85 61 09 5c a1 02 15 00 26 ff 00 75 08 95 01 09 38 b1 02 09 5c 26 ff 00 b1 02 09 5d 75 01 95 01 25 01 b1 02 95 07 b1 03 c0 85 62 09 5e a1 02 09 38 15 00 25 02 75 08 95 01 b1 02 09 5e 26 ff 00 b1 02 09 5f 75 01 25 01 b1 02 75 07 b1 03 c0 85 63 09 70 a1 02 75 08 95 01 15 00 25 02 09 38 b1 02 09 70 a1 02 25 06 09 72 09 73 09 74 09 75 09 76 09 77 b1 20 c0 09 71 75 01 25 01 b1 02 75 07 b1 03 c0 85 64 09 80 15 00 25 ff 75 40 95 01 b1 02 85 65 09 44 a1 02 09 38 75 08 95 01 25 02 b1 02 15 01 25 03 09 44 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 5a a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 45 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 c0 85 66 75 08 95 01 05 0d 09 90 a1 02 09 38 25 02 b1 02 09 91 75 10 26 ff 0f b1 02 09 92 75 40 25 ff b1 02 05 06 09 2a 75 08 26 ff 00 a1 02 09 2d b1 02 09 2e b1 02 c0 c0 85 67 05 06 09 2b a1 02 05 0d 25 02 09 38 b1 02 05 06 09 2b a1 02 09 2d 26 ff 00 b1 02 09 2e b1 02 c0 c0 85 68 06 00 ff 09 01 a1 02 05 0d 09 38 75 08 95 01 25 02 b1 02 06 00 ff 09 01 75 10 27 ff ff 00 00 b1 02 c0 85 69 05 0d 09 38 75 08 95 01 15 00 25 02 b1 02 c0 06 00 ff 09 81 a1 01 85 17 75 08 95 1f 09 05 81 02 c0",
input_info=(BusType.I2C, 0x04F3, 0x2A49),
)
class TestGoodix_27c6_0e00(BaseTest.TestTablet):
def create_device(self):
return USIPen(
"uhid test Elan_04f3_2A49",
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 55 0e 65 11 35 00 15 00 09 42 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 95 01 75 08 09 51 81 02 75 10 05 01 26 04 20 46 e6 09 09 30 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 95 01 75 08 09 51 81 02 75 10 05 01 26 04 20 46 e6 09 09 30 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 95 01 75 08 09 51 81 02 75 10 05 01 26 04 20 46 e6 09 09 30 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 75 08 09 51 95 01 81 02 05 01 26 04 20 75 10 55 0e 65 11 09 30 35 00 46 e6 09 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 75 08 09 51 95 01 81 02 05 01 26 04 20 75 10 55 0e 65 11 09 30 35 00 46 e6 09 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 54 15 00 25 7f 75 08 95 01 81 02 85 02 09 55 95 01 25 0a b1 02 85 03 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 02 a1 01 09 20 a1 00 85 08 05 01 a4 09 30 35 00 46 e6 09 15 00 26 04 20 55 0d 65 13 75 10 95 01 81 02 09 31 46 9a 06 26 60 15 81 02 b4 05 0d 09 38 95 01 75 08 15 00 25 01 81 02 09 30 75 10 26 ff 0f 81 02 09 31 81 02 09 42 09 44 09 5a 09 3c 09 45 09 32 75 01 95 06 25 01 81 02 95 02 81 03 09 3d 55 0e 65 14 36 d8 dc 46 28 23 16 d8 dc 26 28 23 95 01 75 10 81 02 09 3e 81 02 09 41 15 00 27 a0 8c 00 00 35 00 47 a0 8c 00 00 81 02 05 20 0a 53 04 65 00 16 01 f8 26 ff 07 75 10 95 01 81 02 0a 54 04 81 02 0a 55 04 81 02 0a 57 04 81 02 0a 58 04 81 02 0a 59 04 81 02 0a 72 04 81 02 0a 73 04 81 02 0a 74 04 81 02 05 0d 09 3b 15 00 25 64 75 08 81 02 09 5b 25 ff 75 40 81 02 06 00 ff 09 5b 75 20 81 02 05 0d 09 5c 26 ff 00 75 08 81 02 09 5e 81 02 09 70 a1 02 15 01 25 06 09 72 09 73 09 74 09 75 09 76 09 77 81 20 c0 06 00 ff 09 01 15 00 27 ff ff 00 00 75 10 95 01 81 02 85 09 09 81 a1 02 09 81 15 01 25 04 09 82 09 83 09 84 09 85 81 20 c0 85 10 09 5c a1 02 15 00 25 01 75 08 95 01 09 38 b1 02 09 5c 26 ff 00 b1 02 09 5d 75 01 95 01 25 01 b1 02 95 07 b1 03 c0 85 11 09 5e a1 02 09 38 15 00 25 01 75 08 95 01 b1 02 09 5e 26 ff 00 b1 02 09 5f 75 01 25 01 b1 02 75 07 b1 03 c0 85 12 09 70 a1 02 75 08 95 01 15 00 25 01 09 38 b1 02 09 70 a1 02 25 06 09 72 09 73 09 74 09 75 09 76 09 77 b1 20 c0 09 71 75 01 25 01 b1 02 75 07 b1 03 c0 85 13 09 80 15 00 25 ff 75 40 95 01 b1 02 85 14 09 44 a1 02 09 38 75 08 95 01 25 01 b1 02 15 01 25 03 09 44 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 5a a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 45 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 c0 85 15 75 08 95 01 05 0d 09 90 a1 02 09 38 25 01 b1 02 09 91 75 10 26 ff 0f b1 02 09 92 75 40 25 ff b1 02 05 06 09 2a 75 08 26 ff 00 a1 02 09 2d b1 02 09 2e b1 02 c0 c0 85 16 05 06 09 2b a1 02 05 0d 25 01 09 38 b1 02 05 06 09 2b a1 02 09 2d 26 ff 00 b1 02 09 2e b1 02 c0 c0 85 17 06 00 ff 09 01 a1 02 05 0d 09 38 75 08 95 01 25 01 b1 02 06 00 ff 09 01 75 10 27 ff ff 00 00 b1 02 c0 85 18 05 0d 09 38 75 08 95 01 15 00 25 01 b1 02 c0 c0 06 f0 ff 09 01 a1 01 85 0e 09 01 15 00 25 ff 75 08 95 40 91 02 09 01 15 00 25 ff 75 08 95 40 81 02 c0",
input_info=(BusType.I2C, 0x27C6, 0x0E00),
)
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2021 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2021 Red Hat, Inc.
#
# This is to ensure we don't crash when emulating USB devices
from . import base
import pytest
import logging
logger = logging.getLogger("hidtools.test.usb")
class USBDev(base.UHIDTestDevice):
# fmt: off
report_descriptor = [
0x05, 0x01, # .Usage Page (Generic Desktop) 0
0x09, 0x02, # .Usage (Mouse) 2
0xa1, 0x01, # .Collection (Application) 4
0x09, 0x02, # ..Usage (Mouse) 6
0xa1, 0x02, # ..Collection (Logical) 8
0x09, 0x01, # ...Usage (Pointer) 10
0xa1, 0x00, # ...Collection (Physical) 12
0x05, 0x09, # ....Usage Page (Button) 14
0x19, 0x01, # ....Usage Minimum (1) 16
0x29, 0x03, # ....Usage Maximum (3) 18
0x15, 0x00, # ....Logical Minimum (0) 20
0x25, 0x01, # ....Logical Maximum (1) 22
0x75, 0x01, # ....Report Size (1) 24
0x95, 0x03, # ....Report Count (3) 26
0x81, 0x02, # ....Input (Data,Var,Abs) 28
0x75, 0x05, # ....Report Size (5) 30
0x95, 0x01, # ....Report Count (1) 32
0x81, 0x03, # ....Input (Cnst,Var,Abs) 34
0x05, 0x01, # ....Usage Page (Generic Desktop) 36
0x09, 0x30, # ....Usage (X) 38
0x09, 0x31, # ....Usage (Y) 40
0x15, 0x81, # ....Logical Minimum (-127) 42
0x25, 0x7f, # ....Logical Maximum (127) 44
0x75, 0x08, # ....Report Size (8) 46
0x95, 0x02, # ....Report Count (2) 48
0x81, 0x06, # ....Input (Data,Var,Rel) 50
0xc0, # ...End Collection 52
0xc0, # ..End Collection 53
0xc0, # .End Collection 54
]
# fmt: on
def __init__(self, name=None, input_info=None):
super().__init__(
name, "Mouse", input_info=input_info, rdesc=USBDev.report_descriptor
)
# skip witing for udev events, it's likely that the report
# descriptor is wrong
def is_ready(self):
return True
# we don't have an evdev node here, so paper over
# the checks
def get_evdev(self, application=None):
return "OK"
class TestUSBDevice(base.BaseTestCase.TestUhid):
"""
Test class to test if an emulated USB device crashes
the kernel.
"""
# conftest.py is generating the following fixture:
#
# @pytest.fixture(params=[('modulename', 1, 2)])
# def usbVidPid(self, request):
# return request.param
@pytest.fixture()
def new_uhdev(self, usbVidPid, request):
self.module, self.vid, self.pid = usbVidPid
self._load_kernel_module(None, self.module)
return USBDev(input_info=(3, self.vid, self.pid))
def test_creation(self):
"""
inject the USB dev through uhid and immediately see if there is a crash:
uhid can create a USB device with the BUS_USB bus, and some
drivers assume that they can then access USB related structures
when they are actually provided a uhid device. This leads to
a crash because those access result in a segmentation fault.
The kernel should not crash on any (random) user space correct
use of its API. So run through all available modules and declared
devices to see if we can generate a uhid device without a crash.
The test is empty as the fixture `check_taint` is doing the job (and
honestly, when the kernel crashes, the whole machine freezes).
"""
assert True
#!/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2017 Red Hat, Inc.
# Copyright (c) 2020 Wacom Technology Corp.
#
# Authors:
# Jason Gerecke <jason.gerecke@wacom.com>
"""
Tests for the Wacom driver generic codepath.
This module tests the function of the Wacom driver's generic codepath.
The generic codepath is used by devices which are not explicitly listed
in the driver's device table. It uses the device's HID descriptor to
decode reports sent by the device.
"""
from .descriptors_wacom import (
wacom_pth660_v145,
wacom_pth660_v150,
wacom_pth860_v145,
wacom_pth860_v150,
wacom_pth460_v105,
)
import attr
from enum import Enum
from hidtools.hut import HUT
from hidtools.hid import HidUnit
from . import base
import libevdev
import pytest
import logging
logger = logging.getLogger("hidtools.test.wacom")
KERNEL_MODULE = ("wacom", "wacom")
class ProximityState(Enum):
"""
Enumeration of allowed proximity states.
"""
# Tool is not able to be sensed by the device
OUT = 0
# Tool is close enough to be sensed, but some data may be invalid
# or inaccurate
IN_PROXIMITY = 1
# Tool is close enough to be sensed with high accuracy. All data
# valid.
IN_RANGE = 2
def fill(self, reportdata):
"""Fill a report with approrpiate HID properties/values."""
reportdata.inrange = self in [ProximityState.IN_RANGE]
reportdata.wacomsense = self in [
ProximityState.IN_PROXIMITY,
ProximityState.IN_RANGE,
]
class ReportData:
"""
Placeholder for HID report values.
"""
pass
@attr.s
class Buttons:
"""
Stylus button state.
Describes the state of each of the buttons / "side switches" that
may be present on a stylus. Buttons set to 'None' indicate the
state is "unchanged" since the previous event.
"""
primary = attr.ib(default=None)
secondary = attr.ib(default=None)
tertiary = attr.ib(default=None)
@staticmethod
def clear():
"""Button object with all states cleared."""
return Buttons(False, False, False)
def fill(self, reportdata):
"""Fill a report with approrpiate HID properties/values."""
reportdata.barrelswitch = int(self.primary or 0)
reportdata.secondarybarrelswitch = int(self.secondary or 0)
reportdata.b3 = int(self.tertiary or 0)
@attr.s
class ToolID:
"""
Stylus tool identifiers.
Contains values used to identify a specific stylus, e.g. its serial
number and tool-type identifier. Values of ``0`` may sometimes be
used for the out-of-range condition.
"""
serial = attr.ib()
tooltype = attr.ib()
@staticmethod
def clear():
"""ToolID object with all fields cleared."""
return ToolID(0, 0)
def fill(self, reportdata):
"""Fill a report with approrpiate HID properties/values."""
reportdata.transducerserialnumber = self.serial & 0xFFFFFFFF
reportdata.serialhi = (self.serial >> 32) & 0xFFFFFFFF
reportdata.tooltype = self.tooltype
@attr.s
class PhysRange:
"""
Range of HID physical values, with units.
"""
unit = attr.ib()
min_size = attr.ib()
max_size = attr.ib()
CENTIMETER = HidUnit.from_string("SILinear: cm")
DEGREE = HidUnit.from_string("EnglishRotation: deg")
def contains(self, field):
"""
Check if the physical size of the provided field is in range.
Compare the physical size described by the provided HID field
against the range of sizes described by this object. This is
an exclusive range comparison (e.g. 0 cm is not within the
range 0 cm - 5 cm) and exact unit comparison (e.g. 1 inch is
not within the range 0 cm - 5 cm).
"""
phys_size = (field.physical_max - field.physical_min) * 10 ** (field.unit_exp)
return (
field.unit == self.unit.value
and phys_size > self.min_size
and phys_size < self.max_size
)
class BaseTablet(base.UHIDTestDevice):
"""
Skeleton object for all kinds of tablet devices.
"""
def __init__(self, rdesc, name=None, info=None):
assert rdesc is not None
super().__init__(name, "Pen", input_info=info, rdesc=rdesc)
self.buttons = Buttons.clear()
self.toolid = ToolID.clear()
self.proximity = ProximityState.OUT
self.offset = 0
self.ring = -1
self.ek0 = False
def match_evdev_rule(self, application, evdev):
"""
Filter out evdev nodes based on the requested application.
The Wacom driver may create several device nodes for each USB
interface device. It is crucial that we run tests with the
expected device node or things will obviously go off the rails.
Use the Wacom driver's usual naming conventions to apply a
sensible default filter.
"""
if application in ["Pen", "Pad"]:
return evdev.name.endswith(application)
else:
return True
def create_report(
self, x, y, pressure, buttons=None, toolid=None, proximity=None, reportID=None
):
"""
Return an input report for this device.
:param x: absolute x
:param y: absolute y
:param pressure: pressure
:param buttons: stylus button state. Use ``None`` for unchanged.
:param toolid: tool identifiers. Use ``None`` for unchanged.
:param proximity: a ProximityState indicating the sensor's ability
to detect and report attributes of this tool. Use ``None``
for unchanged.
:param reportID: the numeric report ID for this report, if needed
"""
if buttons is not None:
self.buttons = buttons
buttons = self.buttons
if toolid is not None:
self.toolid = toolid
toolid = self.toolid
if proximity is not None:
self.proximity = proximity
proximity = self.proximity
reportID = reportID or self.default_reportID
report = ReportData()
report.x = x
report.y = y
report.tippressure = pressure
report.tipswitch = pressure > 0
buttons.fill(report)
proximity.fill(report)
toolid.fill(report)
return super().create_report(report, reportID=reportID)
def create_report_heartbeat(self, reportID):
"""
Return a heartbeat input report for this device.
Heartbeat reports generally contain battery status information,
among other things.
"""
report = ReportData()
report.wacombatterycharging = 1
return super().create_report(report, reportID=reportID)
def create_report_pad(self, reportID, ring, ek0):
report = ReportData()
if ring is not None:
self.ring = ring
ring = self.ring
if ek0 is not None:
self.ek0 = ek0
ek0 = self.ek0
if ring >= 0:
report.wacomtouchring = ring
report.wacomtouchringstatus = 1
else:
report.wacomtouchring = 0x7F
report.wacomtouchringstatus = 0
report.wacomexpresskey00 = ek0
return super().create_report(report, reportID=reportID)
def event(self, x, y, pressure, buttons=None, toolid=None, proximity=None):
"""
Send an input event on the default report ID.
:param x: absolute x
:param y: absolute y
:param buttons: stylus button state. Use ``None`` for unchanged.
:param toolid: tool identifiers. Use ``None`` for unchanged.
:param proximity: a ProximityState indicating the sensor's ability
to detect and report attributes of this tool. Use ``None``
for unchanged.
"""
r = self.create_report(x, y, pressure, buttons, toolid, proximity)
self.call_input_event(r)
return [r]
def event_heartbeat(self, reportID):
"""
Send a heartbeat event on the requested report ID.
"""
r = self.create_report_heartbeat(reportID)
self.call_input_event(r)
return [r]
def event_pad(self, reportID, ring=None, ek0=None):
"""
Send a pad event on the requested report ID.
"""
r = self.create_report_pad(reportID, ring, ek0)
self.call_input_event(r)
return [r]
def get_report(self, req, rnum, rtype):
if rtype != self.UHID_FEATURE_REPORT:
return (1, [])
rdesc = None
for v in self.parsed_rdesc.feature_reports.values():
if v.report_ID == rnum:
rdesc = v
if rdesc is None:
return (1, [])
result = (1, [])
result = self.create_report_offset(rdesc) or result
return result
def create_report_offset(self, rdesc):
require = [
"Wacom Offset Left",
"Wacom Offset Top",
"Wacom Offset Right",
"Wacom Offset Bottom",
]
if not set(require).issubset(set([f.usage_name for f in rdesc])):
return None
report = ReportData()
report.wacomoffsetleft = self.offset
report.wacomoffsettop = self.offset
report.wacomoffsetright = self.offset
report.wacomoffsetbottom = self.offset
r = rdesc.create_report([report], None)
return (0, r)
class OpaqueTablet(BaseTablet):
"""
Bare-bones opaque tablet with a minimum of features.
A tablet stripped down to its absolute core. It is capable of
reporting X/Y position and if the pen is in contact. No pressure,
no barrel switches, no eraser. Notably it *does* report an "In
Range" flag, but this is only because the Wacom driver expects
one to function properly. The device uses only standard HID usages,
not any of Wacom's vendor-defined pages.
"""
# fmt: off
report_descriptor = [
0x05, 0x0D, # . Usage Page (Digitizer),
0x09, 0x01, # . Usage (Digitizer),
0xA1, 0x01, # . Collection (Application),
0x85, 0x01, # . Report ID (1),
0x09, 0x20, # . Usage (Stylus),
0xA1, 0x00, # . Collection (Physical),
0x09, 0x42, # . Usage (Tip Switch),
0x09, 0x32, # . Usage (In Range),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x02, # . Report Count (2),
0x81, 0x02, # . Input (Variable),
0x95, 0x06, # . Report Count (6),
0x81, 0x03, # . Input (Constant, Variable),
0x05, 0x01, # . Usage Page (Desktop),
0x09, 0x30, # . Usage (X),
0x27, 0x80, 0x3E, 0x00, 0x00, # . Logical Maximum (16000),
0x47, 0x80, 0x3E, 0x00, 0x00, # . Physical Maximum (16000),
0x65, 0x11, # . Unit (Centimeter),
0x55, 0x0D, # . Unit Exponent (13),
0x75, 0x10, # . Report Size (16),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x09, 0x31, # . Usage (Y),
0x27, 0x28, 0x23, 0x00, 0x00, # . Logical Maximum (9000),
0x47, 0x28, 0x23, 0x00, 0x00, # . Physical Maximum (9000),
0x81, 0x02, # . Input (Variable),
0xC0, # . End Collection,
0xC0, # . End Collection,
]
# fmt: on
def __init__(self, rdesc=report_descriptor, name=None, info=(0x3, 0x056A, 0x9999)):
super().__init__(rdesc, name, info)
self.default_reportID = 1
class OpaqueCTLTablet(BaseTablet):
"""
Opaque tablet similar to something in the CTL product line.
A pen-only tablet with most basic features you would expect from
an actual device. Position, eraser, pressure, barrel buttons.
Uses the Wacom vendor-defined usage page.
"""
# fmt: off
report_descriptor = [
0x06, 0x0D, 0xFF, # . Usage Page (Vnd Wacom Emr),
0x09, 0x01, # . Usage (Digitizer),
0xA1, 0x01, # . Collection (Application),
0x85, 0x10, # . Report ID (16),
0x09, 0x20, # . Usage (Stylus),
0x35, 0x00, # . Physical Minimum (0),
0x45, 0x00, # . Physical Maximum (0),
0x15, 0x00, # . Logical Minimum (0),
0x25, 0x01, # . Logical Maximum (1),
0xA1, 0x00, # . Collection (Physical),
0x09, 0x42, # . Usage (Tip Switch),
0x09, 0x44, # . Usage (Barrel Switch),
0x09, 0x5A, # . Usage (Secondary Barrel Switch),
0x09, 0x45, # . Usage (Eraser),
0x09, 0x3C, # . Usage (Invert),
0x09, 0x32, # . Usage (In Range),
0x09, 0x36, # . Usage (In Proximity),
0x25, 0x01, # . Logical Maximum (1),
0x75, 0x01, # . Report Size (1),
0x95, 0x07, # . Report Count (7),
0x81, 0x02, # . Input (Variable),
0x95, 0x01, # . Report Count (1),
0x81, 0x03, # . Input (Constant, Variable),
0x0A, 0x30, 0x01, # . Usage (X),
0x65, 0x11, # . Unit (Centimeter),
0x55, 0x0D, # . Unit Exponent (13),
0x47, 0x80, 0x3E, 0x00, 0x00, # . Physical Maximum (16000),
0x27, 0x80, 0x3E, 0x00, 0x00, # . Logical Maximum (16000),
0x75, 0x18, # . Report Size (24),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x0A, 0x31, 0x01, # . Usage (Y),
0x47, 0x28, 0x23, 0x00, 0x00, # . Physical Maximum (9000),
0x27, 0x28, 0x23, 0x00, 0x00, # . Logical Maximum (9000),
0x81, 0x02, # . Input (Variable),
0x09, 0x30, # . Usage (Tip Pressure),
0x55, 0x00, # . Unit Exponent (0),
0x65, 0x00, # . Unit,
0x47, 0x00, 0x00, 0x00, 0x00, # . Physical Maximum (0),
0x26, 0xFF, 0x0F, # . Logical Maximum (4095),
0x75, 0x10, # . Report Size (16),
0x81, 0x02, # . Input (Variable),
0x75, 0x08, # . Report Size (8),
0x95, 0x06, # . Report Count (6),
0x81, 0x03, # . Input (Constant, Variable),
0x0A, 0x32, 0x01, # . Usage (Z),
0x25, 0x3F, # . Logical Maximum (63),
0x75, 0x08, # . Report Size (8),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0x09, 0x5B, # . Usage (Transducer Serial Number),
0x09, 0x5C, # . Usage (Transducer Serial Number Hi),
0x17, 0x00, 0x00, 0x00, 0x80, # . Logical Minimum (-2147483648),
0x27, 0xFF, 0xFF, 0xFF, 0x7F, # . Logical Maximum (2147483647),
0x75, 0x20, # . Report Size (32),
0x95, 0x02, # . Report Count (2),
0x81, 0x02, # . Input (Variable),
0x09, 0x77, # . Usage (Tool Type),
0x15, 0x00, # . Logical Minimum (0),
0x26, 0xFF, 0x0F, # . Logical Maximum (4095),
0x75, 0x10, # . Report Size (16),
0x95, 0x01, # . Report Count (1),
0x81, 0x02, # . Input (Variable),
0xC0, # . End Collection,
0xC0 # . End Collection
]
# fmt: on
def __init__(self, rdesc=report_descriptor, name=None, info=(0x3, 0x056A, 0x9999)):
super().__init__(rdesc, name, info)
self.default_reportID = 16
class PTHX60_Pen(BaseTablet):
"""
Pen interface of a PTH-660 / PTH-860 / PTH-460 tablet.
This generation of devices are nearly identical to each other, though
the PTH-460 uses a slightly different descriptor construction (splits
the pad among several physical collections)
"""
def __init__(self, rdesc=None, name=None, info=None):
super().__init__(rdesc, name, info)
self.default_reportID = 16
class BaseTest:
class TestTablet(base.BaseTestCase.TestUhid):
kernel_modules = [KERNEL_MODULE]
def sync_and_assert_events(
self, report, expected_events, auto_syn=True, strict=False
):
"""
Assert we see the expected events in response to a report.
"""
uhdev = self.uhdev
syn_event = self.syn_event
if auto_syn:
expected_events.append(syn_event)
actual_events = uhdev.next_sync_events()
self.debug_reports(report, uhdev, actual_events)
if strict:
self.assertInputEvents(expected_events, actual_events)
else:
self.assertInputEventsIn(expected_events, actual_events)
def get_usages(self, uhdev):
def get_report_usages(report):
application = report.application
for field in report.fields:
if field.usages is not None:
for usage in field.usages:
yield (field, usage, application)
else:
yield (field, field.usage, application)
desc = uhdev.parsed_rdesc
reports = [
*desc.input_reports.values(),
*desc.feature_reports.values(),
*desc.output_reports.values(),
]
for report in reports:
for usage in get_report_usages(report):
yield usage
def assertName(self, uhdev):
"""
Assert that the name is as we expect.
The Wacom driver applies a number of decorations to the name
provided by the hardware. We cannot rely on the definition of
this assertion from the base class to work properly.
"""
evdev = uhdev.get_evdev()
expected_name = uhdev.name + " Pen"
if "wacom" not in expected_name.lower():
expected_name = "Wacom " + expected_name
assert evdev.name == expected_name
def test_descriptor_physicals(self):
"""
Verify that all HID usages which should have a physical range
actually do, and those which shouldn't don't. Also verify that
the associated unit is correct and within a sensible range.
"""
def usage_id(page_name, usage_name):
page = HUT.usage_page_from_name(page_name)
return (page.page_id << 16) | page[usage_name].usage
required = {
usage_id("Generic Desktop", "X"): PhysRange(
PhysRange.CENTIMETER, 5, 150
),
usage_id("Generic Desktop", "Y"): PhysRange(
PhysRange.CENTIMETER, 5, 150
),
usage_id("Digitizers", "X Tilt"): PhysRange(PhysRange.DEGREE, 90, 180),
usage_id("Digitizers", "Y Tilt"): PhysRange(PhysRange.DEGREE, 90, 180),
usage_id("Digitizers", "Twist"): PhysRange(PhysRange.DEGREE, 358, 360),
usage_id("Wacom", "X Tilt"): PhysRange(PhysRange.DEGREE, 90, 180),
usage_id("Wacom", "Y Tilt"): PhysRange(PhysRange.DEGREE, 90, 180),
usage_id("Wacom", "Twist"): PhysRange(PhysRange.DEGREE, 358, 360),
usage_id("Wacom", "X"): PhysRange(PhysRange.CENTIMETER, 5, 150),
usage_id("Wacom", "Y"): PhysRange(PhysRange.CENTIMETER, 5, 150),
usage_id("Wacom", "Wacom TouchRing"): PhysRange(
PhysRange.DEGREE, 358, 360
),
usage_id("Wacom", "Wacom Offset Left"): PhysRange(
PhysRange.CENTIMETER, 0, 0.5
),
usage_id("Wacom", "Wacom Offset Top"): PhysRange(
PhysRange.CENTIMETER, 0, 0.5
),
usage_id("Wacom", "Wacom Offset Right"): PhysRange(
PhysRange.CENTIMETER, 0, 0.5
),
usage_id("Wacom", "Wacom Offset Bottom"): PhysRange(
PhysRange.CENTIMETER, 0, 0.5
),
}
for field, usage, application in self.get_usages(self.uhdev):
if application == usage_id("Generic Desktop", "Mouse"):
# Ignore the vestigial Mouse collection which exists
# on Wacom tablets only for backwards compatibility.
continue
expect_physical = usage in required
phys_set = field.physical_min != 0 or field.physical_max != 0
assert phys_set == expect_physical
unit_set = field.unit != 0
assert unit_set == expect_physical
if unit_set:
assert required[usage].contains(field)
def test_prop_direct(self):
"""
Todo: Verify that INPUT_PROP_DIRECT is set on display devices.
"""
pass
def test_prop_pointer(self):
"""
Todo: Verify that INPUT_PROP_POINTER is set on opaque devices.
"""
pass
class TestOpaqueTablet(BaseTest.TestTablet):
def create_device(self):
return OpaqueTablet()
def test_sanity(self):
"""
Bring a pen into contact with the tablet, then remove it.
Ensure that we get the basic tool/touch/motion events that should
be sent by the driver.
"""
uhdev = self.uhdev
self.sync_and_assert_events(
uhdev.event(
100,
200,
pressure=300,
buttons=Buttons.clear(),
toolid=ToolID(serial=1, tooltype=1),
proximity=ProximityState.IN_RANGE,
),
[
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1),
libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 100),
libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 200),
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1),
],
)
self.sync_and_assert_events(
uhdev.event(110, 220, pressure=0),
[
libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 110),
libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 220),
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0),
],
)
self.sync_and_assert_events(
uhdev.event(
120,
230,
pressure=0,
toolid=ToolID.clear(),
proximity=ProximityState.OUT,
),
[
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 0),
],
)
self.sync_and_assert_events(
uhdev.event(130, 240, pressure=0), [], auto_syn=False, strict=True
)
class TestOpaqueCTLTablet(TestOpaqueTablet):
def create_device(self):
return OpaqueCTLTablet()
def test_buttons(self):
"""
Test that the barrel buttons (side switches) work as expected.
Press and release each button individually to verify that we get
the expected events.
"""
uhdev = self.uhdev
self.sync_and_assert_events(
uhdev.event(
100,
200,
pressure=0,
buttons=Buttons.clear(),
toolid=ToolID(serial=1, tooltype=1),
proximity=ProximityState.IN_RANGE,
),
[
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1),
libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 100),
libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 200),
libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1),
],
)
self.sync_and_assert_events(
uhdev.event(100, 200, pressure=0, buttons=Buttons(primary=True)),
[
libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 1),
libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1),
],
)
self.sync_and_assert_events(
uhdev.event(100, 200, pressure=0, buttons=Buttons(primary=False)),
[
libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 0),
libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1),
],
)
self.sync_and_assert_events(
uhdev.event(100, 200, pressure=0, buttons=Buttons(secondary=True)),
[
libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2, 1),
libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1),
],
)
self.sync_and_assert_events(
uhdev.event(100, 200, pressure=0, buttons=Buttons(secondary=False)),
[
libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2, 0),
libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1),
],
)
PTHX60_Devices = [
{"rdesc": wacom_pth660_v145, "info": (0x3, 0x056A, 0x0357)},
{"rdesc": wacom_pth660_v150, "info": (0x3, 0x056A, 0x0357)},
{"rdesc": wacom_pth860_v145, "info": (0x3, 0x056A, 0x0358)},
{"rdesc": wacom_pth860_v150, "info": (0x3, 0x056A, 0x0358)},
{"rdesc": wacom_pth460_v105, "info": (0x3, 0x056A, 0x0392)},
]
PTHX60_Names = [
"PTH-660/v145",
"PTH-660/v150",
"PTH-860/v145",
"PTH-860/v150",
"PTH-460/v105",
]
class TestPTHX60_Pen(TestOpaqueCTLTablet):
@pytest.fixture(
autouse=True, scope="class", params=PTHX60_Devices, ids=PTHX60_Names
)
def set_device_params(self, request):
request.cls.device_params = request.param
def create_device(self):
return PTHX60_Pen(**self.device_params)
@pytest.mark.xfail
def test_descriptor_physicals(self):
# XFAIL: Various documented errata
super().test_descriptor_physicals()
def test_heartbeat_spurious(self):
"""
Test that the heartbeat report does not send spurious events.
"""
uhdev = self.uhdev
self.sync_and_assert_events(
uhdev.event(
100,
200,
pressure=300,
buttons=Buttons.clear(),
toolid=ToolID(serial=1, tooltype=0x822),
proximity=ProximityState.IN_RANGE,
),
[
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1),
libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 100),
libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 200),
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1),
],
)
# Exactly zero events: not even a SYN
self.sync_and_assert_events(
uhdev.event_heartbeat(19), [], auto_syn=False, strict=True
)
self.sync_and_assert_events(
uhdev.event(110, 200, pressure=300),
[
libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 110),
],
)
def test_empty_pad_sync(self):
self.empty_pad_sync(num=3, denom=16, reverse=True)
def empty_pad_sync(self, num, denom, reverse):
"""
Test that multiple pad collections do not trigger empty syncs.
"""
def offset_rotation(value):
"""
Offset touchring rotation values by the same factor as the
Linux kernel. Tablets historically don't use the same origin
as HID, and it sometimes changes from tablet to tablet...
"""
evdev = self.uhdev.get_evdev()
info = evdev.absinfo[libevdev.EV_ABS.ABS_WHEEL]
delta = info.maximum - info.minimum + 1
if reverse:
value = info.maximum - value
value += num * delta // denom
if value > info.maximum:
value -= delta
elif value < info.minimum:
value += delta
return value
uhdev = self.uhdev
uhdev.application = "Pad"
evdev = uhdev.get_evdev()
print(evdev.name)
self.sync_and_assert_events(
uhdev.event_pad(reportID=17, ring=0, ek0=1),
[
libevdev.InputEvent(libevdev.EV_KEY.BTN_0, 1),
libevdev.InputEvent(libevdev.EV_ABS.ABS_WHEEL, offset_rotation(0)),
libevdev.InputEvent(libevdev.EV_ABS.ABS_MISC, 15),
],
)
self.sync_and_assert_events(
uhdev.event_pad(reportID=17, ring=1, ek0=1),
[libevdev.InputEvent(libevdev.EV_ABS.ABS_WHEEL, offset_rotation(1))],
)
self.sync_and_assert_events(
uhdev.event_pad(reportID=17, ring=2, ek0=0),
[
libevdev.InputEvent(libevdev.EV_ABS.ABS_WHEEL, offset_rotation(2)),
libevdev.InputEvent(libevdev.EV_KEY.BTN_0, 0),
],
)
......@@ -16,7 +16,6 @@ x86_64)
exit 1
;;
esac
DEFAULT_COMMAND="./hid_bpf"
SCRIPT_DIR="$(dirname $(realpath $0))"
OUTPUT_DIR="$SCRIPT_DIR/results"
KCONFIG_REL_PATHS=("${SCRIPT_DIR}/config" "${SCRIPT_DIR}/config.common" "${SCRIPT_DIR}/config.${ARCH}")
......@@ -25,7 +24,10 @@ NUM_COMPILE_JOBS="$(nproc)"
LOG_FILE_BASE="$(date +"hid_selftests.%Y-%m-%d_%H-%M-%S")"
LOG_FILE="${LOG_FILE_BASE}.log"
EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status"
CONTAINER_IMAGE="registry.fedoraproject.org/fedora:36"
CONTAINER_IMAGE="registry.freedesktop.org/libevdev/hid-tools/fedora/37:2023-02-17.1"
TARGETS="${TARGETS:=$(basename ${SCRIPT_DIR})}"
DEFAULT_COMMAND="pip3 install hid-tools; make -C tools/testing/selftests TARGETS=${TARGETS} run_tests"
usage()
{
......@@ -33,9 +35,9 @@ usage()
Usage: $0 [-i] [-s] [-d <output_dir>] -- [<command>]
<command> is the command you would normally run when you are in
tools/testing/selftests/bpf. e.g:
the source kernel direcory. e.g:
$0 -- ./hid_bpf
$0 -- ./tools/testing/selftests/hid/hid_bpf
If no command is specified and a debug shell (-s) is not requested,
"${DEFAULT_COMMAND}" will be run by default.
......@@ -43,11 +45,11 @@ If no command is specified and a debug shell (-s) is not requested,
If you build your kernel using KBUILD_OUTPUT= or O= options, these
can be passed as environment variables to the script:
O=<kernel_build_path> $0 -- ./hid_bpf
O=<kernel_build_path> $0 -- ./tools/testing/selftests/hid/hid_bpf
or
KBUILD_OUTPUT=<kernel_build_path> $0 -- ./hid_bpf
KBUILD_OUTPUT=<kernel_build_path> $0 -- ./tools/testing/selftests/hid/hid_bpf
Options:
......@@ -91,11 +93,14 @@ update_selftests()
run_vm()
{
local b2c="$1"
local kernel_bzimage="$2"
local command="$3"
local run_dir="$1"
local b2c="$2"
local kernel_bzimage="$3"
local command="$4"
local post_command=""
cd "${run_dir}"
if ! which "${QEMU_BINARY}" &> /dev/null; then
cat <<EOF
Could not find ${QEMU_BINARY}
......@@ -273,7 +278,7 @@ main()
fi
update_selftests "${kernel_checkout}" "${make_command}"
run_vm $b2c "${kernel_bzimage}" "${command}"
run_vm "${kernel_checkout}" $b2c "${kernel_bzimage}" "${command}"
if [[ "${debug_shell}" != "yes" ]]; then
echo "Logs saved in ${OUTPUT_DIR}/${LOG_FILE}"
fi
......
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