diff options
| -rw-r--r-- | drivers/hid/Kconfig | 3 | ||||
| -rw-r--r-- | drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c | 2 | ||||
| -rw-r--r-- | drivers/hid/hid-apple.c | 1 | ||||
| -rw-r--r-- | drivers/hid/hid-corsair-void.c | 5 | ||||
| -rw-r--r-- | drivers/hid/hid-elecom.c | 6 | ||||
| -rw-r--r-- | drivers/hid/hid-evision.c | 21 | ||||
| -rw-r--r-- | drivers/hid/hid-generic.c | 9 | ||||
| -rw-r--r-- | drivers/hid/hid-ids.h | 13 | ||||
| -rw-r--r-- | drivers/hid/hid-input.c | 12 | ||||
| -rw-r--r-- | drivers/hid/hid-lenovo.c | 17 | ||||
| -rw-r--r-- | drivers/hid/hid-lg-g15.c | 483 | ||||
| -rw-r--r-- | drivers/hid/hid-logitech-dj.c | 192 | ||||
| -rw-r--r-- | drivers/hid/hid-logitech-hidpp.c | 12 | ||||
| -rw-r--r-- | drivers/hid/hid-nintendo.c | 5 | ||||
| -rw-r--r-- | drivers/hid/hid-ntrig.c | 7 | ||||
| -rw-r--r-- | drivers/hid/hid-playstation.c | 2 | ||||
| -rw-r--r-- | drivers/hid/hid-quirks.c | 16 | ||||
| -rw-r--r-- | drivers/hid/hid-uclogic-core.c | 19 | ||||
| -rw-r--r-- | drivers/hid/hid-uclogic-params.c | 51 | ||||
| -rw-r--r-- | drivers/hid/hid-uclogic-params.h | 5 | ||||
| -rw-r--r-- | drivers/hid/hid-uclogic-rdesc.c | 125 | ||||
| -rw-r--r-- | drivers/hid/hid-uclogic-rdesc.h | 8 | ||||
| -rw-r--r-- | drivers/hid/hid-winwing.c | 171 | ||||
| -rw-r--r-- | drivers/hid/usbhid/hid-pidff.c | 4 | ||||
| -rw-r--r-- | include/linux/hid.h | 1 |
25 files changed, 1026 insertions, 164 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 04420a713be0..920a64b66b25 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -383,6 +383,7 @@ config HID_EVISION help Support for some EVision keyboards. Note that this is needed only when applying customization using userspace programs. + Support for some EVision devices requiring report descriptor fixups. config HID_EZKEY tristate "Ezkey BTC 8193 keyboard" @@ -1318,6 +1319,8 @@ config HID_WINWING help Support for WinWing Orion2 throttle base with the following grips: + * TGRIP-15E + * TGRIP-15EX * TGRIP-16EX * TGRIP-18 diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c index 0a9b44ce4904..b0bab2a1ddcc 100644 --- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c @@ -194,6 +194,8 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata) if (rc) goto cleanup; + mp2_ops->stop(privdata, cl_data->sensor_idx[i]); + amd_sfh_wait_for_response(privdata, cl_data->sensor_idx[i], DISABLE_SENSOR); writel(0, privdata->mmio + amd_get_p2c_val(privdata, 0)); mp2_ops->start(privdata, info); status = amd_sfh_wait_for_response diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 61404d7a43ee..57da4f86a9fa 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -355,6 +355,7 @@ static const struct apple_key_translation swapped_fn_leftctrl_keys[] = { static const struct apple_non_apple_keyboard non_apple_keyboards[] = { { "SONiX USB DEVICE" }, + { "SONiX AK870 PRO" }, { "Keychron" }, { "AONE" }, { "GANSS" }, diff --git a/drivers/hid/hid-corsair-void.c b/drivers/hid/hid-corsair-void.c index fee134a7eba3..5e9a5b8f7f16 100644 --- a/drivers/hid/hid-corsair-void.c +++ b/drivers/hid/hid-corsair-void.c @@ -553,9 +553,8 @@ static void corsair_void_add_battery(struct corsair_void_drvdata *drvdata) if (IS_ERR(new_supply)) { hid_err(drvdata->hid_dev, - "failed to register battery '%s' (reason: %ld)\n", - drvdata->battery_desc.name, - PTR_ERR(new_supply)); + "failed to register battery '%s' (reason: %pe)\n", + drvdata->battery_desc.name, new_supply); return; } diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c index 69771fd35006..981d1b6e9658 100644 --- a/drivers/hid/hid-elecom.c +++ b/drivers/hid/hid-elecom.c @@ -75,7 +75,8 @@ static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, */ mouse_button_fixup(hdev, rdesc, *rsize, 20, 28, 22, 14, 8); break; - case USB_DEVICE_ID_ELECOM_M_XT3URBK: + case USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB: + case USB_DEVICE_ID_ELECOM_M_XT3URBK_018F: case USB_DEVICE_ID_ELECOM_M_XT3DRBK: case USB_DEVICE_ID_ELECOM_M_XT4DRBK: /* @@ -119,7 +120,8 @@ static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, static const struct hid_device_id elecom_devices[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_018F) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) }, diff --git a/drivers/hid/hid-evision.c b/drivers/hid/hid-evision.c index bb5997078491..3e7f43ab80bb 100644 --- a/drivers/hid/hid-evision.c +++ b/drivers/hid/hid-evision.c @@ -18,6 +18,10 @@ static int evision_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { + /* mapping only applies to USB_DEVICE_ID_EVISION_ICL01 */ + if (hdev->product != USB_DEVICE_ID_EVISION_ICL01) + return 0; + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) return 0; @@ -37,8 +41,24 @@ static int evision_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } +#define REP_DSC_SIZE 236 +#define USAGE_MAX_INDEX 59 + +static const __u8 *evision_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + if (hdev->product == USB_DEVICE_ID_EV_TELINK_RECEIVER && + *rsize == REP_DSC_SIZE && rdesc[USAGE_MAX_INDEX] == 0x29 && + rdesc[USAGE_MAX_INDEX + 1] == 3) { + hid_info(hdev, "fixing EVision:TeLink Receiver report descriptor\n"); + rdesc[USAGE_MAX_INDEX + 1] = 5; // change usage max from 3 to 5 + } + return rdesc; +} + static const struct hid_device_id evision_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_EVISION, USB_DEVICE_ID_EVISION_ICL01) }, + { HID_USB_DEVICE(USB_VENDOR_ID_EVISION, USB_DEVICE_ID_EV_TELINK_RECEIVER) }, { } }; MODULE_DEVICE_TABLE(hid, evision_devices); @@ -47,6 +67,7 @@ static struct hid_driver evision_driver = { .name = "evision", .id_table = evision_devices, .input_mapping = evision_input_mapping, + .report_fixup = evision_report_fixup, }; module_hid_driver(evision_driver); diff --git a/drivers/hid/hid-generic.c b/drivers/hid/hid-generic.c index 9e04c6d0fcc8..c2de916747de 100644 --- a/drivers/hid/hid-generic.c +++ b/drivers/hid/hid-generic.c @@ -70,6 +70,14 @@ static int hid_generic_probe(struct hid_device *hdev, return hid_hw_start(hdev, HID_CONNECT_DEFAULT); } +static int hid_generic_reset_resume(struct hid_device *hdev) +{ + if (hdev->claimed & HID_CLAIMED_INPUT) + hidinput_reset_resume(hdev); + + return 0; +} + static const struct hid_device_id hid_table[] = { { HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, HID_ANY_ID, HID_ANY_ID) }, { } @@ -81,6 +89,7 @@ static struct hid_driver hid_generic = { .id_table = hid_table, .match = hid_generic_match, .probe = hid_generic_probe, + .reset_resume = hid_generic_reset_resume, }; module_hid_driver(hid_generic); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 0723b4b1c9ec..d31711f1aaec 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -449,7 +449,8 @@ #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 #define USB_DEVICE_ID_ELECOM_M_XGL20DLBK 0x00e6 -#define USB_DEVICE_ID_ELECOM_M_XT3URBK 0x00fb +#define USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB 0x00fb +#define USB_DEVICE_ID_ELECOM_M_XT3URBK_018F 0x018f #define USB_DEVICE_ID_ELECOM_M_XT3DRBK 0x00fc #define USB_DEVICE_ID_ELECOM_M_XT4DRBK 0x00fd #define USB_DEVICE_ID_ELECOM_M_DT1URBK 0x00fe @@ -476,6 +477,7 @@ #define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118 #define USB_VENDOR_ID_EVISION 0x320f +#define USB_DEVICE_ID_EV_TELINK_RECEIVER 0x226f #define USB_DEVICE_ID_EVISION_ICL01 0x5041 #define USB_VENDOR_ID_FFBEAST 0x045b @@ -718,6 +720,7 @@ #define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350 #define I2C_DEVICE_ID_ITE_LENOVO_LEGION_Y720 0x837a #define USB_DEVICE_ID_ITE_LENOVO_YOGA900 0x8396 +#define I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD 0x8987 #define USB_DEVICE_ID_ITE8595 0x8595 #define USB_DEVICE_ID_ITE_MEDION_E1239T 0xce50 @@ -879,6 +882,7 @@ #define USB_DEVICE_ID_LOGITECH_DUAL_ACTION 0xc216 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219 +#define USB_DEVICE_ID_LOGITECH_G13 0xc21c #define USB_DEVICE_ID_LOGITECH_G15_LCD 0xc222 #define USB_DEVICE_ID_LOGITECH_G11 0xc225 #define USB_DEVICE_ID_LOGITECH_G15_V2_LCD 0xc227 @@ -914,6 +918,8 @@ #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1 0xc539 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1 0xc53f #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_2 0xc543 +#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_3 0xc547 +#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_4 0xc54d #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY 0xc53a #define USB_DEVICE_ID_LOGITECH_BOLT_RECEIVER 0xc548 #define USB_DEVICE_ID_SPACETRAVELLER 0xc623 @@ -1419,6 +1425,7 @@ #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW 0x0933 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06 0x0078 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO 0x091b +#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_24_PRO 0x092d #define USB_DEVICE_ID_UGEE_TABLET_G5 0x0074 #define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071 #define USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720 0x0055 @@ -1543,7 +1550,7 @@ #define USB_VENDOR_ID_SIGNOTEC 0x2133 #define USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011 0x0018 -#define USB_VENDOR_ID_SMARTLINKTECHNOLOGY 0x4c4a -#define USB_DEVICE_ID_SMARTLINKTECHNOLOGY_4155 0x4155 +#define USB_VENDOR_ID_JIELI_SDK_DEFAULT 0x4c4a +#define USB_DEVICE_ID_JIELI_SDK_4155 0x4155 #endif diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index e56e7de53279..9f899ee83f0b 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -399,10 +399,11 @@ static const struct hid_device_id hid_battery_quirks[] = { { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_CHROMEBOOK_TROGDOR_POMPOM), HID_BATTERY_QUIRK_AVOID_QUERY }, /* - * Elan I2C-HID touchscreens seem to all report a non present battery, - * set HID_BATTERY_QUIRK_IGNORE for all Elan I2C-HID devices. + * Elan HID touchscreens seem to all report a non present battery, + * set HID_BATTERY_QUIRK_IGNORE for all Elan I2C and USB HID devices. */ { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE }, {} }; @@ -2399,6 +2400,13 @@ void hidinput_disconnect(struct hid_device *hid) } EXPORT_SYMBOL_GPL(hidinput_disconnect); +void hidinput_reset_resume(struct hid_device *hid) +{ + /* renegotiate host-device shared state after reset */ + hidinput_change_resolution_multipliers(hid); +} +EXPORT_SYMBOL_GPL(hidinput_reset_resume); + #ifdef CONFIG_HID_KUNIT_TEST #include "hid-input-test.c" #endif diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index 654879814f97..9cc3e029e9f6 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -148,6 +148,14 @@ static const __u8 lenovo_tpIIbtkbd_need_fixup_collection[] = { 0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */ }; +static const __u8 lenovo_yoga7x_kbd_need_fixup_collection[] = { + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x65, // Logical Maximum (101) + 0x05, 0x07, // Usage Page (Keyboard) + 0x19, 0x00, // Usage Minimum (0) + 0x29, 0xDD, // Usage Maximum (221) +}; + static const __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { @@ -177,6 +185,13 @@ static const __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc, rdesc[260] = 0x01; /* report count (2) = 0x01 */ } break; + case I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD: + if (*rsize == 176 && + memcmp(&rdesc[52], lenovo_yoga7x_kbd_need_fixup_collection, + sizeof(lenovo_yoga7x_kbd_need_fixup_collection)) == 0) { + rdesc[55] = rdesc[61]; // logical maximum = usage maximum + } + break; } return rdesc; } @@ -1538,6 +1553,8 @@ static const struct hid_device_id lenovo_devices[] = { USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB) }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB2) }, + { HID_DEVICE(BUS_I2C, HID_GROUP_GENERIC, + USB_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD) }, { } }; diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index f8605656257b..1a88bc44ada4 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -26,7 +26,24 @@ #define LG_G510_FEATURE_BACKLIGHT_RGB 0x05 #define LG_G510_FEATURE_POWER_ON_RGB 0x06 +#define LG_G510_INPUT_MACRO_KEYS 0x03 +#define LG_G510_INPUT_KBD_BACKLIGHT 0x04 + +#define LG_G13_INPUT_REPORT 0x01 +#define LG_G13_FEATURE_M_KEYS_LEDS 0x05 +#define LG_G13_FEATURE_BACKLIGHT_RGB 0x07 +#define LG_G13_BACKLIGHT_HW_ON_BIT 23 + +/** + * g13_input_report.keybits[] is not 32-bit aligned, so we can't use the bitops macros. + * + * @ary: Pointer to array of u8s + * @b: Bit index into ary, LSB first. Not range checked. + */ +#define TEST_BIT(ary, b) ((1 << ((b) & 7)) & (ary)[(b) >> 3]) + enum lg_g15_model { + LG_G13, LG_G15, LG_G15_V2, LG_G510, @@ -45,6 +62,12 @@ enum lg_g15_led_type { LG_G15_LED_MAX }; +struct g13_input_report { + u8 report_id; /* Report ID is always set to 1. */ + u8 joy_x, joy_y; + u8 keybits[5]; +}; + struct lg_g15_led { union { struct led_classdev cdev; @@ -63,12 +86,188 @@ struct lg_g15_data { struct mutex mutex; struct work_struct work; struct input_dev *input; + struct input_dev *input_js; /* Separate joystick device for G13. */ struct hid_device *hdev; enum lg_g15_model model; struct lg_g15_led leds[LG_G15_LED_MAX]; bool game_mode_enabled; + bool backlight_disabled; /* true == HW backlight toggled *OFF* */ }; +/********* G13 LED functions ***********/ +/* + * G13 retains no state across power cycles, and always powers up with the backlight on, + * color #5AFF6E, all macro key LEDs off. + */ +static int lg_g13_get_leds_state(struct lg_g15_data *g15) +{ + u8 * const tbuf = g15->transfer_buf; + int ret, high; + + /* RGB backlight. */ + ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_BACKLIGHT_RGB, + tbuf, 5, + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret != 5) { + hid_err(g15->hdev, "Error getting backlight brightness: %d\n", ret); + return (ret < 0) ? ret : -EIO; + } + + /* Normalize RGB intensities against the highest component. */ + high = max3(tbuf[1], tbuf[2], tbuf[3]); + if (high) { + g15->leds[LG_G15_KBD_BRIGHTNESS].red = + DIV_ROUND_CLOSEST(tbuf[1] * 255, high); + g15->leds[LG_G15_KBD_BRIGHTNESS].green = + DIV_ROUND_CLOSEST(tbuf[2] * 255, high); + g15->leds[LG_G15_KBD_BRIGHTNESS].blue = + DIV_ROUND_CLOSEST(tbuf[3] * 255, high); + g15->leds[LG_G15_KBD_BRIGHTNESS].brightness = high; + } else { + g15->leds[LG_G15_KBD_BRIGHTNESS].red = 255; + g15->leds[LG_G15_KBD_BRIGHTNESS].green = 255; + g15->leds[LG_G15_KBD_BRIGHTNESS].blue = 255; + g15->leds[LG_G15_KBD_BRIGHTNESS].brightness = 0; + } + + /* Macro LEDs. */ + ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_M_KEYS_LEDS, + tbuf, 5, + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret != 5) { + hid_err(g15->hdev, "Error getting macro LED brightness: %d\n", ret); + return (ret < 0) ? ret : -EIO; + } + + for (int i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; ++i) + g15->leds[i].brightness = !!(tbuf[1] & (1 << (i - LG_G15_MACRO_PRESET1))); + + /* + * Bit 23 of g13_input_report.keybits[] contains the backlight's + * current HW toggle state. Retrieve it from the device. + */ + ret = hid_hw_raw_request(g15->hdev, LG_G13_INPUT_REPORT, + tbuf, sizeof(struct g13_input_report), + HID_INPUT_REPORT, HID_REQ_GET_REPORT); + if (ret != sizeof(struct g13_input_report)) { + hid_err(g15->hdev, "Error getting backlight on/off state: %d\n", ret); + return (ret < 0) ? ret : -EIO; + } + g15->backlight_disabled = + !TEST_BIT(((struct g13_input_report *) tbuf)->keybits, + LG_G13_BACKLIGHT_HW_ON_BIT); + + return 0; +} + +static int lg_g13_kbd_led_write(struct lg_g15_data *g15, + struct lg_g15_led *g15_led, + enum led_brightness brightness) +{ + struct mc_subled const * const subleds = g15_led->mcdev.subled_info; + u8 * const tbuf = g15->transfer_buf; + int ret; + + guard(mutex)(&g15->mutex); + + led_mc_calc_color_components(&g15_led->mcdev, brightness); + + tbuf[0] = 5; + tbuf[1] = subleds[0].brightness; + tbuf[2] = subleds[1].brightness; + tbuf[3] = subleds[2].brightness; + tbuf[4] = 0; + + ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_BACKLIGHT_RGB, + tbuf, 5, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (ret != 5) { + hid_err(g15->hdev, "Error setting backlight brightness: %d\n", ret); + return (ret < 0) ? ret : -EIO; + } + + g15_led->brightness = brightness; + return 0; +} + +static int lg_g13_kbd_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) +{ + struct led_classdev_mc *mc = lcdev_to_mccdev(led_cdev); + struct lg_g15_led *g15_led = + container_of(mc, struct lg_g15_led, mcdev); + struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent); + + /* Ignore LED off on unregister / keyboard unplug */ + if (led_cdev->flags & LED_UNREGISTERING) + return 0; + + return lg_g13_kbd_led_write(g15, g15_led, brightness); +} + +static enum led_brightness lg_g13_kbd_led_get(struct led_classdev *led_cdev) +{ + struct led_classdev_mc const * const mc = lcdev_to_mccdev(led_cdev); + struct lg_g15_led const *g15_led = + container_of(mc, struct lg_g15_led, mcdev); + + return g15_led->brightness; +} + +static int lg_g13_mkey_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) +{ + struct lg_g15_led *g15_led = + container_of(led_cdev, struct lg_g15_led, cdev); + struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent); + int i, ret; + u8 * const tbuf = g15->transfer_buf; + u8 val, mask = 0; + + /* Ignore LED off on unregister / keyboard unplug */ + if (led_cdev->flags & LED_UNREGISTERING) + return 0; + + guard(mutex)(&g15->mutex); + + for (i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; ++i) { + if (i == g15_led->led) + val = brightness; + else + val = g15->leds[i].brightness; + + if (val) + mask |= 1 << (i - LG_G15_MACRO_PRESET1); + } + + tbuf[0] = 5; + tbuf[1] = mask; + tbuf[2] = 0; + tbuf[3] = 0; + tbuf[4] = 0; + + ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_M_KEYS_LEDS, + tbuf, 5, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (ret != 5) { + hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret); + return (ret < 0) ? ret : -EIO; + } + + g15_led->brightness = brightness; + return 0; +} + +static enum led_brightness lg_g13_mkey_led_get(struct led_classdev *led_cdev) +{ + /* + * G13 doesn't change macro key LEDs behind our back, so they're + * whatever we last set them to. + */ + struct lg_g15_led *g15_led = + container_of(led_cdev, struct lg_g15_led, cdev); + + return g15_led->brightness; +} + /******** G15 and G15 v2 LED functions ********/ static int lg_g15_update_led_brightness(struct lg_g15_data *g15) @@ -227,6 +426,20 @@ static int lg_g510_get_initial_led_brightness(struct lg_g15_data *g15, int i) g15->leds[i].brightness = 0; } + if (i) + return 0; + + ret = hid_hw_raw_request(g15->hdev, LG_G510_INPUT_KBD_BACKLIGHT, + g15->transfer_buf, 2, + HID_INPUT_REPORT, HID_REQ_GET_REPORT); + if (ret != 2) { + /* This can happen when a KVM switch is used, so only warn. */ + hid_warn(g15->hdev, "Error getting backlight state: %d\n", ret); + return 0; + } + + g15->backlight_disabled = g15->transfer_buf[1] & 0x04; + return 0; } @@ -390,6 +603,8 @@ static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15) int ret; switch (g15->model) { + case LG_G13: + return lg_g13_get_leds_state(g15); case LG_G15: case LG_G15_V2: return lg_g15_update_led_brightness(g15); @@ -417,6 +632,108 @@ static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15) /******** Input functions ********/ +/* Table mapping keybits[] bit positions to event codes. */ +/* Note: Indices are discontinuous to aid readability. */ +static const u16 g13_keys_for_bits[] = { + /* Main keypad - keys G1 - G22 */ + [0] = KEY_MACRO1, + [1] = KEY_MACRO2, + [2] = KEY_MACRO3, + [3] = KEY_MACRO4, + [4] = KEY_MACRO5, + [5] = KEY_MACRO6, + [6] = KEY_MACRO7, + [7] = KEY_MACRO8, + [8] = KEY_MACRO9, + [9] = KEY_MACRO10, + [10] = KEY_MACRO11, + [11] = KEY_MACRO12, + [12] = KEY_MACRO13, + [13] = KEY_MACRO14, + [14] = KEY_MACRO15, + [15] = KEY_MACRO16, + [16] = KEY_MACRO17, + [17] = KEY_MACRO18, + [18] = KEY_MACRO19, + [19] = KEY_MACRO20, + [20] = KEY_MACRO21, + [21] = KEY_MACRO22, + + /* LCD menu buttons. */ + [24] = KEY_KBD_LCD_MENU5, /* "Next page" button */ + [25] = KEY_KBD_LCD_MENU1, /* Left-most */ + [26] = KEY_KBD_LCD_MENU2, + [27] = KEY_KBD_LCD_MENU3, + [28] = KEY_KBD_LCD_MENU4, /* Right-most */ + + /* Macro preset and record buttons; have red LEDs under them. */ + [29] = KEY_MACRO_PRESET1, + [30] = KEY_MACRO_PRESET2, + [31] = KEY_MACRO_PRESET3, + [32] = KEY_MACRO_RECORD_START, + + /* 33-35 handled by joystick device. */ + + /* Backlight toggle. */ + [37] = KEY_LIGHTS_TOGGLE, +}; + +#define G13_JS_KEYBITS_OFFSET 33 + +static const u16 g13_keys_for_bits_js[] = { + /* Joystick buttons */ + /* These keybits are at bit indices 33, 34, and 35. */ + BTN_BASE, /* Left side */ + BTN_BASE2, /* Bottom side */ + BTN_THUMB, /* Stick depress */ +}; + +static int lg_g13_event(struct lg_g15_data *g15, u8 const *data) +{ + struct g13_input_report const * const rep = (struct g13_input_report *) data; + int i, val; + bool backlight_disabled; + + /* + * Main macropad and menu keys. + * Emit key events defined for each bit position. + */ + for (i = 0; i < ARRAY_SIZE(g13_keys_for_bits); ++i) { + if (g13_keys_for_bits[i]) { + val = TEST_BIT(rep->keybits, i); + input_report_key(g15->input, g13_keys_for_bits[i], val); + } + } + input_sync(g15->input); + + /* + * Joystick. + * Emit button and deflection events. + */ + for (i = 0; i < ARRAY_SIZE(g13_keys_for_bits_js); ++i) { + val = TEST_BIT(rep->keybits, i + G13_JS_KEYBITS_OFFSET); + input_report_key(g15->input_js, g13_keys_for_bits_js[i], val); + } + input_report_abs(g15->input_js, ABS_X, rep->joy_x); + input_report_abs(g15->input_js, ABS_Y, rep->joy_y); + input_sync(g15->input_js); + + /* + * Bit 23 of keybits[] reports the current backlight on/off state. If + * it has changed from the last cached value, apply an update. + */ + backlight_disabled = !TEST_BIT(rep->keybits, LG_G13_BACKLIGHT_HW_ON_BIT); + if (backlight_disabled ^ g15->backlight_disabled) { + led_classdev_notify_brightness_hw_changed( + &g15->leds[LG_G15_KBD_BRIGHTNESS].mcdev.led_cdev, + backlight_disabled + ? 0 : g15->leds[LG_G15_KBD_BRIGHTNESS].brightness); + g15->backlight_disabled = backlight_disabled; + } + + return 0; +} + /* On the G15 Mark I Logitech has been quite creative with which bit is what */ static void lg_g15_handle_lcd_menu_keys(struct lg_g15_data *g15, u8 *data) { @@ -549,14 +866,24 @@ static int lg_g510_event(struct lg_g15_data *g15, u8 *data) static int lg_g510_leds_event(struct lg_g15_data *g15, u8 *data) { + struct lg_g15_led *g15_led = &g15->leds[LG_G15_KBD_BRIGHTNESS]; bool backlight_disabled; + backlight_disabled = data[1] & 0x04; + if (backlight_disabled == g15->backlight_disabled) + return 0; + + led_classdev_notify_brightness_hw_changed( + &g15_led->mcdev.led_cdev, + backlight_disabled ? 0 : g15_led->brightness); + + g15->backlight_disabled = backlight_disabled; + /* * The G510 ignores backlight updates when the backlight is turned off * through the light toggle button on the keyboard, to work around this * we queue a workitem to sync values when the backlight is turned on. */ - backlight_disabled = data[1] & 0x04; if (!backlight_disabled) schedule_work(&g15->work); @@ -572,6 +899,10 @@ static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report, return 0; switch (g15->model) { + case LG_G13: + if (data[0] == 0x01 && size == sizeof(struct g13_input_report)) + return lg_g13_event(g15, data); + break; case LG_G15: if (data[0] == 0x02 && size == 9) return lg_g15_event(g15, data); @@ -588,9 +919,9 @@ static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report, break; case LG_G510: case LG_G510_USB_AUDIO: - if (data[0] == 0x03 && size == 5) + if (data[0] == LG_G510_INPUT_MACRO_KEYS && size == 5) return lg_g510_event(g15, data); - if (data[0] == 0x04 && size == 2) + if (data[0] == LG_G510_INPUT_KBD_BACKLIGHT && size == 2) return lg_g510_leds_event(g15, data); break; } @@ -616,13 +947,24 @@ static void lg_g15_setup_led_rgb(struct lg_g15_data *g15, int index) { int i; struct mc_subled *subled_info; - - g15->leds[index].mcdev.led_cdev.brightness_set_blocking = - lg_g510_kbd_led_set; - g15->leds[index].mcdev.led_cdev.brightness_get = - lg_g510_kbd_led_get; - g15->leds[index].mcdev.led_cdev.max_brightness = 255; - g15->leds[index].mcdev.num_colors = 3; + struct lg_g15_led * const gled = &g15->leds[index]; + + if (g15->model == LG_G13) { + gled->mcdev.led_cdev.brightness_set_blocking = + lg_g13_kbd_led_set; + gled->mcdev.led_cdev.brightness_get = + lg_g13_kbd_led_get; + gled->mcdev.led_cdev.flags = LED_BRIGHT_HW_CHANGED; + } else { + gled->mcdev.led_cdev.brightness_set_blocking = + lg_g510_kbd_led_set; + gled->mcdev.led_cdev.brightness_get = + lg_g510_kbd_led_get; + if (index == LG_G15_KBD_BRIGHTNESS) + g15->leds[index].mcdev.led_cdev.flags = LED_BRIGHT_HW_CHANGED; + } + gled->mcdev.led_cdev.max_brightness = 255; + gled->mcdev.num_colors = 3; subled_info = devm_kcalloc(&g15->hdev->dev, 3, sizeof(*subled_info), GFP_KERNEL); if (!subled_info) @@ -632,20 +974,20 @@ static void lg_g15_setup_led_rgb(struct lg_g15_data *g15, int index) switch (i + 1) { case LED_COLOR_ID_RED: subled_info[i].color_index = LED_COLOR_ID_RED; - subled_info[i].intensity = g15->leds[index].red; + subled_info[i].intensity = gled->red; break; case LED_COLOR_ID_GREEN: subled_info[i].color_index = LED_COLOR_ID_GREEN; - subled_info[i].intensity = g15->leds[index].green; + subled_info[i].intensity = gled->green; break; case LED_COLOR_ID_BLUE: subled_info[i].color_index = LED_COLOR_ID_BLUE; - subled_info[i].intensity = g15->leds[index].blue; + subled_info[i].intensity = gled->blue; break; } subled_info[i].channel = i; } - g15->leds[index].mcdev.subled_info = subled_info; + gled->mcdev.subled_info = subled_info; } static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name) @@ -656,6 +998,23 @@ static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name) g15->leds[i].cdev.name = name; switch (g15->model) { + case LG_G13: + if (i < LG_G15_BRIGHTNESS_MAX) { + /* RGB backlight. */ + lg_g15_setup_led_rgb(g15, i); + ret = devm_led_classdev_multicolor_register_ext(&g15->hdev->dev, + &g15->leds[i].mcdev, + NULL); + } else { + /* Macro keys */ + g15->leds[i].cdev.brightness_set_blocking = lg_g13_mkey_led_set; + g15->leds[i].cdev.brightness_get = lg_g13_mkey_led_get; + g15->leds[i].cdev.max_brightness = 1; + + ret = devm_led_classdev_register(&g15->hdev->dev, + &g15->leds[i].cdev); + } + break; case LG_G15: case LG_G15_V2: g15->leds[i].cdev.brightness_get = lg_g15_led_get; @@ -702,11 +1061,9 @@ static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name) } /* Common input device init code shared between keyboards and Z-10 speaker handling */ -static void lg_g15_init_input_dev(struct hid_device *hdev, struct input_dev *input, - const char *name) +static void lg_g15_init_input_dev_core(struct hid_device *hdev, struct input_dev *input, + char const *name) { - int i; - input->name = name; input->phys = hdev->phys; input->uniq = hdev->uniq; @@ -717,12 +1074,42 @@ static void lg_g15_init_input_dev(struct hid_device *hdev, struct input_dev *inp input->dev.parent = &hdev->dev; input->open = lg_g15_input_open; input->close = lg_g15_input_close; +} + +static void lg_g15_init_input_dev(struct hid_device *hdev, struct input_dev *input, + const char *name) +{ + int i; + + lg_g15_init_input_dev_core(hdev, input, name); /* Keys below the LCD, intended for controlling a menu on the LCD */ for (i = 0; i < 5; i++) input_set_capability(input, EV_KEY, KEY_KBD_LCD_MENU1 + i); } +static void lg_g13_init_input_dev(struct hid_device *hdev, + struct input_dev *input, const char *name, + struct input_dev *input_js, const char *name_js) +{ + /* Macropad. */ + lg_g15_init_input_dev_core(hdev, input, name); + for (int i = 0; i < ARRAY_SIZE(g13_keys_for_bits); ++i) { + if (g13_keys_for_bits[i]) + input_set_capability(input, EV_KEY, g13_keys_for_bits[i]); + } + + /* OBTW, we're a joystick, too... */ + lg_g15_init_input_dev_core(hdev, input_js, name_js); + for (int i = 0; i < ARRAY_SIZE(g13_keys_for_bits_js); ++i) + input_set_capability(input_js, EV_KEY, g13_keys_for_bits_js[i]); + + input_set_capability(input_js, EV_ABS, ABS_X); + input_set_abs_params(input_js, ABS_X, 0, 255, 0, 0); + input_set_capability(input_js, EV_ABS, ABS_Y); + input_set_abs_params(input_js, ABS_Y, 0, 255, 0, 0); +} + static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) { static const char * const led_names[] = { @@ -739,7 +1126,7 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) unsigned int connect_mask = 0; bool has_ff000000 = false; struct lg_g15_data *g15; - struct input_dev *input; + struct input_dev *input, *input_js; struct hid_report *rep; int ret, i, gkeys = 0; @@ -778,6 +1165,25 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) hid_set_drvdata(hdev, (void *)g15); switch (g15->model) { + case LG_G13: + /* + * The G13 has an analog thumbstick with nearby buttons. Some + * libraries and applications are known to ignore devices that + * don't "look like" a joystick, and a device with two ABS axes + * and 25+ macro keys would confuse them. + * + * Create an additional input device dedicated to appear as a + * simplified joystick (two ABS axes, three BTN buttons). + */ + input_js = devm_input_allocate_device(&hdev->dev); + if (!input_js) + return -ENOMEM; + g15->input_js = input_js; + input_set_drvdata(input_js, hdev); + + connect_mask = HID_CONNECT_HIDRAW; + gkeys = 25; + break; case LG_G15: INIT_WORK(&g15->work, lg_g15_leds_changed_work); /* @@ -859,6 +1265,38 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) goto error_hw_stop; return 0; /* All done */ + } else if (g15->model == LG_G13) { + static char const * const g13_led_names[] = { + /* Backlight is shared between LCD and keys. */ + "g13:rgb:kbd_backlight", + NULL, /* Keep in sync with led_type enum */ + "g13:red:macro_preset_1", + "g13:red:macro_preset_2", + "g13:red:macro_preset_3", + "g13:red:macro_record", + }; + lg_g13_init_input_dev(hdev, + input, "Logitech G13 Gaming Keypad", + input_js, "Logitech G13 Thumbstick"); + ret = input_register_device(input); + if (ret) + goto error_hw_stop; + ret = input_register_device(input_js); + if (ret) + goto error_hw_stop; + + for (i = 0; i < ARRAY_SIZE(g13_led_names); ++i) { + if (g13_led_names[i]) { + ret = lg_g15_register_led(g15, i, g13_led_names[i]); + if (ret) + goto error_hw_stop; + } + } + led_classdev_notify_brightness_hw_changed( + &g15->leds[LG_G15_KBD_BRIGHTNESS].mcdev.led_cdev, + g15->backlight_disabled + ? 0 : g15->leds[LG_G15_KBD_BRIGHTNESS].brightness); + return 0; } /* Setup and register input device */ @@ -903,6 +1341,13 @@ error_hw_stop: } static const struct hid_device_id lg_g15_devices[] = { + /* + * The G13 is a macropad-only device with an LCD, LED backlighing, + * and joystick. + */ + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_G13), + .driver_data = LG_G13 }, /* The G11 is a G15 without the LCD, treat it as a G15 */ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G11), diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index cce54dd9884a..44b716697510 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -116,6 +116,7 @@ enum recvr_type { recvr_type_dj, recvr_type_hidpp, recvr_type_gaming_hidpp, + recvr_type_gaming_hidpp_ls_1_3, recvr_type_mouse_only, recvr_type_27mhz, recvr_type_bluetooth, @@ -148,6 +149,7 @@ struct dj_receiver_dev { struct kfifo notif_fifo; unsigned long last_query; /* in jiffies */ bool ready; + bool dj_mode; enum recvr_type type; unsigned int unnumbered_application; spinlock_t lock; @@ -211,6 +213,44 @@ static const char kbd_descriptor[] = { 0xC0 }; +/* Gaming Keyboard descriptor (1) */ +static const char kbd_lightspeed_1_3_descriptor[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x06, /* Usage (Keyboard) */ + 0xA1, 0x01, /* Collection (Application) */ + 0x85, 0x01, /* Report ID (1) */ + 0x05, 0x07, /* Usage Page (Kbrd/Keypad) */ + 0x19, 0xE0, /* Usage Minimum (0xE0) */ + 0x29, 0xE7, /* Usage Maximum (0xE7) */ + 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) */ + 0x95, 0x70, /* Report Count (112) */ + 0x19, 0x04, /* Usage Minimum (0x04) */ + 0x29, 0x73, /* Usage Maximum (0x73) */ + 0x81, 0x02, /* Input (Data,Var,Abs) */ + 0x95, 0x05, /* Report Count (5) */ + 0x19, 0x87, /* Usage Minimum (0x87) */ + 0x29, 0x8B, /* Usage Maximum (0x8B) */ + 0x81, 0x02, /* Input (Data,Var,Abs) */ + 0x95, 0x03, /* Report Count (3) */ + 0x19, 0x90, /* Usage Minimum (0x90) */ + 0x29, 0x92, /* Usage Maximum (0x92) */ + 0x81, 0x02, /* Input (Data,Var,Abs) */ + 0x95, 0x05, /* Report Count (5) */ + 0x85, 0x0E, /* Report ID (14) */ + 0x05, 0x08, /* Usage Page (LEDs) */ + 0x19, 0x01, /* Usage Minimum (Num Lock) */ + 0x29, 0x05, /* Usage Maximum (Kana) */ + 0x91, 0x02, /* Output (Data,Var,Abs) */ + 0x95, 0x01, /* Report Count (1) */ + 0x75, 0x03, /* Report Size (3) */ + 0x91, 0x03, /* Output (Const,Var,Abs) */ + 0xC0, /* End Collection */ +}; + /* Mouse descriptor (2) */ static const char mse_descriptor[] = { 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ @@ -415,6 +455,51 @@ static const char mse_high_res_descriptor[] = { 0xC0, /* END_COLLECTION */ }; +/* Gaming Mouse descriptor with vendor data (2) */ +static const char mse_high_res_ls_1_3_descriptor[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x02, /* Usage (Mouse) */ + 0xA1, 0x01, /* Collection (Application) */ + 0x85, 0x02, /* Report ID (2) */ + 0x09, 0x01, /* Usage (Pointer) */ + 0xA1, 0x00, /* Collection (Physical) */ + 0x95, 0x10, /* Report Count (16) */ + 0x75, 0x01, /* Report Size (1) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x05, 0x09, /* Usage Page (Button) */ + 0x19, 0x01, /* Usage Minimum (0x01) */ + 0x29, 0x10, /* Usage Maximum (0x10) */ + 0x81, 0x02, /* Input (Data,Var,Abs) */ + 0x95, 0x02, /* Report Count (2) */ + 0x75, 0x10, /* Report Size (16) */ + 0x16, 0x01, 0x80, /* Logical Minimum (-32767) */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767) */ + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x30, /* Usage (X) */ + 0x09, 0x31, /* Usage (Y) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0x95, 0x01, /* Report Count (1) */ + 0x75, 0x08, /* Report Size (8) */ + 0x15, 0x81, /* Logical Minimum (-127) */ + 0x25, 0x7F, /* Logical Maximum (127) */ + 0x09, 0x38, /* Usage (Wheel) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0x95, 0x01, /* Report Count (1) */ + 0x05, 0x0C, /* Usage Page (Consumer) */ + 0x0A, 0x38, 0x02, /* Usage (AC Pan) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0xC0, /* End Collection */ + 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */ + 0x09, 0xF1, /* Usage (0xF1) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x05, /* Report Count (5) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ + 0x81, 0x00, /* Input (Data,Array,Abs) */ + 0xC0, /* End Collection */ +}; + /* Consumer Control descriptor (3) */ static const char consumer_descriptor[] = { 0x05, 0x0C, /* USAGE_PAGE (Consumer Devices) */ @@ -520,9 +605,9 @@ static const char hidpp_descriptor[] = { /* Maximum size of all defined hid reports in bytes (including report id) */ #define MAX_REPORT_SIZE 8 -/* Make sure all descriptors are present here */ +/* Make sure the largest of each descriptor type is present here */ #define MAX_RDESC_SIZE \ - (sizeof(kbd_descriptor) + \ + (sizeof(kbd_lightspeed_1_3_descriptor) +\ sizeof(mse_bluetooth_descriptor) + \ sizeof(mse5_bluetooth_descriptor) + \ sizeof(consumer_descriptor) + \ @@ -557,6 +642,8 @@ static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = { static const struct hid_ll_driver logi_dj_ll_driver; static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev); +static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, + unsigned int timeout); static void delayedwork_callback(struct work_struct *work); static LIST_HEAD(dj_hdev_list); @@ -805,7 +892,6 @@ static void delayedwork_callback(struct work_struct *work) struct dj_workitem workitem; unsigned long flags; int count; - int retval; dbg_hid("%s\n", __func__); @@ -842,11 +928,10 @@ static void delayedwork_callback(struct work_struct *work) logi_dj_recv_destroy_djhid_device(djrcv_dev, &workitem); break; case WORKITEM_TYPE_UNKNOWN: - retval = logi_dj_recv_query_paired_devices(djrcv_dev); - if (retval) { - hid_err(djrcv_dev->hidpp, "%s: logi_dj_recv_query_paired_devices error: %d\n", - __func__, retval); - } + if (!djrcv_dev->dj_mode) + logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0); + + logi_dj_recv_query_paired_devices(djrcv_dev); break; case WORKITEM_TYPE_EMPTY: dbg_hid("%s: device list is empty\n", __func__); @@ -1239,8 +1324,13 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev) djrcv_dev->last_query = jiffies; - if (djrcv_dev->type != recvr_type_dj) - return logi_dj_recv_query_hidpp_devices(djrcv_dev); + if (!djrcv_dev->dj_mode) + return 0; + + if (djrcv_dev->type != recvr_type_dj) { + retval = logi_dj_recv_query_hidpp_devices(djrcv_dev); + goto out; + } dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL); if (!dj_report) @@ -1250,6 +1340,10 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev) dj_report->report_type = REPORT_TYPE_CMD_GET_PAIRED_DEVICES; retval = logi_dj_recv_send_report(djrcv_dev, dj_report); kfree(dj_report); +out: + if (retval < 0) + hid_err(djrcv_dev->hidpp, "%s error:%d\n", __func__, retval); + return retval; } @@ -1275,6 +1369,8 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, (u8)timeout; retval = logi_dj_recv_send_report(djrcv_dev, dj_report); + if (retval) + goto out; /* * Ugly sleep to work around a USB 3.0 bug when the receiver is @@ -1283,11 +1379,6 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, * 50 msec should gives enough time to the receiver to be ready. */ msleep(50); - - if (retval) { - kfree(dj_report); - return retval; - } } /* @@ -1313,7 +1404,13 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, HIDPP_REPORT_SHORT_LENGTH, HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); +out: kfree(dj_report); + + if (retval < 0) + hid_err(hdev, "%s error:%d\n", __func__, retval); + + djrcv_dev->dj_mode = retval >= 0; return retval; } @@ -1374,12 +1471,19 @@ static int logi_dj_ll_raw_request(struct hid_device *hid, return -EINVAL; if (djrcv_dev->type != recvr_type_dj && count >= 2) { + unsigned char led_report_id = 0; + if (!djrcv_dev->keyboard) { hid_warn(hid, "Received REPORT_TYPE_LEDS request before the keyboard interface was enumerated\n"); return 0; } + + /* This Lightspeed receiver expects LED reports with report ID 1 */ + if (djrcv_dev->type == recvr_type_gaming_hidpp_ls_1_3) + led_report_id = 1; + /* usbhid overrides the report ID and ignores the first byte */ - return hid_hw_raw_request(djrcv_dev->keyboard, 0, buf, count, + return hid_hw_raw_request(djrcv_dev->keyboard, led_report_id, buf, count, report_type, reqtype); } @@ -1426,7 +1530,11 @@ static int logi_dj_ll_parse(struct hid_device *hid) if (djdev->reports_supported & STD_KEYBOARD) { dbg_hid("%s: sending a kbd descriptor, reports_supported: %llx\n", __func__, djdev->reports_supported); - rdcat(rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor)); + if (djdev->dj_receiver_dev->type == recvr_type_gaming_hidpp_ls_1_3) + rdcat(rdesc, &rsize, kbd_lightspeed_1_3_descriptor, + sizeof(kbd_lightspeed_1_3_descriptor)); + else + rdcat(rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor)); } if (djdev->reports_supported & STD_MOUSE) { @@ -1436,6 +1544,9 @@ static int logi_dj_ll_parse(struct hid_device *hid) djdev->dj_receiver_dev->type == recvr_type_mouse_only) rdcat(rdesc, &rsize, mse_high_res_descriptor, sizeof(mse_high_res_descriptor)); + else if (djdev->dj_receiver_dev->type == recvr_type_gaming_hidpp_ls_1_3) + rdcat(rdesc, &rsize, mse_high_res_ls_1_3_descriptor, + sizeof(mse_high_res_ls_1_3_descriptor)); else if (djdev->dj_receiver_dev->type == recvr_type_27mhz) rdcat(rdesc, &rsize, mse_27mhz_descriptor, sizeof(mse_27mhz_descriptor)); @@ -1695,11 +1806,12 @@ static int logi_dj_raw_event(struct hid_device *hdev, } /* * Mouse-only receivers send unnumbered mouse data. The 27 MHz - * receiver uses 6 byte packets, the nano receiver 8 bytes. + * receiver uses 6 byte packets, the nano receiver 8 bytes, + * the lightspeed receiver (Pro X Superlight) 13 bytes. */ if (djrcv_dev->unnumbered_application == HID_GD_MOUSE && - size <= 8) { - u8 mouse_report[9]; + size <= 13){ + u8 mouse_report[14]; /* Prepend report id */ mouse_report[0] = REPORT_TYPE_MOUSE; @@ -1776,6 +1888,7 @@ static int logi_dj_probe(struct hid_device *hdev, case recvr_type_dj: no_dj_interfaces = 3; break; case recvr_type_hidpp: no_dj_interfaces = 2; break; case recvr_type_gaming_hidpp: no_dj_interfaces = 3; break; + case recvr_type_gaming_hidpp_ls_1_3: no_dj_interfaces = 3; break; case recvr_type_mouse_only: no_dj_interfaces = 2; break; case recvr_type_27mhz: no_dj_interfaces = 2; break; case recvr_type_bluetooth: no_dj_interfaces = 2; break; @@ -1834,12 +1947,11 @@ static int logi_dj_probe(struct hid_device *hdev, } if (has_hidpp) { - retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0); - if (retval < 0) { - hid_err(hdev, "%s: logi_dj_recv_switch_to_dj_mode returned error:%d\n", - __func__, retval); - goto switch_to_dj_mode_fail; - } + /* + * This can fail with a KVM. Ignore errors to let the probe + * succeed, logi_dj_recv_queue_unknown_work will retry later. + */ + logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0); } /* This is enabling the polling urb on the IN endpoint */ @@ -1857,21 +1969,13 @@ static int logi_dj_probe(struct hid_device *hdev, spin_lock_irqsave(&djrcv_dev->lock, flags); djrcv_dev->ready = true; spin_unlock_irqrestore(&djrcv_dev->lock, flags); - retval = logi_dj_recv_query_paired_devices(djrcv_dev); - if (retval < 0) { - hid_err(hdev, "%s: logi_dj_recv_query_paired_devices error:%d\n", - __func__, retval); - /* - * This can happen with a KVM, let the probe succeed, - * logi_dj_recv_queue_unknown_work will retry later. - */ - } + /* This too can fail with a KVM, ignore errors. */ + logi_dj_recv_query_paired_devices(djrcv_dev); } return 0; llopen_failed: -switch_to_dj_mode_fail: hid_hw_stop(hdev); hid_hw_start_fail: @@ -1882,18 +1986,12 @@ hid_hw_start_fail: #ifdef CONFIG_PM static int logi_dj_reset_resume(struct hid_device *hdev) { - int retval; struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev); if (!djrcv_dev || djrcv_dev->hidpp != hdev) return 0; - retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0); - if (retval < 0) { - hid_err(hdev, "%s: logi_dj_recv_switch_to_dj_mode returned error:%d\n", - __func__, retval); - } - + logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0); return 0; } #endif @@ -1987,6 +2085,14 @@ static const struct hid_device_id logi_dj_receivers[] = { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_2), .driver_data = recvr_type_gaming_hidpp}, + { /* Logitech lightspeed receiver (0xc547) */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_3), + .driver_data = recvr_type_gaming_hidpp_ls_1_3}, + { /* Logitech lightspeed receiver (0xc54d) */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_4), + .driver_data = recvr_type_gaming_hidpp_ls_1_3}, { /* Logitech 27 MHz HID++ 1.0 receiver (0xc513) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER), diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 5e763de4b94f..d5011a5d0890 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -352,10 +352,15 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp, do { ret = __do_hidpp_send_message_sync(hidpp, message, response); - if (ret != HIDPP20_ERROR_BUSY) + if (response->report_id == REPORT_ID_HIDPP_SHORT && + ret != HIDPP_ERROR_BUSY) + break; + if ((response->report_id == REPORT_ID_HIDPP_LONG || + response->report_id == REPORT_ID_HIDPP_VERY_LONG) && + ret != HIDPP20_ERROR_BUSY) break; - dbg_hid("%s:got busy hidpp 2.0 error %02X, retrying\n", __func__, ret); + dbg_hid("%s:got busy hidpp error %02X, retrying\n", __func__, ret); } while (--max_retries); mutex_unlock(&hidpp->send_mutex); @@ -971,7 +976,8 @@ static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp) } /* the device might not be connected */ - if (ret == HIDPP_ERROR_RESOURCE_ERROR) + if (ret == HIDPP_ERROR_RESOURCE_ERROR || + ret == HIDPP_ERROR_UNKNOWN_DEVICE) return -EIO; if (ret > 0) { diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index c2849a541f65..7ac9217d9096 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -819,7 +819,7 @@ static void joycon_wait_for_input_report(struct joycon_ctlr *ctlr) #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_MAX_ATTEMPTS 25 #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) @@ -2648,7 +2648,8 @@ static int nintendo_hid_probe(struct hid_device *hdev, init_waitqueue_head(&ctlr->wait); spin_lock_init(&ctlr->lock); ctlr->rumble_queue = alloc_workqueue("hid-nintendo-rumble_wq", - WQ_FREEZABLE | WQ_MEM_RECLAIM, 0); + WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU, + 0); if (!ctlr->rumble_queue) { ret = -ENOMEM; goto err; diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 0f76e241e0af..a7f10c45f62b 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -142,13 +142,13 @@ static void ntrig_report_version(struct hid_device *hdev) int ret; char buf[20]; struct usb_device *usb_dev = hid_to_usb_dev(hdev); - unsigned char *data = kmalloc(8, GFP_KERNEL); + unsigned char *data __free(kfree) = kmalloc(8, GFP_KERNEL); if (!hid_is_usb(hdev)) return; if (!data) - goto err_free; + return; ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), USB_REQ_CLEAR_FEATURE, @@ -163,9 +163,6 @@ static void ntrig_report_version(struct hid_device *hdev) hid_info(hdev, "Firmware version: %s (%02x%02x %02x%02x)\n", buf, data[2], data[3], data[4], data[5]); } - -err_free: - kfree(data); } static ssize_t show_phys_width(struct device *dev, diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 63f6eb9030d1..128aa6abd10b 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -1942,6 +1942,7 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) "Failed to retrieve DualShock4 calibration info: %d\n", ret); ret = -EILSEQ; + kfree(buf); goto transfer_failed; } else { break; @@ -1959,6 +1960,7 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) if (ret) { hid_warn(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); + kfree(buf); goto transfer_failed; } } diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index bcd4bccf1a7c..c89a015686c0 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -410,7 +410,8 @@ static const struct hid_device_id hid_have_special_driver[] = { #if IS_ENABLED(CONFIG_HID_ELECOM) { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_018F) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) }, @@ -915,7 +916,6 @@ static const struct hid_device_id hid_ignore_list[] = { #endif { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_HP_5MP_CAMERA_5473) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SMARTLINKTECHNOLOGY, USB_DEVICE_ID_SMARTLINKTECHNOLOGY_4155) }, { } }; @@ -1064,6 +1064,18 @@ bool hid_ignore(struct hid_device *hdev) strlen(elan_acpi_id[i].id))) return true; break; + case USB_VENDOR_ID_JIELI_SDK_DEFAULT: + /* + * Multiple USB devices with identical IDs (mic & touchscreen). + * The touch screen requires hid core processing, but the + * microphone does not. They can be distinguished by manufacturer + * and serial number. + */ + if (hdev->product == USB_DEVICE_ID_JIELI_SDK_4155 && + strncmp(hdev->name, "SmartlinkTechnology", 19) == 0 && + strncmp(hdev->uniq, "20201111000001", 14) == 0) + return true; + break; } if (hdev->type == HID_TYPE_USBMOUSE && diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index 34fb03ae8ee2..90ebb81041ea 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -362,6 +362,23 @@ static int uclogic_raw_event_pen(struct uclogic_drvdata *drvdata, data[8] = pressure_low_byte; data[9] = pressure_high_byte; } + if (size == 12 && pen->fragmented_hires2) { + // 00 00 when on the left side, 01 00 in the right + // we move these to the end of the x coord (u16) to create a correct x coord (u32) + u8 lsb_low_byte = data[10]; + u8 lsb_high_byte = data[11]; + + // shift everything right by 2 bytes, to make space for the moved lsb + data[11] = data[9]; + data[10] = data[8]; + data[9] = data[7]; + data[8] = data[6]; + data[7] = data[5]; + data[6] = data[4]; + + data[4] = lsb_low_byte; + data[5] = lsb_high_byte; + } /* If we need to emulate in-range detection */ if (pen->inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) { /* Set in-range bit */ @@ -604,6 +621,8 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_24_PRO) }, { } }; MODULE_DEVICE_TABLE(hid, uclogic_devices); diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index ffa14a4621ef..e28176d9d9c9 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -1123,6 +1123,9 @@ static int uclogic_params_parse_ugee_v2_desc(const __u8 *str_desc, return -EINVAL; pen_x_lm = get_unaligned_le16(str_desc + 2); + if (str_desc_size > 12) + pen_x_lm += (u8)str_desc[12] << 16; + pen_y_lm = get_unaligned_le16(str_desc + 4); frame_num_buttons = str_desc[6]; *frame_type = str_desc[7]; @@ -1369,8 +1372,10 @@ static int uclogic_params_ugee_v2_init_event_hooks(struct hid_device *hdev, event_hook->hdev = hdev; event_hook->size = ARRAY_SIZE(reconnect_event); event_hook->event = kmemdup(reconnect_event, event_hook->size, GFP_KERNEL); - if (!event_hook->event) + if (!event_hook->event) { + kfree(event_hook); return -ENOMEM; + } list_add_tail(&event_hook->list, &p->event_hooks->list); @@ -1532,7 +1537,7 @@ cleanup: } /* - * uclogic_params_init_ugee_xppen_pro_22r() - Initializes a UGEE XP-Pen Pro 22R tablet device. + * uclogic_params_init_ugee_xppen_pro() - Initializes a UGEE XP-Pen Pro tablet device. * * @hdev: The HID device of the tablet interface to initialize and get * parameters from. Cannot be NULL. @@ -1543,15 +1548,17 @@ cleanup: * Returns: * Zero, if successful. A negative errno code on error. */ -static int uclogic_params_init_ugee_xppen_pro_22r(struct uclogic_params *params, - struct hid_device *hdev, - const u8 rdesc_frame_arr[], - const size_t rdesc_frame_size) +static int uclogic_params_init_ugee_xppen_pro(struct uclogic_params *params, + struct hid_device *hdev, + const u8 rdesc_pen_arr[], + const size_t rdesc_pen_size, + const u8 rdesc_frame_arr[], + const size_t rdesc_frame_size, + size_t str_desc_len) { int rc = 0; struct usb_interface *iface; __u8 bInterfaceNumber; - const int str_desc_len = 12; u8 *str_desc = NULL; __u8 *rdesc_pen = NULL; s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; @@ -1614,8 +1621,8 @@ static int uclogic_params_init_ugee_xppen_pro_22r(struct uclogic_params *params, /* Initialize the pen interface */ rdesc_pen = uclogic_rdesc_template_apply( - uclogic_rdesc_ugee_v2_pen_template_arr, - uclogic_rdesc_ugee_v2_pen_template_size, + rdesc_pen_arr, + rdesc_pen_size, desc_params, ARRAY_SIZE(desc_params)); if (!rdesc_pen) { rc = -ENOMEM; @@ -1623,7 +1630,7 @@ static int uclogic_params_init_ugee_xppen_pro_22r(struct uclogic_params *params, } p.pen.desc_ptr = rdesc_pen; - p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size; + p.pen.desc_size = rdesc_pen_size; p.pen.id = 0x02; p.pen.subreport_list[0].value = 0xf0; p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID; @@ -1970,10 +1977,30 @@ int uclogic_params_init(struct uclogic_params *params, break; case VID_PID(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO): - rc = uclogic_params_init_ugee_xppen_pro_22r(&p, + rc = uclogic_params_init_ugee_xppen_pro(&p, hdev, + uclogic_rdesc_ugee_v2_pen_template_arr, + uclogic_rdesc_ugee_v2_pen_template_size, uclogic_rdesc_xppen_artist_22r_pro_frame_arr, - uclogic_rdesc_xppen_artist_22r_pro_frame_size); + uclogic_rdesc_xppen_artist_22r_pro_frame_size, + 12); + if (rc != 0) + goto cleanup; + + break; + case VID_PID(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_24_PRO): + rc = uclogic_params_init_ugee_xppen_pro(&p, + hdev, + uclogic_rdesc_xppen_artist_24_pro_pen_template_arr, + uclogic_rdesc_xppen_artist_24_pro_pen_template_size, + uclogic_rdesc_xppen_artist_24_pro_frame_arr, + uclogic_rdesc_xppen_artist_24_pro_frame_size, + 14); + + // The 24 Pro has a fragmented X Coord. + p.pen.fragmented_hires2 = true; + if (rc != 0) goto cleanup; diff --git a/drivers/hid/hid-uclogic-params.h b/drivers/hid/hid-uclogic-params.h index 6ec8643d2ee5..c84ff17fb5d5 100644 --- a/drivers/hid/hid-uclogic-params.h +++ b/drivers/hid/hid-uclogic-params.h @@ -103,6 +103,11 @@ struct uclogic_params_pen { * Only valid if "id" is not zero. */ bool tilt_y_flipped; + /* + * True, if reports include fragmented high resolution X coords. + * This moves bytes 10-11 to the LSB of the X coordinate. + */ + bool fragmented_hires2; }; /* diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c index 08a89c6aae3b..a1b31511b625 100644 --- a/drivers/hid/hid-uclogic-rdesc.c +++ b/drivers/hid/hid-uclogic-rdesc.c @@ -1237,6 +1237,131 @@ const __u8 uclogic_rdesc_xppen_artist_22r_pro_frame_arr[] = { const size_t uclogic_rdesc_xppen_artist_22r_pro_frame_size = sizeof(uclogic_rdesc_xppen_artist_22r_pro_frame_arr); +/* Fixed report descriptor template for XP-PEN 24 Pro reports + * Mostly identical to uclogic_rdesc_ugee_v2_pen_template_arr except that the X coordinate has to be + * 32-bits instead of 16-bits. + */ +const __u8 uclogic_rdesc_xppen_artist_24_pro_pen_template_arr[] = { + 0x05, 0x0d, /* Usage Page (Digitizers), */ + 0x09, 0x01, /* Usage (Digitizer), */ + 0xa1, 0x01, /* Collection (Application), */ + 0x85, 0x02, /* Report ID (2), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xa1, 0x00, /* Collection (Physical), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x35, 0x00, /* Physical Minimum (0), */ + 0xa4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x30, /* Usage (X), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x55, 0x0d, /* Unit Exponent (-3), */ + 0x27, UCLOGIC_RDESC_PEN_PH(X_LM), + /* Logical Maximum (PLACEHOLDER), */ + 0x47, UCLOGIC_RDESC_PEN_PH(X_PM), + /* Physical Maximum (PLACEHOLDER), */ + 0x75, 0x20, /* Report Size (32), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x09, 0x31, /* Usage (Y), */ + 0x27, UCLOGIC_RDESC_PEN_PH(Y_LM), + /* Logical Maximum (PLACEHOLDER), */ + 0x47, UCLOGIC_RDESC_PEN_PH(Y_PM), + /* Physical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0xb4, /* Pop, */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x45, 0x00, /* Physical Maximum (0), */ + 0x27, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM), + /* Logical Maximum (PLACEHOLDER), */ + 0x75, 0x0D, /* Report Size (13), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x01, /* Input (Constant), */ + 0x09, 0x3d, /* Usage (X Tilt), */ + 0x35, 0xC3, /* Physical Minimum (-61), */ + 0x45, 0x3C, /* Physical Maximum (60), */ + 0x15, 0xC3, /* Logical Minimum (-61), */ + 0x25, 0x3C, /* Logical Maximum (60), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x3e, /* Usage (Y Tilt), */ + 0x35, 0xC3, /* Physical Minimum (-61), */ + 0x45, 0x3C, /* Physical Maximum (60), */ + 0x15, 0xC3, /* Logical Minimum (-61), */ + 0x25, 0x3C, /* Logical Maximum (60), */ + 0x81, 0x02, /* Input (Variable), */ + 0xc0, /* End Collection, */ + 0xc0, /* End Collection */ +}; +const size_t uclogic_rdesc_xppen_artist_24_pro_pen_template_size = + sizeof(uclogic_rdesc_xppen_artist_24_pro_pen_template_arr); + +/* Fixed report descriptor for XP-Pen Arist 24 Pro frame */ +const __u8 uclogic_rdesc_xppen_artist_24_pro_frame_arr[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x07, /* Usage (Keypad), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, UCLOGIC_RDESC_V1_FRAME_ID, + /* Report ID (Virtual report), */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x39, /* Usage (Tablet Function Keys), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x01, /* Input (Constant), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x14, /* Usage Maximum (14h), */ + 0x95, 0x14, /* Report Count (20), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x14, /* Report Count (20), */ + 0x81, 0x01, /* Input (Constant), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x38, /* Usage (Wheel), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x15, 0xFF, /* Logical Minimum (-1), */ + 0x25, 0x08, /* Logical Maximum (8), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x05, 0x0C, /* Usage Page (Consumer Devices), */ + 0x0A, 0x38, 0x02, /* Usage (AC PAN), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 16, /* Report Count (16), */ + 0x81, 0x01, /* Input (Constant), */ + 0xC0, /* End Collection */ + 0xC0, /* End Collection */ +}; + +const size_t uclogic_rdesc_xppen_artist_24_pro_frame_size = + sizeof(uclogic_rdesc_xppen_artist_24_pro_frame_arr); + /** * uclogic_rdesc_template_apply() - apply report descriptor parameters to a * report descriptor template, creating a report descriptor. Copies the diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h index 644a35ff12f2..0619daa6849d 100644 --- a/drivers/hid/hid-uclogic-rdesc.h +++ b/drivers/hid/hid-uclogic-rdesc.h @@ -214,4 +214,12 @@ extern const size_t uclogic_rdesc_ugee_g5_frame_size; extern const __u8 uclogic_rdesc_xppen_artist_22r_pro_frame_arr[]; extern const size_t uclogic_rdesc_xppen_artist_22r_pro_frame_size; +/* Fixed report descriptor for XP-Pen Arist 24 Pro frame */ +extern const __u8 uclogic_rdesc_xppen_artist_24_pro_pen_template_arr[]; +extern const size_t uclogic_rdesc_xppen_artist_24_pro_pen_template_size; + +/* Fixed report descriptor for XP-Pen Arist 24 Pro frame */ +extern const __u8 uclogic_rdesc_xppen_artist_24_pro_frame_arr[]; +extern const size_t uclogic_rdesc_xppen_artist_24_pro_frame_size; + #endif /* _HID_UCLOGIC_RDESC_H */ diff --git a/drivers/hid/hid-winwing.c b/drivers/hid/hid-winwing.c index d4afbbd27807..ab65dc12d1e0 100644 --- a/drivers/hid/hid-winwing.c +++ b/drivers/hid/hid-winwing.c @@ -37,6 +37,7 @@ struct winwing_drv_data { struct hid_device *hdev; __u8 *report_buf; struct mutex lock; + int map_more_buttons; unsigned int num_leds; struct winwing_led leds[]; }; @@ -81,12 +82,10 @@ static int winwing_init_led(struct hid_device *hdev, int ret; int i; - size_t data_size = struct_size(data, leds, 3); - - data = devm_kzalloc(&hdev->dev, data_size, GFP_KERNEL); + data = hid_get_drvdata(hdev); if (!data) - return -ENOMEM; + return -EINVAL; data->report_buf = devm_kmalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL); @@ -106,6 +105,7 @@ static int winwing_init_led(struct hid_device *hdev, "%s::%s", dev_name(&input->dev), info->led_name); + if (!led->cdev.name) return -ENOMEM; @@ -114,14 +114,98 @@ static int winwing_init_led(struct hid_device *hdev, return ret; } - hid_set_drvdata(hdev, data); - return ret; } +static int winwing_map_button(int button, int map_more_buttons) +{ + if (button < 1) + return KEY_RESERVED; + + if (button > 112) + return KEY_RESERVED; + + if (button <= 16) { + /* + * Grip buttons [1 .. 16] are mapped to + * key codes BTN_TRIGGER .. BTN_DEAD + */ + return (button - 1) + BTN_JOYSTICK; + } + + if (button >= 65) { + /* + * Base buttons [65 .. 112] are mapped to + * key codes BTN_TRIGGER_HAPPY17 .. KEY_MAX + */ + return (button - 65) + BTN_TRIGGER_HAPPY17; + } + + if (!map_more_buttons) { + /* + * Not mapping numbers [33 .. 64] which + * are not assigned to any real buttons + */ + if (button >= 33) + return KEY_RESERVED; + /* + * Grip buttons [17 .. 32] are mapped to + * BTN_TRIGGER_HAPPY1 .. BTN_TRIGGER_HAPPY16 + */ + return (button - 17) + BTN_TRIGGER_HAPPY1; + } + + if (button >= 49) { + /* + * Grip buttons [49 .. 64] are mapped to + * BTN_TRIGGER_HAPPY1 .. BTN_TRIGGER_HAPPY16 + */ + return (button - 49) + BTN_TRIGGER_HAPPY1; + } + + /* + * Grip buttons [17 .. 44] are mapped to + * key codes KEY_MACRO1 .. KEY_MACRO28; + * also mapping numbers [45 .. 48] which + * are not assigned to any real buttons. + */ + return (button - 17) + KEY_MACRO1; +} + +static int winwing_input_mapping(struct hid_device *hdev, + struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct winwing_drv_data *data; + int code = KEY_RESERVED; + int button = 0; + + data = hid_get_drvdata(hdev); + + if (!data) + return -EINVAL; + + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON) + return 0; + + if (field->application != HID_GD_JOYSTICK) + return 0; + + /* Button numbers start with 1 */ + button = usage->hid & HID_USAGE; + + code = winwing_map_button(button, data->map_more_buttons); + + hid_map_usage(hi, usage, bit, max, EV_KEY, code); + + return 1; +} + static int winwing_probe(struct hid_device *hdev, const struct hid_device_id *id) { + struct winwing_drv_data *data; + size_t data_size = struct_size(data, leds, 3); int ret; ret = hid_parse(hdev); @@ -130,6 +214,15 @@ static int winwing_probe(struct hid_device *hdev, return ret; } + data = devm_kzalloc(&hdev->dev, data_size, GFP_KERNEL); + + if (!data) + return -ENOMEM; + + data->map_more_buttons = id->driver_data; + + hid_set_drvdata(hdev, data); + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hw start failed\n"); @@ -152,64 +245,11 @@ static int winwing_input_configured(struct hid_device *hdev, return ret; } -static const __u8 original_rdesc_buttons[] = { - 0x05, 0x09, 0x19, 0x01, 0x29, 0x6F, - 0x15, 0x00, 0x25, 0x01, 0x35, 0x00, - 0x45, 0x01, 0x75, 0x01, 0x95, 0x6F, - 0x81, 0x02, 0x75, 0x01, 0x95, 0x01, - 0x81, 0x01 -}; - -/* - * HID report descriptor shows 111 buttons, which exceeds maximum - * number of buttons (80) supported by Linux kernel HID subsystem. - * - * This module skips numbers 32-63, unused on some throttle grips. - */ - -static const __u8 *winwing_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) -{ - int sig_length = sizeof(original_rdesc_buttons); - int unused_button_numbers = 32; - - if (*rsize < 34) - return rdesc; - - if (memcmp(rdesc + 8, original_rdesc_buttons, sig_length) == 0) { - - /* Usage Maximum */ - rdesc[13] -= unused_button_numbers; - - /* Report Count for buttons */ - rdesc[25] -= unused_button_numbers; - - /* Report Count for padding [HID1_11, 6.2.2.9] */ - rdesc[31] += unused_button_numbers; - - hid_info(hdev, "winwing descriptor fixed\n"); - } - - return rdesc; -} - -static int winwing_raw_event(struct hid_device *hdev, - struct hid_report *report, u8 *raw_data, int size) -{ - if (size >= 15) { - /* Skip buttons 32 .. 63 */ - memmove(raw_data + 5, raw_data + 9, 6); - - /* Clear the padding */ - memset(raw_data + 11, 0, 4); - } - - return 0; -} - static const struct hid_device_id winwing_devices[] = { - { HID_USB_DEVICE(0x4098, 0xbe62) }, /* TGRIP-18 */ - { HID_USB_DEVICE(0x4098, 0xbe68) }, /* TGRIP-16EX */ + { HID_USB_DEVICE(0x4098, 0xbd65), .driver_data = 1 }, /* TGRIP-15E */ + { HID_USB_DEVICE(0x4098, 0xbd64), .driver_data = 1 }, /* TGRIP-15EX */ + { HID_USB_DEVICE(0x4098, 0xbe68), .driver_data = 0 }, /* TGRIP-16EX */ + { HID_USB_DEVICE(0x4098, 0xbe62), .driver_data = 0 }, /* TGRIP-18 */ {} }; @@ -218,10 +258,9 @@ MODULE_DEVICE_TABLE(hid, winwing_devices); static struct hid_driver winwing_driver = { .name = "winwing", .id_table = winwing_devices, - .probe = winwing_probe, .input_configured = winwing_input_configured, - .report_fixup = winwing_report_fixup, - .raw_event = winwing_raw_event, + .input_mapping = winwing_input_mapping, + .probe = winwing_probe, }; module_hid_driver(winwing_driver); diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index edd61ef50e16..95377c5f6335 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -806,8 +806,8 @@ static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum) static int pidff_needs_playback(struct pidff_device *pidff, int effect_id, int n) { - return pidff->effect[effect_id].is_infinite || - pidff->effect[effect_id].loop_count != n; + return !pidff->effect[effect_id].is_infinite || + pidff->effect[effect_id].loop_count != n; } /* diff --git a/include/linux/hid.h b/include/linux/hid.h index a4ddb94e3ee5..dce862cafbbd 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -984,6 +984,7 @@ extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct h extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report); extern int hidinput_connect(struct hid_device *hid, unsigned int force); extern void hidinput_disconnect(struct hid_device *); +void hidinput_reset_resume(struct hid_device *hid); struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_type, unsigned int application, unsigned int usage); |
