diff options
Diffstat (limited to 'drivers/hid')
42 files changed, 1259 insertions, 1338 deletions
diff --git a/drivers/hid/.kunitconfig b/drivers/hid/.kunitconfig index 04daeff5c970..675a8209c7ae 100644 --- a/drivers/hid/.kunitconfig +++ b/drivers/hid/.kunitconfig @@ -1,5 +1,6 @@ CONFIG_KUNIT=y CONFIG_USB=y CONFIG_USB_HID=y +CONFIG_HID_BATTERY_STRENGTH=y CONFIG_HID_UCLOGIC=y CONFIG_HID_KUNIT_TEST=y diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index e2a5d30c8895..3fa0fa75ce7a 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -329,6 +329,13 @@ config HID_ELO Support for the ELO USB 4000/4500 touchscreens. Note that this is for different devices than those handled by CONFIG_TOUCHSCREEN_USB_ELO. +config HID_EVISION + tristate "EVision Keyboards Support" + depends on HID + help + Support for some EVision keyboards. Note that this is needed only when + applying customization using userspace programs. + config HID_EZKEY tristate "Ezkey BTC 8193 keyboard" default !EXPERT @@ -1018,13 +1025,21 @@ config HID_SPEEDLINK Support for Speedlink Vicious and Divine Cezanne mouse. config HID_STEAM - tristate "Steam Controller support" + tristate "Steam Controller/Deck support" select POWER_SUPPLY help - Say Y here if you have a Steam Controller if you want to use it + Say Y here if you have a Steam Controller or Deck if you want to use it without running the Steam Client. It supports both the wired and the wireless adaptor. +config STEAM_FF + bool "Steam Deck force feedback support" + depends on HID_STEAM + select INPUT_FF_MEMLESS + help + Say Y here if you want to enable force feedback support for the Steam + Deck. + config HID_STEELSERIES tristate "Steelseries SRW-S1 steering wheel support" help @@ -1264,6 +1279,7 @@ config HID_MCP2221 config HID_KUNIT_TEST tristate "KUnit tests for HID" if !KUNIT_ALL_TESTS depends on KUNIT=y + depends on HID_BATTERY_STRENGTH depends on HID_UCLOGIC default KUNIT_ALL_TESTS help diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index e8014c1a2f8b..bd01571ddfe7 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o obj-$(CONFIG_HID_ELAN) += hid-elan.o obj-$(CONFIG_HID_ELECOM) += hid-elecom.o obj-$(CONFIG_HID_ELO) += hid-elo.o +obj-$(CONFIG_HID_EVISION) += hid-evision.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_FT260) += hid-ft260.o obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c index ab125f79408f..c751d12f5df8 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_client.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c @@ -227,6 +227,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) cl_data->num_hid_devices = amd_mp2_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); @@ -282,11 +283,12 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) } rc = mp2_ops->get_rep_desc(cl_idx, cl_data->report_descr[i]); if (rc) - return rc; + goto cleanup; mp2_ops->start(privdata, info); status = amd_sfh_wait_for_response (privdata, cl_data->sensor_idx[i], SENSOR_ENABLED); if (status == SENSOR_ENABLED) { + cl_data->is_any_sensor_enabled = true; cl_data->sensor_sts[i] = SENSOR_ENABLED; rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data); if (rc) { @@ -301,19 +303,26 @@ int amd_sfh_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]); } 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 (mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0) { + if (!cl_data->is_any_sensor_enabled || + (mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0)) { amd_sfh_hid_client_deinit(privdata); for (i = 0; i < cl_data->num_hid_devices; i++) { devm_kfree(dev, cl_data->feature_report[i]); devm_kfree(dev, in_data->input_report[i]); devm_kfree(dev, cl_data->report_descr[i]); } - dev_warn(dev, "Failed to discover, sensors not enabled\n"); + dev_warn(dev, "Failed to discover, sensors not enabled is %d\n", cl_data->is_any_sensor_enabled); return -EOPNOTSUPP; } schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c index 1b18291fc5af..705b52337068 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c @@ -112,7 +112,7 @@ void amdtp_hid_wakeup(struct hid_device *hid) } } -static struct hid_ll_driver amdtp_hid_ll_driver = { +static const struct hid_ll_driver amdtp_hid_ll_driver = { .parse = amdtp_hid_parse, .start = amdtp_hid_start, .stop = amdtp_hid_stop, diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h index 3754fb423e3a..528036892c9d 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h +++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h @@ -32,6 +32,7 @@ struct amd_input_data { struct amdtp_cl_data { u8 init_done; u32 cur_hid_dev; + bool is_any_sensor_enabled; u32 hid_dev_count; u32 num_hid_devices; struct device_info *hid_devices; 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 4da2f9f62aba..a1d6e08fab7d 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 @@ -160,7 +160,7 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata) } rc = mp2_ops->get_rep_desc(cl_idx, cl_data->report_descr[i]); if (rc) - return rc; + goto cleanup; writel(0, privdata->mmio + AMD_P2C_MSG(0)); mp2_ops->start(privdata, info); diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index f99752b998f3..d1094bb1aa42 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -98,6 +98,7 @@ struct asus_kbd_leds { struct hid_device *hdev; struct work_struct work; unsigned int brightness; + spinlock_t lock; bool removed; }; @@ -490,21 +491,42 @@ static int rog_nkey_led_init(struct hid_device *hdev) return ret; } +static void asus_schedule_work(struct asus_kbd_leds *led) +{ + unsigned long flags; + + spin_lock_irqsave(&led->lock, flags); + if (!led->removed) + schedule_work(&led->work); + spin_unlock_irqrestore(&led->lock, flags); +} + static void asus_kbd_backlight_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds, cdev); + unsigned long flags; + + spin_lock_irqsave(&led->lock, flags); led->brightness = brightness; - schedule_work(&led->work); + spin_unlock_irqrestore(&led->lock, flags); + + asus_schedule_work(led); } static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev) { struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds, cdev); + enum led_brightness brightness; + unsigned long flags; + + spin_lock_irqsave(&led->lock, flags); + brightness = led->brightness; + spin_unlock_irqrestore(&led->lock, flags); - return led->brightness; + return brightness; } static void asus_kbd_backlight_work(struct work_struct *work) @@ -512,11 +534,11 @@ static void asus_kbd_backlight_work(struct work_struct *work) struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work); u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 }; int ret; + unsigned long flags; - if (led->removed) - return; - + spin_lock_irqsave(&led->lock, flags); buf[4] = led->brightness; + spin_unlock_irqrestore(&led->lock, flags); ret = asus_kbd_set_report(led->hdev, buf, sizeof(buf)); if (ret < 0) @@ -584,6 +606,7 @@ static int asus_kbd_register_leds(struct hid_device *hdev) drvdata->kbd_backlight->cdev.brightness_set = asus_kbd_backlight_set; drvdata->kbd_backlight->cdev.brightness_get = asus_kbd_backlight_get; INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work); + spin_lock_init(&drvdata->kbd_backlight->lock); ret = devm_led_classdev_register(&hdev->dev, &drvdata->kbd_backlight->cdev); if (ret < 0) { @@ -1119,9 +1142,13 @@ err_stop_hw: static void asus_remove(struct hid_device *hdev) { struct asus_drvdata *drvdata = hid_get_drvdata(hdev); + unsigned long flags; if (drvdata->kbd_backlight) { + spin_lock_irqsave(&drvdata->kbd_backlight->lock, flags); drvdata->kbd_backlight->removed = true; + spin_unlock_irqrestore(&drvdata->kbd_backlight->lock, flags); + cancel_work_sync(&drvdata->kbd_backlight->work); } diff --git a/drivers/hid/hid-betopff.c b/drivers/hid/hid-betopff.c index 467d789f9bc2..25ed7b9a917e 100644 --- a/drivers/hid/hid-betopff.c +++ b/drivers/hid/hid-betopff.c @@ -60,7 +60,6 @@ static int betopff_init(struct hid_device *hid) struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev; - int field_count = 0; int error; int i, j; @@ -86,19 +85,21 @@ static int betopff_init(struct hid_device *hid) * ----------------------------------------- * Do init them with default value. */ + if (report->maxfield < 4) { + hid_err(hid, "not enough fields in the report: %d\n", + report->maxfield); + return -ENODEV; + } for (i = 0; i < report->maxfield; i++) { + if (report->field[i]->report_count < 1) { + hid_err(hid, "no values in the field\n"); + return -ENODEV; + } for (j = 0; j < report->field[i]->report_count; j++) { report->field[i]->value[j] = 0x00; - field_count++; } } - if (field_count < 4) { - hid_err(hid, "not enough fields in the report: %d\n", - field_count); - return -ENODEV; - } - betopff = kzalloc(sizeof(*betopff), GFP_KERNEL); if (!betopff) return -ENOMEM; diff --git a/drivers/hid/hid-bigbenff.c b/drivers/hid/hid-bigbenff.c index e8c5e3ac9fff..a02cb517b4c4 100644 --- a/drivers/hid/hid-bigbenff.c +++ b/drivers/hid/hid-bigbenff.c @@ -174,6 +174,7 @@ static __u8 pid0902_rdesc_fixed[] = { struct bigben_device { struct hid_device *hid; struct hid_report *report; + spinlock_t lock; bool removed; u8 led_state; /* LED1 = 1 .. LED4 = 8 */ u8 right_motor_on; /* right motor off/on 0/1 */ @@ -184,18 +185,39 @@ struct bigben_device { struct work_struct worker; }; +static inline void bigben_schedule_work(struct bigben_device *bigben) +{ + unsigned long flags; + + spin_lock_irqsave(&bigben->lock, flags); + if (!bigben->removed) + schedule_work(&bigben->worker); + spin_unlock_irqrestore(&bigben->lock, flags); +} static void bigben_worker(struct work_struct *work) { struct bigben_device *bigben = container_of(work, struct bigben_device, worker); struct hid_field *report_field = bigben->report->field[0]; - - if (bigben->removed || !report_field) + bool do_work_led = false; + bool do_work_ff = false; + u8 *buf; + u32 len; + unsigned long flags; + + buf = hid_alloc_report_buf(bigben->report, GFP_KERNEL); + if (!buf) return; + len = hid_report_len(bigben->report); + + /* LED work */ + spin_lock_irqsave(&bigben->lock, flags); + if (bigben->work_led) { bigben->work_led = false; + do_work_led = true; report_field->value[0] = 0x01; /* 1 = led message */ report_field->value[1] = 0x08; /* reserved value, always 8 */ report_field->value[2] = bigben->led_state; @@ -204,11 +226,22 @@ static void bigben_worker(struct work_struct *work) report_field->value[5] = 0x00; /* padding */ report_field->value[6] = 0x00; /* padding */ report_field->value[7] = 0x00; /* padding */ - hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT); + hid_output_report(bigben->report, buf); } + spin_unlock_irqrestore(&bigben->lock, flags); + + if (do_work_led) { + hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len, + bigben->report->type, HID_REQ_SET_REPORT); + } + + /* FF work */ + spin_lock_irqsave(&bigben->lock, flags); + if (bigben->work_ff) { bigben->work_ff = false; + do_work_ff = true; report_field->value[0] = 0x02; /* 2 = rumble effect message */ report_field->value[1] = 0x08; /* reserved value, always 8 */ report_field->value[2] = bigben->right_motor_on; @@ -217,8 +250,17 @@ static void bigben_worker(struct work_struct *work) report_field->value[5] = 0x00; /* padding */ report_field->value[6] = 0x00; /* padding */ report_field->value[7] = 0x00; /* padding */ - hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT); + hid_output_report(bigben->report, buf); } + + spin_unlock_irqrestore(&bigben->lock, flags); + + if (do_work_ff) { + hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len, + bigben->report->type, HID_REQ_SET_REPORT); + } + + kfree(buf); } static int hid_bigben_play_effect(struct input_dev *dev, void *data, @@ -228,6 +270,7 @@ static int hid_bigben_play_effect(struct input_dev *dev, void *data, struct bigben_device *bigben = hid_get_drvdata(hid); u8 right_motor_on; u8 left_motor_force; + unsigned long flags; if (!bigben) { hid_err(hid, "no device data\n"); @@ -242,10 +285,13 @@ static int hid_bigben_play_effect(struct input_dev *dev, void *data, if (right_motor_on != bigben->right_motor_on || left_motor_force != bigben->left_motor_force) { + spin_lock_irqsave(&bigben->lock, flags); bigben->right_motor_on = right_motor_on; bigben->left_motor_force = left_motor_force; bigben->work_ff = true; - schedule_work(&bigben->worker); + spin_unlock_irqrestore(&bigben->lock, flags); + + bigben_schedule_work(bigben); } return 0; @@ -259,6 +305,7 @@ static void bigben_set_led(struct led_classdev *led, struct bigben_device *bigben = hid_get_drvdata(hid); int n; bool work; + unsigned long flags; if (!bigben) { hid_err(hid, "no device data\n"); @@ -267,6 +314,7 @@ static void bigben_set_led(struct led_classdev *led, for (n = 0; n < NUM_LEDS; n++) { if (led == bigben->leds[n]) { + spin_lock_irqsave(&bigben->lock, flags); if (value == LED_OFF) { work = (bigben->led_state & BIT(n)); bigben->led_state &= ~BIT(n); @@ -274,10 +322,11 @@ static void bigben_set_led(struct led_classdev *led, work = !(bigben->led_state & BIT(n)); bigben->led_state |= BIT(n); } + spin_unlock_irqrestore(&bigben->lock, flags); if (work) { bigben->work_led = true; - schedule_work(&bigben->worker); + bigben_schedule_work(bigben); } return; } @@ -307,8 +356,12 @@ static enum led_brightness bigben_get_led(struct led_classdev *led) static void bigben_remove(struct hid_device *hid) { struct bigben_device *bigben = hid_get_drvdata(hid); + unsigned long flags; + spin_lock_irqsave(&bigben->lock, flags); bigben->removed = true; + spin_unlock_irqrestore(&bigben->lock, flags); + cancel_work_sync(&bigben->worker); hid_hw_stop(hid); } @@ -318,7 +371,6 @@ static int bigben_probe(struct hid_device *hid, { struct bigben_device *bigben; struct hid_input *hidinput; - struct list_head *report_list; struct led_classdev *led; char *name; size_t name_sz; @@ -343,9 +395,12 @@ static int bigben_probe(struct hid_device *hid, return error; } - report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - bigben->report = list_entry(report_list->next, - struct hid_report, list); + bigben->report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 8); + if (!bigben->report) { + hid_err(hid, "no output report found\n"); + error = -ENODEV; + goto error_hw_stop; + } if (list_empty(&hid->inputs)) { hid_err(hid, "no inputs found\n"); @@ -357,6 +412,7 @@ static int bigben_probe(struct hid_device *hid, set_bit(FF_RUMBLE, hidinput->input->ffbit); INIT_WORK(&bigben->worker, bigben_worker); + spin_lock_init(&bigben->lock); error = input_ff_create_memless(hidinput->input, NULL, hid_bigben_play_effect); @@ -397,7 +453,7 @@ static int bigben_probe(struct hid_device *hid, bigben->left_motor_force = 0; bigben->work_led = true; bigben->work_ff = true; - schedule_work(&bigben->worker); + bigben_schedule_work(bigben); hid_info(hid, "LED and force feedback support for BigBen gamepad\n"); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bd47628da6be..53e4d5831caf 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -41,11 +41,6 @@ #define DRIVER_DESC "HID core driver" -int hid_debug = 0; -module_param_named(debug, hid_debug, int, 0600); -MODULE_PARM_DESC(debug, "toggle HID debugging messages"); -EXPORT_SYMBOL_GPL(hid_debug); - static int hid_ignore_special_drivers = 0; module_param_named(ignore_special_drivers, hid_ignore_special_drivers, int, 0600); MODULE_PARM_DESC(ignore_special_drivers, "Ignore any special drivers and handle all devices by generic driver"); @@ -804,7 +799,8 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type) int i; if (((parser->global.usage_page << 16) == HID_UP_SENSOR) && - type == HID_COLLECTION_PHYSICAL) + (type == HID_COLLECTION_PHYSICAL || + type == HID_COLLECTION_APPLICATION)) hid->group = HID_GROUP_SENSOR_HUB; if (hid->vendor == USB_VENDOR_ID_MICROSOFT && @@ -993,8 +989,8 @@ struct hid_report *hid_validate_values(struct hid_device *hid, * Validating on id 0 means we should examine the first * report in the list. */ - report = list_entry( - hid->report_enum[type].report_list.next, + report = list_first_entry_or_null( + &hid->report_enum[type].report_list, struct hid_report, list); } else { report = hid->report_enum[type].report_id_hash[id]; @@ -1202,6 +1198,7 @@ int hid_open_report(struct hid_device *device) __u8 *end; __u8 *next; int ret; + int i; static int (*dispatch_type[])(struct hid_parser *parser, struct hid_item *item) = { hid_parser_main, @@ -1252,6 +1249,8 @@ int hid_open_report(struct hid_device *device) goto err; } device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; + for (i = 0; i < HID_DEFAULT_NUM_COLLECTIONS; i++) + device->collection[i].parent_idx = -1; ret = -EINVAL; while ((next = fetch_item(start, end, &item)) != NULL) { @@ -2909,10 +2908,6 @@ static int __init hid_init(void) { int ret; - if (hid_debug) - pr_warn("hid_debug is now used solely for parser and driver debugging.\n" - "debugfs is now used for inspecting the device (report descriptor, reports)\n"); - ret = bus_register(&hid_bus_type); if (ret) { pr_err("can't register hid bus\n"); diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index e213bdde543a..e7ef1ea107c9 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -975,6 +975,7 @@ static const char *keys[KEY_MAX + 1] = { [KEY_CAMERA_ACCESS_DISABLE] = "CameraAccessDisable", [KEY_CAMERA_ACCESS_TOGGLE] = "CameraAccessToggle", [KEY_DICTATE] = "Dictate", + [KEY_MICMUTE] = "MicrophoneMute", [KEY_BRIGHTNESS_MIN] = "BrightnessMin", [KEY_BRIGHTNESS_MAX] = "BrightnessMax", [KEY_BRIGHTNESS_AUTO] = "BrightnessAuto", diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c index e59e9911fc37..4fa45ee77503 100644 --- a/drivers/hid/hid-elecom.c +++ b/drivers/hid/hid-elecom.c @@ -12,6 +12,7 @@ * Copyright (c) 2017 Alex Manoussakis <amanou@gnu.org> * Copyright (c) 2017 Tomasz Kramkowski <tk@the-tk.com> * Copyright (c) 2020 YOSHIOKA Takuma <lo48576@hard-wi.red> + * Copyright (c) 2022 Takahiro Fujii <fujii@xaxxi.net> */ /* @@ -89,7 +90,7 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, case USB_DEVICE_ID_ELECOM_M_DT1URBK: case USB_DEVICE_ID_ELECOM_M_DT1DRBK: case USB_DEVICE_ID_ELECOM_M_HT1URBK: - case USB_DEVICE_ID_ELECOM_M_HT1DRBK: + case USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D: /* * Report descriptor format: * 12: button bit count @@ -99,6 +100,16 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, */ mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 8); break; + case USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C: + /* + * Report descriptor format: + * 22: button bit count + * 30: padding bit count + * 24: button report size + * 16: button usage maximum + */ + mouse_button_fixup(hdev, rdesc, *rsize, 22, 30, 24, 16, 8); + break; } return rdesc; } @@ -112,7 +123,8 @@ static const struct hid_device_id elecom_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C) }, { } }; MODULE_DEVICE_TABLE(hid, elecom_devices); diff --git a/drivers/hid/hid-evision.c b/drivers/hid/hid-evision.c new file mode 100644 index 000000000000..ef6b4b435215 --- /dev/null +++ b/drivers/hid/hid-evision.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HID driver for EVision devices + * For now, only ignore bogus consumer reports + * sent after the keyboard has been configured + * + * Copyright (c) 2022 Philippe Valembois + */ + +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +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) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) + return 0; + + /* Ignore key down event */ + if ((usage->hid & HID_USAGE) >> 8 == 0x05) + return -1; + /* Ignore key up event */ + if ((usage->hid & HID_USAGE) >> 8 == 0x06) + return -1; + + switch (usage->hid & HID_USAGE) { + /* Ignore configuration saved event */ + case 0x0401: return -1; + /* Ignore reset event */ + case 0x0402: return -1; + } + return 0; +} + +static const struct hid_device_id evision_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_EVISION, USB_DEVICE_ID_EVISION_ICL01) }, + { } +}; +MODULE_DEVICE_TABLE(hid, evision_devices); + +static struct hid_driver evision_driver = { + .name = "evision", + .id_table = evision_devices, + .input_mapping = evision_input_mapping, +}; +module_hid_driver(evision_driver); + +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index cf12f17e6533..819eb38eb5df 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -424,7 +424,7 @@ static int mousevsc_hid_raw_request(struct hid_device *hid, return 0; } -static struct hid_ll_driver mousevsc_ll_driver = { +static const struct hid_ll_driver mousevsc_ll_driver = { .parse = mousevsc_hid_parse, .open = mousevsc_hid_open, .close = mousevsc_hid_close, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 5df7f4e4d610..63545cd307e5 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -274,7 +274,6 @@ #define USB_DEVICE_ID_CH_AXIS_295 0x001c #define USB_VENDOR_ID_CHERRY 0x046a -#define USB_DEVICE_ID_CHERRY_MOUSE_000C 0x000c #define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 #define USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR 0x0027 @@ -414,6 +413,8 @@ #define I2C_DEVICE_ID_HP_ENVY_X360_15T_DR100 0x29CF #define I2C_DEVICE_ID_HP_ENVY_X360_EU0009NV 0x2CF9 #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 USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN 0x2544 #define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706 #define I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN 0x261A @@ -429,7 +430,8 @@ #define USB_DEVICE_ID_ELECOM_M_DT1URBK 0x00fe #define USB_DEVICE_ID_ELECOM_M_DT1DRBK 0x00ff #define USB_DEVICE_ID_ELECOM_M_HT1URBK 0x010c -#define USB_DEVICE_ID_ELECOM_M_HT1DRBK 0x010d +#define USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D 0x010d +#define USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C 0x011c #define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34 #define USB_DEVICE_ID_DREAM_CHEEKY_WN 0x0004 @@ -446,6 +448,9 @@ #define USB_VENDOR_ID_EMS 0x2006 #define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118 +#define USB_VENDOR_ID_EVISION 0x320f +#define USB_DEVICE_ID_EVISION_ICL01 0x5041 + #define USB_VENDOR_ID_FLATFROG 0x25b5 #define USB_DEVICE_ID_MULTITOUCH_3200 0x0002 @@ -820,6 +825,7 @@ #define USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO 0xc22e #define USB_DEVICE_ID_LOGITECH_G29_WHEEL 0xc24f #define USB_DEVICE_ID_LOGITECH_G920_WHEEL 0xc262 +#define USB_DEVICE_ID_LOGITECH_G923_XBOX_WHEEL 0xc26e #define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 #define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286 #define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287 @@ -1183,6 +1189,7 @@ #define USB_VENDOR_ID_VALVE 0x28de #define USB_DEVICE_ID_STEAM_CONTROLLER 0x1102 #define USB_DEVICE_ID_STEAM_CONTROLLER_WIRELESS 0x1142 +#define USB_DEVICE_ID_STEAM_DECK 0x1205 #define USB_VENDOR_ID_STEELSERIES 0x1038 #define USB_DEVICE_ID_STEELSERIES_SRWS1 0x1410 @@ -1295,6 +1302,7 @@ #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 0x0075 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640 0x0094 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042 +#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2 0x0905 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L 0x0935 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW 0x0934 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S 0x0909 diff --git a/drivers/hid/hid-input-test.c b/drivers/hid/hid-input-test.c new file mode 100644 index 000000000000..77c2d45ac62a --- /dev/null +++ b/drivers/hid/hid-input-test.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HID to Linux Input mapping + * + * Copyright (c) 2022 José Expósito <jose.exposito89@gmail.com> + */ + +#include <kunit/test.h> + +static void hid_test_input_set_battery_charge_status(struct kunit *test) +{ + struct hid_device *dev; + bool handled; + + dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + handled = hidinput_set_battery_charge_status(dev, HID_DG_HEIGHT, 0); + KUNIT_EXPECT_FALSE(test, handled); + KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_UNKNOWN); + + handled = hidinput_set_battery_charge_status(dev, HID_BAT_CHARGING, 0); + KUNIT_EXPECT_TRUE(test, handled); + KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_DISCHARGING); + + handled = hidinput_set_battery_charge_status(dev, HID_BAT_CHARGING, 1); + KUNIT_EXPECT_TRUE(test, handled); + KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_CHARGING); +} + +static void hid_test_input_get_battery_property(struct kunit *test) +{ + struct power_supply *psy; + struct hid_device *dev; + union power_supply_propval val; + int ret; + + dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + dev->battery_avoid_query = true; + + psy = kunit_kzalloc(test, sizeof(*psy), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, psy); + psy->drv_data = dev; + + dev->battery_status = HID_BATTERY_UNKNOWN; + dev->battery_charge_status = POWER_SUPPLY_STATUS_CHARGING; + ret = hidinput_get_battery_property(psy, POWER_SUPPLY_PROP_STATUS, &val); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, val.intval, POWER_SUPPLY_STATUS_UNKNOWN); + + dev->battery_status = HID_BATTERY_REPORTED; + dev->battery_charge_status = POWER_SUPPLY_STATUS_CHARGING; + ret = hidinput_get_battery_property(psy, POWER_SUPPLY_PROP_STATUS, &val); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, val.intval, POWER_SUPPLY_STATUS_CHARGING); + + dev->battery_status = HID_BATTERY_REPORTED; + dev->battery_charge_status = POWER_SUPPLY_STATUS_DISCHARGING; + ret = hidinput_get_battery_property(psy, POWER_SUPPLY_PROP_STATUS, &val); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, val.intval, POWER_SUPPLY_STATUS_DISCHARGING); +} + +static struct kunit_case hid_input_tests[] = { + KUNIT_CASE(hid_test_input_set_battery_charge_status), + KUNIT_CASE(hid_test_input_get_battery_property), + { } +}; + +static struct kunit_suite hid_input_test_suite = { + .name = "hid_input", + .test_cases = hid_input_tests, +}; + +kunit_test_suite(hid_input_test_suite); + +MODULE_DESCRIPTION("HID input KUnit tests"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("José Expósito <jose.exposito89@gmail.com>"); diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 4f6596febedf..7fc967964dd8 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -370,6 +370,8 @@ static const struct hid_device_id hid_battery_quirks[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DINOVO_EDGE_KBD), HID_BATTERY_QUIRK_IGNORE }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_TP420IA_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), @@ -388,6 +390,8 @@ static const struct hid_device_id hid_battery_quirks[] = { HID_BATTERY_QUIRK_IGNORE }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_15), HID_BATTERY_QUIRK_IGNORE }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_13_AW0020NG), + HID_BATTERY_QUIRK_IGNORE }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN), HID_BATTERY_QUIRK_IGNORE }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_SURFACE_GO2_TOUCHSCREEN), @@ -486,7 +490,7 @@ static int hidinput_get_battery_property(struct power_supply *psy, if (dev->battery_status == HID_BATTERY_UNKNOWN) val->intval = POWER_SUPPLY_STATUS_UNKNOWN; else - val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + val->intval = dev->battery_charge_status; break; case POWER_SUPPLY_PROP_SCOPE: @@ -554,6 +558,7 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, dev->battery_max = max; dev->battery_report_type = report_type; dev->battery_report_id = field->report->id; + dev->battery_charge_status = POWER_SUPPLY_STATUS_DISCHARGING; /* * Stylus is normally not connected to the device and thus we @@ -620,6 +625,20 @@ static void hidinput_update_battery(struct hid_device *dev, int value) power_supply_changed(dev->battery); } } + +static bool hidinput_set_battery_charge_status(struct hid_device *dev, + unsigned int usage, int value) +{ + switch (usage) { + case HID_BAT_CHARGING: + dev->battery_charge_status = value ? + POWER_SUPPLY_STATUS_CHARGING : + POWER_SUPPLY_STATUS_DISCHARGING; + return true; + } + + return false; +} #else /* !CONFIG_HID_BATTERY_STRENGTH */ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field, bool is_percentage) @@ -634,6 +653,12 @@ static void hidinput_cleanup_battery(struct hid_device *dev) static void hidinput_update_battery(struct hid_device *dev, int value) { } + +static bool hidinput_set_battery_charge_status(struct hid_device *dev, + unsigned int usage, int value) +{ + return false; +} #endif /* CONFIG_HID_BATTERY_STRENGTH */ static bool hidinput_field_in_collection(struct hid_device *device, struct hid_field *field, @@ -793,6 +818,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel break; } + if ((usage->hid & 0xf0) == 0xa0) { /* SystemControl */ + switch (usage->hid & 0xf) { + case 0x9: map_key_clear(KEY_MICMUTE); break; + default: goto ignore; + } + break; + } + if ((usage->hid & 0xf0) == 0xb0) { /* SC - Display */ switch (usage->hid & 0xf) { case 0x05: map_key_clear(KEY_SWITCHVIDEOMODE); break; @@ -1223,6 +1256,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel hidinput_setup_battery(device, HID_INPUT_REPORT, field, true); usage->type = EV_PWR; return; + case HID_BAT_CHARGING: + usage->type = EV_PWR; + return; } goto unknown; @@ -1465,7 +1501,11 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; if (usage->type == EV_PWR) { - hidinput_update_battery(hid, value); + bool handled = hidinput_set_battery_charge_status(hid, usage->hid, value); + + if (!handled) + hidinput_update_battery(hid, value); + return; } @@ -2321,3 +2361,7 @@ void hidinput_disconnect(struct hid_device *hid) cancel_work_sync(&hid->led_work); } EXPORT_SYMBOL_GPL(hidinput_disconnect); + +#ifdef CONFIG_HID_KUNIT_TEST +#include "hid-input-test.c" +#endif diff --git a/drivers/hid/hid-letsketch.c b/drivers/hid/hid-letsketch.c index 74d17cf518ba..97f047f18136 100644 --- a/drivers/hid/hid-letsketch.c +++ b/drivers/hid/hid-letsketch.c @@ -238,7 +238,7 @@ static int letsketch_probe(struct hid_device *hdev, const struct hid_device_id * char buf[256]; int i, ret; - if (!hid_is_using_ll_driver(hdev, &usb_hid_driver)) + if (!hid_is_usb(hdev)) return -ENODEV; intf = to_usb_interface(hdev->dev.parent); diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index c358778e070b..62180414efcc 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -554,7 +554,7 @@ static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = { #define LOGITECH_DJ_INTERFACE_NUMBER 0x02 -static struct hid_ll_driver logi_dj_ll_driver; +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 void delayedwork_callback(struct work_struct *work); @@ -1506,7 +1506,7 @@ static bool logi_dj_ll_may_wakeup(struct hid_device *hid) return hid_hw_may_wakeup(djrcv_dev->hidpp); } -static struct hid_ll_driver logi_dj_ll_driver = { +static const struct hid_ll_driver logi_dj_ll_driver = { .parse = logi_dj_ll_parse, .start = logi_dj_ll_start, .stop = logi_dj_ll_stop, diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index abf2c95e4d0b..25dcda76d6c7 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -30,11 +30,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>"); MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>"); - -static bool disable_raw_mode; -module_param(disable_raw_mode, bool, 0644); -MODULE_PARM_DESC(disable_raw_mode, - "Disable Raw mode reporting for touchpads and keep firmware gestures."); +MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>"); static bool disable_tap_to_click; module_param(disable_tap_to_click, bool, 0644); @@ -71,12 +67,13 @@ MODULE_PARM_DESC(disable_tap_to_click, /* bits 2..20 are reserved for classes */ /* #define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) disabled */ #define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22) -#define HIDPP_QUIRK_NO_HIDINPUT BIT(23) +#define HIDPP_QUIRK_DELAYED_INIT BIT(23) #define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24) #define HIDPP_QUIRK_UNIFYING BIT(25) #define HIDPP_QUIRK_HIDPP_WHEELS BIT(26) #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) /* These are just aliases for now */ #define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS @@ -87,8 +84,6 @@ MODULE_PARM_DESC(disable_tap_to_click, HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL | \ HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL) -#define HIDPP_QUIRK_DELAYED_INIT HIDPP_QUIRK_NO_HIDINPUT - #define HIDPP_CAPABILITY_HIDPP10_BATTERY BIT(0) #define HIDPP_CAPABILITY_HIDPP20_BATTERY BIT(1) #define HIDPP_CAPABILITY_BATTERY_MILEAGE BIT(2) @@ -225,6 +220,16 @@ struct hidpp_device { #define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b #define HIDPP_ERROR_WRONG_PIN_CODE 0x0c /* HID++ 2.0 error codes */ +#define HIDPP20_ERROR_NO_ERROR 0x00 +#define HIDPP20_ERROR_UNKNOWN 0x01 +#define HIDPP20_ERROR_INVALID_ARGS 0x02 +#define HIDPP20_ERROR_OUT_OF_RANGE 0x03 +#define HIDPP20_ERROR_HW_ERROR 0x04 +#define HIDPP20_ERROR_LOGITECH_INTERNAL 0x05 +#define HIDPP20_ERROR_INVALID_FEATURE_INDEX 0x06 +#define HIDPP20_ERROR_INVALID_FUNCTION_ID 0x07 +#define HIDPP20_ERROR_BUSY 0x08 +#define HIDPP20_ERROR_UNSUPPORTED 0x09 #define HIDPP20_ERROR 0xff static void hidpp_connect_event(struct hidpp_device *hidpp_dev); @@ -279,6 +284,7 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp, struct hidpp_report *response) { int ret; + int max_retries = 3; mutex_lock(&hidpp->send_mutex); @@ -291,34 +297,39 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp, */ *response = *message; - ret = __hidpp_send_report(hidpp->hid_dev, message); + for (; max_retries != 0; max_retries--) { + ret = __hidpp_send_report(hidpp->hid_dev, message); - if (ret) { - dbg_hid("__hidpp_send_report returned err: %d\n", ret); - memset(response, 0, sizeof(struct hidpp_report)); - goto exit; - } + if (ret) { + dbg_hid("__hidpp_send_report returned err: %d\n", ret); + memset(response, 0, sizeof(struct hidpp_report)); + goto exit; + } - if (!wait_event_timeout(hidpp->wait, hidpp->answer_available, - 5*HZ)) { - dbg_hid("%s:timeout waiting for response\n", __func__); - memset(response, 0, sizeof(struct hidpp_report)); - ret = -ETIMEDOUT; - } + if (!wait_event_timeout(hidpp->wait, hidpp->answer_available, + 5*HZ)) { + dbg_hid("%s:timeout waiting for response\n", __func__); + memset(response, 0, sizeof(struct hidpp_report)); + ret = -ETIMEDOUT; + } - if (response->report_id == REPORT_ID_HIDPP_SHORT && - response->rap.sub_id == HIDPP_ERROR) { - ret = response->rap.params[1]; - dbg_hid("%s:got hidpp error %02X\n", __func__, ret); - goto exit; - } + if (response->report_id == REPORT_ID_HIDPP_SHORT && + response->rap.sub_id == HIDPP_ERROR) { + ret = response->rap.params[1]; + dbg_hid("%s:got hidpp error %02X\n", __func__, ret); + goto exit; + } - if ((response->report_id == REPORT_ID_HIDPP_LONG || - response->report_id == REPORT_ID_HIDPP_VERY_LONG) && - response->fap.feature_index == HIDPP20_ERROR) { - ret = response->fap.params[1]; - dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret); - goto exit; + if ((response->report_id == REPORT_ID_HIDPP_LONG || + response->report_id == REPORT_ID_HIDPP_VERY_LONG) && + response->fap.feature_index == HIDPP20_ERROR) { + ret = response->fap.params[1]; + if (ret != HIDPP20_ERROR_BUSY) { + dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret); + goto exit; + } + dbg_hid("%s:got busy hidpp 2.0 error %02X, retrying\n", __func__, ret); + } } exit: @@ -334,8 +345,13 @@ static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp, struct hidpp_report *message; int ret; - if (param_count > sizeof(message->fap.params)) + if (param_count > sizeof(message->fap.params)) { + hid_dbg(hidpp->hid_dev, + "Invalid number of parameters passed to command (%d != %llu)\n", + param_count, + (unsigned long long) sizeof(message->fap.params)); return -EINVAL; + } message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL); if (!message) @@ -3436,11 +3452,17 @@ static int hi_res_scroll_enable(struct hidpp_device *hidpp) ret = hidpp10_enable_scrolling_acceleration(hidpp); multiplier = 8; } - if (ret) + if (ret) { + hid_dbg(hidpp->hid_dev, + "Could not enable hi-res scrolling: %d\n", ret); return ret; + } - if (multiplier == 0) + if (multiplier == 0) { + hid_dbg(hidpp->hid_dev, + "Invalid multiplier 0 from device, setting it to 1\n"); multiplier = 1; + } hidpp->vertical_wheel_counter.wheel_multiplier = multiplier; hid_dbg(hidpp->hid_dev, "wheel multiplier = %d\n", multiplier); @@ -3472,14 +3494,8 @@ static int hidpp_initialize_hires_scroll(struct hidpp_device *hidpp) hid_dbg(hidpp->hid_dev, "Detected HID++ 2.0 hi-res scrolling\n"); } } else { - struct hidpp_report response; - - ret = hidpp_send_rap_command_sync(hidpp, - REPORT_ID_HIDPP_SHORT, - HIDPP_GET_REGISTER, - HIDPP_ENABLE_FAST_SCROLL, - NULL, 0, &response); - if (!ret) { + /* We cannot detect fast scrolling support on HID++ 1.0 devices */ + if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_1P0) { hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP10_FAST_SCROLL; hid_dbg(hidpp->hid_dev, "Detected HID++ 1.0 fast scroll\n"); } @@ -3978,7 +3994,8 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) } hidpp_initialize_battery(hidpp); - hidpp_initialize_hires_scroll(hidpp); + if (!hid_is_usb(hidpp->hid_dev)) + hidpp_initialize_hires_scroll(hidpp); /* forward current battery state */ if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) { @@ -4001,7 +4018,7 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) if (hidpp->capabilities & HIDPP_CAPABILITY_HI_RES_SCROLL) hi_res_scroll_enable(hidpp); - if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input) + if (!(hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) || hidpp->delayed_input) /* if the input nodes are already created, we can stop now */ return; @@ -4106,6 +4123,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) bool connected; unsigned int connect_mask = HID_CONNECT_DEFAULT; struct hidpp_ff_private_data data; + bool will_restart = false; /* report_fixup needs drvdata to be set before we call hid_parse */ hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL); @@ -4146,11 +4164,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) hidpp_application_equals(hdev, HID_GD_KEYBOARD)) hidpp->quirks |= HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS; - if (disable_raw_mode) { - hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP; - hidpp->quirks &= ~HIDPP_QUIRK_NO_HIDINPUT; - } - if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) { ret = wtp_allocate(hdev, id); if (ret) @@ -4161,6 +4174,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } + if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT || + hidpp->quirks & HIDPP_QUIRK_UNIFYING) + will_restart = true; + INIT_WORK(&hidpp->work, delayed_work_cb); mutex_init(&hidpp->send_mutex); init_waitqueue_head(&hidpp->wait); @@ -4175,7 +4192,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) * Plain USB connections need to actually call start and open * on the transport driver to allow incoming data. */ - ret = hid_hw_start(hdev, 0); + ret = hid_hw_start(hdev, will_restart ? 0 : connect_mask); if (ret) { hid_err(hdev, "hw start failed\n"); goto hid_hw_start_fail; @@ -4212,6 +4229,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) hidpp->wireless_feature_index = 0; else if (ret) goto hid_hw_init_fail; + ret = 0; } if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) { @@ -4226,19 +4244,21 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) hidpp_connect_event(hidpp); - /* Reset the HID node state */ - hid_device_io_stop(hdev); - hid_hw_close(hdev); - hid_hw_stop(hdev); + if (will_restart) { + /* Reset the HID node state */ + hid_device_io_stop(hdev); + hid_hw_close(hdev); + hid_hw_stop(hdev); - if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) - connect_mask &= ~HID_CONNECT_HIDINPUT; + if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) + connect_mask &= ~HID_CONNECT_HIDINPUT; - /* Now export the actual inputs and hidraw nodes to the world */ - ret = hid_hw_start(hdev, connect_mask); - if (ret) { - hid_err(hdev, "%s:hid_hw_start returned error\n", __func__); - goto hid_hw_start_fail; + /* Now export the actual inputs and hidraw nodes to the world */ + ret = hid_hw_start(hdev, connect_mask); + if (ret) { + hid_err(hdev, "%s:hid_hw_start returned error\n", __func__); + goto hid_hw_start_fail; + } } if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) { @@ -4296,9 +4316,15 @@ static const struct hid_device_id hidpp_devices[] = { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651), .driver_data = HIDPP_QUIRK_CLASS_WTP }, + { /* Mouse Logitech Anywhere MX */ + LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, { /* Mouse logitech M560 */ LDJ_DEVICE(0x402d), .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 }, + { /* Mouse Logitech M705 (firmware RQM17) */ + LDJ_DEVICE(0x101b), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, + { /* Mouse Logitech Performance MX */ + LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, { /* Keyboard logitech K400 */ LDJ_DEVICE(0x4024), .driver_data = HIDPP_QUIRK_CLASS_K400 }, @@ -4347,6 +4373,9 @@ static const struct hid_device_id hidpp_devices[] = { { /* Logitech G920 Wheel over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL), .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS}, + { /* Logitech G923 Wheel (Xbox version) over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G923_XBOX_WHEEL), + .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS }, { /* Logitech G Pro Gaming Mouse over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) }, @@ -4366,6 +4395,8 @@ static const struct hid_device_id hidpp_devices[] = { { /* MX Ergo trackball over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01d) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01e) }, + { /* Signature M650 over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb02a) }, { /* MX Master 3 mouse over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb023) }, {} diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c index e61dd039354b..f74a977cf8f8 100644 --- a/drivers/hid/hid-mcp2221.c +++ b/drivers/hid/hid-mcp2221.c @@ -922,6 +922,9 @@ static void mcp2221_hid_unregister(void *ptr) /* This is needed to be sure hid_hw_stop() isn't called twice by the subsystem */ static void mcp2221_remove(struct hid_device *hdev) { + struct mcp2221 *mcp = hid_get_drvdata(hdev); + + cancel_delayed_work_sync(&mcp->init_work); } #if IS_REACHABLE(CONFIG_IIO) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 372cbdd223e0..e31be0cb8b85 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -71,6 +71,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_SEPARATE_APP_REPORT BIT(19) #define MT_QUIRK_FORCE_MULTI_INPUT BIT(20) #define MT_QUIRK_DISABLE_WAKEUP BIT(21) +#define MT_QUIRK_ORIENTATION_INVERT BIT(22) #define MT_INPUTMODE_TOUCHSCREEN 0x02 #define MT_INPUTMODE_TOUCHPAD 0x03 @@ -1009,6 +1010,7 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, struct mt_usages *slot) { struct input_mt *mt = input->mt; + struct hid_device *hdev = td->hdev; __s32 quirks = app->quirks; bool valid = true; bool confidence_state = true; @@ -1086,6 +1088,10 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, int orientation = wide; int max_azimuth; int azimuth; + int x; + int y; + int cx; + int cy; if (slot->a != DEFAULT_ZERO) { /* @@ -1104,6 +1110,9 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, if (azimuth > max_azimuth * 2) azimuth -= max_azimuth * 4; orientation = -azimuth; + if (quirks & MT_QUIRK_ORIENTATION_INVERT) + orientation = -orientation; + } if (quirks & MT_QUIRK_TOUCH_SIZE_SCALING) { @@ -1115,10 +1124,23 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, minor = minor >> 1; } - input_event(input, EV_ABS, ABS_MT_POSITION_X, *slot->x); - input_event(input, EV_ABS, ABS_MT_POSITION_Y, *slot->y); - input_event(input, EV_ABS, ABS_MT_TOOL_X, *slot->cx); - input_event(input, EV_ABS, ABS_MT_TOOL_Y, *slot->cy); + x = hdev->quirks & HID_QUIRK_X_INVERT ? + input_abs_get_max(input, ABS_MT_POSITION_X) - *slot->x : + *slot->x; + y = hdev->quirks & HID_QUIRK_Y_INVERT ? + input_abs_get_max(input, ABS_MT_POSITION_Y) - *slot->y : + *slot->y; + cx = hdev->quirks & HID_QUIRK_X_INVERT ? + input_abs_get_max(input, ABS_MT_POSITION_X) - *slot->cx : + *slot->cx; + cy = hdev->quirks & HID_QUIRK_Y_INVERT ? + input_abs_get_max(input, ABS_MT_POSITION_Y) - *slot->cy : + *slot->cy; + + input_event(input, EV_ABS, ABS_MT_POSITION_X, x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, y); + input_event(input, EV_ABS, ABS_MT_TOOL_X, cx); + input_event(input, EV_ABS, ABS_MT_TOOL_Y, cy); input_event(input, EV_ABS, ABS_MT_DISTANCE, !*slot->tip_state); input_event(input, EV_ABS, ABS_MT_ORIENTATION, orientation); input_event(input, EV_ABS, ABS_MT_PRESSURE, *slot->p); @@ -1735,6 +1757,15 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID) td->serial_maybe = true; + + /* Orientation is inverted if the X or Y axes are + * flipped, but normalized if both are inverted. + */ + if (hdev->quirks & (HID_QUIRK_X_INVERT | HID_QUIRK_Y_INVERT) && + !((hdev->quirks & HID_QUIRK_X_INVERT) + && (hdev->quirks & HID_QUIRK_Y_INVERT))) + td->mtclass.quirks = MT_QUIRK_ORIENTATION_INVERT; + /* This allows the driver to correctly support devices * that emit events over several HID messages. */ diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index f399bf0d3c8c..8ac8f7b8e317 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -944,6 +944,7 @@ ATTRIBUTE_GROUPS(ps_device); static int dualsense_get_calibration_data(struct dualsense *ds) { + struct hid_device *hdev = ds->base.hdev; short gyro_pitch_bias, gyro_pitch_plus, gyro_pitch_minus; short gyro_yaw_bias, gyro_yaw_plus, gyro_yaw_minus; short gyro_roll_bias, gyro_roll_plus, gyro_roll_minus; @@ -954,6 +955,7 @@ static int dualsense_get_calibration_data(struct dualsense *ds) int speed_2x; int range_2g; int ret = 0; + int i; uint8_t *buf; buf = kzalloc(DS_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL); @@ -991,19 +993,37 @@ static int dualsense_get_calibration_data(struct dualsense *ds) */ speed_2x = (gyro_speed_plus + gyro_speed_minus); ds->gyro_calib_data[0].abs_code = ABS_RX; - ds->gyro_calib_data[0].bias = gyro_pitch_bias; + ds->gyro_calib_data[0].bias = 0; ds->gyro_calib_data[0].sens_numer = speed_2x*DS_GYRO_RES_PER_DEG_S; - ds->gyro_calib_data[0].sens_denom = gyro_pitch_plus - gyro_pitch_minus; + ds->gyro_calib_data[0].sens_denom = abs(gyro_pitch_plus - gyro_pitch_bias) + + abs(gyro_pitch_minus - gyro_pitch_bias); ds->gyro_calib_data[1].abs_code = ABS_RY; - ds->gyro_calib_data[1].bias = gyro_yaw_bias; + ds->gyro_calib_data[1].bias = 0; ds->gyro_calib_data[1].sens_numer = speed_2x*DS_GYRO_RES_PER_DEG_S; - ds->gyro_calib_data[1].sens_denom = gyro_yaw_plus - gyro_yaw_minus; + ds->gyro_calib_data[1].sens_denom = abs(gyro_yaw_plus - gyro_yaw_bias) + + abs(gyro_yaw_minus - gyro_yaw_bias); ds->gyro_calib_data[2].abs_code = ABS_RZ; - ds->gyro_calib_data[2].bias = gyro_roll_bias; + ds->gyro_calib_data[2].bias = 0; ds->gyro_calib_data[2].sens_numer = speed_2x*DS_GYRO_RES_PER_DEG_S; - ds->gyro_calib_data[2].sens_denom = gyro_roll_plus - gyro_roll_minus; + ds->gyro_calib_data[2].sens_denom = abs(gyro_roll_plus - gyro_roll_bias) + + abs(gyro_roll_minus - gyro_roll_bias); + + /* + * Sanity check gyro calibration data. This is needed to prevent crashes + * during report handling of virtual, clone or broken devices not implementing + * calibration data properly. + */ + for (i = 0; i < ARRAY_SIZE(ds->gyro_calib_data); i++) { + if (ds->gyro_calib_data[i].sens_denom == 0) { + hid_warn(hdev, "Invalid gyro calibration data for axis (%d), disabling calibration.", + ds->gyro_calib_data[i].abs_code); + ds->gyro_calib_data[i].bias = 0; + ds->gyro_calib_data[i].sens_numer = DS_GYRO_RANGE; + ds->gyro_calib_data[i].sens_denom = S16_MAX; + } + } /* * Set accelerometer calibration and normalization parameters. @@ -1027,6 +1047,21 @@ static int dualsense_get_calibration_data(struct dualsense *ds) ds->accel_calib_data[2].sens_numer = 2*DS_ACC_RES_PER_G; ds->accel_calib_data[2].sens_denom = range_2g; + /* + * Sanity check accelerometer calibration data. This is needed to prevent crashes + * during report handling of virtual, clone or broken devices not implementing calibration + * data properly. + */ + for (i = 0; i < ARRAY_SIZE(ds->accel_calib_data); i++) { + if (ds->accel_calib_data[i].sens_denom == 0) { + hid_warn(hdev, "Invalid accelerometer calibration data for axis (%d), disabling calibration.", + ds->accel_calib_data[i].abs_code); + ds->accel_calib_data[i].bias = 0; + ds->accel_calib_data[i].sens_numer = DS_ACC_RANGE; + ds->accel_calib_data[i].sens_denom = S16_MAX; + } + } + err_free: kfree(buf); return ret; @@ -1356,8 +1391,7 @@ static int dualsense_parse_report(struct ps_device *ps_dev, struct hid_report *r for (i = 0; i < ARRAY_SIZE(ds_report->gyro); i++) { int raw_data = (short)le16_to_cpu(ds_report->gyro[i]); int calib_data = mult_frac(ds->gyro_calib_data[i].sens_numer, - raw_data - ds->gyro_calib_data[i].bias, - ds->gyro_calib_data[i].sens_denom); + raw_data, ds->gyro_calib_data[i].sens_denom); input_report_abs(ds->sensors, ds->gyro_calib_data[i].abs_code, calib_data); } @@ -1737,6 +1771,7 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) int speed_2x; int range_2g; int ret = 0; + int i; uint8_t *buf; if (ds4->base.hdev->bus == BUS_USB) { @@ -1759,11 +1794,10 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) if (retries < 2) { hid_warn(hdev, "Retrying DualShock 4 get calibration report (0x02) request\n"); continue; - } else { - ret = -EILSEQ; - goto err_free; } + hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); + ret = -EILSEQ; goto err_free; } else { break; @@ -1816,19 +1850,37 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) */ speed_2x = (gyro_speed_plus + gyro_speed_minus); ds4->gyro_calib_data[0].abs_code = ABS_RX; - ds4->gyro_calib_data[0].bias = gyro_pitch_bias; + ds4->gyro_calib_data[0].bias = 0; ds4->gyro_calib_data[0].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S; - ds4->gyro_calib_data[0].sens_denom = gyro_pitch_plus - gyro_pitch_minus; + ds4->gyro_calib_data[0].sens_denom = abs(gyro_pitch_plus - gyro_pitch_bias) + + abs(gyro_pitch_minus - gyro_pitch_bias); ds4->gyro_calib_data[1].abs_code = ABS_RY; - ds4->gyro_calib_data[1].bias = gyro_yaw_bias; + ds4->gyro_calib_data[1].bias = 0; ds4->gyro_calib_data[1].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S; - ds4->gyro_calib_data[1].sens_denom = gyro_yaw_plus - gyro_yaw_minus; + ds4->gyro_calib_data[1].sens_denom = abs(gyro_yaw_plus - gyro_yaw_bias) + + abs(gyro_yaw_minus - gyro_yaw_bias); ds4->gyro_calib_data[2].abs_code = ABS_RZ; - ds4->gyro_calib_data[2].bias = gyro_roll_bias; + ds4->gyro_calib_data[2].bias = 0; ds4->gyro_calib_data[2].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S; - ds4->gyro_calib_data[2].sens_denom = gyro_roll_plus - gyro_roll_minus; + ds4->gyro_calib_data[2].sens_denom = abs(gyro_roll_plus - gyro_roll_bias) + + abs(gyro_roll_minus - gyro_roll_bias); + + /* + * Sanity check gyro calibration data. This is needed to prevent crashes + * during report handling of virtual, clone or broken devices not implementing + * calibration data properly. + */ + for (i = 0; i < ARRAY_SIZE(ds4->gyro_calib_data); i++) { + if (ds4->gyro_calib_data[i].sens_denom == 0) { + hid_warn(hdev, "Invalid gyro calibration data for axis (%d), disabling calibration.", + ds4->gyro_calib_data[i].abs_code); + ds4->gyro_calib_data[i].bias = 0; + ds4->gyro_calib_data[i].sens_numer = DS4_GYRO_RANGE; + ds4->gyro_calib_data[i].sens_denom = S16_MAX; + } + } /* * Set accelerometer calibration and normalization parameters. @@ -1852,6 +1904,21 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) ds4->accel_calib_data[2].sens_numer = 2*DS4_ACC_RES_PER_G; ds4->accel_calib_data[2].sens_denom = range_2g; + /* + * Sanity check accelerometer calibration data. This is needed to prevent crashes + * during report handling of virtual, clone or broken devices not implementing calibration + * data properly. + */ + for (i = 0; i < ARRAY_SIZE(ds4->accel_calib_data); i++) { + if (ds4->accel_calib_data[i].sens_denom == 0) { + hid_warn(hdev, "Invalid accelerometer calibration data for axis (%d), disabling calibration.", + ds4->accel_calib_data[i].abs_code); + ds4->accel_calib_data[i].bias = 0; + ds4->accel_calib_data[i].sens_numer = DS4_ACC_RANGE; + ds4->accel_calib_data[i].sens_denom = S16_MAX; + } + } + err_free: kfree(buf); return ret; @@ -2179,8 +2246,7 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * for (i = 0; i < ARRAY_SIZE(ds4_report->gyro); i++) { int raw_data = (short)le16_to_cpu(ds4_report->gyro[i]); int calib_data = mult_frac(ds4->gyro_calib_data[i].sens_numer, - raw_data - ds4->gyro_calib_data[i].bias, - ds4->gyro_calib_data[i].sens_denom); + raw_data, ds4->gyro_calib_data[i].sens_denom); input_report_abs(ds4->sensors, ds4->gyro_calib_data[i].abs_code, calib_data); } diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 0e9702c7f7d6..66e64350f138 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -54,7 +54,6 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE), HID_QUIRK_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS), HID_QUIRK_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_THROTTLE), HID_QUIRK_NOGET }, - { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_MOUSE_000C), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB_RAPIDFIRE), HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70RGB), HID_QUIRK_NO_INIT_REPORTS }, @@ -394,7 +393,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C) }, #endif #if IS_ENABLED(CONFIG_HID_ELO) { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) }, @@ -1237,7 +1237,7 @@ EXPORT_SYMBOL_GPL(hid_quirks_exit); static unsigned long hid_gets_squirk(const struct hid_device *hdev) { const struct hid_device_id *bl_entry; - unsigned long quirks = 0; + unsigned long quirks = hdev->initial_quirks; if (hid_match_id(hdev, hid_ignore_list)) quirks |= HID_QUIRK_IGNORE; diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c index f444e63e9f36..3e3f89e01d81 100644 --- a/drivers/hid/hid-sensor-custom.c +++ b/drivers/hid/hid-sensor-custom.c @@ -5,6 +5,7 @@ */ #include <linux/ctype.h> +#include <linux/dmi.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -750,114 +751,209 @@ static void hid_sensor_custom_dev_if_remove(struct hid_sensor_custom } -/* luid defined in FW (e.g. ISH). Maybe used to identify sensor. */ -static const char *const known_sensor_luid[] = { "020B000000000000" }; +/* + * Match a known custom sensor. + * tag and luid is mandatory. + */ +struct hid_sensor_custom_match { + const char *tag; + const char *luid; + const char *model; + const char *manufacturer; + bool check_dmi; + struct dmi_system_id dmi; +}; -static int get_luid_table_index(unsigned char *usage_str) -{ - int i; +/* + * Custom sensor properties used for matching. + */ +struct hid_sensor_custom_properties { + u16 serial_num[HID_CUSTOM_MAX_FEATURE_BYTES]; + u16 model[HID_CUSTOM_MAX_FEATURE_BYTES]; + u16 manufacturer[HID_CUSTOM_MAX_FEATURE_BYTES]; +}; + +static const struct hid_sensor_custom_match hid_sensor_custom_known_table[] = { + /* + * Intel Integrated Sensor Hub (ISH) + */ + { /* Intel ISH hinge */ + .tag = "INT", + .luid = "020B000000000000", + .manufacturer = "INTEL", + }, + /* + * Lenovo Intelligent Sensing Solution (LISS) + */ + { /* ambient light */ + .tag = "LISS", + .luid = "0041010200000082", + .model = "STK3X3X Sensor", + .manufacturer = "Vendor 258", + .check_dmi = true, + .dmi.matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + } + }, + { /* human presence */ + .tag = "LISS", + .luid = "0226000171AC0081", + .model = "VL53L1_HOD Sensor", + .manufacturer = "ST_MICRO", + .check_dmi = true, + .dmi.matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + } + }, + {} +}; - for (i = 0; i < ARRAY_SIZE(known_sensor_luid); i++) { - if (!strncmp(usage_str, known_sensor_luid[i], - strlen(known_sensor_luid[i]))) - return i; +static bool hid_sensor_custom_prop_match_str(const u16 *prop, const char *match, + size_t count) +{ + while (count-- && *prop && *match) { + if (*prop != (u16) *match) + return false; + prop++; + match++; } - return -ENODEV; + return (count == -1) || *prop == (u16)*match; } -static int get_known_custom_sensor_index(struct hid_sensor_hub_device *hsdev) +static int hid_sensor_custom_get_prop(struct hid_sensor_hub_device *hsdev, + u32 prop_usage_id, size_t prop_size, + u16 *prop) { - struct hid_sensor_hub_attribute_info sensor_manufacturer = { 0 }; - struct hid_sensor_hub_attribute_info sensor_luid_info = { 0 }; - int report_size; + struct hid_sensor_hub_attribute_info prop_attr = { 0 }; int ret; - static u16 w_buf[HID_CUSTOM_MAX_FEATURE_BYTES]; - static char buf[HID_CUSTOM_MAX_FEATURE_BYTES]; - int i; - memset(w_buf, 0, sizeof(w_buf)); - memset(buf, 0, sizeof(buf)); + memset(prop, 0, prop_size); - /* get manufacturer info */ - ret = sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, hsdev->usage, - HID_USAGE_SENSOR_PROP_MANUFACTURER, &sensor_manufacturer); + ret = sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, + hsdev->usage, prop_usage_id, + &prop_attr); if (ret < 0) return ret; - report_size = - sensor_hub_get_feature(hsdev, sensor_manufacturer.report_id, - sensor_manufacturer.index, sizeof(w_buf), - w_buf); - if (report_size <= 0) { - hid_err(hsdev->hdev, - "Failed to get sensor manufacturer info %d\n", - report_size); - return -ENODEV; + ret = sensor_hub_get_feature(hsdev, prop_attr.report_id, + prop_attr.index, prop_size, prop); + if (ret < 0) { + hid_err(hsdev->hdev, "Failed to get sensor property %08x %d\n", + prop_usage_id, ret); + return ret; } - /* convert from wide char to char */ - for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++) - buf[i] = (char)w_buf[i]; + return 0; +} - /* ensure it's ISH sensor */ - if (strncmp(buf, "INTEL", strlen("INTEL"))) - return -ENODEV; +static bool +hid_sensor_custom_do_match(struct hid_sensor_hub_device *hsdev, + const struct hid_sensor_custom_match *match, + const struct hid_sensor_custom_properties *prop) +{ + struct dmi_system_id dmi[] = { match->dmi, { 0 } }; - memset(w_buf, 0, sizeof(w_buf)); - memset(buf, 0, sizeof(buf)); + if (!hid_sensor_custom_prop_match_str(prop->serial_num, "LUID:", 5) || + !hid_sensor_custom_prop_match_str(prop->serial_num + 5, match->luid, + HID_CUSTOM_MAX_FEATURE_BYTES - 5)) + return false; - /* get real usage id */ - ret = sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, hsdev->usage, - HID_USAGE_SENSOR_PROP_SERIAL_NUM, &sensor_luid_info); + if (match->model && + !hid_sensor_custom_prop_match_str(prop->model, match->model, + HID_CUSTOM_MAX_FEATURE_BYTES)) + return false; + + if (match->manufacturer && + !hid_sensor_custom_prop_match_str(prop->manufacturer, match->manufacturer, + HID_CUSTOM_MAX_FEATURE_BYTES)) + return false; + + if (match->check_dmi && !dmi_check_system(dmi)) + return false; + + return true; +} + +static int +hid_sensor_custom_properties_get(struct hid_sensor_hub_device *hsdev, + struct hid_sensor_custom_properties *prop) +{ + int ret; + + ret = hid_sensor_custom_get_prop(hsdev, + HID_USAGE_SENSOR_PROP_SERIAL_NUM, + HID_CUSTOM_MAX_FEATURE_BYTES, + prop->serial_num); if (ret < 0) return ret; - report_size = sensor_hub_get_feature(hsdev, sensor_luid_info.report_id, - sensor_luid_info.index, sizeof(w_buf), - w_buf); - if (report_size <= 0) { - hid_err(hsdev->hdev, "Failed to get real usage info %d\n", - report_size); - return -ENODEV; - } + /* + * Ignore errors on the following model and manufacturer properties. + * Because these are optional, it is not an error if they are missing. + */ - /* convert from wide char to char */ - for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++) - buf[i] = (char)w_buf[i]; + hid_sensor_custom_get_prop(hsdev, HID_USAGE_SENSOR_PROP_MODEL, + HID_CUSTOM_MAX_FEATURE_BYTES, + prop->model); - if (strlen(buf) != strlen(known_sensor_luid[0]) + 5) { - hid_err(hsdev->hdev, - "%s luid length not match %zu != (%zu + 5)\n", __func__, - strlen(buf), strlen(known_sensor_luid[0])); - return -ENODEV; - } + hid_sensor_custom_get_prop(hsdev, HID_USAGE_SENSOR_PROP_MANUFACTURER, + HID_CUSTOM_MAX_FEATURE_BYTES, + prop->manufacturer); - /* get table index with luid (not matching 'LUID: ' in luid) */ - return get_luid_table_index(&buf[5]); + return 0; +} + +static int +hid_sensor_custom_get_known(struct hid_sensor_hub_device *hsdev, + const struct hid_sensor_custom_match **known) +{ + int ret; + const struct hid_sensor_custom_match *match = + hid_sensor_custom_known_table; + struct hid_sensor_custom_properties *prop; + + prop = kmalloc(sizeof(struct hid_sensor_custom_properties), GFP_KERNEL); + if (!prop) + return -ENOMEM; + + ret = hid_sensor_custom_properties_get(hsdev, prop); + if (ret < 0) + goto out; + + while (match->tag) { + if (hid_sensor_custom_do_match(hsdev, match, prop)) { + *known = match; + ret = 0; + goto out; + } + match++; + } + ret = -ENODATA; +out: + kfree(prop); + return ret; } static struct platform_device * hid_sensor_register_platform_device(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, - int index) + const struct hid_sensor_custom_match *match) { - char real_usage[HID_SENSOR_USAGE_LENGTH] = { 0 }; + char real_usage[HID_SENSOR_USAGE_LENGTH]; struct platform_device *custom_pdev; const char *dev_name; char *c; - /* copy real usage id */ - memcpy(real_usage, known_sensor_luid[index], 4); + memcpy(real_usage, match->luid, 4); /* usage id are all lowcase */ for (c = real_usage; *c != '\0'; c++) *c = tolower(*c); - /* HID-SENSOR-INT-REAL_USAGE_ID */ - dev_name = kasprintf(GFP_KERNEL, "HID-SENSOR-INT-%s", real_usage); + /* HID-SENSOR-TAG-REAL_USAGE_ID */ + dev_name = kasprintf(GFP_KERNEL, "HID-SENSOR-%s-%s", + match->tag, real_usage); if (!dev_name) return ERR_PTR(-ENOMEM); @@ -873,7 +969,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) struct hid_sensor_custom *sensor_inst; struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; int ret; - int index; + const struct hid_sensor_custom_match *match; sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst), GFP_KERNEL); @@ -888,10 +984,10 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) mutex_init(&sensor_inst->mutex); platform_set_drvdata(pdev, sensor_inst); - index = get_known_custom_sensor_index(hsdev); - if (index >= 0 && index < ARRAY_SIZE(known_sensor_luid)) { + ret = hid_sensor_custom_get_known(hsdev, &match); + if (!ret) { sensor_inst->custom_pdev = - hid_sensor_register_platform_device(pdev, hsdev, index); + hid_sensor_register_platform_device(pdev, hsdev, match); ret = PTR_ERR_OR_ZERO(sensor_inst->custom_pdev); if (ret) { diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 6abd3e2a9094..83237b86c8ff 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -397,7 +397,8 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, for (i = 0; i < report->maxfield; ++i) { field = report->field[i]; if (field->maxusage) { - if (field->physical == usage_id && + if ((field->physical == usage_id || + field->application == usage_id) && (field->logical == attr_usage_id || field->usage[0].hid == attr_usage_id) && @@ -506,7 +507,8 @@ static int sensor_hub_raw_event(struct hid_device *hdev, collection->usage); callback = sensor_hub_get_callback(hdev, - report->field[i]->physical, + report->field[i]->physical ? report->field[i]->physical : + report->field[i]->application, report->field[i]->usage[0].collection_index, &hsdev, &priv); if (!callback) { diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 13125997ab5e..dd942061fd77 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -49,38 +49,28 @@ #define SIXAXIS_CONTROLLER_BT BIT(2) #define BUZZ_CONTROLLER BIT(3) #define PS3REMOTE BIT(4) -#define DUALSHOCK4_CONTROLLER_USB BIT(5) -#define DUALSHOCK4_CONTROLLER_BT BIT(6) -#define DUALSHOCK4_DONGLE BIT(7) -#define MOTION_CONTROLLER_USB BIT(8) -#define MOTION_CONTROLLER_BT BIT(9) -#define NAVIGATION_CONTROLLER_USB BIT(10) -#define NAVIGATION_CONTROLLER_BT BIT(11) -#define SINO_LITE_CONTROLLER BIT(12) -#define FUTUREMAX_DANCE_MAT BIT(13) -#define NSG_MR5U_REMOTE_BT BIT(14) -#define NSG_MR7U_REMOTE_BT BIT(15) -#define SHANWAN_GAMEPAD BIT(16) -#define GH_GUITAR_CONTROLLER BIT(17) -#define GHL_GUITAR_PS3WIIU BIT(18) -#define GHL_GUITAR_PS4 BIT(19) +#define MOTION_CONTROLLER_USB BIT(5) +#define MOTION_CONTROLLER_BT BIT(6) +#define NAVIGATION_CONTROLLER_USB BIT(7) +#define NAVIGATION_CONTROLLER_BT BIT(8) +#define SINO_LITE_CONTROLLER BIT(9) +#define FUTUREMAX_DANCE_MAT BIT(10) +#define NSG_MR5U_REMOTE_BT BIT(11) +#define NSG_MR7U_REMOTE_BT BIT(12) +#define SHANWAN_GAMEPAD BIT(13) +#define GH_GUITAR_CONTROLLER BIT(14) +#define GHL_GUITAR_PS3WIIU BIT(15) +#define GHL_GUITAR_PS4 BIT(16) #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT) #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT) #define NAVIGATION_CONTROLLER (NAVIGATION_CONTROLLER_USB |\ NAVIGATION_CONTROLLER_BT) -#define DUALSHOCK4_CONTROLLER (DUALSHOCK4_CONTROLLER_USB |\ - DUALSHOCK4_CONTROLLER_BT | \ - DUALSHOCK4_DONGLE) #define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER | BUZZ_CONTROLLER |\ - DUALSHOCK4_CONTROLLER | MOTION_CONTROLLER |\ - NAVIGATION_CONTROLLER) -#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\ - MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER) -#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\ - MOTION_CONTROLLER) -#define SONY_BT_DEVICE (SIXAXIS_CONTROLLER_BT | DUALSHOCK4_CONTROLLER_BT |\ - MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER_BT) + MOTION_CONTROLLER | NAVIGATION_CONTROLLER) +#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER) +#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | MOTION_CONTROLLER) +#define SONY_BT_DEVICE (SIXAXIS_CONTROLLER_BT | MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER_BT) #define NSG_MRXU_REMOTE (NSG_MR5U_REMOTE_BT | NSG_MR7U_REMOTE_BT) #define MAX_LEDS 4 @@ -428,36 +418,6 @@ static const unsigned int sixaxis_keymap[] = { [0x11] = BTN_MODE, /* PS */ }; -static const unsigned int ds4_absmap[] = { - [0x30] = ABS_X, - [0x31] = ABS_Y, - [0x32] = ABS_RX, /* right stick X */ - [0x33] = ABS_Z, /* L2 */ - [0x34] = ABS_RZ, /* R2 */ - [0x35] = ABS_RY, /* right stick Y */ -}; - -static const unsigned int ds4_keymap[] = { - [0x1] = BTN_WEST, /* Square */ - [0x2] = BTN_SOUTH, /* Cross */ - [0x3] = BTN_EAST, /* Circle */ - [0x4] = BTN_NORTH, /* Triangle */ - [0x5] = BTN_TL, /* L1 */ - [0x6] = BTN_TR, /* R1 */ - [0x7] = BTN_TL2, /* L2 */ - [0x8] = BTN_TR2, /* R2 */ - [0x9] = BTN_SELECT, /* Share */ - [0xa] = BTN_START, /* Options */ - [0xb] = BTN_THUMBL, /* L3 */ - [0xc] = BTN_THUMBR, /* R3 */ - [0xd] = BTN_MODE, /* PS */ -}; - -static const struct {int x; int y; } ds4_hat_mapping[] = { - {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, - {0, 0} -}; - static enum power_supply_property sony_battery_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CAPACITY, @@ -502,35 +462,12 @@ struct motion_output_report_02 { u8 rumble; }; -#define DS4_FEATURE_REPORT_0x02_SIZE 37 -#define DS4_FEATURE_REPORT_0x05_SIZE 41 -#define DS4_FEATURE_REPORT_0x81_SIZE 7 -#define DS4_FEATURE_REPORT_0xA3_SIZE 49 -#define DS4_INPUT_REPORT_0x11_SIZE 78 -#define DS4_OUTPUT_REPORT_0x05_SIZE 32 -#define DS4_OUTPUT_REPORT_0x11_SIZE 78 #define SIXAXIS_REPORT_0xF2_SIZE 17 #define SIXAXIS_REPORT_0xF5_SIZE 8 #define MOTION_REPORT_0x02_SIZE 49 -/* Offsets relative to USB input report (0x1). Bluetooth (0x11) requires an - * additional +2. - */ -#define DS4_INPUT_REPORT_AXIS_OFFSET 1 -#define DS4_INPUT_REPORT_BUTTON_OFFSET 5 -#define DS4_INPUT_REPORT_TIMESTAMP_OFFSET 10 -#define DS4_INPUT_REPORT_GYRO_X_OFFSET 13 -#define DS4_INPUT_REPORT_BATTERY_OFFSET 30 -#define DS4_INPUT_REPORT_TOUCHPAD_OFFSET 33 - #define SENSOR_SUFFIX " Motion Sensors" -#define DS4_TOUCHPAD_SUFFIX " Touchpad" - -/* Default to 4ms poll interval, which is same as USB (not adjustable). */ -#define DS4_BT_DEFAULT_POLL_INTERVAL_MS 4 -#define DS4_BT_MAX_POLL_INTERVAL_MS 62 -#define DS4_GYRO_RES_PER_DEG_S 1024 -#define DS4_ACC_RES_PER_G 8192 +#define TOUCHPAD_SUFFIX " Touchpad" #define SIXAXIS_INPUT_REPORT_ACC_X_OFFSET 41 #define SIXAXIS_ACC_RES_PER_G 113 @@ -539,28 +476,8 @@ static DEFINE_SPINLOCK(sony_dev_list_lock); static LIST_HEAD(sony_device_list); static DEFINE_IDA(sony_device_id_allocator); -/* Used for calibration of DS4 accelerometer and gyro. */ -struct ds4_calibration_data { - int abs_code; - short bias; - /* Calibration requires scaling against a sensitivity value, which is a - * float. Store sensitivity as a fraction to limit floating point - * calculations until final calibration. - */ - int sens_numer; - int sens_denom; -}; - -enum ds4_dongle_state { - DONGLE_DISCONNECTED, - DONGLE_CALIBRATING, - DONGLE_CONNECTED, - DONGLE_DISABLED -}; - enum sony_worker { - SONY_WORKER_STATE, - SONY_WORKER_HOTPLUG + SONY_WORKER_STATE }; struct sony_sc { @@ -571,16 +488,11 @@ struct sony_sc { struct input_dev *sensor_dev; struct led_classdev *leds[MAX_LEDS]; unsigned long quirks; - struct work_struct hotplug_worker; struct work_struct state_worker; void (*send_output_report)(struct sony_sc *); struct power_supply *battery; struct power_supply_desc battery_desc; int device_id; - unsigned fw_version; - bool fw_version_created; - unsigned hw_version; - bool hw_version_created; u8 *output_report_dmabuf; #ifdef CONFIG_SONY_FF @@ -589,7 +501,6 @@ struct sony_sc { #endif u8 mac_address[6]; - u8 hotplug_worker_initialized; u8 state_worker_initialized; u8 defer_initialization; u8 battery_capacity; @@ -599,14 +510,6 @@ struct sony_sc { u8 led_delay_off[MAX_LEDS]; u8 led_count; - bool timestamp_initialized; - u16 prev_timestamp; - unsigned int timestamp_us; - - u8 ds4_bt_poll_interval; - enum ds4_dongle_state ds4_dongle_state; - /* DS4 calibration data */ - struct ds4_calibration_data ds4_calib_data[6]; /* GH Live */ struct urb *ghl_urb; struct timer_list ghl_poke_timer; @@ -626,10 +529,6 @@ static inline void sony_schedule_work(struct sony_sc *sc, schedule_work(&sc->state_worker); spin_unlock_irqrestore(&sc->lock, flags); break; - case SONY_WORKER_HOTPLUG: - if (sc->hotplug_worker_initialized) - schedule_work(&sc->hotplug_worker); - break; } } @@ -700,67 +599,6 @@ static int guitar_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } -static ssize_t ds4_show_poll_interval(struct device *dev, - struct device_attribute - *attr, char *buf) -{ - struct hid_device *hdev = to_hid_device(dev); - struct sony_sc *sc = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "%i\n", sc->ds4_bt_poll_interval); -} - -static ssize_t ds4_store_poll_interval(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct hid_device *hdev = to_hid_device(dev); - struct sony_sc *sc = hid_get_drvdata(hdev); - unsigned long flags; - u8 interval; - - if (kstrtou8(buf, 0, &interval)) - return -EINVAL; - - if (interval > DS4_BT_MAX_POLL_INTERVAL_MS) - return -EINVAL; - - spin_lock_irqsave(&sc->lock, flags); - sc->ds4_bt_poll_interval = interval; - spin_unlock_irqrestore(&sc->lock, flags); - - sony_schedule_work(sc, SONY_WORKER_STATE); - - return count; -} - -static DEVICE_ATTR(bt_poll_interval, 0644, ds4_show_poll_interval, - ds4_store_poll_interval); - -static ssize_t sony_show_firmware_version(struct device *dev, - struct device_attribute - *attr, char *buf) -{ - struct hid_device *hdev = to_hid_device(dev); - struct sony_sc *sc = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "0x%04x\n", sc->fw_version); -} - -static DEVICE_ATTR(firmware_version, 0444, sony_show_firmware_version, NULL); - -static ssize_t sony_show_hardware_version(struct device *dev, - struct device_attribute - *attr, char *buf) -{ - struct hid_device *hdev = to_hid_device(dev); - struct sony_sc *sc = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "0x%04x\n", sc->hw_version); -} - -static DEVICE_ATTR(hardware_version, 0444, sony_show_hardware_version, NULL); - static u8 *motion_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *rsize) { @@ -905,37 +743,6 @@ static int sixaxis_mapping(struct hid_device *hdev, struct hid_input *hi, return -1; } -static int ds4_mapping(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) { - unsigned int key = usage->hid & HID_USAGE; - - if (key >= ARRAY_SIZE(ds4_keymap)) - return -1; - - key = ds4_keymap[key]; - hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key); - return 1; - } else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) { - unsigned int abs = usage->hid & HID_USAGE; - - /* Let the HID parser deal with the HAT. */ - if (usage->hid == HID_GD_HATSWITCH) - return 0; - - if (abs >= ARRAY_SIZE(ds4_absmap)) - return -1; - - abs = ds4_absmap[abs]; - hid_map_usage_clear(hi, usage, bit, max, EV_ABS, abs); - return 1; - } - - return 0; -} - static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *rsize) { @@ -1034,216 +841,6 @@ static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size) } } -static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size) -{ - struct hid_input *hidinput = list_entry(sc->hdev->inputs.next, - struct hid_input, list); - struct input_dev *input_dev = hidinput->input; - unsigned long flags; - int n, m, offset, num_touch_data, max_touch_data; - u8 cable_state, battery_capacity; - int battery_status; - u16 timestamp; - - /* When using Bluetooth the header is 2 bytes longer, so skip these. */ - int data_offset = (sc->quirks & DUALSHOCK4_CONTROLLER_BT) ? 2 : 0; - - /* Second bit of third button byte is for the touchpad button. */ - offset = data_offset + DS4_INPUT_REPORT_BUTTON_OFFSET; - input_report_key(sc->touchpad, BTN_LEFT, rd[offset+2] & 0x2); - - /* - * The default behavior of the Dualshock 4 is to send reports using - * report type 1 when running over Bluetooth. However, when feature - * report 2 is requested during the controller initialization it starts - * sending input reports in report 17. Since report 17 is undefined - * in the default HID descriptor, the HID layer won't generate events. - * While it is possible (and this was done before) to fixup the HID - * descriptor to add this mapping, it was better to do this manually. - * The reason is there were various pieces software both open and closed - * source, relying on the descriptors to be the same across various - * operating systems. If the descriptors wouldn't match some - * applications e.g. games on Wine would not be able to function due - * to different descriptors, which such applications are not parsing. - */ - if (rd[0] == 17) { - int value; - - offset = data_offset + DS4_INPUT_REPORT_AXIS_OFFSET; - input_report_abs(input_dev, ABS_X, rd[offset]); - input_report_abs(input_dev, ABS_Y, rd[offset+1]); - input_report_abs(input_dev, ABS_RX, rd[offset+2]); - input_report_abs(input_dev, ABS_RY, rd[offset+3]); - - value = rd[offset+4] & 0xf; - if (value > 7) - value = 8; /* Center 0, 0 */ - input_report_abs(input_dev, ABS_HAT0X, ds4_hat_mapping[value].x); - input_report_abs(input_dev, ABS_HAT0Y, ds4_hat_mapping[value].y); - - input_report_key(input_dev, BTN_WEST, rd[offset+4] & 0x10); - input_report_key(input_dev, BTN_SOUTH, rd[offset+4] & 0x20); - input_report_key(input_dev, BTN_EAST, rd[offset+4] & 0x40); - input_report_key(input_dev, BTN_NORTH, rd[offset+4] & 0x80); - - input_report_key(input_dev, BTN_TL, rd[offset+5] & 0x1); - input_report_key(input_dev, BTN_TR, rd[offset+5] & 0x2); - input_report_key(input_dev, BTN_TL2, rd[offset+5] & 0x4); - input_report_key(input_dev, BTN_TR2, rd[offset+5] & 0x8); - input_report_key(input_dev, BTN_SELECT, rd[offset+5] & 0x10); - input_report_key(input_dev, BTN_START, rd[offset+5] & 0x20); - input_report_key(input_dev, BTN_THUMBL, rd[offset+5] & 0x40); - input_report_key(input_dev, BTN_THUMBR, rd[offset+5] & 0x80); - - input_report_key(input_dev, BTN_MODE, rd[offset+6] & 0x1); - - input_report_abs(input_dev, ABS_Z, rd[offset+7]); - input_report_abs(input_dev, ABS_RZ, rd[offset+8]); - - input_sync(input_dev); - } - - /* Convert timestamp (in 5.33us unit) to timestamp_us */ - offset = data_offset + DS4_INPUT_REPORT_TIMESTAMP_OFFSET; - timestamp = get_unaligned_le16(&rd[offset]); - if (!sc->timestamp_initialized) { - sc->timestamp_us = ((unsigned int)timestamp * 16) / 3; - sc->timestamp_initialized = true; - } else { - u16 delta; - - if (sc->prev_timestamp > timestamp) - delta = (U16_MAX - sc->prev_timestamp + timestamp + 1); - else - delta = timestamp - sc->prev_timestamp; - sc->timestamp_us += (delta * 16) / 3; - } - sc->prev_timestamp = timestamp; - input_event(sc->sensor_dev, EV_MSC, MSC_TIMESTAMP, sc->timestamp_us); - - offset = data_offset + DS4_INPUT_REPORT_GYRO_X_OFFSET; - for (n = 0; n < 6; n++) { - /* Store data in int for more precision during mult_frac. */ - int raw_data = (short)((rd[offset+1] << 8) | rd[offset]); - struct ds4_calibration_data *calib = &sc->ds4_calib_data[n]; - - /* High precision is needed during calibration, but the - * calibrated values are within 32-bit. - * Note: we swap numerator 'x' and 'numer' in mult_frac for - * precision reasons so we don't need 64-bit. - */ - int calib_data = mult_frac(calib->sens_numer, - raw_data - calib->bias, - calib->sens_denom); - - input_report_abs(sc->sensor_dev, calib->abs_code, calib_data); - offset += 2; - } - input_sync(sc->sensor_dev); - - /* - * The lower 4 bits of byte 30 (or 32 for BT) contain the battery level - * and the 5th bit contains the USB cable state. - */ - offset = data_offset + DS4_INPUT_REPORT_BATTERY_OFFSET; - cable_state = (rd[offset] >> 4) & 0x01; - - /* - * Interpretation of the battery_capacity data depends on the cable state. - * When no cable is connected (bit4 is 0): - * - 0:10: percentage in units of 10%. - * When a cable is plugged in: - * - 0-10: percentage in units of 10%. - * - 11: battery is full - * - 14: not charging due to Voltage or temperature error - * - 15: charge error - */ - if (cable_state) { - u8 battery_data = rd[offset] & 0xf; - - if (battery_data < 10) { - /* Take the mid-point for each battery capacity value, - * because on the hardware side 0 = 0-9%, 1=10-19%, etc. - * This matches official platform behavior, which does - * the same. - */ - battery_capacity = battery_data * 10 + 5; - battery_status = POWER_SUPPLY_STATUS_CHARGING; - } else if (battery_data == 10) { - battery_capacity = 100; - battery_status = POWER_SUPPLY_STATUS_CHARGING; - } else if (battery_data == 11) { - battery_capacity = 100; - battery_status = POWER_SUPPLY_STATUS_FULL; - } else { /* 14, 15 and undefined values */ - battery_capacity = 0; - battery_status = POWER_SUPPLY_STATUS_UNKNOWN; - } - } else { - u8 battery_data = rd[offset] & 0xf; - - if (battery_data < 10) - battery_capacity = battery_data * 10 + 5; - else /* 10 */ - battery_capacity = 100; - - battery_status = POWER_SUPPLY_STATUS_DISCHARGING; - } - - spin_lock_irqsave(&sc->lock, flags); - sc->battery_capacity = battery_capacity; - sc->battery_status = battery_status; - spin_unlock_irqrestore(&sc->lock, flags); - - /* - * The Dualshock 4 multi-touch trackpad data starts at offset 33 on USB - * and 35 on Bluetooth. - * The first byte indicates the number of touch data in the report. - * Trackpad data starts 2 bytes later (e.g. 35 for USB). - */ - offset = data_offset + DS4_INPUT_REPORT_TOUCHPAD_OFFSET; - max_touch_data = (sc->quirks & DUALSHOCK4_CONTROLLER_BT) ? 4 : 3; - if (rd[offset] > 0 && rd[offset] <= max_touch_data) - num_touch_data = rd[offset]; - else - num_touch_data = 1; - offset += 1; - - for (m = 0; m < num_touch_data; m++) { - /* Skip past timestamp */ - offset += 1; - - /* - * The first 7 bits of the first byte is a counter and bit 8 is - * a touch indicator that is 0 when pressed and 1 when not - * pressed. - * The next 3 bytes are two 12 bit touch coordinates, X and Y. - * The data for the second touch is in the same format and - * immediately follows the data for the first. - */ - for (n = 0; n < 2; n++) { - u16 x, y; - bool active; - - x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8); - y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4); - - active = !(rd[offset] >> 7); - input_mt_slot(sc->touchpad, n); - input_mt_report_slot_state(sc->touchpad, MT_TOOL_FINGER, active); - - if (active) { - input_report_abs(sc->touchpad, ABS_MT_POSITION_X, x); - input_report_abs(sc->touchpad, ABS_MT_POSITION_Y, y); - } - - offset += 4; - } - input_mt_sync_frame(sc->touchpad); - input_sync(sc->touchpad); - } -} - static void nsg_mrxu_parse_report(struct sony_sc *sc, u8 *rd, int size) { int n, offset, relx, rely; @@ -1350,83 +947,6 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, } else if ((sc->quirks & NAVIGATION_CONTROLLER) && rd[0] == 0x01 && size == 49) { sixaxis_parse_report(sc, rd, size); - } else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 && - size == 64) { - dualshock4_parse_report(sc, rd, size); - } else if (((sc->quirks & DUALSHOCK4_CONTROLLER_BT) && rd[0] == 0x11 && - size == 78)) { - /* CRC check */ - u8 bthdr = 0xA1; - u32 crc; - u32 report_crc; - - crc = crc32_le(0xFFFFFFFF, &bthdr, 1); - crc = ~crc32_le(crc, rd, DS4_INPUT_REPORT_0x11_SIZE-4); - report_crc = get_unaligned_le32(&rd[DS4_INPUT_REPORT_0x11_SIZE-4]); - if (crc != report_crc) { - hid_dbg(sc->hdev, "DualShock 4 input report's CRC check failed, received crc 0x%0x != 0x%0x\n", - report_crc, crc); - return -EILSEQ; - } - - dualshock4_parse_report(sc, rd, size); - } else if ((sc->quirks & DUALSHOCK4_DONGLE) && rd[0] == 0x01 && - size == 64) { - unsigned long flags; - enum ds4_dongle_state dongle_state; - - /* - * In the case of a DS4 USB dongle, bit[2] of byte 31 indicates - * if a DS4 is actually connected (indicated by '0'). - * For non-dongle, this bit is always 0 (connected). - */ - bool connected = (rd[31] & 0x04) ? false : true; - - spin_lock_irqsave(&sc->lock, flags); - dongle_state = sc->ds4_dongle_state; - spin_unlock_irqrestore(&sc->lock, flags); - - /* - * The dongle always sends input reports even when no - * DS4 is attached. When a DS4 is connected, we need to - * obtain calibration data before we can use it. - * The code below tracks dongle state and kicks of - * calibration when needed and only allows us to process - * input if a DS4 is actually connected. - */ - if (dongle_state == DONGLE_DISCONNECTED && connected) { - hid_info(sc->hdev, "DualShock 4 USB dongle: controller connected\n"); - sony_set_leds(sc); - - spin_lock_irqsave(&sc->lock, flags); - sc->ds4_dongle_state = DONGLE_CALIBRATING; - spin_unlock_irqrestore(&sc->lock, flags); - - sony_schedule_work(sc, SONY_WORKER_HOTPLUG); - - /* Don't process the report since we don't have - * calibration data, but let hidraw have it anyway. - */ - return 0; - } else if ((dongle_state == DONGLE_CONNECTED || - dongle_state == DONGLE_DISABLED) && !connected) { - hid_info(sc->hdev, "DualShock 4 USB dongle: controller disconnected\n"); - - spin_lock_irqsave(&sc->lock, flags); - sc->ds4_dongle_state = DONGLE_DISCONNECTED; - spin_unlock_irqrestore(&sc->lock, flags); - - /* Return 0, so hidraw can get the report. */ - return 0; - } else if (dongle_state == DONGLE_CALIBRATING || - dongle_state == DONGLE_DISABLED || - dongle_state == DONGLE_DISCONNECTED) { - /* Return 0, so hidraw can get the report. */ - return 0; - } - - dualshock4_parse_report(sc, rd, size); - } else if ((sc->quirks & NSG_MRXU_REMOTE) && rd[0] == 0x02) { nsg_mrxu_parse_report(sc, rd, size); return 1; @@ -1478,9 +998,6 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, if (sc->quirks & SIXAXIS_CONTROLLER) return sixaxis_mapping(hdev, hi, field, usage, bit, max); - if (sc->quirks & DUALSHOCK4_CONTROLLER) - return ds4_mapping(hdev, hi, field, usage, bit, max); - if (sc->quirks & GH_GUITAR_CONTROLLER) return guitar_mapping(hdev, hi, field, usage, bit, max); @@ -1508,14 +1025,17 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count, sc->touchpad->id.product = sc->hdev->product; sc->touchpad->id.version = sc->hdev->version; - /* Append a suffix to the controller name as there are various - * DS4 compatible non-Sony devices with different names. + /* This suffix was originally apended when hid-sony also + * supported DS4 devices. The DS4 was implemented using multiple + * evdev nodes and hence had the need to separete them out using + * a suffix. Other devices which were added later like Sony TV remotes + * inhirited this suffix. */ - name_sz = strlen(sc->hdev->name) + sizeof(DS4_TOUCHPAD_SUFFIX); + name_sz = strlen(sc->hdev->name) + sizeof(TOUCHPAD_SUFFIX); name = devm_kzalloc(&sc->hdev->dev, name_sz, GFP_KERNEL); if (!name) return -ENOMEM; - snprintf(name, name_sz, "%s" DS4_TOUCHPAD_SUFFIX, sc->hdev->name); + snprintf(name, name_sz, "%s" TOUCHPAD_SUFFIX, sc->hdev->name); sc->touchpad->name = name; /* We map the button underneath the touchpad to BTN_LEFT. */ @@ -1557,7 +1077,6 @@ static int sony_register_sensors(struct sony_sc *sc) size_t name_sz; char *name; int ret; - int range; sc->sensor_dev = devm_input_allocate_device(&sc->hdev->dev); if (!sc->sensor_dev) @@ -1595,25 +1114,6 @@ static int sony_register_sensors(struct sony_sc *sc) input_abs_set_res(sc->sensor_dev, ABS_X, SIXAXIS_ACC_RES_PER_G); input_abs_set_res(sc->sensor_dev, ABS_Y, SIXAXIS_ACC_RES_PER_G); input_abs_set_res(sc->sensor_dev, ABS_Z, SIXAXIS_ACC_RES_PER_G); - } else if (sc->quirks & DUALSHOCK4_CONTROLLER) { - range = DS4_ACC_RES_PER_G*4; - input_set_abs_params(sc->sensor_dev, ABS_X, -range, range, 16, 0); - input_set_abs_params(sc->sensor_dev, ABS_Y, -range, range, 16, 0); - input_set_abs_params(sc->sensor_dev, ABS_Z, -range, range, 16, 0); - input_abs_set_res(sc->sensor_dev, ABS_X, DS4_ACC_RES_PER_G); - input_abs_set_res(sc->sensor_dev, ABS_Y, DS4_ACC_RES_PER_G); - input_abs_set_res(sc->sensor_dev, ABS_Z, DS4_ACC_RES_PER_G); - - range = DS4_GYRO_RES_PER_DEG_S*2048; - input_set_abs_params(sc->sensor_dev, ABS_RX, -range, range, 16, 0); - input_set_abs_params(sc->sensor_dev, ABS_RY, -range, range, 16, 0); - input_set_abs_params(sc->sensor_dev, ABS_RZ, -range, range, 16, 0); - input_abs_set_res(sc->sensor_dev, ABS_RX, DS4_GYRO_RES_PER_DEG_S); - input_abs_set_res(sc->sensor_dev, ABS_RY, DS4_GYRO_RES_PER_DEG_S); - input_abs_set_res(sc->sensor_dev, ABS_RZ, DS4_GYRO_RES_PER_DEG_S); - - __set_bit(EV_MSC, sc->sensor_dev->evbit); - __set_bit(MSC_TIMESTAMP, sc->sensor_dev->mscbit); } __set_bit(INPUT_PROP_ACCELEROMETER, sc->sensor_dev->propbit); @@ -1697,224 +1197,6 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev) return ret; } -/* - * Request DS4 calibration data for the motion sensors. - * For Bluetooth this also affects the operating mode (see below). - */ -static int dualshock4_get_calibration_data(struct sony_sc *sc) -{ - u8 *buf; - int ret; - short gyro_pitch_bias, gyro_pitch_plus, gyro_pitch_minus; - short gyro_yaw_bias, gyro_yaw_plus, gyro_yaw_minus; - short gyro_roll_bias, gyro_roll_plus, gyro_roll_minus; - short gyro_speed_plus, gyro_speed_minus; - short acc_x_plus, acc_x_minus; - short acc_y_plus, acc_y_minus; - short acc_z_plus, acc_z_minus; - int speed_2x; - int range_2g; - - /* For Bluetooth we use a different request, which supports CRC. - * Note: in Bluetooth mode feature report 0x02 also changes the state - * of the controller, so that it sends input reports of type 0x11. - */ - if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE)) { - int retries; - - buf = kmalloc(DS4_FEATURE_REPORT_0x02_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* We should normally receive the feature report data we asked - * for, but hidraw applications such as Steam can issue feature - * reports as well. In particular for Dongle reconnects, Steam - * and this function are competing resulting in often receiving - * data for a different HID report, so retry a few times. - */ - for (retries = 0; retries < 3; retries++) { - ret = hid_hw_raw_request(sc->hdev, 0x02, buf, - DS4_FEATURE_REPORT_0x02_SIZE, - HID_FEATURE_REPORT, - HID_REQ_GET_REPORT); - if (ret < 0) - goto err_stop; - - if (buf[0] != 0x02) { - if (retries < 2) { - hid_warn(sc->hdev, "Retrying DualShock 4 get calibration report (0x02) request\n"); - continue; - } else { - ret = -EILSEQ; - goto err_stop; - } - } else { - break; - } - } - } else { - u8 bthdr = 0xA3; - u32 crc; - u32 report_crc; - int retries; - - buf = kmalloc(DS4_FEATURE_REPORT_0x05_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - for (retries = 0; retries < 3; retries++) { - ret = hid_hw_raw_request(sc->hdev, 0x05, buf, - DS4_FEATURE_REPORT_0x05_SIZE, - HID_FEATURE_REPORT, - HID_REQ_GET_REPORT); - if (ret < 0) - goto err_stop; - - /* CRC check */ - crc = crc32_le(0xFFFFFFFF, &bthdr, 1); - crc = ~crc32_le(crc, buf, DS4_FEATURE_REPORT_0x05_SIZE-4); - report_crc = get_unaligned_le32(&buf[DS4_FEATURE_REPORT_0x05_SIZE-4]); - if (crc != report_crc) { - hid_warn(sc->hdev, "DualShock 4 calibration report's CRC check failed, received crc 0x%0x != 0x%0x\n", - report_crc, crc); - if (retries < 2) { - hid_warn(sc->hdev, "Retrying DualShock 4 get calibration report request\n"); - continue; - } else { - ret = -EILSEQ; - goto err_stop; - } - } else { - break; - } - } - } - - gyro_pitch_bias = get_unaligned_le16(&buf[1]); - gyro_yaw_bias = get_unaligned_le16(&buf[3]); - gyro_roll_bias = get_unaligned_le16(&buf[5]); - if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { - gyro_pitch_plus = get_unaligned_le16(&buf[7]); - gyro_pitch_minus = get_unaligned_le16(&buf[9]); - gyro_yaw_plus = get_unaligned_le16(&buf[11]); - gyro_yaw_minus = get_unaligned_le16(&buf[13]); - gyro_roll_plus = get_unaligned_le16(&buf[15]); - gyro_roll_minus = get_unaligned_le16(&buf[17]); - } else { - /* BT + Dongle */ - gyro_pitch_plus = get_unaligned_le16(&buf[7]); - gyro_yaw_plus = get_unaligned_le16(&buf[9]); - gyro_roll_plus = get_unaligned_le16(&buf[11]); - gyro_pitch_minus = get_unaligned_le16(&buf[13]); - gyro_yaw_minus = get_unaligned_le16(&buf[15]); - gyro_roll_minus = get_unaligned_le16(&buf[17]); - } - gyro_speed_plus = get_unaligned_le16(&buf[19]); - gyro_speed_minus = get_unaligned_le16(&buf[21]); - acc_x_plus = get_unaligned_le16(&buf[23]); - acc_x_minus = get_unaligned_le16(&buf[25]); - acc_y_plus = get_unaligned_le16(&buf[27]); - acc_y_minus = get_unaligned_le16(&buf[29]); - acc_z_plus = get_unaligned_le16(&buf[31]); - acc_z_minus = get_unaligned_le16(&buf[33]); - - /* Set gyroscope calibration and normalization parameters. - * Data values will be normalized to 1/DS4_GYRO_RES_PER_DEG_S degree/s. - */ - speed_2x = (gyro_speed_plus + gyro_speed_minus); - sc->ds4_calib_data[0].abs_code = ABS_RX; - sc->ds4_calib_data[0].bias = gyro_pitch_bias; - sc->ds4_calib_data[0].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S; - sc->ds4_calib_data[0].sens_denom = gyro_pitch_plus - gyro_pitch_minus; - - sc->ds4_calib_data[1].abs_code = ABS_RY; - sc->ds4_calib_data[1].bias = gyro_yaw_bias; - sc->ds4_calib_data[1].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S; - sc->ds4_calib_data[1].sens_denom = gyro_yaw_plus - gyro_yaw_minus; - - sc->ds4_calib_data[2].abs_code = ABS_RZ; - sc->ds4_calib_data[2].bias = gyro_roll_bias; - sc->ds4_calib_data[2].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S; - sc->ds4_calib_data[2].sens_denom = gyro_roll_plus - gyro_roll_minus; - - /* Set accelerometer calibration and normalization parameters. - * Data values will be normalized to 1/DS4_ACC_RES_PER_G G. - */ - range_2g = acc_x_plus - acc_x_minus; - sc->ds4_calib_data[3].abs_code = ABS_X; - sc->ds4_calib_data[3].bias = acc_x_plus - range_2g / 2; - sc->ds4_calib_data[3].sens_numer = 2*DS4_ACC_RES_PER_G; - sc->ds4_calib_data[3].sens_denom = range_2g; - - range_2g = acc_y_plus - acc_y_minus; - sc->ds4_calib_data[4].abs_code = ABS_Y; - sc->ds4_calib_data[4].bias = acc_y_plus - range_2g / 2; - sc->ds4_calib_data[4].sens_numer = 2*DS4_ACC_RES_PER_G; - sc->ds4_calib_data[4].sens_denom = range_2g; - - range_2g = acc_z_plus - acc_z_minus; - sc->ds4_calib_data[5].abs_code = ABS_Z; - sc->ds4_calib_data[5].bias = acc_z_plus - range_2g / 2; - sc->ds4_calib_data[5].sens_numer = 2*DS4_ACC_RES_PER_G; - sc->ds4_calib_data[5].sens_denom = range_2g; - -err_stop: - kfree(buf); - return ret; -} - -static void dualshock4_calibration_work(struct work_struct *work) -{ - struct sony_sc *sc = container_of(work, struct sony_sc, hotplug_worker); - unsigned long flags; - enum ds4_dongle_state dongle_state; - int ret; - - ret = dualshock4_get_calibration_data(sc); - if (ret < 0) { - /* This call is very unlikely to fail for the dongle. When it - * fails we are probably in a very bad state, so mark the - * dongle as disabled. We will re-enable the dongle if a new - * DS4 hotplug is detect from sony_raw_event as any issues - * are likely resolved then (the dongle is quite stupid). - */ - hid_err(sc->hdev, "DualShock 4 USB dongle: calibration failed, disabling device\n"); - dongle_state = DONGLE_DISABLED; - } else { - hid_info(sc->hdev, "DualShock 4 USB dongle: calibration completed\n"); - dongle_state = DONGLE_CONNECTED; - } - - spin_lock_irqsave(&sc->lock, flags); - sc->ds4_dongle_state = dongle_state; - spin_unlock_irqrestore(&sc->lock, flags); -} - -static int dualshock4_get_version_info(struct sony_sc *sc) -{ - u8 *buf; - int ret; - - buf = kmalloc(DS4_FEATURE_REPORT_0xA3_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = hid_hw_raw_request(sc->hdev, 0xA3, buf, - DS4_FEATURE_REPORT_0xA3_SIZE, - HID_FEATURE_REPORT, - HID_REQ_GET_REPORT); - if (ret < 0) { - kfree(buf); - return ret; - } - - sc->hw_version = get_unaligned_le16(&buf[35]); - sc->fw_version = get_unaligned_le16(&buf[41]); - - kfree(buf); - return 0; -} - static void sixaxis_set_leds_from_id(struct sony_sc *sc) { static const u8 sixaxis_leds[10][4] = { @@ -1941,30 +1223,6 @@ static void sixaxis_set_leds_from_id(struct sony_sc *sc) memcpy(sc->led_state, sixaxis_leds[id], sizeof(sixaxis_leds[id])); } -static void dualshock4_set_leds_from_id(struct sony_sc *sc) -{ - /* The first 4 color/index entries match what the PS4 assigns */ - static const u8 color_code[7][3] = { - /* Blue */ { 0x00, 0x00, 0x40 }, - /* Red */ { 0x40, 0x00, 0x00 }, - /* Green */ { 0x00, 0x40, 0x00 }, - /* Pink */ { 0x20, 0x00, 0x20 }, - /* Orange */ { 0x02, 0x01, 0x00 }, - /* Teal */ { 0x00, 0x01, 0x01 }, - /* White */ { 0x01, 0x01, 0x01 } - }; - - int id = sc->device_id; - - BUILD_BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0])); - - if (id < 0) - return; - - id %= 7; - memcpy(sc->led_state, color_code[id], sizeof(color_code[id])); -} - static void buzz_set_leds(struct sony_sc *sc) { struct hid_device *hdev = sc->hdev; @@ -2110,13 +1368,13 @@ static int sony_leds_init(struct sony_sc *sc) { struct hid_device *hdev = sc->hdev; int n, ret = 0; - int use_ds4_names; + int use_color_names; struct led_classdev *led; size_t name_sz; char *name; size_t name_len; const char *name_fmt; - static const char * const ds4_name_str[] = { "red", "green", "blue", + static const char * const color_name_str[] = { "red", "green", "blue", "global" }; u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 }; u8 use_hw_blink[MAX_LEDS] = { 0 }; @@ -2125,25 +1383,16 @@ static int sony_leds_init(struct sony_sc *sc) if (sc->quirks & BUZZ_CONTROLLER) { sc->led_count = 4; - use_ds4_names = 0; + use_color_names = 0; name_len = strlen("::buzz#"); name_fmt = "%s::buzz%d"; /* Validate expected report characteristics. */ if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7)) return -ENODEV; - } else if (sc->quirks & DUALSHOCK4_CONTROLLER) { - dualshock4_set_leds_from_id(sc); - sc->led_state[3] = 1; - sc->led_count = 4; - memset(max_brightness, 255, 3); - use_hw_blink[3] = 1; - use_ds4_names = 1; - name_len = 0; - name_fmt = "%s:%s"; } else if (sc->quirks & MOTION_CONTROLLER) { sc->led_count = 3; memset(max_brightness, 255, 3); - use_ds4_names = 1; + use_color_names = 1; name_len = 0; name_fmt = "%s:%s"; } else if (sc->quirks & NAVIGATION_CONTROLLER) { @@ -2152,14 +1401,14 @@ static int sony_leds_init(struct sony_sc *sc) memcpy(sc->led_state, navigation_leds, sizeof(navigation_leds)); sc->led_count = 1; memset(use_hw_blink, 1, 4); - use_ds4_names = 0; + use_color_names = 0; name_len = strlen("::sony#"); name_fmt = "%s::sony%d"; } else { sixaxis_set_leds_from_id(sc); sc->led_count = 4; memset(use_hw_blink, 1, 4); - use_ds4_names = 0; + use_color_names = 0; name_len = strlen("::sony#"); name_fmt = "%s::sony%d"; } @@ -2175,8 +1424,8 @@ static int sony_leds_init(struct sony_sc *sc) for (n = 0; n < sc->led_count; n++) { - if (use_ds4_names) - name_sz = strlen(dev_name(&hdev->dev)) + strlen(ds4_name_str[n]) + 2; + if (use_color_names) + name_sz = strlen(dev_name(&hdev->dev)) + strlen(color_name_str[n]) + 2; led = devm_kzalloc(&hdev->dev, sizeof(struct led_classdev) + name_sz, GFP_KERNEL); if (!led) { @@ -2185,9 +1434,9 @@ static int sony_leds_init(struct sony_sc *sc) } name = (void *)(&led[1]); - if (use_ds4_names) + if (use_color_names) snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), - ds4_name_str[n]); + color_name_str[n]); else snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1); led->name = name; @@ -2273,68 +1522,6 @@ static void sixaxis_send_output_report(struct sony_sc *sc) HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); } -static void dualshock4_send_output_report(struct sony_sc *sc) -{ - struct hid_device *hdev = sc->hdev; - u8 *buf = sc->output_report_dmabuf; - int offset; - - /* - * NOTE: The lower 6 bits of buf[1] field of the Bluetooth report - * control the interval at which Dualshock 4 reports data: - * 0x00 - 1ms - * 0x01 - 1ms - * 0x02 - 2ms - * 0x3E - 62ms - * 0x3F - disabled - */ - if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE)) { - memset(buf, 0, DS4_OUTPUT_REPORT_0x05_SIZE); - buf[0] = 0x05; - buf[1] = 0x07; /* blink + LEDs + motor */ - offset = 4; - } else { - memset(buf, 0, DS4_OUTPUT_REPORT_0x11_SIZE); - buf[0] = 0x11; - buf[1] = 0xC0 /* HID + CRC */ | sc->ds4_bt_poll_interval; - buf[3] = 0x07; /* blink + LEDs + motor */ - offset = 6; - } - -#ifdef CONFIG_SONY_FF - buf[offset++] = sc->right; - buf[offset++] = sc->left; -#else - offset += 2; -#endif - - /* LED 3 is the global control */ - if (sc->led_state[3]) { - buf[offset++] = sc->led_state[0]; - buf[offset++] = sc->led_state[1]; - buf[offset++] = sc->led_state[2]; - } else { - offset += 3; - } - - /* If both delay values are zero the DualShock 4 disables blinking. */ - buf[offset++] = sc->led_delay_on[3]; - buf[offset++] = sc->led_delay_off[3]; - - if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE)) - hid_hw_output_report(hdev, buf, DS4_OUTPUT_REPORT_0x05_SIZE); - else { - /* CRC generation */ - u8 bthdr = 0xA2; - u32 crc; - - crc = crc32_le(0xFFFFFFFF, &bthdr, 1); - crc = ~crc32_le(crc, buf, DS4_OUTPUT_REPORT_0x11_SIZE-4); - put_unaligned_le32(crc, &buf[74]); - hid_hw_output_report(hdev, buf, DS4_OUTPUT_REPORT_0x11_SIZE); - } -} - static void motion_send_output_report(struct sony_sc *sc) { struct hid_device *hdev = sc->hdev; @@ -2378,14 +1565,6 @@ static int sony_allocate_output_report(struct sony_sc *sc) devm_kmalloc(&sc->hdev->dev, sizeof(union sixaxis_output_report_01), GFP_KERNEL); - else if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) - sc->output_report_dmabuf = devm_kmalloc(&sc->hdev->dev, - DS4_OUTPUT_REPORT_0x11_SIZE, - GFP_KERNEL); - else if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE)) - sc->output_report_dmabuf = devm_kmalloc(&sc->hdev->dev, - DS4_OUTPUT_REPORT_0x05_SIZE, - GFP_KERNEL); else if (sc->quirks & MOTION_CONTROLLER) sc->output_report_dmabuf = devm_kmalloc(&sc->hdev->dev, MOTION_REPORT_0x02_SIZE, @@ -2600,8 +1779,7 @@ static int sony_check_add(struct sony_sc *sc) u8 *buf = NULL; int n, ret; - if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) || - (sc->quirks & MOTION_CONTROLLER_BT) || + if ((sc->quirks & MOTION_CONTROLLER_BT) || (sc->quirks & NAVIGATION_CONTROLLER_BT) || (sc->quirks & SIXAXIS_CONTROLLER_BT)) { /* @@ -2614,30 +1792,6 @@ static int sony_check_add(struct sony_sc *sc) hid_warn(sc->hdev, "UNIQ does not contain a MAC address; duplicate check skipped\n"); return 0; } - } else if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE)) { - buf = kmalloc(DS4_FEATURE_REPORT_0x81_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* - * The MAC address of a DS4 controller connected via USB can be - * retrieved with feature report 0x81. The address begins at - * offset 1. - */ - ret = hid_hw_raw_request(sc->hdev, 0x81, buf, - DS4_FEATURE_REPORT_0x81_SIZE, HID_FEATURE_REPORT, - HID_REQ_GET_REPORT); - - if (ret != DS4_FEATURE_REPORT_0x81_SIZE) { - hid_err(sc->hdev, "failed to retrieve feature report 0x81 with the DualShock 4 MAC address\n"); - ret = ret < 0 ? ret : -EINVAL; - goto out_free; - } - - memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address)); - - snprintf(sc->hdev->uniq, sizeof(sc->hdev->uniq), - "%pMR", sc->mac_address); } else if ((sc->quirks & SIXAXIS_CONTROLLER_USB) || (sc->quirks & NAVIGATION_CONTROLLER_USB)) { buf = kmalloc(SIXAXIS_REPORT_0xF2_SIZE, GFP_KERNEL); @@ -2686,11 +1840,10 @@ static int sony_set_device_id(struct sony_sc *sc) int ret; /* - * Only DualShock 4 or Sixaxis controllers get an id. + * Only Sixaxis controllers get an id. * All others are set to -1. */ - if ((sc->quirks & SIXAXIS_CONTROLLER) || - (sc->quirks & DUALSHOCK4_CONTROLLER)) { + if (sc->quirks & SIXAXIS_CONTROLLER) { ret = ida_simple_get(&sony_device_id_allocator, 0, 0, GFP_KERNEL); if (ret < 0) { @@ -2728,8 +1881,6 @@ static inline void sony_cancel_work_sync(struct sony_sc *sc) { unsigned long flags; - if (sc->hotplug_worker_initialized) - cancel_work_sync(&sc->hotplug_worker); if (sc->state_worker_initialized) { spin_lock_irqsave(&sc->lock, flags); sc->state_worker_initialized = 0; @@ -2849,68 +2000,6 @@ static int sony_input_configured(struct hid_device *hdev, } sony_init_output_report(sc, sixaxis_send_output_report); - } else if (sc->quirks & DUALSHOCK4_CONTROLLER) { - ret = dualshock4_get_calibration_data(sc); - if (ret < 0) { - hid_err(hdev, "Failed to get calibration data from Dualshock 4\n"); - goto err_stop; - } - - ret = dualshock4_get_version_info(sc); - if (ret < 0) { - hid_err(sc->hdev, "Failed to get version data from Dualshock 4\n"); - goto err_stop; - } - - ret = device_create_file(&sc->hdev->dev, &dev_attr_firmware_version); - if (ret) { - hid_err(sc->hdev, "can't create sysfs firmware_version attribute err: %d\n", ret); - goto err_stop; - } - sc->fw_version_created = true; - - ret = device_create_file(&sc->hdev->dev, &dev_attr_hardware_version); - if (ret) { - hid_err(sc->hdev, "can't create sysfs hardware_version attribute err: %d\n", ret); - goto err_stop; - } - sc->hw_version_created = true; - - /* - * The Dualshock 4 touchpad supports 2 touches and has a - * resolution of 1920x942 (44.86 dots/mm). - */ - ret = sony_register_touchpad(sc, 2, 1920, 942, 0, 0, 0); - if (ret) { - hid_err(sc->hdev, - "Unable to initialize multi-touch slots: %d\n", - ret); - goto err_stop; - } - - ret = sony_register_sensors(sc); - if (ret) { - hid_err(sc->hdev, - "Unable to initialize motion sensors: %d\n", ret); - goto err_stop; - } - - if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) { - sc->ds4_bt_poll_interval = DS4_BT_DEFAULT_POLL_INTERVAL_MS; - ret = device_create_file(&sc->hdev->dev, &dev_attr_bt_poll_interval); - if (ret) - hid_warn(sc->hdev, - "can't create sysfs bt_poll_interval attribute err: %d\n", - ret); - } - - if (sc->quirks & DUALSHOCK4_DONGLE) { - INIT_WORK(&sc->hotplug_worker, dualshock4_calibration_work); - sc->hotplug_worker_initialized = 1; - sc->ds4_dongle_state = DONGLE_DISCONNECTED; - } - - sony_init_output_report(sc, dualshock4_send_output_report); } else if (sc->quirks & NSG_MRXU_REMOTE) { /* * The NSG-MRxU touchpad supports 2 touches and has a @@ -2960,16 +2049,6 @@ static int sony_input_configured(struct hid_device *hdev, err_close: hid_hw_close(hdev); err_stop: - /* Piggy back on the default ds4_bt_ poll_interval to determine - * if we need to remove the file as we don't know for sure if we - * executed that logic. - */ - if (sc->ds4_bt_poll_interval) - device_remove_file(&sc->hdev->dev, &dev_attr_bt_poll_interval); - if (sc->fw_version_created) - device_remove_file(&sc->hdev->dev, &dev_attr_firmware_version); - if (sc->hw_version_created) - device_remove_file(&sc->hdev->dev, &dev_attr_hardware_version); sony_cancel_work_sync(sc); sony_remove_dev_list(sc); sony_release_device_id(sc); @@ -3014,13 +2093,13 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) else if (sc->quirks & SIXAXIS_CONTROLLER) connect_mask |= HID_CONNECT_HIDDEV_FORCE; - /* Patch the hw version on DS3/4 compatible devices, so applications can + /* Patch the hw version on DS3 compatible devices, so applications can * distinguish between the default HID mappings and the mappings defined * by the Linux game controller spec. This is important for the SDL2 * library, which has a game controller database, which uses device ids * in combination with version as a key. */ - if (sc->quirks & (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)) + if (sc->quirks & SIXAXIS_CONTROLLER) hdev->version |= 0x8000; ret = hid_hw_start(hdev, connect_mask); @@ -3091,15 +2170,6 @@ static void sony_remove(struct hid_device *hdev) hid_hw_close(hdev); - if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) - device_remove_file(&sc->hdev->dev, &dev_attr_bt_poll_interval); - - if (sc->fw_version_created) - device_remove_file(&sc->hdev->dev, &dev_attr_firmware_version); - - if (sc->hw_version_created) - device_remove_file(&sc->hdev->dev, &dev_attr_hardware_version); - sony_cancel_work_sync(sc); sony_remove_dev_list(sc); @@ -3180,17 +2250,6 @@ static const struct hid_device_id sony_devices[] = { /* SMK-Link PS3 BD Remote Control */ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE), .driver_data = PS3REMOTE }, - /* Sony Dualshock 4 controllers for PS4 */ - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), - .driver_data = DUALSHOCK4_CONTROLLER_USB }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), - .driver_data = DUALSHOCK4_CONTROLLER_BT }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2), - .driver_data = DUALSHOCK4_CONTROLLER_USB }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2), - .driver_data = DUALSHOCK4_CONTROLLER_BT }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE), - .driver_data = DUALSHOCK4_DONGLE }, /* Nyko Core Controller for PS3 */ { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER), .driver_data = SIXAXIS_CONTROLLER_USB | SINO_LITE_CONTROLLER }, diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c index 8ee43cb225fc..b110818fc945 100644 --- a/drivers/hid/hid-steam.c +++ b/drivers/hid/hid-steam.c @@ -3,6 +3,7 @@ * HID driver for Valve Steam Controller * * Copyright (c) 2018 Rodrigo Rivas Costa <rodrigorivascosta@gmail.com> + * Copyright (c) 2022 Valve Software * * Supports both the wired and wireless interfaces. * @@ -53,6 +54,7 @@ static DEFINE_MUTEX(steam_devices_lock); static LIST_HEAD(steam_devices); #define STEAM_QUIRK_WIRELESS BIT(0) +#define STEAM_QUIRK_DECK BIT(1) /* Touch pads are 40 mm in diameter and 65535 units */ #define STEAM_PAD_RESOLUTION 1638 @@ -60,6 +62,10 @@ static LIST_HEAD(steam_devices); #define STEAM_TRIGGER_RESOLUTION 51 /* Joystick runs are about 5 mm and 256 units */ #define STEAM_JOYSTICK_RESOLUTION 51 +/* Trigger runs are about 6 mm and 32768 units */ +#define STEAM_DECK_TRIGGER_RESOLUTION 5461 +/* Joystick runs are about 5 mm and 32768 units */ +#define STEAM_DECK_JOYSTICK_RESOLUTION 6553 #define STEAM_PAD_FUZZ 256 @@ -85,6 +91,7 @@ static LIST_HEAD(steam_devices); #define STEAM_CMD_FORCEFEEDBAK 0x8f #define STEAM_CMD_REQUEST_COMM_STATUS 0xb4 #define STEAM_CMD_GET_SERIAL 0xae +#define STEAM_CMD_HAPTIC_RUMBLE 0xeb /* Some useful register ids */ #define STEAM_REG_LPAD_MODE 0x07 @@ -92,11 +99,14 @@ static LIST_HEAD(steam_devices); #define STEAM_REG_RPAD_MARGIN 0x18 #define STEAM_REG_LED 0x2d #define STEAM_REG_GYRO_MODE 0x30 +#define STEAM_REG_LPAD_CLICK_PRESSURE 0x34 +#define STEAM_REG_RPAD_CLICK_PRESSURE 0x35 /* Raw event identifiers */ #define STEAM_EV_INPUT_DATA 0x01 #define STEAM_EV_CONNECT 0x03 #define STEAM_EV_BATTERY 0x04 +#define STEAM_EV_DECK_INPUT_DATA 0x09 /* Values for GYRO_MODE (bitmask) */ #define STEAM_GYRO_MODE_OFF 0x0000 @@ -124,6 +134,10 @@ struct steam_device { struct power_supply __rcu *battery; u8 battery_charge; u16 voltage; + struct delayed_work heartbeat; + struct work_struct rumble_work; + u16 rumble_left; + u16 rumble_right; }; static int steam_recv_report(struct steam_device *steam, @@ -193,7 +207,7 @@ static int steam_send_report(struct steam_device *steam, */ do { ret = hid_hw_raw_request(steam->hdev, 0, - buf, size + 1, + buf, max(size, 64) + 1, HID_FEATURE_REPORT, HID_REQ_SET_REPORT); if (ret != -EPIPE) break; @@ -219,6 +233,7 @@ static int steam_write_registers(struct steam_device *steam, u8 reg; u16 val; u8 cmd[64] = {STEAM_CMD_WRITE_REGISTER, 0x00}; + int ret; va_list args; va_start(args, steam); @@ -234,7 +249,16 @@ static int steam_write_registers(struct steam_device *steam, } va_end(args); - return steam_send_report(steam, cmd, 2 + cmd[1]); + ret = steam_send_report(steam, cmd, 2 + cmd[1]); + if (ret < 0) + return ret; + + /* + * Sometimes a lingering report for this command can + * get read back instead of the last set report if + * this isn't explicitly queried + */ + return steam_recv_report(steam, cmd, 2 + cmd[1]); } static int steam_get_serial(struct steam_device *steam) @@ -270,6 +294,45 @@ static inline int steam_request_conn_status(struct steam_device *steam) return steam_send_report_byte(steam, STEAM_CMD_REQUEST_COMM_STATUS); } +static inline int steam_haptic_rumble(struct steam_device *steam, + u16 intensity, u16 left_speed, u16 right_speed, + u8 left_gain, u8 right_gain) +{ + u8 report[11] = {STEAM_CMD_HAPTIC_RUMBLE, 9}; + + report[3] = intensity & 0xFF; + report[4] = intensity >> 8; + report[5] = left_speed & 0xFF; + report[6] = left_speed >> 8; + report[7] = right_speed & 0xFF; + report[8] = right_speed >> 8; + report[9] = left_gain; + report[10] = right_gain; + + return steam_send_report(steam, report, sizeof(report)); +} + +static void steam_haptic_rumble_cb(struct work_struct *work) +{ + struct steam_device *steam = container_of(work, struct steam_device, + rumble_work); + steam_haptic_rumble(steam, 0, steam->rumble_left, + steam->rumble_right, 2, 0); +} + +#ifdef CONFIG_STEAM_FF +static int steam_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct steam_device *steam = input_get_drvdata(dev); + + steam->rumble_left = effect->u.rumble.strong_magnitude; + steam->rumble_right = effect->u.rumble.weak_magnitude; + + return schedule_work(&steam->rumble_work); +} +#endif + static void steam_set_lizard_mode(struct steam_device *steam, bool enable) { if (enable) { @@ -280,13 +343,33 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable) steam_write_registers(steam, STEAM_REG_RPAD_MARGIN, 0x01, /* enable margin */ 0); + + cancel_delayed_work_sync(&steam->heartbeat); } else { /* disable esc, enter, cursor */ steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS); - steam_write_registers(steam, - STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */ - STEAM_REG_RPAD_MARGIN, 0x00, /* disable margin */ - 0); + + if (steam->quirks & STEAM_QUIRK_DECK) { + steam_write_registers(steam, + STEAM_REG_RPAD_MARGIN, 0x00, /* disable margin */ + STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */ + STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */ + STEAM_REG_LPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */ + STEAM_REG_RPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */ + 0); + /* + * The Steam Deck has a watchdog that automatically enables + * lizard mode if it doesn't see any traffic for too long + */ + if (!work_busy(&steam->heartbeat.work)) + schedule_delayed_work(&steam->heartbeat, 5 * HZ); + } else { + steam_write_registers(steam, + STEAM_REG_RPAD_MARGIN, 0x00, /* disable margin */ + STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */ + STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */ + 0); + } } } @@ -413,8 +496,8 @@ static int steam_input_register(struct steam_device *steam) input->open = steam_input_open; input->close = steam_input_close; - input->name = (steam->quirks & STEAM_QUIRK_WIRELESS) ? - "Wireless Steam Controller" : + input->name = (steam->quirks & STEAM_QUIRK_WIRELESS) ? "Wireless Steam Controller" : + (steam->quirks & STEAM_QUIRK_DECK) ? "Steam Deck" : "Steam Controller"; input->phys = hdev->phys; input->uniq = steam->serial_no; @@ -438,33 +521,76 @@ static int steam_input_register(struct steam_device *steam) input_set_capability(input, EV_KEY, BTN_SELECT); input_set_capability(input, EV_KEY, BTN_MODE); input_set_capability(input, EV_KEY, BTN_START); - input_set_capability(input, EV_KEY, BTN_GEAR_DOWN); - input_set_capability(input, EV_KEY, BTN_GEAR_UP); input_set_capability(input, EV_KEY, BTN_THUMBR); input_set_capability(input, EV_KEY, BTN_THUMBL); input_set_capability(input, EV_KEY, BTN_THUMB); input_set_capability(input, EV_KEY, BTN_THUMB2); + if (steam->quirks & STEAM_QUIRK_DECK) { + input_set_capability(input, EV_KEY, BTN_BASE); + input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY1); + input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY2); + input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY3); + input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY4); + } else { + input_set_capability(input, EV_KEY, BTN_GEAR_DOWN); + input_set_capability(input, EV_KEY, BTN_GEAR_UP); + } - input_set_abs_params(input, ABS_HAT2Y, 0, 255, 0, 0); - input_set_abs_params(input, ABS_HAT2X, 0, 255, 0, 0); input_set_abs_params(input, ABS_X, -32767, 32767, 0, 0); input_set_abs_params(input, ABS_Y, -32767, 32767, 0, 0); - input_set_abs_params(input, ABS_RX, -32767, 32767, - STEAM_PAD_FUZZ, 0); - input_set_abs_params(input, ABS_RY, -32767, 32767, - STEAM_PAD_FUZZ, 0); + input_set_abs_params(input, ABS_HAT0X, -32767, 32767, STEAM_PAD_FUZZ, 0); input_set_abs_params(input, ABS_HAT0Y, -32767, 32767, STEAM_PAD_FUZZ, 0); - input_abs_set_res(input, ABS_X, STEAM_JOYSTICK_RESOLUTION); - input_abs_set_res(input, ABS_Y, STEAM_JOYSTICK_RESOLUTION); - input_abs_set_res(input, ABS_RX, STEAM_PAD_RESOLUTION); - input_abs_set_res(input, ABS_RY, STEAM_PAD_RESOLUTION); + + if (steam->quirks & STEAM_QUIRK_DECK) { + input_set_abs_params(input, ABS_HAT2Y, 0, 32767, 0, 0); + input_set_abs_params(input, ABS_HAT2X, 0, 32767, 0, 0); + + input_set_abs_params(input, ABS_RX, -32767, 32767, 0, 0); + input_set_abs_params(input, ABS_RY, -32767, 32767, 0, 0); + + input_set_abs_params(input, ABS_HAT1X, -32767, 32767, + STEAM_PAD_FUZZ, 0); + input_set_abs_params(input, ABS_HAT1Y, -32767, 32767, + STEAM_PAD_FUZZ, 0); + + input_abs_set_res(input, ABS_X, STEAM_DECK_JOYSTICK_RESOLUTION); + input_abs_set_res(input, ABS_Y, STEAM_DECK_JOYSTICK_RESOLUTION); + input_abs_set_res(input, ABS_RX, STEAM_DECK_JOYSTICK_RESOLUTION); + input_abs_set_res(input, ABS_RY, STEAM_DECK_JOYSTICK_RESOLUTION); + input_abs_set_res(input, ABS_HAT1X, STEAM_PAD_RESOLUTION); + input_abs_set_res(input, ABS_HAT1Y, STEAM_PAD_RESOLUTION); + input_abs_set_res(input, ABS_HAT2Y, STEAM_DECK_TRIGGER_RESOLUTION); + input_abs_set_res(input, ABS_HAT2X, STEAM_DECK_TRIGGER_RESOLUTION); + } else { + input_set_abs_params(input, ABS_HAT2Y, 0, 255, 0, 0); + input_set_abs_params(input, ABS_HAT2X, 0, 255, 0, 0); + + input_set_abs_params(input, ABS_RX, -32767, 32767, + STEAM_PAD_FUZZ, 0); + input_set_abs_params(input, ABS_RY, -32767, 32767, + STEAM_PAD_FUZZ, 0); + + input_abs_set_res(input, ABS_X, STEAM_JOYSTICK_RESOLUTION); + input_abs_set_res(input, ABS_Y, STEAM_JOYSTICK_RESOLUTION); + input_abs_set_res(input, ABS_RX, STEAM_PAD_RESOLUTION); + input_abs_set_res(input, ABS_RY, STEAM_PAD_RESOLUTION); + input_abs_set_res(input, ABS_HAT2Y, STEAM_TRIGGER_RESOLUTION); + input_abs_set_res(input, ABS_HAT2X, STEAM_TRIGGER_RESOLUTION); + } input_abs_set_res(input, ABS_HAT0X, STEAM_PAD_RESOLUTION); input_abs_set_res(input, ABS_HAT0Y, STEAM_PAD_RESOLUTION); - input_abs_set_res(input, ABS_HAT2Y, STEAM_TRIGGER_RESOLUTION); - input_abs_set_res(input, ABS_HAT2X, STEAM_TRIGGER_RESOLUTION); + +#ifdef CONFIG_STEAM_FF + if (steam->quirks & STEAM_QUIRK_DECK) { + input_set_capability(input, EV_FF, FF_RUMBLE); + ret = input_ff_create_memless(input, NULL, steam_play_effect); + if (ret) + goto input_register_fail; + } +#endif ret = input_register_device(input); if (ret) @@ -612,6 +738,22 @@ static bool steam_is_valve_interface(struct hid_device *hdev) return !list_empty(&rep_enum->report_list); } +static void steam_lizard_mode_heartbeat(struct work_struct *work) +{ + struct steam_device *steam = container_of(work, struct steam_device, + heartbeat.work); + + mutex_lock(&steam->mutex); + if (!steam->client_opened && steam->client_hdev) { + steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS); + steam_write_registers(steam, + STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */ + 0); + schedule_delayed_work(&steam->heartbeat, 5 * HZ); + } + mutex_unlock(&steam->mutex); +} + static int steam_client_ll_parse(struct hid_device *hdev) { struct steam_device *steam = hdev->driver_data; @@ -674,7 +816,7 @@ static int steam_client_ll_raw_request(struct hid_device *hdev, report_type, reqtype); } -static struct hid_ll_driver steam_client_ll_driver = { +static const struct hid_ll_driver steam_client_ll_driver = { .parse = steam_client_ll_parse, .start = steam_client_ll_start, .stop = steam_client_ll_stop, @@ -750,6 +892,8 @@ static int steam_probe(struct hid_device *hdev, steam->quirks = id->driver_data; INIT_WORK(&steam->work_connect, steam_work_connect_cb); INIT_LIST_HEAD(&steam->list); + INIT_DEFERRABLE_WORK(&steam->heartbeat, steam_lizard_mode_heartbeat); + INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb); steam->client_hdev = steam_create_client_hid(hdev); if (IS_ERR(steam->client_hdev)) { @@ -805,6 +949,8 @@ hid_hw_start_fail: hid_destroy_device(steam->client_hdev); client_hdev_fail: cancel_work_sync(&steam->work_connect); + cancel_delayed_work_sync(&steam->heartbeat); + cancel_work_sync(&steam->rumble_work); steam_alloc_fail: hid_err(hdev, "%s: failed with error %d\n", __func__, ret); @@ -821,7 +967,11 @@ static void steam_remove(struct hid_device *hdev) } hid_destroy_device(steam->client_hdev); + mutex_lock(&steam->mutex); + steam->client_hdev = NULL; steam->client_opened = false; + cancel_delayed_work_sync(&steam->heartbeat); + mutex_unlock(&steam->mutex); cancel_work_sync(&steam->work_connect); if (steam->quirks & STEAM_QUIRK_WIRELESS) { hid_info(hdev, "Steam wireless receiver disconnected"); @@ -906,10 +1056,10 @@ static inline s16 steam_le16(u8 *data) * 8.5 | BTN_B | button B * 8.6 | BTN_X | button X * 8.7 | BTN_A | button A - * 9.0 | BTN_DPAD_UP | lef-pad up - * 9.1 | BTN_DPAD_RIGHT | lef-pad right - * 9.2 | BTN_DPAD_LEFT | lef-pad left - * 9.3 | BTN_DPAD_DOWN | lef-pad down + * 9.0 | BTN_DPAD_UP | left-pad up + * 9.1 | BTN_DPAD_RIGHT | left-pad right + * 9.2 | BTN_DPAD_LEFT | left-pad left + * 9.3 | BTN_DPAD_DOWN | left-pad down * 9.4 | BTN_SELECT | menu left * 9.5 | BTN_MODE | steam logo * 9.6 | BTN_START | menu right @@ -994,6 +1144,172 @@ static void steam_do_input_event(struct steam_device *steam, } /* + * The size for this message payload is 56. + * The known values are: + * Offset| Type | Mapped to |Meaning + * -------+-------+-----------+-------------------------- + * 4-7 | u32 | -- | sequence number + * 8-15 | u64 | see below | buttons + * 16-17 | s16 | ABS_HAT0X | left-pad X value + * 18-19 | s16 | ABS_HAT0Y | left-pad Y value + * 20-21 | s16 | ABS_HAT1X | right-pad X value + * 22-23 | s16 | ABS_HAT1Y | right-pad Y value + * 24-25 | s16 | -- | accelerometer X value + * 26-27 | s16 | -- | accelerometer Y value + * 28-29 | s16 | -- | accelerometer Z value + * 30-31 | s16 | -- | gyro X value + * 32-33 | s16 | -- | gyro Y value + * 34-35 | s16 | -- | gyro Z value + * 36-37 | s16 | -- | quaternion W value + * 38-39 | s16 | -- | quaternion X value + * 40-41 | s16 | -- | quaternion Y value + * 42-43 | s16 | -- | quaternion Z value + * 44-45 | u16 | ABS_HAT2Y | left trigger (uncalibrated) + * 46-47 | u16 | ABS_HAT2X | right trigger (uncalibrated) + * 48-49 | s16 | ABS_X | left joystick X + * 50-51 | s16 | ABS_Y | left joystick Y + * 52-53 | s16 | ABS_RX | right joystick X + * 54-55 | s16 | ABS_RY | right joystick Y + * 56-57 | u16 | -- | left pad pressure + * 58-59 | u16 | -- | right pad pressure + * + * The buttons are: + * Bit | Mapped to | Description + * ------+------------+-------------------------------- + * 8.0 | BTN_TR2 | right trigger fully pressed + * 8.1 | BTN_TL2 | left trigger fully pressed + * 8.2 | BTN_TR | right shoulder + * 8.3 | BTN_TL | left shoulder + * 8.4 | BTN_Y | button Y + * 8.5 | BTN_B | button B + * 8.6 | BTN_X | button X + * 8.7 | BTN_A | button A + * 9.0 | BTN_DPAD_UP | left-pad up + * 9.1 | BTN_DPAD_RIGHT | left-pad right + * 9.2 | BTN_DPAD_LEFT | left-pad left + * 9.3 | BTN_DPAD_DOWN | left-pad down + * 9.4 | BTN_SELECT | menu left + * 9.5 | BTN_MODE | steam logo + * 9.6 | BTN_START | menu right + * 9.7 | BTN_TRIGGER_HAPPY3 | left bottom grip button + * 10.0 | BTN_TRIGGER_HAPPY4 | right bottom grip button + * 10.1 | BTN_THUMB | left pad pressed + * 10.2 | BTN_THUMB2 | right pad pressed + * 10.3 | -- | left pad touched + * 10.4 | -- | right pad touched + * 10.5 | -- | unknown + * 10.6 | BTN_THUMBL | left joystick clicked + * 10.7 | -- | unknown + * 11.0 | -- | unknown + * 11.1 | -- | unknown + * 11.2 | BTN_THUMBR | right joystick clicked + * 11.3 | -- | unknown + * 11.4 | -- | unknown + * 11.5 | -- | unknown + * 11.6 | -- | unknown + * 11.7 | -- | unknown + * 12.0 | -- | unknown + * 12.1 | -- | unknown + * 12.2 | -- | unknown + * 12.3 | -- | unknown + * 12.4 | -- | unknown + * 12.5 | -- | unknown + * 12.6 | -- | unknown + * 12.7 | -- | unknown + * 13.0 | -- | unknown + * 13.1 | BTN_TRIGGER_HAPPY1 | left top grip button + * 13.2 | BTN_TRIGGER_HAPPY2 | right top grip button + * 13.3 | -- | unknown + * 13.4 | -- | unknown + * 13.5 | -- | unknown + * 13.6 | -- | left joystick touched + * 13.7 | -- | right joystick touched + * 14.0 | -- | unknown + * 14.1 | -- | unknown + * 14.2 | BTN_BASE | quick access button + * 14.3 | -- | unknown + * 14.4 | -- | unknown + * 14.5 | -- | unknown + * 14.6 | -- | unknown + * 14.7 | -- | unknown + * 15.0 | -- | unknown + * 15.1 | -- | unknown + * 15.2 | -- | unknown + * 15.3 | -- | unknown + * 15.4 | -- | unknown + * 15.5 | -- | unknown + * 15.6 | -- | unknown + * 15.7 | -- | unknown + */ +static void steam_do_deck_input_event(struct steam_device *steam, + struct input_dev *input, u8 *data) +{ + u8 b8, b9, b10, b11, b13, b14; + bool lpad_touched, rpad_touched; + + b8 = data[8]; + b9 = data[9]; + b10 = data[10]; + b11 = data[11]; + b13 = data[13]; + b14 = data[14]; + + lpad_touched = b10 & BIT(3); + rpad_touched = b10 & BIT(4); + + if (lpad_touched) { + input_report_abs(input, ABS_HAT0X, steam_le16(data + 16)); + input_report_abs(input, ABS_HAT0Y, steam_le16(data + 18)); + } else { + input_report_abs(input, ABS_HAT0X, 0); + input_report_abs(input, ABS_HAT0Y, 0); + } + + if (rpad_touched) { + input_report_abs(input, ABS_HAT1X, steam_le16(data + 20)); + input_report_abs(input, ABS_HAT1Y, steam_le16(data + 22)); + } else { + input_report_abs(input, ABS_HAT1X, 0); + input_report_abs(input, ABS_HAT1Y, 0); + } + + input_report_abs(input, ABS_X, steam_le16(data + 48)); + input_report_abs(input, ABS_Y, -steam_le16(data + 50)); + input_report_abs(input, ABS_RX, steam_le16(data + 52)); + input_report_abs(input, ABS_RY, -steam_le16(data + 54)); + + input_report_abs(input, ABS_HAT2Y, steam_le16(data + 44)); + input_report_abs(input, ABS_HAT2X, steam_le16(data + 46)); + + input_event(input, EV_KEY, BTN_TR2, !!(b8 & BIT(0))); + input_event(input, EV_KEY, BTN_TL2, !!(b8 & BIT(1))); + input_event(input, EV_KEY, BTN_TR, !!(b8 & BIT(2))); + input_event(input, EV_KEY, BTN_TL, !!(b8 & BIT(3))); + input_event(input, EV_KEY, BTN_Y, !!(b8 & BIT(4))); + input_event(input, EV_KEY, BTN_B, !!(b8 & BIT(5))); + input_event(input, EV_KEY, BTN_X, !!(b8 & BIT(6))); + input_event(input, EV_KEY, BTN_A, !!(b8 & BIT(7))); + input_event(input, EV_KEY, BTN_SELECT, !!(b9 & BIT(4))); + input_event(input, EV_KEY, BTN_MODE, !!(b9 & BIT(5))); + input_event(input, EV_KEY, BTN_START, !!(b9 & BIT(6))); + input_event(input, EV_KEY, BTN_TRIGGER_HAPPY3, !!(b9 & BIT(7))); + input_event(input, EV_KEY, BTN_TRIGGER_HAPPY4, !!(b10 & BIT(0))); + input_event(input, EV_KEY, BTN_THUMBL, !!(b10 & BIT(6))); + input_event(input, EV_KEY, BTN_THUMBR, !!(b11 & BIT(2))); + input_event(input, EV_KEY, BTN_DPAD_UP, !!(b9 & BIT(0))); + input_event(input, EV_KEY, BTN_DPAD_RIGHT, !!(b9 & BIT(1))); + input_event(input, EV_KEY, BTN_DPAD_LEFT, !!(b9 & BIT(2))); + input_event(input, EV_KEY, BTN_DPAD_DOWN, !!(b9 & BIT(3))); + input_event(input, EV_KEY, BTN_THUMB, !!(b10 & BIT(1))); + input_event(input, EV_KEY, BTN_THUMB2, !!(b10 & BIT(2))); + input_event(input, EV_KEY, BTN_TRIGGER_HAPPY1, !!(b13 & BIT(1))); + input_event(input, EV_KEY, BTN_TRIGGER_HAPPY2, !!(b13 & BIT(2))); + input_event(input, EV_KEY, BTN_BASE, !!(b14 & BIT(2))); + + input_sync(input); +} + +/* * The size for this message payload is 11. * The known values are: * Offset| Type | Meaning @@ -1052,6 +1368,7 @@ static int steam_raw_event(struct hid_device *hdev, * 0x01: input data (60 bytes) * 0x03: wireless connect/disconnect (1 byte) * 0x04: battery status (11 bytes) + * 0x09: Steam Deck input data (56 bytes) */ if (size != 64 || data[0] != 1 || data[1] != 0) @@ -1067,6 +1384,15 @@ static int steam_raw_event(struct hid_device *hdev, steam_do_input_event(steam, input, data); rcu_read_unlock(); break; + case STEAM_EV_DECK_INPUT_DATA: + if (steam->client_opened) + return 0; + rcu_read_lock(); + input = rcu_dereference(steam->input); + if (likely(input)) + steam_do_deck_input_event(steam, input, data); + rcu_read_unlock(); + break; case STEAM_EV_CONNECT: /* * The payload of this event is a single byte: @@ -1141,6 +1467,11 @@ static const struct hid_device_id steam_controllers[] = { USB_DEVICE_ID_STEAM_CONTROLLER_WIRELESS), .driver_data = STEAM_QUIRK_WIRELESS }, + { /* Steam Deck */ + HID_USB_DEVICE(USB_VENDOR_ID_VALVE, + USB_DEVICE_ID_STEAM_DECK), + .driver_data = STEAM_QUIRK_DECK + }, {} }; diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index a31e06b38a67..f67835f9ed4c 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -539,6 +539,8 @@ static const struct hid_device_id uclogic_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW), diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index 7fdb87a2714b..9859dad36495 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -1764,6 +1764,8 @@ int uclogic_params_init(struct uclogic_params *params, case VID_PID(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_PARBLO_A610_PRO): case VID_PID(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2): + case VID_PID(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L): case VID_PID(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW): diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 09db8111dc26..26167cfb696f 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -1771,7 +1771,7 @@ static void wiimote_destroy(struct wiimote_data *wdata) spin_unlock_irqrestore(&wdata->state.lock, flags); cancel_work_sync(&wdata->init_worker); - del_timer_sync(&wdata->timer); + timer_shutdown_sync(&wdata->timer); device_remove_file(&wdata->hdev->dev, &dev_attr_devtype); device_remove_file(&wdata->hdev->dev, &dev_attr_extension); diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi.c b/drivers/hid/i2c-hid/i2c-hid-acpi.c index 375c77c3db74..3a7e2bcb5412 100644 --- a/drivers/hid/i2c-hid/i2c-hid-acpi.c +++ b/drivers/hid/i2c-hid/i2c-hid-acpi.c @@ -39,8 +39,8 @@ static const struct acpi_device_id i2c_hid_acpi_blacklist[] = { * The CHPN0001 ACPI device, which is used to describe the Chipone * ICN8505 controller, has a _CID of PNP0C50 but is not HID compatible. */ - {"CHPN0001", 0 }, - { }, + { "CHPN0001" }, + { } }; /* HID I²C Device: 3cdff6f7-4267-4555-ad05-b30a3d8938de */ @@ -48,8 +48,9 @@ static guid_t i2c_hid_guid = GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555, 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE); -static int i2c_hid_acpi_get_descriptor(struct acpi_device *adev) +static int i2c_hid_acpi_get_descriptor(struct i2c_hid_acpi *ihid_acpi) { + struct acpi_device *adev = ihid_acpi->adev; acpi_handle handle = acpi_device_handle(adev); union acpi_object *obj; u16 hid_descriptor_address; @@ -81,38 +82,31 @@ static int i2c_hid_acpi_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct i2c_hid_acpi *ihid_acpi; - struct acpi_device *adev; u16 hid_descriptor_address; int ret; - adev = ACPI_COMPANION(dev); - if (!adev) { - dev_err(&client->dev, "Error could not get ACPI device\n"); - return -ENODEV; - } - ihid_acpi = devm_kzalloc(&client->dev, sizeof(*ihid_acpi), GFP_KERNEL); if (!ihid_acpi) return -ENOMEM; - ihid_acpi->adev = adev; + ihid_acpi->adev = ACPI_COMPANION(dev); ihid_acpi->ops.shutdown_tail = i2c_hid_acpi_shutdown_tail; - ret = i2c_hid_acpi_get_descriptor(adev); + ret = i2c_hid_acpi_get_descriptor(ihid_acpi); if (ret < 0) return ret; hid_descriptor_address = ret; - acpi_device_fix_up_power(adev); + acpi_device_fix_up_power(ihid_acpi->adev); return i2c_hid_core_probe(client, &ihid_acpi->ops, hid_descriptor_address, 0); } static const struct acpi_device_id i2c_hid_acpi_match[] = { - {"ACPI0C50", 0 }, - {"PNP0C50", 0 }, - { }, + { "ACPI0C50" }, + { "PNP0C50" }, + { } }; MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match); diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index b86b62f97108..efbba0465eef 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -67,16 +67,7 @@ #define I2C_HID_PWR_ON 0x00 #define I2C_HID_PWR_SLEEP 0x01 -/* debug option */ -static bool debug; -module_param(debug, bool, 0444); -MODULE_PARM_DESC(debug, "print a lot of debug information"); - -#define i2c_hid_dbg(ihid, fmt, arg...) \ -do { \ - if (debug) \ - dev_printk(KERN_DEBUG, &(ihid)->client->dev, fmt, ##arg); \ -} while (0) +#define i2c_hid_dbg(ihid, ...) dev_dbg(&(ihid)->client->dev, __VA_ARGS__) struct i2c_hid_desc { __le16 wHIDDescLength; @@ -842,7 +833,7 @@ static void i2c_hid_close(struct hid_device *hid) clear_bit(I2C_HID_STARTED, &ihid->flags); } -struct hid_ll_driver i2c_hid_ll_driver = { +static const struct hid_ll_driver i2c_hid_ll_driver = { .parse = i2c_hid_parse, .start = i2c_hid_start, .stop = i2c_hid_stop, @@ -851,7 +842,6 @@ struct hid_ll_driver i2c_hid_ll_driver = { .output_report = i2c_hid_output_report, .raw_request = i2c_hid_raw_request, }; -EXPORT_SYMBOL_GPL(i2c_hid_ll_driver); static int i2c_hid_init_irq(struct i2c_client *client) { @@ -859,7 +849,7 @@ static int i2c_hid_init_irq(struct i2c_client *client) unsigned long irqflags = 0; int ret; - dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq); + i2c_hid_dbg(ihid, "Requesting IRQ: %d\n", client->irq); if (!irq_get_trigger_type(client->irq)) irqflags = IRQF_TRIGGER_LOW; @@ -1003,7 +993,7 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, /* Make sure there is something at this address */ ret = i2c_smbus_read_byte(client); if (ret < 0) { - dev_dbg(&client->dev, "nothing at this address: %d\n", ret); + i2c_hid_dbg(ihid, "nothing at this address: %d\n", ret); ret = -ENXIO; goto err_powered; } @@ -1035,6 +1025,10 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID); hid->product = le16_to_cpu(ihid->hdesc.wProductID); + hid->initial_quirks = quirks; + hid->initial_quirks |= i2c_hid_get_dmi_quirks(hid->vendor, + hid->product); + snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", client->name, (u16)hid->vendor, (u16)hid->product); strscpy(hid->phys, dev_name(&client->dev), sizeof(hid->phys)); @@ -1048,8 +1042,6 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, goto err_mem_free; } - hid->quirks |= quirks; - return 0; err_mem_free: diff --git a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c index 8e0f67455c09..210f17c3a0be 100644 --- a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c +++ b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c @@ -10,8 +10,10 @@ #include <linux/types.h> #include <linux/dmi.h> #include <linux/mod_devicetable.h> +#include <linux/hid.h> #include "i2c-hid.h" +#include "../hid-ids.h" struct i2c_hid_desc_override { @@ -416,6 +418,28 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = { { } /* Terminate list */ }; +static const struct hid_device_id i2c_hid_elan_flipped_quirks = { + HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, USB_VENDOR_ID_ELAN, 0x2dcd), + HID_QUIRK_X_INVERT | HID_QUIRK_Y_INVERT +}; + +/* + * This list contains devices which have specific issues based on the system + * they're on and not just the device itself. The driver_data will have a + * specific hid device to match against. + */ +static const struct dmi_system_id i2c_hid_dmi_quirk_table[] = { + { + .ident = "DynaBook K50/FR", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dynabook Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "dynabook K50/FR"), + }, + .driver_data = (void *)&i2c_hid_elan_flipped_quirks, + }, + { } /* Terminate list */ +}; + struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name) { @@ -450,3 +474,21 @@ char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name, *size = override->hid_report_desc_size; return override->hid_report_desc; } + +u32 i2c_hid_get_dmi_quirks(const u16 vendor, const u16 product) +{ + u32 quirks = 0; + const struct dmi_system_id *system_id = + dmi_first_match(i2c_hid_dmi_quirk_table); + + if (system_id) { + const struct hid_device_id *device_id = + (struct hid_device_id *)(system_id->driver_data); + + if (device_id && device_id->vendor == vendor && + device_id->product == product) + quirks = device_id->driver_data; + } + + return quirks; +} diff --git a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c index 29c6cb174032..0060e3dcd775 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c +++ b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c @@ -26,28 +26,33 @@ struct i2c_hid_of_goodix { struct i2chid_ops ops; struct regulator *vdd; - struct notifier_block nb; + struct regulator *vddio; struct gpio_desc *reset_gpio; const struct goodix_i2c_hid_timing_data *timings; }; -static void goodix_i2c_hid_deassert_reset(struct i2c_hid_of_goodix *ihid_goodix, - bool regulator_just_turned_on) +static int goodix_i2c_hid_power_up(struct i2chid_ops *ops) { - if (regulator_just_turned_on && ihid_goodix->timings->post_power_delay_ms) + struct i2c_hid_of_goodix *ihid_goodix = + container_of(ops, struct i2c_hid_of_goodix, ops); + int ret; + + ret = regulator_enable(ihid_goodix->vdd); + if (ret) + return ret; + + ret = regulator_enable(ihid_goodix->vddio); + if (ret) + return ret; + + if (ihid_goodix->timings->post_power_delay_ms) msleep(ihid_goodix->timings->post_power_delay_ms); gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 0); if (ihid_goodix->timings->post_gpio_reset_delay_ms) msleep(ihid_goodix->timings->post_gpio_reset_delay_ms); -} - -static int goodix_i2c_hid_power_up(struct i2chid_ops *ops) -{ - struct i2c_hid_of_goodix *ihid_goodix = - container_of(ops, struct i2c_hid_of_goodix, ops); - return regulator_enable(ihid_goodix->vdd); + return 0; } static void goodix_i2c_hid_power_down(struct i2chid_ops *ops) @@ -55,42 +60,15 @@ static void goodix_i2c_hid_power_down(struct i2chid_ops *ops) struct i2c_hid_of_goodix *ihid_goodix = container_of(ops, struct i2c_hid_of_goodix, ops); + gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1); + regulator_disable(ihid_goodix->vddio); regulator_disable(ihid_goodix->vdd); } -static int ihid_goodix_vdd_notify(struct notifier_block *nb, - unsigned long event, - void *ignored) -{ - struct i2c_hid_of_goodix *ihid_goodix = - container_of(nb, struct i2c_hid_of_goodix, nb); - int ret = NOTIFY_OK; - - switch (event) { - case REGULATOR_EVENT_PRE_DISABLE: - gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1); - break; - - case REGULATOR_EVENT_ENABLE: - goodix_i2c_hid_deassert_reset(ihid_goodix, true); - break; - - case REGULATOR_EVENT_ABORT_DISABLE: - goodix_i2c_hid_deassert_reset(ihid_goodix, false); - break; - - default: - ret = NOTIFY_DONE; - break; - } - - return ret; -} - static int i2c_hid_of_goodix_probe(struct i2c_client *client) { struct i2c_hid_of_goodix *ihid_goodix; - int ret; + ihid_goodix = devm_kzalloc(&client->dev, sizeof(*ihid_goodix), GFP_KERNEL); if (!ihid_goodix) @@ -109,41 +87,11 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client) if (IS_ERR(ihid_goodix->vdd)) return PTR_ERR(ihid_goodix->vdd); - ihid_goodix->timings = device_get_match_data(&client->dev); + ihid_goodix->vddio = devm_regulator_get(&client->dev, "mainboard-vddio"); + if (IS_ERR(ihid_goodix->vddio)) + return PTR_ERR(ihid_goodix->vddio); - /* - * We need to control the "reset" line in lockstep with the regulator - * actually turning on an off instead of just when we make the request. - * This matters if the regulator is shared with another consumer. - * - If the regulator is off then we must assert reset. The reset - * line is active low and on some boards it could cause a current - * leak if left high. - * - If the regulator is on then we don't want reset asserted for very - * long. Holding the controller in reset apparently draws extra - * power. - */ - ihid_goodix->nb.notifier_call = ihid_goodix_vdd_notify; - ret = devm_regulator_register_notifier(ihid_goodix->vdd, &ihid_goodix->nb); - if (ret) - return dev_err_probe(&client->dev, ret, - "regulator notifier request failed\n"); - - /* - * If someone else is holding the regulator on (or the regulator is - * an always-on one) we might never be told to deassert reset. Do it - * now... and temporarily bump the regulator reference count just to - * make sure it is impossible for this to race with our own notifier! - * We also assume that someone else might have _just barely_ turned - * the regulator on so we'll do the full "post_power_delay" just in - * case. - */ - if (ihid_goodix->reset_gpio && regulator_is_enabled(ihid_goodix->vdd)) { - ret = regulator_enable(ihid_goodix->vdd); - if (ret) - return ret; - goodix_i2c_hid_deassert_reset(ihid_goodix, true); - regulator_disable(ihid_goodix->vdd); - } + ihid_goodix->timings = device_get_match_data(&client->dev); return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001, 0); } diff --git a/drivers/hid/i2c-hid/i2c-hid.h b/drivers/hid/i2c-hid/i2c-hid.h index 96c75510ad3f..2c7b66d5caa0 100644 --- a/drivers/hid/i2c-hid/i2c-hid.h +++ b/drivers/hid/i2c-hid/i2c-hid.h @@ -9,6 +9,7 @@ struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name); char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name, unsigned int *size); +u32 i2c_hid_get_dmi_quirks(const u16 vendor, const u16 product); #else static inline struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name) @@ -16,6 +17,8 @@ static inline struct i2c_hid_desc static inline char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name, unsigned int *size) { return NULL; } +static inline u32 i2c_hid_get_dmi_quirks(const u16 vendor, const u16 product) +{ return 0; } #endif /** diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.c b/drivers/hid/intel-ish-hid/ishtp-hid.c index 14c271d7d8a9..00c6f0ebf356 100644 --- a/drivers/hid/intel-ish-hid/ishtp-hid.c +++ b/drivers/hid/intel-ish-hid/ishtp-hid.c @@ -183,7 +183,7 @@ void ishtp_hid_wakeup(struct hid_device *hid) wake_up_interruptible(&hid_data->hid_wait); } -static struct hid_ll_driver ishtp_hid_ll_driver = { +static const struct hid_ll_driver ishtp_hid_ll_driver = { .parse = ishtp_hid_parse, .start = ishtp_hid_start, .stop = ishtp_hid_stop, diff --git a/drivers/hid/intel-ish-hid/ishtp/dma-if.c b/drivers/hid/intel-ish-hid/ishtp/dma-if.c index 40554c8daca0..00046cbfd4ed 100644 --- a/drivers/hid/intel-ish-hid/ishtp/dma-if.c +++ b/drivers/hid/intel-ish-hid/ishtp/dma-if.c @@ -104,6 +104,11 @@ void *ishtp_cl_get_dma_send_buf(struct ishtp_device *dev, int required_slots = (size / DMA_SLOT_SIZE) + 1 * (size % DMA_SLOT_SIZE != 0); + if (!dev->ishtp_dma_tx_map) { + dev_err(dev->devc, "Fail to allocate Tx map\n"); + return NULL; + } + spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags); for (i = 0; i <= (dev->ishtp_dma_num_slots - required_slots); i++) { free = 1; @@ -150,6 +155,11 @@ void ishtp_cl_release_dma_acked_mem(struct ishtp_device *dev, return; } + if (!dev->ishtp_dma_tx_map) { + dev_err(dev->devc, "Fail to allocate Tx map\n"); + return; + } + i = (msg_addr - dev->ishtp_host_dma_tx_buf) / DMA_SLOT_SIZE; spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags); for (j = 0; j < acked_slots; j++) { diff --git a/drivers/hid/surface-hid/surface_hid_core.c b/drivers/hid/surface-hid/surface_hid_core.c index 87637f813de2..a3e9cceddfac 100644 --- a/drivers/hid/surface-hid/surface_hid_core.c +++ b/drivers/hid/surface-hid/surface_hid_core.c @@ -174,7 +174,7 @@ static int surface_hid_raw_request(struct hid_device *hid, unsigned char reportn return -EIO; } -static struct hid_ll_driver surface_hid_ll_driver = { +static const struct hid_ll_driver surface_hid_ll_driver = { .start = surface_hid_start, .stop = surface_hid_stop, .open = surface_hid_open, diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 2a918aeb0af1..f161c95a1ad2 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -387,7 +387,7 @@ static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf, return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT); } -struct hid_ll_driver uhid_hid_driver = { +static const struct hid_ll_driver uhid_hid_driver = { .start = uhid_hid_start, .stop = uhid_hid_stop, .open = uhid_hid_open, @@ -396,7 +396,6 @@ struct hid_ll_driver uhid_hid_driver = { .raw_request = uhid_hid_raw_request, .output_report = uhid_hid_output_report, }; -EXPORT_SYMBOL_GPL(uhid_hid_driver); #ifdef CONFIG_COMPAT diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index be4c731aaa65..257dd73e37bf 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1318,7 +1318,7 @@ static bool usbhid_may_wakeup(struct hid_device *hid) return device_may_wakeup(&dev->dev); } -struct hid_ll_driver usb_hid_driver = { +static const struct hid_ll_driver usb_hid_driver = { .parse = usbhid_parse, .start = usbhid_start, .stop = usbhid_stop, @@ -1332,7 +1332,12 @@ struct hid_ll_driver usb_hid_driver = { .idle = usbhid_idle, .may_wakeup = usbhid_may_wakeup, }; -EXPORT_SYMBOL_GPL(usb_hid_driver); + +bool hid_is_usb(const struct hid_device *hdev) +{ + return hdev->ll_driver == &usb_hid_driver; +} +EXPORT_SYMBOL_GPL(hid_is_usb); static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id) { |