mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
Merge tag 'hid-for-linus-2026041601' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
Pull HID updates from Jiri Kosina:
"Core:
- fixed handling of 0-sized reports (Dmitry Torokhov)
- convert core code to __free() (Dmitry Torokhov)
- support for multiple batteries per HID device (Lucas Zampieri)
Drivers:
- support for rumble effects in winwing driver (Ivan Gorinov)
- new support for a variety of Sony Rock Band and Sony DJ Hero
Turntable devices (Rosalie Wanders)
- new driver for Lenovo Legion Go / S devices (Derek J. Clark)
- power management improvements to intel-thc-hid driver (Even Xu)
... other assorted cleanups, fixes and device-specific quirks"
* tag 'hid-for-linus-2026041601' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (73 commits)
HID: core: clamp report_size in s32ton() to avoid undefined shift
HID: logitech-dj: fix wrong detection of bad DJ_SHORT output report
HID: logitech-hidpp: fix race condition when accessing stale stack pointer
HID: winwing: Enable rumble effects
HID: core: do not allow parsing 0-sized reports
HID: usbhid: refactor endpoint lookup
HID: huawei: fix CD30 keyboard report descriptor issue
HID: playstation: validate num_touch_reports in DualShock 4 reports
HID: drop 'default !EXPERT' from tristate symbols
HID: usbhid: fix deadlock in hid_post_reset()
HID: apple: ensure the keyboard backlight is off if suspending
HID: quirks: Set ALWAYS_POLL for LOGITECH_BOLT_RECEIVER
HID: alps: fix NULL pointer dereference in alps_raw_event()
HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write
HID: logitech-dj: Standardise hid_report_enum variable nomenclature
HID: sony: update module description
HID: logitech-hidpp: Check bounds when deleting force-feedback effects
HID: sony: add battery status support for Rock Band 4 PS5 guitars
HID: sony: fix style issues
HID: quirks: update hid-sony supported devices
...
This commit is contained in:
724
Documentation/ABI/testing/sysfs-driver-hid-lenovo-go
Normal file
724
Documentation/ABI/testing/sysfs-driver-hid-lenovo-go
Normal file
@@ -0,0 +1,724 @@
|
||||
What: /sys/class/leds/go:rgb:joystick_rings/effect
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the display effect of the RGB interface.
|
||||
|
||||
Values are monocolor, breathe, chroma, or rainbow.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go:rgb:joystick_rings/effect_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the effect attribute.
|
||||
|
||||
Values are monocolor, breathe, chroma, or rainbow.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go:rgb:joystick_rings/enabled
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls enabling or disabling the RGB interface.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go:rgb:joystick_rings/enabled_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the enabled attribute.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go:rgb:joystick_rings/mode
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the operating mode of the RGB interface.
|
||||
|
||||
Values are dynamic or custom. Custom allows setting the RGB effect and color.
|
||||
Dynamic is a Windows mode for syncing Lenovo RGB interfaces not currently
|
||||
supported under Linux.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go:rgb:joystick_rings/mode_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the mode attribute.
|
||||
|
||||
Values are dynamic or custom.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go:rgb:joystick_rings/profile
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls selecting the configured RGB profile.
|
||||
|
||||
Values are 1-3.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go:rgb:joystick_rings/profile_range
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the profile attribute.
|
||||
|
||||
Values are 1-3.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go:rgb:joystick_rings/speed
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the change rate for the breathe, chroma, and rainbow effects.
|
||||
|
||||
Values are 0-100.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go:rgb:joystick_rings/speed_range
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the speed attribute.
|
||||
|
||||
Values are 0-100.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/firmware_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the firmware version of the internal MCU.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/fps_mode_dpi
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the DPI of the right handle when the FPS mode switch is on.
|
||||
|
||||
Values are 500, 800, 1200, and 1800.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/fps_mode_dpi_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the fps_mode_dpi attribute.
|
||||
|
||||
Values are 500, 800, 1200, and 1800.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/hardware_generation
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the hardware generation of the internal MCU.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/hardware_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the hardware version of the internal MCU.
|
||||
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/auto_sleep_time
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the sleep timer due to inactivity for the left removable controller.
|
||||
|
||||
Values are 0-255.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/auto_sleep_time_range
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the left_handle/auto_sleep_time attribute.
|
||||
|
||||
Values are 0-255.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_gyro
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This initiates or halts calibration of the left removable controller's IMU.
|
||||
|
||||
Values are start, stop.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_gyro_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the left_handle/calibrate_gyro attribute.
|
||||
|
||||
Values are start, stop.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_gyro_status
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the result of the last attempted calibration of the left removable controller's IMU.
|
||||
|
||||
Values are unknown, success, failure.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_joystick
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This initiates or halts calibration of the left removable controller's joystick.
|
||||
|
||||
Values are start, stop.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_joystick_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the left_handle/calibrate_jotstick attribute.
|
||||
|
||||
Values are start, stop.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_joystick_status
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the result of the last attempted calibration of the left removable controller's joystick.
|
||||
|
||||
Values are unknown, success, failure.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_tirgger
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This initiates or halts calibration of the left removable controller's trigger.
|
||||
|
||||
Values are start, stop.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_gyro_trigger
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the left_handle/calibrate_trigger attribute.
|
||||
|
||||
Values are start, stop.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_trigger_status
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the result of the last attempted calibration of the left removable controller's trigger.
|
||||
|
||||
Values are unknown, success, failure.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/firmware_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the left removable controller's firmware version.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/hardware_generation
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the hardware generation of the left removable controller.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/hardware_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the hardware version of the left removable controller.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/imu_bypass_enabled
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls enabling or disabling the IMU bypass function of the left removable controller.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/imu_bypass_enabled_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the left_handle/imu_bypass_enabled attribute.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/imu_enabled
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls enabling or disabling the IMU of the left removable controller.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/imu_enabled_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the left_handle/imu_enabled attribute.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/product_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the product version of the left removable controller.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/protocol_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the protocol version of the left removable controller.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/reset
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: Resets the left removable controller to factory defaults.
|
||||
|
||||
Writing 1 to this path initiates.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/rumble_mode
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls setting the response behavior for rumble events for the left removable controller.
|
||||
|
||||
Values are fps, racing, standarg, spg, rpg.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/rumble_mode_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the left_handle/rumble_mode attribute.
|
||||
|
||||
Values are fps, racing, standarg, spg, rpg.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/rumble_notification
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls enabling haptic rumble events for the left removable controller.
|
||||
|
||||
Values are true, false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/rumble_notification_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the left_handle/rumble_notification attribute.
|
||||
|
||||
Values are true, false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/mode
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the operating mode of the built-in controller.
|
||||
|
||||
Values are xinput or dinput.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/mode_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the mode attribute.
|
||||
|
||||
Values are xinput or dinput.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/os_mode
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the behavior of built in chord combinations.
|
||||
|
||||
Values are windows or linux.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/os_mode_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the os_mode attribute.
|
||||
|
||||
Values are windows or linux.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/product_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the product version of the internal MCU.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/protocol_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the protocol version of the internal MCU.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/reset_mcu
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: Resets the internal MCU to factory defaults.
|
||||
|
||||
Writing 1 to this path initiates.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/auto_sleep_time
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the sleep timer due to inactivity for the right removable controller.
|
||||
|
||||
Values are 0-255.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/auto_sleep_time_range
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the right_handle/auto_sleep_time attribute.
|
||||
|
||||
Values are 0-255.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_gyro
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This initiates or halts calibration of the right removable controller's IMU.
|
||||
|
||||
Values are start, stop.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_gyro_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the right_handle/calibrate_gyro attribute.
|
||||
|
||||
Values are start, stop.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_gyro_status
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the result of the last attempted calibration of the right removable controller's IMU.
|
||||
|
||||
Values are unknown, success, failure.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_joystick
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This initiates or halts calibration of the right removable controller's joystick.
|
||||
|
||||
Values are start, stop.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_joystick_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the right_handle/calibrate_jotstick attribute.
|
||||
|
||||
Values are start, stop.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_joystick_status
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the result of the last attempted calibration of the right removable controller's joystick.
|
||||
|
||||
Values are unknown, success, failure.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_tirgger
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This initiates or halts calibration of the right removable controller's trigger.
|
||||
|
||||
Values are start, stop.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_gyro_trigger
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the right_handle/calibrate_trigger attribute.
|
||||
|
||||
Values are start, stop.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_trigger_status
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the result of the last attempted calibration of the right removable controller's trigger.
|
||||
|
||||
Values are unknown, success, failure.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/firmware_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the right removable controller's firmware version.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/hardware_generation
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the hardware generation of the right removable controller.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/hardware_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the hardware version of the right removable controller.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/imu_bypass_enabled
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls enabling or disabling the IMU bypass function of the right removable controller.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/imu_bypass_enabled_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the right_handle/imu_bypass_enabled attribute.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/imu_enabled
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls enabling or disabling the IMU of the right removable controller.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/imu_enabled_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the right_handle/imu_enabled attribute.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/product_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the product version of the right removable controller.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/protocol_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the protocol version of the right removable controller.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/reset
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: Resets the right removable controller to factory defaults.
|
||||
|
||||
Writing 1 to this path initiates.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/rumble_mode
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls setting the response behavior for rumble events for the right removable controller.
|
||||
|
||||
Values are fps, racing, standarg, spg, rpg.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/rumble_mode_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the right_handle/rumble_mode attribute.
|
||||
|
||||
Values are fps, racing, standarg, spg, rpg.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/rumble_notification
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls enabling haptic rumble events for the right removable controller.
|
||||
|
||||
Values are true, false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/rumble_notification_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the right_handle/rumble_notification attribute.
|
||||
|
||||
Values are true, false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/rumble_intensity
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls setting the rumble intensity for both removable controllers.
|
||||
|
||||
Values are off, low, medium, high.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/rumble_intensity_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the rumble_intensity attribute.
|
||||
|
||||
Values are off, low, medium, high.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/enabled
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls enabling or disabling the touchpad.
|
||||
|
||||
Values are true, false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/enabled_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the touchpad/enabled attribute.
|
||||
|
||||
Values are true, false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/vibration_enabled
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls enabling haptic rumble events for the touchpad.
|
||||
|
||||
Values are true, false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/vibration_enabled_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the touchpad/vibration_enabled attribute.
|
||||
|
||||
Values are true, false.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/vibration_intensity
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls setting the intensity of the touchpad haptics.
|
||||
|
||||
Values are off, low, medium, high.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/vibration_intensity_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the touchpad/vibration_intensity attribute.
|
||||
|
||||
Values are off, low, medium, high.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/tx_dongle/firmware_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the firmware version of the internal wireless transmission dongle.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/tx_dongle/hardware_generation
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the hardware generation of the internal wireless transmission dongle.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/tx_dongle/hardware_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the hardware version of the internal wireless transmission dongle.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/tx_dongle/product_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the product version of the internal wireless transmission dongle.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/tx_dongle/protocol_version
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the protocol version of the internal wireless transmission dongle.
|
||||
|
||||
Applies to Lenovo Legion Go and Go 2 line of handheld devices.
|
||||
|
||||
304
Documentation/ABI/testing/sysfs-driver-hid-lenovo-go-s
Normal file
304
Documentation/ABI/testing/sysfs-driver-hid-lenovo-go-s
Normal file
@@ -0,0 +1,304 @@
|
||||
What: /sys/class/leds/go_s:rgb:joystick_rings/effect
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the display effect of the RGB interface.
|
||||
|
||||
Values are monocolor, breathe, chroma, or rainbow.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go_s:rgb:joystick_rings/effect_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the effect attribute.
|
||||
|
||||
Values are monocolor, breathe, chroma, or rainbow.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go_s:rgb:joystick_rings/enabled
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls enabling or disabling the RGB interface.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go_s:rgb:joystick_rings/enabled_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the enabled attribute.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go_s:rgb:joystick_rings/mode
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the operating mode of the RGB interface.
|
||||
|
||||
Values are dynamic or custom. Custom allows setting the RGB effect and color.
|
||||
Dynamic is a Windows mode for syncing Lenovo RGB interfaces not currently
|
||||
supported under Linux.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go_s:rgb:joystick_rings/mode_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the mode attribute.
|
||||
|
||||
Values are dynamic or custom.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go_s:rgb:joystick_rings/profile
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls selecting the configured RGB profile.
|
||||
|
||||
Values are 1-3.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go_s:rgb:joystick_rings/profile_range
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the profile attribute.
|
||||
|
||||
Values are 1-3.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go_s:rgb:joystick_rings/speed
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the change rate for the breathe, chroma, and rainbow effects.
|
||||
|
||||
Values are 0-100.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/class/leds/go_s:rgb:joystick_rings/speed_range
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the speed attribute.
|
||||
|
||||
Values are 0-100.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/auto_sleep_time
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the sleep timer due to inactivity for the built-in controller.
|
||||
|
||||
Values are 0-255.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/auto_sleep_time_range
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the gamepad/auto_sleep_time attribute.
|
||||
|
||||
Values are 0-255.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/dpad_mode
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the operating mode of the built-in controllers D-pad.
|
||||
|
||||
Values are 4-way or 8-way.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/dpad_mode_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the gamepad/dpad_mode attribute.
|
||||
|
||||
Values are 4-way or 8-way.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/mode
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the operating mode of the built-in controller.
|
||||
|
||||
Values are xinput or dinput.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/mode_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the gamepad/mode attribute.
|
||||
|
||||
Values are xinput or dinput.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/poll_rate
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls the poll rate in Hz of the built-in controller.
|
||||
|
||||
Values are 125, 250, 500, or 1000.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/poll_rate_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the gamepad/poll_rate attribute.
|
||||
|
||||
Values are 125, 250, 500, or 1000.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/imu/bypass_enabled
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls enabling or disabling the IMU bypass function. When enabled the IMU data is directly reported to the OS through
|
||||
an HIDRAW interface.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/imu/bypass_enabled_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the imu/bypass_enabled attribute.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/imu/manufacturer
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the manufacturer of the intertial measurment unit.
|
||||
|
||||
Values are Bosch or ST.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/imu/sensor_enabled
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls enabling or disabling the IMU.
|
||||
|
||||
Values are true, false, or wake-2s.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/imu/sensor_enabled_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the imu/sensor_enabled attribute.
|
||||
|
||||
Values are true, false, or wake-2s.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/mcu_id
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the MCU Identification Number
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/mouse/step
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls which value is used for the mouse sensitivity.
|
||||
|
||||
Values are 1-127.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/mouse/step_range
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the mouse/step attribute.
|
||||
|
||||
Values are 1-127.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/os_mode
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls which value is used for the touchpads operating mode.
|
||||
|
||||
Values are windows or linux.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/os_mode_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the os_mode attribute.
|
||||
|
||||
Values are windows or linux.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/enabled
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls enabling or disabling the built-in touchpad.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/enabled_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the touchpad/enabled attribute.
|
||||
|
||||
Values are true or false.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/linux_mode
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls behavior of the touchpad events when os_mode is set to linux.
|
||||
|
||||
Values are absolute or relative.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/linux_mode_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the touchpad/linux_mode attribute.
|
||||
|
||||
Values are absolute or relative.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/windows_mode
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls behavior of the touchpad events when os_mode is set to windows.
|
||||
|
||||
Values are absolute or relative.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/windows_mode_index
|
||||
Date: April 2026
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This displays the available options for the touchpad/windows_mode attribute.
|
||||
|
||||
Values are absolute or relative.
|
||||
|
||||
Applies to Lenovo Legion Go S line of handheld devices.
|
||||
28
MAINTAINERS
28
MAINTAINERS
@@ -11432,14 +11432,6 @@ F: drivers/hid/hid-sensor-*
|
||||
F: drivers/iio/*/hid-*
|
||||
F: include/linux/hid-sensor-*
|
||||
|
||||
HID UNIVERSAL PIDFF DRIVER
|
||||
M: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
|
||||
M: Oleg Makarenko <oleg@makarenk.ooo>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
B: https://github.com/JacKeTUs/universal-pidff/issues
|
||||
F: drivers/hid/hid-universal-pidff.c
|
||||
|
||||
HID VRC-2 CAR CONTROLLER DRIVER
|
||||
M: Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
@@ -14555,6 +14547,17 @@ L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/lenovo/wmi-hotkey-utilities.c
|
||||
|
||||
LENOVO HID drivers
|
||||
M: Derek J. Clark <derekjohn.clark@gmail.com>
|
||||
M: Mark Pearson <mpearson-lenovo@squebb.ca>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-driver-hid-lenovo-go
|
||||
F: Documentation/ABI/testing/sysfs-driver-hid-lenovo-go-s
|
||||
F: drivers/hid/hid-lenovo-go-s.c
|
||||
F: drivers/hid/hid-lenovo-go.c
|
||||
F: drivers/hid/hid-lenovo.c
|
||||
|
||||
LETSKETCH HID TABLET DRIVER
|
||||
M: Hans de Goede <hansg@kernel.org>
|
||||
L: linux-input@vger.kernel.org
|
||||
@@ -27492,6 +27495,15 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git
|
||||
F: Documentation/hid/hiddev.rst
|
||||
F: drivers/hid/usbhid/
|
||||
|
||||
USB HID PID DRIVERS (USB WHEELBASES, JOYSTICKS, RUDDERS, ...)
|
||||
M: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
|
||||
M: Oleg Makarenko <oleg@makarenk.ooo>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
B: https://github.com/JacKeTUs/universal-pidff/issues
|
||||
F: drivers/hid/usbhid/hid-pidff*
|
||||
F: drivers/hid/hid-universal-pidff.c
|
||||
|
||||
USB INTEL XHCI ROLE MUX DRIVER
|
||||
M: Hans de Goede <hansg@kernel.org>
|
||||
L: linux-usb@vger.kernel.org
|
||||
|
||||
@@ -107,7 +107,6 @@ menu "Special HID drivers"
|
||||
|
||||
config HID_A4TECH
|
||||
tristate "A4TECH mice"
|
||||
default !EXPERT
|
||||
help
|
||||
Support for some A4TECH mice with two scroll wheels.
|
||||
|
||||
@@ -140,7 +139,6 @@ config HID_APPLE
|
||||
tristate "Apple {i,Power,Mac}Books"
|
||||
depends on LEDS_CLASS
|
||||
depends on NEW_LEDS
|
||||
default !EXPERT
|
||||
help
|
||||
Support for some Apple devices which less or more break
|
||||
HID specification.
|
||||
@@ -209,7 +207,6 @@ config HID_AUREAL
|
||||
|
||||
config HID_BELKIN
|
||||
tristate "Belkin Flip KVM and Wireless keyboard"
|
||||
default !EXPERT
|
||||
help
|
||||
Support for Belkin Flip KVM and Wireless keyboard.
|
||||
|
||||
@@ -237,14 +234,12 @@ config HID_BIGBEN_FF
|
||||
|
||||
config HID_CHERRY
|
||||
tristate "Cherry Cymotion keyboard"
|
||||
default !EXPERT
|
||||
help
|
||||
Support for Cherry Cymotion keyboard.
|
||||
|
||||
config HID_CHICONY
|
||||
tristate "Chicony devices"
|
||||
depends on USB_HID
|
||||
default !EXPERT
|
||||
help
|
||||
Support for Chicony Tactical pad and special keys on Chicony keyboards.
|
||||
|
||||
@@ -322,7 +317,6 @@ config HID_CREATIVE_SB0540
|
||||
|
||||
config HID_CYPRESS
|
||||
tristate "Cypress mouse and barcode readers"
|
||||
default !EXPERT
|
||||
help
|
||||
Support for cypress mouse and barcode readers.
|
||||
|
||||
@@ -388,7 +382,6 @@ config HID_EVISION
|
||||
|
||||
config HID_EZKEY
|
||||
tristate "Ezkey BTC 8193 keyboard"
|
||||
default !EXPERT
|
||||
help
|
||||
Support for Ezkey BTC 8193 keyboard.
|
||||
|
||||
@@ -564,7 +557,6 @@ config HID_ICADE
|
||||
|
||||
config HID_ITE
|
||||
tristate "ITE devices"
|
||||
default !EXPERT
|
||||
help
|
||||
Support for ITE devices not fully compliant with HID standard.
|
||||
|
||||
@@ -585,7 +577,6 @@ config HID_TWINHAN
|
||||
|
||||
config HID_KENSINGTON
|
||||
tristate "Kensington Slimblade Trackball"
|
||||
default !EXPERT
|
||||
help
|
||||
Support for Kensington Slimblade Trackball.
|
||||
|
||||
@@ -610,8 +601,7 @@ config HID_LED
|
||||
|
||||
config HID_LENOVO
|
||||
tristate "Lenovo / Thinkpad devices"
|
||||
select NEW_LEDS
|
||||
select LEDS_CLASS
|
||||
depends on LEDS_CLASS
|
||||
help
|
||||
Support for IBM/Lenovo devices that are not fully compliant with HID standard.
|
||||
|
||||
@@ -623,6 +613,28 @@ config HID_LENOVO
|
||||
- ThinkPad Compact Bluetooth Keyboard with TrackPoint (supports Fn keys)
|
||||
- ThinkPad Compact USB Keyboard with TrackPoint (supports Fn keys)
|
||||
|
||||
config HID_LENOVO_GO
|
||||
tristate "HID Driver for Lenovo Legion Go Series Controllers"
|
||||
depends on USB_HID
|
||||
depends on LEDS_CLASS_MULTICOLOR
|
||||
help
|
||||
Support for Lenovo Legion Go devices with detachable controllers.
|
||||
|
||||
Say Y here to include configuration interface support for the Lenovo Legion Go
|
||||
and Legion Go 2 Handheld Console Controllers. Say M here to compile this
|
||||
driver as a module. The module will be called hid-lenovo-go.
|
||||
|
||||
config HID_LENOVO_GO_S
|
||||
tristate "HID Driver for Lenovo Legion Go S Controller"
|
||||
depends on USB_HID
|
||||
depends on LEDS_CLASS_MULTICOLOR
|
||||
help
|
||||
Support for Lenovo Legion Go S Handheld Console Controller.
|
||||
|
||||
Say Y here to include configuration interface support for the Lenovo Legion Go
|
||||
S. Say M here to compile this driver as a module. The module will be called
|
||||
hid-lenovo-go-s.
|
||||
|
||||
config HID_LETSKETCH
|
||||
tristate "Letsketch WP9620N tablets"
|
||||
depends on USB_HID
|
||||
@@ -642,7 +654,6 @@ config HID_LOGITECH
|
||||
depends on USB_HID
|
||||
depends on LEDS_CLASS
|
||||
depends on LEDS_CLASS_MULTICOLOR
|
||||
default !EXPERT
|
||||
help
|
||||
Support for Logitech devices that are not fully compliant with HID standard.
|
||||
|
||||
@@ -757,20 +768,17 @@ config HID_MEGAWORLD_FF
|
||||
|
||||
config HID_REDRAGON
|
||||
tristate "Redragon keyboards"
|
||||
default !EXPERT
|
||||
help
|
||||
Support for Redragon keyboards that need fix-ups to work properly.
|
||||
|
||||
config HID_MICROSOFT
|
||||
tristate "Microsoft non-fully HID-compliant devices"
|
||||
default !EXPERT
|
||||
select INPUT_FF_MEMLESS
|
||||
help
|
||||
Support for Microsoft devices that are not fully compliant with HID standard.
|
||||
|
||||
config HID_MONTEREY
|
||||
tristate "Monterey Genius KB29E keyboard"
|
||||
default !EXPERT
|
||||
help
|
||||
Support for Monterey Genius KB29E.
|
||||
|
||||
@@ -1100,13 +1108,15 @@ config HID_SONY
|
||||
help
|
||||
Support for
|
||||
|
||||
* Sony PS3 6-axis controllers
|
||||
* Sixaxis controllers for PS3
|
||||
* Buzz controllers
|
||||
* Sony PS3 Blue-ray Disk Remote Control (Bluetooth)
|
||||
* Logitech Harmony adapter for Sony Playstation 3 (Bluetooth)
|
||||
* Guitar Hero Live PS3, Wii U and PS4 guitar dongles
|
||||
* Guitar Hero PS3 and PC guitar dongles
|
||||
* Blu-ray Disc Remote Control for PS3
|
||||
* Logitech Harmony adapter for PS3
|
||||
* Guitar Hero Live PS3, Wii U and PS4 guitars
|
||||
* Guitar Hero PS3 and PC guitars
|
||||
* Rock Band 1, 2 and 3 PS3 and Wii instruments
|
||||
* Rock Band 4 PS4 and PS5 guitars
|
||||
* DJ Hero Turntable for PS3
|
||||
|
||||
config SONY_FF
|
||||
bool "Sony PS2/3/4 accessories force feedback support"
|
||||
@@ -1435,6 +1445,13 @@ config HID_KUNIT_TEST
|
||||
|
||||
If in doubt, say "N".
|
||||
|
||||
config HID_HUAWEI
|
||||
tristate "Huawei HID devices support"
|
||||
depends on USB_HID
|
||||
help
|
||||
Support for huawei cd30 keyboard or other hid devices
|
||||
that need fix-ups to work properly.
|
||||
|
||||
endmenu
|
||||
|
||||
source "drivers/hid/bpf/Kconfig"
|
||||
|
||||
@@ -76,6 +76,8 @@ obj-$(CONFIG_HID_KYE) += hid-kye.o
|
||||
obj-$(CONFIG_HID_KYSONA) += hid-kysona.o
|
||||
obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o
|
||||
obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o
|
||||
obj-$(CONFIG_HID_LENOVO_GO) += hid-lenovo-go.o
|
||||
obj-$(CONFIG_HID_LENOVO_GO_S) += hid-lenovo-go-s.o
|
||||
obj-$(CONFIG_HID_LETSKETCH) += hid-letsketch.o
|
||||
obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
|
||||
obj-$(CONFIG_HID_LOGITECH) += hid-lg-g15.o
|
||||
@@ -152,6 +154,7 @@ obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
|
||||
obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
|
||||
obj-$(CONFIG_HID_VIEWSONIC) += hid-viewsonic.o
|
||||
obj-$(CONFIG_HID_VRC2) += hid-vrc2.o
|
||||
obj-$(CONFIG_HID_HUAWEI) += hid-huawei.o
|
||||
|
||||
wacom-objs := wacom_wac.o wacom_sys.o
|
||||
obj-$(CONFIG_HID_WACOM) += wacom.o
|
||||
|
||||
90
drivers/hid/bpf/progs/Generic__touchpad.bpf.c
Normal file
90
drivers/hid/bpf/progs/Generic__touchpad.bpf.c
Normal file
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2025 Benjamin Tissoires
|
||||
*/
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include "hid_bpf.h"
|
||||
#include "hid_bpf_helpers.h"
|
||||
#include "hid_report_helpers.h"
|
||||
#include "hid_usages.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
HID_BPF_CONFIG(
|
||||
HID_DEVICE(BUS_ANY, HID_GROUP_MULTITOUCH_WIN_8, HID_VID_ANY, HID_PID_ANY),
|
||||
);
|
||||
|
||||
EXPORT_UDEV_PROP(HID_DIGITIZER_PAD_TYPE, 32);
|
||||
|
||||
__u8 hw_req_buf[1024];
|
||||
|
||||
/* to be filled by udev-hid-bpf */
|
||||
struct hid_rdesc_descriptor HID_REPORT_DESCRIPTOR;
|
||||
|
||||
SEC("syscall")
|
||||
int probe(struct hid_bpf_probe_args *ctx)
|
||||
{
|
||||
struct hid_rdesc_report *pad_type_feature = NULL;
|
||||
struct hid_rdesc_field *pad_type = NULL;
|
||||
struct hid_rdesc_report *feature;
|
||||
struct hid_bpf_ctx *hid_ctx;
|
||||
char *pad_type_str = "";
|
||||
int ret;
|
||||
|
||||
hid_bpf_for_each_feature_report(&HID_REPORT_DESCRIPTOR, feature) {
|
||||
struct hid_rdesc_field *field;
|
||||
|
||||
hid_bpf_for_each_field(feature, field) {
|
||||
if (field->usage_page == HidUsagePage_Digitizers &&
|
||||
field->usage_id == HidUsage_Dig_PadType) {
|
||||
pad_type = field;
|
||||
pad_type_feature = feature;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pad_type)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!pad_type || !pad_type_feature) {
|
||||
ctx->retval = -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
hid_ctx = hid_bpf_allocate_context(ctx->hid);
|
||||
|
||||
if (!hid_ctx)
|
||||
return -1; /* EPERM check */
|
||||
|
||||
hw_req_buf[0] = pad_type_feature->report_id;
|
||||
|
||||
ret = hid_bpf_hw_request(hid_ctx, hw_req_buf, sizeof(hw_req_buf),
|
||||
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
|
||||
hid_bpf_release_context(hid_ctx);
|
||||
|
||||
if (ret < 0) {
|
||||
ctx->retval = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx->retval = 0;
|
||||
|
||||
switch (EXTRACT_BITS(hw_req_buf, pad_type)) {
|
||||
case 0:
|
||||
pad_type_str = "Clickpad";
|
||||
break;
|
||||
case 1:
|
||||
pad_type_str = "Pressurepad";
|
||||
break;
|
||||
case 2:
|
||||
pad_type_str = "Discrete";
|
||||
break;
|
||||
default:
|
||||
pad_type_str = "Unknown";
|
||||
}
|
||||
|
||||
UDEV_PROP_SPRINTF(HID_DIGITIZER_PAD_TYPE, "%s", pad_type_str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
492
drivers/hid/bpf/progs/Huion__KeydialK20-Bluetooth.bpf.c
Normal file
492
drivers/hid/bpf/progs/Huion__KeydialK20-Bluetooth.bpf.c
Normal file
@@ -0,0 +1,492 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2024 Red Hat, Inc
|
||||
*/
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include "hid_bpf.h"
|
||||
#include "hid_bpf_helpers.h"
|
||||
#include "hid_report_helpers.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
#define VID_HUION 0x256C
|
||||
#define PID_KEYDIAL_K20_BLUETOOTH 0x8251
|
||||
|
||||
HID_BPF_CONFIG(
|
||||
HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_GENERIC, VID_HUION, PID_KEYDIAL_K20_BLUETOOTH),
|
||||
);
|
||||
|
||||
/* This is the same device as in 0010-Huion__KeydialK20 but connected via Bluetooth.
|
||||
* It does not need (to support?) switching to a vendor mode so we just modify the
|
||||
* existing mode.
|
||||
*
|
||||
* By default it exports two hidraw nodes, only the second one sends events.
|
||||
*
|
||||
* This is the first hidraw node which we disable:
|
||||
*
|
||||
* # Keydial mini-050
|
||||
* # Report descriptor length: 114 bytes
|
||||
* # Bytes // Field Name Offset
|
||||
* # ----------------------------------------------------------------------------------
|
||||
* # 🮥 0x05, 0x01, // Usage Page (Generic Desktop) 0
|
||||
* # 🭬 0x09, 0x0e, // Usage (System Multi-Axis Controller) 2
|
||||
* # 0xa1, 0x01, // Collection (Application) 4
|
||||
* # ┅ 0x85, 0x03, // Report ID (3) 6
|
||||
* # 🮥 0x05, 0x0d, // Usage Page (Digitizers) 8
|
||||
* # 0x75, 0x08, // Report Size (8) 10
|
||||
* # 0x95, 0x01, // Report Count (1) 12
|
||||
* # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 14
|
||||
* # 🭬 0x09, 0x21, // Usage (Puck) 16
|
||||
* # 0xa1, 0x02, // Collection (Logical) 18
|
||||
* # 0x15, 0x00, // Logical Minimum (0) 20
|
||||
* # 0x25, 0x01, // Logical Maximum (1) 22
|
||||
* # 0x75, 0x01, // Report Size (1) 24
|
||||
* # 0x95, 0x01, // Report Count (1) 26
|
||||
* # 0xa1, 0x00, // Collection (Physical) 28
|
||||
* # 🮥 0x05, 0x09, // Usage Page (Button) 30
|
||||
* # 🭬 0x09, 0x01, // Usage (Button 1) 32
|
||||
* # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 34
|
||||
* # 🮥 0x05, 0x0d, // Usage Page (Digitizers) 36
|
||||
* # 🭬 0x09, 0x33, // Usage (Touch) 38
|
||||
* # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 40
|
||||
* # 0x95, 0x06, // Report Count (6) 42
|
||||
* # ┇ 0x81, 0x03, // Input (Cnst,Var,Abs) 44
|
||||
* # 0xa1, 0x02, // Collection (Logical) 46
|
||||
* # 🮥 0x05, 0x01, // Usage Page (Generic Desktop) 48
|
||||
* # 🭬 0x09, 0x37, // Usage (Dial) 50
|
||||
* # 0x16, 0x00, 0x80, // Logical Minimum (32768) 52
|
||||
* # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 55
|
||||
* # 0x75, 0x10, // Report Size (16) 58
|
||||
* # 0x95, 0x01, // Report Count (1) 60
|
||||
* # ┇ 0x81, 0x06, // Input (Data,Var,Rel) 62
|
||||
* # 0x35, 0x00, // Physical Minimum (0) 64
|
||||
* # 0x46, 0x10, 0x0e, // Physical Maximum (3600) 66
|
||||
* # 0x15, 0x00, // Logical Minimum (0) 69
|
||||
* # 0x26, 0x10, 0x0e, // Logical Maximum (3600) 71
|
||||
* # 🭬 0x09, 0x48, // Usage (Resolution Multiplier) 74
|
||||
* # ║ 0xb1, 0x02, // Feature (Data,Var,Abs) 76
|
||||
* # 0x45, 0x00, // Physical Maximum (0) 78
|
||||
* # 0xc0, // End Collection 80
|
||||
* # 0x75, 0x08, // Report Size (8) 81
|
||||
* # 0x95, 0x01, // Report Count (1) 83
|
||||
* # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 85
|
||||
* # 0x75, 0x08, // Report Size (8) 87
|
||||
* # 0x95, 0x01, // Report Count (1) 89
|
||||
* # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 91
|
||||
* # 0x75, 0x08, // Report Size (8) 93
|
||||
* # 0x95, 0x01, // Report Count (1) 95
|
||||
* # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 97
|
||||
* # 0x75, 0x08, // Report Size (8) 99
|
||||
* # 0x95, 0x01, // Report Count (1) 101
|
||||
* # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 103
|
||||
* # 0x75, 0x08, // Report Size (8) 105
|
||||
* # 0x95, 0x01, // Report Count (1) 107
|
||||
* # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 109
|
||||
* # 0xc0, // End Collection 111
|
||||
* # 0xc0, // End Collection 112
|
||||
* # 0xc0, // End Collection 113
|
||||
* R: 114 05 01 09 0e a1 01 85 03 05 0d 75 08 95 01 81 01 09 21 a1 02 15 00 25 01 75 01 95 01 a1 00 05 09 09 01 81 02 05 0d 09 33 81 02 95 06 81 03 a1 02 05 01 09 37 16 00 80 26 ff 7f 75 10 95 01 81 06 35 00 46 10 0e 15 00 26 10 0e 09 48 b1 02 45 00 c0 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 c0 c0 c0
|
||||
* N: Keydial mini-050
|
||||
* I: 5 256c 8251
|
||||
*
|
||||
* The second hidraw node is what sends events:
|
||||
*
|
||||
* # Keydial mini-050
|
||||
* # Report descriptor length: 160 bytes
|
||||
* # Bytes // Field Name Offset
|
||||
* # ----------------------------------------------------------------------------------
|
||||
* # 🮥 0x05, 0x01, // Usage Page (Generic Desktop) 0
|
||||
* # 🭬 0x09, 0x06, // Usage (Keyboard) 2
|
||||
* # 0xa1, 0x01, // Collection (Application) 4
|
||||
* # ┅ 0x85, 0x01, // Report ID (1) 6
|
||||
* # 🮥 0x05, 0x07, // Usage Page (Keyboard/Keypad) 8
|
||||
* # 🭬 0x19, 0xe0, // Usage Minimum (224) 10
|
||||
* # 🭬 0x29, 0xe7, // Usage Maximum (231) 12
|
||||
* # 0x15, 0x00, // Logical Minimum (0) 14
|
||||
* # 0x25, 0x01, // Logical Maximum (1) 16
|
||||
* # 0x75, 0x01, // Report Size (1) 18
|
||||
* # 0x95, 0x08, // Report Count (8) 20
|
||||
* # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 22
|
||||
* # 0x95, 0x01, // Report Count (1) 24
|
||||
* # 0x75, 0x08, // Report Size (8) 26
|
||||
* # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 28
|
||||
* # 0x95, 0x05, // Report Count (5) 30
|
||||
* # 0x75, 0x01, // Report Size (1) 32
|
||||
* # 🮥 0x05, 0x08, // Usage Page (LED) 34
|
||||
* # 🭬 0x19, 0x01, // Usage Minimum (1) 36
|
||||
* # 🭬 0x29, 0x05, // Usage Maximum (5) 38
|
||||
* # ┊ 0x91, 0x02, // Output (Data,Var,Abs) 40
|
||||
* # 0x95, 0x01, // Report Count (1) 42
|
||||
* # 0x75, 0x03, // Report Size (3) 44
|
||||
* # ┊ 0x91, 0x01, // Output (Cnst,Arr,Abs) 46
|
||||
* # 0x95, 0x06, // Report Count (6) 48
|
||||
* # 0x75, 0x08, // Report Size (8) 50
|
||||
* # 0x15, 0x00, // Logical Minimum (0) 52
|
||||
* # 0x25, 0xf1, // Logical Maximum (241) 54
|
||||
* # 🮥 0x05, 0x07, // Usage Page (Keyboard/Keypad) 56
|
||||
* # 🭬 0x19, 0x00, // Usage Minimum (0) 58
|
||||
* # 🭬 0x29, 0xf1, // Usage Maximum (241) 60
|
||||
* # ┇ 0x81, 0x00, // Input (Data,Arr,Abs) 62
|
||||
* # 0xc0, // End Collection 64
|
||||
* # 🮥 0x05, 0x0c, // Usage Page (Consumer) 65
|
||||
* # 🭬 0x09, 0x01, // Usage (Consumer Control) 67
|
||||
* # 0xa1, 0x01, // Collection (Application) 69
|
||||
* # ┅ 0x85, 0x02, // Report ID (2) 71
|
||||
* # 🮥 0x05, 0x0c, // Usage Page (Consumer) 73
|
||||
* # 🭬 0x19, 0x00, // Usage Minimum (0) 75
|
||||
* # 🭬 0x2a, 0x80, 0x03, // Usage Maximum (896) 77
|
||||
* # 0x15, 0x00, // Logical Minimum (0) 80
|
||||
* # 0x26, 0x80, 0x03, // Logical Maximum (896) 82
|
||||
* # 0x75, 0x10, // Report Size (16) 85
|
||||
* # 0x95, 0x01, // Report Count (1) 87
|
||||
* # ┇ 0x81, 0x00, // Input (Data,Arr,Abs) 89
|
||||
* # 0xc0, // End Collection 91
|
||||
* # 🮥 0x05, 0x01, // Usage Page (Generic Desktop) 92
|
||||
* # 🭬 0x09, 0x02, // Usage (Mouse) 94
|
||||
* # 0xa1, 0x01, // Collection (Application) 96
|
||||
* # 🭬 0x09, 0x01, // Usage (Pointer) 98
|
||||
* # ┅ 0x85, 0x05, // Report ID (5) 100
|
||||
* # 0xa1, 0x00, // Collection (Physical) 102
|
||||
* # 🮥 0x05, 0x09, // Usage Page (Button) 104
|
||||
* # 🭬 0x19, 0x01, // Usage Minimum (1) 106
|
||||
* # 🭬 0x29, 0x05, // Usage Maximum (5) 108
|
||||
* # 0x15, 0x00, // Logical Minimum (0) 110
|
||||
* # 0x25, 0x01, // Logical Maximum (1) 112
|
||||
* # 0x95, 0x05, // Report Count (5) 114
|
||||
* # 0x75, 0x01, // Report Size (1) 116
|
||||
* # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 118
|
||||
* # 0x95, 0x01, // Report Count (1) 120
|
||||
* # 0x75, 0x03, // Report Size (3) 122
|
||||
* # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 124
|
||||
* # 🮥 0x05, 0x01, // Usage Page (Generic Desktop) 126
|
||||
* # 🭬 0x09, 0x30, // Usage (X) 128
|
||||
* # 🭬 0x09, 0x31, // Usage (Y) 130
|
||||
* # 0x16, 0x01, 0x80, // Logical Minimum (32769) 132
|
||||
* # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 135
|
||||
* # 0x75, 0x10, // Report Size (16) 138
|
||||
* # 0x95, 0x02, // Report Count (2) 140
|
||||
* # ┇ 0x81, 0x06, // Input (Data,Var,Rel) 142
|
||||
* # 🮥 0x05, 0x01, // Usage Page (Generic Desktop) 144
|
||||
* # 🭬 0x09, 0x38, // Usage (Wheel) 146
|
||||
* # 0x15, 0x81, // Logical Minimum (129) 148
|
||||
* # 0x25, 0x7f, // Logical Maximum (127) 150
|
||||
* # 0x95, 0x01, // Report Count (1) 152
|
||||
* # 0x75, 0x08, // Report Size (8) 154
|
||||
* # ┇ 0x81, 0x06, // Input (Data,Var,Rel) 156
|
||||
* # 0xc0, // End Collection 158
|
||||
* # 0xc0, // End Collection 159
|
||||
* R: 160 05 01 09 06 a1 01 85 01 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 95 01 75 08 81 01 95 05 75 01 05 08 19 01 29 05 91 02 95 01 75 03 91 01 95 06 75 08 15 00 25 f1 05 07 19 00 29 f1 81 00 c0 05 0c 09 01 a1 01 85 02 05 0c 19 00 2a 80 03 15 00 26 80 03 75 10 95 01 81 00 c0 05 01 09 02 a1 01 09 01 85 05 a1 00 05 09 19 01 29 05 15 00 25 01 95 05 75 01 81 02 95 01 75 03 81 01 05 01 09 30 09 31 16 01 80 26 ff 7f 75 10 95 02 81 06 05 01 09 38 15 81 25 7f 95 01 75 08 81 06 c0 c0
|
||||
* N: Keydial mini-050
|
||||
* I: 5 256c 8251
|
||||
* # Report descriptor:
|
||||
* # ------- Input Report -------
|
||||
* # ░ Report ID: 1
|
||||
* # ░ | Report size: 72 bits
|
||||
* # ░ Bit: 8 Usage: 0007/00e0: Keyboard/Keypad / Keyboard LeftControl Logical Range: 0..=1
|
||||
* # ░ Bit: 9 Usage: 0007/00e1: Keyboard/Keypad / Keyboard LeftShift Logical Range: 0..=1
|
||||
* # ░ Bit: 10 Usage: 0007/00e2: Keyboard/Keypad / Keyboard LeftAlt Logical Range: 0..=1
|
||||
* # ░ Bit: 11 Usage: 0007/00e3: Keyboard/Keypad / Keyboard Left GUI Logical Range: 0..=1
|
||||
* # ░ Bit: 12 Usage: 0007/00e4: Keyboard/Keypad / Keyboard RightControl Logical Range: 0..=1
|
||||
* # ░ Bit: 13 Usage: 0007/00e5: Keyboard/Keypad / Keyboard RightShift Logical Range: 0..=1
|
||||
* # ░ Bit: 14 Usage: 0007/00e6: Keyboard/Keypad / Keyboard RightAlt Logical Range: 0..=1
|
||||
* # ░ Bit: 15 Usage: 0007/00e7: Keyboard/Keypad / Keyboard Right GUI Logical Range: 0..=1
|
||||
* # ░ Bits: 16..=23 ######### Padding
|
||||
* # ░ Bits: 24..=71 Usages: Logical Range: 0..=241
|
||||
* # ░ 0007/0000: <unknown>
|
||||
* # ░ 0007/0001: Keyboard/Keypad / ErrorRollOver
|
||||
* # ░ 0007/0002: Keyboard/Keypad / POSTFail
|
||||
* # ░ 0007/0003: Keyboard/Keypad / ErrorUndefined
|
||||
* # ░ 0007/0004: Keyboard/Keypad / Keyboard A
|
||||
* # ░ ... use --full to see all usages
|
||||
* # ------- Input Report -------
|
||||
* # ▒ Report ID: 2
|
||||
* # ▒ | Report size: 24 bits
|
||||
* # ▒ Bits: 8..=23 Usages: Logical Range: 0..=896
|
||||
* # ▒ 000c/0000: <unknown>
|
||||
* # ▒ 000c/0001: Consumer / Consumer Control
|
||||
* # ▒ 000c/0002: Consumer / Numeric Key Pad
|
||||
* # ▒ 000c/0003: Consumer / Programmable Buttons
|
||||
* # ▒ 000c/0004: Consumer / Microphone
|
||||
* # ▒ ... use --full to see all usages
|
||||
* # ------- Input Report -------
|
||||
* # ▞ Report ID: 5
|
||||
* # ▞ | Report size: 56 bits
|
||||
* # ▞ Bit: 8 Usage: 0009/0001: Button / Button 1 Logical Range: 0..=1
|
||||
* # ▞ Bit: 9 Usage: 0009/0002: Button / Button 2 Logical Range: 0..=1
|
||||
* # ▞ Bit: 10 Usage: 0009/0003: Button / Button 3 Logical Range: 0..=1
|
||||
* # ▞ Bit: 11 Usage: 0009/0004: Button / Button 4 Logical Range: 0..=1
|
||||
* # ▞ Bit: 12 Usage: 0009/0005: Button / Button 5 Logical Range: 0..=1
|
||||
* # ▞ Bits: 13..=15 ######### Padding
|
||||
* # ▞ Bits: 16..=31 Usage: 0001/0030: Generic Desktop / X Logical Range: 32769..=32767
|
||||
* # ▞ Bits: 32..=47 Usage: 0001/0031: Generic Desktop / Y Logical Range: 32769..=32767
|
||||
* # ▞ Bits: 48..=55 Usage: 0001/0038: Generic Desktop / Wheel Logical Range: 129..=127
|
||||
* # ------- Output Report -------
|
||||
* # ░ Report ID: 1
|
||||
* # ░ | Report size: 16 bits
|
||||
* # ░ Bit: 8 Usage: 0008/0001: LED / Num Lock Logical Range: 0..=1
|
||||
* # ░ Bit: 9 Usage: 0008/0002: LED / Caps Lock Logical Range: 0..=1
|
||||
* # ░ Bit: 10 Usage: 0008/0003: LED / Scroll Lock Logical Range: 0..=1
|
||||
* # ░ Bit: 11 Usage: 0008/0004: LED / Compose Logical Range: 0..=1
|
||||
* # ░ Bit: 12 Usage: 0008/0005: LED / Kana Logical Range: 0..=1
|
||||
* # ░ Bits: 13..=15 ######### Padding
|
||||
* ##############################################################################
|
||||
* # Event nodes:
|
||||
* # - /dev/input/event12: "Keydial mini-050 Keyboard"
|
||||
* # - /dev/input/event14: "Keydial mini-050 Mouse"
|
||||
* ##############################################################################
|
||||
* # Recorded events below in format:
|
||||
* # E: <seconds>.<microseconds> <length-in-bytes> [bytes ...]
|
||||
* #
|
||||
*
|
||||
* - Report ID 1 sends keyboard shortcuts when pressing the buttons, e.g.
|
||||
*
|
||||
* # ░ Report ID: 1 /
|
||||
* # ░ Keyboard LeftControl: 0 |Keyboard LeftShift: 0 |Keyboard LeftAlt: 0 |Keyboard Left GUI: 0 |Keyboard RightControl: 0 |Keyboard RightShift: 0 |Keyboard RightAlt: 0 |Keyboard Right GUI: 0 |<8 bits padding> |0007/0000: 0| Keyboard K: 14| 0007/0000: 0| 0007/0000: 0| 0007/0000: 0| 0007/0000: 0
|
||||
* E: 000000.000292 9 01 00 00 00 0e 00 00 00 00
|
||||
*
|
||||
* - Report ID 2 sends the button inside the wheel/dial thing
|
||||
* # ▒ Report ID: 2 /
|
||||
* # ▒ Play/Pause: 205
|
||||
* E: 000134.347845 3 02 cd 00
|
||||
* # ▒ Report ID: 2 /
|
||||
* # ▒ 000c/0000: 0
|
||||
* E: 000134.444965 3 02 00 00
|
||||
*
|
||||
* - Report ID 5 sends the wheel relative events (always a double-event with the second as zero)
|
||||
* # ▞ Report ID: 5 /
|
||||
* # ▞ Button 1: 0 |Button 2: 0 |Button 3: 0 |Button 4: 0 |Button 5: 0 |<3 bits padding> |X: 0 |Y: 0 |Wheel: 255
|
||||
* E: 000064.859915 7 05 00 00 00 00 00 ff
|
||||
* # ▞ Report ID: 5 /
|
||||
* # ▞ Button 1: 0 |Button 2: 0 |Button 3: 0 |Button 4: 0 |Button 5: 0 |<3 bits padding> |X: 0 |Y: 0 |Wheel: 0
|
||||
* E: 000064.882009 7 05 00 00 00 00 00 00
|
||||
*/
|
||||
|
||||
#define BT_PAD_REPORT_DESCRIPTOR_LENGTH 160
|
||||
#define BT_PUCK_REPORT_DESCRIPTOR_LENGTH 114 // This one doesn't send events
|
||||
#define BT_PAD_KBD_REPORT_ID 1
|
||||
#define BT_PAD_CC_REPORT_ID 2
|
||||
#define BT_PAD_MOUSE_REPORT_ID 5
|
||||
#define BT_PAD_KBD_REPORT_LENGTH 9
|
||||
#define BT_PAD_CC_REPORT_LENGTH 3
|
||||
#define BT_PAD_MOUSE_REPORT_LENGTH 7
|
||||
#define OUR_REPORT_ID 11 /* "randomly" picked report ID for our reports */
|
||||
|
||||
__u32 last_button_state = 0;
|
||||
|
||||
static const __u8 disabled_rdesc_puck[] = {
|
||||
FixedSizeVendorReport(BT_PUCK_REPORT_DESCRIPTOR_LENGTH)
|
||||
};
|
||||
|
||||
static const __u8 fixed_rdesc_pad[] = {
|
||||
UsagePage_GenericDesktop
|
||||
Usage_GD_Keypad
|
||||
CollectionApplication(
|
||||
// Byte 0
|
||||
ReportId(OUR_REPORT_ID)
|
||||
UsagePage_Digitizers
|
||||
Usage_Dig_TabletFunctionKeys
|
||||
CollectionPhysical(
|
||||
// Byte 1 is a button so we look like a tablet
|
||||
Usage_Dig_BarrelSwitch // BTN_STYLUS, needed so we get to be a tablet pad
|
||||
ReportCount(1)
|
||||
ReportSize(1)
|
||||
Input(Var|Abs)
|
||||
ReportCount(7) // Padding
|
||||
Input(Const)
|
||||
// Bytes 2/3 - x/y just exist so we get to be a tablet pad
|
||||
UsagePage_GenericDesktop
|
||||
Usage_GD_X
|
||||
Usage_GD_Y
|
||||
LogicalMinimum_i8(0x0)
|
||||
LogicalMaximum_i8(0x1)
|
||||
ReportCount(2)
|
||||
ReportSize(8)
|
||||
Input(Var|Abs)
|
||||
// Bytes 4-7 are the button state for 19 buttons + pad out to u32
|
||||
// We send the first 10 buttons as buttons 1-10 which is BTN_0 -> BTN_9
|
||||
UsagePage_Button
|
||||
UsageMinimum_i8(1)
|
||||
UsageMaximum_i8(10)
|
||||
LogicalMinimum_i8(0x0)
|
||||
LogicalMaximum_i8(0x1)
|
||||
ReportCount(10)
|
||||
ReportSize(1)
|
||||
Input(Var|Abs)
|
||||
// We send the other 9 buttons as buttons 0x31 and above -> BTN_A - BTN_TL2
|
||||
UsageMinimum_i8(0x31)
|
||||
UsageMaximum_i8(0x3a)
|
||||
ReportCount(9)
|
||||
ReportSize(1)
|
||||
Input(Var|Abs)
|
||||
ReportCount(13)
|
||||
ReportSize(1)
|
||||
Input(Const) // padding
|
||||
// Byte 8 is the wheel
|
||||
UsagePage_GenericDesktop
|
||||
Usage_GD_Wheel
|
||||
LogicalMinimum_i8(-1)
|
||||
LogicalMaximum_i8(1)
|
||||
ReportCount(1)
|
||||
ReportSize(8)
|
||||
Input(Var|Rel)
|
||||
)
|
||||
// Make sure we match our original report length
|
||||
FixedSizeVendorReport(BT_PAD_KBD_REPORT_LENGTH)
|
||||
)
|
||||
};
|
||||
|
||||
SEC(HID_BPF_RDESC_FIXUP)
|
||||
int BPF_PROG(k20_bt_fix_rdesc, struct hid_bpf_ctx *hctx)
|
||||
{
|
||||
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
|
||||
__s32 rdesc_size = hctx->size;
|
||||
|
||||
if (!data)
|
||||
return 0; /* EPERM check */
|
||||
|
||||
if (rdesc_size == BT_PAD_REPORT_DESCRIPTOR_LENGTH) {
|
||||
__builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad));
|
||||
return sizeof(fixed_rdesc_pad);
|
||||
}
|
||||
if (rdesc_size == BT_PUCK_REPORT_DESCRIPTOR_LENGTH) {
|
||||
// This hidraw node doesn't send anything and can be ignored
|
||||
__builtin_memcpy(data, disabled_rdesc_puck, sizeof(disabled_rdesc_puck));
|
||||
return sizeof(disabled_rdesc_puck);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC(HID_BPF_DEVICE_EVENT)
|
||||
int BPF_PROG(k20_bt_fix_events, struct hid_bpf_ctx *hctx)
|
||||
{
|
||||
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 12 /* size */);
|
||||
struct pad_report {
|
||||
__u8 report_id;
|
||||
__u8 btn_stylus:1;
|
||||
__u8 pad:7;
|
||||
__u8 x;
|
||||
__u8 y;
|
||||
__u32 buttons;
|
||||
__u8 wheel;
|
||||
} __packed * pad_report = (struct pad_report *)data;
|
||||
|
||||
if (!data)
|
||||
return 0; /* EPERM check */
|
||||
|
||||
/* Report ID 1 - Keyboard events (button presses) */
|
||||
if (data[0] == BT_PAD_KBD_REPORT_ID) {
|
||||
const __u8 button_mapping[] = {
|
||||
0x0e, /* Button 1: K */
|
||||
0x0a, /* Button 2: G */
|
||||
0x0f, /* Button 3: L */
|
||||
0x4c, /* Button 4: Delete */
|
||||
0x0c, /* Button 5: I */
|
||||
0x07, /* Button 6: D */
|
||||
0x05, /* Button 7: B */
|
||||
0x08, /* Button 8: E */
|
||||
0x16, /* Button 9: S */
|
||||
0x1d, /* Button 10: Z */
|
||||
0x06, /* Button 11: C */
|
||||
0x19, /* Button 12: V */
|
||||
0xff, /* Button 13: LeftControl */
|
||||
0xff, /* Button 14: LeftAlt */
|
||||
0xff, /* Button 15: LeftShift */
|
||||
0x28, /* Button 16: Return Enter */
|
||||
0x2c, /* Button 17: Spacebar */
|
||||
0x11, /* Button 18: N */
|
||||
};
|
||||
|
||||
__u8 modifiers = data[1];
|
||||
__u32 buttons = 0;
|
||||
|
||||
if (modifiers & 0x01) { /* Control */
|
||||
buttons |= BIT(12);
|
||||
}
|
||||
if (modifiers & 0x02) { /* Shift */
|
||||
buttons |= BIT(14);
|
||||
}
|
||||
if (modifiers & 0x04) { /* Alt */
|
||||
buttons |= BIT(13);
|
||||
}
|
||||
|
||||
for (int i = 4; i < BT_PAD_KBD_REPORT_LENGTH; i++) {
|
||||
if (!data[i])
|
||||
break;
|
||||
|
||||
for (size_t b = 0; b < ARRAY_SIZE(button_mapping); b++) {
|
||||
if (data[i] != 0xff && data[i] == button_mapping[b]) {
|
||||
buttons |= BIT(b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
last_button_state = buttons;
|
||||
|
||||
pad_report->report_id = OUR_REPORT_ID;
|
||||
pad_report->btn_stylus = 0;
|
||||
pad_report->x = 0;
|
||||
pad_report->y = 0;
|
||||
pad_report->buttons = buttons;
|
||||
pad_report->wheel = 0;
|
||||
|
||||
return sizeof(struct pad_report);
|
||||
}
|
||||
|
||||
/* Report ID 2 - Consumer control events (the button inside the wheel) */
|
||||
if (data[0] == BT_PAD_CC_REPORT_ID) {
|
||||
const __u8 PlayPause = 0xcd;
|
||||
|
||||
if (data[1] == PlayPause)
|
||||
last_button_state |= BIT(18);
|
||||
else
|
||||
last_button_state &= ~BIT(18);
|
||||
|
||||
pad_report->report_id = OUR_REPORT_ID;
|
||||
pad_report->btn_stylus = 0;
|
||||
pad_report->x = 0;
|
||||
pad_report->y = 0;
|
||||
pad_report->buttons = last_button_state;
|
||||
pad_report->wheel = 0;
|
||||
|
||||
return sizeof(struct pad_report);
|
||||
}
|
||||
|
||||
/* Report ID 5 - Mouse events (wheel rotation) */
|
||||
if (data[0] == BT_PAD_MOUSE_REPORT_ID) {
|
||||
__u8 wheel_delta = data[6];
|
||||
|
||||
pad_report->report_id = OUR_REPORT_ID;
|
||||
pad_report->btn_stylus = 0;
|
||||
pad_report->x = 0;
|
||||
pad_report->y = 0;
|
||||
pad_report->buttons = last_button_state;
|
||||
pad_report->wheel = wheel_delta;
|
||||
|
||||
return sizeof(struct pad_report);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
HID_BPF_OPS(keydial_k20_bluetooth) = {
|
||||
.hid_device_event = (void *)k20_bt_fix_events,
|
||||
.hid_rdesc_fixup = (void *)k20_bt_fix_rdesc,
|
||||
};
|
||||
|
||||
SEC("syscall")
|
||||
int probe(struct hid_bpf_probe_args *ctx)
|
||||
{
|
||||
switch (ctx->rdesc_size) {
|
||||
case BT_PAD_REPORT_DESCRIPTOR_LENGTH:
|
||||
case BT_PUCK_REPORT_DESCRIPTOR_LENGTH:
|
||||
ctx->retval = 0;
|
||||
break;
|
||||
default:
|
||||
ctx->retval = -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -462,7 +462,8 @@ int BPF_PROG(k20_fix_events, struct hid_bpf_ctx *hctx)
|
||||
__u32 buttons;
|
||||
__u8 wheel;
|
||||
} __attribute__((packed)) *pad_report;
|
||||
int i, b;
|
||||
int i;
|
||||
size_t b;
|
||||
__u8 modifiers = data[1];
|
||||
__u32 buttons = 0;
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
|
||||
if (data[3] != 0x06)
|
||||
return 0;
|
||||
|
||||
for (int idx = 0; idx < ARRAY_SIZE(offsets); idx++) {
|
||||
for (size_t idx = 0; idx < ARRAY_SIZE(offsets); idx++) {
|
||||
u8 offset = offsets[idx];
|
||||
|
||||
/* if Input (Cnst,Var,Abs) , make it Input (Data,Var,Abs) */
|
||||
|
||||
49
drivers/hid/bpf/progs/Trust__Philips-SPK6327.bpf.c
Normal file
49
drivers/hid/bpf/progs/Trust__Philips-SPK6327.bpf.c
Normal file
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Fix for Trust Philips SPK6327 (145f:024b)
|
||||
* Modifier keys report as Array (0x00) instead of Variable (0x02)
|
||||
* causing LCtrl, LAlt, Super etc. to all act as LShift
|
||||
*/
|
||||
#include "vmlinux.h"
|
||||
#include "hid_bpf.h"
|
||||
#include "hid_bpf_helpers.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
#define VID_TRUST 0x145F
|
||||
#define PID_SPK6327 0x024B
|
||||
|
||||
HID_BPF_CONFIG(
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_TRUST, PID_SPK6327)
|
||||
);
|
||||
|
||||
SEC(HID_BPF_RDESC_FIXUP)
|
||||
int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
|
||||
{
|
||||
__u8 *data = hid_bpf_get_data(hctx, 0, 4096);
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
/* Fix modifier keys: Input Array (0x00) -> Input Variable (0x02) */
|
||||
if (data[101] == 0x00)
|
||||
data[101] = 0x02;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
HID_BPF_OPS(trust_spk6327) = {
|
||||
.hid_rdesc_fixup = (void *)hid_fix_rdesc,
|
||||
};
|
||||
|
||||
SEC("syscall")
|
||||
int probe(struct hid_bpf_probe_args *ctx)
|
||||
{
|
||||
/* Only apply to interface 1 (169 bytes) not interface 0 (62 bytes) */
|
||||
if (ctx->rdesc_size == 169)
|
||||
ctx->retval = 0;
|
||||
else
|
||||
ctx->retval = -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -148,7 +148,7 @@ int probe(struct hid_bpf_probe_args *ctx)
|
||||
{
|
||||
struct hid_bpf_ctx *hid_ctx;
|
||||
__u16 pid;
|
||||
int i;
|
||||
size_t i;
|
||||
|
||||
/* get a struct hid_device to access the actual pid of the device */
|
||||
hid_ctx = hid_bpf_allocate_context(ctx->hid);
|
||||
|
||||
@@ -173,7 +173,7 @@ int BPF_PROG(hid_device_event_xppen_deco_mini_4, struct hid_bpf_ctx *hctx)
|
||||
{
|
||||
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 8 /* size */);
|
||||
__u8 button_mask = 0;
|
||||
int d, b;
|
||||
size_t d, b;
|
||||
|
||||
if (!data)
|
||||
return 0; /* EPERM check */
|
||||
|
||||
@@ -116,15 +116,14 @@ static int hid_bpf_async_find_empty_key(void)
|
||||
if (!elem)
|
||||
return -ENOMEM; /* should never happen */
|
||||
|
||||
bpf_spin_lock(&elem->lock);
|
||||
{
|
||||
guard(bpf_spin)(&elem->lock);
|
||||
|
||||
if (elem->state == HID_BPF_ASYNC_STATE_UNSET) {
|
||||
elem->state = HID_BPF_ASYNC_STATE_INITIALIZING;
|
||||
bpf_spin_unlock(&elem->lock);
|
||||
return i;
|
||||
if (elem->state == HID_BPF_ASYNC_STATE_UNSET) {
|
||||
elem->state = HID_BPF_ASYNC_STATE_INITIALIZING;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
bpf_spin_unlock(&elem->lock);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
@@ -175,18 +174,19 @@ static int hid_bpf_async_delayed_call(struct hid_bpf_ctx *hctx, u64 milliseconds
|
||||
if (!elem)
|
||||
return -EINVAL;
|
||||
|
||||
bpf_spin_lock(&elem->lock);
|
||||
/* The wq must be:
|
||||
* - HID_BPF_ASYNC_STATE_INITIALIZED -> it's been initialized and ready to be called
|
||||
* - HID_BPF_ASYNC_STATE_RUNNING -> possible re-entry from the wq itself
|
||||
*/
|
||||
if (elem->state != HID_BPF_ASYNC_STATE_INITIALIZED &&
|
||||
elem->state != HID_BPF_ASYNC_STATE_RUNNING) {
|
||||
bpf_spin_unlock(&elem->lock);
|
||||
return -EINVAL;
|
||||
{
|
||||
guard(bpf_spin)(&elem->lock);
|
||||
|
||||
/* The wq must be:
|
||||
* - HID_BPF_ASYNC_STATE_INITIALIZED -> it's been initialized and ready to be called
|
||||
* - HID_BPF_ASYNC_STATE_RUNNING -> possible re-entry from the wq itself
|
||||
*/
|
||||
if (elem->state != HID_BPF_ASYNC_STATE_INITIALIZED &&
|
||||
elem->state != HID_BPF_ASYNC_STATE_RUNNING)
|
||||
return -EINVAL;
|
||||
|
||||
elem->state = HID_BPF_ASYNC_STATE_STARTING;
|
||||
}
|
||||
elem->state = HID_BPF_ASYNC_STATE_STARTING;
|
||||
bpf_spin_unlock(&elem->lock);
|
||||
|
||||
elem->hid = hctx->hid->id;
|
||||
|
||||
|
||||
@@ -7,7 +7,18 @@
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
#include <linux/errno.h>
|
||||
#include "hid_report_descriptor_helpers.h"
|
||||
|
||||
/* Compiler attributes */
|
||||
#ifndef __packed
|
||||
#define __packed __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#ifndef __maybe_unused
|
||||
#define __maybe_unused __attribute__((__unused__))
|
||||
#endif
|
||||
|
||||
extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx,
|
||||
unsigned int offset,
|
||||
@@ -40,6 +51,86 @@ extern int bpf_wq_set_callback(struct bpf_wq *wq,
|
||||
#define HID_MAX_DESCRIPTOR_SIZE 4096
|
||||
#define HID_IGNORE_EVENT -1
|
||||
|
||||
/**
|
||||
* Use: _cleanup_(somefunction) struct foo *bar;
|
||||
*/
|
||||
#define _cleanup_(_x) __attribute__((cleanup(_x)))
|
||||
|
||||
/**
|
||||
* Use: _release_(foo) *bar;
|
||||
*
|
||||
* This requires foo_releasep() to be present, use DEFINE_RELEASE_CLEANUP_FUNC.
|
||||
*/
|
||||
#define _release_(_type) struct _type __attribute__((cleanup(_type##_releasep)))
|
||||
|
||||
/**
|
||||
* Define a cleanup function for the struct type foo with a matching
|
||||
* foo_release(). Use:
|
||||
* DEFINE_RELEASE_CLEANUP_FUNC(foo)
|
||||
* _unref_(foo) struct foo *bar;
|
||||
*/
|
||||
#define DEFINE_RELEASE_CLEANUP_FUNC(_type) \
|
||||
static inline void _type##_releasep(struct _type **_p) { \
|
||||
if (*_p) \
|
||||
_type##_release(*_p); \
|
||||
} \
|
||||
struct __useless_struct_to_allow_trailing_semicolon__
|
||||
|
||||
/* for being able to have a cleanup function */
|
||||
#define hid_bpf_ctx_release hid_bpf_release_context
|
||||
DEFINE_RELEASE_CLEANUP_FUNC(hid_bpf_ctx);
|
||||
|
||||
/*
|
||||
* Kernel-style guard macros adapted for BPF
|
||||
* Based on include/linux/cleanup.h from the Linux kernel
|
||||
*
|
||||
* These provide automatic lock/unlock using __attribute__((cleanup))
|
||||
* similar to how _release_() works for contexts.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DEFINE_GUARD(name, type, lock, unlock):
|
||||
* Define a guard for automatic lock/unlock using the same pattern as _release_()
|
||||
* @name: identifier for the guard (e.g., bpf_spin)
|
||||
* @type: lock variable type (e.g., struct bpf_spin_lock)
|
||||
* @lock: lock function name (e.g., bpf_spin_lock)
|
||||
* @unlock: unlock function name (e.g., bpf_spin_unlock)
|
||||
*
|
||||
* guard(name):
|
||||
* Declare and lock in one statement - lock held until end of scope
|
||||
*
|
||||
* Example:
|
||||
* DEFINE_GUARD(bpf_spin, struct bpf_spin_lock, bpf_spin_lock, bpf_spin_unlock)
|
||||
*
|
||||
* void foo(struct bpf_spin_lock *lock) {
|
||||
* guard(bpf_spin)(lock);
|
||||
* // lock held until end of scope
|
||||
* }
|
||||
*/
|
||||
|
||||
/* Guard helper struct - stores lock pointer for cleanup */
|
||||
#define DEFINE_GUARD(_name, _type, _lock, _unlock) \
|
||||
struct _name##_guard { \
|
||||
_type *lock; \
|
||||
}; \
|
||||
static inline void _name##_guard_cleanup(struct _name##_guard *g) { \
|
||||
if (g && g->lock) \
|
||||
_unlock(g->lock); \
|
||||
} \
|
||||
static inline struct _name##_guard _name##_guard_init(_type *l) { \
|
||||
if (l) \
|
||||
_lock(l); \
|
||||
return (struct _name##_guard){.lock = l}; \
|
||||
} \
|
||||
struct __useless_struct_to_allow_trailing_semicolon__
|
||||
|
||||
#define guard(_name) \
|
||||
struct _name##_guard COMBINE(guard, __LINE__) __attribute__((cleanup(_name##_guard_cleanup))) = \
|
||||
_name##_guard_init
|
||||
|
||||
/* Define BPF spinlock guard */
|
||||
DEFINE_GUARD(bpf_spin, struct bpf_spin_lock, bpf_spin_lock, bpf_spin_unlock);
|
||||
|
||||
/* extracted from <linux/input.h> */
|
||||
#define BUS_ANY 0x00
|
||||
#define BUS_PCI 0x01
|
||||
@@ -183,4 +274,234 @@ extern int bpf_wq_set_callback(struct bpf_wq *wq,
|
||||
_EXPAND(_ARG, __VA_ARGS__) \
|
||||
} _device_ids SEC(".hid_bpf_config")
|
||||
|
||||
|
||||
/* Equivalency macros for bpf_htons and friends which are
|
||||
* Big Endian only - HID needs little endian so these are the
|
||||
* corresponding macros for that. See bpf/bpf_endian.h
|
||||
*/
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define __hid_bpf_le16_to_cpu(x) (x)
|
||||
# define __hid_bpf_le32_to_cpu(x) (x)
|
||||
# define __hid_bpf_le64_to_cpu(x) (x)
|
||||
# define __hid_bpf_cpu_to_le16(x) (x)
|
||||
# define __hid_bpf_cpu_to_le32(x) (x)
|
||||
# define __hid_bpf_cpu_to_le64(x) (x)
|
||||
# define __hid_bpf_constant_le16_to_cpu(x) (x)
|
||||
# define __hid_bpf_constant_le32_to_cpu(x) (x)
|
||||
# define __hid_bpf_constant_le64_to_cpu(x) (x)
|
||||
# define __hid_bpf_constant_cpu_to_le16(x) (x)
|
||||
# define __hid_bpf_constant_cpu_to_le32(x) (x)
|
||||
# define __hid_bpf_constant_cpu_to_le64(x) (x)
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define __hid_bpf_le16_to_cpu(x) __builtin_bswap16(x)
|
||||
# define __hid_bpf_le32_to_cpu(x) __builtin_bswap32(x)
|
||||
# define __hid_bpf_le64_to_cpu(x) __builtin_bswap64(x)
|
||||
# define __hid_bpf_cpu_to_le16(x) __builtin_bswap16(x)
|
||||
# define __hid_bpf_cpu_to_le32(x) __builtin_bswap32(x)
|
||||
# define __hid_bpf_cpu_to_le64(x) __builtin_bswap64(x)
|
||||
# define __hid_bpf_constant_le16_to_cpu(x) __bpf_swab16(x)
|
||||
# define __hid_bpf_constant_le32_to_cpu(x) __bpf_swab32(x)
|
||||
# define __hid_bpf_constant_le64_to_cpu(x) __bpf_swab64(x)
|
||||
# define __hid_bpf_constant_cpu_to_le16(x) __bpf_swab16(x)
|
||||
# define __hid_bpf_constant_cpu_to_le32(x) __bpf_swab32(x)
|
||||
# define __hid_bpf_constant_cpu_to_le64(x) __bpf_swab64(x)
|
||||
#else
|
||||
# error "Invalid __BYTE_ORDER__"
|
||||
#endif
|
||||
|
||||
#define hid_bpf_le16_to_cpu(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__hid_bpf_constant_le16_to_cpu(x) : __hid_bpf_le16_to_cpu(x))
|
||||
|
||||
#define hid_bpf_le32_to_cpu(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__hid_bpf_constant_le32_to_cpu(x) : __hid_bpf_le32_to_cpu(x))
|
||||
|
||||
#define hid_bpf_le64_to_cpu(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__hid_bpf_constant_le64_to_cpu(x) : __hid_bpf_le64_to_cpu(x))
|
||||
|
||||
#define hid_bpf_cpu_to_le16(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__hid_bpf_constant_cpu_to_le16(x) : __hid_bpf_cpu_to_le16(x))
|
||||
|
||||
#define hid_bpf_cpu_to_le32(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__hid_bpf_constant_cpu_to_le32(x) : __hid_bpf_cpu_to_le32(x))
|
||||
|
||||
#define hid_bpf_cpu_to_le64(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__hid_bpf_constant_cpu_to_le64(x) : __hid_bpf_cpu_to_le64(x))
|
||||
|
||||
#define hid_bpf_be16_to_cpu(x) bpf_ntohs(x)
|
||||
#define hid_bpf_be32_to_cpu(x) bpf_ntohl(x)
|
||||
#define hid_bpf_be64_to_cpu(x) bpf_be64_to_cpu(x)
|
||||
#define hid_bpf_cpu_to_be16(x) bpf_htons(x)
|
||||
#define hid_bpf_cpu_to_be32(x) bpf_htonl(x)
|
||||
#define hid_bpf_cpu_to_be64(x) bpf_cpu_to_be64(x)
|
||||
|
||||
/*
|
||||
* The following macros are helpers for exporting udev properties:
|
||||
*
|
||||
* EXPORT_UDEV_PROP(name, len) generates:
|
||||
* - a map with a single element UDEV_PROP_##name, of size len
|
||||
* - a const global declaration of that len: SIZEOF_##name
|
||||
*
|
||||
* udev_prop_ptr(name) retrieves the data pointer behind the map.
|
||||
*
|
||||
* UDEV_PROP_SPRINTF(name, fmt, ...) writes data into the udev property.
|
||||
*
|
||||
* Can be used as such:
|
||||
* EXPORT_UDEV_PROP(HID_FOO, 32);
|
||||
*
|
||||
* SEC("syscall")
|
||||
* int probe(struct hid_bpf_probe_args *ctx)
|
||||
* {
|
||||
* const char *foo = "foo";
|
||||
* UDEV_PROP_SPRINTF(HID_FOO, "%s", foo);
|
||||
*
|
||||
* return 0;
|
||||
* }
|
||||
*/
|
||||
#define EXPORT_UDEV_PROP(name, len) \
|
||||
const __u32 SIZEOF_##name = len; \
|
||||
struct COMBINE(udev_prop, __LINE__) { \
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY); \
|
||||
__uint(max_entries, 1); \
|
||||
__type(key, __u32); \
|
||||
__type(value, __u8[len]); \
|
||||
} UDEV_PROP_##name SEC(".maps");
|
||||
|
||||
#define udev_prop_ptr(name) \
|
||||
bpf_map_lookup_elem(&UDEV_PROP_##name, &(__u32){0})
|
||||
|
||||
#define UDEV_PROP_SPRINTF(name, fmt, ...) \
|
||||
BPF_SNPRINTF(udev_prop_ptr(name), SIZEOF_##name, fmt, ##__VA_ARGS__)
|
||||
|
||||
static inline __maybe_unused __u16 field_start_byte(struct hid_rdesc_field *field)
|
||||
{
|
||||
return field->bits_start / 8;
|
||||
}
|
||||
|
||||
static inline __maybe_unused __u16 field_end_byte(struct hid_rdesc_field *field)
|
||||
{
|
||||
if (!field->bits_end)
|
||||
return 0;
|
||||
|
||||
return (__u16)(field->bits_end - 1) / 8;
|
||||
}
|
||||
|
||||
static __maybe_unused __u32 extract_bits(__u8 *buffer, const size_t size, struct hid_rdesc_field *field)
|
||||
{
|
||||
__s32 nbits = field->bits_end - field->bits_start;
|
||||
__u32 start = field_start_byte(field);
|
||||
__u32 end = field_end_byte(field);
|
||||
__u8 base_shift = field->bits_start % 8;
|
||||
|
||||
if (nbits <= 0 || nbits > 32 || start >= size || end >= size)
|
||||
return 0;
|
||||
|
||||
/* Fast path for byte-aligned standard-sized reads */
|
||||
if (base_shift == 0) {
|
||||
/* 8-bit aligned read */
|
||||
if (nbits == 8 && start < size)
|
||||
return buffer[start];
|
||||
|
||||
/* 16-bit aligned read - use separate variables for verifier */
|
||||
if (nbits == 16) {
|
||||
__u32 off0 = start;
|
||||
__u32 off1 = start + 1;
|
||||
|
||||
if (off0 < size && off1 < size) {
|
||||
return buffer[off0] |
|
||||
((__u32)buffer[off1] << 8);
|
||||
}
|
||||
}
|
||||
|
||||
/* 32-bit aligned read - use separate variables for verifier */
|
||||
if (nbits == 32) {
|
||||
__u32 off0 = start;
|
||||
__u32 off1 = start + 1;
|
||||
__u32 off2 = start + 2;
|
||||
__u32 off3 = start + 3;
|
||||
|
||||
if (off0 < size && off1 < size &&
|
||||
off2 < size && off3 < size) {
|
||||
return buffer[off0] |
|
||||
((__u32)buffer[off1] << 8) |
|
||||
((__u32)buffer[off2] << 16) |
|
||||
((__u32)buffer[off3] << 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* General case: bit manipulation for unaligned or non-standard sizes */
|
||||
int mask = 0xffffffff >> (32 - nbits);
|
||||
__u64 value = 0;
|
||||
__u32 i;
|
||||
|
||||
bpf_for (i, start, end + 1) {
|
||||
value |= (__u64)buffer[i] << ((i - start) * 8);
|
||||
}
|
||||
|
||||
return (value >> base_shift) & mask;
|
||||
}
|
||||
|
||||
#define EXTRACT_BITS(buffer, field) extract_bits(buffer, sizeof(buffer), field)
|
||||
|
||||
/* Base macro for iterating over HID arrays with bounds checking.
|
||||
* Follows the bpf_for pattern from libbpf.
|
||||
*/
|
||||
#define __hid_bpf_for_each_array(array, num_elements, max_elements, var) \
|
||||
for ( \
|
||||
/* initialize and define destructor */ \
|
||||
struct bpf_iter_num ___it __attribute__((aligned(8), \
|
||||
cleanup(bpf_iter_num_destroy))), \
|
||||
/* ___p pointer is necessary to call bpf_iter_num_new() *once* */ \
|
||||
*___p __attribute__((unused)) = ( \
|
||||
/* always initialize iterator; if bounds fail, iterate 0 times */ \
|
||||
bpf_iter_num_new(&___it, 0, \
|
||||
(num_elements) > (max_elements) ? \
|
||||
0 : (num_elements)), \
|
||||
/* workaround for Clang bug */ \
|
||||
(void)bpf_iter_num_destroy, (void *)0); \
|
||||
({ \
|
||||
/* iteration step */ \
|
||||
int *___t = bpf_iter_num_next(&___it); \
|
||||
int ___i; \
|
||||
/* termination and bounds check, assign var */ \
|
||||
(___t && (___i = *___t, ___i >= 0 && ___i < (num_elements)) && \
|
||||
((num_elements) <= (max_elements)) && \
|
||||
(var = &(array)[___i], 1)); \
|
||||
}); \
|
||||
)
|
||||
|
||||
/* Iterate over input reports in a descriptor */
|
||||
#define hid_bpf_for_each_input_report(descriptor, report_var) \
|
||||
__hid_bpf_for_each_array((descriptor)->input_reports, \
|
||||
(descriptor)->num_input_reports, \
|
||||
HID_MAX_REPORTS, report_var)
|
||||
|
||||
/* Iterate over feature reports in a descriptor */
|
||||
#define hid_bpf_for_each_feature_report(descriptor, report_var) \
|
||||
__hid_bpf_for_each_array((descriptor)->feature_reports, \
|
||||
(descriptor)->num_feature_reports, \
|
||||
HID_MAX_REPORTS, report_var)
|
||||
|
||||
/* Iterate over output reports in a descriptor */
|
||||
#define hid_bpf_for_each_output_report(descriptor, report_var) \
|
||||
__hid_bpf_for_each_array((descriptor)->output_reports, \
|
||||
(descriptor)->num_output_reports, \
|
||||
HID_MAX_REPORTS, report_var)
|
||||
|
||||
/* Iterate over fields in a report */
|
||||
#define hid_bpf_for_each_field(report, field_var) \
|
||||
__hid_bpf_for_each_array((report)->fields, (report)->num_fields, \
|
||||
HID_MAX_FIELDS, field_var)
|
||||
|
||||
/* Iterate over collections in a field */
|
||||
#define hid_bpf_for_each_collection(field, collection_var) \
|
||||
__hid_bpf_for_each_array((field)->collections, (field)->num_collections, \
|
||||
HID_MAX_COLLECTIONS, collection_var)
|
||||
|
||||
#endif /* __HID_BPF_HELPERS_H */
|
||||
|
||||
80
drivers/hid/bpf/progs/hid_report_descriptor_helpers.h
Normal file
80
drivers/hid/bpf/progs/hid_report_descriptor_helpers.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2022 Benjamin Tissoires
|
||||
*/
|
||||
|
||||
#ifndef __HID_REPORT_DESCRIPTOR_HELPERS_H
|
||||
#define __HID_REPORT_DESCRIPTOR_HELPERS_H
|
||||
|
||||
#include "vmlinux.h"
|
||||
|
||||
/* Compiler attributes */
|
||||
#ifndef __packed
|
||||
#define __packed __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#ifndef __maybe_unused
|
||||
#define __maybe_unused __attribute__((__unused__))
|
||||
#endif
|
||||
|
||||
/* Report Descriptor Structures */
|
||||
#define HID_MAX_COLLECTIONS 32
|
||||
#define HID_MAX_FIELDS 64
|
||||
#define HID_MAX_REPORTS 16
|
||||
|
||||
enum hid_rdesc_field_type {
|
||||
HID_FIELD_VARIABLE = 0,
|
||||
HID_FIELD_ARRAY = 1,
|
||||
HID_FIELD_CONSTANT = 2,
|
||||
};
|
||||
|
||||
struct hid_rdesc_collection {
|
||||
__u16 usage_page;
|
||||
__u16 usage_id;
|
||||
__u8 collection_type;
|
||||
} __packed;
|
||||
|
||||
struct hid_rdesc_field {
|
||||
__u8 field_type; /* enum hid_rdesc_field_type */
|
||||
__u8 num_collections;
|
||||
__u16 bits_start;
|
||||
__u16 bits_end;
|
||||
__u16 usage_page;
|
||||
union {
|
||||
__u16 usage_id; /* For Variable fields */
|
||||
struct __packed { /* For Array fields */
|
||||
__u16 usage_minimum;
|
||||
__u16 usage_maximum;
|
||||
};
|
||||
};
|
||||
__s32 logical_minimum;
|
||||
__s32 logical_maximum;
|
||||
struct {
|
||||
__u8 is_relative:1; /* Data is relative to previous value */
|
||||
__u8 wraps:1; /* Value wraps around (e.g., rotary encoder) */
|
||||
__u8 is_nonlinear:1; /* Non-linear relationship between logical/physical */
|
||||
__u8 has_no_preferred_state:1; /* No rest position (e.g., free-floating joystick) */
|
||||
__u8 has_null_state:1; /* Can report null/no-data values */
|
||||
__u8 is_volatile:1; /* Volatile (Output/Feature) - NOT POPULATED, always 0 */
|
||||
__u8 is_buffered_bytes:1; /* Fixed-size byte stream vs bitfield */
|
||||
__u8 reserved:1; /* Reserved for future use */
|
||||
} flags;
|
||||
struct hid_rdesc_collection collections[HID_MAX_COLLECTIONS];
|
||||
} __packed;
|
||||
|
||||
struct hid_rdesc_report {
|
||||
__u8 report_id; /* 0 means no report ID */
|
||||
__u16 size_in_bits;
|
||||
__u8 num_fields;
|
||||
struct hid_rdesc_field fields[HID_MAX_FIELDS];
|
||||
} __packed;
|
||||
|
||||
struct hid_rdesc_descriptor {
|
||||
__u8 num_input_reports;
|
||||
__u8 num_output_reports;
|
||||
__u8 num_feature_reports;
|
||||
struct hid_rdesc_report input_reports[HID_MAX_REPORTS];
|
||||
struct hid_rdesc_report output_reports[HID_MAX_REPORTS];
|
||||
struct hid_rdesc_report feature_reports[HID_MAX_REPORTS];
|
||||
} __packed;
|
||||
|
||||
#endif /* __HID_REPORT_DESCRIPTOR_HELPERS_H */
|
||||
2810
drivers/hid/bpf/progs/hid_usages.h
Normal file
2810
drivers/hid/bpf/progs/hid_usages.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -437,6 +437,9 @@ static int alps_raw_event(struct hid_device *hdev,
|
||||
int ret = 0;
|
||||
struct alps_dev *hdata = hid_get_drvdata(hdev);
|
||||
|
||||
if (!(hdev->claimed & HID_CLAIMED_INPUT) || !hdata->input)
|
||||
return 0;
|
||||
|
||||
switch (hdev->product) {
|
||||
case HID_PRODUCT_ID_T4_BTNLESS:
|
||||
ret = t4_raw_event(hdata, data, size);
|
||||
|
||||
@@ -623,17 +623,19 @@ static int apple_fetch_battery(struct hid_device *hdev)
|
||||
struct apple_sc *asc = hid_get_drvdata(hdev);
|
||||
struct hid_report_enum *report_enum;
|
||||
struct hid_report *report;
|
||||
struct hid_battery *bat;
|
||||
|
||||
if (!(asc->quirks & APPLE_RDESC_BATTERY) || !hdev->battery)
|
||||
bat = hid_get_battery(hdev);
|
||||
if (!(asc->quirks & APPLE_RDESC_BATTERY) || !bat)
|
||||
return -1;
|
||||
|
||||
report_enum = &hdev->report_enum[hdev->battery_report_type];
|
||||
report = report_enum->report_id_hash[hdev->battery_report_id];
|
||||
report_enum = &hdev->report_enum[bat->report_type];
|
||||
report = report_enum->report_id_hash[bat->report_id];
|
||||
|
||||
if (!report || report->maxfield < 1)
|
||||
return -1;
|
||||
|
||||
if (hdev->battery_capacity == hdev->battery_max)
|
||||
if (bat->capacity == bat->max)
|
||||
return -1;
|
||||
|
||||
hid_hw_request(hdev, report, HID_REQ_GET_REPORT);
|
||||
@@ -858,6 +860,7 @@ static int apple_backlight_init(struct hid_device *hdev)
|
||||
asc->backlight->cdev.name = "apple::kbd_backlight";
|
||||
asc->backlight->cdev.max_brightness = rep->backlight_on_max;
|
||||
asc->backlight->cdev.brightness_set_blocking = apple_backlight_led_set;
|
||||
asc->backlight->cdev.flags = LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = apple_backlight_set(hdev, 0, 0);
|
||||
if (ret < 0) {
|
||||
@@ -926,6 +929,7 @@ static int apple_magic_backlight_init(struct hid_device *hdev)
|
||||
backlight->cdev.name = ":white:" LED_FUNCTION_KBD_BACKLIGHT;
|
||||
backlight->cdev.max_brightness = backlight->brightness->field[0]->logical_maximum;
|
||||
backlight->cdev.brightness_set_blocking = apple_magic_backlight_led_set;
|
||||
backlight->cdev.flags = LED_CORE_SUSPENDRESUME;
|
||||
|
||||
apple_magic_backlight_set(backlight, 0, 0);
|
||||
|
||||
|
||||
@@ -20,10 +20,8 @@
|
||||
* Copyright (c) 2016 Frederik Wenigwieser <frederik.wenigwieser@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
@@ -101,7 +99,6 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
||||
#define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12)
|
||||
#define QUIRK_ROG_ALLY_XPAD BIT(13)
|
||||
#define QUIRK_HID_FN_LOCK BIT(14)
|
||||
#define QUIRK_ROG_NKEY_ID1ID2_INIT BIT(15)
|
||||
|
||||
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
|
||||
QUIRK_NO_INIT_REPORTS | \
|
||||
@@ -208,6 +205,12 @@ static const struct asus_touchpad_info medion_e1239t_tp = {
|
||||
.report_size = 32 /* 2 byte header + 5 * 5 + 5 byte footer */,
|
||||
};
|
||||
|
||||
static const u8 asus_report_id_init[] = {
|
||||
FEATURE_KBD_REPORT_ID,
|
||||
FEATURE_KBD_LED_REPORT_ID1,
|
||||
FEATURE_KBD_LED_REPORT_ID2
|
||||
};
|
||||
|
||||
static void asus_report_contact_down(struct asus_drvdata *drvdat,
|
||||
int toolType, u8 *data)
|
||||
{
|
||||
@@ -354,7 +357,7 @@ static int asus_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
|
||||
|
||||
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR &&
|
||||
(usage->hid & HID_USAGE) != 0x00 &&
|
||||
(usage->hid & HID_USAGE) != 0xff && !usage->type) {
|
||||
@@ -443,21 +446,18 @@ static int asus_raw_event(struct hid_device *hdev,
|
||||
/*
|
||||
* G713 and G733 send these codes on some keypresses, depending on
|
||||
* the key pressed it can trigger a shutdown event if not caught.
|
||||
*/
|
||||
if (data[0] == 0x02 && data[1] == 0x30) {
|
||||
*/
|
||||
if (data[0] == 0x02 && data[1] == 0x30)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (drvdata->quirks & QUIRK_ROG_CLAYMORE_II_KEYBOARD) {
|
||||
/*
|
||||
* CLAYMORE II keyboard sends this packet when it goes to sleep
|
||||
* this causes the whole system to go into suspend.
|
||||
*/
|
||||
|
||||
if(size == 2 && data[0] == 0x02 && data[1] == 0x00) {
|
||||
*/
|
||||
if (size == 2 && data[0] == 0x02 && data[1] == 0x00)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -465,23 +465,16 @@ static int asus_raw_event(struct hid_device *hdev,
|
||||
|
||||
static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t buf_size)
|
||||
{
|
||||
unsigned char *dmabuf;
|
||||
int ret;
|
||||
|
||||
dmabuf = kmemdup(buf, buf_size, GFP_KERNEL);
|
||||
u8 *dmabuf __free(kfree) = kmemdup(buf, buf_size, GFP_KERNEL);
|
||||
if (!dmabuf)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* The report ID should be set from the incoming buffer due to LED and key
|
||||
* interfaces having different pages
|
||||
*/
|
||||
ret = hid_hw_raw_request(hdev, buf[0], dmabuf,
|
||||
buf_size, HID_FEATURE_REPORT,
|
||||
HID_REQ_SET_REPORT);
|
||||
kfree(dmabuf);
|
||||
|
||||
return ret;
|
||||
*/
|
||||
return hid_hw_raw_request(hdev, buf[0], dmabuf, buf_size, HID_FEATURE_REPORT,
|
||||
HID_REQ_SET_REPORT);
|
||||
}
|
||||
|
||||
static int asus_kbd_init(struct hid_device *hdev, u8 report_id)
|
||||
@@ -722,6 +715,21 @@ static void validate_mcu_fw_version(struct hid_device *hdev, int idProduct)
|
||||
}
|
||||
}
|
||||
|
||||
static bool asus_has_report_id(struct hid_device *hdev, u16 report_id)
|
||||
{
|
||||
struct hid_report *report;
|
||||
int t;
|
||||
|
||||
for (t = HID_INPUT_REPORT; t <= HID_FEATURE_REPORT; t++) {
|
||||
list_for_each_entry(report, &hdev->report_enum[t].report_list, list) {
|
||||
if (report->id == report_id)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int asus_kbd_register_leds(struct hid_device *hdev)
|
||||
{
|
||||
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
@@ -730,10 +738,6 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
|
||||
unsigned char kbd_func;
|
||||
int ret;
|
||||
|
||||
ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Get keyboard functions */
|
||||
ret = asus_kbd_get_functions(hdev, &kbd_func, FEATURE_KBD_REPORT_ID);
|
||||
if (ret < 0)
|
||||
@@ -743,11 +747,6 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
|
||||
if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
|
||||
return -ENODEV;
|
||||
|
||||
if (drvdata->quirks & QUIRK_ROG_NKEY_ID1ID2_INIT) {
|
||||
asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1);
|
||||
asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2);
|
||||
}
|
||||
|
||||
if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) {
|
||||
ret = asus_kbd_disable_oobe(hdev);
|
||||
if (ret < 0)
|
||||
@@ -1163,7 +1162,8 @@ static int asus_start_multitouch(struct hid_device *hdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused asus_resume(struct hid_device *hdev) {
|
||||
static int __maybe_unused asus_resume(struct hid_device *hdev)
|
||||
{
|
||||
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
int ret = 0;
|
||||
|
||||
@@ -1294,8 +1294,19 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (int r = 0; r < ARRAY_SIZE(asus_report_id_init); r++) {
|
||||
if (asus_has_report_id(hdev, asus_report_id_init[r])) {
|
||||
ret = asus_kbd_init(hdev, asus_report_id_init[r]);
|
||||
if (ret < 0)
|
||||
hid_warn(hdev, "Failed to initialize 0x%x: %d.\n",
|
||||
asus_report_id_init[r], ret);
|
||||
}
|
||||
}
|
||||
|
||||
/* Laptops keyboard backlight is always at 0x5a */
|
||||
if (is_vendor && (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) &&
|
||||
asus_kbd_register_leds(hdev))
|
||||
(asus_has_report_id(hdev, FEATURE_KBD_REPORT_ID)) &&
|
||||
(asus_kbd_register_leds(hdev)))
|
||||
hid_warn(hdev, "Failed to initialize backlight.\n");
|
||||
|
||||
/*
|
||||
@@ -1311,22 +1322,17 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
* were freed during registration due to no usages being mapped,
|
||||
* leaving drvdata->input pointing to freed memory.
|
||||
*/
|
||||
if (!drvdata->input || !(hdev->claimed & HID_CLAIMED_INPUT)) {
|
||||
hid_err(hdev, "Asus input not registered\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_stop_hw;
|
||||
}
|
||||
if (drvdata->input && (hdev->claimed & HID_CLAIMED_INPUT)) {
|
||||
if (drvdata->tp)
|
||||
drvdata->input->name = "Asus TouchPad";
|
||||
else
|
||||
drvdata->input->name = "Asus Keyboard";
|
||||
|
||||
if (drvdata->tp) {
|
||||
drvdata->input->name = "Asus TouchPad";
|
||||
} else {
|
||||
drvdata->input->name = "Asus Keyboard";
|
||||
}
|
||||
|
||||
if (drvdata->tp) {
|
||||
ret = asus_start_multitouch(hdev);
|
||||
if (ret)
|
||||
goto err_stop_hw;
|
||||
if (drvdata->tp) {
|
||||
ret = asus_start_multitouch(hdev);
|
||||
if (ret)
|
||||
goto err_stop_hw;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1484,10 +1490,10 @@ static const struct hid_device_id asus_devices[] = {
|
||||
QUIRK_USE_KBD_BACKLIGHT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD),
|
||||
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_ID1ID2_INIT },
|
||||
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2),
|
||||
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_HID_FN_LOCK | QUIRK_ROG_NKEY_ID1ID2_INIT },
|
||||
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_HID_FN_LOCK },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR),
|
||||
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
|
||||
|
||||
@@ -71,6 +71,9 @@ static u32 s32ton(__s32 value, unsigned int n)
|
||||
if (!value || !n)
|
||||
return 0;
|
||||
|
||||
if (n > 32)
|
||||
n = 32;
|
||||
|
||||
a = value >> (n - 1);
|
||||
if (a && a != -1)
|
||||
return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1;
|
||||
@@ -924,7 +927,6 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
|
||||
*/
|
||||
static int hid_scan_report(struct hid_device *hid)
|
||||
{
|
||||
struct hid_parser *parser;
|
||||
struct hid_item item;
|
||||
const __u8 *start = hid->dev_rdesc;
|
||||
const __u8 *end = start + hid->dev_rsize;
|
||||
@@ -936,7 +938,7 @@ static int hid_scan_report(struct hid_device *hid)
|
||||
hid_parser_reserved
|
||||
};
|
||||
|
||||
parser = vzalloc(sizeof(struct hid_parser));
|
||||
struct hid_parser *parser __free(kvfree) = vzalloc(sizeof(*parser));
|
||||
if (!parser)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -987,7 +989,6 @@ static int hid_scan_report(struct hid_device *hid)
|
||||
}
|
||||
|
||||
kfree(parser->collection_stack);
|
||||
vfree(parser);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1244,6 +1245,90 @@ void hid_setup_resolution_multiplier(struct hid_device *hid)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_setup_resolution_multiplier);
|
||||
|
||||
static int hid_parse_collections(struct hid_device *device)
|
||||
{
|
||||
struct hid_item item;
|
||||
const u8 *start = device->rdesc;
|
||||
const u8 *end = start + device->rsize;
|
||||
const u8 *next;
|
||||
int ret;
|
||||
static typeof(hid_parser_main) (* const dispatch_type[]) = {
|
||||
hid_parser_main,
|
||||
hid_parser_global,
|
||||
hid_parser_local,
|
||||
hid_parser_reserved
|
||||
};
|
||||
|
||||
struct hid_parser *parser __free(kvfree) = vzalloc(sizeof(*parser));
|
||||
if (!parser)
|
||||
return -ENOMEM;
|
||||
|
||||
parser->device = device;
|
||||
|
||||
device->collection = kzalloc_objs(*device->collection,
|
||||
HID_DEFAULT_NUM_COLLECTIONS);
|
||||
if (!device->collection)
|
||||
return -ENOMEM;
|
||||
|
||||
device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
|
||||
for (unsigned int i = 0; i < HID_DEFAULT_NUM_COLLECTIONS; i++)
|
||||
device->collection[i].parent_idx = -1;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (start == end) {
|
||||
hid_err(device, "rejecting 0-sized report descriptor\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
while ((next = fetch_item(start, end, &item)) != NULL) {
|
||||
start = next;
|
||||
|
||||
if (item.format != HID_ITEM_FORMAT_SHORT) {
|
||||
hid_err(device, "unexpected long global item\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dispatch_type[item.type](parser, &item)) {
|
||||
hid_err(device, "item %u %u %u %u parsing failed\n",
|
||||
item.format,
|
||||
(unsigned int)item.size,
|
||||
(unsigned int)item.type,
|
||||
(unsigned int)item.tag);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (start != end) {
|
||||
hid_err(device, "item fetching failed at offset %u/%u\n",
|
||||
device->rsize - (unsigned int)(end - start),
|
||||
device->rsize);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (parser->collection_stack_ptr) {
|
||||
hid_err(device, "unbalanced collection at end of report description\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (parser->local.delimiter_depth) {
|
||||
hid_err(device, "unbalanced delimiter at end of report description\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* fetch initial values in case the device's
|
||||
* default multiplier isn't the recommended 1
|
||||
*/
|
||||
hid_setup_resolution_multiplier(device);
|
||||
|
||||
device->status |= HID_STAT_PARSED;
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
kfree(parser->collection_stack);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* hid_open_report - open a driver-specific device report
|
||||
*
|
||||
@@ -1258,21 +1343,9 @@ EXPORT_SYMBOL_GPL(hid_setup_resolution_multiplier);
|
||||
*/
|
||||
int hid_open_report(struct hid_device *device)
|
||||
{
|
||||
struct hid_parser *parser;
|
||||
struct hid_item item;
|
||||
unsigned int size;
|
||||
const __u8 *start;
|
||||
const __u8 *end;
|
||||
const __u8 *next;
|
||||
int ret;
|
||||
int i;
|
||||
static int (*dispatch_type[])(struct hid_parser *parser,
|
||||
struct hid_item *item) = {
|
||||
hid_parser_main,
|
||||
hid_parser_global,
|
||||
hid_parser_local,
|
||||
hid_parser_reserved
|
||||
};
|
||||
const u8 *start;
|
||||
int error;
|
||||
|
||||
if (WARN_ON(device->status & HID_STAT_PARSED))
|
||||
return -EBUSY;
|
||||
@@ -1288,9 +1361,9 @@ int hid_open_report(struct hid_device *device)
|
||||
* on a copy of our report descriptor so it can
|
||||
* change it.
|
||||
*/
|
||||
__u8 *buf = kmemdup(start, size, GFP_KERNEL);
|
||||
u8 *buf __free(kfree) = kmemdup(start, size, GFP_KERNEL);
|
||||
|
||||
if (buf == NULL)
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
start = device->driver->report_fixup(device, buf, &size);
|
||||
@@ -1301,82 +1374,20 @@ int hid_open_report(struct hid_device *device)
|
||||
* needs to be cleaned up or not at the end.
|
||||
*/
|
||||
start = kmemdup(start, size, GFP_KERNEL);
|
||||
kfree(buf);
|
||||
if (start == NULL)
|
||||
if (!start)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
device->rdesc = start;
|
||||
device->rsize = size;
|
||||
|
||||
parser = vzalloc(sizeof(struct hid_parser));
|
||||
if (!parser) {
|
||||
ret = -ENOMEM;
|
||||
goto alloc_err;
|
||||
error = hid_parse_collections(device);
|
||||
if (error) {
|
||||
hid_close_report(device);
|
||||
return error;
|
||||
}
|
||||
|
||||
parser->device = device;
|
||||
|
||||
end = start + size;
|
||||
|
||||
device->collection = kzalloc_objs(struct hid_collection,
|
||||
HID_DEFAULT_NUM_COLLECTIONS);
|
||||
if (!device->collection) {
|
||||
ret = -ENOMEM;
|
||||
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) {
|
||||
start = next;
|
||||
|
||||
if (item.format != HID_ITEM_FORMAT_SHORT) {
|
||||
hid_err(device, "unexpected long global item\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (dispatch_type[item.type](parser, &item)) {
|
||||
hid_err(device, "item %u %u %u %u parsing failed\n",
|
||||
item.format, (unsigned)item.size,
|
||||
(unsigned)item.type, (unsigned)item.tag);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (start == end) {
|
||||
if (parser->collection_stack_ptr) {
|
||||
hid_err(device, "unbalanced collection at end of report description\n");
|
||||
goto err;
|
||||
}
|
||||
if (parser->local.delimiter_depth) {
|
||||
hid_err(device, "unbalanced delimiter at end of report description\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* fetch initial values in case the device's
|
||||
* default multiplier isn't the recommended 1
|
||||
*/
|
||||
hid_setup_resolution_multiplier(device);
|
||||
|
||||
kfree(parser->collection_stack);
|
||||
vfree(parser);
|
||||
device->status |= HID_STAT_PARSED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
hid_err(device, "item fetching failed at offset %u/%u\n",
|
||||
size - (unsigned int)(end - start), size);
|
||||
err:
|
||||
kfree(parser->collection_stack);
|
||||
alloc_err:
|
||||
vfree(parser);
|
||||
hid_close_report(device);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_open_report);
|
||||
|
||||
@@ -1989,11 +2000,11 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
|
||||
int __hid_request(struct hid_device *hid, struct hid_report *report,
|
||||
enum hid_class_request reqtype)
|
||||
{
|
||||
char *buf, *data_buf;
|
||||
u8 *data_buf;
|
||||
int ret;
|
||||
u32 len;
|
||||
|
||||
buf = hid_alloc_report_buf(report, GFP_KERNEL);
|
||||
u8 *buf __free(kfree) = hid_alloc_report_buf(report, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -2012,17 +2023,13 @@ int __hid_request(struct hid_device *hid, struct hid_report *report,
|
||||
ret = hid_hw_raw_request(hid, report->id, buf, len, report->type, reqtype);
|
||||
if (ret < 0) {
|
||||
dbg_hid("unable to complete request: %d\n", ret);
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (reqtype == HID_REQ_GET_REPORT)
|
||||
hid_input_report(hid, report->type, buf, ret, 0);
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__hid_request);
|
||||
|
||||
@@ -2888,6 +2895,11 @@ static int hid_uevent(const struct device *dev, struct kobj_uevent_env *env)
|
||||
if (add_uevent_var(env, "MODALIAS=hid:b%04Xg%04Xv%08Xp%08X",
|
||||
hdev->bus, hdev->group, hdev->vendor, hdev->product))
|
||||
return -ENOMEM;
|
||||
if (hdev->firmware_version) {
|
||||
if (add_uevent_var(env, "HID_FIRMWARE_VERSION=0x%04llX",
|
||||
hdev->firmware_version))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2991,6 +3003,10 @@ struct hid_device *hid_allocate_device(void)
|
||||
mutex_init(&hdev->ll_open_lock);
|
||||
kref_init(&hdev->ref);
|
||||
|
||||
#ifdef CONFIG_HID_BATTERY_STRENGTH
|
||||
INIT_LIST_HEAD(&hdev->batteries);
|
||||
#endif
|
||||
|
||||
ret = hid_bpf_device_init(hdev);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
82
drivers/hid/hid-huawei.c
Normal file
82
drivers/hid/hid-huawei.c
Normal file
@@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* HID driver for some huawei "special" devices
|
||||
*
|
||||
* Copyright (c) 2026 Miao Li <limiao@kylinos.cn>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
static const __u8 huawei_cd30_kbd_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop) */
|
||||
0x09, 0x80, /* Usage (System Control) */
|
||||
0xa1, 0x01, /* Collection (Application) */
|
||||
0x85, 0x01, /* Report ID (1) */
|
||||
0x19, 0x81, /* Usage Minimum (System Power Down) */
|
||||
0x29, 0x83, /* Usage Maximum (System Wake Up) */
|
||||
0x15, 0x00, /* Logical Minimum (0) */
|
||||
0x25, 0x01, /* Logical Maximum (1) */
|
||||
0x75, 0x01, /* Report Size (1 bit) */
|
||||
0x95, 0x03, /* Report Count (3) */
|
||||
0x81, 0x02, /* Input (Data,Var,Abs) */
|
||||
0x95, 0x05, /* Report Count (5) */
|
||||
0x81, 0x01, /* Input (Cnst,Ary,Abs) */
|
||||
0xc0, /* End Collection */
|
||||
0x05, 0x0c, /* Usage Page (Consumer) */
|
||||
0x09, 0x01, /* Usage (Consumer Control) */
|
||||
0xa1, 0x01, /* Collection (Application) */
|
||||
0x85, 0x02, /* Report ID (2) */
|
||||
0x19, 0x00, /* Usage Minimum (0) */
|
||||
0x2a, 0x3c, 0x02, /* Usage Maximum (0x023C) */
|
||||
0x15, 0x00, /* Logical Minimum (0) */
|
||||
0x26, 0x3c, 0x02, /* Logical Maximum (0x023C) */
|
||||
0x95, 0x01, /* Report Count (1) */
|
||||
0x75, 0x10, /* Report Size (16 bits) */
|
||||
0x81, 0x00, /* Input (Data,Ary,Abs) */
|
||||
0xc0 /* End Collection */
|
||||
};
|
||||
|
||||
static const __u8 *huawei_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_HUAWEI_CD30KBD:
|
||||
if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
|
||||
if (*rsize != sizeof(huawei_cd30_kbd_rdesc_fixed) ||
|
||||
memcmp(huawei_cd30_kbd_rdesc_fixed, rdesc,
|
||||
sizeof(huawei_cd30_kbd_rdesc_fixed)) != 0) {
|
||||
hid_info(hdev, "Replacing Huawei cd30 keyboard report descriptor.\n");
|
||||
*rsize = sizeof(huawei_cd30_kbd_rdesc_fixed);
|
||||
return huawei_cd30_kbd_rdesc_fixed;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static const struct hid_device_id huawei_devices[] = {
|
||||
/* HUAWEI cd30 keyboard */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HUAWEI, USB_DEVICE_ID_HUAWEI_CD30KBD)},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, huawei_devices);
|
||||
|
||||
static struct hid_driver huawei_driver = {
|
||||
.name = "huawei",
|
||||
.id_table = huawei_devices,
|
||||
.report_fixup = huawei_report_fixup,
|
||||
};
|
||||
module_hid_driver(huawei_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Miao Li <limiao@kylinos.cn>");
|
||||
MODULE_DESCRIPTION("HID driver for some huawei \"special\" devices");
|
||||
@@ -667,6 +667,18 @@
|
||||
#define USB_DEVICE_ID_UGCI_FLYING 0x0020
|
||||
#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
|
||||
|
||||
#define USB_VENDOR_ID_HARMONIX 0x1bad
|
||||
#define USB_DEVICE_ID_HARMONIX_WII_RB1_GUITAR 0x0004
|
||||
#define USB_DEVICE_ID_HARMONIX_WII_RB2_GUITAR 0x3010
|
||||
#define USB_DEVICE_ID_HARMONIX_WII_RB1_DRUMS 0x0005
|
||||
#define USB_DEVICE_ID_HARMONIX_WII_RB2_DRUMS 0x3110
|
||||
#define USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_DRUMS_MODE 0x3138
|
||||
#define USB_DEVICE_ID_HARMONIX_WII_RB3_MUSTANG_GUITAR 0x3430
|
||||
#define USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_MUSTANG_MODE 0x3438
|
||||
#define USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_SQUIER_MODE 0x3538
|
||||
#define USB_DEVICE_ID_HARMONIX_WII_RB3_KEYBOARD 0x3330
|
||||
#define USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_KEYBOARD_MODE 0x3338
|
||||
|
||||
#define USB_VENDOR_ID_HP 0x03f0
|
||||
#define USB_PRODUCT_ID_HP_ELITE_PRESENTER_MOUSE_464A 0x464a
|
||||
#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A 0x0a4a
|
||||
@@ -742,6 +754,10 @@
|
||||
#define USB_DEVICE_ID_ITE8595 0x8595
|
||||
#define USB_DEVICE_ID_ITE_MEDION_E1239T 0xce50
|
||||
|
||||
#define USB_VENDOR_ID_QHE 0x1a86
|
||||
#define USB_DEVICE_ID_LENOVO_LEGION_GO_S_XINPUT 0xe310
|
||||
#define USB_DEVICE_ID_LENOVO_LEGION_GO_S_DINPUT 0xe311
|
||||
|
||||
#define USB_VENDOR_ID_JABRA 0x0b0e
|
||||
#define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412
|
||||
#define USB_DEVICE_ID_JABRA_SPEAK_510 0x0420
|
||||
@@ -861,7 +877,10 @@
|
||||
#define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_602E 0x602e
|
||||
#define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_6093 0x6093
|
||||
#define USB_DEVICE_ID_LENOVO_LEGION_GO_DUAL_DINPUT 0x6184
|
||||
#define USB_DEVICE_ID_LENOVO_LEGION_GO2_XINPUT 0x61eb
|
||||
#define USB_DEVICE_ID_LENOVO_LEGION_GO2_DINPUT 0x61ec
|
||||
#define USB_DEVICE_ID_LENOVO_LEGION_GO2_DUAL_DINPUT 0x61ed
|
||||
#define USB_DEVICE_ID_LENOVO_LEGION_GO2_FPS 0x61ee
|
||||
|
||||
#define USB_VENDOR_ID_LETSKETCH 0x6161
|
||||
#define USB_DEVICE_ID_WP9620N 0x4d15
|
||||
@@ -1301,8 +1320,18 @@
|
||||
#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000
|
||||
|
||||
#define USB_VENDOR_ID_SONY_RHYTHM 0x12ba
|
||||
#define USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE 0x074b
|
||||
#define USB_DEVICE_ID_SONY_PS3_GUITAR_DONGLE 0x0100
|
||||
#define USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE 0x074b
|
||||
#define USB_DEVICE_ID_SONY_PS3_GH_GUITAR 0x0100
|
||||
#define USB_DEVICE_ID_SONY_PS3_GH_DRUMS 0x0120
|
||||
#define USB_DEVICE_ID_SONY_PS3_DJH_TURNTABLE 0x0140
|
||||
#define USB_DEVICE_ID_SONY_PS3_RB_GUITAR 0x0200
|
||||
#define USB_DEVICE_ID_SONY_PS3_RB_DRUMS 0x0210
|
||||
#define USB_DEVICE_ID_SONY_PS3_RB3_MPA_DRUMS_MODE 0x0218
|
||||
#define USB_DEVICE_ID_SONY_PS3_RB3_MUSTANG_GUITAR 0x2430
|
||||
#define USB_DEVICE_ID_SONY_PS3_RB3_MPA_MUSTANG_MODE 0x2438
|
||||
#define USB_DEVICE_ID_SONY_PS3_RB3_MPA_SQUIER_MODE 0x2538
|
||||
#define USB_DEVICE_ID_SONY_PS3_RB3_KEYBOARD 0x2330
|
||||
#define USB_DEVICE_ID_SONY_PS3_RB3_MPA_KEYBOARD_MODE 0x2338
|
||||
|
||||
#define USB_VENDOR_ID_SINO_LITE 0x1345
|
||||
#define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008
|
||||
@@ -1587,4 +1616,7 @@
|
||||
#define USB_VENDOR_ID_JIELI_SDK_DEFAULT 0x4c4a
|
||||
#define USB_DEVICE_ID_JIELI_SDK_4155 0x4155
|
||||
|
||||
#define USB_VENDOR_ID_HUAWEI 0x12d1
|
||||
#define USB_DEVICE_ID_HUAWEI_CD30KBD 0x109b
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,54 +9,59 @@
|
||||
|
||||
static void hid_test_input_update_battery_charge_status(struct kunit *test)
|
||||
{
|
||||
struct hid_device *dev;
|
||||
struct hid_battery *bat;
|
||||
bool handled;
|
||||
|
||||
dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
|
||||
bat = kunit_kzalloc(test, sizeof(*bat), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bat);
|
||||
|
||||
handled = hidinput_update_battery_charge_status(dev, HID_DG_HEIGHT, 0);
|
||||
handled = hidinput_update_battery_charge_status(bat, HID_DG_HEIGHT, 0);
|
||||
KUNIT_EXPECT_FALSE(test, handled);
|
||||
KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_UNKNOWN);
|
||||
KUNIT_EXPECT_EQ(test, bat->charge_status, POWER_SUPPLY_STATUS_UNKNOWN);
|
||||
|
||||
handled = hidinput_update_battery_charge_status(dev, HID_BAT_CHARGING, 0);
|
||||
handled = hidinput_update_battery_charge_status(bat, HID_BAT_CHARGING, 0);
|
||||
KUNIT_EXPECT_TRUE(test, handled);
|
||||
KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_DISCHARGING);
|
||||
KUNIT_EXPECT_EQ(test, bat->charge_status, POWER_SUPPLY_STATUS_DISCHARGING);
|
||||
|
||||
handled = hidinput_update_battery_charge_status(dev, HID_BAT_CHARGING, 1);
|
||||
handled = hidinput_update_battery_charge_status(bat, HID_BAT_CHARGING, 1);
|
||||
KUNIT_EXPECT_TRUE(test, handled);
|
||||
KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_CHARGING);
|
||||
KUNIT_EXPECT_EQ(test, bat->charge_status, POWER_SUPPLY_STATUS_CHARGING);
|
||||
}
|
||||
|
||||
static void hid_test_input_get_battery_property(struct kunit *test)
|
||||
{
|
||||
struct power_supply *psy;
|
||||
struct hid_battery *bat;
|
||||
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;
|
||||
|
||||
bat = kunit_kzalloc(test, sizeof(*bat), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bat);
|
||||
bat->dev = dev;
|
||||
bat->avoid_query = true;
|
||||
|
||||
psy = kunit_kzalloc(test, sizeof(*psy), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, psy);
|
||||
psy->drv_data = dev;
|
||||
psy->drv_data = bat;
|
||||
|
||||
dev->battery_status = HID_BATTERY_UNKNOWN;
|
||||
dev->battery_charge_status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
bat->status = HID_BATTERY_UNKNOWN;
|
||||
bat->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;
|
||||
bat->status = HID_BATTERY_REPORTED;
|
||||
bat->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;
|
||||
bat->status = HID_BATTERY_REPORTED;
|
||||
bat->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);
|
||||
|
||||
@@ -416,43 +416,39 @@ static unsigned find_battery_quirk(struct hid_device *hdev)
|
||||
return quirks;
|
||||
}
|
||||
|
||||
static int hidinput_scale_battery_capacity(struct hid_device *dev,
|
||||
static int hidinput_scale_battery_capacity(struct hid_battery *bat,
|
||||
int value)
|
||||
{
|
||||
if (dev->battery_min < dev->battery_max &&
|
||||
value >= dev->battery_min && value <= dev->battery_max)
|
||||
value = ((value - dev->battery_min) * 100) /
|
||||
(dev->battery_max - dev->battery_min);
|
||||
if (bat->min < bat->max &&
|
||||
value >= bat->min && value <= bat->max)
|
||||
value = ((value - bat->min) * 100) /
|
||||
(bat->max - bat->min);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int hidinput_query_battery_capacity(struct hid_device *dev)
|
||||
static int hidinput_query_battery_capacity(struct hid_battery *bat)
|
||||
{
|
||||
u8 *buf;
|
||||
int ret;
|
||||
|
||||
buf = kmalloc(4, GFP_KERNEL);
|
||||
u8 *buf __free(kfree) = kmalloc(4, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 4,
|
||||
dev->battery_report_type, HID_REQ_GET_REPORT);
|
||||
if (ret < 2) {
|
||||
kfree(buf);
|
||||
ret = hid_hw_raw_request(bat->dev, bat->report_id, buf, 4,
|
||||
bat->report_type, HID_REQ_GET_REPORT);
|
||||
if (ret < 2)
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
ret = hidinput_scale_battery_capacity(dev, buf[1]);
|
||||
kfree(buf);
|
||||
return ret;
|
||||
return hidinput_scale_battery_capacity(bat, buf[1]);
|
||||
}
|
||||
|
||||
static int hidinput_get_battery_property(struct power_supply *psy,
|
||||
enum power_supply_property prop,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct hid_device *dev = power_supply_get_drvdata(psy);
|
||||
struct hid_battery *bat = power_supply_get_drvdata(psy);
|
||||
struct hid_device *dev = bat->dev;
|
||||
int value;
|
||||
int ret = 0;
|
||||
|
||||
@@ -462,17 +458,17 @@ static int hidinput_get_battery_property(struct power_supply *psy,
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = dev->battery_present;
|
||||
val->intval = bat->present;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
if (dev->battery_status != HID_BATTERY_REPORTED &&
|
||||
!dev->battery_avoid_query) {
|
||||
value = hidinput_query_battery_capacity(dev);
|
||||
if (bat->status != HID_BATTERY_REPORTED &&
|
||||
!bat->avoid_query) {
|
||||
value = hidinput_query_battery_capacity(bat);
|
||||
if (value < 0)
|
||||
return value;
|
||||
} else {
|
||||
value = dev->battery_capacity;
|
||||
value = bat->capacity;
|
||||
}
|
||||
|
||||
val->intval = value;
|
||||
@@ -483,20 +479,20 @@ static int hidinput_get_battery_property(struct power_supply *psy,
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
if (dev->battery_status != HID_BATTERY_REPORTED &&
|
||||
!dev->battery_avoid_query) {
|
||||
value = hidinput_query_battery_capacity(dev);
|
||||
if (bat->status != HID_BATTERY_REPORTED &&
|
||||
!bat->avoid_query) {
|
||||
value = hidinput_query_battery_capacity(bat);
|
||||
if (value < 0)
|
||||
return value;
|
||||
|
||||
dev->battery_capacity = value;
|
||||
dev->battery_status = HID_BATTERY_QUERIED;
|
||||
bat->capacity = value;
|
||||
bat->status = HID_BATTERY_QUERIED;
|
||||
}
|
||||
|
||||
if (dev->battery_status == HID_BATTERY_UNKNOWN)
|
||||
if (bat->status == HID_BATTERY_UNKNOWN)
|
||||
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
else
|
||||
val->intval = dev->battery_charge_status;
|
||||
val->intval = bat->charge_status;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_SCOPE:
|
||||
@@ -511,36 +507,59 @@ static int hidinput_get_battery_property(struct power_supply *psy,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct hid_battery *hidinput_find_battery(struct hid_device *dev,
|
||||
int report_id)
|
||||
{
|
||||
struct hid_battery *bat;
|
||||
|
||||
list_for_each_entry(bat, &dev->batteries, list) {
|
||||
if (bat->report_id == report_id)
|
||||
return bat;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
|
||||
struct hid_field *field, bool is_percentage)
|
||||
{
|
||||
struct hid_battery *bat;
|
||||
struct power_supply_desc *psy_desc;
|
||||
struct power_supply_config psy_cfg = { .drv_data = dev, };
|
||||
struct power_supply_config psy_cfg = { 0 };
|
||||
unsigned quirks;
|
||||
s32 min, max;
|
||||
int error;
|
||||
|
||||
if (dev->battery)
|
||||
return 0; /* already initialized? */
|
||||
/* Check if battery for this report ID already exists */
|
||||
if (hidinput_find_battery(dev, field->report->id))
|
||||
return 0;
|
||||
|
||||
quirks = find_battery_quirk(dev);
|
||||
|
||||
hid_dbg(dev, "device %x:%x:%x %d quirks %d\n",
|
||||
dev->bus, dev->vendor, dev->product, dev->version, quirks);
|
||||
hid_dbg(dev, "device %x:%x:%x %d quirks %d report_id %d\n",
|
||||
dev->bus, dev->vendor, dev->product, dev->version, quirks,
|
||||
field->report->id);
|
||||
|
||||
if (quirks & HID_BATTERY_QUIRK_IGNORE)
|
||||
return 0;
|
||||
|
||||
psy_desc = kzalloc_obj(*psy_desc);
|
||||
if (!psy_desc)
|
||||
bat = devm_kzalloc(&dev->dev, sizeof(*bat), GFP_KERNEL);
|
||||
if (!bat)
|
||||
return -ENOMEM;
|
||||
|
||||
psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery",
|
||||
strlen(dev->uniq) ?
|
||||
dev->uniq : dev_name(&dev->dev));
|
||||
psy_desc = devm_kzalloc(&dev->dev, sizeof(*psy_desc), GFP_KERNEL);
|
||||
if (!psy_desc) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_bat;
|
||||
}
|
||||
|
||||
psy_desc->name = devm_kasprintf(&dev->dev, GFP_KERNEL,
|
||||
"hid-%s-battery-%d",
|
||||
strlen(dev->uniq) ?
|
||||
dev->uniq : dev_name(&dev->dev),
|
||||
field->report->id);
|
||||
if (!psy_desc->name) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
goto err_free_desc;
|
||||
}
|
||||
|
||||
psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
@@ -560,102 +579,95 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
|
||||
if (quirks & HID_BATTERY_QUIRK_FEATURE)
|
||||
report_type = HID_FEATURE_REPORT;
|
||||
|
||||
dev->battery_min = min;
|
||||
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;
|
||||
bat->dev = dev;
|
||||
bat->min = min;
|
||||
bat->max = max;
|
||||
bat->report_type = report_type;
|
||||
bat->report_id = field->report->id;
|
||||
bat->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
bat->status = HID_BATTERY_UNKNOWN;
|
||||
|
||||
/*
|
||||
* Stylus is normally not connected to the device and thus we
|
||||
* can't query the device and get meaningful battery strength.
|
||||
* We have to wait for the device to report it on its own.
|
||||
*/
|
||||
dev->battery_avoid_query = report_type == HID_INPUT_REPORT &&
|
||||
field->physical == HID_DG_STYLUS;
|
||||
bat->avoid_query = report_type == HID_INPUT_REPORT &&
|
||||
field->physical == HID_DG_STYLUS;
|
||||
|
||||
if (quirks & HID_BATTERY_QUIRK_AVOID_QUERY)
|
||||
dev->battery_avoid_query = true;
|
||||
bat->avoid_query = true;
|
||||
|
||||
dev->battery_present = (quirks & HID_BATTERY_QUIRK_DYNAMIC) ? false : true;
|
||||
bat->present = (quirks & HID_BATTERY_QUIRK_DYNAMIC) ? false : true;
|
||||
|
||||
dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
|
||||
if (IS_ERR(dev->battery)) {
|
||||
error = PTR_ERR(dev->battery);
|
||||
psy_cfg.drv_data = bat;
|
||||
bat->ps = devm_power_supply_register(&dev->dev, psy_desc, &psy_cfg);
|
||||
if (IS_ERR(bat->ps)) {
|
||||
error = PTR_ERR(bat->ps);
|
||||
hid_warn(dev, "can't register power supply: %d\n", error);
|
||||
goto err_free_name;
|
||||
}
|
||||
|
||||
power_supply_powers(dev->battery, &dev->dev);
|
||||
power_supply_powers(bat->ps, &dev->dev);
|
||||
list_add_tail(&bat->list, &dev->batteries);
|
||||
return 0;
|
||||
|
||||
err_free_name:
|
||||
kfree(psy_desc->name);
|
||||
err_free_mem:
|
||||
kfree(psy_desc);
|
||||
dev->battery = NULL;
|
||||
devm_kfree(&dev->dev, psy_desc->name);
|
||||
err_free_desc:
|
||||
devm_kfree(&dev->dev, psy_desc);
|
||||
err_free_bat:
|
||||
devm_kfree(&dev->dev, bat);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void hidinput_cleanup_battery(struct hid_device *dev)
|
||||
{
|
||||
const struct power_supply_desc *psy_desc;
|
||||
|
||||
if (!dev->battery)
|
||||
return;
|
||||
|
||||
psy_desc = dev->battery->desc;
|
||||
power_supply_unregister(dev->battery);
|
||||
kfree(psy_desc->name);
|
||||
kfree(psy_desc);
|
||||
dev->battery = NULL;
|
||||
}
|
||||
|
||||
static bool hidinput_update_battery_charge_status(struct hid_device *dev,
|
||||
static bool hidinput_update_battery_charge_status(struct hid_battery *bat,
|
||||
unsigned int usage, int value)
|
||||
{
|
||||
switch (usage) {
|
||||
case HID_BAT_CHARGING:
|
||||
dev->battery_charge_status = value ?
|
||||
POWER_SUPPLY_STATUS_CHARGING :
|
||||
POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
bat->charge_status = value ?
|
||||
POWER_SUPPLY_STATUS_CHARGING :
|
||||
POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
|
||||
int value)
|
||||
static void hidinput_update_battery(struct hid_device *dev, int report_id,
|
||||
unsigned int usage, int value)
|
||||
{
|
||||
struct hid_battery *bat;
|
||||
int capacity;
|
||||
|
||||
if (!dev->battery)
|
||||
bat = hidinput_find_battery(dev, report_id);
|
||||
if (!bat)
|
||||
return;
|
||||
|
||||
if (hidinput_update_battery_charge_status(dev, usage, value)) {
|
||||
dev->battery_present = true;
|
||||
power_supply_changed(dev->battery);
|
||||
if (hidinput_update_battery_charge_status(bat, usage, value)) {
|
||||
bat->present = true;
|
||||
power_supply_changed(bat->ps);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((usage & HID_USAGE_PAGE) == HID_UP_DIGITIZER && value == 0)
|
||||
return;
|
||||
|
||||
if (value < dev->battery_min || value > dev->battery_max)
|
||||
if (value < bat->min || value > bat->max)
|
||||
return;
|
||||
|
||||
capacity = hidinput_scale_battery_capacity(dev, value);
|
||||
capacity = hidinput_scale_battery_capacity(bat, value);
|
||||
|
||||
if (dev->battery_status != HID_BATTERY_REPORTED ||
|
||||
capacity != dev->battery_capacity ||
|
||||
ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) {
|
||||
dev->battery_present = true;
|
||||
dev->battery_capacity = capacity;
|
||||
dev->battery_status = HID_BATTERY_REPORTED;
|
||||
dev->battery_ratelimit_time =
|
||||
if (bat->status != HID_BATTERY_REPORTED ||
|
||||
capacity != bat->capacity ||
|
||||
ktime_after(ktime_get_coarse(), bat->ratelimit_time)) {
|
||||
bat->present = true;
|
||||
bat->capacity = capacity;
|
||||
bat->status = HID_BATTERY_REPORTED;
|
||||
bat->ratelimit_time =
|
||||
ktime_add_ms(ktime_get_coarse(), 30 * 1000);
|
||||
power_supply_changed(dev->battery);
|
||||
power_supply_changed(bat->ps);
|
||||
}
|
||||
}
|
||||
#else /* !CONFIG_HID_BATTERY_STRENGTH */
|
||||
@@ -665,12 +677,8 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hidinput_cleanup_battery(struct hid_device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
|
||||
int value)
|
||||
static void hidinput_update_battery(struct hid_device *dev, int report_id,
|
||||
unsigned int usage, int value)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_HID_BATTERY_STRENGTH */
|
||||
@@ -1557,7 +1565,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
||||
return;
|
||||
|
||||
if (usage->type == EV_PWR) {
|
||||
hidinput_update_battery(hid, usage->hid, value);
|
||||
hidinput_update_battery(hid, report->id, usage->hid, value);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1839,7 +1847,6 @@ static void hidinput_led_worker(struct work_struct *work)
|
||||
struct hid_report *report;
|
||||
int ret;
|
||||
u32 len;
|
||||
__u8 *buf;
|
||||
|
||||
field = hidinput_get_led_field(hid);
|
||||
if (!field)
|
||||
@@ -1866,7 +1873,7 @@ static void hidinput_led_worker(struct work_struct *work)
|
||||
|
||||
/* fall back to generic raw-output-report */
|
||||
len = hid_report_len(report);
|
||||
buf = hid_alloc_report_buf(report, GFP_KERNEL);
|
||||
u8 *buf __free(kfree) = hid_alloc_report_buf(report, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
@@ -1876,7 +1883,6 @@ static void hidinput_led_worker(struct work_struct *work)
|
||||
if (ret == -ENOSYS)
|
||||
hid_hw_raw_request(hid, report->id, buf, len, HID_OUTPUT_REPORT,
|
||||
HID_REQ_SET_REPORT);
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
static int hidinput_input_event(struct input_dev *dev, unsigned int type,
|
||||
@@ -2403,8 +2409,6 @@ void hidinput_disconnect(struct hid_device *hid)
|
||||
{
|
||||
struct hid_input *hidinput, *next;
|
||||
|
||||
hidinput_cleanup_battery(hid);
|
||||
|
||||
list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
|
||||
list_del(&hidinput->list);
|
||||
if (hidinput->registered)
|
||||
|
||||
1504
drivers/hid/hid-lenovo-go-s.c
Normal file
1504
drivers/hid/hid-lenovo-go-s.c
Normal file
File diff suppressed because it is too large
Load Diff
2500
drivers/hid/hid-lenovo-go.c
Normal file
2500
drivers/hid/hid-lenovo-go.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1858,7 +1858,8 @@ static int logi_dj_raw_event(struct hid_device *hdev,
|
||||
static int logi_dj_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
struct hid_report_enum *rep_enum;
|
||||
struct hid_report_enum *input_report_enum;
|
||||
struct hid_report_enum *output_report_enum;
|
||||
struct hid_report *rep;
|
||||
struct dj_receiver_dev *djrcv_dev;
|
||||
struct usb_interface *intf;
|
||||
@@ -1903,10 +1904,20 @@ static int logi_dj_probe(struct hid_device *hdev,
|
||||
}
|
||||
}
|
||||
|
||||
rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
|
||||
output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT];
|
||||
rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT];
|
||||
|
||||
if (rep && (rep->maxfield < 1 ||
|
||||
rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1)) {
|
||||
hid_err(hdev, "Expected size of DJ short report is %d, but got %d",
|
||||
DJREPORT_SHORT_LENGTH - 1, rep->field[0]->report_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
input_report_enum = &hdev->report_enum[HID_INPUT_REPORT];
|
||||
|
||||
/* no input reports, bail out */
|
||||
if (list_empty(&rep_enum->report_list))
|
||||
if (list_empty(&input_report_enum->report_list))
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
@@ -1914,7 +1925,7 @@ static int logi_dj_probe(struct hid_device *hdev,
|
||||
* Note: we should theoretically check for HID++ and DJ
|
||||
* collections, but this will do.
|
||||
*/
|
||||
list_for_each_entry(rep, &rep_enum->report_list, list) {
|
||||
list_for_each_entry(rep, &input_report_enum->report_list, list) {
|
||||
if (rep->application == 0xff000001)
|
||||
has_hidpp = true;
|
||||
}
|
||||
@@ -1927,7 +1938,7 @@ static int logi_dj_probe(struct hid_device *hdev,
|
||||
return -ENODEV;
|
||||
|
||||
/* get the current application attached to the node */
|
||||
rep = list_first_entry(&rep_enum->report_list, struct hid_report, list);
|
||||
rep = list_first_entry(&input_report_enum->report_list, struct hid_report, list);
|
||||
djrcv_dev = dj_get_receiver_dev(hdev, id->driver_data,
|
||||
rep->application, has_hidpp);
|
||||
if (!djrcv_dev) {
|
||||
@@ -1935,7 +1946,7 @@ static int logi_dj_probe(struct hid_device *hdev,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!rep_enum->numbered)
|
||||
if (!input_report_enum->numbered)
|
||||
djrcv_dev->unnumbered_application = rep->application;
|
||||
|
||||
/* Starts the usb device and connects to upper interfaces hiddev and
|
||||
|
||||
@@ -306,21 +306,22 @@ static int __do_hidpp_send_message_sync(struct hidpp_device *hidpp,
|
||||
if (ret) {
|
||||
dbg_hid("__hidpp_send_report returned err: %d\n", ret);
|
||||
memset(response, 0, sizeof(struct hidpp_report));
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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));
|
||||
return -ETIMEDOUT;
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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);
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((response->report_id == REPORT_ID_HIDPP_LONG ||
|
||||
@@ -328,10 +329,14 @@ static int __do_hidpp_send_message_sync(struct hidpp_device *hidpp,
|
||||
response->fap.feature_index == HIDPP20_ERROR) {
|
||||
ret = response->fap.params[1];
|
||||
dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret);
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
hidpp->send_receive_buf = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2502,12 +2507,15 @@ static void hidpp_ff_work_handler(struct work_struct *w)
|
||||
}
|
||||
break;
|
||||
case HIDPP_FF_DESTROY_EFFECT:
|
||||
if (wd->effect_id >= 0)
|
||||
/* regular effect destroyed */
|
||||
data->effect_ids[wd->params[0]-1] = -1;
|
||||
else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER)
|
||||
/* autocenter spring destroyed */
|
||||
data->slot_autocenter = 0;
|
||||
slot = wd->params[0];
|
||||
if (slot > 0 && slot <= data->num_effects) {
|
||||
if (wd->effect_id >= 0)
|
||||
/* regular effect destroyed */
|
||||
data->effect_ids[slot-1] = -1;
|
||||
else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER)
|
||||
/* autocenter spring destroyed */
|
||||
data->slot_autocenter = 0;
|
||||
}
|
||||
break;
|
||||
case HIDPP_FF_SET_GLOBAL_GAINS:
|
||||
data->gain = (wd->params[0] << 8) + wd->params[1];
|
||||
@@ -3840,8 +3848,7 @@ static int hidpp_input_configured(struct hid_device *hdev,
|
||||
static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
|
||||
int size)
|
||||
{
|
||||
struct hidpp_report *question = hidpp->send_receive_buf;
|
||||
struct hidpp_report *answer = hidpp->send_receive_buf;
|
||||
struct hidpp_report *question, *answer;
|
||||
struct hidpp_report *report = (struct hidpp_report *)data;
|
||||
int ret;
|
||||
int last_online;
|
||||
@@ -3851,6 +3858,12 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
|
||||
* previously sent command.
|
||||
*/
|
||||
if (unlikely(mutex_is_locked(&hidpp->send_mutex))) {
|
||||
question = hidpp->send_receive_buf;
|
||||
answer = hidpp->send_receive_buf;
|
||||
|
||||
if (!question)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Check for a correct hidpp20 answer or the corresponding
|
||||
* error
|
||||
|
||||
@@ -817,19 +817,21 @@ static int magicmouse_fetch_battery(struct hid_device *hdev)
|
||||
#ifdef CONFIG_HID_BATTERY_STRENGTH
|
||||
struct hid_report_enum *report_enum;
|
||||
struct hid_report *report;
|
||||
struct hid_battery *bat;
|
||||
|
||||
if (!hdev->battery ||
|
||||
bat = hid_get_battery(hdev);
|
||||
if (!bat ||
|
||||
(!is_usb_magicmouse2(hdev->vendor, hdev->product) &&
|
||||
!is_usb_magictrackpad2(hdev->vendor, hdev->product)))
|
||||
return -1;
|
||||
|
||||
report_enum = &hdev->report_enum[hdev->battery_report_type];
|
||||
report = report_enum->report_id_hash[hdev->battery_report_id];
|
||||
report_enum = &hdev->report_enum[bat->report_type];
|
||||
report = report_enum->report_id_hash[bat->report_id];
|
||||
|
||||
if (!report || report->maxfield < 1)
|
||||
return -1;
|
||||
|
||||
if (hdev->battery_capacity == hdev->battery_max)
|
||||
if (bat->capacity == bat->max)
|
||||
return -1;
|
||||
|
||||
hid_hw_request(hdev, report, HID_REQ_GET_REPORT);
|
||||
|
||||
@@ -19,8 +19,15 @@
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include "hid-ids.h"
|
||||
|
||||
static bool gpio_mode_enforce;
|
||||
|
||||
module_param(gpio_mode_enforce, bool, 0644);
|
||||
MODULE_PARM_DESC(gpio_mode_enforce,
|
||||
"Enforce GPIO mode for GP0 thru GP3 (default: false, will be used for IIO)");
|
||||
|
||||
/* Commands codes in a raw output report */
|
||||
enum {
|
||||
MCP2221_I2C_WR_DATA = 0x90,
|
||||
@@ -536,10 +543,10 @@ static int mcp_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
mcp->rxbuf_idx = 0;
|
||||
mcp->rxbuf = data->block;
|
||||
mcp->txbuf[0] = MCP2221_I2C_GET_DATA;
|
||||
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
|
||||
ret = mcp_i2c_smbus_read(mcp, NULL,
|
||||
MCP2221_I2C_RD_RPT_START,
|
||||
addr, data->block[0] + 1,
|
||||
data->block);
|
||||
if (ret)
|
||||
goto exit;
|
||||
} else {
|
||||
@@ -555,14 +562,14 @@ static int mcp_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
if (read_write == I2C_SMBUS_READ) {
|
||||
ret = mcp_smbus_write(mcp, addr, command, NULL,
|
||||
0, MCP2221_I2C_WR_NO_STOP, 1);
|
||||
0, MCP2221_I2C_WR_NO_STOP, 0);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
mcp->rxbuf_idx = 0;
|
||||
mcp->rxbuf = data->block;
|
||||
mcp->txbuf[0] = MCP2221_I2C_GET_DATA;
|
||||
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
|
||||
ret = mcp_i2c_smbus_read(mcp, NULL,
|
||||
MCP2221_I2C_RD_RPT_START,
|
||||
addr, data->block[0],
|
||||
&data->block[1]);
|
||||
if (ret)
|
||||
goto exit;
|
||||
} else {
|
||||
@@ -650,7 +657,7 @@ static int mcp2221_check_gpio_pinfunc(struct mcp2221 *mcp)
|
||||
int needgpiofix = 0;
|
||||
int ret;
|
||||
|
||||
if (IS_ENABLED(CONFIG_IIO))
|
||||
if (IS_ENABLED(CONFIG_IIO) && !gpio_mode_enforce)
|
||||
return 0;
|
||||
|
||||
ret = mcp_gpio_read_sram(mcp);
|
||||
@@ -1045,7 +1052,8 @@ static void mcp2221_remove(struct hid_device *hdev)
|
||||
#if IS_REACHABLE(CONFIG_IIO)
|
||||
struct mcp2221 *mcp = hid_get_drvdata(hdev);
|
||||
|
||||
cancel_delayed_work_sync(&mcp->init_work);
|
||||
if (!gpio_mode_enforce)
|
||||
cancel_delayed_work_sync(&mcp->init_work);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1319,8 +1327,10 @@ static int mcp2221_probe(struct hid_device *hdev,
|
||||
#endif
|
||||
|
||||
#if IS_REACHABLE(CONFIG_IIO)
|
||||
INIT_DELAYED_WORK(&mcp->init_work, mcp_init_work);
|
||||
schedule_delayed_work(&mcp->init_work, msecs_to_jiffies(100));
|
||||
if (!gpio_mode_enforce) {
|
||||
INIT_DELAYED_WORK(&mcp->init_work, mcp_init_work);
|
||||
schedule_delayed_work(&mcp->init_work, msecs_to_jiffies(100));
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -24,10 +24,6 @@
|
||||
*/
|
||||
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg)
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
@@ -53,14 +49,14 @@ static int hid_plff_play(struct input_dev *dev, void *data,
|
||||
|
||||
left = effect->u.rumble.strong_magnitude;
|
||||
right = effect->u.rumble.weak_magnitude;
|
||||
debug("called with 0x%04x 0x%04x", left, right);
|
||||
hid_dbg(dev, "called with 0x%04x 0x%04x", left, right);
|
||||
|
||||
left = left * plff->maxval / 0xffff;
|
||||
right = right * plff->maxval / 0xffff;
|
||||
|
||||
*plff->strong = left;
|
||||
*plff->weak = right;
|
||||
debug("running with 0x%02x 0x%02x", left, right);
|
||||
hid_dbg(dev, "running with 0x%02x 0x%02x", left, right);
|
||||
hid_hw_request(hid, plff->report, HID_REQ_SET_REPORT);
|
||||
|
||||
return 0;
|
||||
@@ -119,7 +115,7 @@ static int plff_init(struct hid_device *hid)
|
||||
report->field[0]->value[1] = 0x00;
|
||||
strong = &report->field[0]->value[2];
|
||||
weak = &report->field[0]->value[3];
|
||||
debug("detected single-field device");
|
||||
hid_dbg(hid, "detected single-field device");
|
||||
} else if (report->field[0]->maxusage == 1 &&
|
||||
report->field[0]->usage[0].hid ==
|
||||
(HID_UP_LED | 0x43) &&
|
||||
@@ -134,7 +130,7 @@ static int plff_init(struct hid_device *hid)
|
||||
weak = &report->field[3]->value[0];
|
||||
if (hid->vendor == USB_VENDOR_ID_JESS2)
|
||||
maxval = 0xff;
|
||||
debug("detected 4-field device");
|
||||
hid_dbg(hid, "detected 4-field device");
|
||||
} else {
|
||||
hid_err(hid, "not enough fields or values\n");
|
||||
return -ENODEV;
|
||||
|
||||
@@ -2377,6 +2377,12 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report *
|
||||
struct dualshock4_input_report_usb *usb =
|
||||
(struct dualshock4_input_report_usb *)data;
|
||||
|
||||
if (usb->num_touch_reports > ARRAY_SIZE(usb->touch_reports)) {
|
||||
hid_err(hdev, "DualShock4 USB input report has invalid num_touch_reports=%d\n",
|
||||
usb->num_touch_reports);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ds4_report = &usb->common;
|
||||
num_touch_reports = usb->num_touch_reports;
|
||||
touch_reports = usb->touch_reports;
|
||||
@@ -2391,6 +2397,12 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report *
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
||||
if (bt->num_touch_reports > ARRAY_SIZE(bt->touch_reports)) {
|
||||
hid_err(hdev, "DualShock4 BT input report has invalid num_touch_reports=%d\n",
|
||||
bt->num_touch_reports);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ds4_report = &bt->common;
|
||||
num_touch_reports = bt->num_touch_reports;
|
||||
touch_reports = bt->touch_reports;
|
||||
|
||||
@@ -134,6 +134,7 @@ static const struct hid_device_id hid_quirks[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_6019), HID_QUIRK_ALWAYS_POLL },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_602E), HID_QUIRK_ALWAYS_POLL },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_6093), HID_QUIRK_ALWAYS_POLL },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_BOLT_RECEIVER), HID_QUIRK_ALWAYS_POLL },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C007), HID_QUIRK_ALWAYS_POLL },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C077), HID_QUIRK_ALWAYS_POLL },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KEYBOARD_G710_PLUS), HID_QUIRK_NOGET },
|
||||
@@ -691,22 +692,54 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_HID_SONY)
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG_DONGLE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG_DONGLE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_DRUMS) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_GUITAR) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_DRUMS) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_GUITAR) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_KEYBOARD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_DRUMS_MODE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_KEYBOARD_MODE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_MUSTANG_MODE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_SQUIER_MODE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MUSTANG_GUITAR) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_PS4_STRATOCASTER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_JAGUAR) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_RIFFMASTER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS5_RIFFMASTER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_GUITAR_DONGLE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_PS4_GHLIVE_DONGLE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_DJH_TURNTABLE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_DRUMS) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_GUITAR) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_KEYBOARD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_DRUMS_MODE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_KEYBOARD_MODE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_MUSTANG_MODE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_SQUIER_MODE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MUSTANG_GUITAR) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_DRUMS) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_GUITAR) },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_HID_SPEEDLINK)
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* HID driver for Sony / PS2 / PS3 / PS4 BD devices.
|
||||
* HID driver for Sony / PS2 / PS3 BD / PS4 / PS5 devices.
|
||||
*
|
||||
* Copyright (c) 1999 Andreas Gal
|
||||
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
|
||||
@@ -12,9 +12,10 @@
|
||||
* Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com>
|
||||
* Copyright (c) 2018 Todd Kelner
|
||||
* Copyright (c) 2020-2021 Pascal Giard <pascal.giard@etsmtl.ca>
|
||||
* Copyright (c) 2020 Sanjay Govind <sanjay.govind9@gmail.com>
|
||||
* Copyright (c) 2020-2026 Sanjay Govind <sanjay.govind9@gmail.com>
|
||||
* Copyright (c) 2021 Daniel Nguyen <daniel.nguyen.1@ens.etsmtl.ca>
|
||||
* Copyright (c) 2026 Rosalie Wanders <rosalie@mailbox.org>
|
||||
* Copyright (c) 2026 Brenton Simpson <appsforartists@google.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -59,12 +60,15 @@
|
||||
#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 RB4_GUITAR_PS4_USB BIT(17)
|
||||
#define RB4_GUITAR_PS4_BT BIT(18)
|
||||
#define RB4_GUITAR_PS5 BIT(19)
|
||||
#define INSTRUMENT BIT(14)
|
||||
#define GH_GUITAR_TILT BIT(15)
|
||||
#define GHL_GUITAR_PS3WIIU BIT(16)
|
||||
#define GHL_GUITAR_PS4 BIT(17)
|
||||
#define RB4_GUITAR_PS4_USB BIT(18)
|
||||
#define RB4_GUITAR_PS4_BT BIT(19)
|
||||
#define RB4_GUITAR_PS5 BIT(20)
|
||||
#define RB3_PRO_INSTRUMENT BIT(21)
|
||||
#define DJH_TURNTABLE BIT(22)
|
||||
|
||||
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
|
||||
#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
|
||||
@@ -72,7 +76,8 @@
|
||||
NAVIGATION_CONTROLLER_BT)
|
||||
#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER | BUZZ_CONTROLLER |\
|
||||
MOTION_CONTROLLER | NAVIGATION_CONTROLLER)
|
||||
#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER)
|
||||
#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER |\
|
||||
RB4_GUITAR_PS5)
|
||||
#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)
|
||||
@@ -87,6 +92,10 @@
|
||||
#define GHL_GUITAR_POKE_INTERVAL 8 /* In seconds */
|
||||
#define GUITAR_TILT_USAGE 44
|
||||
|
||||
#define TURNTABLE_EFFECTS_KNOB_USAGE 44
|
||||
#define TURNTABLE_PLATTER_BUTTONS_USAGE 45
|
||||
#define TURNTABLE_CROSS_FADER_USAGE 46
|
||||
|
||||
/* Magic data taken from GHLtarUtility:
|
||||
* https://github.com/ghlre/GHLtarUtility/blob/master/PS3Guitar.cs
|
||||
* Note: The Wii U and PS3 dongles happen to share the same!
|
||||
@@ -427,20 +436,25 @@ static const unsigned int rb4_absmap[] = {
|
||||
[0x31] = ABS_Y,
|
||||
};
|
||||
|
||||
static const unsigned int rb4_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 unsigned int ps3_turntable_absmap[] = {
|
||||
[0x32] = ABS_X,
|
||||
[0x35] = ABS_Y,
|
||||
};
|
||||
|
||||
static const unsigned int instrument_keymap[] = {
|
||||
[0x1] = BTN_WEST,
|
||||
[0x2] = BTN_SOUTH,
|
||||
[0x3] = BTN_EAST,
|
||||
[0x4] = BTN_NORTH,
|
||||
[0x5] = BTN_TL,
|
||||
[0x6] = BTN_TR,
|
||||
[0x7] = BTN_TL2,
|
||||
[0x8] = BTN_TR2,
|
||||
[0x9] = BTN_SELECT,
|
||||
[0xa] = BTN_START,
|
||||
[0xb] = BTN_THUMBL,
|
||||
[0xc] = BTN_THUMBR,
|
||||
[0xd] = BTN_MODE,
|
||||
};
|
||||
|
||||
static enum power_supply_property sony_battery_props[] = {
|
||||
@@ -457,6 +471,7 @@ struct sixaxis_led {
|
||||
u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */
|
||||
u8 duty_on; /* % of duty_length the led is on (0xff mean 100%) */
|
||||
} __packed;
|
||||
static_assert(sizeof(struct sixaxis_led) == 5);
|
||||
|
||||
struct sixaxis_rumble {
|
||||
u8 padding;
|
||||
@@ -465,6 +480,7 @@ struct sixaxis_rumble {
|
||||
u8 left_duration; /* Left motor duration (0xff means forever) */
|
||||
u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
|
||||
} __packed;
|
||||
static_assert(sizeof(struct sixaxis_rumble) == 5);
|
||||
|
||||
struct sixaxis_output_report {
|
||||
u8 report_id;
|
||||
@@ -474,11 +490,13 @@ struct sixaxis_output_report {
|
||||
struct sixaxis_led led[4]; /* LEDx at (4 - x) */
|
||||
struct sixaxis_led _reserved; /* LED5, not actually soldered */
|
||||
} __packed;
|
||||
static_assert(sizeof(struct sixaxis_output_report) == 36);
|
||||
|
||||
union sixaxis_output_report_01 {
|
||||
struct sixaxis_output_report data;
|
||||
u8 buf[36];
|
||||
};
|
||||
static_assert(sizeof(union sixaxis_output_report_01) == 36);
|
||||
|
||||
struct motion_output_report_02 {
|
||||
u8 type, zero;
|
||||
@@ -486,10 +504,12 @@ struct motion_output_report_02 {
|
||||
u8 zero2;
|
||||
u8 rumble;
|
||||
};
|
||||
static_assert(sizeof(struct motion_output_report_02) == 7);
|
||||
|
||||
#define SIXAXIS_REPORT_0xF2_SIZE 17
|
||||
#define SIXAXIS_REPORT_0xF5_SIZE 8
|
||||
#define MOTION_REPORT_0x02_SIZE 49
|
||||
#define PRO_INSTRUMENT_0x00_SIZE 8
|
||||
|
||||
#define SENSOR_SUFFIX " Motion Sensors"
|
||||
#define TOUCHPAD_SUFFIX " Touchpad"
|
||||
@@ -515,7 +535,7 @@ struct sony_sc {
|
||||
struct led_classdev *leds[MAX_LEDS];
|
||||
unsigned long quirks;
|
||||
struct work_struct state_worker;
|
||||
void (*send_output_report)(struct sony_sc *);
|
||||
void (*send_output_report)(struct sony_sc *sc);
|
||||
struct power_supply *battery;
|
||||
struct power_supply_desc battery_desc;
|
||||
int device_id;
|
||||
@@ -539,6 +559,9 @@ struct sony_sc {
|
||||
/* GH Live */
|
||||
struct urb *ghl_urb;
|
||||
struct timer_list ghl_poke_timer;
|
||||
|
||||
/* Rock Band 3 Pro Instruments */
|
||||
unsigned long rb3_pro_poke_jiffies;
|
||||
};
|
||||
|
||||
static void sony_set_leds(struct sony_sc *sc);
|
||||
@@ -589,11 +612,11 @@ static int ghl_init_urb(struct sony_sc *sc, struct usb_device *usbdev,
|
||||
pipe = usb_sndctrlpipe(usbdev, 0);
|
||||
|
||||
cr = devm_kzalloc(&sc->hdev->dev, sizeof(*cr), GFP_ATOMIC);
|
||||
if (cr == NULL)
|
||||
if (!cr)
|
||||
return -ENOMEM;
|
||||
|
||||
databuf = devm_kzalloc(&sc->hdev->dev, poke_size, GFP_ATOMIC);
|
||||
if (databuf == NULL)
|
||||
if (!databuf)
|
||||
return -ENOMEM;
|
||||
|
||||
cr->bRequestType =
|
||||
@@ -610,6 +633,88 @@ static int ghl_init_urb(struct sony_sc *sc, struct usb_device *usbdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Sending HID_REQ_SET_REPORT enables the full report. Without this
|
||||
* Rock Band 3 Pro instruments only report navigation events
|
||||
*/
|
||||
static int rb3_pro_instrument_enable_full_report(struct sony_sc *sc)
|
||||
{
|
||||
struct hid_device *hdev = sc->hdev;
|
||||
static const u8 report[] = { 0x00, 0xE9, 0x00, 0x89, 0x1B,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x89, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xE9, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00 };
|
||||
u8 *buf;
|
||||
int ret;
|
||||
|
||||
buf = kmemdup(report, sizeof(report), GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(report),
|
||||
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int djh_turntable_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_MSVENDOR) {
|
||||
unsigned int abs = usage->hid & HID_USAGE;
|
||||
|
||||
if (abs == TURNTABLE_CROSS_FADER_USAGE) {
|
||||
hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_RX);
|
||||
return 1;
|
||||
} else if (abs == TURNTABLE_EFFECTS_KNOB_USAGE) {
|
||||
hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_RY);
|
||||
return 1;
|
||||
} else if (abs == TURNTABLE_PLATTER_BUTTONS_USAGE) {
|
||||
hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_RZ);
|
||||
return 1;
|
||||
}
|
||||
} else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) {
|
||||
unsigned int abs = usage->hid & HID_USAGE;
|
||||
|
||||
if (abs >= ARRAY_SIZE(ps3_turntable_absmap))
|
||||
return -1;
|
||||
|
||||
abs = ps3_turntable_absmap[abs];
|
||||
|
||||
hid_map_usage_clear(hi, usage, bit, max, EV_ABS, abs);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int instrument_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(instrument_keymap))
|
||||
return 0;
|
||||
|
||||
key = instrument_keymap[key];
|
||||
hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gh_guitar_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
@@ -629,16 +734,7 @@ static int rb4_guitar_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(rb4_keymap))
|
||||
return 0;
|
||||
|
||||
key = rb4_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) {
|
||||
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) {
|
||||
unsigned int abs = usage->hid & HID_USAGE;
|
||||
|
||||
/* Let the HID parser deal with the HAT. */
|
||||
@@ -855,6 +951,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size)
|
||||
static const u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
|
||||
unsigned long flags;
|
||||
int offset;
|
||||
u8 index;
|
||||
u8 battery_capacity;
|
||||
int battery_status;
|
||||
|
||||
@@ -870,7 +967,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size)
|
||||
battery_capacity = 100;
|
||||
battery_status = (rd[offset] & 0x01) ? POWER_SUPPLY_STATUS_FULL : POWER_SUPPLY_STATUS_CHARGING;
|
||||
} else {
|
||||
u8 index = rd[offset] <= 5 ? rd[offset] : 5;
|
||||
index = rd[offset] <= 5 ? rd[offset] : 5;
|
||||
battery_capacity = sixaxis_battery_capacity[index];
|
||||
battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
}
|
||||
@@ -908,7 +1005,7 @@ static void nsg_mrxu_parse_report(struct sony_sc *sc, u8 *rd, int size)
|
||||
* the touch-related data starts at offset 2.
|
||||
* For the first byte, bit 0 is set when touchpad button is pressed.
|
||||
* Bit 2 is set when a touch is active and the drag (Fn) key is pressed.
|
||||
* This drag key is mapped to BTN_LEFT. It is operational only when a
|
||||
* This drag key is mapped to BTN_LEFT. It is operational only when a
|
||||
* touch point is active.
|
||||
* Bit 4 is set when only the first touch point is active.
|
||||
* Bit 6 is set when only the second touch point is active.
|
||||
@@ -991,6 +1088,12 @@ static void rb4_ps4_guitar_parse_report(struct sony_sc *sc, u8 *rd, int size)
|
||||
|
||||
static void rb4_ps5_guitar_parse_report(struct sony_sc *sc, u8 *rd, int size)
|
||||
{
|
||||
u8 charging_status;
|
||||
u8 battery_data;
|
||||
u8 battery_capacity;
|
||||
u8 battery_status;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Rock Band 4 PS5 guitars have whammy and
|
||||
* tilt functionality, they're located at
|
||||
@@ -1003,6 +1106,37 @@ static void rb4_ps5_guitar_parse_report(struct sony_sc *sc, u8 *rd, int size)
|
||||
input_report_abs(sc->input_dev, ABS_Z, rd[41]);
|
||||
input_report_abs(sc->input_dev, ABS_RZ, rd[42]);
|
||||
|
||||
/*
|
||||
* Rock Band 4 PS5 guitars also report the
|
||||
* battery status and level at byte 30.
|
||||
*/
|
||||
charging_status = (rd[30] >> 4) & 0x0F;
|
||||
battery_data = rd[30] & 0x0F;
|
||||
|
||||
switch (charging_status) {
|
||||
case 0x0:
|
||||
battery_capacity = min(battery_data * 10 + 5, 100);
|
||||
battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
break;
|
||||
case 0x1:
|
||||
battery_capacity = min(battery_data * 10 + 5, 100);
|
||||
battery_status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
break;
|
||||
case 0x2:
|
||||
battery_capacity = 100;
|
||||
battery_status = POWER_SUPPLY_STATUS_FULL;
|
||||
break;
|
||||
default:
|
||||
battery_capacity = 0;
|
||||
battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&sc->lock, flags);
|
||||
sc->battery_capacity = battery_capacity;
|
||||
sc->battery_status = battery_status;
|
||||
spin_unlock_irqrestore(&sc->lock, flags);
|
||||
|
||||
input_sync(sc->input_dev);
|
||||
}
|
||||
|
||||
@@ -1052,6 +1186,17 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Rock Band 3 PS3 Pro instruments set rd[24] to 0xE0 when they're
|
||||
* sending full reports, and 0x02 when only sending navigation.
|
||||
*/
|
||||
if ((sc->quirks & RB3_PRO_INSTRUMENT) && rd[24] == 0x02) {
|
||||
/* Only attempt to enable full report every 8 seconds */
|
||||
if (time_after(jiffies, sc->rb3_pro_poke_jiffies)) {
|
||||
sc->rb3_pro_poke_jiffies = jiffies + secs_to_jiffies(8);
|
||||
rb3_pro_instrument_enable_full_report(sc);
|
||||
}
|
||||
}
|
||||
|
||||
if (sc->defer_initialization) {
|
||||
sc->defer_initialization = 0;
|
||||
sony_schedule_work(sc, SONY_WORKER_STATE);
|
||||
@@ -1065,6 +1210,7 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
struct sony_sc *sc = hid_get_drvdata(hdev);
|
||||
int ret;
|
||||
|
||||
if (sc->quirks & BUZZ_CONTROLLER) {
|
||||
unsigned int key = usage->hid & HID_USAGE;
|
||||
@@ -1098,9 +1244,19 @@ 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 & GH_GUITAR_CONTROLLER)
|
||||
/* INSTRUMENT quirk is used as a base mapping for instruments */
|
||||
if (sc->quirks & INSTRUMENT) {
|
||||
ret = instrument_mapping(hdev, hi, field, usage, bit, max);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (sc->quirks & GH_GUITAR_TILT)
|
||||
return gh_guitar_mapping(hdev, hi, field, usage, bit, max);
|
||||
|
||||
if (sc->quirks & DJH_TURNTABLE)
|
||||
return djh_turntable_mapping(hdev, hi, field, usage, bit, max);
|
||||
|
||||
if (sc->quirks & (RB4_GUITAR_PS4_USB | RB4_GUITAR_PS4_BT))
|
||||
return rb4_guitar_mapping(hdev, hi, field, usage, bit, max);
|
||||
|
||||
@@ -1153,19 +1309,18 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
|
||||
input_set_abs_params(sc->touchpad, ABS_MT_POSITION_Y, 0, h, 0, 0);
|
||||
|
||||
if (touch_major > 0) {
|
||||
input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MAJOR,
|
||||
input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MAJOR,
|
||||
0, touch_major, 0, 0);
|
||||
if (touch_minor > 0)
|
||||
input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MINOR,
|
||||
input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MINOR,
|
||||
0, touch_minor, 0, 0);
|
||||
if (orientation > 0)
|
||||
input_set_abs_params(sc->touchpad, ABS_MT_ORIENTATION,
|
||||
input_set_abs_params(sc->touchpad, ABS_MT_ORIENTATION,
|
||||
0, orientation, 0, 0);
|
||||
}
|
||||
|
||||
if (sc->quirks & NSG_MRXU_REMOTE) {
|
||||
if (sc->quirks & NSG_MRXU_REMOTE)
|
||||
__set_bit(EV_REL, sc->touchpad->evbit);
|
||||
}
|
||||
|
||||
ret = input_mt_init_slots(sc->touchpad, touch_count, INPUT_MT_POINTER);
|
||||
if (ret < 0)
|
||||
@@ -1320,7 +1475,7 @@ static void sixaxis_set_leds_from_id(struct sony_sc *sc)
|
||||
|
||||
int id = sc->device_id;
|
||||
|
||||
BUILD_BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
|
||||
BUILD_BUG_ON(ARRAY_SIZE(sixaxis_leds[0]) > MAX_LEDS);
|
||||
|
||||
if (id < 0)
|
||||
return;
|
||||
@@ -1338,7 +1493,7 @@ static void buzz_set_leds(struct sony_sc *sc)
|
||||
struct hid_report, list);
|
||||
s32 *value = report->field[0]->value;
|
||||
|
||||
BUILD_BUG_ON(MAX_LEDS < 4);
|
||||
BUILD_BUG_ON(4 > MAX_LEDS);
|
||||
|
||||
value[0] = 0x00;
|
||||
value[1] = sc->led_state[0] ? 0xff : 0x00;
|
||||
@@ -1535,15 +1690,12 @@ static int sony_leds_init(struct sony_sc *sc)
|
||||
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) {
|
||||
hid_err(hdev, "Couldn't allocate memory for LED %d\n", n);
|
||||
if (!led)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
name = (void *)(&led[1]);
|
||||
if (use_color_names)
|
||||
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev),
|
||||
color_name_str[n]);
|
||||
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), color_name_str[n]);
|
||||
else
|
||||
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
|
||||
led->name = name;
|
||||
@@ -2060,6 +2212,19 @@ static int sony_input_configured(struct hid_device *hdev,
|
||||
}
|
||||
|
||||
sony_init_output_report(sc, sixaxis_send_output_report);
|
||||
} else if (sc->quirks & RB3_PRO_INSTRUMENT) {
|
||||
/*
|
||||
* Rock Band 3 PS3 Pro Instruments also do not handle HID Output
|
||||
* Reports on the interrupt EP like they should, so we need to force
|
||||
* HID output reports to use HID_REQ_SET_REPORT on the Control EP.
|
||||
*
|
||||
* There is also another issue about HID Output Reports via USB,
|
||||
* these instruments do not want the report_id as part of the data
|
||||
* packet, so we have to discard buf[0] when sending the actual
|
||||
* control message, even for numbered reports.
|
||||
*/
|
||||
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
|
||||
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
|
||||
} else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
|
||||
/*
|
||||
* The Sony Sixaxis does not handle HID Output Reports on the
|
||||
@@ -2176,10 +2341,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
quirks |= SHANWAN_GAMEPAD;
|
||||
|
||||
sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL);
|
||||
if (sc == NULL) {
|
||||
hid_err(hdev, "can't alloc sony descriptor\n");
|
||||
if (!sc)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_init(&sc->lock);
|
||||
|
||||
@@ -2227,6 +2390,9 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (sc->quirks & RB3_PRO_INSTRUMENT)
|
||||
sc->rb3_pro_poke_jiffies = 0;
|
||||
|
||||
if (sc->quirks & (GHL_GUITAR_PS3WIIU | GHL_GUITAR_PS4)) {
|
||||
if (!hid_is_usb(hdev)) {
|
||||
ret = -EINVAL;
|
||||
@@ -2364,35 +2530,82 @@ static const struct hid_device_id sony_devices[] = {
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE),
|
||||
.driver_data = NSG_MR7U_REMOTE_BT },
|
||||
/* Guitar Hero Live PS3 and Wii U guitar dongles */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE),
|
||||
.driver_data = GHL_GUITAR_PS3WIIU | GH_GUITAR_CONTROLLER },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE),
|
||||
.driver_data = GHL_GUITAR_PS3WIIU | GH_GUITAR_TILT | INSTRUMENT },
|
||||
/* Guitar Hero PC Guitar Dongle */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_GUITAR_DONGLE),
|
||||
.driver_data = GH_GUITAR_CONTROLLER },
|
||||
/* Guitar Hero PS3 World Tour Guitar Dongle */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GUITAR_DONGLE),
|
||||
.driver_data = GH_GUITAR_CONTROLLER },
|
||||
.driver_data = GH_GUITAR_TILT | INSTRUMENT },
|
||||
/* Guitar Hero PS3 Guitar Dongle */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_GUITAR),
|
||||
.driver_data = GH_GUITAR_TILT | INSTRUMENT },
|
||||
/* Guitar Hero PS3 Drum Dongle */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_DRUMS),
|
||||
.driver_data = INSTRUMENT },
|
||||
/* DJ Hero PS3 Guitar Dongle */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_DJH_TURNTABLE),
|
||||
.driver_data = DJH_TURNTABLE | INSTRUMENT },
|
||||
/* Guitar Hero Live PS4 guitar dongles */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_PS4_GHLIVE_DONGLE),
|
||||
.driver_data = GHL_GUITAR_PS4 | GH_GUITAR_CONTROLLER },
|
||||
.driver_data = GHL_GUITAR_PS4 | GH_GUITAR_TILT | INSTRUMENT },
|
||||
/* Rock Band 1 Wii instruments */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_GUITAR),
|
||||
.driver_data = INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_DRUMS),
|
||||
.driver_data = INSTRUMENT },
|
||||
/* Rock Band 2 Wii instruments */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_GUITAR),
|
||||
.driver_data = INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_DRUMS),
|
||||
.driver_data = INSTRUMENT },
|
||||
/* Rock Band 3 Wii instruments */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_DRUMS_MODE),
|
||||
.driver_data = INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MUSTANG_GUITAR),
|
||||
.driver_data = INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_MUSTANG_MODE),
|
||||
.driver_data = INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_SQUIER_MODE),
|
||||
.driver_data = INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_KEYBOARD),
|
||||
.driver_data = INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_KEYBOARD_MODE),
|
||||
.driver_data = INSTRUMENT },
|
||||
/* Rock Band 3 PS3 instruments */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_GUITAR),
|
||||
.driver_data = INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_DRUMS),
|
||||
.driver_data = INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_DRUMS_MODE),
|
||||
.driver_data = INSTRUMENT },
|
||||
/* Rock Band 3 PS3 Pro instruments */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MUSTANG_GUITAR),
|
||||
.driver_data = INSTRUMENT | RB3_PRO_INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_MUSTANG_MODE),
|
||||
.driver_data = INSTRUMENT | RB3_PRO_INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_SQUIER_MODE),
|
||||
.driver_data = INSTRUMENT | RB3_PRO_INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_KEYBOARD),
|
||||
.driver_data = INSTRUMENT | RB3_PRO_INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_KEYBOARD_MODE),
|
||||
.driver_data = INSTRUMENT | RB3_PRO_INSTRUMENT },
|
||||
/* Rock Band 4 PS4 guitars */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_RIFFMASTER),
|
||||
.driver_data = RB4_GUITAR_PS4_USB },
|
||||
.driver_data = RB4_GUITAR_PS4_USB | INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG),
|
||||
.driver_data = RB4_GUITAR_PS4_USB },
|
||||
.driver_data = RB4_GUITAR_PS4_USB | INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG_DONGLE),
|
||||
.driver_data = RB4_GUITAR_PS4_USB },
|
||||
.driver_data = RB4_GUITAR_PS4_USB | INSTRUMENT },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_JAGUAR),
|
||||
.driver_data = RB4_GUITAR_PS4_BT },
|
||||
.driver_data = RB4_GUITAR_PS4_BT | INSTRUMENT },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_PS4_STRATOCASTER),
|
||||
.driver_data = RB4_GUITAR_PS4_BT },
|
||||
.driver_data = RB4_GUITAR_PS4_BT | INSTRUMENT },
|
||||
/* Rock Band 4 PS5 guitars */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS5_RIFFMASTER),
|
||||
.driver_data = RB4_GUITAR_PS5 },
|
||||
.driver_data = RB4_GUITAR_PS5 | INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG),
|
||||
.driver_data = RB4_GUITAR_PS5 },
|
||||
.driver_data = RB4_GUITAR_PS5 | INSTRUMENT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG_DONGLE),
|
||||
.driver_data = RB4_GUITAR_PS5 },
|
||||
.driver_data = RB4_GUITAR_PS5 | INSTRUMENT },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, sony_devices);
|
||||
@@ -2428,5 +2641,5 @@ static void __exit sony_exit(void)
|
||||
module_init(sony_init);
|
||||
module_exit(sony_exit);
|
||||
|
||||
MODULE_DESCRIPTION("HID driver for Sony / PS2 / PS3 / PS4 BD devices");
|
||||
MODULE_DESCRIPTION("HID driver for Sony / PS2 / PS3 BD / PS4 / PS5 devices");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define MAX_REPORT 16
|
||||
|
||||
@@ -35,10 +36,14 @@ static const struct winwing_led_info led_info[3] = {
|
||||
|
||||
struct winwing_drv_data {
|
||||
struct hid_device *hdev;
|
||||
__u8 *report_buf;
|
||||
struct mutex lock;
|
||||
int map_more_buttons;
|
||||
unsigned int num_leds;
|
||||
struct mutex lights_lock;
|
||||
__u8 *report_lights;
|
||||
__u8 *report_rumble;
|
||||
struct work_struct rumble_work;
|
||||
struct ff_rumble_effect rumble;
|
||||
int rumble_left;
|
||||
int rumble_right;
|
||||
int has_grip15;
|
||||
struct winwing_led leds[];
|
||||
};
|
||||
|
||||
@@ -47,11 +52,15 @@ static int winwing_led_write(struct led_classdev *cdev,
|
||||
{
|
||||
struct winwing_led *led = (struct winwing_led *) cdev;
|
||||
struct winwing_drv_data *data = hid_get_drvdata(led->hdev);
|
||||
__u8 *buf = data->report_buf;
|
||||
__u8 *buf = data->report_lights;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
mutex_lock(&data->lights_lock);
|
||||
|
||||
/*
|
||||
* Mimicking requests captured by usbmon when LEDs
|
||||
* are controlled by the vendor's app in a VM.
|
||||
*/
|
||||
buf[0] = 0x02;
|
||||
buf[1] = 0x60;
|
||||
buf[2] = 0xbe;
|
||||
@@ -69,7 +78,7 @@ static int winwing_led_write(struct led_classdev *cdev,
|
||||
|
||||
ret = hid_hw_output_report(led->hdev, buf, 14);
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
mutex_unlock(&data->lights_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -87,9 +96,9 @@ static int winwing_init_led(struct hid_device *hdev,
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
data->report_buf = devm_kmalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL);
|
||||
data->report_lights = devm_kzalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL);
|
||||
|
||||
if (!data->report_buf)
|
||||
if (!data->report_lights)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < 3; i += 1) {
|
||||
@@ -117,7 +126,7 @@ static int winwing_init_led(struct hid_device *hdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int winwing_map_button(int button, int map_more_buttons)
|
||||
static int winwing_map_button(int button, int has_grip15)
|
||||
{
|
||||
if (button < 1)
|
||||
return KEY_RESERVED;
|
||||
@@ -141,7 +150,7 @@ static int winwing_map_button(int button, int map_more_buttons)
|
||||
return (button - 65) + BTN_TRIGGER_HAPPY17;
|
||||
}
|
||||
|
||||
if (!map_more_buttons) {
|
||||
if (!has_grip15) {
|
||||
/*
|
||||
* Not mapping numbers [33 .. 64] which
|
||||
* are not assigned to any real buttons
|
||||
@@ -194,13 +203,149 @@ static int winwing_input_mapping(struct hid_device *hdev,
|
||||
/* Button numbers start with 1 */
|
||||
button = usage->hid & HID_USAGE;
|
||||
|
||||
code = winwing_map_button(button, data->map_more_buttons);
|
||||
code = winwing_map_button(button, data->has_grip15);
|
||||
|
||||
hid_map_usage(hi, usage, bit, max, EV_KEY, code);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If x ≤ 0, return 0;
|
||||
* if x is in [1 .. 65535], return a value in [1 .. 255]
|
||||
*/
|
||||
static inline int convert_magnitude(int x)
|
||||
{
|
||||
if (x < 1)
|
||||
return 0;
|
||||
|
||||
return ((x * 255) >> 16) + 1;
|
||||
}
|
||||
|
||||
static int winwing_haptic_rumble(struct winwing_drv_data *data)
|
||||
{
|
||||
__u8 *buf;
|
||||
__u8 m;
|
||||
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
if (!data->hdev)
|
||||
return -EINVAL;
|
||||
|
||||
buf = data->report_rumble;
|
||||
|
||||
if (!buf)
|
||||
return -EINVAL;
|
||||
|
||||
m = convert_magnitude(data->rumble.strong_magnitude);
|
||||
if (m != data->rumble_left) {
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Mimicking requests captured by usbmon when rumble
|
||||
* is activated by the vendor's app in a VM.
|
||||
*/
|
||||
buf[0] = 0x02;
|
||||
buf[1] = 0x01;
|
||||
buf[2] = 0xbf;
|
||||
buf[3] = 0x00;
|
||||
buf[4] = 0x00;
|
||||
buf[5] = 0x03;
|
||||
buf[6] = 0x49;
|
||||
buf[7] = 0x00;
|
||||
buf[8] = m;
|
||||
buf[9] = 0x00;
|
||||
buf[10] = 0;
|
||||
buf[11] = 0;
|
||||
buf[12] = 0;
|
||||
buf[13] = 0;
|
||||
|
||||
ret = hid_hw_output_report(data->hdev, buf, 14);
|
||||
if (ret < 0) {
|
||||
hid_err(data->hdev, "error %d (%*ph)\n", ret, 14, buf);
|
||||
return ret;
|
||||
}
|
||||
data->rumble_left = m;
|
||||
}
|
||||
|
||||
m = convert_magnitude(data->rumble.weak_magnitude);
|
||||
if (m != data->rumble_right) {
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Mimicking requests captured by usbmon when rumble
|
||||
* is activated by the vendor's app in a VM.
|
||||
*/
|
||||
buf[0] = 0x02;
|
||||
buf[1] = 0x03;
|
||||
buf[2] = 0xbf;
|
||||
buf[3] = 0x00;
|
||||
buf[4] = 0x00;
|
||||
buf[5] = 0x03;
|
||||
buf[6] = 0x49;
|
||||
buf[7] = 0x00;
|
||||
buf[8] = m;
|
||||
buf[9] = 0x00;
|
||||
buf[10] = 0;
|
||||
buf[11] = 0;
|
||||
buf[12] = 0;
|
||||
buf[13] = 0;
|
||||
|
||||
ret = hid_hw_output_report(data->hdev, buf, 14);
|
||||
if (ret < 0) {
|
||||
hid_err(data->hdev, "error %d (%*ph)\n", ret, 14, buf);
|
||||
return ret;
|
||||
}
|
||||
data->rumble_right = m;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void winwing_haptic_rumble_cb(struct work_struct *work)
|
||||
{
|
||||
struct winwing_drv_data *data;
|
||||
|
||||
data = container_of(work, struct winwing_drv_data, rumble_work);
|
||||
winwing_haptic_rumble(data);
|
||||
}
|
||||
|
||||
static int winwing_play_effect(struct input_dev *dev, void *context,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct winwing_drv_data *data = (struct winwing_drv_data *) context;
|
||||
|
||||
if (effect->type != FF_RUMBLE)
|
||||
return 0;
|
||||
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
data->rumble = effect->u.rumble;
|
||||
|
||||
return schedule_work(&data->rumble_work);
|
||||
}
|
||||
|
||||
static int winwing_init_ff(struct hid_device *hdev, struct hid_input *hidinput)
|
||||
{
|
||||
struct winwing_drv_data *data;
|
||||
|
||||
data = (struct winwing_drv_data *) hid_get_drvdata(hdev);
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
data->report_rumble = devm_kzalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL);
|
||||
data->rumble_left = -1;
|
||||
data->rumble_right = -1;
|
||||
|
||||
input_set_capability(hidinput->input, EV_FF, FF_RUMBLE);
|
||||
|
||||
return input_ff_create_memless(hidinput->input, data,
|
||||
winwing_play_effect);
|
||||
}
|
||||
|
||||
static int winwing_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
@@ -219,10 +364,12 @@ static int winwing_probe(struct hid_device *hdev,
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->map_more_buttons = id->driver_data;
|
||||
|
||||
data->hdev = hdev;
|
||||
data->has_grip15 = id->driver_data;
|
||||
hid_set_drvdata(hdev, data);
|
||||
|
||||
INIT_WORK(&data->rumble_work, winwing_haptic_rumble_cb);
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
@@ -232,19 +379,39 @@ static int winwing_probe(struct hid_device *hdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void winwing_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct winwing_drv_data *data;
|
||||
|
||||
data = (struct winwing_drv_data *) hid_get_drvdata(hdev);
|
||||
|
||||
if (data)
|
||||
cancel_work_sync(&data->rumble_work);
|
||||
|
||||
hid_hw_close(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static int winwing_input_configured(struct hid_device *hdev,
|
||||
struct hid_input *hidinput)
|
||||
{
|
||||
struct winwing_drv_data *data;
|
||||
int ret;
|
||||
|
||||
data = (struct winwing_drv_data *) hid_get_drvdata(hdev);
|
||||
|
||||
ret = winwing_init_led(hdev, hidinput->input);
|
||||
|
||||
if (ret)
|
||||
hid_err(hdev, "led init failed\n");
|
||||
|
||||
if (data->has_grip15)
|
||||
winwing_init_ff(hdev, hidinput);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set driver_data to 1 for grips with rumble motor and more than 32 buttons */
|
||||
static const struct hid_device_id winwing_devices[] = {
|
||||
{ HID_USB_DEVICE(0x4098, 0xbd65), .driver_data = 1 }, /* TGRIP-15E */
|
||||
{ HID_USB_DEVICE(0x4098, 0xbd64), .driver_data = 1 }, /* TGRIP-15EX */
|
||||
@@ -261,6 +428,7 @@ static struct hid_driver winwing_driver = {
|
||||
.input_configured = winwing_input_configured,
|
||||
.input_mapping = winwing_input_mapping,
|
||||
.probe = winwing_probe,
|
||||
.remove = winwing_remove,
|
||||
};
|
||||
module_hid_driver(winwing_driver);
|
||||
|
||||
|
||||
@@ -753,9 +753,11 @@ static int quickspi_suspend(struct device *device)
|
||||
if (!qsdev)
|
||||
return -ENODEV;
|
||||
|
||||
ret = quickspi_set_power(qsdev, HIDSPI_SLEEP);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!device_may_wakeup(qsdev->dev)) {
|
||||
ret = quickspi_set_power(qsdev, HIDSPI_SLEEP);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = thc_interrupt_quiesce(qsdev->thc_hw, true);
|
||||
if (ret)
|
||||
@@ -794,9 +796,8 @@ static int quickspi_resume(struct device *device)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = quickspi_set_power(qsdev, HIDSPI_ON);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!device_may_wakeup(qsdev->dev))
|
||||
return quickspi_set_power(qsdev, HIDSPI_ON);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -855,6 +856,9 @@ static int quickspi_poweroff(struct device *device)
|
||||
if (!qsdev)
|
||||
return -ENODEV;
|
||||
|
||||
/* Ignore the return value as platform will be poweroff soon */
|
||||
quickspi_set_power(qsdev, HIDSPI_OFF);
|
||||
|
||||
ret = thc_interrupt_quiesce(qsdev->thc_hw, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -1112,12 +1112,15 @@ int thc_port_select(struct thc_device *dev, enum thc_port_type port_type)
|
||||
EXPORT_SYMBOL_NS_GPL(thc_port_select, "INTEL_THC");
|
||||
|
||||
#define THC_SPI_FREQUENCY_7M 7812500
|
||||
#define THC_SPI_FREQUENCY_10M 10416700
|
||||
#define THC_SPI_FREQUENCY_15M 15625000
|
||||
#define THC_SPI_FREQUENCY_17M 17857100
|
||||
#define THC_SPI_FREQUENCY_20M 20833000
|
||||
#define THC_SPI_FREQUENCY_25M 25000000
|
||||
#define THC_SPI_FREQUENCY_31M 31250000
|
||||
#define THC_SPI_FREQUENCY_35M 35714200
|
||||
#define THC_SPI_FREQUENCY_41M 41666700
|
||||
#define THC_SPI_FREQUENCY_50M 50000000
|
||||
|
||||
#define THC_SPI_LOW_FREQUENCY THC_SPI_FREQUENCY_17M
|
||||
|
||||
@@ -1125,21 +1128,27 @@ static u8 thc_get_spi_freq_div_val(struct thc_device *dev, u32 spi_freq_val)
|
||||
{
|
||||
static const int frequency[] = {
|
||||
THC_SPI_FREQUENCY_7M,
|
||||
THC_SPI_FREQUENCY_10M,
|
||||
THC_SPI_FREQUENCY_15M,
|
||||
THC_SPI_FREQUENCY_17M,
|
||||
THC_SPI_FREQUENCY_20M,
|
||||
THC_SPI_FREQUENCY_25M,
|
||||
THC_SPI_FREQUENCY_31M,
|
||||
THC_SPI_FREQUENCY_35M,
|
||||
THC_SPI_FREQUENCY_41M,
|
||||
THC_SPI_FREQUENCY_50M,
|
||||
};
|
||||
static const u8 frequency_div[] = {
|
||||
THC_SPI_FRQ_DIV_2,
|
||||
THC_SPI_FRQ_DIV_1,
|
||||
THC_SPI_FRQ_DIV_1,
|
||||
THC_SPI_FRQ_DIV_7,
|
||||
THC_SPI_FRQ_DIV_6,
|
||||
THC_SPI_FRQ_DIV_5,
|
||||
THC_SPI_FRQ_DIV_4,
|
||||
THC_SPI_FRQ_DIV_3,
|
||||
THC_SPI_FRQ_DIV_3,
|
||||
THC_SPI_FRQ_DIV_2,
|
||||
};
|
||||
int size = ARRAY_SIZE(frequency);
|
||||
u32 closest_freq;
|
||||
@@ -1190,6 +1199,25 @@ int thc_spi_read_config(struct thc_device *dev, u32 spi_freq_val,
|
||||
if (spi_freq_val < THC_SPI_LOW_FREQUENCY)
|
||||
is_low_freq = true;
|
||||
|
||||
/* 10M, 35M and 50M CLK need 1.5, 3.5 and 2.5 half divider */
|
||||
if ((freq_div == THC_SPI_FRQ_DIV_2 && spi_freq_val >= THC_SPI_FREQUENCY_50M) ||
|
||||
(freq_div == THC_SPI_FRQ_DIV_3 && spi_freq_val < THC_SPI_FREQUENCY_41M) ||
|
||||
(freq_div == THC_SPI_FRQ_DIV_1 && spi_freq_val < THC_SPI_FREQUENCY_15M)) {
|
||||
regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET,
|
||||
THC_M_PRT_SPI_DUTYC_CFG_SPI_TCRF_HALF_DIV_EN,
|
||||
THC_M_PRT_SPI_DUTYC_CFG_SPI_TCRF_HALF_DIV_EN);
|
||||
|
||||
regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPARE_REG_OFFSET,
|
||||
THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE,
|
||||
THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE);
|
||||
} else {
|
||||
regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET,
|
||||
THC_M_PRT_SPI_DUTYC_CFG_SPI_TCRF_HALF_DIV_EN, 0);
|
||||
|
||||
regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPARE_REG_OFFSET,
|
||||
THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE, 0);
|
||||
}
|
||||
|
||||
cfg = FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TCRF, freq_div) |
|
||||
FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TRMODE, io_mode) |
|
||||
(is_low_freq ? THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN : 0) |
|
||||
@@ -1243,6 +1271,25 @@ int thc_spi_write_config(struct thc_device *dev, u32 spi_freq_val,
|
||||
if (spi_freq_val < THC_SPI_LOW_FREQUENCY)
|
||||
is_low_freq = true;
|
||||
|
||||
/* 10M, 35M and 50M CLK need 1.5, 3.5 and 2.5 half divider */
|
||||
if ((freq_div == THC_SPI_FRQ_DIV_2 && spi_freq_val >= THC_SPI_FREQUENCY_50M) ||
|
||||
(freq_div == THC_SPI_FRQ_DIV_3 && spi_freq_val < THC_SPI_FREQUENCY_41M) ||
|
||||
(freq_div == THC_SPI_FRQ_DIV_1 && spi_freq_val < THC_SPI_FREQUENCY_15M)) {
|
||||
regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET,
|
||||
THC_M_PRT_SPI_DUTYC_CFG_SPI_TCWF_HALF_DIV_EN,
|
||||
THC_M_PRT_SPI_DUTYC_CFG_SPI_TCWF_HALF_DIV_EN);
|
||||
|
||||
regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPARE_REG_OFFSET,
|
||||
THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE,
|
||||
THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE);
|
||||
} else {
|
||||
regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET,
|
||||
THC_M_PRT_SPI_DUTYC_CFG_SPI_TCWF_HALF_DIV_EN, 0);
|
||||
|
||||
regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPARE_REG_OFFSET,
|
||||
THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE, 0);
|
||||
}
|
||||
|
||||
cfg = FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TCWF, freq_div) |
|
||||
FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TWMODE, io_mode) |
|
||||
(is_low_freq ? THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN : 0) |
|
||||
|
||||
@@ -643,6 +643,10 @@
|
||||
|
||||
#define THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_VAL GENMASK(3, 0)
|
||||
#define THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_EN BIT(25)
|
||||
#define THC_M_PRT_SPI_DUTYC_CFG_SPI_TCRF_HALF_DIV_EN BIT(30)
|
||||
#define THC_M_PRT_SPI_DUTYC_CFG_SPI_TCWF_HALF_DIV_EN BIT(31)
|
||||
|
||||
#define THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE BIT(2)
|
||||
|
||||
/* CS Assertion delay default value */
|
||||
#define THC_CSA_CK_DELAY_VAL_DEFAULT 4
|
||||
|
||||
@@ -1363,19 +1363,17 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
|
||||
{
|
||||
struct usb_host_interface *interface = intf->cur_altsetting;
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_endpoint_descriptor *ep;
|
||||
struct usbhid_device *usbhid;
|
||||
struct hid_device *hid;
|
||||
unsigned int n, has_in = 0;
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
dbg_hid("HID probe called for ifnum %d\n",
|
||||
intf->altsetting->desc.bInterfaceNumber);
|
||||
|
||||
for (n = 0; n < interface->desc.bNumEndpoints; n++)
|
||||
if (usb_endpoint_is_int_in(&interface->endpoint[n].desc))
|
||||
has_in++;
|
||||
if (!has_in) {
|
||||
ret = usb_find_int_in_endpoint(interface, &ep);
|
||||
if (ret) {
|
||||
hid_err(intf, "couldn't find an input interrupt endpoint\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
@@ -1552,7 +1550,7 @@ static int hid_post_reset(struct usb_interface *intf)
|
||||
* configuration descriptors passed, we already know that
|
||||
* the size of the HID report descriptor has not changed.
|
||||
*/
|
||||
rdesc = kmalloc(hid->dev_rsize, GFP_KERNEL);
|
||||
rdesc = kmalloc(hid->dev_rsize, GFP_NOIO);
|
||||
if (!rdesc)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <linux/input.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#define PID_EFFECTS_MAX 64
|
||||
@@ -81,7 +82,7 @@ static const u8 pidff_set_envelope[] = { 0x22, 0x5b, 0x5c, 0x5d, 0x5e };
|
||||
#define PID_NEG_COEFFICIENT 4
|
||||
#define PID_POS_SATURATION 5
|
||||
#define PID_NEG_SATURATION 6
|
||||
#define PID_DEAD_BAND 7
|
||||
#define PID_DEADBAND 7
|
||||
static const u8 pidff_set_condition[] = {
|
||||
0x22, 0x23, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65
|
||||
};
|
||||
@@ -618,14 +619,24 @@ static void pidff_set_condition_report(struct pidff_device *pidff,
|
||||
effect->u.condition[i].center);
|
||||
pidff_set_signed(&pidff->set_condition[PID_POS_COEFFICIENT],
|
||||
effect->u.condition[i].right_coeff);
|
||||
pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT],
|
||||
effect->u.condition[i].left_coeff);
|
||||
pidff_set(&pidff->set_condition[PID_POS_SATURATION],
|
||||
effect->u.condition[i].right_saturation);
|
||||
pidff_set(&pidff->set_condition[PID_NEG_SATURATION],
|
||||
effect->u.condition[i].left_saturation);
|
||||
pidff_set(&pidff->set_condition[PID_DEAD_BAND],
|
||||
effect->u.condition[i].deadband);
|
||||
|
||||
/* Omit Negative Coefficient if missing */
|
||||
if (!(pidff->quirks & HID_PIDFF_QUIRK_MISSING_NEG_COEFFICIENT))
|
||||
pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT],
|
||||
effect->u.condition[i].left_coeff);
|
||||
|
||||
/* Omit Negative Saturation if missing */
|
||||
if (!(pidff->quirks & HID_PIDFF_QUIRK_MISSING_NEG_SATURATION))
|
||||
pidff_set_signed(&pidff->set_condition[PID_NEG_SATURATION],
|
||||
effect->u.condition[i].left_saturation);
|
||||
|
||||
/* Omit Deadband field if missing */
|
||||
if (!(pidff->quirks & HID_PIDFF_QUIRK_MISSING_DEADBAND))
|
||||
pidff_set(&pidff->set_condition[PID_DEADBAND],
|
||||
effect->u.condition[i].deadband);
|
||||
|
||||
hid_hw_request(pidff->hid, pidff->reports[PID_SET_CONDITION],
|
||||
HID_REQ_SET_REPORT);
|
||||
}
|
||||
@@ -1053,6 +1064,11 @@ static int pidff_find_field_with_usage(int *usage_index,
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define PIDFF_MISSING_FIELD(name, quirks) \
|
||||
({ pr_debug("%s field not found, but that's OK\n", __stringify(name)); \
|
||||
pr_debug("Setting MISSING_%s quirk\n", __stringify(name)); \
|
||||
*quirks |= HID_PIDFF_QUIRK_MISSING_ ## name; })
|
||||
|
||||
/*
|
||||
* Find fields from a report and fill a pidff_usage
|
||||
*/
|
||||
@@ -1060,9 +1076,6 @@ static int pidff_find_fields(struct pidff_usage *usage, const u8 *table,
|
||||
struct hid_report *report, int count, int strict,
|
||||
u32 *quirks)
|
||||
{
|
||||
const u8 block_offset = pidff_set_condition[PID_PARAM_BLOCK_OFFSET];
|
||||
const u8 delay = pidff_set_effect[PID_START_DELAY];
|
||||
|
||||
if (!report) {
|
||||
pr_debug("%s, null report\n", __func__);
|
||||
return -1;
|
||||
@@ -1080,17 +1093,23 @@ static int pidff_find_fields(struct pidff_usage *usage, const u8 *table,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (table[i] == delay) {
|
||||
pr_debug("Delay field not found, but that's OK\n");
|
||||
pr_debug("Setting MISSING_DELAY quirk\n");
|
||||
*quirks |= HID_PIDFF_QUIRK_MISSING_DELAY;
|
||||
/* Field quirks auto-detection */
|
||||
if (table[i] == pidff_set_effect[PID_START_DELAY])
|
||||
PIDFF_MISSING_FIELD(DELAY, quirks);
|
||||
|
||||
} else if (table[i] == block_offset) {
|
||||
pr_debug("PBO field not found, but that's OK\n");
|
||||
pr_debug("Setting MISSING_PBO quirk\n");
|
||||
*quirks |= HID_PIDFF_QUIRK_MISSING_PBO;
|
||||
else if (table[i] == pidff_set_condition[PID_PARAM_BLOCK_OFFSET])
|
||||
PIDFF_MISSING_FIELD(PBO, quirks);
|
||||
|
||||
} else if (strict) {
|
||||
else if (table[i] == pidff_set_condition[PID_NEG_COEFFICIENT])
|
||||
PIDFF_MISSING_FIELD(NEG_COEFFICIENT, quirks);
|
||||
|
||||
else if (table[i] == pidff_set_condition[PID_NEG_SATURATION])
|
||||
PIDFF_MISSING_FIELD(NEG_SATURATION, quirks);
|
||||
|
||||
else if (table[i] == pidff_set_condition[PID_DEADBAND])
|
||||
PIDFF_MISSING_FIELD(DEADBAND, quirks);
|
||||
|
||||
else if (strict) {
|
||||
pr_debug("failed to locate %d\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,15 @@
|
||||
/* Force all periodic effects to be uploaded as SINE */
|
||||
#define HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY BIT(4)
|
||||
|
||||
/* Allow devices with missing negative coefficient in the set condition usage */
|
||||
#define HID_PIDFF_QUIRK_MISSING_NEG_COEFFICIENT BIT(5)
|
||||
|
||||
/* Allow devices with missing negative saturation in the set condition usage */
|
||||
#define HID_PIDFF_QUIRK_MISSING_NEG_SATURATION BIT(6)
|
||||
|
||||
/* Allow devices with missing deadband in the set condition usage */
|
||||
#define HID_PIDFF_QUIRK_MISSING_DEADBAND BIT(7)
|
||||
|
||||
#ifdef CONFIG_HID_PID
|
||||
int hid_pidff_init(struct hid_device *hid);
|
||||
int hid_pidff_init_with_quirks(struct hid_device *hid, u32 initial_quirks);
|
||||
|
||||
@@ -189,6 +189,22 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr,
|
||||
#define DEVICE_ATTR_ADMIN_RW(_name) \
|
||||
struct device_attribute dev_attr_##_name = __ATTR_RW_MODE(_name, 0600)
|
||||
|
||||
/**
|
||||
* DEVICE_ATTR_RW_NAMED - Define a read-write device attribute with a sysfs name
|
||||
* that differs from the function name.
|
||||
* @_name: Attribute function preface
|
||||
* @_attrname: Attribute name as it wil be exposed in the sysfs.
|
||||
*
|
||||
* Like DEVICE_ATTR_RW(), but allows for reusing names under separate paths in
|
||||
* the same driver.
|
||||
*/
|
||||
#define DEVICE_ATTR_RW_NAMED(_name, _attrname) \
|
||||
struct device_attribute dev_attr_##_name = { \
|
||||
.attr = { .name = _attrname, .mode = 0644 }, \
|
||||
.show = _name##_show, \
|
||||
.store = _name##_store, \
|
||||
}
|
||||
|
||||
/**
|
||||
* DEVICE_ATTR_RO - Define a readable device attribute.
|
||||
* @_name: Attribute name.
|
||||
@@ -207,6 +223,21 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr,
|
||||
#define DEVICE_ATTR_ADMIN_RO(_name) \
|
||||
struct device_attribute dev_attr_##_name = __ATTR_RO_MODE(_name, 0400)
|
||||
|
||||
/**
|
||||
* DEVICE_ATTR_RO_NAMED - Define a read-only device attribute with a sysfs name
|
||||
* that differs from the function name.
|
||||
* @_name: Attribute function preface
|
||||
* @_attrname: Attribute name as it wil be exposed in the sysfs.
|
||||
*
|
||||
* Like DEVICE_ATTR_RO(), but allows for reusing names under separate paths in
|
||||
* the same driver.
|
||||
*/
|
||||
#define DEVICE_ATTR_RO_NAMED(_name, _attrname) \
|
||||
struct device_attribute dev_attr_##_name = { \
|
||||
.attr = { .name = _attrname, .mode = 0444 }, \
|
||||
.show = _name##_show, \
|
||||
}
|
||||
|
||||
/**
|
||||
* DEVICE_ATTR_WO - Define an admin-only writable device attribute.
|
||||
* @_name: Attribute name.
|
||||
@@ -216,6 +247,21 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr,
|
||||
#define DEVICE_ATTR_WO(_name) \
|
||||
struct device_attribute dev_attr_##_name = __ATTR_WO(_name)
|
||||
|
||||
/**
|
||||
* DEVICE_ATTR_WO_NAMED - Define a read-only device attribute with a sysfs name
|
||||
* that differs from the function name.
|
||||
* @_name: Attribute function preface
|
||||
* @_attrname: Attribute name as it wil be exposed in the sysfs.
|
||||
*
|
||||
* Like DEVICE_ATTR_WO(), but allows for reusing names under separate paths in
|
||||
* the same driver.
|
||||
*/
|
||||
#define DEVICE_ATTR_WO_NAMED(_name, _attrname) \
|
||||
struct device_attribute dev_attr_##_name = { \
|
||||
.attr = { .name = _attrname, .mode = 0200 }, \
|
||||
.store = _name##_store, \
|
||||
}
|
||||
|
||||
/**
|
||||
* DEVICE_ULONG_ATTR - Define a device attribute backed by an unsigned long.
|
||||
* @_name: Attribute name.
|
||||
|
||||
@@ -634,6 +634,38 @@ enum hid_battery_status {
|
||||
HID_BATTERY_REPORTED, /* Device sent unsolicited battery strength report */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct hid_battery - represents a single battery power supply
|
||||
* @dev: pointer to the parent hid_device
|
||||
* @ps: the power supply instance
|
||||
* @min: minimum battery value from HID descriptor
|
||||
* @max: maximum battery value from HID descriptor
|
||||
* @report_type: HID report type (input/feature)
|
||||
* @report_id: HID report ID for this battery
|
||||
* @charge_status: current charging status
|
||||
* @status: battery reporting status
|
||||
* @capacity: current battery capacity (0-100)
|
||||
* @avoid_query: if true, avoid querying battery (e.g., for stylus)
|
||||
* @present: if true, battery is present (may be dynamic)
|
||||
* @ratelimit_time: rate limiting for battery reports
|
||||
* @list: list node for linking into hid_device's battery list
|
||||
*/
|
||||
struct hid_battery {
|
||||
struct hid_device *dev;
|
||||
struct power_supply *ps;
|
||||
__s32 min;
|
||||
__s32 max;
|
||||
__s32 report_type;
|
||||
__s32 report_id;
|
||||
__s32 charge_status;
|
||||
enum hid_battery_status status;
|
||||
__s32 capacity;
|
||||
bool avoid_query;
|
||||
bool present;
|
||||
ktime_t ratelimit_time;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct hid_driver;
|
||||
struct hid_ll_driver;
|
||||
|
||||
@@ -670,20 +702,10 @@ struct hid_device {
|
||||
#ifdef CONFIG_HID_BATTERY_STRENGTH
|
||||
/*
|
||||
* Power supply information for HID devices which report
|
||||
* battery strength. power_supply was successfully registered if
|
||||
* battery is non-NULL.
|
||||
* battery strength. Each battery is tracked separately in the
|
||||
* batteries list.
|
||||
*/
|
||||
struct power_supply *battery;
|
||||
__s32 battery_capacity;
|
||||
__s32 battery_min;
|
||||
__s32 battery_max;
|
||||
__s32 battery_report_type;
|
||||
__s32 battery_report_id;
|
||||
__s32 battery_charge_status;
|
||||
enum hid_battery_status battery_status;
|
||||
bool battery_avoid_query;
|
||||
bool battery_present;
|
||||
ktime_t battery_ratelimit_time;
|
||||
struct list_head batteries;
|
||||
#endif
|
||||
|
||||
unsigned long status; /* see STAT flags above */
|
||||
@@ -699,6 +721,7 @@ struct hid_device {
|
||||
char name[128]; /* Device name */
|
||||
char phys[64]; /* Device physical location */
|
||||
char uniq[64]; /* Device unique identifier (serial #) */
|
||||
u64 firmware_version; /* Firmware version */
|
||||
|
||||
void *driver_data;
|
||||
|
||||
@@ -744,6 +767,15 @@ static inline void hid_set_drvdata(struct hid_device *hdev, void *data)
|
||||
dev_set_drvdata(&hdev->dev, data);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HID_BATTERY_STRENGTH
|
||||
static inline struct hid_battery *hid_get_battery(struct hid_device *hdev)
|
||||
{
|
||||
if (list_empty(&hdev->batteries))
|
||||
return NULL;
|
||||
return list_first_entry(&hdev->batteries, struct hid_battery, list);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define HID_GLOBAL_STACK_SIZE 4
|
||||
#define HID_COLLECTION_STACK_SIZE 4
|
||||
|
||||
|
||||
Reference in New Issue
Block a user