1d69ccfcbSDerek J. Clark // SPDX-License-Identifier: GPL-2.0-or-later 2d69ccfcbSDerek J. Clark /* 3d69ccfcbSDerek J. Clark * HID driver for Lenovo Legion Go series gamepads. 4d69ccfcbSDerek J. Clark * 5d69ccfcbSDerek J. Clark * Copyright (c) 2026 Derek J. Clark <derekjohn.clark@gmail.com> 6d69ccfcbSDerek J. Clark * Copyright (c) 2026 Valve Corporation 7d69ccfcbSDerek J. Clark */ 8d69ccfcbSDerek J. Clark 9d69ccfcbSDerek J. Clark #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 10d69ccfcbSDerek J. Clark 11d69ccfcbSDerek J. Clark #include <linux/array_size.h> 12d69ccfcbSDerek J. Clark #include <linux/cleanup.h> 13d69ccfcbSDerek J. Clark #include <linux/completion.h> 14d69ccfcbSDerek J. Clark #include <linux/delay.h> 15d69ccfcbSDerek J. Clark #include <linux/dev_printk.h> 16d69ccfcbSDerek J. Clark #include <linux/device.h> 17d69ccfcbSDerek J. Clark #include <linux/device/devres.h> 18d69ccfcbSDerek J. Clark #include <linux/hid.h> 19d69ccfcbSDerek J. Clark #include <linux/jiffies.h> 20d69ccfcbSDerek J. Clark #include <linux/kstrtox.h> 21325262faSDerek J. Clark #include <linux/led-class-multicolor.h> 22d69ccfcbSDerek J. Clark #include <linux/mutex.h> 23d69ccfcbSDerek J. Clark #include <linux/printk.h> 24d69ccfcbSDerek J. Clark #include <linux/sysfs.h> 25d69ccfcbSDerek J. Clark #include <linux/types.h> 26d69ccfcbSDerek J. Clark #include <linux/unaligned.h> 27d69ccfcbSDerek J. Clark #include <linux/usb.h> 28d69ccfcbSDerek J. Clark #include <linux/workqueue.h> 29d69ccfcbSDerek J. Clark #include <linux/workqueue_types.h> 30d69ccfcbSDerek J. Clark 31d69ccfcbSDerek J. Clark #include "hid-ids.h" 32d69ccfcbSDerek J. Clark 33d69ccfcbSDerek J. Clark #define GO_GP_INTF_IN 0x83 34d69ccfcbSDerek J. Clark #define GO_OUTPUT_REPORT_ID 0x05 35d69ccfcbSDerek J. Clark #define GO_GP_RESET_SUCCESS 0x01 36d69ccfcbSDerek J. Clark #define GO_PACKET_SIZE 64 37d69ccfcbSDerek J. Clark 38d69ccfcbSDerek J. Clark static struct hid_go_cfg { 39d69ccfcbSDerek J. Clark struct delayed_work go_cfg_setup; 40d69ccfcbSDerek J. Clark struct completion send_cmd_complete; 41325262faSDerek J. Clark struct led_classdev *led_cdev; 42d69ccfcbSDerek J. Clark struct hid_device *hdev; 43d69ccfcbSDerek J. Clark struct mutex cfg_mutex; /*ensure single synchronous output report*/ 4482cd9bc8SDerek J. Clark u8 fps_mode; 4582cd9bc8SDerek J. Clark u8 gp_left_auto_sleep_time; 46995887a1SDerek J. Clark u8 gp_left_gyro_cal_status; 47995887a1SDerek J. Clark u8 gp_left_joy_cal_status; 4896b20c1fSDerek J. Clark u8 gp_left_notify_en; 4996b20c1fSDerek J. Clark u8 gp_left_rumble_mode; 50995887a1SDerek J. Clark u8 gp_left_trigg_cal_status; 51d69ccfcbSDerek J. Clark u32 gp_left_version_firmware; 52d69ccfcbSDerek J. Clark u8 gp_left_version_gen; 53d69ccfcbSDerek J. Clark u32 gp_left_version_hardware; 54d69ccfcbSDerek J. Clark u32 gp_left_version_product; 55d69ccfcbSDerek J. Clark u32 gp_left_version_protocol; 5682cd9bc8SDerek J. Clark u8 gp_mode; 5782cd9bc8SDerek J. Clark u8 gp_right_auto_sleep_time; 58995887a1SDerek J. Clark u8 gp_right_gyro_cal_status; 59995887a1SDerek J. Clark u8 gp_right_joy_cal_status; 6096b20c1fSDerek J. Clark u8 gp_right_notify_en; 6196b20c1fSDerek J. Clark u8 gp_right_rumble_mode; 62995887a1SDerek J. Clark u8 gp_right_trigg_cal_status; 63d69ccfcbSDerek J. Clark u32 gp_right_version_firmware; 64d69ccfcbSDerek J. Clark u8 gp_right_version_gen; 65d69ccfcbSDerek J. Clark u32 gp_right_version_hardware; 66d69ccfcbSDerek J. Clark u32 gp_right_version_product; 67d69ccfcbSDerek J. Clark u32 gp_right_version_protocol; 6896b20c1fSDerek J. Clark u8 gp_rumble_intensity; 6982cd9bc8SDerek J. Clark u8 imu_left_bypass_en; 7082cd9bc8SDerek J. Clark u8 imu_left_sensor_en; 7182cd9bc8SDerek J. Clark u8 imu_right_bypass_en; 7282cd9bc8SDerek J. Clark u8 imu_right_sensor_en; 73d69ccfcbSDerek J. Clark u32 mcu_version_firmware; 74d69ccfcbSDerek J. Clark u8 mcu_version_gen; 75d69ccfcbSDerek J. Clark u32 mcu_version_hardware; 76d69ccfcbSDerek J. Clark u32 mcu_version_product; 77d69ccfcbSDerek J. Clark u32 mcu_version_protocol; 78f0bedee6SDerek J. Clark u32 mouse_dpi; 7951e4270cSDerek J. Clark u8 os_mode; 80325262faSDerek J. Clark u8 rgb_effect; 8182cd9bc8SDerek J. Clark u8 rgb_en; 82325262faSDerek J. Clark u8 rgb_mode; 83325262faSDerek J. Clark u8 rgb_profile; 84325262faSDerek J. Clark u8 rgb_speed; 8582cd9bc8SDerek J. Clark u8 tp_en; 8696b20c1fSDerek J. Clark u8 tp_vibration_en; 8796b20c1fSDerek J. Clark u8 tp_vibration_intensity; 88d69ccfcbSDerek J. Clark u32 tx_dongle_version_firmware; 89d69ccfcbSDerek J. Clark u8 tx_dongle_version_gen; 90d69ccfcbSDerek J. Clark u32 tx_dongle_version_hardware; 91d69ccfcbSDerek J. Clark u32 tx_dongle_version_product; 92d69ccfcbSDerek J. Clark u32 tx_dongle_version_protocol; 93d69ccfcbSDerek J. Clark } drvdata; 94d69ccfcbSDerek J. Clark 95d69ccfcbSDerek J. Clark struct go_cfg_attr { 96d69ccfcbSDerek J. Clark u8 index; 97d69ccfcbSDerek J. Clark }; 98d69ccfcbSDerek J. Clark 99d69ccfcbSDerek J. Clark struct command_report { 100d69ccfcbSDerek J. Clark u8 report_id; 101d69ccfcbSDerek J. Clark u8 id; 102d69ccfcbSDerek J. Clark u8 cmd; 103d69ccfcbSDerek J. Clark u8 sub_cmd; 104d69ccfcbSDerek J. Clark u8 device_type; 105d69ccfcbSDerek J. Clark u8 data[59]; 106d69ccfcbSDerek J. Clark } __packed; 107d69ccfcbSDerek J. Clark 108d69ccfcbSDerek J. Clark enum command_id { 109d69ccfcbSDerek J. Clark MCU_CONFIG_DATA = 0x00, 110d69ccfcbSDerek J. Clark OS_MODE_DATA = 0x06, 111d69ccfcbSDerek J. Clark GAMEPAD_DATA = 0x3c, 112d69ccfcbSDerek J. Clark }; 113d69ccfcbSDerek J. Clark 114d69ccfcbSDerek J. Clark enum mcu_command_index { 115d69ccfcbSDerek J. Clark GET_VERSION_DATA = 0x02, 116d69ccfcbSDerek J. Clark GET_FEATURE_STATUS, 117d69ccfcbSDerek J. Clark SET_FEATURE_STATUS, 118d69ccfcbSDerek J. Clark GET_MOTOR_CFG, 119d69ccfcbSDerek J. Clark SET_MOTOR_CFG, 120d69ccfcbSDerek J. Clark GET_DPI_CFG, 121d69ccfcbSDerek J. Clark SET_DPI_CFG, 122d69ccfcbSDerek J. Clark SET_TRIGGER_CFG = 0x0a, 123d69ccfcbSDerek J. Clark SET_JOYSTICK_CFG = 0x0c, 124d69ccfcbSDerek J. Clark SET_GYRO_CFG = 0x0e, 125d69ccfcbSDerek J. Clark GET_RGB_CFG, 126d69ccfcbSDerek J. Clark SET_RGB_CFG, 127d69ccfcbSDerek J. Clark GET_DEVICE_STATUS = 0xa0, 128d69ccfcbSDerek J. Clark 129d69ccfcbSDerek J. Clark }; 130d69ccfcbSDerek J. Clark 131d69ccfcbSDerek J. Clark enum dev_type { 132d69ccfcbSDerek J. Clark UNSPECIFIED, 133d69ccfcbSDerek J. Clark USB_MCU, 134d69ccfcbSDerek J. Clark TX_DONGLE, 135d69ccfcbSDerek J. Clark LEFT_CONTROLLER, 136d69ccfcbSDerek J. Clark RIGHT_CONTROLLER, 137d69ccfcbSDerek J. Clark }; 138d69ccfcbSDerek J. Clark 13982cd9bc8SDerek J. Clark enum enabled_status_index { 14082cd9bc8SDerek J. Clark FEATURE_UNKNOWN, 14182cd9bc8SDerek J. Clark FEATURE_ENABLED, 14282cd9bc8SDerek J. Clark FEATURE_DISABLED, 14382cd9bc8SDerek J. Clark }; 14482cd9bc8SDerek J. Clark 14582cd9bc8SDerek J. Clark static const char *const enabled_status_text[] = { 14682cd9bc8SDerek J. Clark [FEATURE_UNKNOWN] = "unknown", 14782cd9bc8SDerek J. Clark [FEATURE_ENABLED] = "true", 14882cd9bc8SDerek J. Clark [FEATURE_DISABLED] = "false", 14982cd9bc8SDerek J. Clark }; 15082cd9bc8SDerek J. Clark 151d69ccfcbSDerek J. Clark enum version_data_index { 152d69ccfcbSDerek J. Clark PRODUCT_VERSION = 0x02, 153d69ccfcbSDerek J. Clark PROTOCOL_VERSION, 154d69ccfcbSDerek J. Clark FIRMWARE_VERSION, 155d69ccfcbSDerek J. Clark HARDWARE_VERSION, 156d69ccfcbSDerek J. Clark HARDWARE_GENERATION, 157d69ccfcbSDerek J. Clark }; 158d69ccfcbSDerek J. Clark 15982cd9bc8SDerek J. Clark enum feature_status_index { 16082cd9bc8SDerek J. Clark FEATURE_RESET_GAMEPAD = 0x02, 16182cd9bc8SDerek J. Clark FEATURE_IMU_BYPASS, 16282cd9bc8SDerek J. Clark FEATURE_IMU_ENABLE = 0x05, 16382cd9bc8SDerek J. Clark FEATURE_TOUCHPAD_ENABLE = 0x07, 16482cd9bc8SDerek J. Clark FEATURE_LIGHT_ENABLE, 16582cd9bc8SDerek J. Clark FEATURE_AUTO_SLEEP_TIME, 16682cd9bc8SDerek J. Clark FEATURE_FPS_SWITCH_STATUS = 0x0b, 16782cd9bc8SDerek J. Clark FEATURE_GAMEPAD_MODE = 0x0e, 16882cd9bc8SDerek J. Clark }; 16982cd9bc8SDerek J. Clark 17051e4270cSDerek J. Clark #define FEATURE_OS_MODE 0x69 17151e4270cSDerek J. Clark 17282cd9bc8SDerek J. Clark enum fps_switch_status_index { 17382cd9bc8SDerek J. Clark FPS_STATUS_UNKNOWN, 17482cd9bc8SDerek J. Clark GAMEPAD, 17582cd9bc8SDerek J. Clark FPS, 17682cd9bc8SDerek J. Clark }; 17782cd9bc8SDerek J. Clark 17882cd9bc8SDerek J. Clark static const char *const fps_switch_text[] = { 17982cd9bc8SDerek J. Clark [FPS_STATUS_UNKNOWN] = "unknown", 18082cd9bc8SDerek J. Clark [GAMEPAD] = "gamepad", 18182cd9bc8SDerek J. Clark [FPS] = "fps", 18282cd9bc8SDerek J. Clark }; 18382cd9bc8SDerek J. Clark 18482cd9bc8SDerek J. Clark enum gamepad_mode_index { 18582cd9bc8SDerek J. Clark GAMEPAD_MODE_UNKNOWN, 18682cd9bc8SDerek J. Clark XINPUT, 18782cd9bc8SDerek J. Clark DINPUT, 18882cd9bc8SDerek J. Clark }; 18982cd9bc8SDerek J. Clark 19082cd9bc8SDerek J. Clark static const char *const gamepad_mode_text[] = { 19182cd9bc8SDerek J. Clark [GAMEPAD_MODE_UNKNOWN] = "unknown", 19282cd9bc8SDerek J. Clark [XINPUT] = "xinput", 19382cd9bc8SDerek J. Clark [DINPUT] = "dinput", 19482cd9bc8SDerek J. Clark }; 19582cd9bc8SDerek J. Clark 19696b20c1fSDerek J. Clark enum motor_cfg_index { 19796b20c1fSDerek J. Clark MOTOR_CFG_ALL = 0x01, 19896b20c1fSDerek J. Clark MOTOR_INTENSITY, 19996b20c1fSDerek J. Clark VIBRATION_NOTIFY_ENABLE, 20096b20c1fSDerek J. Clark RUMBLE_MODE, 20196b20c1fSDerek J. Clark TP_VIBRATION_ENABLE, 20296b20c1fSDerek J. Clark TP_VIBRATION_INTENSITY, 20396b20c1fSDerek J. Clark }; 20496b20c1fSDerek J. Clark 20596b20c1fSDerek J. Clark enum intensity_index { 20696b20c1fSDerek J. Clark INTENSITY_UNKNOWN, 20796b20c1fSDerek J. Clark INTENSITY_OFF, 20896b20c1fSDerek J. Clark INTENSITY_LOW, 20996b20c1fSDerek J. Clark INTENSITY_MEDIUM, 21096b20c1fSDerek J. Clark INTENSITY_HIGH, 21196b20c1fSDerek J. Clark }; 21296b20c1fSDerek J. Clark 21396b20c1fSDerek J. Clark static const char *const intensity_text[] = { 21496b20c1fSDerek J. Clark [INTENSITY_UNKNOWN] = "unknown", 21596b20c1fSDerek J. Clark [INTENSITY_OFF] = "off", 21696b20c1fSDerek J. Clark [INTENSITY_LOW] = "low", 21796b20c1fSDerek J. Clark [INTENSITY_MEDIUM] = "medium", 21896b20c1fSDerek J. Clark [INTENSITY_HIGH] = "high", 21996b20c1fSDerek J. Clark }; 22096b20c1fSDerek J. Clark 22196b20c1fSDerek J. Clark enum rumble_mode_index { 22296b20c1fSDerek J. Clark RUMBLE_MODE_UNKNOWN, 22396b20c1fSDerek J. Clark RUMBLE_MODE_FPS, 22496b20c1fSDerek J. Clark RUMBLE_MODE_RACE, 22596b20c1fSDerek J. Clark RUMBLE_MODE_AVERAGE, 22696b20c1fSDerek J. Clark RUMBLE_MODE_SPG, 22796b20c1fSDerek J. Clark RUMBLE_MODE_RPG, 22896b20c1fSDerek J. Clark }; 22996b20c1fSDerek J. Clark 23096b20c1fSDerek J. Clark static const char *const rumble_mode_text[] = { 23196b20c1fSDerek J. Clark [RUMBLE_MODE_UNKNOWN] = "unknown", 23296b20c1fSDerek J. Clark [RUMBLE_MODE_FPS] = "fps", 23396b20c1fSDerek J. Clark [RUMBLE_MODE_RACE] = "racing", 23496b20c1fSDerek J. Clark [RUMBLE_MODE_AVERAGE] = "standard", 23596b20c1fSDerek J. Clark [RUMBLE_MODE_SPG] = "spg", 23696b20c1fSDerek J. Clark [RUMBLE_MODE_RPG] = "rpg", 23796b20c1fSDerek J. Clark }; 23896b20c1fSDerek J. Clark 239f0bedee6SDerek J. Clark #define FPS_MODE_DPI 0x02 240995887a1SDerek J. Clark #define TRIGGER_CALIBRATE 0x04 241995887a1SDerek J. Clark #define JOYSTICK_CALIBRATE 0x04 242995887a1SDerek J. Clark #define GYRO_CALIBRATE 0x06 243995887a1SDerek J. Clark 244995887a1SDerek J. Clark enum cal_device_type { 245995887a1SDerek J. Clark CALDEV_GYROSCOPE = 0x01, 246995887a1SDerek J. Clark CALDEV_JOYSTICK, 247995887a1SDerek J. Clark CALDEV_TRIGGER, 248995887a1SDerek J. Clark CALDEV_JOY_TRIGGER, 249995887a1SDerek J. Clark }; 250995887a1SDerek J. Clark 251995887a1SDerek J. Clark enum cal_enable { 252995887a1SDerek J. Clark CAL_UNKNOWN, 253995887a1SDerek J. Clark CAL_START, 254995887a1SDerek J. Clark CAL_STOP, 255995887a1SDerek J. Clark }; 256995887a1SDerek J. Clark 257995887a1SDerek J. Clark static const char *const cal_enabled_text[] = { 258995887a1SDerek J. Clark [CAL_UNKNOWN] = "unknown", 259995887a1SDerek J. Clark [CAL_START] = "start", 260995887a1SDerek J. Clark [CAL_STOP] = "stop", 261995887a1SDerek J. Clark }; 262995887a1SDerek J. Clark 263995887a1SDerek J. Clark enum cal_status_index { 264995887a1SDerek J. Clark CAL_STAT_UNKNOWN, 265995887a1SDerek J. Clark CAL_STAT_SUCCESS, 266995887a1SDerek J. Clark CAL_STAT_FAILURE, 267995887a1SDerek J. Clark }; 268995887a1SDerek J. Clark 269995887a1SDerek J. Clark static const char *const cal_status_text[] = { 270995887a1SDerek J. Clark [CAL_STAT_UNKNOWN] = "unknown", 271995887a1SDerek J. Clark [CAL_STAT_SUCCESS] = "success", 272995887a1SDerek J. Clark [CAL_STAT_FAILURE] = "failure", 273995887a1SDerek J. Clark }; 274f0bedee6SDerek J. Clark 275325262faSDerek J. Clark enum rgb_config_index { 276325262faSDerek J. Clark LIGHT_CFG_ALL = 0x01, 277325262faSDerek J. Clark LIGHT_MODE_SEL, 278325262faSDerek J. Clark LIGHT_PROFILE_SEL, 279325262faSDerek J. Clark USR_LIGHT_PROFILE_1, 280325262faSDerek J. Clark USR_LIGHT_PROFILE_2, 281325262faSDerek J. Clark USR_LIGHT_PROFILE_3, 282325262faSDerek J. Clark }; 283325262faSDerek J. Clark 284325262faSDerek J. Clark enum rgb_mode_index { 285325262faSDerek J. Clark RGB_MODE_UNKNOWN, 286325262faSDerek J. Clark RGB_MODE_DYNAMIC, 287325262faSDerek J. Clark RGB_MODE_CUSTOM, 288325262faSDerek J. Clark }; 289325262faSDerek J. Clark 290325262faSDerek J. Clark static const char *const rgb_mode_text[] = { 291325262faSDerek J. Clark [RGB_MODE_UNKNOWN] = "unknown", 292325262faSDerek J. Clark [RGB_MODE_DYNAMIC] = "dynamic", 293325262faSDerek J. Clark [RGB_MODE_CUSTOM] = "custom", 294325262faSDerek J. Clark }; 295325262faSDerek J. Clark 296325262faSDerek J. Clark enum rgb_effect_index { 297325262faSDerek J. Clark RGB_EFFECT_MONO, 298325262faSDerek J. Clark RGB_EFFECT_BREATHE, 299325262faSDerek J. Clark RGB_EFFECT_CHROMA, 300325262faSDerek J. Clark RGB_EFFECT_RAINBOW, 301325262faSDerek J. Clark }; 302325262faSDerek J. Clark 303325262faSDerek J. Clark static const char *const rgb_effect_text[] = { 304325262faSDerek J. Clark [RGB_EFFECT_MONO] = "monocolor", 305325262faSDerek J. Clark [RGB_EFFECT_BREATHE] = "breathe", 306325262faSDerek J. Clark [RGB_EFFECT_CHROMA] = "chroma", 307325262faSDerek J. Clark [RGB_EFFECT_RAINBOW] = "rainbow", 308325262faSDerek J. Clark }; 309325262faSDerek J. Clark 310995887a1SDerek J. Clark enum device_status_index { 311995887a1SDerek J. Clark GET_CAL_STATUS = 0x02, 312995887a1SDerek J. Clark GET_UPGRADE_STATUS, 313995887a1SDerek J. Clark GET_MACRO_REC_STATUS, 314995887a1SDerek J. Clark GET_HOTKEY_TRIGG_STATUS, 315995887a1SDerek J. Clark }; 316995887a1SDerek J. Clark 31751e4270cSDerek J. Clark enum os_mode_cfg_index { 31851e4270cSDerek J. Clark SET_OS_MODE = 0x09, 31951e4270cSDerek J. Clark GET_OS_MODE, 32051e4270cSDerek J. Clark }; 32151e4270cSDerek J. Clark 32251e4270cSDerek J. Clark enum os_mode_type_index { 32351e4270cSDerek J. Clark OS_UNKNOWN, 32451e4270cSDerek J. Clark WINDOWS, 32551e4270cSDerek J. Clark LINUX, 32651e4270cSDerek J. Clark }; 32751e4270cSDerek J. Clark 32851e4270cSDerek J. Clark static const char *const os_mode_text[] = { 32951e4270cSDerek J. Clark [OS_UNKNOWN] = "unknown", 33051e4270cSDerek J. Clark [WINDOWS] = "windows", 33151e4270cSDerek J. Clark [LINUX] = "linux", 33251e4270cSDerek J. Clark }; 33351e4270cSDerek J. Clark 334d69ccfcbSDerek J. Clark static int hid_go_version_event(struct command_report *cmd_rep) 335d69ccfcbSDerek J. Clark { 336d69ccfcbSDerek J. Clark switch (cmd_rep->sub_cmd) { 337d69ccfcbSDerek J. Clark case PRODUCT_VERSION: 338d69ccfcbSDerek J. Clark switch (cmd_rep->device_type) { 339d69ccfcbSDerek J. Clark case USB_MCU: 340d69ccfcbSDerek J. Clark drvdata.mcu_version_product = 341d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 342d69ccfcbSDerek J. Clark return 0; 343d69ccfcbSDerek J. Clark case TX_DONGLE: 344d69ccfcbSDerek J. Clark drvdata.tx_dongle_version_product = 345d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 346d69ccfcbSDerek J. Clark return 0; 347d69ccfcbSDerek J. Clark case LEFT_CONTROLLER: 348d69ccfcbSDerek J. Clark drvdata.gp_left_version_product = 349d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 350d69ccfcbSDerek J. Clark return 0; 351d69ccfcbSDerek J. Clark case RIGHT_CONTROLLER: 352d69ccfcbSDerek J. Clark drvdata.gp_right_version_product = 353d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 354d69ccfcbSDerek J. Clark return 0; 355d69ccfcbSDerek J. Clark default: 356d69ccfcbSDerek J. Clark return -EINVAL; 357d69ccfcbSDerek J. Clark } 358d69ccfcbSDerek J. Clark case PROTOCOL_VERSION: 359d69ccfcbSDerek J. Clark switch (cmd_rep->device_type) { 360d69ccfcbSDerek J. Clark case USB_MCU: 361d69ccfcbSDerek J. Clark drvdata.mcu_version_protocol = 362d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 363d69ccfcbSDerek J. Clark return 0; 364d69ccfcbSDerek J. Clark case TX_DONGLE: 365d69ccfcbSDerek J. Clark drvdata.tx_dongle_version_protocol = 366d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 367d69ccfcbSDerek J. Clark return 0; 368d69ccfcbSDerek J. Clark case LEFT_CONTROLLER: 369d69ccfcbSDerek J. Clark drvdata.gp_left_version_protocol = 370d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 371d69ccfcbSDerek J. Clark return 0; 372d69ccfcbSDerek J. Clark case RIGHT_CONTROLLER: 373d69ccfcbSDerek J. Clark drvdata.gp_right_version_protocol = 374d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 375d69ccfcbSDerek J. Clark return 0; 376d69ccfcbSDerek J. Clark default: 377d69ccfcbSDerek J. Clark return -EINVAL; 378d69ccfcbSDerek J. Clark } 379d69ccfcbSDerek J. Clark case FIRMWARE_VERSION: 380d69ccfcbSDerek J. Clark switch (cmd_rep->device_type) { 381d69ccfcbSDerek J. Clark case USB_MCU: 382d69ccfcbSDerek J. Clark drvdata.mcu_version_firmware = 383d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 384d69ccfcbSDerek J. Clark return 0; 385d69ccfcbSDerek J. Clark case TX_DONGLE: 386d69ccfcbSDerek J. Clark drvdata.tx_dongle_version_firmware = 387d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 388d69ccfcbSDerek J. Clark return 0; 389d69ccfcbSDerek J. Clark case LEFT_CONTROLLER: 390d69ccfcbSDerek J. Clark drvdata.gp_left_version_firmware = 391d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 392d69ccfcbSDerek J. Clark return 0; 393d69ccfcbSDerek J. Clark case RIGHT_CONTROLLER: 394d69ccfcbSDerek J. Clark drvdata.gp_right_version_firmware = 395d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 396d69ccfcbSDerek J. Clark return 0; 397d69ccfcbSDerek J. Clark default: 398d69ccfcbSDerek J. Clark return -EINVAL; 399d69ccfcbSDerek J. Clark } 400d69ccfcbSDerek J. Clark case HARDWARE_VERSION: 401d69ccfcbSDerek J. Clark switch (cmd_rep->device_type) { 402d69ccfcbSDerek J. Clark case USB_MCU: 403d69ccfcbSDerek J. Clark drvdata.mcu_version_hardware = 404d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 405d69ccfcbSDerek J. Clark return 0; 406d69ccfcbSDerek J. Clark case TX_DONGLE: 407d69ccfcbSDerek J. Clark drvdata.tx_dongle_version_hardware = 408d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 409d69ccfcbSDerek J. Clark return 0; 410d69ccfcbSDerek J. Clark case LEFT_CONTROLLER: 411d69ccfcbSDerek J. Clark drvdata.gp_left_version_hardware = 412d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 413d69ccfcbSDerek J. Clark return 0; 414d69ccfcbSDerek J. Clark case RIGHT_CONTROLLER: 415d69ccfcbSDerek J. Clark drvdata.gp_right_version_hardware = 416d69ccfcbSDerek J. Clark get_unaligned_be32(cmd_rep->data); 417d69ccfcbSDerek J. Clark return 0; 418d69ccfcbSDerek J. Clark default: 419d69ccfcbSDerek J. Clark return -EINVAL; 420d69ccfcbSDerek J. Clark } 421d69ccfcbSDerek J. Clark case HARDWARE_GENERATION: 422d69ccfcbSDerek J. Clark switch (cmd_rep->device_type) { 423d69ccfcbSDerek J. Clark case USB_MCU: 424d69ccfcbSDerek J. Clark drvdata.mcu_version_gen = cmd_rep->data[0]; 425d69ccfcbSDerek J. Clark return 0; 426d69ccfcbSDerek J. Clark case TX_DONGLE: 427d69ccfcbSDerek J. Clark drvdata.tx_dongle_version_gen = cmd_rep->data[0]; 428d69ccfcbSDerek J. Clark return 0; 429d69ccfcbSDerek J. Clark case LEFT_CONTROLLER: 430d69ccfcbSDerek J. Clark drvdata.gp_left_version_gen = cmd_rep->data[0]; 431d69ccfcbSDerek J. Clark return 0; 432d69ccfcbSDerek J. Clark case RIGHT_CONTROLLER: 433d69ccfcbSDerek J. Clark drvdata.gp_right_version_gen = cmd_rep->data[0]; 434d69ccfcbSDerek J. Clark return 0; 435d69ccfcbSDerek J. Clark default: 436d69ccfcbSDerek J. Clark return -EINVAL; 437d69ccfcbSDerek J. Clark } 438d69ccfcbSDerek J. Clark default: 439d69ccfcbSDerek J. Clark return -EINVAL; 440d69ccfcbSDerek J. Clark } 441d69ccfcbSDerek J. Clark } 442d69ccfcbSDerek J. Clark 44382cd9bc8SDerek J. Clark static int hid_go_feature_status_event(struct command_report *cmd_rep) 44482cd9bc8SDerek J. Clark { 44582cd9bc8SDerek J. Clark switch (cmd_rep->sub_cmd) { 44682cd9bc8SDerek J. Clark case FEATURE_RESET_GAMEPAD: 44782cd9bc8SDerek J. Clark return 0; 44882cd9bc8SDerek J. Clark case FEATURE_IMU_ENABLE: 44982cd9bc8SDerek J. Clark switch (cmd_rep->device_type) { 45082cd9bc8SDerek J. Clark case LEFT_CONTROLLER: 45182cd9bc8SDerek J. Clark drvdata.imu_left_sensor_en = cmd_rep->data[0]; 45282cd9bc8SDerek J. Clark return 0; 45382cd9bc8SDerek J. Clark case RIGHT_CONTROLLER: 45482cd9bc8SDerek J. Clark drvdata.imu_right_sensor_en = cmd_rep->data[0]; 45582cd9bc8SDerek J. Clark return 0; 45682cd9bc8SDerek J. Clark default: 45782cd9bc8SDerek J. Clark return -EINVAL; 458*54549af8SChen Ni } 45982cd9bc8SDerek J. Clark case FEATURE_IMU_BYPASS: 46082cd9bc8SDerek J. Clark switch (cmd_rep->device_type) { 46182cd9bc8SDerek J. Clark case LEFT_CONTROLLER: 46282cd9bc8SDerek J. Clark drvdata.imu_left_bypass_en = cmd_rep->data[0]; 46382cd9bc8SDerek J. Clark return 0; 46482cd9bc8SDerek J. Clark case RIGHT_CONTROLLER: 46582cd9bc8SDerek J. Clark drvdata.imu_right_bypass_en = cmd_rep->data[0]; 46682cd9bc8SDerek J. Clark return 0; 46782cd9bc8SDerek J. Clark default: 46882cd9bc8SDerek J. Clark return -EINVAL; 469*54549af8SChen Ni } 47082cd9bc8SDerek J. Clark break; 47182cd9bc8SDerek J. Clark case FEATURE_LIGHT_ENABLE: 47282cd9bc8SDerek J. Clark drvdata.rgb_en = cmd_rep->data[0]; 47382cd9bc8SDerek J. Clark return 0; 47482cd9bc8SDerek J. Clark case FEATURE_AUTO_SLEEP_TIME: 47582cd9bc8SDerek J. Clark switch (cmd_rep->device_type) { 47682cd9bc8SDerek J. Clark case LEFT_CONTROLLER: 47782cd9bc8SDerek J. Clark drvdata.gp_left_auto_sleep_time = cmd_rep->data[0]; 47882cd9bc8SDerek J. Clark return 0; 47982cd9bc8SDerek J. Clark case RIGHT_CONTROLLER: 48082cd9bc8SDerek J. Clark drvdata.gp_right_auto_sleep_time = cmd_rep->data[0]; 48182cd9bc8SDerek J. Clark return 0; 48282cd9bc8SDerek J. Clark default: 48382cd9bc8SDerek J. Clark return -EINVAL; 484*54549af8SChen Ni } 48582cd9bc8SDerek J. Clark break; 48682cd9bc8SDerek J. Clark case FEATURE_TOUCHPAD_ENABLE: 48782cd9bc8SDerek J. Clark drvdata.tp_en = cmd_rep->data[0]; 48882cd9bc8SDerek J. Clark return 0; 48982cd9bc8SDerek J. Clark case FEATURE_GAMEPAD_MODE: 49082cd9bc8SDerek J. Clark drvdata.gp_mode = cmd_rep->data[0]; 49182cd9bc8SDerek J. Clark return 0; 49282cd9bc8SDerek J. Clark case FEATURE_FPS_SWITCH_STATUS: 49382cd9bc8SDerek J. Clark drvdata.fps_mode = cmd_rep->data[0]; 49482cd9bc8SDerek J. Clark return 0; 49582cd9bc8SDerek J. Clark default: 49682cd9bc8SDerek J. Clark return -EINVAL; 49782cd9bc8SDerek J. Clark } 49882cd9bc8SDerek J. Clark } 49982cd9bc8SDerek J. Clark 50096b20c1fSDerek J. Clark static int hid_go_motor_event(struct command_report *cmd_rep) 50196b20c1fSDerek J. Clark { 50296b20c1fSDerek J. Clark switch (cmd_rep->sub_cmd) { 50396b20c1fSDerek J. Clark case MOTOR_CFG_ALL: 50496b20c1fSDerek J. Clark return -EINVAL; 50596b20c1fSDerek J. Clark case MOTOR_INTENSITY: 50696b20c1fSDerek J. Clark drvdata.gp_rumble_intensity = cmd_rep->data[0]; 50796b20c1fSDerek J. Clark return 0; 50896b20c1fSDerek J. Clark case VIBRATION_NOTIFY_ENABLE: 50996b20c1fSDerek J. Clark switch (cmd_rep->device_type) { 51096b20c1fSDerek J. Clark case LEFT_CONTROLLER: 51196b20c1fSDerek J. Clark drvdata.gp_left_notify_en = cmd_rep->data[0]; 51296b20c1fSDerek J. Clark return 0; 51396b20c1fSDerek J. Clark case RIGHT_CONTROLLER: 51496b20c1fSDerek J. Clark drvdata.gp_right_notify_en = cmd_rep->data[0]; 51596b20c1fSDerek J. Clark return 0; 51696b20c1fSDerek J. Clark default: 51796b20c1fSDerek J. Clark return -EINVAL; 518*54549af8SChen Ni } 51996b20c1fSDerek J. Clark break; 52096b20c1fSDerek J. Clark case RUMBLE_MODE: 52196b20c1fSDerek J. Clark switch (cmd_rep->device_type) { 52296b20c1fSDerek J. Clark case LEFT_CONTROLLER: 52396b20c1fSDerek J. Clark drvdata.gp_left_rumble_mode = cmd_rep->data[0]; 52496b20c1fSDerek J. Clark return 0; 52596b20c1fSDerek J. Clark case RIGHT_CONTROLLER: 52696b20c1fSDerek J. Clark drvdata.gp_right_rumble_mode = cmd_rep->data[0]; 52796b20c1fSDerek J. Clark return 0; 52896b20c1fSDerek J. Clark default: 52996b20c1fSDerek J. Clark return -EINVAL; 530*54549af8SChen Ni } 53196b20c1fSDerek J. Clark case TP_VIBRATION_ENABLE: 53296b20c1fSDerek J. Clark drvdata.tp_vibration_en = cmd_rep->data[0]; 53396b20c1fSDerek J. Clark return 0; 53496b20c1fSDerek J. Clark case TP_VIBRATION_INTENSITY: 53596b20c1fSDerek J. Clark drvdata.tp_vibration_intensity = cmd_rep->data[0]; 53696b20c1fSDerek J. Clark return 0; 53796b20c1fSDerek J. Clark } 53896b20c1fSDerek J. Clark return -EINVAL; 53996b20c1fSDerek J. Clark } 54096b20c1fSDerek J. Clark 541f0bedee6SDerek J. Clark static int hid_go_fps_dpi_event(struct command_report *cmd_rep) 542f0bedee6SDerek J. Clark { 543f0bedee6SDerek J. Clark if (cmd_rep->sub_cmd != FPS_MODE_DPI) 544f0bedee6SDerek J. Clark return -EINVAL; 545f0bedee6SDerek J. Clark 546f0bedee6SDerek J. Clark drvdata.mouse_dpi = get_unaligned_le32(cmd_rep->data); 547f0bedee6SDerek J. Clark 548f0bedee6SDerek J. Clark return 0; 549f0bedee6SDerek J. Clark } 550f0bedee6SDerek J. Clark 551325262faSDerek J. Clark static int hid_go_light_event(struct command_report *cmd_rep) 552325262faSDerek J. Clark { 553325262faSDerek J. Clark struct led_classdev_mc *mc_cdev; 554325262faSDerek J. Clark 555325262faSDerek J. Clark switch (cmd_rep->sub_cmd) { 556325262faSDerek J. Clark case LIGHT_MODE_SEL: 557325262faSDerek J. Clark drvdata.rgb_mode = cmd_rep->data[0]; 558325262faSDerek J. Clark return 0; 559325262faSDerek J. Clark case LIGHT_PROFILE_SEL: 560325262faSDerek J. Clark drvdata.rgb_profile = cmd_rep->data[0]; 561325262faSDerek J. Clark return 0; 562325262faSDerek J. Clark case USR_LIGHT_PROFILE_1: 563325262faSDerek J. Clark case USR_LIGHT_PROFILE_2: 564325262faSDerek J. Clark case USR_LIGHT_PROFILE_3: 565325262faSDerek J. Clark mc_cdev = lcdev_to_mccdev(drvdata.led_cdev); 566325262faSDerek J. Clark drvdata.rgb_effect = cmd_rep->data[0]; 567325262faSDerek J. Clark mc_cdev->subled_info[0].intensity = cmd_rep->data[1]; 568325262faSDerek J. Clark mc_cdev->subled_info[1].intensity = cmd_rep->data[2]; 569325262faSDerek J. Clark mc_cdev->subled_info[2].intensity = cmd_rep->data[3]; 570325262faSDerek J. Clark drvdata.led_cdev->brightness = cmd_rep->data[4]; 571325262faSDerek J. Clark drvdata.rgb_speed = 100 - cmd_rep->data[5]; 572325262faSDerek J. Clark return 0; 573325262faSDerek J. Clark default: 574325262faSDerek J. Clark return -EINVAL; 575325262faSDerek J. Clark } 576325262faSDerek J. Clark } 577325262faSDerek J. Clark 578995887a1SDerek J. Clark static int hid_go_device_status_event(struct command_report *cmd_rep) 579995887a1SDerek J. Clark { 580995887a1SDerek J. Clark switch (cmd_rep->device_type) { 581995887a1SDerek J. Clark case LEFT_CONTROLLER: 582995887a1SDerek J. Clark switch (cmd_rep->data[0]) { 583995887a1SDerek J. Clark case CALDEV_GYROSCOPE: 584995887a1SDerek J. Clark drvdata.gp_left_gyro_cal_status = cmd_rep->data[1]; 585995887a1SDerek J. Clark return 0; 586995887a1SDerek J. Clark case CALDEV_JOYSTICK: 587995887a1SDerek J. Clark drvdata.gp_left_joy_cal_status = cmd_rep->data[1]; 588995887a1SDerek J. Clark return 0; 589995887a1SDerek J. Clark case CALDEV_TRIGGER: 590995887a1SDerek J. Clark drvdata.gp_left_trigg_cal_status = cmd_rep->data[1]; 591995887a1SDerek J. Clark return 0; 592995887a1SDerek J. Clark default: 593995887a1SDerek J. Clark return -EINVAL; 594995887a1SDerek J. Clark } 595995887a1SDerek J. Clark break; 596995887a1SDerek J. Clark case RIGHT_CONTROLLER: 597995887a1SDerek J. Clark switch (cmd_rep->data[0]) { 598995887a1SDerek J. Clark case CALDEV_GYROSCOPE: 599995887a1SDerek J. Clark drvdata.gp_right_gyro_cal_status = cmd_rep->data[1]; 600995887a1SDerek J. Clark return 0; 601995887a1SDerek J. Clark case CALDEV_JOYSTICK: 602995887a1SDerek J. Clark drvdata.gp_right_joy_cal_status = cmd_rep->data[1]; 603995887a1SDerek J. Clark return 0; 604995887a1SDerek J. Clark case CALDEV_TRIGGER: 605995887a1SDerek J. Clark drvdata.gp_right_trigg_cal_status = cmd_rep->data[1]; 606995887a1SDerek J. Clark return 0; 607995887a1SDerek J. Clark default: 608995887a1SDerek J. Clark return -EINVAL; 609995887a1SDerek J. Clark } 610995887a1SDerek J. Clark break; 611995887a1SDerek J. Clark default: 612995887a1SDerek J. Clark return -EINVAL; 613995887a1SDerek J. Clark } 614995887a1SDerek J. Clark } 615995887a1SDerek J. Clark 61651e4270cSDerek J. Clark static int hid_go_os_mode_cfg_event(struct command_report *cmd_rep) 61751e4270cSDerek J. Clark { 61851e4270cSDerek J. Clark switch (cmd_rep->sub_cmd) { 61951e4270cSDerek J. Clark case SET_OS_MODE: 62051e4270cSDerek J. Clark if (cmd_rep->data[0] != 1) 62151e4270cSDerek J. Clark return -EIO; 62251e4270cSDerek J. Clark return 0; 62351e4270cSDerek J. Clark case GET_OS_MODE: 62451e4270cSDerek J. Clark drvdata.os_mode = cmd_rep->data[0]; 62551e4270cSDerek J. Clark return 0; 62651e4270cSDerek J. Clark default: 62751e4270cSDerek J. Clark return -EINVAL; 628*54549af8SChen Ni } 62951e4270cSDerek J. Clark } 63051e4270cSDerek J. Clark 63182cd9bc8SDerek J. Clark static int hid_go_set_event_return(struct command_report *cmd_rep) 63282cd9bc8SDerek J. Clark { 63382cd9bc8SDerek J. Clark if (cmd_rep->data[0] != 0) 63482cd9bc8SDerek J. Clark return -EIO; 63582cd9bc8SDerek J. Clark 63682cd9bc8SDerek J. Clark return 0; 63782cd9bc8SDerek J. Clark } 63882cd9bc8SDerek J. Clark 639d69ccfcbSDerek J. Clark static int get_endpoint_address(struct hid_device *hdev) 640d69ccfcbSDerek J. Clark { 641d69ccfcbSDerek J. Clark struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 642d69ccfcbSDerek J. Clark struct usb_host_endpoint *ep; 643d69ccfcbSDerek J. Clark 644d69ccfcbSDerek J. Clark if (!intf) 645d69ccfcbSDerek J. Clark return -ENODEV; 646d69ccfcbSDerek J. Clark 647d69ccfcbSDerek J. Clark ep = intf->cur_altsetting->endpoint; 648d69ccfcbSDerek J. Clark if (!ep) 649d69ccfcbSDerek J. Clark return -ENODEV; 650d69ccfcbSDerek J. Clark 651d69ccfcbSDerek J. Clark return ep->desc.bEndpointAddress; 652d69ccfcbSDerek J. Clark } 653d69ccfcbSDerek J. Clark 654d69ccfcbSDerek J. Clark static int hid_go_raw_event(struct hid_device *hdev, struct hid_report *report, 655d69ccfcbSDerek J. Clark u8 *data, int size) 656d69ccfcbSDerek J. Clark { 657d69ccfcbSDerek J. Clark struct command_report *cmd_rep; 658d69ccfcbSDerek J. Clark int ep, ret; 659d69ccfcbSDerek J. Clark 660d69ccfcbSDerek J. Clark if (size != GO_PACKET_SIZE) 661d69ccfcbSDerek J. Clark goto passthrough; 662d69ccfcbSDerek J. Clark 663d69ccfcbSDerek J. Clark ep = get_endpoint_address(hdev); 664d69ccfcbSDerek J. Clark if (ep != GO_GP_INTF_IN) 665d69ccfcbSDerek J. Clark goto passthrough; 666d69ccfcbSDerek J. Clark 667d69ccfcbSDerek J. Clark cmd_rep = (struct command_report *)data; 668d69ccfcbSDerek J. Clark 669d69ccfcbSDerek J. Clark switch (cmd_rep->id) { 670d69ccfcbSDerek J. Clark case MCU_CONFIG_DATA: 671d69ccfcbSDerek J. Clark switch (cmd_rep->cmd) { 672d69ccfcbSDerek J. Clark case GET_VERSION_DATA: 673d69ccfcbSDerek J. Clark ret = hid_go_version_event(cmd_rep); 674d69ccfcbSDerek J. Clark break; 67582cd9bc8SDerek J. Clark case GET_FEATURE_STATUS: 67682cd9bc8SDerek J. Clark ret = hid_go_feature_status_event(cmd_rep); 67782cd9bc8SDerek J. Clark break; 67896b20c1fSDerek J. Clark case GET_MOTOR_CFG: 67996b20c1fSDerek J. Clark ret = hid_go_motor_event(cmd_rep); 68096b20c1fSDerek J. Clark break; 681f0bedee6SDerek J. Clark case GET_DPI_CFG: 682f0bedee6SDerek J. Clark ret = hid_go_fps_dpi_event(cmd_rep); 683f0bedee6SDerek J. Clark break; 684325262faSDerek J. Clark case GET_RGB_CFG: 685325262faSDerek J. Clark ret = hid_go_light_event(cmd_rep); 686325262faSDerek J. Clark break; 687995887a1SDerek J. Clark case GET_DEVICE_STATUS: 688995887a1SDerek J. Clark ret = hid_go_device_status_event(cmd_rep); 689995887a1SDerek J. Clark break; 69082cd9bc8SDerek J. Clark case SET_FEATURE_STATUS: 69196b20c1fSDerek J. Clark case SET_MOTOR_CFG: 692f0bedee6SDerek J. Clark case SET_DPI_CFG: 693325262faSDerek J. Clark case SET_RGB_CFG: 694995887a1SDerek J. Clark case SET_TRIGGER_CFG: 695995887a1SDerek J. Clark case SET_JOYSTICK_CFG: 696995887a1SDerek J. Clark case SET_GYRO_CFG: 69782cd9bc8SDerek J. Clark ret = hid_go_set_event_return(cmd_rep); 69882cd9bc8SDerek J. Clark break; 699d69ccfcbSDerek J. Clark default: 700d69ccfcbSDerek J. Clark ret = -EINVAL; 701d69ccfcbSDerek J. Clark break; 702*54549af8SChen Ni } 703d69ccfcbSDerek J. Clark break; 70451e4270cSDerek J. Clark case OS_MODE_DATA: 70551e4270cSDerek J. Clark ret = hid_go_os_mode_cfg_event(cmd_rep); 70651e4270cSDerek J. Clark break; 707d69ccfcbSDerek J. Clark default: 708d69ccfcbSDerek J. Clark goto passthrough; 709*54549af8SChen Ni } 710d69ccfcbSDerek J. Clark dev_dbg(&hdev->dev, "Rx data as raw input report: [%*ph]\n", 711d69ccfcbSDerek J. Clark GO_PACKET_SIZE, data); 712d69ccfcbSDerek J. Clark 713d69ccfcbSDerek J. Clark complete(&drvdata.send_cmd_complete); 714d69ccfcbSDerek J. Clark return ret; 715d69ccfcbSDerek J. Clark 716d69ccfcbSDerek J. Clark passthrough: 717d69ccfcbSDerek J. Clark /* Forward other HID reports so they generate events */ 718d69ccfcbSDerek J. Clark hid_input_report(hdev, HID_INPUT_REPORT, data, size, 1); 719d69ccfcbSDerek J. Clark return 0; 720d69ccfcbSDerek J. Clark } 721d69ccfcbSDerek J. Clark 722d69ccfcbSDerek J. Clark static int mcu_property_out(struct hid_device *hdev, u8 id, u8 command, 723d69ccfcbSDerek J. Clark u8 index, enum dev_type device, u8 *data, size_t len) 724d69ccfcbSDerek J. Clark { 725d69ccfcbSDerek J. Clark unsigned char *dmabuf __free(kfree) = NULL; 726d69ccfcbSDerek J. Clark u8 header[] = { GO_OUTPUT_REPORT_ID, id, command, index, device }; 727d69ccfcbSDerek J. Clark size_t header_size = ARRAY_SIZE(header); 728d69ccfcbSDerek J. Clark int timeout = 50; 729d69ccfcbSDerek J. Clark int ret; 730d69ccfcbSDerek J. Clark 731d69ccfcbSDerek J. Clark if (header_size + len > GO_PACKET_SIZE) 732d69ccfcbSDerek J. Clark return -EINVAL; 733d69ccfcbSDerek J. Clark 734d69ccfcbSDerek J. Clark guard(mutex)(&drvdata.cfg_mutex); 735d69ccfcbSDerek J. Clark /* We can't use a devm_alloc reusable buffer without side effects during suspend */ 736d69ccfcbSDerek J. Clark dmabuf = kzalloc(GO_PACKET_SIZE, GFP_KERNEL); 737d69ccfcbSDerek J. Clark if (!dmabuf) 738d69ccfcbSDerek J. Clark return -ENOMEM; 739d69ccfcbSDerek J. Clark 740d69ccfcbSDerek J. Clark memcpy(dmabuf, header, header_size); 741d69ccfcbSDerek J. Clark memcpy(dmabuf + header_size, data, len); 742d69ccfcbSDerek J. Clark 743d69ccfcbSDerek J. Clark dev_dbg(&hdev->dev, "Send data as raw output report: [%*ph]\n", 744d69ccfcbSDerek J. Clark GO_PACKET_SIZE, dmabuf); 745d69ccfcbSDerek J. Clark 746d69ccfcbSDerek J. Clark ret = hid_hw_output_report(hdev, dmabuf, GO_PACKET_SIZE); 747d69ccfcbSDerek J. Clark if (ret < 0) 748d69ccfcbSDerek J. Clark return ret; 749d69ccfcbSDerek J. Clark 750d69ccfcbSDerek J. Clark ret = ret == GO_PACKET_SIZE ? 0 : -EINVAL; 751d69ccfcbSDerek J. Clark if (ret) 752d69ccfcbSDerek J. Clark return ret; 753d69ccfcbSDerek J. Clark 754d69ccfcbSDerek J. Clark ret = wait_for_completion_interruptible_timeout(&drvdata.send_cmd_complete, 755d69ccfcbSDerek J. Clark msecs_to_jiffies(timeout)); 756d69ccfcbSDerek J. Clark 757d69ccfcbSDerek J. Clark if (ret == 0) /* timeout occurred */ 758d69ccfcbSDerek J. Clark ret = -EBUSY; 759d69ccfcbSDerek J. Clark 760d69ccfcbSDerek J. Clark reinit_completion(&drvdata.send_cmd_complete); 761d69ccfcbSDerek J. Clark return 0; 762d69ccfcbSDerek J. Clark } 763d69ccfcbSDerek J. Clark 764d69ccfcbSDerek J. Clark static ssize_t version_show(struct device *dev, struct device_attribute *attr, 765d69ccfcbSDerek J. Clark char *buf, enum version_data_index index, 766d69ccfcbSDerek J. Clark enum dev_type device_type) 767d69ccfcbSDerek J. Clark { 768d69ccfcbSDerek J. Clark ssize_t count = 0; 769325262faSDerek J. Clark int ret; 770325262faSDerek J. Clark 771325262faSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 772325262faSDerek J. Clark index, device_type, NULL, 0); 773325262faSDerek J. Clark if (ret) 774325262faSDerek J. Clark return ret; 775d69ccfcbSDerek J. Clark 776d69ccfcbSDerek J. Clark switch (index) { 777d69ccfcbSDerek J. Clark case PRODUCT_VERSION: 778d69ccfcbSDerek J. Clark switch (device_type) { 779d69ccfcbSDerek J. Clark case USB_MCU: 780d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 781d69ccfcbSDerek J. Clark drvdata.mcu_version_product); 782d69ccfcbSDerek J. Clark break; 783d69ccfcbSDerek J. Clark case TX_DONGLE: 784d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 785d69ccfcbSDerek J. Clark drvdata.tx_dongle_version_product); 786d69ccfcbSDerek J. Clark break; 787d69ccfcbSDerek J. Clark case LEFT_CONTROLLER: 788d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 789d69ccfcbSDerek J. Clark drvdata.gp_left_version_product); 790d69ccfcbSDerek J. Clark break; 791d69ccfcbSDerek J. Clark case RIGHT_CONTROLLER: 792d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 793d69ccfcbSDerek J. Clark drvdata.gp_right_version_product); 794d69ccfcbSDerek J. Clark break; 795d69ccfcbSDerek J. Clark default: 796d69ccfcbSDerek J. Clark return -EINVAL; 797d69ccfcbSDerek J. Clark } 798d69ccfcbSDerek J. Clark break; 799d69ccfcbSDerek J. Clark case PROTOCOL_VERSION: 800d69ccfcbSDerek J. Clark switch (device_type) { 801d69ccfcbSDerek J. Clark case USB_MCU: 802d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 803d69ccfcbSDerek J. Clark drvdata.mcu_version_protocol); 804d69ccfcbSDerek J. Clark break; 805d69ccfcbSDerek J. Clark case TX_DONGLE: 806d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 807d69ccfcbSDerek J. Clark drvdata.tx_dongle_version_protocol); 808d69ccfcbSDerek J. Clark break; 809d69ccfcbSDerek J. Clark case LEFT_CONTROLLER: 810d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 811d69ccfcbSDerek J. Clark drvdata.gp_left_version_protocol); 812d69ccfcbSDerek J. Clark break; 813d69ccfcbSDerek J. Clark case RIGHT_CONTROLLER: 814d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 815d69ccfcbSDerek J. Clark drvdata.gp_right_version_protocol); 816d69ccfcbSDerek J. Clark break; 817d69ccfcbSDerek J. Clark default: 818d69ccfcbSDerek J. Clark return -EINVAL; 819d69ccfcbSDerek J. Clark } 820d69ccfcbSDerek J. Clark break; 821d69ccfcbSDerek J. Clark case FIRMWARE_VERSION: 822d69ccfcbSDerek J. Clark switch (device_type) { 823d69ccfcbSDerek J. Clark case USB_MCU: 824d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 825d69ccfcbSDerek J. Clark drvdata.mcu_version_firmware); 826d69ccfcbSDerek J. Clark break; 827d69ccfcbSDerek J. Clark case TX_DONGLE: 828d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 829d69ccfcbSDerek J. Clark drvdata.tx_dongle_version_firmware); 830d69ccfcbSDerek J. Clark break; 831d69ccfcbSDerek J. Clark case LEFT_CONTROLLER: 832d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 833d69ccfcbSDerek J. Clark drvdata.gp_left_version_firmware); 834d69ccfcbSDerek J. Clark break; 835d69ccfcbSDerek J. Clark case RIGHT_CONTROLLER: 836d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 837d69ccfcbSDerek J. Clark drvdata.gp_right_version_firmware); 838d69ccfcbSDerek J. Clark break; 839d69ccfcbSDerek J. Clark default: 840d69ccfcbSDerek J. Clark return -EINVAL; 841d69ccfcbSDerek J. Clark } 842d69ccfcbSDerek J. Clark break; 843d69ccfcbSDerek J. Clark case HARDWARE_VERSION: 844d69ccfcbSDerek J. Clark switch (device_type) { 845d69ccfcbSDerek J. Clark case USB_MCU: 846d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 847d69ccfcbSDerek J. Clark drvdata.mcu_version_hardware); 848d69ccfcbSDerek J. Clark break; 849d69ccfcbSDerek J. Clark case TX_DONGLE: 850d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 851d69ccfcbSDerek J. Clark drvdata.tx_dongle_version_hardware); 852d69ccfcbSDerek J. Clark break; 853d69ccfcbSDerek J. Clark case LEFT_CONTROLLER: 854d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 855d69ccfcbSDerek J. Clark drvdata.gp_left_version_hardware); 856d69ccfcbSDerek J. Clark break; 857d69ccfcbSDerek J. Clark case RIGHT_CONTROLLER: 858d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 859d69ccfcbSDerek J. Clark drvdata.gp_right_version_hardware); 860d69ccfcbSDerek J. Clark break; 861d69ccfcbSDerek J. Clark default: 862d69ccfcbSDerek J. Clark return -EINVAL; 863d69ccfcbSDerek J. Clark } 864d69ccfcbSDerek J. Clark break; 865d69ccfcbSDerek J. Clark case HARDWARE_GENERATION: 866d69ccfcbSDerek J. Clark switch (device_type) { 867d69ccfcbSDerek J. Clark case USB_MCU: 868d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 869d69ccfcbSDerek J. Clark drvdata.mcu_version_gen); 870d69ccfcbSDerek J. Clark break; 871d69ccfcbSDerek J. Clark case TX_DONGLE: 872d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 873d69ccfcbSDerek J. Clark drvdata.tx_dongle_version_gen); 874d69ccfcbSDerek J. Clark break; 875d69ccfcbSDerek J. Clark case LEFT_CONTROLLER: 876d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 877d69ccfcbSDerek J. Clark drvdata.gp_left_version_gen); 878d69ccfcbSDerek J. Clark break; 879d69ccfcbSDerek J. Clark case RIGHT_CONTROLLER: 880d69ccfcbSDerek J. Clark count = sysfs_emit(buf, "%x\n", 881d69ccfcbSDerek J. Clark drvdata.gp_right_version_gen); 882d69ccfcbSDerek J. Clark break; 883d69ccfcbSDerek J. Clark default: 884d69ccfcbSDerek J. Clark return -EINVAL; 885d69ccfcbSDerek J. Clark } 886d69ccfcbSDerek J. Clark break; 887d69ccfcbSDerek J. Clark } 888d69ccfcbSDerek J. Clark 889d69ccfcbSDerek J. Clark return count; 890d69ccfcbSDerek J. Clark } 891d69ccfcbSDerek J. Clark 89282cd9bc8SDerek J. Clark static ssize_t feature_status_store(struct device *dev, 89382cd9bc8SDerek J. Clark struct device_attribute *attr, 89482cd9bc8SDerek J. Clark const char *buf, size_t count, 89582cd9bc8SDerek J. Clark enum feature_status_index index, 89682cd9bc8SDerek J. Clark enum dev_type device_type) 89782cd9bc8SDerek J. Clark { 89882cd9bc8SDerek J. Clark size_t size = 1; 89982cd9bc8SDerek J. Clark u8 val = 0; 90082cd9bc8SDerek J. Clark int ret; 90182cd9bc8SDerek J. Clark 90282cd9bc8SDerek J. Clark switch (index) { 90382cd9bc8SDerek J. Clark case FEATURE_IMU_ENABLE: 90482cd9bc8SDerek J. Clark case FEATURE_IMU_BYPASS: 90582cd9bc8SDerek J. Clark case FEATURE_LIGHT_ENABLE: 90682cd9bc8SDerek J. Clark case FEATURE_TOUCHPAD_ENABLE: 90782cd9bc8SDerek J. Clark ret = sysfs_match_string(enabled_status_text, buf); 90882cd9bc8SDerek J. Clark val = ret; 90982cd9bc8SDerek J. Clark break; 91082cd9bc8SDerek J. Clark case FEATURE_AUTO_SLEEP_TIME: 91182cd9bc8SDerek J. Clark ret = kstrtou8(buf, 10, &val); 91282cd9bc8SDerek J. Clark break; 91382cd9bc8SDerek J. Clark case FEATURE_RESET_GAMEPAD: 91482cd9bc8SDerek J. Clark ret = kstrtou8(buf, 10, &val); 91582cd9bc8SDerek J. Clark if (val != GO_GP_RESET_SUCCESS) 91682cd9bc8SDerek J. Clark return -EINVAL; 91782cd9bc8SDerek J. Clark break; 91882cd9bc8SDerek J. Clark case FEATURE_FPS_SWITCH_STATUS: 91982cd9bc8SDerek J. Clark ret = sysfs_match_string(fps_switch_text, buf); 92082cd9bc8SDerek J. Clark val = ret; 92182cd9bc8SDerek J. Clark break; 92282cd9bc8SDerek J. Clark case FEATURE_GAMEPAD_MODE: 92382cd9bc8SDerek J. Clark ret = sysfs_match_string(gamepad_mode_text, buf); 92482cd9bc8SDerek J. Clark val = ret; 92582cd9bc8SDerek J. Clark break; 92682cd9bc8SDerek J. Clark default: 92782cd9bc8SDerek J. Clark return -EINVAL; 928*54549af8SChen Ni } 92982cd9bc8SDerek J. Clark 93082cd9bc8SDerek J. Clark if (ret < 0) 93182cd9bc8SDerek J. Clark return ret; 93282cd9bc8SDerek J. Clark 93382cd9bc8SDerek J. Clark if (!val) 93482cd9bc8SDerek J. Clark size = 0; 93582cd9bc8SDerek J. Clark 93682cd9bc8SDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, 93782cd9bc8SDerek J. Clark SET_FEATURE_STATUS, index, device_type, &val, 93882cd9bc8SDerek J. Clark size); 93982cd9bc8SDerek J. Clark if (ret < 0) 94082cd9bc8SDerek J. Clark return ret; 94182cd9bc8SDerek J. Clark 94282cd9bc8SDerek J. Clark return count; 94382cd9bc8SDerek J. Clark } 94482cd9bc8SDerek J. Clark 94582cd9bc8SDerek J. Clark static ssize_t feature_status_show(struct device *dev, 94682cd9bc8SDerek J. Clark struct device_attribute *attr, char *buf, 94782cd9bc8SDerek J. Clark enum feature_status_index index, 94882cd9bc8SDerek J. Clark enum dev_type device_type) 94982cd9bc8SDerek J. Clark { 95082cd9bc8SDerek J. Clark ssize_t count = 0; 95182cd9bc8SDerek J. Clark int ret; 95282cd9bc8SDerek J. Clark u8 i; 95382cd9bc8SDerek J. Clark 95482cd9bc8SDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, 95582cd9bc8SDerek J. Clark GET_FEATURE_STATUS, index, device_type, NULL, 0); 95682cd9bc8SDerek J. Clark if (ret) 95782cd9bc8SDerek J. Clark return ret; 95882cd9bc8SDerek J. Clark 95982cd9bc8SDerek J. Clark switch (index) { 96082cd9bc8SDerek J. Clark case FEATURE_IMU_ENABLE: 96182cd9bc8SDerek J. Clark switch (device_type) { 96282cd9bc8SDerek J. Clark case LEFT_CONTROLLER: 96382cd9bc8SDerek J. Clark i = drvdata.imu_left_sensor_en; 96482cd9bc8SDerek J. Clark break; 96582cd9bc8SDerek J. Clark case RIGHT_CONTROLLER: 96682cd9bc8SDerek J. Clark i = drvdata.imu_right_sensor_en; 96782cd9bc8SDerek J. Clark break; 96882cd9bc8SDerek J. Clark default: 96982cd9bc8SDerek J. Clark return -EINVAL; 97082cd9bc8SDerek J. Clark } 97182cd9bc8SDerek J. Clark if (i >= ARRAY_SIZE(enabled_status_text)) 97282cd9bc8SDerek J. Clark return -EINVAL; 97382cd9bc8SDerek J. Clark 97482cd9bc8SDerek J. Clark count = sysfs_emit(buf, "%s\n", enabled_status_text[i]); 97582cd9bc8SDerek J. Clark break; 97682cd9bc8SDerek J. Clark case FEATURE_IMU_BYPASS: 97782cd9bc8SDerek J. Clark switch (device_type) { 97882cd9bc8SDerek J. Clark case LEFT_CONTROLLER: 97982cd9bc8SDerek J. Clark i = drvdata.imu_left_bypass_en; 98082cd9bc8SDerek J. Clark break; 98182cd9bc8SDerek J. Clark case RIGHT_CONTROLLER: 98282cd9bc8SDerek J. Clark i = drvdata.imu_right_bypass_en; 98382cd9bc8SDerek J. Clark break; 98482cd9bc8SDerek J. Clark default: 98582cd9bc8SDerek J. Clark return -EINVAL; 98682cd9bc8SDerek J. Clark } 98782cd9bc8SDerek J. Clark if (i >= ARRAY_SIZE(enabled_status_text)) 98882cd9bc8SDerek J. Clark return -EINVAL; 98982cd9bc8SDerek J. Clark 99082cd9bc8SDerek J. Clark count = sysfs_emit(buf, "%s\n", enabled_status_text[i]); 99182cd9bc8SDerek J. Clark break; 99282cd9bc8SDerek J. Clark case FEATURE_LIGHT_ENABLE: 99382cd9bc8SDerek J. Clark i = drvdata.rgb_en; 99482cd9bc8SDerek J. Clark if (i >= ARRAY_SIZE(enabled_status_text)) 99582cd9bc8SDerek J. Clark return -EINVAL; 99682cd9bc8SDerek J. Clark 99782cd9bc8SDerek J. Clark count = sysfs_emit(buf, "%s\n", enabled_status_text[i]); 99882cd9bc8SDerek J. Clark break; 99982cd9bc8SDerek J. Clark case FEATURE_TOUCHPAD_ENABLE: 100082cd9bc8SDerek J. Clark i = drvdata.tp_en; 100182cd9bc8SDerek J. Clark if (i >= ARRAY_SIZE(enabled_status_text)) 100282cd9bc8SDerek J. Clark return -EINVAL; 100382cd9bc8SDerek J. Clark 100482cd9bc8SDerek J. Clark count = sysfs_emit(buf, "%s\n", enabled_status_text[i]); 100582cd9bc8SDerek J. Clark break; 100682cd9bc8SDerek J. Clark case FEATURE_AUTO_SLEEP_TIME: 100782cd9bc8SDerek J. Clark switch (device_type) { 100882cd9bc8SDerek J. Clark case LEFT_CONTROLLER: 100982cd9bc8SDerek J. Clark i = drvdata.gp_left_auto_sleep_time; 101082cd9bc8SDerek J. Clark break; 101182cd9bc8SDerek J. Clark case RIGHT_CONTROLLER: 101282cd9bc8SDerek J. Clark i = drvdata.gp_right_auto_sleep_time; 101382cd9bc8SDerek J. Clark break; 101482cd9bc8SDerek J. Clark default: 101582cd9bc8SDerek J. Clark return -EINVAL; 1016*54549af8SChen Ni } 101782cd9bc8SDerek J. Clark count = sysfs_emit(buf, "%u\n", i); 101882cd9bc8SDerek J. Clark break; 101982cd9bc8SDerek J. Clark case FEATURE_FPS_SWITCH_STATUS: 102082cd9bc8SDerek J. Clark i = drvdata.fps_mode; 102182cd9bc8SDerek J. Clark if (i >= ARRAY_SIZE(fps_switch_text)) 102282cd9bc8SDerek J. Clark return -EINVAL; 102382cd9bc8SDerek J. Clark 102482cd9bc8SDerek J. Clark count = sysfs_emit(buf, "%s\n", fps_switch_text[i]); 102582cd9bc8SDerek J. Clark break; 102682cd9bc8SDerek J. Clark case FEATURE_GAMEPAD_MODE: 102782cd9bc8SDerek J. Clark i = drvdata.gp_mode; 102882cd9bc8SDerek J. Clark if (i >= ARRAY_SIZE(gamepad_mode_text)) 102982cd9bc8SDerek J. Clark return -EINVAL; 103082cd9bc8SDerek J. Clark 103182cd9bc8SDerek J. Clark count = sysfs_emit(buf, "%s\n", gamepad_mode_text[i]); 103282cd9bc8SDerek J. Clark break; 103382cd9bc8SDerek J. Clark default: 103482cd9bc8SDerek J. Clark return -EINVAL; 1035*54549af8SChen Ni } 103682cd9bc8SDerek J. Clark 103782cd9bc8SDerek J. Clark return count; 103882cd9bc8SDerek J. Clark } 103982cd9bc8SDerek J. Clark 104082cd9bc8SDerek J. Clark static ssize_t feature_status_options(struct device *dev, 104182cd9bc8SDerek J. Clark struct device_attribute *attr, char *buf, 104282cd9bc8SDerek J. Clark enum feature_status_index index) 104382cd9bc8SDerek J. Clark { 104482cd9bc8SDerek J. Clark ssize_t count = 0; 104582cd9bc8SDerek J. Clark unsigned int i; 104682cd9bc8SDerek J. Clark 104782cd9bc8SDerek J. Clark switch (index) { 104882cd9bc8SDerek J. Clark case FEATURE_IMU_ENABLE: 104982cd9bc8SDerek J. Clark case FEATURE_IMU_BYPASS: 105082cd9bc8SDerek J. Clark case FEATURE_LIGHT_ENABLE: 105182cd9bc8SDerek J. Clark case FEATURE_TOUCHPAD_ENABLE: 105282cd9bc8SDerek J. Clark for (i = 1; i < ARRAY_SIZE(enabled_status_text); i++) { 105382cd9bc8SDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", 105482cd9bc8SDerek J. Clark enabled_status_text[i]); 105582cd9bc8SDerek J. Clark } 105682cd9bc8SDerek J. Clark break; 105782cd9bc8SDerek J. Clark case FEATURE_AUTO_SLEEP_TIME: 105882cd9bc8SDerek J. Clark return sysfs_emit(buf, "0-255\n"); 105982cd9bc8SDerek J. Clark case FEATURE_FPS_SWITCH_STATUS: 106082cd9bc8SDerek J. Clark for (i = 1; i < ARRAY_SIZE(fps_switch_text); i++) { 106182cd9bc8SDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", 106282cd9bc8SDerek J. Clark fps_switch_text[i]); 106382cd9bc8SDerek J. Clark } 106482cd9bc8SDerek J. Clark break; 106582cd9bc8SDerek J. Clark case FEATURE_GAMEPAD_MODE: 106682cd9bc8SDerek J. Clark for (i = 1; i < ARRAY_SIZE(gamepad_mode_text); i++) { 106782cd9bc8SDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", 106882cd9bc8SDerek J. Clark gamepad_mode_text[i]); 106982cd9bc8SDerek J. Clark } 107082cd9bc8SDerek J. Clark break; 107182cd9bc8SDerek J. Clark default: 107282cd9bc8SDerek J. Clark return -EINVAL; 1073*54549af8SChen Ni } 107482cd9bc8SDerek J. Clark 107582cd9bc8SDerek J. Clark if (count) 107682cd9bc8SDerek J. Clark buf[count - 1] = '\n'; 107782cd9bc8SDerek J. Clark 107882cd9bc8SDerek J. Clark return count; 107982cd9bc8SDerek J. Clark } 108082cd9bc8SDerek J. Clark 108196b20c1fSDerek J. Clark static ssize_t motor_config_store(struct device *dev, 108296b20c1fSDerek J. Clark struct device_attribute *attr, 108396b20c1fSDerek J. Clark const char *buf, size_t count, 108496b20c1fSDerek J. Clark enum motor_cfg_index index, 108596b20c1fSDerek J. Clark enum dev_type device_type) 108696b20c1fSDerek J. Clark { 108796b20c1fSDerek J. Clark size_t size = 1; 108896b20c1fSDerek J. Clark u8 val = 0; 108996b20c1fSDerek J. Clark int ret; 109096b20c1fSDerek J. Clark 109196b20c1fSDerek J. Clark switch (index) { 109296b20c1fSDerek J. Clark case MOTOR_CFG_ALL: 109396b20c1fSDerek J. Clark return -EINVAL; 109496b20c1fSDerek J. Clark case MOTOR_INTENSITY: 109596b20c1fSDerek J. Clark ret = sysfs_match_string(intensity_text, buf); 109696b20c1fSDerek J. Clark val = ret; 109796b20c1fSDerek J. Clark break; 109896b20c1fSDerek J. Clark case VIBRATION_NOTIFY_ENABLE: 109996b20c1fSDerek J. Clark ret = sysfs_match_string(enabled_status_text, buf); 110096b20c1fSDerek J. Clark val = ret; 110196b20c1fSDerek J. Clark break; 110296b20c1fSDerek J. Clark case RUMBLE_MODE: 110396b20c1fSDerek J. Clark ret = sysfs_match_string(rumble_mode_text, buf); 110496b20c1fSDerek J. Clark val = ret; 110596b20c1fSDerek J. Clark break; 110696b20c1fSDerek J. Clark case TP_VIBRATION_ENABLE: 110796b20c1fSDerek J. Clark ret = sysfs_match_string(enabled_status_text, buf); 110896b20c1fSDerek J. Clark val = ret; 110996b20c1fSDerek J. Clark break; 111096b20c1fSDerek J. Clark case TP_VIBRATION_INTENSITY: 111196b20c1fSDerek J. Clark ret = sysfs_match_string(intensity_text, buf); 111296b20c1fSDerek J. Clark val = ret; 111396b20c1fSDerek J. Clark break; 1114*54549af8SChen Ni } 111596b20c1fSDerek J. Clark 111696b20c1fSDerek J. Clark if (ret < 0) 111796b20c1fSDerek J. Clark return ret; 111896b20c1fSDerek J. Clark 111996b20c1fSDerek J. Clark if (!val) 112096b20c1fSDerek J. Clark size = 0; 112196b20c1fSDerek J. Clark 112296b20c1fSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, SET_MOTOR_CFG, 112396b20c1fSDerek J. Clark index, device_type, &val, size); 112496b20c1fSDerek J. Clark if (ret < 0) 112596b20c1fSDerek J. Clark return ret; 112696b20c1fSDerek J. Clark 112796b20c1fSDerek J. Clark return count; 112896b20c1fSDerek J. Clark } 112996b20c1fSDerek J. Clark 113096b20c1fSDerek J. Clark static ssize_t motor_config_show(struct device *dev, 113196b20c1fSDerek J. Clark struct device_attribute *attr, char *buf, 113296b20c1fSDerek J. Clark enum motor_cfg_index index, 113396b20c1fSDerek J. Clark enum dev_type device_type) 113496b20c1fSDerek J. Clark { 113596b20c1fSDerek J. Clark ssize_t count = 0; 113696b20c1fSDerek J. Clark int ret; 113796b20c1fSDerek J. Clark u8 i; 113896b20c1fSDerek J. Clark 113996b20c1fSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_MOTOR_CFG, 114096b20c1fSDerek J. Clark index, device_type, NULL, 0); 114196b20c1fSDerek J. Clark if (ret) 114296b20c1fSDerek J. Clark return ret; 114396b20c1fSDerek J. Clark 114496b20c1fSDerek J. Clark switch (index) { 114596b20c1fSDerek J. Clark case MOTOR_CFG_ALL: 114696b20c1fSDerek J. Clark return -EINVAL; 114796b20c1fSDerek J. Clark case MOTOR_INTENSITY: 114896b20c1fSDerek J. Clark i = drvdata.gp_rumble_intensity; 114996b20c1fSDerek J. Clark if (i >= ARRAY_SIZE(intensity_text)) 115096b20c1fSDerek J. Clark return -EINVAL; 115196b20c1fSDerek J. Clark 115296b20c1fSDerek J. Clark count = sysfs_emit(buf, "%s\n", intensity_text[i]); 115396b20c1fSDerek J. Clark break; 115496b20c1fSDerek J. Clark case VIBRATION_NOTIFY_ENABLE: 115596b20c1fSDerek J. Clark switch (device_type) { 115696b20c1fSDerek J. Clark case LEFT_CONTROLLER: 115796b20c1fSDerek J. Clark i = drvdata.gp_left_notify_en; 115896b20c1fSDerek J. Clark break; 115996b20c1fSDerek J. Clark case RIGHT_CONTROLLER: 116096b20c1fSDerek J. Clark i = drvdata.gp_right_notify_en; 116196b20c1fSDerek J. Clark break; 116296b20c1fSDerek J. Clark default: 116396b20c1fSDerek J. Clark return -EINVAL; 1164*54549af8SChen Ni } 116596b20c1fSDerek J. Clark if (i >= ARRAY_SIZE(enabled_status_text)) 116696b20c1fSDerek J. Clark return -EINVAL; 116796b20c1fSDerek J. Clark 116896b20c1fSDerek J. Clark count = sysfs_emit(buf, "%s\n", enabled_status_text[i]); 116996b20c1fSDerek J. Clark break; 117096b20c1fSDerek J. Clark case RUMBLE_MODE: 117196b20c1fSDerek J. Clark switch (device_type) { 117296b20c1fSDerek J. Clark case LEFT_CONTROLLER: 117396b20c1fSDerek J. Clark i = drvdata.gp_left_rumble_mode; 117496b20c1fSDerek J. Clark break; 117596b20c1fSDerek J. Clark case RIGHT_CONTROLLER: 117696b20c1fSDerek J. Clark i = drvdata.gp_right_rumble_mode; 117796b20c1fSDerek J. Clark break; 117896b20c1fSDerek J. Clark default: 117996b20c1fSDerek J. Clark return -EINVAL; 1180*54549af8SChen Ni } 118196b20c1fSDerek J. Clark if (i >= ARRAY_SIZE(rumble_mode_text)) 118296b20c1fSDerek J. Clark return -EINVAL; 118396b20c1fSDerek J. Clark 118496b20c1fSDerek J. Clark count = sysfs_emit(buf, "%s\n", rumble_mode_text[i]); 118596b20c1fSDerek J. Clark break; 118696b20c1fSDerek J. Clark case TP_VIBRATION_ENABLE: 118796b20c1fSDerek J. Clark i = drvdata.tp_vibration_en; 118896b20c1fSDerek J. Clark if (i >= ARRAY_SIZE(enabled_status_text)) 118996b20c1fSDerek J. Clark return -EINVAL; 119096b20c1fSDerek J. Clark 119196b20c1fSDerek J. Clark count = sysfs_emit(buf, "%s\n", enabled_status_text[i]); 119296b20c1fSDerek J. Clark break; 119396b20c1fSDerek J. Clark case TP_VIBRATION_INTENSITY: 119496b20c1fSDerek J. Clark i = drvdata.tp_vibration_intensity; 119596b20c1fSDerek J. Clark if (i >= ARRAY_SIZE(intensity_text)) 119696b20c1fSDerek J. Clark return -EINVAL; 119796b20c1fSDerek J. Clark 119896b20c1fSDerek J. Clark count = sysfs_emit(buf, "%s\n", intensity_text[i]); 119996b20c1fSDerek J. Clark break; 1200*54549af8SChen Ni } 120196b20c1fSDerek J. Clark 120296b20c1fSDerek J. Clark return count; 120396b20c1fSDerek J. Clark } 120496b20c1fSDerek J. Clark 120596b20c1fSDerek J. Clark static ssize_t motor_config_options(struct device *dev, 120696b20c1fSDerek J. Clark struct device_attribute *attr, char *buf, 120796b20c1fSDerek J. Clark enum motor_cfg_index index) 120896b20c1fSDerek J. Clark { 120996b20c1fSDerek J. Clark ssize_t count = 0; 121096b20c1fSDerek J. Clark unsigned int i; 121196b20c1fSDerek J. Clark 121296b20c1fSDerek J. Clark switch (index) { 121396b20c1fSDerek J. Clark case MOTOR_CFG_ALL: 121496b20c1fSDerek J. Clark break; 121596b20c1fSDerek J. Clark case RUMBLE_MODE: 121696b20c1fSDerek J. Clark for (i = 1; i < ARRAY_SIZE(rumble_mode_text); i++) { 121796b20c1fSDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", 121896b20c1fSDerek J. Clark rumble_mode_text[i]); 121996b20c1fSDerek J. Clark } 122096b20c1fSDerek J. Clark break; 122196b20c1fSDerek J. Clark case MOTOR_INTENSITY: 122296b20c1fSDerek J. Clark case TP_VIBRATION_INTENSITY: 122396b20c1fSDerek J. Clark for (i = 1; i < ARRAY_SIZE(intensity_text); i++) { 122496b20c1fSDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", 122596b20c1fSDerek J. Clark intensity_text[i]); 122696b20c1fSDerek J. Clark } 122796b20c1fSDerek J. Clark break; 122896b20c1fSDerek J. Clark case VIBRATION_NOTIFY_ENABLE: 122996b20c1fSDerek J. Clark case TP_VIBRATION_ENABLE: 123096b20c1fSDerek J. Clark for (i = 1; i < ARRAY_SIZE(enabled_status_text); i++) { 123196b20c1fSDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", 123296b20c1fSDerek J. Clark enabled_status_text[i]); 123396b20c1fSDerek J. Clark } 123496b20c1fSDerek J. Clark break; 1235*54549af8SChen Ni } 123696b20c1fSDerek J. Clark 123796b20c1fSDerek J. Clark if (count) 123896b20c1fSDerek J. Clark buf[count - 1] = '\n'; 123996b20c1fSDerek J. Clark 124096b20c1fSDerek J. Clark return count; 124196b20c1fSDerek J. Clark } 124296b20c1fSDerek J. Clark 1243f0bedee6SDerek J. Clark static ssize_t fps_mode_dpi_store(struct device *dev, 1244f0bedee6SDerek J. Clark struct device_attribute *attr, 1245f0bedee6SDerek J. Clark const char *buf, size_t count) 1246f0bedee6SDerek J. Clark 1247f0bedee6SDerek J. Clark { 1248f0bedee6SDerek J. Clark size_t size = 4; 1249f0bedee6SDerek J. Clark u32 value; 1250f0bedee6SDerek J. Clark u8 val[4]; 1251f0bedee6SDerek J. Clark int ret; 1252f0bedee6SDerek J. Clark 1253f0bedee6SDerek J. Clark ret = kstrtou32(buf, 10, &value); 1254f0bedee6SDerek J. Clark if (ret) 1255f0bedee6SDerek J. Clark return ret; 1256f0bedee6SDerek J. Clark 1257f0bedee6SDerek J. Clark if (value != 500 && value != 800 && value != 1200 && value != 1800) 1258f0bedee6SDerek J. Clark return -EINVAL; 1259f0bedee6SDerek J. Clark 1260f0bedee6SDerek J. Clark put_unaligned_le32(value, val); 1261f0bedee6SDerek J. Clark 1262f0bedee6SDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, SET_DPI_CFG, 1263f0bedee6SDerek J. Clark FPS_MODE_DPI, UNSPECIFIED, val, size); 1264f0bedee6SDerek J. Clark if (ret < 0) 1265f0bedee6SDerek J. Clark return ret; 1266f0bedee6SDerek J. Clark 1267f0bedee6SDerek J. Clark return count; 1268f0bedee6SDerek J. Clark } 1269f0bedee6SDerek J. Clark 1270f0bedee6SDerek J. Clark static ssize_t fps_mode_dpi_show(struct device *dev, 1271f0bedee6SDerek J. Clark struct device_attribute *attr, char *buf) 1272f0bedee6SDerek J. Clark { 1273f0bedee6SDerek J. Clark int ret; 1274f0bedee6SDerek J. Clark 1275f0bedee6SDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_DPI_CFG, 1276f0bedee6SDerek J. Clark FPS_MODE_DPI, UNSPECIFIED, NULL, 0); 1277f0bedee6SDerek J. Clark if (ret < 0) 1278f0bedee6SDerek J. Clark return ret; 1279f0bedee6SDerek J. Clark 1280f0bedee6SDerek J. Clark return sysfs_emit(buf, "%u\n", drvdata.mouse_dpi); 1281f0bedee6SDerek J. Clark } 1282f0bedee6SDerek J. Clark 1283f0bedee6SDerek J. Clark static ssize_t fps_mode_dpi_index_show(struct device *dev, 1284f0bedee6SDerek J. Clark struct device_attribute *attr, char *buf) 1285f0bedee6SDerek J. Clark { 1286f0bedee6SDerek J. Clark return sysfs_emit(buf, "500 800 1200 1800\n"); 1287f0bedee6SDerek J. Clark } 1288f0bedee6SDerek J. Clark 1289995887a1SDerek J. Clark static ssize_t device_status_show(struct device *dev, 1290995887a1SDerek J. Clark struct device_attribute *attr, char *buf, 1291995887a1SDerek J. Clark enum device_status_index index, 1292995887a1SDerek J. Clark enum dev_type device_type, 1293995887a1SDerek J. Clark enum cal_device_type cal_type) 1294995887a1SDerek J. Clark { 1295995887a1SDerek J. Clark u8 i; 1296995887a1SDerek J. Clark 1297995887a1SDerek J. Clark switch (index) { 1298995887a1SDerek J. Clark case GET_CAL_STATUS: 1299995887a1SDerek J. Clark switch (device_type) { 1300995887a1SDerek J. Clark case LEFT_CONTROLLER: 1301995887a1SDerek J. Clark switch (cal_type) { 1302995887a1SDerek J. Clark case CALDEV_GYROSCOPE: 1303995887a1SDerek J. Clark i = drvdata.gp_left_gyro_cal_status; 1304995887a1SDerek J. Clark break; 1305995887a1SDerek J. Clark case CALDEV_JOYSTICK: 1306995887a1SDerek J. Clark i = drvdata.gp_left_joy_cal_status; 1307995887a1SDerek J. Clark break; 1308995887a1SDerek J. Clark case CALDEV_TRIGGER: 1309995887a1SDerek J. Clark i = drvdata.gp_left_trigg_cal_status; 1310995887a1SDerek J. Clark break; 1311995887a1SDerek J. Clark default: 1312995887a1SDerek J. Clark return -EINVAL; 1313995887a1SDerek J. Clark } 1314995887a1SDerek J. Clark break; 1315995887a1SDerek J. Clark case RIGHT_CONTROLLER: 1316995887a1SDerek J. Clark switch (cal_type) { 1317995887a1SDerek J. Clark case CALDEV_GYROSCOPE: 1318995887a1SDerek J. Clark i = drvdata.gp_right_gyro_cal_status; 1319995887a1SDerek J. Clark break; 1320995887a1SDerek J. Clark case CALDEV_JOYSTICK: 1321995887a1SDerek J. Clark i = drvdata.gp_right_joy_cal_status; 1322995887a1SDerek J. Clark break; 1323995887a1SDerek J. Clark case CALDEV_TRIGGER: 1324995887a1SDerek J. Clark i = drvdata.gp_right_trigg_cal_status; 1325995887a1SDerek J. Clark break; 1326995887a1SDerek J. Clark default: 1327995887a1SDerek J. Clark return -EINVAL; 1328995887a1SDerek J. Clark } 1329995887a1SDerek J. Clark break; 1330995887a1SDerek J. Clark default: 1331995887a1SDerek J. Clark return -EINVAL; 1332995887a1SDerek J. Clark } 1333995887a1SDerek J. Clark break; 1334995887a1SDerek J. Clark default: 1335995887a1SDerek J. Clark return -EINVAL; 1336*54549af8SChen Ni } 1337995887a1SDerek J. Clark 1338995887a1SDerek J. Clark if (i >= ARRAY_SIZE(cal_status_text)) 1339995887a1SDerek J. Clark return -EINVAL; 1340995887a1SDerek J. Clark 1341995887a1SDerek J. Clark return sysfs_emit(buf, "%s\n", cal_status_text[i]); 1342995887a1SDerek J. Clark } 1343995887a1SDerek J. Clark 1344995887a1SDerek J. Clark static ssize_t calibrate_config_store(struct device *dev, 1345995887a1SDerek J. Clark struct device_attribute *attr, 1346995887a1SDerek J. Clark const char *buf, u8 cmd, u8 sub_cmd, 1347995887a1SDerek J. Clark size_t count, enum dev_type device_type) 1348995887a1SDerek J. Clark { 1349995887a1SDerek J. Clark size_t size = 1; 1350995887a1SDerek J. Clark u8 val = 0; 1351995887a1SDerek J. Clark int ret; 1352995887a1SDerek J. Clark 1353995887a1SDerek J. Clark ret = sysfs_match_string(cal_enabled_text, buf); 1354995887a1SDerek J. Clark if (ret < 0) 1355995887a1SDerek J. Clark return ret; 1356995887a1SDerek J. Clark 1357995887a1SDerek J. Clark val = ret; 1358995887a1SDerek J. Clark if (!val) 1359995887a1SDerek J. Clark size = 0; 1360995887a1SDerek J. Clark 1361995887a1SDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, cmd, sub_cmd, 1362995887a1SDerek J. Clark device_type, &val, size); 1363995887a1SDerek J. Clark if (ret < 0) 1364995887a1SDerek J. Clark return ret; 1365995887a1SDerek J. Clark 1366995887a1SDerek J. Clark return count; 1367995887a1SDerek J. Clark } 1368995887a1SDerek J. Clark 1369995887a1SDerek J. Clark static ssize_t calibrate_config_options(struct device *dev, 1370995887a1SDerek J. Clark struct device_attribute *attr, 1371995887a1SDerek J. Clark char *buf) 1372995887a1SDerek J. Clark { 1373995887a1SDerek J. Clark ssize_t count = 0; 1374995887a1SDerek J. Clark unsigned int i; 1375995887a1SDerek J. Clark 1376995887a1SDerek J. Clark for (i = 1; i < ARRAY_SIZE(cal_enabled_text); i++) 1377995887a1SDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", cal_enabled_text[i]); 1378995887a1SDerek J. Clark 1379995887a1SDerek J. Clark buf[count - 1] = '\n'; 1380995887a1SDerek J. Clark 1381995887a1SDerek J. Clark return count; 1382995887a1SDerek J. Clark } 1383995887a1SDerek J. Clark 138451e4270cSDerek J. Clark static ssize_t os_mode_store(struct device *dev, struct device_attribute *attr, 138551e4270cSDerek J. Clark const char *buf, size_t count) 138651e4270cSDerek J. Clark { 138751e4270cSDerek J. Clark size_t size = 1; 138851e4270cSDerek J. Clark int ret; 138951e4270cSDerek J. Clark u8 val; 139051e4270cSDerek J. Clark 139151e4270cSDerek J. Clark ret = sysfs_match_string(os_mode_text, buf); 139251e4270cSDerek J. Clark if (ret <= 0) 139351e4270cSDerek J. Clark return ret; 139451e4270cSDerek J. Clark 139551e4270cSDerek J. Clark val = ret; 139651e4270cSDerek J. Clark ret = mcu_property_out(drvdata.hdev, OS_MODE_DATA, FEATURE_OS_MODE, 139751e4270cSDerek J. Clark SET_OS_MODE, USB_MCU, &val, size); 139851e4270cSDerek J. Clark if (ret < 0) 139951e4270cSDerek J. Clark return ret; 140051e4270cSDerek J. Clark 140151e4270cSDerek J. Clark drvdata.os_mode = val; 140251e4270cSDerek J. Clark 140351e4270cSDerek J. Clark return count; 140451e4270cSDerek J. Clark } 140551e4270cSDerek J. Clark 140651e4270cSDerek J. Clark static ssize_t os_mode_show(struct device *dev, struct device_attribute *attr, 140751e4270cSDerek J. Clark char *buf) 140851e4270cSDerek J. Clark { 140951e4270cSDerek J. Clark ssize_t count = 0; 141051e4270cSDerek J. Clark int ret; 141151e4270cSDerek J. Clark u8 i; 141251e4270cSDerek J. Clark 141351e4270cSDerek J. Clark ret = mcu_property_out(drvdata.hdev, OS_MODE_DATA, FEATURE_OS_MODE, 141451e4270cSDerek J. Clark GET_OS_MODE, USB_MCU, NULL, 0); 141551e4270cSDerek J. Clark if (ret) 141651e4270cSDerek J. Clark return ret; 141751e4270cSDerek J. Clark 141851e4270cSDerek J. Clark i = drvdata.os_mode; 141951e4270cSDerek J. Clark if (i >= ARRAY_SIZE(os_mode_text)) 142051e4270cSDerek J. Clark return -EINVAL; 142151e4270cSDerek J. Clark 142251e4270cSDerek J. Clark count = sysfs_emit(buf, "%s\n", os_mode_text[i]); 142351e4270cSDerek J. Clark 142451e4270cSDerek J. Clark return count; 142551e4270cSDerek J. Clark } 142651e4270cSDerek J. Clark 142751e4270cSDerek J. Clark static ssize_t os_mode_index_show(struct device *dev, 142851e4270cSDerek J. Clark struct device_attribute *attr, char *buf) 142951e4270cSDerek J. Clark { 143051e4270cSDerek J. Clark ssize_t count = 0; 143151e4270cSDerek J. Clark unsigned int i; 143251e4270cSDerek J. Clark 143351e4270cSDerek J. Clark for (i = 1; i < ARRAY_SIZE(os_mode_text); i++) 143451e4270cSDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", os_mode_text[i]); 143551e4270cSDerek J. Clark 143651e4270cSDerek J. Clark if (count) 143751e4270cSDerek J. Clark buf[count - 1] = '\n'; 143851e4270cSDerek J. Clark 143951e4270cSDerek J. Clark return count; 144051e4270cSDerek J. Clark } 144151e4270cSDerek J. Clark 1442325262faSDerek J. Clark static int rgb_cfg_call(struct hid_device *hdev, enum mcu_command_index cmd, 1443325262faSDerek J. Clark enum rgb_config_index index, u8 *val, size_t size) 1444325262faSDerek J. Clark { 1445325262faSDerek J. Clark if (cmd != SET_RGB_CFG && cmd != GET_RGB_CFG) 1446325262faSDerek J. Clark return -EINVAL; 1447325262faSDerek J. Clark 1448325262faSDerek J. Clark if (index < LIGHT_CFG_ALL || index > USR_LIGHT_PROFILE_3) 1449325262faSDerek J. Clark return -EINVAL; 1450325262faSDerek J. Clark 1451325262faSDerek J. Clark return mcu_property_out(hdev, MCU_CONFIG_DATA, cmd, index, UNSPECIFIED, 1452325262faSDerek J. Clark val, size); 1453325262faSDerek J. Clark } 1454325262faSDerek J. Clark 1455325262faSDerek J. Clark static int rgb_attr_show(void) 1456325262faSDerek J. Clark { 1457325262faSDerek J. Clark enum rgb_config_index index; 1458325262faSDerek J. Clark 1459325262faSDerek J. Clark index = drvdata.rgb_profile + 3; 1460325262faSDerek J. Clark 1461325262faSDerek J. Clark return rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, index, NULL, 0); 1462*54549af8SChen Ni } 1463325262faSDerek J. Clark 1464325262faSDerek J. Clark static ssize_t rgb_effect_store(struct device *dev, 1465325262faSDerek J. Clark struct device_attribute *attr, const char *buf, 1466325262faSDerek J. Clark size_t count) 1467325262faSDerek J. Clark { 1468325262faSDerek J. Clark struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev); 1469325262faSDerek J. Clark enum rgb_config_index index; 1470325262faSDerek J. Clark u8 effect; 1471325262faSDerek J. Clark int ret; 1472325262faSDerek J. Clark 1473325262faSDerek J. Clark ret = sysfs_match_string(rgb_effect_text, buf); 1474325262faSDerek J. Clark if (ret < 0) 1475325262faSDerek J. Clark return ret; 1476325262faSDerek J. Clark 1477325262faSDerek J. Clark effect = ret; 1478325262faSDerek J. Clark index = drvdata.rgb_profile + 3; 1479325262faSDerek J. Clark u8 rgb_profile[6] = { effect, 1480325262faSDerek J. Clark mc_cdev->subled_info[0].intensity, 1481325262faSDerek J. Clark mc_cdev->subled_info[1].intensity, 1482325262faSDerek J. Clark mc_cdev->subled_info[2].intensity, 1483325262faSDerek J. Clark drvdata.led_cdev->brightness, 1484325262faSDerek J. Clark drvdata.rgb_speed }; 1485325262faSDerek J. Clark 1486325262faSDerek J. Clark ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6); 1487325262faSDerek J. Clark if (ret) 1488325262faSDerek J. Clark return ret; 1489325262faSDerek J. Clark 1490325262faSDerek J. Clark drvdata.rgb_effect = effect; 1491325262faSDerek J. Clark return count; 1492*54549af8SChen Ni } 1493325262faSDerek J. Clark 1494325262faSDerek J. Clark static ssize_t rgb_effect_show(struct device *dev, 1495325262faSDerek J. Clark struct device_attribute *attr, char *buf) 1496325262faSDerek J. Clark { 1497325262faSDerek J. Clark int ret; 1498325262faSDerek J. Clark 1499325262faSDerek J. Clark ret = rgb_attr_show(); 1500325262faSDerek J. Clark if (ret) 1501325262faSDerek J. Clark return ret; 1502325262faSDerek J. Clark 1503325262faSDerek J. Clark if (drvdata.rgb_effect >= ARRAY_SIZE(rgb_effect_text)) 1504325262faSDerek J. Clark return -EINVAL; 1505325262faSDerek J. Clark 1506325262faSDerek J. Clark return sysfs_emit(buf, "%s\n", rgb_effect_text[drvdata.rgb_effect]); 1507325262faSDerek J. Clark } 1508325262faSDerek J. Clark 1509325262faSDerek J. Clark static ssize_t rgb_effect_index_show(struct device *dev, 1510325262faSDerek J. Clark struct device_attribute *attr, char *buf) 1511325262faSDerek J. Clark { 1512325262faSDerek J. Clark ssize_t count = 0; 1513325262faSDerek J. Clark unsigned int i; 1514325262faSDerek J. Clark 1515325262faSDerek J. Clark for (i = 0; i < ARRAY_SIZE(rgb_effect_text); i++) 1516325262faSDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", rgb_effect_text[i]); 1517325262faSDerek J. Clark 1518325262faSDerek J. Clark if (count) 1519325262faSDerek J. Clark buf[count - 1] = '\n'; 1520325262faSDerek J. Clark 1521325262faSDerek J. Clark return count; 1522325262faSDerek J. Clark } 1523325262faSDerek J. Clark 1524325262faSDerek J. Clark static ssize_t rgb_speed_store(struct device *dev, 1525325262faSDerek J. Clark struct device_attribute *attr, const char *buf, 1526325262faSDerek J. Clark size_t count) 1527325262faSDerek J. Clark { 1528325262faSDerek J. Clark struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev); 1529325262faSDerek J. Clark enum rgb_config_index index; 1530325262faSDerek J. Clark int val = 0; 1531325262faSDerek J. Clark int ret; 1532325262faSDerek J. Clark 1533325262faSDerek J. Clark ret = kstrtoint(buf, 10, &val); 1534325262faSDerek J. Clark if (ret) 1535325262faSDerek J. Clark return ret; 1536325262faSDerek J. Clark 1537325262faSDerek J. Clark if (val < 0 || val > 100) 1538325262faSDerek J. Clark return -EINVAL; 1539325262faSDerek J. Clark 1540325262faSDerek J. Clark /* This is a delay setting, invert logic for consistency with other drivers */ 1541325262faSDerek J. Clark val = 100 - val; 1542325262faSDerek J. Clark 1543325262faSDerek J. Clark index = drvdata.rgb_profile + 3; 1544325262faSDerek J. Clark u8 rgb_profile[6] = { drvdata.rgb_effect, 1545325262faSDerek J. Clark mc_cdev->subled_info[0].intensity, 1546325262faSDerek J. Clark mc_cdev->subled_info[1].intensity, 1547325262faSDerek J. Clark mc_cdev->subled_info[2].intensity, 1548325262faSDerek J. Clark drvdata.led_cdev->brightness, 1549325262faSDerek J. Clark val }; 1550325262faSDerek J. Clark 1551325262faSDerek J. Clark ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6); 1552325262faSDerek J. Clark if (ret) 1553325262faSDerek J. Clark return ret; 1554325262faSDerek J. Clark 1555325262faSDerek J. Clark drvdata.rgb_speed = val; 1556325262faSDerek J. Clark 1557325262faSDerek J. Clark return count; 1558*54549af8SChen Ni } 1559325262faSDerek J. Clark 1560325262faSDerek J. Clark static ssize_t rgb_speed_show(struct device *dev, struct device_attribute *attr, 1561325262faSDerek J. Clark char *buf) 1562325262faSDerek J. Clark { 1563325262faSDerek J. Clark int ret, val; 1564325262faSDerek J. Clark 1565325262faSDerek J. Clark ret = rgb_attr_show(); 1566325262faSDerek J. Clark if (ret) 1567325262faSDerek J. Clark return ret; 1568325262faSDerek J. Clark 1569325262faSDerek J. Clark if (drvdata.rgb_speed > 100) 1570325262faSDerek J. Clark return -EINVAL; 1571325262faSDerek J. Clark 1572325262faSDerek J. Clark val = drvdata.rgb_speed; 1573325262faSDerek J. Clark 1574325262faSDerek J. Clark return sysfs_emit(buf, "%hhu\n", val); 1575325262faSDerek J. Clark } 1576325262faSDerek J. Clark 1577325262faSDerek J. Clark static ssize_t rgb_speed_range_show(struct device *dev, 1578325262faSDerek J. Clark struct device_attribute *attr, char *buf) 1579325262faSDerek J. Clark { 1580325262faSDerek J. Clark return sysfs_emit(buf, "0-100\n"); 1581325262faSDerek J. Clark } 1582325262faSDerek J. Clark 1583325262faSDerek J. Clark static ssize_t rgb_mode_store(struct device *dev, struct device_attribute *attr, 1584325262faSDerek J. Clark const char *buf, size_t count) 1585325262faSDerek J. Clark { 1586325262faSDerek J. Clark int ret; 1587325262faSDerek J. Clark u8 val; 1588325262faSDerek J. Clark 1589325262faSDerek J. Clark ret = sysfs_match_string(rgb_mode_text, buf); 1590325262faSDerek J. Clark if (ret <= 0) 1591325262faSDerek J. Clark return ret; 1592325262faSDerek J. Clark 1593325262faSDerek J. Clark val = ret; 1594325262faSDerek J. Clark 1595325262faSDerek J. Clark ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_MODE_SEL, &val, 1); 1596325262faSDerek J. Clark if (ret) 1597325262faSDerek J. Clark return ret; 1598325262faSDerek J. Clark 1599325262faSDerek J. Clark drvdata.rgb_mode = val; 1600325262faSDerek J. Clark 1601325262faSDerek J. Clark return count; 1602*54549af8SChen Ni } 1603325262faSDerek J. Clark 1604325262faSDerek J. Clark static ssize_t rgb_mode_show(struct device *dev, struct device_attribute *attr, 1605325262faSDerek J. Clark char *buf) 1606325262faSDerek J. Clark { 1607325262faSDerek J. Clark int ret; 1608325262faSDerek J. Clark 1609325262faSDerek J. Clark ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_MODE_SEL, NULL, 0); 1610325262faSDerek J. Clark if (ret) 1611325262faSDerek J. Clark return ret; 1612325262faSDerek J. Clark 1613325262faSDerek J. Clark if (drvdata.rgb_mode >= ARRAY_SIZE(rgb_mode_text)) 1614325262faSDerek J. Clark return -EINVAL; 1615325262faSDerek J. Clark 1616325262faSDerek J. Clark return sysfs_emit(buf, "%s\n", rgb_mode_text[drvdata.rgb_mode]); 1617*54549af8SChen Ni } 1618325262faSDerek J. Clark 1619325262faSDerek J. Clark static ssize_t rgb_mode_index_show(struct device *dev, 1620325262faSDerek J. Clark struct device_attribute *attr, char *buf) 1621325262faSDerek J. Clark { 1622325262faSDerek J. Clark ssize_t count = 0; 1623325262faSDerek J. Clark unsigned int i; 1624325262faSDerek J. Clark 1625325262faSDerek J. Clark for (i = 1; i < ARRAY_SIZE(rgb_mode_text); i++) 1626325262faSDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", rgb_mode_text[i]); 1627325262faSDerek J. Clark 1628325262faSDerek J. Clark if (count) 1629325262faSDerek J. Clark buf[count - 1] = '\n'; 1630325262faSDerek J. Clark 1631325262faSDerek J. Clark return count; 1632325262faSDerek J. Clark } 1633325262faSDerek J. Clark 1634325262faSDerek J. Clark static ssize_t rgb_profile_store(struct device *dev, 1635325262faSDerek J. Clark struct device_attribute *attr, const char *buf, 1636325262faSDerek J. Clark size_t count) 1637325262faSDerek J. Clark { 1638325262faSDerek J. Clark size_t size = 1; 1639325262faSDerek J. Clark int ret; 1640325262faSDerek J. Clark u8 val; 1641325262faSDerek J. Clark 1642325262faSDerek J. Clark ret = kstrtou8(buf, 10, &val); 1643325262faSDerek J. Clark if (ret < 0) 1644325262faSDerek J. Clark return ret; 1645325262faSDerek J. Clark 1646325262faSDerek J. Clark if (val < 1 || val > 3) 1647325262faSDerek J. Clark return -EINVAL; 1648325262faSDerek J. Clark 1649325262faSDerek J. Clark ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_PROFILE_SEL, &val, size); 1650325262faSDerek J. Clark if (ret) 1651325262faSDerek J. Clark return ret; 1652325262faSDerek J. Clark 1653325262faSDerek J. Clark drvdata.rgb_profile = val; 1654325262faSDerek J. Clark 1655325262faSDerek J. Clark return count; 1656*54549af8SChen Ni } 1657325262faSDerek J. Clark 1658325262faSDerek J. Clark static ssize_t rgb_profile_show(struct device *dev, 1659325262faSDerek J. Clark struct device_attribute *attr, char *buf) 1660325262faSDerek J. Clark { 1661325262faSDerek J. Clark int ret; 1662325262faSDerek J. Clark 1663325262faSDerek J. Clark ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_PROFILE_SEL, NULL, 0); 1664325262faSDerek J. Clark if (ret) 1665325262faSDerek J. Clark return ret; 1666325262faSDerek J. Clark 1667325262faSDerek J. Clark if (drvdata.rgb_profile < 1 || drvdata.rgb_profile > 3) 1668325262faSDerek J. Clark return -EINVAL; 1669325262faSDerek J. Clark 1670325262faSDerek J. Clark return sysfs_emit(buf, "%hhu\n", drvdata.rgb_profile); 1671*54549af8SChen Ni } 1672325262faSDerek J. Clark 1673325262faSDerek J. Clark static ssize_t rgb_profile_range_show(struct device *dev, 1674325262faSDerek J. Clark struct device_attribute *attr, char *buf) 1675325262faSDerek J. Clark { 1676325262faSDerek J. Clark return sysfs_emit(buf, "1-3\n"); 1677325262faSDerek J. Clark } 1678325262faSDerek J. Clark 1679325262faSDerek J. Clark static void hid_go_brightness_set(struct led_classdev *led_cdev, 1680325262faSDerek J. Clark enum led_brightness brightness) 1681325262faSDerek J. Clark { 1682325262faSDerek J. Clark struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev); 1683325262faSDerek J. Clark enum rgb_config_index index; 1684325262faSDerek J. Clark int ret; 1685325262faSDerek J. Clark 1686325262faSDerek J. Clark if (brightness > led_cdev->max_brightness) { 1687325262faSDerek J. Clark dev_err(led_cdev->dev, "Invalid argument\n"); 1688325262faSDerek J. Clark return; 1689325262faSDerek J. Clark } 1690325262faSDerek J. Clark 1691325262faSDerek J. Clark index = drvdata.rgb_profile + 3; 1692325262faSDerek J. Clark u8 rgb_profile[6] = { drvdata.rgb_effect, 1693325262faSDerek J. Clark mc_cdev->subled_info[0].intensity, 1694325262faSDerek J. Clark mc_cdev->subled_info[1].intensity, 1695325262faSDerek J. Clark mc_cdev->subled_info[2].intensity, 1696325262faSDerek J. Clark brightness, 1697325262faSDerek J. Clark drvdata.rgb_speed }; 1698325262faSDerek J. Clark 1699325262faSDerek J. Clark ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6); 1700325262faSDerek J. Clark switch (ret) { 1701325262faSDerek J. Clark case 0: 1702325262faSDerek J. Clark led_cdev->brightness = brightness; 1703325262faSDerek J. Clark break; 1704325262faSDerek J. Clark case -ENODEV: /* during switch to IAP -ENODEV is expected */ 1705325262faSDerek J. Clark case -ENOSYS: /* during rmmod -ENOSYS is expected */ 1706325262faSDerek J. Clark dev_dbg(led_cdev->dev, "Failed to write RGB profile: %i\n", ret); 1707325262faSDerek J. Clark break; 1708325262faSDerek J. Clark default: 1709325262faSDerek J. Clark dev_err(led_cdev->dev, "Failed to write RGB profile: %i\n", ret); 1710*54549af8SChen Ni } 1711325262faSDerek J. Clark } 1712325262faSDerek J. Clark 1713d69ccfcbSDerek J. Clark #define LEGO_DEVICE_ATTR_RW(_name, _attrname, _dtype, _rtype, _group) \ 1714d69ccfcbSDerek J. Clark static ssize_t _name##_store(struct device *dev, \ 1715d69ccfcbSDerek J. Clark struct device_attribute *attr, \ 1716d69ccfcbSDerek J. Clark const char *buf, size_t count) \ 1717d69ccfcbSDerek J. Clark { \ 1718d69ccfcbSDerek J. Clark return _group##_store(dev, attr, buf, count, _name.index, \ 1719d69ccfcbSDerek J. Clark _dtype); \ 1720d69ccfcbSDerek J. Clark } \ 1721d69ccfcbSDerek J. Clark static ssize_t _name##_show(struct device *dev, \ 1722d69ccfcbSDerek J. Clark struct device_attribute *attr, char *buf) \ 1723d69ccfcbSDerek J. Clark { \ 1724d69ccfcbSDerek J. Clark return _group##_show(dev, attr, buf, _name.index, _dtype); \ 1725d69ccfcbSDerek J. Clark } \ 1726d69ccfcbSDerek J. Clark static ssize_t _name##_##_rtype##_show( \ 1727d69ccfcbSDerek J. Clark struct device *dev, struct device_attribute *attr, char *buf) \ 1728d69ccfcbSDerek J. Clark { \ 1729d69ccfcbSDerek J. Clark return _group##_options(dev, attr, buf, _name.index); \ 1730d69ccfcbSDerek J. Clark } \ 1731d69ccfcbSDerek J. Clark static DEVICE_ATTR_RW_NAMED(_name, _attrname) 1732d69ccfcbSDerek J. Clark 1733d69ccfcbSDerek J. Clark #define LEGO_DEVICE_ATTR_WO(_name, _attrname, _dtype, _group) \ 1734d69ccfcbSDerek J. Clark static ssize_t _name##_store(struct device *dev, \ 1735d69ccfcbSDerek J. Clark struct device_attribute *attr, \ 1736d69ccfcbSDerek J. Clark const char *buf, size_t count) \ 1737d69ccfcbSDerek J. Clark { \ 1738d69ccfcbSDerek J. Clark return _group##_store(dev, attr, buf, count, _name.index, \ 1739d69ccfcbSDerek J. Clark _dtype); \ 1740d69ccfcbSDerek J. Clark } \ 1741d69ccfcbSDerek J. Clark static DEVICE_ATTR_WO_NAMED(_name, _attrname) 1742d69ccfcbSDerek J. Clark 1743d69ccfcbSDerek J. Clark #define LEGO_DEVICE_ATTR_RO(_name, _attrname, _dtype, _group) \ 1744d69ccfcbSDerek J. Clark static ssize_t _name##_show(struct device *dev, \ 1745d69ccfcbSDerek J. Clark struct device_attribute *attr, char *buf) \ 1746d69ccfcbSDerek J. Clark { \ 1747d69ccfcbSDerek J. Clark return _group##_show(dev, attr, buf, _name.index, _dtype); \ 1748d69ccfcbSDerek J. Clark } \ 1749d69ccfcbSDerek J. Clark static DEVICE_ATTR_RO_NAMED(_name, _attrname) 1750d69ccfcbSDerek J. Clark 1751995887a1SDerek J. Clark #define LEGO_CAL_DEVICE_ATTR(_name, _attrname, _scmd, _dtype, _rtype) \ 1752995887a1SDerek J. Clark static ssize_t _name##_store(struct device *dev, \ 1753995887a1SDerek J. Clark struct device_attribute *attr, \ 1754995887a1SDerek J. Clark const char *buf, size_t count) \ 1755995887a1SDerek J. Clark { \ 1756995887a1SDerek J. Clark return calibrate_config_store(dev, attr, buf, _name.index, \ 1757995887a1SDerek J. Clark _scmd, count, _dtype); \ 1758995887a1SDerek J. Clark } \ 1759995887a1SDerek J. Clark static ssize_t _name##_##_rtype##_show( \ 1760995887a1SDerek J. Clark struct device *dev, struct device_attribute *attr, char *buf) \ 1761995887a1SDerek J. Clark { \ 1762995887a1SDerek J. Clark return calibrate_config_options(dev, attr, buf); \ 1763995887a1SDerek J. Clark } \ 1764995887a1SDerek J. Clark static DEVICE_ATTR_WO_NAMED(_name, _attrname) 1765995887a1SDerek J. Clark 1766995887a1SDerek J. Clark #define LEGO_DEVICE_STATUS_ATTR(_name, _attrname, _scmd, _dtype) \ 1767995887a1SDerek J. Clark static ssize_t _name##_show(struct device *dev, \ 1768995887a1SDerek J. Clark struct device_attribute *attr, char *buf) \ 1769995887a1SDerek J. Clark { \ 1770995887a1SDerek J. Clark return device_status_show(dev, attr, buf, _name.index, _scmd, \ 1771995887a1SDerek J. Clark _dtype); \ 1772995887a1SDerek J. Clark } \ 1773995887a1SDerek J. Clark static DEVICE_ATTR_RO_NAMED(_name, _attrname) 1774995887a1SDerek J. Clark 1775d69ccfcbSDerek J. Clark /* Gamepad - MCU */ 1776d69ccfcbSDerek J. Clark static struct go_cfg_attr version_product_mcu = { PRODUCT_VERSION }; 1777d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_product_mcu, "product_version", USB_MCU, version); 1778d69ccfcbSDerek J. Clark 1779d69ccfcbSDerek J. Clark static struct go_cfg_attr version_protocol_mcu = { PROTOCOL_VERSION }; 1780d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_protocol_mcu, "protocol_version", USB_MCU, version); 1781d69ccfcbSDerek J. Clark 1782d69ccfcbSDerek J. Clark static struct go_cfg_attr version_firmware_mcu = { FIRMWARE_VERSION }; 1783d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_firmware_mcu, "firmware_version", USB_MCU, version); 1784d69ccfcbSDerek J. Clark 1785d69ccfcbSDerek J. Clark static struct go_cfg_attr version_hardware_mcu = { HARDWARE_VERSION }; 1786d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_hardware_mcu, "hardware_version", USB_MCU, version); 1787d69ccfcbSDerek J. Clark 1788d69ccfcbSDerek J. Clark static struct go_cfg_attr version_gen_mcu = { HARDWARE_GENERATION }; 1789d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_gen_mcu, "hardware_generation", USB_MCU, version); 1790d69ccfcbSDerek J. Clark 179182cd9bc8SDerek J. Clark static struct go_cfg_attr fps_switch_status = { FEATURE_FPS_SWITCH_STATUS }; 179282cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RO(fps_switch_status, "fps_switch_status", UNSPECIFIED, 179382cd9bc8SDerek J. Clark feature_status); 179482cd9bc8SDerek J. Clark 179582cd9bc8SDerek J. Clark static struct go_cfg_attr gamepad_mode = { FEATURE_GAMEPAD_MODE }; 179682cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(gamepad_mode, "mode", UNSPECIFIED, index, feature_status); 179782cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(gamepad_mode_index, "mode_index"); 179882cd9bc8SDerek J. Clark 179982cd9bc8SDerek J. Clark static struct go_cfg_attr reset_mcu = { FEATURE_RESET_GAMEPAD }; 180082cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_WO(reset_mcu, "reset_mcu", USB_MCU, feature_status); 180182cd9bc8SDerek J. Clark 180296b20c1fSDerek J. Clark static struct go_cfg_attr gamepad_rumble_intensity = { MOTOR_INTENSITY }; 180396b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(gamepad_rumble_intensity, "rumble_intensity", UNSPECIFIED, 180496b20c1fSDerek J. Clark index, motor_config); 180596b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(gamepad_rumble_intensity_index, 180696b20c1fSDerek J. Clark "rumble_intensity_index"); 180796b20c1fSDerek J. Clark 1808f0bedee6SDerek J. Clark static DEVICE_ATTR_RW(fps_mode_dpi); 1809f0bedee6SDerek J. Clark static DEVICE_ATTR_RO(fps_mode_dpi_index); 1810f0bedee6SDerek J. Clark 181151e4270cSDerek J. Clark static DEVICE_ATTR_RW(os_mode); 181251e4270cSDerek J. Clark static DEVICE_ATTR_RO(os_mode_index); 181351e4270cSDerek J. Clark 1814d69ccfcbSDerek J. Clark static struct attribute *mcu_attrs[] = { 1815f0bedee6SDerek J. Clark &dev_attr_fps_mode_dpi.attr, 1816f0bedee6SDerek J. Clark &dev_attr_fps_mode_dpi_index.attr, 181782cd9bc8SDerek J. Clark &dev_attr_fps_switch_status.attr, 181882cd9bc8SDerek J. Clark &dev_attr_gamepad_mode.attr, 181982cd9bc8SDerek J. Clark &dev_attr_gamepad_mode_index.attr, 182096b20c1fSDerek J. Clark &dev_attr_gamepad_rumble_intensity.attr, 182196b20c1fSDerek J. Clark &dev_attr_gamepad_rumble_intensity_index.attr, 182251e4270cSDerek J. Clark &dev_attr_os_mode.attr, 182351e4270cSDerek J. Clark &dev_attr_os_mode_index.attr, 182482cd9bc8SDerek J. Clark &dev_attr_reset_mcu.attr, 1825d69ccfcbSDerek J. Clark &dev_attr_version_firmware_mcu.attr, 1826d69ccfcbSDerek J. Clark &dev_attr_version_gen_mcu.attr, 1827d69ccfcbSDerek J. Clark &dev_attr_version_hardware_mcu.attr, 1828d69ccfcbSDerek J. Clark &dev_attr_version_product_mcu.attr, 1829d69ccfcbSDerek J. Clark &dev_attr_version_protocol_mcu.attr, 1830d69ccfcbSDerek J. Clark NULL, 1831d69ccfcbSDerek J. Clark }; 1832d69ccfcbSDerek J. Clark 1833d69ccfcbSDerek J. Clark static const struct attribute_group mcu_attr_group = { 1834d69ccfcbSDerek J. Clark .attrs = mcu_attrs, 1835d69ccfcbSDerek J. Clark }; 1836d69ccfcbSDerek J. Clark 1837d69ccfcbSDerek J. Clark /* Gamepad - TX Dongle */ 1838d69ccfcbSDerek J. Clark static struct go_cfg_attr version_product_tx_dongle = { PRODUCT_VERSION }; 1839d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_product_tx_dongle, "product_version", TX_DONGLE, version); 1840d69ccfcbSDerek J. Clark 1841d69ccfcbSDerek J. Clark static struct go_cfg_attr version_protocol_tx_dongle = { PROTOCOL_VERSION }; 1842d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_protocol_tx_dongle, "protocol_version", TX_DONGLE, version); 1843d69ccfcbSDerek J. Clark 1844d69ccfcbSDerek J. Clark static struct go_cfg_attr version_firmware_tx_dongle = { FIRMWARE_VERSION }; 1845d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_firmware_tx_dongle, "firmware_version", TX_DONGLE, version); 1846d69ccfcbSDerek J. Clark 1847d69ccfcbSDerek J. Clark static struct go_cfg_attr version_hardware_tx_dongle = { HARDWARE_VERSION }; 1848d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_hardware_tx_dongle, "hardware_version", TX_DONGLE, version); 1849d69ccfcbSDerek J. Clark 1850d69ccfcbSDerek J. Clark static struct go_cfg_attr version_gen_tx_dongle = { HARDWARE_GENERATION }; 1851d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_gen_tx_dongle, "hardware_generation", TX_DONGLE, version); 1852d69ccfcbSDerek J. Clark 185382cd9bc8SDerek J. Clark static struct go_cfg_attr reset_tx_dongle = { FEATURE_RESET_GAMEPAD }; 185482cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RO(reset_tx_dongle, "reset", TX_DONGLE, feature_status); 185582cd9bc8SDerek J. Clark 1856d69ccfcbSDerek J. Clark static struct attribute *tx_dongle_attrs[] = { 185782cd9bc8SDerek J. Clark &dev_attr_reset_tx_dongle.attr, 1858d69ccfcbSDerek J. Clark &dev_attr_version_hardware_tx_dongle.attr, 1859d69ccfcbSDerek J. Clark &dev_attr_version_firmware_tx_dongle.attr, 1860d69ccfcbSDerek J. Clark &dev_attr_version_gen_tx_dongle.attr, 1861d69ccfcbSDerek J. Clark &dev_attr_version_product_tx_dongle.attr, 1862d69ccfcbSDerek J. Clark &dev_attr_version_protocol_tx_dongle.attr, 1863d69ccfcbSDerek J. Clark NULL, 1864d69ccfcbSDerek J. Clark }; 1865d69ccfcbSDerek J. Clark 1866d69ccfcbSDerek J. Clark static const struct attribute_group tx_dongle_attr_group = { 1867d69ccfcbSDerek J. Clark .name = "tx_dongle", 1868d69ccfcbSDerek J. Clark .attrs = tx_dongle_attrs, 1869d69ccfcbSDerek J. Clark }; 1870d69ccfcbSDerek J. Clark 1871d69ccfcbSDerek J. Clark /* Gamepad - Left */ 1872d69ccfcbSDerek J. Clark static struct go_cfg_attr version_product_left = { PRODUCT_VERSION }; 1873d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_product_left, "product_version", LEFT_CONTROLLER, version); 1874d69ccfcbSDerek J. Clark 1875d69ccfcbSDerek J. Clark static struct go_cfg_attr version_protocol_left = { PROTOCOL_VERSION }; 1876d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_protocol_left, "protocol_version", LEFT_CONTROLLER, version); 1877d69ccfcbSDerek J. Clark 1878d69ccfcbSDerek J. Clark static struct go_cfg_attr version_firmware_left = { FIRMWARE_VERSION }; 1879d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_firmware_left, "firmware_version", LEFT_CONTROLLER, version); 1880d69ccfcbSDerek J. Clark 1881d69ccfcbSDerek J. Clark static struct go_cfg_attr version_hardware_left = { HARDWARE_VERSION }; 1882d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_hardware_left, "hardware_version", LEFT_CONTROLLER, version); 1883d69ccfcbSDerek J. Clark 1884d69ccfcbSDerek J. Clark static struct go_cfg_attr version_gen_left = { HARDWARE_GENERATION }; 1885d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_gen_left, "hardware_generation", LEFT_CONTROLLER, version); 1886d69ccfcbSDerek J. Clark 188782cd9bc8SDerek J. Clark static struct go_cfg_attr auto_sleep_time_left = { FEATURE_AUTO_SLEEP_TIME }; 188882cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(auto_sleep_time_left, "auto_sleep_time", LEFT_CONTROLLER, 188982cd9bc8SDerek J. Clark range, feature_status); 189082cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(auto_sleep_time_left_range, 189182cd9bc8SDerek J. Clark "auto_sleep_time_range"); 189282cd9bc8SDerek J. Clark 189382cd9bc8SDerek J. Clark static struct go_cfg_attr imu_bypass_left = { FEATURE_IMU_BYPASS }; 189482cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(imu_bypass_left, "imu_bypass_enabled", LEFT_CONTROLLER, 189582cd9bc8SDerek J. Clark index, feature_status); 189682cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(imu_bypass_left_index, "imu_bypass_enabled_index"); 189782cd9bc8SDerek J. Clark 189882cd9bc8SDerek J. Clark static struct go_cfg_attr imu_enabled_left = { FEATURE_IMU_ENABLE }; 189982cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(imu_enabled_left, "imu_enabled", LEFT_CONTROLLER, index, 190082cd9bc8SDerek J. Clark feature_status); 190182cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(imu_enabled_left_index, "imu_enabled_index"); 190282cd9bc8SDerek J. Clark 190382cd9bc8SDerek J. Clark static struct go_cfg_attr reset_left = { FEATURE_RESET_GAMEPAD }; 190482cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_WO(reset_left, "reset", LEFT_CONTROLLER, feature_status); 190582cd9bc8SDerek J. Clark 190696b20c1fSDerek J. Clark static struct go_cfg_attr rumble_mode_left = { RUMBLE_MODE }; 190796b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(rumble_mode_left, "rumble_mode", LEFT_CONTROLLER, index, 190896b20c1fSDerek J. Clark motor_config); 190996b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rumble_mode_left_index, "rumble_mode_index"); 191096b20c1fSDerek J. Clark 191196b20c1fSDerek J. Clark static struct go_cfg_attr rumble_notification_left = { VIBRATION_NOTIFY_ENABLE }; 191296b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(rumble_notification_left, "rumble_notification", 191396b20c1fSDerek J. Clark LEFT_CONTROLLER, index, motor_config); 191496b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rumble_notification_left_index, 191596b20c1fSDerek J. Clark "rumble_notification_index"); 191696b20c1fSDerek J. Clark 1917995887a1SDerek J. Clark static struct go_cfg_attr cal_trigg_left = { TRIGGER_CALIBRATE }; 1918995887a1SDerek J. Clark LEGO_CAL_DEVICE_ATTR(cal_trigg_left, "calibrate_trigger", SET_TRIGGER_CFG, 1919995887a1SDerek J. Clark LEFT_CONTROLLER, index); 1920995887a1SDerek J. Clark static DEVICE_ATTR_RO_NAMED(cal_trigg_left_index, "calibrate_trigger_index"); 1921995887a1SDerek J. Clark 1922995887a1SDerek J. Clark static struct go_cfg_attr cal_joy_left = { JOYSTICK_CALIBRATE }; 1923995887a1SDerek J. Clark LEGO_CAL_DEVICE_ATTR(cal_joy_left, "calibrate_joystick", SET_JOYSTICK_CFG, 1924995887a1SDerek J. Clark LEFT_CONTROLLER, index); 1925995887a1SDerek J. Clark static DEVICE_ATTR_RO_NAMED(cal_joy_left_index, "calibrate_joystick_index"); 1926995887a1SDerek J. Clark 1927995887a1SDerek J. Clark static struct go_cfg_attr cal_gyro_left = { GYRO_CALIBRATE }; 1928995887a1SDerek J. Clark LEGO_CAL_DEVICE_ATTR(cal_gyro_left, "calibrate_gyro", SET_GYRO_CFG, 1929995887a1SDerek J. Clark LEFT_CONTROLLER, index); 1930995887a1SDerek J. Clark static DEVICE_ATTR_RO_NAMED(cal_gyro_left_index, "calibrate_gyro_index"); 1931995887a1SDerek J. Clark 1932995887a1SDerek J. Clark static struct go_cfg_attr cal_trigg_left_status = { GET_CAL_STATUS }; 1933995887a1SDerek J. Clark LEGO_DEVICE_STATUS_ATTR(cal_trigg_left_status, "calibrate_trigger_status", 1934995887a1SDerek J. Clark LEFT_CONTROLLER, CALDEV_TRIGGER); 1935995887a1SDerek J. Clark 1936995887a1SDerek J. Clark static struct go_cfg_attr cal_joy_left_status = { GET_CAL_STATUS }; 1937995887a1SDerek J. Clark LEGO_DEVICE_STATUS_ATTR(cal_joy_left_status, "calibrate_joystick_status", 1938995887a1SDerek J. Clark LEFT_CONTROLLER, CALDEV_JOYSTICK); 1939995887a1SDerek J. Clark 1940995887a1SDerek J. Clark static struct go_cfg_attr cal_gyro_left_status = { GET_CAL_STATUS }; 1941995887a1SDerek J. Clark LEGO_DEVICE_STATUS_ATTR(cal_gyro_left_status, "calibrate_gyro_status", 1942995887a1SDerek J. Clark LEFT_CONTROLLER, CALDEV_GYROSCOPE); 1943995887a1SDerek J. Clark 1944d69ccfcbSDerek J. Clark static struct attribute *left_gamepad_attrs[] = { 194582cd9bc8SDerek J. Clark &dev_attr_auto_sleep_time_left.attr, 194682cd9bc8SDerek J. Clark &dev_attr_auto_sleep_time_left_range.attr, 1947995887a1SDerek J. Clark &dev_attr_cal_gyro_left.attr, 1948995887a1SDerek J. Clark &dev_attr_cal_gyro_left_index.attr, 1949995887a1SDerek J. Clark &dev_attr_cal_gyro_left_status.attr, 1950995887a1SDerek J. Clark &dev_attr_cal_joy_left.attr, 1951995887a1SDerek J. Clark &dev_attr_cal_joy_left_index.attr, 1952995887a1SDerek J. Clark &dev_attr_cal_joy_left_status.attr, 1953995887a1SDerek J. Clark &dev_attr_cal_trigg_left.attr, 1954995887a1SDerek J. Clark &dev_attr_cal_trigg_left_index.attr, 1955995887a1SDerek J. Clark &dev_attr_cal_trigg_left_status.attr, 195682cd9bc8SDerek J. Clark &dev_attr_imu_bypass_left.attr, 195782cd9bc8SDerek J. Clark &dev_attr_imu_bypass_left_index.attr, 195882cd9bc8SDerek J. Clark &dev_attr_imu_enabled_left.attr, 195982cd9bc8SDerek J. Clark &dev_attr_imu_enabled_left_index.attr, 196082cd9bc8SDerek J. Clark &dev_attr_reset_left.attr, 196196b20c1fSDerek J. Clark &dev_attr_rumble_mode_left.attr, 196296b20c1fSDerek J. Clark &dev_attr_rumble_mode_left_index.attr, 196396b20c1fSDerek J. Clark &dev_attr_rumble_notification_left.attr, 196496b20c1fSDerek J. Clark &dev_attr_rumble_notification_left_index.attr, 1965d69ccfcbSDerek J. Clark &dev_attr_version_hardware_left.attr, 1966d69ccfcbSDerek J. Clark &dev_attr_version_firmware_left.attr, 1967d69ccfcbSDerek J. Clark &dev_attr_version_gen_left.attr, 1968d69ccfcbSDerek J. Clark &dev_attr_version_product_left.attr, 1969d69ccfcbSDerek J. Clark &dev_attr_version_protocol_left.attr, 1970d69ccfcbSDerek J. Clark NULL, 1971d69ccfcbSDerek J. Clark }; 1972d69ccfcbSDerek J. Clark 1973d69ccfcbSDerek J. Clark static const struct attribute_group left_gamepad_attr_group = { 1974d69ccfcbSDerek J. Clark .name = "left_handle", 1975d69ccfcbSDerek J. Clark .attrs = left_gamepad_attrs, 1976d69ccfcbSDerek J. Clark }; 1977d69ccfcbSDerek J. Clark 1978d69ccfcbSDerek J. Clark /* Gamepad - Right */ 1979d69ccfcbSDerek J. Clark static struct go_cfg_attr version_product_right = { PRODUCT_VERSION }; 1980d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_product_right, "product_version", RIGHT_CONTROLLER, version); 1981d69ccfcbSDerek J. Clark 1982d69ccfcbSDerek J. Clark static struct go_cfg_attr version_protocol_right = { PROTOCOL_VERSION }; 1983d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_protocol_right, "protocol_version", RIGHT_CONTROLLER, version); 1984d69ccfcbSDerek J. Clark 1985d69ccfcbSDerek J. Clark static struct go_cfg_attr version_firmware_right = { FIRMWARE_VERSION }; 1986d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_firmware_right, "firmware_version", RIGHT_CONTROLLER, version); 1987d69ccfcbSDerek J. Clark 1988d69ccfcbSDerek J. Clark static struct go_cfg_attr version_hardware_right = { HARDWARE_VERSION }; 1989d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_hardware_right, "hardware_version", RIGHT_CONTROLLER, version); 1990d69ccfcbSDerek J. Clark 1991d69ccfcbSDerek J. Clark static struct go_cfg_attr version_gen_right = { HARDWARE_GENERATION }; 1992d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_gen_right, "hardware_generation", RIGHT_CONTROLLER, version); 1993d69ccfcbSDerek J. Clark 199482cd9bc8SDerek J. Clark static struct go_cfg_attr auto_sleep_time_right = { FEATURE_AUTO_SLEEP_TIME }; 199582cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(auto_sleep_time_right, "auto_sleep_time", RIGHT_CONTROLLER, 199682cd9bc8SDerek J. Clark range, feature_status); 199782cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(auto_sleep_time_right_range, 199882cd9bc8SDerek J. Clark "auto_sleep_time_range"); 199982cd9bc8SDerek J. Clark 200082cd9bc8SDerek J. Clark static struct go_cfg_attr imu_bypass_right = { FEATURE_IMU_BYPASS }; 200182cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(imu_bypass_right, "imu_bypass_enabled", RIGHT_CONTROLLER, 200282cd9bc8SDerek J. Clark index, feature_status); 200382cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(imu_bypass_right_index, "imu_bypass_enabled_index"); 200482cd9bc8SDerek J. Clark 200582cd9bc8SDerek J. Clark static struct go_cfg_attr imu_enabled_right = { FEATURE_IMU_BYPASS }; 200682cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(imu_enabled_right, "imu_enabled", RIGHT_CONTROLLER, index, 200782cd9bc8SDerek J. Clark feature_status); 200882cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(imu_enabled_right_index, "imu_enabled_index"); 200982cd9bc8SDerek J. Clark 201082cd9bc8SDerek J. Clark static struct go_cfg_attr reset_right = { FEATURE_RESET_GAMEPAD }; 201182cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_WO(reset_right, "reset", LEFT_CONTROLLER, feature_status); 201282cd9bc8SDerek J. Clark 201396b20c1fSDerek J. Clark static struct go_cfg_attr rumble_mode_right = { RUMBLE_MODE }; 201496b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(rumble_mode_right, "rumble_mode", RIGHT_CONTROLLER, index, 201596b20c1fSDerek J. Clark motor_config); 201696b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rumble_mode_right_index, "rumble_mode_index"); 201796b20c1fSDerek J. Clark 201896b20c1fSDerek J. Clark static struct go_cfg_attr rumble_notification_right = { VIBRATION_NOTIFY_ENABLE }; 201996b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(rumble_notification_right, "rumble_notification", 202096b20c1fSDerek J. Clark RIGHT_CONTROLLER, index, motor_config); 202196b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rumble_notification_right_index, 202296b20c1fSDerek J. Clark "rumble_notification_index"); 202396b20c1fSDerek J. Clark 2024995887a1SDerek J. Clark static struct go_cfg_attr cal_trigg_right = { TRIGGER_CALIBRATE }; 2025995887a1SDerek J. Clark LEGO_CAL_DEVICE_ATTR(cal_trigg_right, "calibrate_trigger", SET_TRIGGER_CFG, 2026995887a1SDerek J. Clark RIGHT_CONTROLLER, index); 2027995887a1SDerek J. Clark static DEVICE_ATTR_RO_NAMED(cal_trigg_right_index, "calibrate_trigger_index"); 2028995887a1SDerek J. Clark 2029995887a1SDerek J. Clark static struct go_cfg_attr cal_joy_right = { JOYSTICK_CALIBRATE }; 2030995887a1SDerek J. Clark LEGO_CAL_DEVICE_ATTR(cal_joy_right, "calibrate_joystick", SET_JOYSTICK_CFG, 2031995887a1SDerek J. Clark RIGHT_CONTROLLER, index); 2032995887a1SDerek J. Clark static DEVICE_ATTR_RO_NAMED(cal_joy_right_index, "calibrate_joystick_index"); 2033995887a1SDerek J. Clark 2034995887a1SDerek J. Clark static struct go_cfg_attr cal_gyro_right = { GYRO_CALIBRATE }; 2035995887a1SDerek J. Clark LEGO_CAL_DEVICE_ATTR(cal_gyro_right, "calibrate_gyro", SET_GYRO_CFG, 2036995887a1SDerek J. Clark RIGHT_CONTROLLER, index); 2037995887a1SDerek J. Clark static DEVICE_ATTR_RO_NAMED(cal_gyro_right_index, "calibrate_gyro_index"); 2038995887a1SDerek J. Clark 2039995887a1SDerek J. Clark static struct go_cfg_attr cal_trigg_right_status = { GET_CAL_STATUS }; 2040995887a1SDerek J. Clark LEGO_DEVICE_STATUS_ATTR(cal_trigg_right_status, "calibrate_trigger_status", 2041995887a1SDerek J. Clark RIGHT_CONTROLLER, CALDEV_TRIGGER); 2042995887a1SDerek J. Clark 2043995887a1SDerek J. Clark static struct go_cfg_attr cal_joy_right_status = { GET_CAL_STATUS }; 2044995887a1SDerek J. Clark LEGO_DEVICE_STATUS_ATTR(cal_joy_right_status, "calibrate_joystick_status", 2045995887a1SDerek J. Clark RIGHT_CONTROLLER, CALDEV_JOYSTICK); 2046995887a1SDerek J. Clark 2047995887a1SDerek J. Clark static struct go_cfg_attr cal_gyro_right_status = { GET_CAL_STATUS }; 2048995887a1SDerek J. Clark LEGO_DEVICE_STATUS_ATTR(cal_gyro_right_status, "calibrate_gyro_status", 2049995887a1SDerek J. Clark RIGHT_CONTROLLER, CALDEV_GYROSCOPE); 2050995887a1SDerek J. Clark 2051d69ccfcbSDerek J. Clark static struct attribute *right_gamepad_attrs[] = { 205282cd9bc8SDerek J. Clark &dev_attr_auto_sleep_time_right.attr, 205382cd9bc8SDerek J. Clark &dev_attr_auto_sleep_time_right_range.attr, 2054995887a1SDerek J. Clark &dev_attr_cal_gyro_right.attr, 2055995887a1SDerek J. Clark &dev_attr_cal_gyro_right_index.attr, 2056995887a1SDerek J. Clark &dev_attr_cal_gyro_right_status.attr, 2057995887a1SDerek J. Clark &dev_attr_cal_joy_right.attr, 2058995887a1SDerek J. Clark &dev_attr_cal_joy_right_index.attr, 2059995887a1SDerek J. Clark &dev_attr_cal_joy_right_status.attr, 2060995887a1SDerek J. Clark &dev_attr_cal_trigg_right.attr, 2061995887a1SDerek J. Clark &dev_attr_cal_trigg_right_index.attr, 2062995887a1SDerek J. Clark &dev_attr_cal_trigg_right_status.attr, 206382cd9bc8SDerek J. Clark &dev_attr_imu_bypass_right.attr, 206482cd9bc8SDerek J. Clark &dev_attr_imu_bypass_right_index.attr, 206582cd9bc8SDerek J. Clark &dev_attr_imu_enabled_right.attr, 206682cd9bc8SDerek J. Clark &dev_attr_imu_enabled_right_index.attr, 206782cd9bc8SDerek J. Clark &dev_attr_reset_right.attr, 206896b20c1fSDerek J. Clark &dev_attr_rumble_mode_right.attr, 206996b20c1fSDerek J. Clark &dev_attr_rumble_mode_right_index.attr, 207096b20c1fSDerek J. Clark &dev_attr_rumble_notification_right.attr, 207196b20c1fSDerek J. Clark &dev_attr_rumble_notification_right_index.attr, 2072d69ccfcbSDerek J. Clark &dev_attr_version_hardware_right.attr, 2073d69ccfcbSDerek J. Clark &dev_attr_version_firmware_right.attr, 2074d69ccfcbSDerek J. Clark &dev_attr_version_gen_right.attr, 2075d69ccfcbSDerek J. Clark &dev_attr_version_product_right.attr, 2076d69ccfcbSDerek J. Clark &dev_attr_version_protocol_right.attr, 2077d69ccfcbSDerek J. Clark NULL, 2078d69ccfcbSDerek J. Clark }; 2079d69ccfcbSDerek J. Clark 2080d69ccfcbSDerek J. Clark static const struct attribute_group right_gamepad_attr_group = { 2081d69ccfcbSDerek J. Clark .name = "right_handle", 2082d69ccfcbSDerek J. Clark .attrs = right_gamepad_attrs, 2083d69ccfcbSDerek J. Clark }; 2084d69ccfcbSDerek J. Clark 2085d69ccfcbSDerek J. Clark /* Touchpad */ 208682cd9bc8SDerek J. Clark static struct go_cfg_attr touchpad_enabled = { FEATURE_TOUCHPAD_ENABLE }; 208782cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(touchpad_enabled, "enabled", UNSPECIFIED, index, 208882cd9bc8SDerek J. Clark feature_status); 208982cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(touchpad_enabled_index, "enabled_index"); 209082cd9bc8SDerek J. Clark 209196b20c1fSDerek J. Clark static struct go_cfg_attr touchpad_vibration_enabled = { TP_VIBRATION_ENABLE }; 209296b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(touchpad_vibration_enabled, "vibration_enabled", UNSPECIFIED, 209396b20c1fSDerek J. Clark index, motor_config); 209496b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(touchpad_vibration_enabled_index, 209596b20c1fSDerek J. Clark "vibration_enabled_index"); 209696b20c1fSDerek J. Clark 209796b20c1fSDerek J. Clark static struct go_cfg_attr touchpad_vibration_intensity = { TP_VIBRATION_INTENSITY }; 209896b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(touchpad_vibration_intensity, "vibration_intensity", 209996b20c1fSDerek J. Clark UNSPECIFIED, index, motor_config); 210096b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(touchpad_vibration_intensity_index, 210196b20c1fSDerek J. Clark "vibration_intensity_index"); 210296b20c1fSDerek J. Clark 2103d69ccfcbSDerek J. Clark static struct attribute *touchpad_attrs[] = { 210482cd9bc8SDerek J. Clark &dev_attr_touchpad_enabled.attr, 210582cd9bc8SDerek J. Clark &dev_attr_touchpad_enabled_index.attr, 210696b20c1fSDerek J. Clark &dev_attr_touchpad_vibration_enabled.attr, 210796b20c1fSDerek J. Clark &dev_attr_touchpad_vibration_enabled_index.attr, 210896b20c1fSDerek J. Clark &dev_attr_touchpad_vibration_intensity.attr, 210996b20c1fSDerek J. Clark &dev_attr_touchpad_vibration_intensity_index.attr, 211096b20c1fSDerek J. Clark NULL, 2111d69ccfcbSDerek J. Clark }; 2112d69ccfcbSDerek J. Clark 2113d69ccfcbSDerek J. Clark static const struct attribute_group touchpad_attr_group = { 2114d69ccfcbSDerek J. Clark .name = "touchpad", 2115d69ccfcbSDerek J. Clark .attrs = touchpad_attrs, 2116d69ccfcbSDerek J. Clark }; 2117d69ccfcbSDerek J. Clark 2118d69ccfcbSDerek J. Clark static const struct attribute_group *top_level_attr_groups[] = { 2119d69ccfcbSDerek J. Clark &mcu_attr_group, &tx_dongle_attr_group, 2120d69ccfcbSDerek J. Clark &left_gamepad_attr_group, &right_gamepad_attr_group, 2121d69ccfcbSDerek J. Clark &touchpad_attr_group, NULL, 2122d69ccfcbSDerek J. Clark }; 2123d69ccfcbSDerek J. Clark 2124325262faSDerek J. Clark /* RGB */ 2125325262faSDerek J. Clark static struct go_cfg_attr rgb_enabled = { FEATURE_LIGHT_ENABLE }; 2126325262faSDerek J. Clark 2127325262faSDerek J. Clark LEGO_DEVICE_ATTR_RW(rgb_enabled, "enabled", UNSPECIFIED, index, feature_status); 2128325262faSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rgb_effect_index, "effect_index"); 2129325262faSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rgb_enabled_index, "enabled_index"); 2130325262faSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rgb_mode_index, "mode_index"); 2131325262faSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rgb_profile_range, "profile_range"); 2132325262faSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rgb_speed_range, "speed_range"); 2133325262faSDerek J. Clark static DEVICE_ATTR_RW_NAMED(rgb_effect, "effect"); 2134325262faSDerek J. Clark static DEVICE_ATTR_RW_NAMED(rgb_mode, "mode"); 2135325262faSDerek J. Clark static DEVICE_ATTR_RW_NAMED(rgb_profile, "profile"); 2136325262faSDerek J. Clark static DEVICE_ATTR_RW_NAMED(rgb_speed, "speed"); 2137325262faSDerek J. Clark 2138325262faSDerek J. Clark static struct attribute *go_rgb_attrs[] = { 2139325262faSDerek J. Clark &dev_attr_rgb_effect.attr, 2140325262faSDerek J. Clark &dev_attr_rgb_effect_index.attr, 2141325262faSDerek J. Clark &dev_attr_rgb_enabled.attr, 2142325262faSDerek J. Clark &dev_attr_rgb_enabled_index.attr, 2143325262faSDerek J. Clark &dev_attr_rgb_mode.attr, 2144325262faSDerek J. Clark &dev_attr_rgb_mode_index.attr, 2145325262faSDerek J. Clark &dev_attr_rgb_profile.attr, 2146325262faSDerek J. Clark &dev_attr_rgb_profile_range.attr, 2147325262faSDerek J. Clark &dev_attr_rgb_speed.attr, 2148325262faSDerek J. Clark &dev_attr_rgb_speed_range.attr, 2149325262faSDerek J. Clark NULL, 2150325262faSDerek J. Clark }; 2151325262faSDerek J. Clark 2152325262faSDerek J. Clark static struct attribute_group rgb_attr_group = { 2153325262faSDerek J. Clark .attrs = go_rgb_attrs, 2154325262faSDerek J. Clark }; 2155325262faSDerek J. Clark 2156325262faSDerek J. Clark static struct mc_subled go_rgb_subled_info[] = { 2157325262faSDerek J. Clark { 2158325262faSDerek J. Clark .color_index = LED_COLOR_ID_RED, 2159325262faSDerek J. Clark .brightness = 0x50, 2160325262faSDerek J. Clark .intensity = 0x24, 2161325262faSDerek J. Clark .channel = 0x1, 2162325262faSDerek J. Clark }, 2163325262faSDerek J. Clark { 2164325262faSDerek J. Clark .color_index = LED_COLOR_ID_GREEN, 2165325262faSDerek J. Clark .brightness = 0x50, 2166325262faSDerek J. Clark .intensity = 0x22, 2167325262faSDerek J. Clark .channel = 0x2, 2168325262faSDerek J. Clark }, 2169325262faSDerek J. Clark { 2170325262faSDerek J. Clark .color_index = LED_COLOR_ID_BLUE, 2171325262faSDerek J. Clark .brightness = 0x50, 2172325262faSDerek J. Clark .intensity = 0x99, 2173325262faSDerek J. Clark .channel = 0x3, 2174325262faSDerek J. Clark }, 2175325262faSDerek J. Clark }; 2176325262faSDerek J. Clark 2177325262faSDerek J. Clark static struct led_classdev_mc go_cdev_rgb = { 2178325262faSDerek J. Clark .led_cdev = { 2179325262faSDerek J. Clark .name = "go:rgb:joystick_rings", 2180325262faSDerek J. Clark .color = LED_COLOR_ID_RGB, 2181325262faSDerek J. Clark .brightness = 0x50, 2182325262faSDerek J. Clark .max_brightness = 0x64, 2183325262faSDerek J. Clark .brightness_set = hid_go_brightness_set, 2184325262faSDerek J. Clark }, 2185325262faSDerek J. Clark .num_colors = ARRAY_SIZE(go_rgb_subled_info), 2186325262faSDerek J. Clark .subled_info = go_rgb_subled_info, 2187325262faSDerek J. Clark }; 2188325262faSDerek J. Clark 2189d69ccfcbSDerek J. Clark static void cfg_setup(struct work_struct *work) 2190d69ccfcbSDerek J. Clark { 2191d69ccfcbSDerek J. Clark int ret; 2192d69ccfcbSDerek J. Clark 2193d69ccfcbSDerek J. Clark /* MCU Version Attrs */ 2194d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2195d69ccfcbSDerek J. Clark PRODUCT_VERSION, USB_MCU, NULL, 0); 2196d69ccfcbSDerek J. Clark if (ret < 0) { 2197d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2198d69ccfcbSDerek J. Clark "Failed to retrieve USB_MCU Product Version: %i\n", ret); 2199d69ccfcbSDerek J. Clark return; 2200d69ccfcbSDerek J. Clark } 2201d69ccfcbSDerek J. Clark 2202d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2203d69ccfcbSDerek J. Clark PROTOCOL_VERSION, USB_MCU, NULL, 0); 2204d69ccfcbSDerek J. Clark if (ret < 0) { 2205d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2206d69ccfcbSDerek J. Clark "Failed to retrieve USB_MCU Protocol Version: %i\n", ret); 2207d69ccfcbSDerek J. Clark return; 2208d69ccfcbSDerek J. Clark } 2209d69ccfcbSDerek J. Clark 2210d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2211d69ccfcbSDerek J. Clark FIRMWARE_VERSION, USB_MCU, NULL, 0); 2212d69ccfcbSDerek J. Clark if (ret < 0) { 2213d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2214d69ccfcbSDerek J. Clark "Failed to retrieve USB_MCU Firmware Version: %i\n", ret); 2215d69ccfcbSDerek J. Clark return; 2216d69ccfcbSDerek J. Clark } 2217d69ccfcbSDerek J. Clark 2218d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2219d69ccfcbSDerek J. Clark HARDWARE_VERSION, USB_MCU, NULL, 0); 2220d69ccfcbSDerek J. Clark if (ret < 0) { 2221d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2222d69ccfcbSDerek J. Clark "Failed to retrieve USB_MCU Hardware Version: %i\n", ret); 2223d69ccfcbSDerek J. Clark return; 2224d69ccfcbSDerek J. Clark } 2225d69ccfcbSDerek J. Clark 2226d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2227d69ccfcbSDerek J. Clark HARDWARE_GENERATION, USB_MCU, NULL, 0); 2228d69ccfcbSDerek J. Clark if (ret < 0) { 2229d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2230d69ccfcbSDerek J. Clark "Failed to retrieve USB_MCU Hardware Generation: %i\n", ret); 2231d69ccfcbSDerek J. Clark return; 2232d69ccfcbSDerek J. Clark } 2233d69ccfcbSDerek J. Clark 2234d69ccfcbSDerek J. Clark /* TX Dongle Version Attrs */ 2235d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2236d69ccfcbSDerek J. Clark PRODUCT_VERSION, TX_DONGLE, NULL, 0); 2237d69ccfcbSDerek J. Clark if (ret < 0) { 2238d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2239d69ccfcbSDerek J. Clark "Failed to retrieve TX_DONGLE Product Version: %i\n", ret); 2240d69ccfcbSDerek J. Clark return; 2241d69ccfcbSDerek J. Clark } 2242d69ccfcbSDerek J. Clark 2243d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2244d69ccfcbSDerek J. Clark PROTOCOL_VERSION, TX_DONGLE, NULL, 0); 2245d69ccfcbSDerek J. Clark if (ret < 0) { 2246d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2247d69ccfcbSDerek J. Clark "Failed to retrieve TX_DONGLE Protocol Version: %i\n", ret); 2248d69ccfcbSDerek J. Clark return; 2249d69ccfcbSDerek J. Clark } 2250d69ccfcbSDerek J. Clark 2251d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2252d69ccfcbSDerek J. Clark FIRMWARE_VERSION, TX_DONGLE, NULL, 0); 2253d69ccfcbSDerek J. Clark if (ret < 0) { 2254d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2255d69ccfcbSDerek J. Clark "Failed to retrieve TX_DONGLE Firmware Version: %i\n", ret); 2256d69ccfcbSDerek J. Clark return; 2257d69ccfcbSDerek J. Clark } 2258d69ccfcbSDerek J. Clark 2259d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2260d69ccfcbSDerek J. Clark HARDWARE_VERSION, TX_DONGLE, NULL, 0); 2261d69ccfcbSDerek J. Clark if (ret < 0) { 2262d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2263d69ccfcbSDerek J. Clark "Failed to retrieve TX_DONGLE Hardware Version: %i\n", ret); 2264d69ccfcbSDerek J. Clark return; 2265d69ccfcbSDerek J. Clark } 2266d69ccfcbSDerek J. Clark 2267d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2268d69ccfcbSDerek J. Clark HARDWARE_GENERATION, TX_DONGLE, NULL, 0); 2269d69ccfcbSDerek J. Clark if (ret < 0) { 2270d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2271d69ccfcbSDerek J. Clark "Failed to retrieve TX_DONGLE Hardware Generation: %i\n", ret); 2272d69ccfcbSDerek J. Clark return; 2273d69ccfcbSDerek J. Clark } 2274d69ccfcbSDerek J. Clark 2275d69ccfcbSDerek J. Clark /* Left Handle Version Attrs */ 2276d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2277d69ccfcbSDerek J. Clark PRODUCT_VERSION, LEFT_CONTROLLER, NULL, 0); 2278d69ccfcbSDerek J. Clark if (ret < 0) { 2279d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2280d69ccfcbSDerek J. Clark "Failed to retrieve LEFT_CONTROLLER Product Version: %i\n", ret); 2281d69ccfcbSDerek J. Clark return; 2282d69ccfcbSDerek J. Clark } 2283d69ccfcbSDerek J. Clark 2284d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2285d69ccfcbSDerek J. Clark PROTOCOL_VERSION, LEFT_CONTROLLER, NULL, 0); 2286d69ccfcbSDerek J. Clark if (ret < 0) { 2287d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2288d69ccfcbSDerek J. Clark "Failed to retrieve LEFT_CONTROLLER Protocol Version: %i\n", ret); 2289d69ccfcbSDerek J. Clark return; 2290d69ccfcbSDerek J. Clark } 2291d69ccfcbSDerek J. Clark 2292d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2293d69ccfcbSDerek J. Clark FIRMWARE_VERSION, LEFT_CONTROLLER, NULL, 0); 2294d69ccfcbSDerek J. Clark if (ret < 0) { 2295d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2296d69ccfcbSDerek J. Clark "Failed to retrieve LEFT_CONTROLLER Firmware Version: %i\n", ret); 2297d69ccfcbSDerek J. Clark return; 2298d69ccfcbSDerek J. Clark } 2299d69ccfcbSDerek J. Clark 2300d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2301d69ccfcbSDerek J. Clark HARDWARE_VERSION, LEFT_CONTROLLER, NULL, 0); 2302d69ccfcbSDerek J. Clark if (ret < 0) { 2303d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2304d69ccfcbSDerek J. Clark "Failed to retrieve LEFT_CONTROLLER Hardware Version: %i\n", ret); 2305d69ccfcbSDerek J. Clark return; 2306d69ccfcbSDerek J. Clark } 2307d69ccfcbSDerek J. Clark 2308d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2309d69ccfcbSDerek J. Clark HARDWARE_GENERATION, LEFT_CONTROLLER, NULL, 0); 2310d69ccfcbSDerek J. Clark if (ret < 0) { 2311d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2312d69ccfcbSDerek J. Clark "Failed to retrieve LEFT_CONTROLLER Hardware Generation: %i\n", ret); 2313d69ccfcbSDerek J. Clark return; 2314d69ccfcbSDerek J. Clark } 2315d69ccfcbSDerek J. Clark 2316d69ccfcbSDerek J. Clark /* Right Handle Version Attrs */ 2317d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2318d69ccfcbSDerek J. Clark PRODUCT_VERSION, RIGHT_CONTROLLER, NULL, 0); 2319d69ccfcbSDerek J. Clark if (ret < 0) { 2320d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2321d69ccfcbSDerek J. Clark "Failed to retrieve RIGHT_CONTROLLER Product Version: %i\n", ret); 2322d69ccfcbSDerek J. Clark return; 2323d69ccfcbSDerek J. Clark } 2324d69ccfcbSDerek J. Clark 2325d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2326d69ccfcbSDerek J. Clark PROTOCOL_VERSION, RIGHT_CONTROLLER, NULL, 0); 2327d69ccfcbSDerek J. Clark if (ret < 0) { 2328d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2329d69ccfcbSDerek J. Clark "Failed to retrieve RIGHT_CONTROLLER Protocol Version: %i\n", ret); 2330d69ccfcbSDerek J. Clark return; 2331d69ccfcbSDerek J. Clark } 2332d69ccfcbSDerek J. Clark 2333d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2334d69ccfcbSDerek J. Clark FIRMWARE_VERSION, RIGHT_CONTROLLER, NULL, 0); 2335d69ccfcbSDerek J. Clark if (ret < 0) { 2336d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2337d69ccfcbSDerek J. Clark "Failed to retrieve RIGHT_CONTROLLER Firmware Version: %i\n", ret); 2338d69ccfcbSDerek J. Clark return; 2339d69ccfcbSDerek J. Clark } 2340d69ccfcbSDerek J. Clark 2341d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2342d69ccfcbSDerek J. Clark HARDWARE_VERSION, RIGHT_CONTROLLER, NULL, 0); 2343d69ccfcbSDerek J. Clark if (ret < 0) { 2344d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2345d69ccfcbSDerek J. Clark "Failed to retrieve RIGHT_CONTROLLER Hardware Version: %i\n", ret); 2346d69ccfcbSDerek J. Clark return; 2347d69ccfcbSDerek J. Clark } 2348d69ccfcbSDerek J. Clark 2349d69ccfcbSDerek J. Clark ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, 2350d69ccfcbSDerek J. Clark HARDWARE_GENERATION, RIGHT_CONTROLLER, NULL, 0); 2351d69ccfcbSDerek J. Clark if (ret < 0) { 2352d69ccfcbSDerek J. Clark dev_err(&drvdata.hdev->dev, 2353d69ccfcbSDerek J. Clark "Failed to retrieve RIGHT_CONTROLLER Hardware Generation: %i\n", ret); 2354d69ccfcbSDerek J. Clark return; 2355d69ccfcbSDerek J. Clark } 2356d69ccfcbSDerek J. Clark } 2357d69ccfcbSDerek J. Clark 2358d69ccfcbSDerek J. Clark static int hid_go_cfg_probe(struct hid_device *hdev, 2359d69ccfcbSDerek J. Clark const struct hid_device_id *_id) 2360d69ccfcbSDerek J. Clark { 2361d69ccfcbSDerek J. Clark unsigned char *buf; 2362d69ccfcbSDerek J. Clark int ret; 2363d69ccfcbSDerek J. Clark 2364d69ccfcbSDerek J. Clark buf = devm_kzalloc(&hdev->dev, GO_PACKET_SIZE, GFP_KERNEL); 2365d69ccfcbSDerek J. Clark if (!buf) 2366d69ccfcbSDerek J. Clark return -ENOMEM; 2367d69ccfcbSDerek J. Clark 2368d69ccfcbSDerek J. Clark hid_set_drvdata(hdev, &drvdata); 2369d69ccfcbSDerek J. Clark drvdata.hdev = hdev; 2370d69ccfcbSDerek J. Clark mutex_init(&drvdata.cfg_mutex); 2371d69ccfcbSDerek J. Clark 2372d69ccfcbSDerek J. Clark ret = sysfs_create_groups(&hdev->dev.kobj, top_level_attr_groups); 2373d69ccfcbSDerek J. Clark if (ret) { 2374d69ccfcbSDerek J. Clark dev_err_probe(&hdev->dev, ret, 2375d69ccfcbSDerek J. Clark "Failed to create gamepad configuration attributes\n"); 2376d69ccfcbSDerek J. Clark return ret; 2377d69ccfcbSDerek J. Clark } 2378d69ccfcbSDerek J. Clark 2379325262faSDerek J. Clark ret = devm_led_classdev_multicolor_register(&hdev->dev, &go_cdev_rgb); 2380325262faSDerek J. Clark if (ret) { 2381325262faSDerek J. Clark dev_err_probe(&hdev->dev, ret, "Failed to create RGB device\n"); 2382325262faSDerek J. Clark return ret; 2383325262faSDerek J. Clark } 2384325262faSDerek J. Clark 2385325262faSDerek J. Clark ret = devm_device_add_group(go_cdev_rgb.led_cdev.dev, &rgb_attr_group); 2386325262faSDerek J. Clark if (ret) { 2387325262faSDerek J. Clark dev_err_probe(&hdev->dev, ret, 2388325262faSDerek J. Clark "Failed to create RGB configuration attributes\n"); 2389325262faSDerek J. Clark return ret; 2390325262faSDerek J. Clark } 2391325262faSDerek J. Clark 2392325262faSDerek J. Clark drvdata.led_cdev = &go_cdev_rgb.led_cdev; 2393325262faSDerek J. Clark 2394d69ccfcbSDerek J. Clark init_completion(&drvdata.send_cmd_complete); 2395d69ccfcbSDerek J. Clark 2396d69ccfcbSDerek J. Clark /* Executing calls prior to returning from probe will lock the MCU. Schedule 2397d69ccfcbSDerek J. Clark * initial data call after probe has completed and MCU can accept calls. 2398d69ccfcbSDerek J. Clark */ 2399d69ccfcbSDerek J. Clark INIT_DELAYED_WORK(&drvdata.go_cfg_setup, &cfg_setup); 2400d69ccfcbSDerek J. Clark ret = schedule_delayed_work(&drvdata.go_cfg_setup, msecs_to_jiffies(2)); 2401d69ccfcbSDerek J. Clark if (!ret) { 2402d69ccfcbSDerek J. Clark dev_err(&hdev->dev, 2403d69ccfcbSDerek J. Clark "Failed to schedule startup delayed work\n"); 2404d69ccfcbSDerek J. Clark return -ENODEV; 2405d69ccfcbSDerek J. Clark } 2406d69ccfcbSDerek J. Clark return 0; 2407d69ccfcbSDerek J. Clark } 2408d69ccfcbSDerek J. Clark 2409d69ccfcbSDerek J. Clark static void hid_go_cfg_remove(struct hid_device *hdev) 2410d69ccfcbSDerek J. Clark { 2411d69ccfcbSDerek J. Clark guard(mutex)(&drvdata.cfg_mutex); 2412d69ccfcbSDerek J. Clark sysfs_remove_groups(&hdev->dev.kobj, top_level_attr_groups); 2413d69ccfcbSDerek J. Clark hid_hw_close(hdev); 2414d69ccfcbSDerek J. Clark hid_hw_stop(hdev); 2415d69ccfcbSDerek J. Clark hid_set_drvdata(hdev, NULL); 2416d69ccfcbSDerek J. Clark } 2417d69ccfcbSDerek J. Clark 2418d69ccfcbSDerek J. Clark static int hid_go_probe(struct hid_device *hdev, const struct hid_device_id *id) 2419d69ccfcbSDerek J. Clark { 2420d69ccfcbSDerek J. Clark int ret, ep; 2421d69ccfcbSDerek J. Clark 2422d69ccfcbSDerek J. Clark hdev->quirks |= HID_QUIRK_INPUT_PER_APP | HID_QUIRK_MULTI_INPUT; 2423d69ccfcbSDerek J. Clark 2424d69ccfcbSDerek J. Clark ret = hid_parse(hdev); 2425d69ccfcbSDerek J. Clark if (ret) { 2426d69ccfcbSDerek J. Clark hid_err(hdev, "Parse failed\n"); 2427d69ccfcbSDerek J. Clark return ret; 2428d69ccfcbSDerek J. Clark } 2429d69ccfcbSDerek J. Clark 2430d69ccfcbSDerek J. Clark ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 2431d69ccfcbSDerek J. Clark if (ret) { 2432d69ccfcbSDerek J. Clark hid_err(hdev, "Failed to start HID device\n"); 2433d69ccfcbSDerek J. Clark return ret; 2434d69ccfcbSDerek J. Clark } 2435d69ccfcbSDerek J. Clark 2436d69ccfcbSDerek J. Clark ret = hid_hw_open(hdev); 2437d69ccfcbSDerek J. Clark if (ret) { 2438d69ccfcbSDerek J. Clark hid_err(hdev, "Failed to open HID device\n"); 2439d69ccfcbSDerek J. Clark hid_hw_stop(hdev); 2440d69ccfcbSDerek J. Clark return ret; 2441d69ccfcbSDerek J. Clark } 2442d69ccfcbSDerek J. Clark 2443d69ccfcbSDerek J. Clark ep = get_endpoint_address(hdev); 2444d69ccfcbSDerek J. Clark if (ep != GO_GP_INTF_IN) { 2445d69ccfcbSDerek J. Clark dev_dbg(&hdev->dev, "Started interface %x as generic HID device\n", ep); 2446d69ccfcbSDerek J. Clark return 0; 2447d69ccfcbSDerek J. Clark } 2448d69ccfcbSDerek J. Clark 2449d69ccfcbSDerek J. Clark ret = hid_go_cfg_probe(hdev, id); 2450d69ccfcbSDerek J. Clark if (ret) 2451d69ccfcbSDerek J. Clark dev_err_probe(&hdev->dev, ret, "Failed to start configuration interface\n"); 2452d69ccfcbSDerek J. Clark 2453d69ccfcbSDerek J. Clark dev_dbg(&hdev->dev, "Started Legion Go HID Device: %x\n", ep); 2454d69ccfcbSDerek J. Clark 2455d69ccfcbSDerek J. Clark return ret; 2456d69ccfcbSDerek J. Clark } 2457d69ccfcbSDerek J. Clark 2458d69ccfcbSDerek J. Clark static void hid_go_remove(struct hid_device *hdev) 2459d69ccfcbSDerek J. Clark { 2460d69ccfcbSDerek J. Clark int ep = get_endpoint_address(hdev); 2461d69ccfcbSDerek J. Clark 2462d69ccfcbSDerek J. Clark if (ep <= 0) 2463d69ccfcbSDerek J. Clark return; 2464d69ccfcbSDerek J. Clark 2465d69ccfcbSDerek J. Clark switch (ep) { 2466d69ccfcbSDerek J. Clark case GO_GP_INTF_IN: 2467d69ccfcbSDerek J. Clark hid_go_cfg_remove(hdev); 2468d69ccfcbSDerek J. Clark break; 2469d69ccfcbSDerek J. Clark default: 2470d69ccfcbSDerek J. Clark hid_hw_close(hdev); 2471d69ccfcbSDerek J. Clark hid_hw_stop(hdev); 2472d69ccfcbSDerek J. Clark break; 2473d69ccfcbSDerek J. Clark } 2474d69ccfcbSDerek J. Clark } 2475d69ccfcbSDerek J. Clark 2476d69ccfcbSDerek J. Clark static const struct hid_device_id hid_go_devices[] = { 2477d69ccfcbSDerek J. Clark { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, 2478d69ccfcbSDerek J. Clark USB_DEVICE_ID_LENOVO_LEGION_GO2_XINPUT) }, 2479d69ccfcbSDerek J. Clark { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, 2480d69ccfcbSDerek J. Clark USB_DEVICE_ID_LENOVO_LEGION_GO2_DINPUT) }, 2481d69ccfcbSDerek J. Clark { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, 2482d69ccfcbSDerek J. Clark USB_DEVICE_ID_LENOVO_LEGION_GO2_DUAL_DINPUT) }, 2483d69ccfcbSDerek J. Clark { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, 2484d69ccfcbSDerek J. Clark USB_DEVICE_ID_LENOVO_LEGION_GO2_FPS) }, 2485d69ccfcbSDerek J. Clark {} 2486d69ccfcbSDerek J. Clark }; 2487d69ccfcbSDerek J. Clark MODULE_DEVICE_TABLE(hid, hid_go_devices); 2488d69ccfcbSDerek J. Clark 2489d69ccfcbSDerek J. Clark static struct hid_driver hid_lenovo_go = { 2490d69ccfcbSDerek J. Clark .name = "hid-lenovo-go", 2491d69ccfcbSDerek J. Clark .id_table = hid_go_devices, 2492d69ccfcbSDerek J. Clark .probe = hid_go_probe, 2493d69ccfcbSDerek J. Clark .remove = hid_go_remove, 2494d69ccfcbSDerek J. Clark .raw_event = hid_go_raw_event, 2495d69ccfcbSDerek J. Clark }; 2496d69ccfcbSDerek J. Clark module_hid_driver(hid_lenovo_go); 2497d69ccfcbSDerek J. Clark 2498d69ccfcbSDerek J. Clark MODULE_AUTHOR("Derek J. Clark"); 2499d69ccfcbSDerek J. Clark MODULE_DESCRIPTION("HID Driver for Lenovo Legion Go Series Gamepads."); 2500d69ccfcbSDerek J. Clark MODULE_LICENSE("GPL"); 2501