1a9517958SDerek J. Clark // SPDX-License-Identifier: GPL-2.0-or-later 2a9517958SDerek J. Clark /* 3a9517958SDerek J. Clark * HID driver for MSI Claw Handheld PC gamepads. 4a9517958SDerek J. Clark * 5a9517958SDerek J. Clark * Provides configuration support for the MSI Claw series of handheld PC 6a9517958SDerek J. Clark * gamepads. Multiple iterations of the device firmware has led to some 7a9517958SDerek J. Clark * quirks for how certain attributes are handled. The original firmware 8a9517958SDerek J. Clark * did not support remapping of the M1 (right) and M2 (left) rear paddles. 9a9517958SDerek J. Clark * Additionally, the MCU RAM address for writing configuration data has 10a9517958SDerek J. Clark * changed twice. Checks are done during probe to enumerate these variances. 11a9517958SDerek J. Clark * 12a9517958SDerek J. Clark * Copyright (c) 2026 Zhouwang Huang <honjow311@gmail.com> 13a9517958SDerek J. Clark * Copyright (c) 2026 Denis Benato <denis.benato@linux.dev> 14a9517958SDerek J. Clark * Copyright (c) 2026 Valve Corporation 15a9517958SDerek J. Clark */ 16a9517958SDerek J. Clark 17a9517958SDerek J. Clark #include <linux/array_size.h> 18a9517958SDerek J. Clark #include <linux/cleanup.h> 19a9517958SDerek J. Clark #include <linux/completion.h> 20a9517958SDerek J. Clark #include <linux/container_of.h> 21a9517958SDerek J. Clark #include <linux/delay.h> 22a9517958SDerek J. Clark #include <linux/device.h> 23a9517958SDerek J. Clark #include <linux/hid.h> 24a9517958SDerek J. Clark #include <linux/kobject.h> 25f27e2e3cSDerek J. Clark #include <linux/led-class-multicolor.h> 26a9517958SDerek J. Clark #include <linux/leds.h> 27a9517958SDerek J. Clark #include <linux/module.h> 28a9517958SDerek J. Clark #include <linux/mutex.h> 29a9517958SDerek J. Clark #include <linux/pm.h> 30a9517958SDerek J. Clark #include <linux/spinlock.h> 31a9517958SDerek J. Clark #include <linux/sysfs.h> 32a9517958SDerek J. Clark #include <linux/types.h> 33a9517958SDerek J. Clark #include <linux/unaligned.h> 34a9517958SDerek J. Clark #include <linux/usb.h> 35a9517958SDerek J. Clark #include <linux/workqueue.h> 36a9517958SDerek J. Clark 37a9517958SDerek J. Clark #include "hid-ids.h" 38a9517958SDerek J. Clark 39a9517958SDerek J. Clark #define CLAW_OUTPUT_REPORT_ID 0x0f 40a9517958SDerek J. Clark #define CLAW_INPUT_REPORT_ID 0x10 41a9517958SDerek J. Clark 42a9517958SDerek J. Clark #define CLAW_PACKET_SIZE 64 43a9517958SDerek J. Clark 44a9517958SDerek J. Clark #define CLAW_DINPUT_CFG_INTF_IN 0x82 45a9517958SDerek J. Clark #define CLAW_XINPUT_CFG_INTF_IN 0x83 46a9517958SDerek J. Clark 4793ff13edSDerek J. Clark #define CLAW_KEYS_MAX 5 4893ff13edSDerek J. Clark 49f27e2e3cSDerek J. Clark #define CLAW_RGB_ZONES 9 50f27e2e3cSDerek J. Clark #define CLAW_RGB_MAX_FRAMES 8 51f27e2e3cSDerek J. Clark #define CLAW_RGB_FRAME_OFFSET 0x24 52f27e2e3cSDerek J. Clark 53a9517958SDerek J. Clark enum claw_command_index { 54a9517958SDerek J. Clark CLAW_COMMAND_TYPE_NONE = 0x00, 55a9517958SDerek J. Clark CLAW_COMMAND_TYPE_READ_PROFILE = 0x04, 56a9517958SDerek J. Clark CLAW_COMMAND_TYPE_READ_PROFILE_ACK = 0x05, 57a9517958SDerek J. Clark CLAW_COMMAND_TYPE_ACK = 0x06, 58a9517958SDerek J. Clark CLAW_COMMAND_TYPE_WRITE_PROFILE_DATA = 0x21, 59a9517958SDerek J. Clark CLAW_COMMAND_TYPE_SYNC_TO_ROM = 0x22, 60a9517958SDerek J. Clark CLAW_COMMAND_TYPE_SWITCH_MODE = 0x24, 61a9517958SDerek J. Clark CLAW_COMMAND_TYPE_READ_GAMEPAD_MODE = 0x26, 62a9517958SDerek J. Clark CLAW_COMMAND_TYPE_GAMEPAD_MODE_ACK = 0x27, 63a9517958SDerek J. Clark CLAW_COMMAND_TYPE_RESET_DEVICE = 0x28, 64a9517958SDerek J. Clark }; 65a9517958SDerek J. Clark 66a9517958SDerek J. Clark enum claw_gamepad_mode_index { 67a9517958SDerek J. Clark CLAW_GAMEPAD_MODE_XINPUT = 0x01, 68a9517958SDerek J. Clark CLAW_GAMEPAD_MODE_DINPUT = 0x02, 69a9517958SDerek J. Clark CLAW_GAMEPAD_MODE_DESKTOP = 0x04, 70a9517958SDerek J. Clark }; 71a9517958SDerek J. Clark 72a9517958SDerek J. Clark static const char * const claw_gamepad_mode_text[] = { 73a9517958SDerek J. Clark [CLAW_GAMEPAD_MODE_XINPUT] = "xinput", 74a9517958SDerek J. Clark [CLAW_GAMEPAD_MODE_DINPUT] = "dinput", 75a9517958SDerek J. Clark [CLAW_GAMEPAD_MODE_DESKTOP] = "desktop", 76a9517958SDerek J. Clark }; 77a9517958SDerek J. Clark 7893ff13edSDerek J. Clark enum claw_profile_ack_pending { 7993ff13edSDerek J. Clark CLAW_NO_PENDING, 8093ff13edSDerek J. Clark CLAW_M1_PENDING, 8193ff13edSDerek J. Clark CLAW_M2_PENDING, 82f27e2e3cSDerek J. Clark CLAW_RGB_PENDING, 83*95547c53SDerek J. Clark CLAW_RUMBLE_LEFT_PENDING, 84*95547c53SDerek J. Clark CLAW_RUMBLE_RIGHT_PENDING, 8593ff13edSDerek J. Clark }; 8693ff13edSDerek J. Clark 8793ff13edSDerek J. Clark enum claw_key_index { 8893ff13edSDerek J. Clark CLAW_KEY_M1, 8993ff13edSDerek J. Clark CLAW_KEY_M2, 9093ff13edSDerek J. Clark }; 9193ff13edSDerek J. Clark 92a9517958SDerek J. Clark enum claw_mkeys_function_index { 93a9517958SDerek J. Clark CLAW_MKEY_FUNCTION_MACRO, 94a9517958SDerek J. Clark CLAW_MKEY_FUNCTION_DISABLED, 95a9517958SDerek J. Clark CLAW_MKEY_FUNCTION_COMBO, 96a9517958SDerek J. Clark }; 97a9517958SDerek J. Clark 98a9517958SDerek J. Clark enum claw_mode_field { 99a9517958SDerek J. Clark CLAW_FIELD_GAMEPAD_MODE, 100a9517958SDerek J. Clark CLAW_FIELD_MKEYS_FUNCTION, 101a9517958SDerek J. Clark }; 102a9517958SDerek J. Clark 103a9517958SDerek J. Clark static const char * const claw_mkeys_function_text[] = { 104a9517958SDerek J. Clark [CLAW_MKEY_FUNCTION_MACRO] = "macro", 105a9517958SDerek J. Clark [CLAW_MKEY_FUNCTION_DISABLED] = "disabled", 106a9517958SDerek J. Clark [CLAW_MKEY_FUNCTION_COMBO] = "combination", 107a9517958SDerek J. Clark }; 108a9517958SDerek J. Clark 10993ff13edSDerek J. Clark static const struct { 11093ff13edSDerek J. Clark u8 code; 11193ff13edSDerek J. Clark const char *name; 11293ff13edSDerek J. Clark } claw_button_mapping_key_map[] = { 11393ff13edSDerek J. Clark /* Gamepad buttons */ 11493ff13edSDerek J. Clark { 0x01, "ABS_HAT0Y_UP" }, 11593ff13edSDerek J. Clark { 0x02, "ABS_HAT0Y_DOWN" }, 11693ff13edSDerek J. Clark { 0x03, "ABS_HAT0X_LEFT" }, 11793ff13edSDerek J. Clark { 0x04, "ABS_HAT0X_RIGHT" }, 11893ff13edSDerek J. Clark { 0x05, "BTN_TL" }, 11993ff13edSDerek J. Clark { 0x06, "BTN_TR" }, 12093ff13edSDerek J. Clark { 0x07, "BTN_THUMBL" }, 12193ff13edSDerek J. Clark { 0x08, "BTN_THUMBR" }, 12293ff13edSDerek J. Clark { 0x09, "BTN_SOUTH" }, 12393ff13edSDerek J. Clark { 0x0a, "BTN_EAST" }, 12493ff13edSDerek J. Clark { 0x0b, "BTN_NORTH" }, 12593ff13edSDerek J. Clark { 0x0c, "BTN_WEST" }, 12693ff13edSDerek J. Clark { 0x0d, "BTN_MODE" }, 12793ff13edSDerek J. Clark { 0x0e, "BTN_SELECT" }, 12893ff13edSDerek J. Clark { 0x0f, "BTN_START" }, 12993ff13edSDerek J. Clark { 0x13, "BTN_TL2"}, 13093ff13edSDerek J. Clark { 0x14, "BTN_TR2"}, 13193ff13edSDerek J. Clark { 0x15, "ABS_Y_UP"}, 13293ff13edSDerek J. Clark { 0x16, "ABS_Y_DOWN"}, 13393ff13edSDerek J. Clark { 0x17, "ABS_X_LEFT"}, 13493ff13edSDerek J. Clark { 0x18, "ABS_X_RIGHT"}, 13593ff13edSDerek J. Clark { 0x19, "ABS_RY_UP"}, 13693ff13edSDerek J. Clark { 0x1a, "ABS_RY_DOWN"}, 13793ff13edSDerek J. Clark { 0x1b, "ABS_RX_LEFT"}, 13893ff13edSDerek J. Clark { 0x1c, "ABS_RX_RIGHT"}, 13993ff13edSDerek J. Clark /* Keyboard keys */ 14093ff13edSDerek J. Clark { 0x32, "KEY_ESC" }, 14193ff13edSDerek J. Clark { 0x33, "KEY_F1" }, 14293ff13edSDerek J. Clark { 0x34, "KEY_F2" }, 14393ff13edSDerek J. Clark { 0x35, "KEY_F3" }, 14493ff13edSDerek J. Clark { 0x36, "KEY_F4" }, 14593ff13edSDerek J. Clark { 0x37, "KEY_F5" }, 14693ff13edSDerek J. Clark { 0x38, "KEY_F6" }, 14793ff13edSDerek J. Clark { 0x39, "KEY_F7" }, 14893ff13edSDerek J. Clark { 0x3a, "KEY_F8" }, 14993ff13edSDerek J. Clark { 0x3b, "KEY_F9" }, 15093ff13edSDerek J. Clark { 0x3c, "KEY_F10" }, 15193ff13edSDerek J. Clark { 0x3d, "KEY_F11" }, 15293ff13edSDerek J. Clark { 0x3e, "KEY_F12" }, 15393ff13edSDerek J. Clark { 0x3f, "KEY_GRAVE" }, 15493ff13edSDerek J. Clark { 0x40, "KEY_1" }, 15593ff13edSDerek J. Clark { 0x41, "KEY_2" }, 15693ff13edSDerek J. Clark { 0x42, "KEY_3" }, 15793ff13edSDerek J. Clark { 0x43, "KEY_4" }, 15893ff13edSDerek J. Clark { 0x44, "KEY_5" }, 15993ff13edSDerek J. Clark { 0x45, "KEY_6" }, 16093ff13edSDerek J. Clark { 0x46, "KEY_7" }, 16193ff13edSDerek J. Clark { 0x47, "KEY_8" }, 16293ff13edSDerek J. Clark { 0x48, "KEY_9" }, 16393ff13edSDerek J. Clark { 0x49, "KEY_0" }, 16493ff13edSDerek J. Clark { 0x4a, "KEY_MINUS" }, 16593ff13edSDerek J. Clark { 0x4b, "KEY_EQUAL" }, 16693ff13edSDerek J. Clark { 0x4c, "KEY_BACKSPACE" }, 16793ff13edSDerek J. Clark { 0x4d, "KEY_TAB" }, 16893ff13edSDerek J. Clark { 0x4e, "KEY_Q" }, 16993ff13edSDerek J. Clark { 0x4f, "KEY_W" }, 17093ff13edSDerek J. Clark { 0x50, "KEY_E" }, 17193ff13edSDerek J. Clark { 0x51, "KEY_R" }, 17293ff13edSDerek J. Clark { 0x52, "KEY_T" }, 17393ff13edSDerek J. Clark { 0x53, "KEY_Y" }, 17493ff13edSDerek J. Clark { 0x54, "KEY_U" }, 17593ff13edSDerek J. Clark { 0x55, "KEY_I" }, 17693ff13edSDerek J. Clark { 0x56, "KEY_O" }, 17793ff13edSDerek J. Clark { 0x57, "KEY_P" }, 17893ff13edSDerek J. Clark { 0x58, "KEY_LEFTBRACE" }, 17993ff13edSDerek J. Clark { 0x59, "KEY_RIGHTBRACE" }, 18093ff13edSDerek J. Clark { 0x5a, "KEY_BACKSLASH" }, 18193ff13edSDerek J. Clark { 0x5b, "KEY_CAPSLOCK" }, 18293ff13edSDerek J. Clark { 0x5c, "KEY_A" }, 18393ff13edSDerek J. Clark { 0x5d, "KEY_S" }, 18493ff13edSDerek J. Clark { 0x5e, "KEY_D" }, 18593ff13edSDerek J. Clark { 0x5f, "KEY_F" }, 18693ff13edSDerek J. Clark { 0x60, "KEY_G" }, 18793ff13edSDerek J. Clark { 0x61, "KEY_H" }, 18893ff13edSDerek J. Clark { 0x62, "KEY_J" }, 18993ff13edSDerek J. Clark { 0x63, "KEY_K" }, 19093ff13edSDerek J. Clark { 0x64, "KEY_L" }, 19193ff13edSDerek J. Clark { 0x65, "KEY_SEMICOLON" }, 19293ff13edSDerek J. Clark { 0x66, "KEY_APOSTROPHE" }, 19393ff13edSDerek J. Clark { 0x67, "KEY_ENTER" }, 19493ff13edSDerek J. Clark { 0x68, "KEY_LEFTSHIFT" }, 19593ff13edSDerek J. Clark { 0x69, "KEY_Z" }, 19693ff13edSDerek J. Clark { 0x6a, "KEY_X" }, 19793ff13edSDerek J. Clark { 0x6b, "KEY_C" }, 19893ff13edSDerek J. Clark { 0x6c, "KEY_V" }, 19993ff13edSDerek J. Clark { 0x6d, "KEY_B" }, 20093ff13edSDerek J. Clark { 0x6e, "KEY_N" }, 20193ff13edSDerek J. Clark { 0x6f, "KEY_M" }, 20293ff13edSDerek J. Clark { 0x70, "KEY_COMMA" }, 20393ff13edSDerek J. Clark { 0x71, "KEY_DOT" }, 20493ff13edSDerek J. Clark { 0x72, "KEY_SLASH" }, 20593ff13edSDerek J. Clark { 0x73, "KEY_RIGHTSHIFT" }, 20693ff13edSDerek J. Clark { 0x74, "KEY_LEFTCTRL" }, 20793ff13edSDerek J. Clark { 0x75, "KEY_LEFTMETA" }, 20893ff13edSDerek J. Clark { 0x76, "KEY_LEFTALT" }, 20993ff13edSDerek J. Clark { 0x77, "KEY_SPACE" }, 21093ff13edSDerek J. Clark { 0x78, "KEY_RIGHTALT" }, 21193ff13edSDerek J. Clark { 0x79, "KEY_RIGHTCTRL" }, 21293ff13edSDerek J. Clark { 0x7a, "KEY_INSERT" }, 21393ff13edSDerek J. Clark { 0x7b, "KEY_HOME" }, 21493ff13edSDerek J. Clark { 0x7c, "KEY_PAGEUP" }, 21593ff13edSDerek J. Clark { 0x7d, "KEY_DELETE" }, 21693ff13edSDerek J. Clark { 0x7e, "KEY_END" }, 21793ff13edSDerek J. Clark { 0x7f, "KEY_PAGEDOWN" }, 21893ff13edSDerek J. Clark { 0x8a, "KEY_KPENTER" }, 21993ff13edSDerek J. Clark { 0x8b, "KEY_KP0" }, 22093ff13edSDerek J. Clark { 0x8c, "KEY_KP1" }, 22193ff13edSDerek J. Clark { 0x8d, "KEY_KP2" }, 22293ff13edSDerek J. Clark { 0x8e, "KEY_KP3" }, 22393ff13edSDerek J. Clark { 0x8f, "KEY_KP4" }, 22493ff13edSDerek J. Clark { 0x90, "KEY_KP5" }, 22593ff13edSDerek J. Clark { 0x91, "KEY_KP6" }, 22693ff13edSDerek J. Clark { 0x92, "KEY_KP7" }, 22793ff13edSDerek J. Clark { 0x93, "KEY_KP8" }, 22893ff13edSDerek J. Clark { 0x94, "KEY_KP9" }, 22993ff13edSDerek J. Clark { 0x95, "MD_PLAY" }, 23093ff13edSDerek J. Clark { 0x96, "MD_STOP" }, 23193ff13edSDerek J. Clark { 0x97, "MD_NEXT" }, 23293ff13edSDerek J. Clark { 0x98, "MD_PREV" }, 23393ff13edSDerek J. Clark { 0x99, "MD_VOL_UP" }, 23493ff13edSDerek J. Clark { 0x9a, "MD_VOL_DOWN" }, 23593ff13edSDerek J. Clark { 0x9b, "MD_VOL_MUTE" }, 23693ff13edSDerek J. Clark { 0x9c, "KEY_F23" }, 23793ff13edSDerek J. Clark /* Mouse events */ 23893ff13edSDerek J. Clark { 0xc8, "BTN_LEFT" }, 23993ff13edSDerek J. Clark { 0xc9, "BTN_MIDDLE" }, 24093ff13edSDerek J. Clark { 0xca, "BTN_RIGHT" }, 24193ff13edSDerek J. Clark { 0xcb, "BTN_SIDE" }, 24293ff13edSDerek J. Clark { 0xcc, "BTN_EXTRA" }, 24393ff13edSDerek J. Clark { 0xcd, "REL_WHEEL_UP" }, 24493ff13edSDerek J. Clark { 0xce, "REL_WHEEL_DOWN" }, 24593ff13edSDerek J. Clark { 0xff, "DISABLED" }, 24693ff13edSDerek J. Clark }; 24793ff13edSDerek J. Clark 248f27e2e3cSDerek J. Clark enum claw_rgb_effect_index { 249f27e2e3cSDerek J. Clark CLAW_RGB_EFFECT_MONOCOLOR, 250f27e2e3cSDerek J. Clark CLAW_RGB_EFFECT_BREATHE, 251f27e2e3cSDerek J. Clark CLAW_RGB_EFFECT_CHROMA, 252f27e2e3cSDerek J. Clark CLAW_RGB_EFFECT_RAINBOW, 253f27e2e3cSDerek J. Clark CLAW_RGB_EFFECT_FROSTFIRE, 254f27e2e3cSDerek J. Clark }; 255f27e2e3cSDerek J. Clark 256f27e2e3cSDerek J. Clark static const char * const claw_rgb_effect_text[] = { 257f27e2e3cSDerek J. Clark [CLAW_RGB_EFFECT_MONOCOLOR] = "monocolor", 258f27e2e3cSDerek J. Clark [CLAW_RGB_EFFECT_BREATHE] = "breathe", 259f27e2e3cSDerek J. Clark [CLAW_RGB_EFFECT_CHROMA] = "chroma", 260f27e2e3cSDerek J. Clark [CLAW_RGB_EFFECT_RAINBOW] = "rainbow", 261f27e2e3cSDerek J. Clark [CLAW_RGB_EFFECT_FROSTFIRE] = "frostfire", 262f27e2e3cSDerek J. Clark }; 263f27e2e3cSDerek J. Clark 26493ff13edSDerek J. Clark static const u16 button_mapping_addr_old[] = { 26593ff13edSDerek J. Clark 0x007a, /* M1 */ 26693ff13edSDerek J. Clark 0x011f, /* M2 */ 26793ff13edSDerek J. Clark }; 26893ff13edSDerek J. Clark 26993ff13edSDerek J. Clark static const u16 button_mapping_addr_new[] = { 27093ff13edSDerek J. Clark 0x00bb, /* M1 */ 27193ff13edSDerek J. Clark 0x0164, /* M2 */ 27293ff13edSDerek J. Clark }; 27393ff13edSDerek J. Clark 274f27e2e3cSDerek J. Clark static const u16 rgb_addr_old = 0x01fa; 275f27e2e3cSDerek J. Clark static const u16 rgb_addr_new = 0x024a; 276f27e2e3cSDerek J. Clark 277*95547c53SDerek J. Clark static const u16 rumble_addr[] = { 278*95547c53SDerek J. Clark 0x0022, /* left */ 279*95547c53SDerek J. Clark 0x0023, /* right */ 280*95547c53SDerek J. Clark }; 281*95547c53SDerek J. Clark 282a9517958SDerek J. Clark struct claw_command_report { 283a9517958SDerek J. Clark u8 report_id; 284a9517958SDerek J. Clark u8 padding[2]; 285a9517958SDerek J. Clark u8 header_tail; 286a9517958SDerek J. Clark u8 cmd; 287a9517958SDerek J. Clark u8 data[59]; 288a9517958SDerek J. Clark } __packed; 289a9517958SDerek J. Clark 29093ff13edSDerek J. Clark struct claw_profile_report { 29193ff13edSDerek J. Clark u8 profile; 29293ff13edSDerek J. Clark __be16 read_addr; 29393ff13edSDerek J. Clark } __packed; 29493ff13edSDerek J. Clark 29593ff13edSDerek J. Clark struct claw_mkey_report { 29693ff13edSDerek J. Clark struct claw_profile_report; 29793ff13edSDerek J. Clark u8 padding_0; 29893ff13edSDerek J. Clark u8 padding_1; 29993ff13edSDerek J. Clark u8 padding_2; 30093ff13edSDerek J. Clark u8 codes[5]; 30193ff13edSDerek J. Clark } __packed; 30293ff13edSDerek J. Clark 303f27e2e3cSDerek J. Clark struct rgb_zone { 304f27e2e3cSDerek J. Clark u8 red; 305f27e2e3cSDerek J. Clark u8 green; 306f27e2e3cSDerek J. Clark u8 blue; 307f27e2e3cSDerek J. Clark }; 308f27e2e3cSDerek J. Clark 309f27e2e3cSDerek J. Clark struct rgb_frame { 310f27e2e3cSDerek J. Clark struct rgb_zone zone[CLAW_RGB_ZONES]; 311f27e2e3cSDerek J. Clark }; 312f27e2e3cSDerek J. Clark 313f27e2e3cSDerek J. Clark struct claw_rgb_report { 314f27e2e3cSDerek J. Clark struct claw_profile_report; 315f27e2e3cSDerek J. Clark u8 frame_bytes; 316f27e2e3cSDerek J. Clark u8 padding; 317f27e2e3cSDerek J. Clark u8 frame_count; 318f27e2e3cSDerek J. Clark u8 state; /* Always 0x09 */ 319f27e2e3cSDerek J. Clark u8 speed; 320f27e2e3cSDerek J. Clark u8 brightness; 321f27e2e3cSDerek J. Clark struct rgb_frame zone_data; 322f27e2e3cSDerek J. Clark } __packed; 323f27e2e3cSDerek J. Clark 324*95547c53SDerek J. Clark struct claw_rumble_report { 325*95547c53SDerek J. Clark struct claw_profile_report; 326*95547c53SDerek J. Clark u8 padding; 327*95547c53SDerek J. Clark u8 intensity; 328*95547c53SDerek J. Clark } __packed; 329*95547c53SDerek J. Clark 330a9517958SDerek J. Clark struct claw_drvdata { 331a9517958SDerek J. Clark /* MCU General Variables */ 33293ff13edSDerek J. Clark enum claw_profile_ack_pending profile_pending; 333a9517958SDerek J. Clark struct completion orphan_ack_complete; 334a9517958SDerek J. Clark struct completion send_cmd_complete; 335a9517958SDerek J. Clark struct delayed_work cfg_resume; 336a9517958SDerek J. Clark struct delayed_work cfg_setup; 337a9517958SDerek J. Clark spinlock_t registration_lock; /* Lock for registration read/write */ 33893ff13edSDerek J. Clark struct mutex profile_mutex; /* mutex for profile_pending calls */ 33993ff13edSDerek J. Clark spinlock_t profile_lock; /* Lock for profile_pending read/write */ 340a9517958SDerek J. Clark struct hid_device *hdev; 341a9517958SDerek J. Clark bool orphan_ack_pending; 342a9517958SDerek J. Clark struct mutex cfg_mutex; /* mutex for synchronous data */ 34393ff13edSDerek J. Clark struct mutex rom_mutex; /* mutex for SYNC_TO_ROM calls */ 344a9517958SDerek J. Clark spinlock_t cmd_lock; /* Lock for cmd data read/write */ 345a9517958SDerek J. Clark u8 waiting_cmd; 346a9517958SDerek J. Clark int cmd_status; 34793ff13edSDerek J. Clark u16 bcd_device; 348a9517958SDerek J. Clark u8 ep; 349a9517958SDerek J. Clark 350a9517958SDerek J. Clark /* Gamepad Variables */ 351a9517958SDerek J. Clark enum claw_mkeys_function_index mkeys_function; 352a9517958SDerek J. Clark enum claw_gamepad_mode_index gamepad_mode; 35393ff13edSDerek J. Clark u8 m1_codes[CLAW_KEYS_MAX]; 35493ff13edSDerek J. Clark u8 m2_codes[CLAW_KEYS_MAX]; 355*95547c53SDerek J. Clark u8 rumble_intensity_right; 356*95547c53SDerek J. Clark u8 rumble_intensity_left; 357f27e2e3cSDerek J. Clark const u16 *bmap_addr; 358*95547c53SDerek J. Clark spinlock_t rumble_lock; /* lock for rumble_intensity read/write */ 359*95547c53SDerek J. Clark spinlock_t mode_lock; /* Lock for mode data read/write */ 360*95547c53SDerek J. Clark bool rumble_support; 361a9517958SDerek J. Clark bool gp_registered; 36293ff13edSDerek J. Clark bool bmap_support; 363f27e2e3cSDerek J. Clark 364f27e2e3cSDerek J. Clark /* RGB Variables */ 365f27e2e3cSDerek J. Clark struct rgb_frame rgb_frames[CLAW_RGB_MAX_FRAMES]; 366f27e2e3cSDerek J. Clark enum claw_rgb_effect_index rgb_effect; 367f27e2e3cSDerek J. Clark struct led_classdev_mc led_mc; 368f27e2e3cSDerek J. Clark struct delayed_work rgb_queue; 369f27e2e3cSDerek J. Clark spinlock_t frame_lock; /* lock for rgb_frames read/write */ 370f27e2e3cSDerek J. Clark bool rgb_registered; 371f27e2e3cSDerek J. Clark u8 rgb_frame_count; 372f27e2e3cSDerek J. Clark bool rgb_enabled; 373f27e2e3cSDerek J. Clark u8 rgb_speed; 374f27e2e3cSDerek J. Clark u16 rgb_addr; 375a9517958SDerek J. Clark }; 376a9517958SDerek J. Clark 377a9517958SDerek J. Clark static int get_endpoint_address(struct hid_device *hdev) 378a9517958SDerek J. Clark { 379a9517958SDerek J. Clark struct usb_host_endpoint *ep; 380a9517958SDerek J. Clark struct usb_interface *intf; 381a9517958SDerek J. Clark 382a9517958SDerek J. Clark intf = to_usb_interface(hdev->dev.parent); 383a9517958SDerek J. Clark ep = intf->cur_altsetting->endpoint; 384a9517958SDerek J. Clark if (ep) 385a9517958SDerek J. Clark return ep->desc.bEndpointAddress; 386a9517958SDerek J. Clark 387a9517958SDerek J. Clark return -ENODEV; 388a9517958SDerek J. Clark } 389a9517958SDerek J. Clark 390a9517958SDerek J. Clark static int claw_gamepad_mode_event(struct claw_drvdata *drvdata, 391a9517958SDerek J. Clark struct claw_command_report *cmd_rep) 392a9517958SDerek J. Clark { 393a9517958SDerek J. Clark if (cmd_rep->data[0] >= ARRAY_SIZE(claw_gamepad_mode_text) || 394a9517958SDerek J. Clark !claw_gamepad_mode_text[cmd_rep->data[0]] || 395a9517958SDerek J. Clark cmd_rep->data[1] >= ARRAY_SIZE(claw_mkeys_function_text)) 396a9517958SDerek J. Clark return -EINVAL; 397a9517958SDerek J. Clark 398a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->mode_lock) { 399a9517958SDerek J. Clark drvdata->gamepad_mode = cmd_rep->data[0]; 400a9517958SDerek J. Clark drvdata->mkeys_function = cmd_rep->data[1]; 401a9517958SDerek J. Clark } 402a9517958SDerek J. Clark 403a9517958SDerek J. Clark return 0; 404a9517958SDerek J. Clark } 405a9517958SDerek J. Clark 40693ff13edSDerek J. Clark static int claw_profile_event(struct claw_drvdata *drvdata, struct claw_command_report *cmd_rep) 40793ff13edSDerek J. Clark { 40893ff13edSDerek J. Clark enum claw_profile_ack_pending profile; 409*95547c53SDerek J. Clark struct claw_rumble_report *rumble; 41093ff13edSDerek J. Clark struct claw_mkey_report *mkeys; 411f27e2e3cSDerek J. Clark struct claw_rgb_report *frame; 412f27e2e3cSDerek J. Clark u16 rgb_addr, read_addr; 413f27e2e3cSDerek J. Clark u8 *codes, key, f_idx; 414f27e2e3cSDerek J. Clark u16 frame_calc; 41593ff13edSDerek J. Clark int i; 41693ff13edSDerek J. Clark 41793ff13edSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->profile_lock) 41893ff13edSDerek J. Clark profile = drvdata->profile_pending; 41993ff13edSDerek J. Clark 42093ff13edSDerek J. Clark switch (profile) { 42193ff13edSDerek J. Clark case CLAW_M1_PENDING: 42293ff13edSDerek J. Clark case CLAW_M2_PENDING: 42393ff13edSDerek J. Clark key = (profile == CLAW_M1_PENDING) ? CLAW_KEY_M1 : CLAW_KEY_M2; 42493ff13edSDerek J. Clark mkeys = (struct claw_mkey_report *)cmd_rep->data; 42593ff13edSDerek J. Clark if (be16_to_cpu(mkeys->read_addr) != drvdata->bmap_addr[key]) 42693ff13edSDerek J. Clark return -EAGAIN; 42793ff13edSDerek J. Clark codes = (profile == CLAW_M1_PENDING) ? drvdata->m1_codes : drvdata->m2_codes; 42893ff13edSDerek J. Clark for (i = 0; i < CLAW_KEYS_MAX; i++) 42993ff13edSDerek J. Clark codes[i] = (mkeys->codes[i]); 43093ff13edSDerek J. Clark break; 431f27e2e3cSDerek J. Clark case CLAW_RGB_PENDING: 432f27e2e3cSDerek J. Clark frame = (struct claw_rgb_report *)cmd_rep->data; 433f27e2e3cSDerek J. Clark rgb_addr = drvdata->rgb_addr; 434f27e2e3cSDerek J. Clark read_addr = be16_to_cpu(frame->read_addr); 435f27e2e3cSDerek J. Clark 436f27e2e3cSDerek J. Clark if (read_addr < drvdata->rgb_addr) 437f27e2e3cSDerek J. Clark return -EAGAIN; 438f27e2e3cSDerek J. Clark 439f27e2e3cSDerek J. Clark frame_calc = (read_addr - rgb_addr) / CLAW_RGB_FRAME_OFFSET; 440f27e2e3cSDerek J. Clark if (frame_calc >= CLAW_RGB_MAX_FRAMES) { 441f27e2e3cSDerek J. Clark dev_err(&drvdata->hdev->dev, "Got unsupported frame index: %x\n", 442f27e2e3cSDerek J. Clark frame_calc); 443f27e2e3cSDerek J. Clark return -EAGAIN; 444f27e2e3cSDerek J. Clark } 445f27e2e3cSDerek J. Clark f_idx = frame_calc; 446f27e2e3cSDerek J. Clark 447f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->frame_lock) { 448f27e2e3cSDerek J. Clark memcpy(&drvdata->rgb_frames[f_idx], &frame->zone_data, 449f27e2e3cSDerek J. Clark sizeof(struct rgb_frame)); 450f27e2e3cSDerek J. Clark 451f27e2e3cSDerek J. Clark /* Only use frame 0 for remaining variable assignment */ 452f27e2e3cSDerek J. Clark if (f_idx != 0) 453f27e2e3cSDerek J. Clark break; 454f27e2e3cSDerek J. Clark 455f27e2e3cSDerek J. Clark drvdata->rgb_speed = frame->speed; 456f27e2e3cSDerek J. Clark drvdata->led_mc.led_cdev.brightness = frame->brightness; 457f27e2e3cSDerek J. Clark drvdata->led_mc.subled_info[0].intensity = frame->zone_data.zone[0].red; 458f27e2e3cSDerek J. Clark drvdata->led_mc.subled_info[1].intensity = frame->zone_data.zone[0].green; 459f27e2e3cSDerek J. Clark drvdata->led_mc.subled_info[2].intensity = frame->zone_data.zone[0].blue; 460f27e2e3cSDerek J. Clark } 461f27e2e3cSDerek J. Clark 462f27e2e3cSDerek J. Clark break; 463*95547c53SDerek J. Clark case CLAW_RUMBLE_LEFT_PENDING: 464*95547c53SDerek J. Clark rumble = (struct claw_rumble_report *)cmd_rep->data; 465*95547c53SDerek J. Clark if (be16_to_cpu(rumble->read_addr) != rumble_addr[0]) 466*95547c53SDerek J. Clark return -EAGAIN; 467*95547c53SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->rumble_lock) 468*95547c53SDerek J. Clark drvdata->rumble_intensity_left = rumble->intensity; 469*95547c53SDerek J. Clark break; 470*95547c53SDerek J. Clark case CLAW_RUMBLE_RIGHT_PENDING: 471*95547c53SDerek J. Clark rumble = (struct claw_rumble_report *)cmd_rep->data; 472*95547c53SDerek J. Clark if (be16_to_cpu(rumble->read_addr) != rumble_addr[1]) 473*95547c53SDerek J. Clark return -EAGAIN; 474*95547c53SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->rumble_lock) 475*95547c53SDerek J. Clark drvdata->rumble_intensity_right = rumble->intensity; 476*95547c53SDerek J. Clark break; 47793ff13edSDerek J. Clark default: 47893ff13edSDerek J. Clark dev_dbg(&drvdata->hdev->dev, 47993ff13edSDerek J. Clark "Got profile event without changes pending from command: %x\n", 48093ff13edSDerek J. Clark cmd_rep->cmd); 48193ff13edSDerek J. Clark return -EINVAL; 48293ff13edSDerek J. Clark } 48393ff13edSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->profile_lock) 48493ff13edSDerek J. Clark drvdata->profile_pending = CLAW_NO_PENDING; 48593ff13edSDerek J. Clark 48693ff13edSDerek J. Clark return 0; 48793ff13edSDerek J. Clark } 48893ff13edSDerek J. Clark 489a9517958SDerek J. Clark static int claw_raw_event(struct claw_drvdata *drvdata, struct hid_report *report, 490a9517958SDerek J. Clark u8 *data, int size) 491a9517958SDerek J. Clark { 492a9517958SDerek J. Clark struct claw_command_report *cmd_rep; 493a9517958SDerek J. Clark int ret = 0; 494a9517958SDerek J. Clark 495a9517958SDerek J. Clark if (size != CLAW_PACKET_SIZE) 496a9517958SDerek J. Clark return 0; 497a9517958SDerek J. Clark 498a9517958SDerek J. Clark cmd_rep = (struct claw_command_report *)data; 499a9517958SDerek J. Clark 500a9517958SDerek J. Clark if (cmd_rep->report_id != CLAW_INPUT_REPORT_ID || cmd_rep->header_tail != 0x3c) 501a9517958SDerek J. Clark return 0; 502a9517958SDerek J. Clark 503a9517958SDerek J. Clark dev_dbg(&drvdata->hdev->dev, "Rx data as raw input report: [%*ph]\n", 504a9517958SDerek J. Clark CLAW_PACKET_SIZE, data); 505a9517958SDerek J. Clark 506a9517958SDerek J. Clark guard(spinlock_irqsave)(&drvdata->cmd_lock); 507a9517958SDerek J. Clark switch (cmd_rep->cmd) { 508a9517958SDerek J. Clark case CLAW_COMMAND_TYPE_GAMEPAD_MODE_ACK: 509a9517958SDerek J. Clark ret = claw_gamepad_mode_event(drvdata, cmd_rep); 510a9517958SDerek J. Clark if (drvdata->waiting_cmd == CLAW_COMMAND_TYPE_READ_GAMEPAD_MODE) { 511a9517958SDerek J. Clark drvdata->cmd_status = ret; 512a9517958SDerek J. Clark complete(&drvdata->send_cmd_complete); 513a9517958SDerek J. Clark } 514a9517958SDerek J. Clark 515a9517958SDerek J. Clark break; 51693ff13edSDerek J. Clark case CLAW_COMMAND_TYPE_READ_PROFILE_ACK: 51793ff13edSDerek J. Clark ret = claw_profile_event(drvdata, cmd_rep); 51893ff13edSDerek J. Clark /* Stale address received, ignore and keep waiting */ 51993ff13edSDerek J. Clark if (ret == -EAGAIN) 52093ff13edSDerek J. Clark return 0; 52193ff13edSDerek J. Clark if (drvdata->waiting_cmd == CLAW_COMMAND_TYPE_READ_PROFILE) { 52293ff13edSDerek J. Clark drvdata->cmd_status = ret; 52393ff13edSDerek J. Clark complete(&drvdata->send_cmd_complete); 52493ff13edSDerek J. Clark } 52593ff13edSDerek J. Clark 52693ff13edSDerek J. Clark break; 527a9517958SDerek J. Clark case CLAW_COMMAND_TYPE_ACK: 528a9517958SDerek J. Clark if (drvdata->orphan_ack_pending) { 529a9517958SDerek J. Clark drvdata->orphan_ack_pending = false; 530a9517958SDerek J. Clark complete(&drvdata->orphan_ack_complete); 531a9517958SDerek J. Clark break; 532a9517958SDerek J. Clark } 533a9517958SDerek J. Clark 534a9517958SDerek J. Clark if (drvdata->waiting_cmd == CLAW_COMMAND_TYPE_NONE) { 535a9517958SDerek J. Clark dev_warn(&drvdata->hdev->dev, "Got unexpected ACK from MCU, ignoring\n"); 536a9517958SDerek J. Clark break; 537a9517958SDerek J. Clark } 538a9517958SDerek J. Clark 539a9517958SDerek J. Clark drvdata->cmd_status = 0; 540a9517958SDerek J. Clark complete(&drvdata->send_cmd_complete); 541a9517958SDerek J. Clark 542a9517958SDerek J. Clark dev_dbg(&drvdata->hdev->dev, "Waiting CMD: %x\n", drvdata->waiting_cmd); 543a9517958SDerek J. Clark 544a9517958SDerek J. Clark break; 545a9517958SDerek J. Clark default: 546a9517958SDerek J. Clark dev_dbg(&drvdata->hdev->dev, "Unknown command: %x\n", cmd_rep->cmd); 547a9517958SDerek J. Clark return 0; 548a9517958SDerek J. Clark } 549a9517958SDerek J. Clark 550a9517958SDerek J. Clark return ret; 551a9517958SDerek J. Clark } 552a9517958SDerek J. Clark 553a9517958SDerek J. Clark static int msi_raw_event(struct hid_device *hdev, struct hid_report *report, 554a9517958SDerek J. Clark u8 *data, int size) 555a9517958SDerek J. Clark { 556a9517958SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 557a9517958SDerek J. Clark 558a9517958SDerek J. Clark if (!drvdata || (drvdata->ep != CLAW_XINPUT_CFG_INTF_IN && 559a9517958SDerek J. Clark drvdata->ep != CLAW_DINPUT_CFG_INTF_IN)) 560a9517958SDerek J. Clark return 0; 561a9517958SDerek J. Clark 562a9517958SDerek J. Clark return claw_raw_event(drvdata, report, data, size); 563a9517958SDerek J. Clark } 564a9517958SDerek J. Clark 565a9517958SDerek J. Clark /* Caller must hold drvdata->cfg_mutex. */ 566a9517958SDerek J. Clark static int __claw_hw_output_report(struct hid_device *hdev, u8 index, u8 *data, 567a9517958SDerek J. Clark size_t len, unsigned int timeout) 568a9517958SDerek J. Clark { 569a9517958SDerek J. Clark unsigned char *dmabuf __free(kfree) = NULL; 570a9517958SDerek J. Clark u8 header[] = { CLAW_OUTPUT_REPORT_ID, 0, 0, 0x3c, index }; 571a9517958SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 572a9517958SDerek J. Clark size_t header_size = ARRAY_SIZE(header); 573a9517958SDerek J. Clark bool orphaned; 574a9517958SDerek J. Clark int ret; 575a9517958SDerek J. Clark 576a9517958SDerek J. Clark lockdep_assert_held(&drvdata->cfg_mutex); 577a9517958SDerek J. Clark 578a9517958SDerek J. Clark /* If expecting an orphan ack, hold next event until MCU has time to clear it */ 579a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->cmd_lock) 580a9517958SDerek J. Clark orphaned = drvdata->orphan_ack_pending; 581a9517958SDerek J. Clark 582a9517958SDerek J. Clark if (orphaned) { 583a9517958SDerek J. Clark wait_for_completion_timeout(&drvdata->orphan_ack_complete, msecs_to_jiffies(25)); 584a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->cmd_lock) 585a9517958SDerek J. Clark drvdata->orphan_ack_pending = false; 586a9517958SDerek J. Clark } 587a9517958SDerek J. Clark 588a9517958SDerek J. Clark if (header_size + len > CLAW_PACKET_SIZE) 589a9517958SDerek J. Clark return -EINVAL; 590a9517958SDerek J. Clark 591a9517958SDerek J. Clark /* We can't use a devm_alloc reusable buffer without side effects during suspend */ 592a9517958SDerek J. Clark dmabuf = kzalloc(CLAW_PACKET_SIZE, GFP_KERNEL); 593a9517958SDerek J. Clark if (!dmabuf) 594a9517958SDerek J. Clark return -ENOMEM; 595a9517958SDerek J. Clark 596a9517958SDerek J. Clark memcpy(dmabuf, header, header_size); 597a9517958SDerek J. Clark if (data && len) 598a9517958SDerek J. Clark memcpy(dmabuf + header_size, data, len); 599a9517958SDerek J. Clark 600a9517958SDerek J. Clark reinit_completion(&drvdata->send_cmd_complete); 601a9517958SDerek J. Clark 602a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->cmd_lock) { 603a9517958SDerek J. Clark if (timeout) { 604a9517958SDerek J. Clark drvdata->waiting_cmd = index; 605a9517958SDerek J. Clark drvdata->cmd_status = -ETIMEDOUT; 606a9517958SDerek J. Clark } else { 607a9517958SDerek J. Clark reinit_completion(&drvdata->orphan_ack_complete); 608a9517958SDerek J. Clark drvdata->waiting_cmd = CLAW_COMMAND_TYPE_NONE; 609a9517958SDerek J. Clark drvdata->orphan_ack_pending = true; 610a9517958SDerek J. Clark } 611a9517958SDerek J. Clark } 612a9517958SDerek J. Clark 613a9517958SDerek J. Clark dev_dbg(&hdev->dev, "Send data as raw output report: [%*ph]\n", 614a9517958SDerek J. Clark CLAW_PACKET_SIZE, dmabuf); 615a9517958SDerek J. Clark 616a9517958SDerek J. Clark ret = hid_hw_output_report(hdev, dmabuf, CLAW_PACKET_SIZE); 617a9517958SDerek J. Clark if (ret < 0) 618a9517958SDerek J. Clark goto err; 619a9517958SDerek J. Clark 620a9517958SDerek J. Clark ret = ret == CLAW_PACKET_SIZE ? 0 : -EIO; 621a9517958SDerek J. Clark if (ret) 622a9517958SDerek J. Clark goto err; 623a9517958SDerek J. Clark 624a9517958SDerek J. Clark if (timeout) { 625a9517958SDerek J. Clark ret = wait_for_completion_interruptible_timeout(&drvdata->send_cmd_complete, 626a9517958SDerek J. Clark msecs_to_jiffies(timeout)); 627a9517958SDerek J. Clark 628a9517958SDerek J. Clark dev_dbg(&hdev->dev, "Remaining timeout: %u\n", ret); 629a9517958SDerek J. Clark ret = ret > 0 ? drvdata->cmd_status : ret ?: -EBUSY; 630a9517958SDerek J. Clark if (ret) 631a9517958SDerek J. Clark goto err; 632a9517958SDerek J. Clark } 633a9517958SDerek J. Clark 634a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->cmd_lock) 635a9517958SDerek J. Clark drvdata->waiting_cmd = CLAW_COMMAND_TYPE_NONE; 636a9517958SDerek J. Clark 637a9517958SDerek J. Clark return ret; 638a9517958SDerek J. Clark 639a9517958SDerek J. Clark err: 640a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->cmd_lock) { 641a9517958SDerek J. Clark drvdata->waiting_cmd = CLAW_COMMAND_TYPE_NONE; 642a9517958SDerek J. Clark drvdata->orphan_ack_pending = false; 643a9517958SDerek J. Clark } 644a9517958SDerek J. Clark return ret; 645a9517958SDerek J. Clark } 646a9517958SDerek J. Clark 647a9517958SDerek J. Clark static int claw_hw_output_report(struct hid_device *hdev, u8 index, u8 *data, 648a9517958SDerek J. Clark size_t len, unsigned int timeout) 649a9517958SDerek J. Clark { 650a9517958SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 651a9517958SDerek J. Clark 652a9517958SDerek J. Clark guard(mutex)(&drvdata->cfg_mutex); 653a9517958SDerek J. Clark return __claw_hw_output_report(hdev, index, data, len, timeout); 654a9517958SDerek J. Clark } 655a9517958SDerek J. Clark 656a9517958SDerek J. Clark static int claw_switch_mode(struct hid_device *hdev, enum claw_mode_field field, u8 val) 657a9517958SDerek J. Clark { 658a9517958SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 659a9517958SDerek J. Clark u8 data[2]; 660a9517958SDerek J. Clark 661a9517958SDerek J. Clark guard(mutex)(&drvdata->cfg_mutex); 662a9517958SDerek J. Clark 663a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->mode_lock) { 664a9517958SDerek J. Clark switch (field) { 665a9517958SDerek J. Clark case CLAW_FIELD_GAMEPAD_MODE: 666a9517958SDerek J. Clark data[0] = val; 667a9517958SDerek J. Clark data[1] = drvdata->mkeys_function; 668a9517958SDerek J. Clark break; 669a9517958SDerek J. Clark case CLAW_FIELD_MKEYS_FUNCTION: 670a9517958SDerek J. Clark data[0] = drvdata->gamepad_mode; 671a9517958SDerek J. Clark data[1] = val; 672a9517958SDerek J. Clark break; 673a9517958SDerek J. Clark } 674a9517958SDerek J. Clark } 675a9517958SDerek J. Clark 676a9517958SDerek J. Clark return __claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_SWITCH_MODE, data, 677a9517958SDerek J. Clark ARRAY_SIZE(data), 0); 678a9517958SDerek J. Clark } 679a9517958SDerek J. Clark 680a9517958SDerek J. Clark static ssize_t gamepad_mode_store(struct device *dev, struct device_attribute *attr, 681a9517958SDerek J. Clark const char *buf, size_t count) 682a9517958SDerek J. Clark { 683a9517958SDerek J. Clark struct hid_device *hdev = to_hid_device(dev); 684a9517958SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 685a9517958SDerek J. Clark int i, ret = -EINVAL; 686a9517958SDerek J. Clark 687a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 688a9517958SDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 689a9517958SDerek J. Clark if (!smp_load_acquire(&drvdata->gp_registered)) 690a9517958SDerek J. Clark return -ENODEV; 691a9517958SDerek J. Clark } 692a9517958SDerek J. Clark 693a9517958SDerek J. Clark for (i = 0; i < ARRAY_SIZE(claw_gamepad_mode_text); i++) { 694a9517958SDerek J. Clark if (claw_gamepad_mode_text[i] && sysfs_streq(buf, claw_gamepad_mode_text[i])) { 695a9517958SDerek J. Clark ret = i; 696a9517958SDerek J. Clark break; 697a9517958SDerek J. Clark } 698a9517958SDerek J. Clark } 699a9517958SDerek J. Clark if (ret < 0) 700a9517958SDerek J. Clark return ret; 701a9517958SDerek J. Clark 702a9517958SDerek J. Clark ret = claw_switch_mode(hdev, CLAW_FIELD_GAMEPAD_MODE, ret); 703a9517958SDerek J. Clark if (ret) 704a9517958SDerek J. Clark return ret; 705a9517958SDerek J. Clark 706a9517958SDerek J. Clark return count; 707a9517958SDerek J. Clark } 708a9517958SDerek J. Clark 709a9517958SDerek J. Clark static ssize_t gamepad_mode_show(struct device *dev, 710a9517958SDerek J. Clark struct device_attribute *attr, char *buf) 711a9517958SDerek J. Clark { 712a9517958SDerek J. Clark struct hid_device *hdev = to_hid_device(dev); 713a9517958SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 714a9517958SDerek J. Clark int ret, i; 715a9517958SDerek J. Clark 716a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 717a9517958SDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 718a9517958SDerek J. Clark if (!smp_load_acquire(&drvdata->gp_registered)) 719a9517958SDerek J. Clark return -ENODEV; 720a9517958SDerek J. Clark } 721a9517958SDerek J. Clark 722a9517958SDerek J. Clark ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_READ_GAMEPAD_MODE, NULL, 0, 25); 723a9517958SDerek J. Clark if (ret) 724a9517958SDerek J. Clark return ret; 725a9517958SDerek J. Clark 726a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->mode_lock) 727a9517958SDerek J. Clark i = drvdata->gamepad_mode; 728a9517958SDerek J. Clark 729a9517958SDerek J. Clark if (!claw_gamepad_mode_text[i] || claw_gamepad_mode_text[i][0] == '\0') 730a9517958SDerek J. Clark return sysfs_emit(buf, "unsupported\n"); 731a9517958SDerek J. Clark 732a9517958SDerek J. Clark return sysfs_emit(buf, "%s\n", claw_gamepad_mode_text[i]); 733a9517958SDerek J. Clark } 734a9517958SDerek J. Clark static DEVICE_ATTR_RW(gamepad_mode); 735a9517958SDerek J. Clark 736a9517958SDerek J. Clark static ssize_t gamepad_mode_index_show(struct device *dev, 737a9517958SDerek J. Clark struct device_attribute *attr, char *buf) 738a9517958SDerek J. Clark { 739a9517958SDerek J. Clark ssize_t count = 0; 740a9517958SDerek J. Clark int i; 741a9517958SDerek J. Clark 742a9517958SDerek J. Clark for (i = 0; i < ARRAY_SIZE(claw_gamepad_mode_text); i++) { 743a9517958SDerek J. Clark if (!claw_gamepad_mode_text[i] || claw_gamepad_mode_text[i][0] == '\0') 744a9517958SDerek J. Clark continue; 745a9517958SDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", claw_gamepad_mode_text[i]); 746a9517958SDerek J. Clark } 747a9517958SDerek J. Clark 748a9517958SDerek J. Clark if (count) 749a9517958SDerek J. Clark buf[count - 1] = '\n'; 750a9517958SDerek J. Clark 751a9517958SDerek J. Clark return count; 752a9517958SDerek J. Clark } 753a9517958SDerek J. Clark static DEVICE_ATTR_RO(gamepad_mode_index); 754a9517958SDerek J. Clark 755a9517958SDerek J. Clark static ssize_t mkeys_function_store(struct device *dev, struct device_attribute *attr, 756a9517958SDerek J. Clark const char *buf, size_t count) 757a9517958SDerek J. Clark { 758a9517958SDerek J. Clark struct hid_device *hdev = to_hid_device(dev); 759a9517958SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 760a9517958SDerek J. Clark int i, ret = -EINVAL; 761a9517958SDerek J. Clark 762a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 763a9517958SDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 764a9517958SDerek J. Clark if (!smp_load_acquire(&drvdata->gp_registered)) 765a9517958SDerek J. Clark return -ENODEV; 766a9517958SDerek J. Clark } 767a9517958SDerek J. Clark 768a9517958SDerek J. Clark for (i = 0; i < ARRAY_SIZE(claw_mkeys_function_text); i++) { 769a9517958SDerek J. Clark if (claw_mkeys_function_text[i] && sysfs_streq(buf, claw_mkeys_function_text[i])) { 770a9517958SDerek J. Clark ret = i; 771a9517958SDerek J. Clark break; 772a9517958SDerek J. Clark } 773a9517958SDerek J. Clark } 774a9517958SDerek J. Clark if (ret < 0) 775a9517958SDerek J. Clark return ret; 776a9517958SDerek J. Clark 777a9517958SDerek J. Clark ret = claw_switch_mode(hdev, CLAW_FIELD_MKEYS_FUNCTION, ret); 778a9517958SDerek J. Clark if (ret) 779a9517958SDerek J. Clark return ret; 780a9517958SDerek J. Clark 781a9517958SDerek J. Clark return count; 782a9517958SDerek J. Clark } 783a9517958SDerek J. Clark 784a9517958SDerek J. Clark static ssize_t mkeys_function_show(struct device *dev, struct device_attribute *attr, 785a9517958SDerek J. Clark char *buf) 786a9517958SDerek J. Clark { 787a9517958SDerek J. Clark struct hid_device *hdev = to_hid_device(dev); 788a9517958SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 789a9517958SDerek J. Clark int ret, i; 790a9517958SDerek J. Clark 791a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 792a9517958SDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 793a9517958SDerek J. Clark if (!smp_load_acquire(&drvdata->gp_registered)) 794a9517958SDerek J. Clark return -ENODEV; 795a9517958SDerek J. Clark } 796a9517958SDerek J. Clark 797a9517958SDerek J. Clark ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_READ_GAMEPAD_MODE, NULL, 0, 25); 798a9517958SDerek J. Clark if (ret) 799a9517958SDerek J. Clark return ret; 800a9517958SDerek J. Clark 801a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->mode_lock) 802a9517958SDerek J. Clark i = drvdata->mkeys_function; 803a9517958SDerek J. Clark 804a9517958SDerek J. Clark if (i >= ARRAY_SIZE(claw_mkeys_function_text)) 805a9517958SDerek J. Clark return sysfs_emit(buf, "unsupported\n"); 806a9517958SDerek J. Clark 807a9517958SDerek J. Clark return sysfs_emit(buf, "%s\n", claw_mkeys_function_text[i]); 808a9517958SDerek J. Clark } 809a9517958SDerek J. Clark static DEVICE_ATTR_RW(mkeys_function); 810a9517958SDerek J. Clark 811a9517958SDerek J. Clark static ssize_t mkeys_function_index_show(struct device *dev, 812a9517958SDerek J. Clark struct device_attribute *attr, char *buf) 813a9517958SDerek J. Clark { 814a9517958SDerek J. Clark int i, count = 0; 815a9517958SDerek J. Clark 816a9517958SDerek J. Clark for (i = 0; i < ARRAY_SIZE(claw_mkeys_function_text); i++) 817a9517958SDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", claw_mkeys_function_text[i]); 818a9517958SDerek J. Clark 819a9517958SDerek J. Clark if (count) 820a9517958SDerek J. Clark buf[count - 1] = '\n'; 821a9517958SDerek J. Clark 822a9517958SDerek J. Clark return count; 823a9517958SDerek J. Clark } 824a9517958SDerek J. Clark static DEVICE_ATTR_RO(mkeys_function_index); 825a9517958SDerek J. Clark 826a9517958SDerek J. Clark static ssize_t reset_store(struct device *dev, struct device_attribute *attr, 827a9517958SDerek J. Clark const char *buf, size_t count) 828a9517958SDerek J. Clark { 829a9517958SDerek J. Clark struct hid_device *hdev = to_hid_device(dev); 830a9517958SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 831a9517958SDerek J. Clark bool val; 832a9517958SDerek J. Clark int ret; 833a9517958SDerek J. Clark 834a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 835a9517958SDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 836a9517958SDerek J. Clark if (!smp_load_acquire(&drvdata->gp_registered)) 837a9517958SDerek J. Clark return -ENODEV; 838a9517958SDerek J. Clark } 839a9517958SDerek J. Clark 840a9517958SDerek J. Clark ret = kstrtobool(buf, &val); 841a9517958SDerek J. Clark if (ret) 842a9517958SDerek J. Clark return ret; 843a9517958SDerek J. Clark 844a9517958SDerek J. Clark if (!val) 845a9517958SDerek J. Clark return -EINVAL; 846a9517958SDerek J. Clark 847a9517958SDerek J. Clark ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_RESET_DEVICE, NULL, 0, 0); 848a9517958SDerek J. Clark if (ret) 849a9517958SDerek J. Clark return ret; 850a9517958SDerek J. Clark 851a9517958SDerek J. Clark return count; 852a9517958SDerek J. Clark } 853a9517958SDerek J. Clark static DEVICE_ATTR_WO(reset); 854a9517958SDerek J. Clark 85593ff13edSDerek J. Clark static int mkey_mapping_name_to_code(const char *name) 85693ff13edSDerek J. Clark { 85793ff13edSDerek J. Clark int i; 85893ff13edSDerek J. Clark 85993ff13edSDerek J. Clark for (i = 0; i < ARRAY_SIZE(claw_button_mapping_key_map); i++) { 86093ff13edSDerek J. Clark if (!strcmp(name, claw_button_mapping_key_map[i].name)) 86193ff13edSDerek J. Clark return claw_button_mapping_key_map[i].code; 86293ff13edSDerek J. Clark } 86393ff13edSDerek J. Clark 86493ff13edSDerek J. Clark return -EINVAL; 86593ff13edSDerek J. Clark } 86693ff13edSDerek J. Clark 86793ff13edSDerek J. Clark static const char *mkey_mapping_code_to_name(u8 code) 86893ff13edSDerek J. Clark { 86993ff13edSDerek J. Clark int i; 87093ff13edSDerek J. Clark 87193ff13edSDerek J. Clark if (code == 0xff) 87293ff13edSDerek J. Clark return NULL; 87393ff13edSDerek J. Clark 87493ff13edSDerek J. Clark for (i = 0; i < ARRAY_SIZE(claw_button_mapping_key_map); i++) { 87593ff13edSDerek J. Clark if (claw_button_mapping_key_map[i].code == code) 87693ff13edSDerek J. Clark return claw_button_mapping_key_map[i].name; 87793ff13edSDerek J. Clark } 87893ff13edSDerek J. Clark 87993ff13edSDerek J. Clark return NULL; 88093ff13edSDerek J. Clark } 88193ff13edSDerek J. Clark 88293ff13edSDerek J. Clark static int claw_mkey_store(struct device *dev, const char *buf, u8 mkey) 88393ff13edSDerek J. Clark { 88493ff13edSDerek J. Clark struct hid_device *hdev = to_hid_device(dev); 88593ff13edSDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 88693ff13edSDerek J. Clark struct claw_mkey_report report = { {0x01, cpu_to_be16(drvdata->bmap_addr[mkey])}, 88793ff13edSDerek J. Clark 0x07, 0x04, 0x00, {0xff, 0xff, 0xff, 0xff, 0xff} }; 88893ff13edSDerek J. Clark char **raw_keys __free(argv_free) = NULL; 88993ff13edSDerek J. Clark int ret, key_count, i; 89093ff13edSDerek J. Clark 89193ff13edSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 89293ff13edSDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 89393ff13edSDerek J. Clark if (!smp_load_acquire(&drvdata->gp_registered)) 89493ff13edSDerek J. Clark return -ENODEV; 89593ff13edSDerek J. Clark } 89693ff13edSDerek J. Clark 89793ff13edSDerek J. Clark raw_keys = argv_split(GFP_KERNEL, buf, &key_count); 89893ff13edSDerek J. Clark if (!raw_keys) 89993ff13edSDerek J. Clark return -ENOMEM; 90093ff13edSDerek J. Clark 90193ff13edSDerek J. Clark if (key_count > CLAW_KEYS_MAX) 90293ff13edSDerek J. Clark return -EINVAL; 90393ff13edSDerek J. Clark 90493ff13edSDerek J. Clark if (key_count == 0) 90593ff13edSDerek J. Clark goto set_buttons; 90693ff13edSDerek J. Clark 90793ff13edSDerek J. Clark for (i = 0; i < key_count; i++) { 90893ff13edSDerek J. Clark ret = mkey_mapping_name_to_code(raw_keys[i]); 90993ff13edSDerek J. Clark if (ret < 0) 91093ff13edSDerek J. Clark return ret; 91193ff13edSDerek J. Clark 91293ff13edSDerek J. Clark report.codes[i] = ret; 91393ff13edSDerek J. Clark } 91493ff13edSDerek J. Clark 91593ff13edSDerek J. Clark set_buttons: 91693ff13edSDerek J. Clark scoped_guard(mutex, &drvdata->rom_mutex) { 91793ff13edSDerek J. Clark ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_WRITE_PROFILE_DATA, 91893ff13edSDerek J. Clark (u8 *)&report, sizeof(report), 25); 91993ff13edSDerek J. Clark if (ret) 92093ff13edSDerek J. Clark return ret; 92193ff13edSDerek J. Clark /* MCU will not send ACK until the USB transaction completes. ACK is sent 92293ff13edSDerek J. Clark * immediately after and will hit the stale state machine, before the next 92393ff13edSDerek J. Clark * command re-arms the state machine. Timeout 0 ensures no deadlock waiting 92493ff13edSDerek J. Clark * for ACK that ill never come. 92593ff13edSDerek J. Clark */ 92693ff13edSDerek J. Clark ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_SYNC_TO_ROM, NULL, 0, 0); 92793ff13edSDerek J. Clark } 92893ff13edSDerek J. Clark 92993ff13edSDerek J. Clark return ret; 93093ff13edSDerek J. Clark } 93193ff13edSDerek J. Clark 93293ff13edSDerek J. Clark static int claw_mkey_show(struct device *dev, char *buf, enum claw_key_index m_key) 93393ff13edSDerek J. Clark { 93493ff13edSDerek J. Clark struct hid_device *hdev = to_hid_device(dev); 93593ff13edSDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 93693ff13edSDerek J. Clark struct claw_mkey_report report = { {0x01, cpu_to_be16(drvdata->bmap_addr[m_key])}, 0x07 }; 93793ff13edSDerek J. Clark int i, ret, count = 0; 93893ff13edSDerek J. Clark const char *name; 93993ff13edSDerek J. Clark u8 *codes; 94093ff13edSDerek J. Clark 94193ff13edSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 94293ff13edSDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 94393ff13edSDerek J. Clark if (!smp_load_acquire(&drvdata->gp_registered)) 94493ff13edSDerek J. Clark return -ENODEV; 94593ff13edSDerek J. Clark } 94693ff13edSDerek J. Clark 94793ff13edSDerek J. Clark codes = (m_key == CLAW_KEY_M1) ? drvdata->m1_codes : drvdata->m2_codes; 94893ff13edSDerek J. Clark 94993ff13edSDerek J. Clark guard(mutex)(&drvdata->profile_mutex); 95093ff13edSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->profile_lock) 95193ff13edSDerek J. Clark drvdata->profile_pending = (m_key == CLAW_KEY_M1) ? CLAW_M1_PENDING 95293ff13edSDerek J. Clark : CLAW_M2_PENDING; 95393ff13edSDerek J. Clark 95493ff13edSDerek J. Clark ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_READ_PROFILE, 95593ff13edSDerek J. Clark (u8 *)&report, sizeof(report), 25); 95693ff13edSDerek J. Clark if (ret) 95793ff13edSDerek J. Clark return ret; 95893ff13edSDerek J. Clark 95993ff13edSDerek J. Clark for (i = 0; i < CLAW_KEYS_MAX; i++) { 96093ff13edSDerek J. Clark name = mkey_mapping_code_to_name(codes[i]); 96193ff13edSDerek J. Clark if (name) 96293ff13edSDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", name); 96393ff13edSDerek J. Clark } 96493ff13edSDerek J. Clark 96593ff13edSDerek J. Clark if (!count) 96693ff13edSDerek J. Clark return sysfs_emit(buf, "(not set)\n"); 96793ff13edSDerek J. Clark 96893ff13edSDerek J. Clark buf[count - 1] = '\n'; 96993ff13edSDerek J. Clark 97093ff13edSDerek J. Clark return count; 97193ff13edSDerek J. Clark } 97293ff13edSDerek J. Clark 97393ff13edSDerek J. Clark static ssize_t button_m1_store(struct device *dev, struct device_attribute *attr, 97493ff13edSDerek J. Clark const char *buf, size_t count) 97593ff13edSDerek J. Clark { 97693ff13edSDerek J. Clark int ret; 97793ff13edSDerek J. Clark 97893ff13edSDerek J. Clark ret = claw_mkey_store(dev, buf, CLAW_KEY_M1); 97993ff13edSDerek J. Clark if (ret) 98093ff13edSDerek J. Clark return ret; 98193ff13edSDerek J. Clark 98293ff13edSDerek J. Clark return count; 98393ff13edSDerek J. Clark } 98493ff13edSDerek J. Clark 98593ff13edSDerek J. Clark static ssize_t button_m1_show(struct device *dev, struct device_attribute *attr, 98693ff13edSDerek J. Clark char *buf) 98793ff13edSDerek J. Clark { 98893ff13edSDerek J. Clark return claw_mkey_show(dev, buf, CLAW_KEY_M1); 98993ff13edSDerek J. Clark } 99093ff13edSDerek J. Clark static DEVICE_ATTR_RW(button_m1); 99193ff13edSDerek J. Clark 99293ff13edSDerek J. Clark static ssize_t button_m2_store(struct device *dev, struct device_attribute *attr, 99393ff13edSDerek J. Clark const char *buf, size_t count) 99493ff13edSDerek J. Clark { 99593ff13edSDerek J. Clark int ret; 99693ff13edSDerek J. Clark 99793ff13edSDerek J. Clark ret = claw_mkey_store(dev, buf, CLAW_KEY_M2); 99893ff13edSDerek J. Clark if (ret) 99993ff13edSDerek J. Clark return ret; 100093ff13edSDerek J. Clark 100193ff13edSDerek J. Clark return count; 100293ff13edSDerek J. Clark } 100393ff13edSDerek J. Clark 100493ff13edSDerek J. Clark static ssize_t button_m2_show(struct device *dev, struct device_attribute *attr, 100593ff13edSDerek J. Clark char *buf) 100693ff13edSDerek J. Clark { 100793ff13edSDerek J. Clark return claw_mkey_show(dev, buf, CLAW_KEY_M2); 100893ff13edSDerek J. Clark } 100993ff13edSDerek J. Clark static DEVICE_ATTR_RW(button_m2); 101093ff13edSDerek J. Clark 101193ff13edSDerek J. Clark static ssize_t button_mapping_options_show(struct device *dev, 101293ff13edSDerek J. Clark struct device_attribute *attr, char *buf) 101393ff13edSDerek J. Clark { 101493ff13edSDerek J. Clark int i, count = 0; 101593ff13edSDerek J. Clark 101693ff13edSDerek J. Clark for (i = 0; i < ARRAY_SIZE(claw_button_mapping_key_map); i++) 101793ff13edSDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", claw_button_mapping_key_map[i].name); 101893ff13edSDerek J. Clark 101993ff13edSDerek J. Clark if (count) 102093ff13edSDerek J. Clark buf[count - 1] = '\n'; 102193ff13edSDerek J. Clark 102293ff13edSDerek J. Clark return count; 102393ff13edSDerek J. Clark } 102493ff13edSDerek J. Clark static DEVICE_ATTR_RO(button_mapping_options); 102593ff13edSDerek J. Clark 1026*95547c53SDerek J. Clark static ssize_t rumble_intensity_left_store(struct device *dev, 1027*95547c53SDerek J. Clark struct device_attribute *attr, 1028*95547c53SDerek J. Clark const char *buf, size_t count) 1029*95547c53SDerek J. Clark { 1030*95547c53SDerek J. Clark struct claw_rumble_report report = { {0x01, cpu_to_be16(rumble_addr[0])}, 0x01 }; 1031*95547c53SDerek J. Clark struct hid_device *hdev = to_hid_device(dev); 1032*95547c53SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 1033*95547c53SDerek J. Clark u8 val; 1034*95547c53SDerek J. Clark int ret; 1035*95547c53SDerek J. Clark 1036*95547c53SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1037*95547c53SDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1038*95547c53SDerek J. Clark if (!smp_load_acquire(&drvdata->gp_registered)) 1039*95547c53SDerek J. Clark return -ENODEV; 1040*95547c53SDerek J. Clark } 1041*95547c53SDerek J. Clark 1042*95547c53SDerek J. Clark ret = kstrtou8(buf, 10, &val); 1043*95547c53SDerek J. Clark if (ret) 1044*95547c53SDerek J. Clark return ret; 1045*95547c53SDerek J. Clark 1046*95547c53SDerek J. Clark if (val > 100) 1047*95547c53SDerek J. Clark return -EINVAL; 1048*95547c53SDerek J. Clark 1049*95547c53SDerek J. Clark report.intensity = val; 1050*95547c53SDerek J. Clark 1051*95547c53SDerek J. Clark guard(mutex)(&drvdata->rom_mutex); 1052*95547c53SDerek J. Clark ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_WRITE_PROFILE_DATA, 1053*95547c53SDerek J. Clark (u8 *)&report, sizeof(report), 25); 1054*95547c53SDerek J. Clark if (ret) 1055*95547c53SDerek J. Clark return ret; 1056*95547c53SDerek J. Clark 1057*95547c53SDerek J. Clark /* MCU will not send ACK until the USB transaction completes. ACK is sent 1058*95547c53SDerek J. Clark * immediately after and will hit the stale state machine, before the next 1059*95547c53SDerek J. Clark * command re-arms the state machine. Timeout 0 ensures no deadlock waiting 1060*95547c53SDerek J. Clark * for ACK that ill never come. 1061*95547c53SDerek J. Clark */ 1062*95547c53SDerek J. Clark ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_SYNC_TO_ROM, NULL, 0, 0); 1063*95547c53SDerek J. Clark if (ret) 1064*95547c53SDerek J. Clark return ret; 1065*95547c53SDerek J. Clark 1066*95547c53SDerek J. Clark return count; 1067*95547c53SDerek J. Clark } 1068*95547c53SDerek J. Clark 1069*95547c53SDerek J. Clark static ssize_t rumble_intensity_left_show(struct device *dev, 1070*95547c53SDerek J. Clark struct device_attribute *attr, 1071*95547c53SDerek J. Clark char *buf) 1072*95547c53SDerek J. Clark { 1073*95547c53SDerek J. Clark struct claw_rumble_report report = { {0x01, cpu_to_be16(rumble_addr[0])}, 0x01 }; 1074*95547c53SDerek J. Clark struct hid_device *hdev = to_hid_device(dev); 1075*95547c53SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 1076*95547c53SDerek J. Clark int ret; 1077*95547c53SDerek J. Clark u8 val; 1078*95547c53SDerek J. Clark 1079*95547c53SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1080*95547c53SDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1081*95547c53SDerek J. Clark if (!smp_load_acquire(&drvdata->gp_registered)) 1082*95547c53SDerek J. Clark return -ENODEV; 1083*95547c53SDerek J. Clark } 1084*95547c53SDerek J. Clark 1085*95547c53SDerek J. Clark guard(mutex)(&drvdata->profile_mutex); 1086*95547c53SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->profile_lock) 1087*95547c53SDerek J. Clark drvdata->profile_pending = CLAW_RUMBLE_LEFT_PENDING; 1088*95547c53SDerek J. Clark ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_READ_PROFILE, 1089*95547c53SDerek J. Clark (u8 *)&report, sizeof(report), 25); 1090*95547c53SDerek J. Clark if (ret) 1091*95547c53SDerek J. Clark return ret; 1092*95547c53SDerek J. Clark 1093*95547c53SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->rumble_lock) 1094*95547c53SDerek J. Clark val = drvdata->rumble_intensity_left; 1095*95547c53SDerek J. Clark 1096*95547c53SDerek J. Clark return sysfs_emit(buf, "%u\n", val); 1097*95547c53SDerek J. Clark } 1098*95547c53SDerek J. Clark static DEVICE_ATTR_RW(rumble_intensity_left); 1099*95547c53SDerek J. Clark 1100*95547c53SDerek J. Clark static ssize_t rumble_intensity_right_store(struct device *dev, 1101*95547c53SDerek J. Clark struct device_attribute *attr, 1102*95547c53SDerek J. Clark const char *buf, size_t count) 1103*95547c53SDerek J. Clark { 1104*95547c53SDerek J. Clark struct claw_rumble_report report = { {0x01, cpu_to_be16(rumble_addr[1])}, 0x01 }; 1105*95547c53SDerek J. Clark struct hid_device *hdev = to_hid_device(dev); 1106*95547c53SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 1107*95547c53SDerek J. Clark u8 val; 1108*95547c53SDerek J. Clark int ret; 1109*95547c53SDerek J. Clark 1110*95547c53SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1111*95547c53SDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1112*95547c53SDerek J. Clark if (!smp_load_acquire(&drvdata->gp_registered)) 1113*95547c53SDerek J. Clark return -ENODEV; 1114*95547c53SDerek J. Clark } 1115*95547c53SDerek J. Clark 1116*95547c53SDerek J. Clark ret = kstrtou8(buf, 10, &val); 1117*95547c53SDerek J. Clark if (ret) 1118*95547c53SDerek J. Clark return ret; 1119*95547c53SDerek J. Clark 1120*95547c53SDerek J. Clark if (val > 100) 1121*95547c53SDerek J. Clark return -EINVAL; 1122*95547c53SDerek J. Clark 1123*95547c53SDerek J. Clark report.intensity = val; 1124*95547c53SDerek J. Clark 1125*95547c53SDerek J. Clark guard(mutex)(&drvdata->rom_mutex); 1126*95547c53SDerek J. Clark ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_WRITE_PROFILE_DATA, 1127*95547c53SDerek J. Clark (u8 *)&report, sizeof(report), 25); 1128*95547c53SDerek J. Clark if (ret) 1129*95547c53SDerek J. Clark return ret; 1130*95547c53SDerek J. Clark 1131*95547c53SDerek J. Clark /* MCU will not send ACK until the USB transaction completes. ACK is sent 1132*95547c53SDerek J. Clark * immediately after and will hit the stale state machine, before the next 1133*95547c53SDerek J. Clark * command re-arms the state machine. Timeout 0 ensures no deadlock waiting 1134*95547c53SDerek J. Clark * for ACK that ill never come. 1135*95547c53SDerek J. Clark */ 1136*95547c53SDerek J. Clark ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_SYNC_TO_ROM, NULL, 0, 0); 1137*95547c53SDerek J. Clark if (ret) 1138*95547c53SDerek J. Clark return ret; 1139*95547c53SDerek J. Clark 1140*95547c53SDerek J. Clark return count; 1141*95547c53SDerek J. Clark } 1142*95547c53SDerek J. Clark 1143*95547c53SDerek J. Clark static ssize_t rumble_intensity_right_show(struct device *dev, 1144*95547c53SDerek J. Clark struct device_attribute *attr, 1145*95547c53SDerek J. Clark char *buf) 1146*95547c53SDerek J. Clark { 1147*95547c53SDerek J. Clark struct claw_rumble_report report = { {0x01, cpu_to_be16(rumble_addr[1])}, 0x01 }; 1148*95547c53SDerek J. Clark struct hid_device *hdev = to_hid_device(dev); 1149*95547c53SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 1150*95547c53SDerek J. Clark int ret; 1151*95547c53SDerek J. Clark u8 val; 1152*95547c53SDerek J. Clark 1153*95547c53SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1154*95547c53SDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1155*95547c53SDerek J. Clark if (!smp_load_acquire(&drvdata->gp_registered)) 1156*95547c53SDerek J. Clark return -ENODEV; 1157*95547c53SDerek J. Clark } 1158*95547c53SDerek J. Clark 1159*95547c53SDerek J. Clark guard(mutex)(&drvdata->profile_mutex); 1160*95547c53SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->profile_lock) 1161*95547c53SDerek J. Clark drvdata->profile_pending = CLAW_RUMBLE_RIGHT_PENDING; 1162*95547c53SDerek J. Clark ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_READ_PROFILE, 1163*95547c53SDerek J. Clark (u8 *)&report, sizeof(report), 25); 1164*95547c53SDerek J. Clark if (ret) 1165*95547c53SDerek J. Clark return ret; 1166*95547c53SDerek J. Clark 1167*95547c53SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->rumble_lock) 1168*95547c53SDerek J. Clark val = drvdata->rumble_intensity_right; 1169*95547c53SDerek J. Clark 1170*95547c53SDerek J. Clark return sysfs_emit(buf, "%u\n", val); 1171*95547c53SDerek J. Clark } 1172*95547c53SDerek J. Clark static DEVICE_ATTR_RW(rumble_intensity_right); 1173*95547c53SDerek J. Clark 1174*95547c53SDerek J. Clark static ssize_t rumble_intensity_range_show(struct device *dev, 1175*95547c53SDerek J. Clark struct device_attribute *attr, 1176*95547c53SDerek J. Clark char *buf) 1177*95547c53SDerek J. Clark { 1178*95547c53SDerek J. Clark return sysfs_emit(buf, "0-100\n"); 1179*95547c53SDerek J. Clark } 1180*95547c53SDerek J. Clark static DEVICE_ATTR_RO(rumble_intensity_range); 1181*95547c53SDerek J. Clark 1182a9517958SDerek J. Clark static umode_t claw_gamepad_attr_is_visible(struct kobject *kobj, struct attribute *attr, 1183a9517958SDerek J. Clark int n) 1184a9517958SDerek J. Clark { 1185a9517958SDerek J. Clark struct hid_device *hdev = to_hid_device(kobj_to_dev(kobj)); 1186a9517958SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 1187a9517958SDerek J. Clark 1188a9517958SDerek J. Clark if (!drvdata) { 1189a9517958SDerek J. Clark dev_warn(&hdev->dev, 1190a9517958SDerek J. Clark "Failed to get drvdata from kobj. Gamepad attributes are not available.\n"); 1191a9517958SDerek J. Clark return 0; 1192a9517958SDerek J. Clark } 1193a9517958SDerek J. Clark 119493ff13edSDerek J. Clark /* Always show attrs available on all firmware */ 119593ff13edSDerek J. Clark if (attr == &dev_attr_gamepad_mode.attr || 119693ff13edSDerek J. Clark attr == &dev_attr_gamepad_mode_index.attr || 119793ff13edSDerek J. Clark attr == &dev_attr_mkeys_function.attr || 119893ff13edSDerek J. Clark attr == &dev_attr_mkeys_function_index.attr || 119993ff13edSDerek J. Clark attr == &dev_attr_reset.attr) 1200a9517958SDerek J. Clark return attr->mode; 120193ff13edSDerek J. Clark 1202*95547c53SDerek J. Clark /* Hide rumble attrs if not supported */ 1203*95547c53SDerek J. Clark if (attr == &dev_attr_rumble_intensity_left.attr || 1204*95547c53SDerek J. Clark attr == &dev_attr_rumble_intensity_right.attr || 1205*95547c53SDerek J. Clark attr == &dev_attr_rumble_intensity_range.attr) 1206*95547c53SDerek J. Clark return drvdata->rumble_support ? attr->mode : 0; 1207*95547c53SDerek J. Clark 120893ff13edSDerek J. Clark /* Hide button mapping attrs if it isn't supported */ 120993ff13edSDerek J. Clark return drvdata->bmap_support ? attr->mode : 0; 1210a9517958SDerek J. Clark } 1211a9517958SDerek J. Clark 1212a9517958SDerek J. Clark static struct attribute *claw_gamepad_attrs[] = { 121393ff13edSDerek J. Clark &dev_attr_button_m1.attr, 121493ff13edSDerek J. Clark &dev_attr_button_m2.attr, 121593ff13edSDerek J. Clark &dev_attr_button_mapping_options.attr, 1216a9517958SDerek J. Clark &dev_attr_gamepad_mode.attr, 1217a9517958SDerek J. Clark &dev_attr_gamepad_mode_index.attr, 1218a9517958SDerek J. Clark &dev_attr_mkeys_function.attr, 1219a9517958SDerek J. Clark &dev_attr_mkeys_function_index.attr, 1220a9517958SDerek J. Clark &dev_attr_reset.attr, 1221*95547c53SDerek J. Clark &dev_attr_rumble_intensity_left.attr, 1222*95547c53SDerek J. Clark &dev_attr_rumble_intensity_right.attr, 1223*95547c53SDerek J. Clark &dev_attr_rumble_intensity_range.attr, 1224a9517958SDerek J. Clark NULL, 1225a9517958SDerek J. Clark }; 1226a9517958SDerek J. Clark 1227a9517958SDerek J. Clark static const struct attribute_group claw_gamepad_attr_group = { 1228a9517958SDerek J. Clark .attrs = claw_gamepad_attrs, 1229a9517958SDerek J. Clark .is_visible = claw_gamepad_attr_is_visible, 1230a9517958SDerek J. Clark }; 1231a9517958SDerek J. Clark 1232f27e2e3cSDerek J. Clark /* Read RGB config from device */ 1233f27e2e3cSDerek J. Clark static int claw_read_rgb_config(struct hid_device *hdev) 1234f27e2e3cSDerek J. Clark { 1235f27e2e3cSDerek J. Clark u8 data[4] = { 0x01, 0x00, 0x00, CLAW_RGB_FRAME_OFFSET }; 1236f27e2e3cSDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 1237f27e2e3cSDerek J. Clark u16 read_addr = drvdata->rgb_addr; 1238f27e2e3cSDerek J. Clark size_t len = ARRAY_SIZE(data); 1239f27e2e3cSDerek J. Clark int ret, i; 1240f27e2e3cSDerek J. Clark 1241f27e2e3cSDerek J. Clark if (!drvdata->rgb_addr) 1242f27e2e3cSDerek J. Clark return -ENODEV; 1243f27e2e3cSDerek J. Clark 1244f27e2e3cSDerek J. Clark /* Loop through all 8 pages of RGB data */ 1245f27e2e3cSDerek J. Clark guard(mutex)(&drvdata->profile_mutex); 1246f27e2e3cSDerek J. Clark for (i = 0; i < CLAW_RGB_MAX_FRAMES; i++) { 1247f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->profile_lock) 1248f27e2e3cSDerek J. Clark drvdata->profile_pending = CLAW_RGB_PENDING; 1249f27e2e3cSDerek J. Clark data[1] = (read_addr >> 8) & 0xff; 1250f27e2e3cSDerek J. Clark data[2] = read_addr & 0x00ff; 1251f27e2e3cSDerek J. Clark ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_READ_PROFILE, data, len, 25); 1252f27e2e3cSDerek J. Clark if (ret) 1253f27e2e3cSDerek J. Clark return ret; 1254f27e2e3cSDerek J. Clark 1255f27e2e3cSDerek J. Clark read_addr += CLAW_RGB_FRAME_OFFSET; 1256f27e2e3cSDerek J. Clark } 1257f27e2e3cSDerek J. Clark 1258f27e2e3cSDerek J. Clark return 0; 1259f27e2e3cSDerek J. Clark } 1260f27e2e3cSDerek J. Clark 1261f27e2e3cSDerek J. Clark /* Send RGB configuration to device */ 1262f27e2e3cSDerek J. Clark static int claw_write_rgb_state(struct claw_drvdata *drvdata) 1263f27e2e3cSDerek J. Clark { 1264f27e2e3cSDerek J. Clark struct claw_rgb_report report = { {0x01, 0}, CLAW_RGB_FRAME_OFFSET, 0x00, 1265f27e2e3cSDerek J. Clark drvdata->rgb_frame_count, 0x09, drvdata->rgb_speed, 1266f27e2e3cSDerek J. Clark drvdata->led_mc.led_cdev.brightness }; 1267f27e2e3cSDerek J. Clark u16 write_addr = drvdata->rgb_addr; 1268f27e2e3cSDerek J. Clark int f, ret; 1269f27e2e3cSDerek J. Clark 1270f27e2e3cSDerek J. Clark if (!drvdata->rgb_addr) 1271f27e2e3cSDerek J. Clark return -ENODEV; 1272f27e2e3cSDerek J. Clark 1273f27e2e3cSDerek J. Clark if (!drvdata->rgb_frame_count) 1274f27e2e3cSDerek J. Clark return -EINVAL; 1275f27e2e3cSDerek J. Clark 1276f27e2e3cSDerek J. Clark guard(mutex)(&drvdata->rom_mutex); 1277f27e2e3cSDerek J. Clark /* Loop through (up to) 8 pages of RGB data */ 1278f27e2e3cSDerek J. Clark for (f = 0; f < drvdata->rgb_frame_count; f++) { 1279f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->frame_lock) 1280f27e2e3cSDerek J. Clark report.zone_data = drvdata->rgb_frames[f]; 1281f27e2e3cSDerek J. Clark 1282f27e2e3cSDerek J. Clark /* Set the MCU address to write the frame data to */ 1283f27e2e3cSDerek J. Clark report.read_addr = cpu_to_be16(write_addr); 1284f27e2e3cSDerek J. Clark 1285f27e2e3cSDerek J. Clark /* Serialize the rgb_report and write it to MCU */ 1286f27e2e3cSDerek J. Clark ret = claw_hw_output_report(drvdata->hdev, CLAW_COMMAND_TYPE_WRITE_PROFILE_DATA, 1287f27e2e3cSDerek J. Clark (u8 *)&report, sizeof(report), 25); 1288f27e2e3cSDerek J. Clark if (ret) 1289f27e2e3cSDerek J. Clark return ret; 1290f27e2e3cSDerek J. Clark 1291f27e2e3cSDerek J. Clark /* Increment the write addr by the offset for the next frame */ 1292f27e2e3cSDerek J. Clark write_addr += CLAW_RGB_FRAME_OFFSET; 1293f27e2e3cSDerek J. Clark } 1294f27e2e3cSDerek J. Clark 1295f27e2e3cSDerek J. Clark /* MCU will not send ACK until the USB transaction completes. ACK is sent 1296f27e2e3cSDerek J. Clark * immediately after and will hit the stale state machine, before the next 1297f27e2e3cSDerek J. Clark * command re-arms the state machine. Timeout 0 ensures no deadlock waiting 1298f27e2e3cSDerek J. Clark * for ACK that ill never come. 1299f27e2e3cSDerek J. Clark */ 1300f27e2e3cSDerek J. Clark ret = claw_hw_output_report(drvdata->hdev, CLAW_COMMAND_TYPE_SYNC_TO_ROM, NULL, 0, 0); 1301f27e2e3cSDerek J. Clark 1302f27e2e3cSDerek J. Clark return ret; 1303f27e2e3cSDerek J. Clark } 1304f27e2e3cSDerek J. Clark 1305f27e2e3cSDerek J. Clark /* Fill all zones with the same color */ 1306f27e2e3cSDerek J. Clark static void claw_frame_fill_solid(struct rgb_frame *frame, struct rgb_zone zone) 1307f27e2e3cSDerek J. Clark { 1308f27e2e3cSDerek J. Clark int z; 1309f27e2e3cSDerek J. Clark 1310f27e2e3cSDerek J. Clark for (z = 0; z < CLAW_RGB_ZONES; z++) 1311f27e2e3cSDerek J. Clark frame->zone[z] = zone; 1312f27e2e3cSDerek J. Clark } 1313f27e2e3cSDerek J. Clark 1314f27e2e3cSDerek J. Clark /* Apply solid effect (1 frame, no color) */ 1315f27e2e3cSDerek J. Clark static int claw_apply_disabled(struct claw_drvdata *drvdata) 1316f27e2e3cSDerek J. Clark { 1317f27e2e3cSDerek J. Clark struct rgb_zone off = { 0x00, 0x00, 0x00}; 1318f27e2e3cSDerek J. Clark 1319f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->frame_lock) { 1320f27e2e3cSDerek J. Clark drvdata->rgb_frame_count = 1; 1321f27e2e3cSDerek J. Clark claw_frame_fill_solid(&drvdata->rgb_frames[0], off); 1322f27e2e3cSDerek J. Clark } 1323f27e2e3cSDerek J. Clark 1324f27e2e3cSDerek J. Clark return claw_write_rgb_state(drvdata); 1325f27e2e3cSDerek J. Clark } 1326f27e2e3cSDerek J. Clark 1327f27e2e3cSDerek J. Clark /* Apply solid effect (1 frame, all zones same color) */ 1328f27e2e3cSDerek J. Clark static int claw_apply_monocolor(struct claw_drvdata *drvdata) 1329f27e2e3cSDerek J. Clark { 1330f27e2e3cSDerek J. Clark struct mc_subled *subleds = drvdata->led_mc.subled_info; 1331f27e2e3cSDerek J. Clark struct rgb_zone zone = { subleds[0].intensity, subleds[1].intensity, 1332f27e2e3cSDerek J. Clark subleds[2].intensity }; 1333f27e2e3cSDerek J. Clark 1334f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->frame_lock) { 1335f27e2e3cSDerek J. Clark drvdata->rgb_frame_count = 1; 1336f27e2e3cSDerek J. Clark claw_frame_fill_solid(&drvdata->rgb_frames[0], zone); 1337f27e2e3cSDerek J. Clark } 1338f27e2e3cSDerek J. Clark 1339f27e2e3cSDerek J. Clark return claw_write_rgb_state(drvdata); 1340f27e2e3cSDerek J. Clark } 1341f27e2e3cSDerek J. Clark 1342f27e2e3cSDerek J. Clark /* Apply breathe effect (2 frames: color -> off) */ 1343f27e2e3cSDerek J. Clark static int claw_apply_breathe(struct claw_drvdata *drvdata) 1344f27e2e3cSDerek J. Clark { 1345f27e2e3cSDerek J. Clark struct mc_subled *subleds = drvdata->led_mc.subled_info; 1346f27e2e3cSDerek J. Clark struct rgb_zone zone = { subleds[0].intensity, subleds[1].intensity, 1347f27e2e3cSDerek J. Clark subleds[2].intensity }; 1348f27e2e3cSDerek J. Clark static const struct rgb_zone off = { 0, 0, 0 }; 1349f27e2e3cSDerek J. Clark 1350f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->frame_lock) { 1351f27e2e3cSDerek J. Clark drvdata->rgb_frame_count = 2; 1352f27e2e3cSDerek J. Clark claw_frame_fill_solid(&drvdata->rgb_frames[0], zone); 1353f27e2e3cSDerek J. Clark claw_frame_fill_solid(&drvdata->rgb_frames[1], off); 1354f27e2e3cSDerek J. Clark } 1355f27e2e3cSDerek J. Clark 1356f27e2e3cSDerek J. Clark return claw_write_rgb_state(drvdata); 1357f27e2e3cSDerek J. Clark } 1358f27e2e3cSDerek J. Clark 1359f27e2e3cSDerek J. Clark /* Apply chroma effect (6 frames: rainbow cycle, all zones sync) */ 1360f27e2e3cSDerek J. Clark static int claw_apply_chroma(struct claw_drvdata *drvdata) 1361f27e2e3cSDerek J. Clark { 1362f27e2e3cSDerek J. Clark static const struct rgb_zone colors[] = { 1363f27e2e3cSDerek J. Clark {255, 0, 0}, /* red */ 1364f27e2e3cSDerek J. Clark {255, 255, 0}, /* yellow */ 1365f27e2e3cSDerek J. Clark { 0, 255, 0}, /* green */ 1366f27e2e3cSDerek J. Clark { 0, 255, 255}, /* cyan */ 1367f27e2e3cSDerek J. Clark { 0, 0, 255}, /* blue */ 1368f27e2e3cSDerek J. Clark {255, 0, 255}, /* magenta */ 1369f27e2e3cSDerek J. Clark }; 1370f27e2e3cSDerek J. Clark u8 frame_count = ARRAY_SIZE(colors); 1371f27e2e3cSDerek J. Clark int f; 1372f27e2e3cSDerek J. Clark 1373f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->frame_lock) { 1374f27e2e3cSDerek J. Clark drvdata->rgb_frame_count = frame_count; 1375f27e2e3cSDerek J. Clark 1376f27e2e3cSDerek J. Clark for (f = 0; f < frame_count; f++) 1377f27e2e3cSDerek J. Clark claw_frame_fill_solid(&drvdata->rgb_frames[f], colors[f]); 1378f27e2e3cSDerek J. Clark } 1379f27e2e3cSDerek J. Clark 1380f27e2e3cSDerek J. Clark return claw_write_rgb_state(drvdata); 1381f27e2e3cSDerek J. Clark } 1382f27e2e3cSDerek J. Clark 1383f27e2e3cSDerek J. Clark /* Apply rainbow effect (4 frames: rotating colors around joysticks) */ 1384f27e2e3cSDerek J. Clark static int claw_apply_rainbow(struct claw_drvdata *drvdata) 1385f27e2e3cSDerek J. Clark { 1386f27e2e3cSDerek J. Clark static const struct rgb_zone colors[] = { 1387f27e2e3cSDerek J. Clark {255, 0, 0}, /* red */ 1388f27e2e3cSDerek J. Clark { 0, 255, 0}, /* green */ 1389f27e2e3cSDerek J. Clark { 0, 255, 255}, /* cyan */ 1390f27e2e3cSDerek J. Clark { 0, 0, 255}, /* blue */ 1391f27e2e3cSDerek J. Clark }; 1392f27e2e3cSDerek J. Clark u8 frame_count = ARRAY_SIZE(colors); 1393f27e2e3cSDerek J. Clark int f, z; 1394f27e2e3cSDerek J. Clark 1395f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->frame_lock) { 1396f27e2e3cSDerek J. Clark drvdata->rgb_frame_count = frame_count; 1397f27e2e3cSDerek J. Clark 1398f27e2e3cSDerek J. Clark for (f = 0; f < frame_count; f++) { 1399f27e2e3cSDerek J. Clark for (z = 0; z < 4; z++) { 1400f27e2e3cSDerek J. Clark drvdata->rgb_frames[f].zone[z] = colors[(z + f) % 4]; 1401f27e2e3cSDerek J. Clark drvdata->rgb_frames[f].zone[z + 4] = colors[(z + f) % 4]; 1402f27e2e3cSDerek J. Clark } 1403f27e2e3cSDerek J. Clark drvdata->rgb_frames[f].zone[8] = colors[f]; 1404f27e2e3cSDerek J. Clark } 1405f27e2e3cSDerek J. Clark } 1406f27e2e3cSDerek J. Clark 1407f27e2e3cSDerek J. Clark return claw_write_rgb_state(drvdata); 1408f27e2e3cSDerek J. Clark } 1409f27e2e3cSDerek J. Clark 1410f27e2e3cSDerek J. Clark /* 1411f27e2e3cSDerek J. Clark * Apply frostfire effect (4 frames: fire vs ice rotating) 1412f27e2e3cSDerek J. Clark * Right joystick: fire red -> dark -> ice blue -> dark (clockwise) 1413f27e2e3cSDerek J. Clark * Left joystick: ice blue -> dark -> fire red -> dark (counter-clockwise) 1414f27e2e3cSDerek J. Clark * ABXY: fire red -> dark -> ice blue -> dark 1415f27e2e3cSDerek J. Clark */ 1416f27e2e3cSDerek J. Clark static int claw_apply_frostfire(struct claw_drvdata *drvdata) 1417f27e2e3cSDerek J. Clark { 1418f27e2e3cSDerek J. Clark static const struct rgb_zone colors[] = { 1419f27e2e3cSDerek J. Clark {255, 0, 0}, /* fire red */ 1420f27e2e3cSDerek J. Clark { 0, 0, 0}, /* dark */ 1421f27e2e3cSDerek J. Clark { 0, 0, 255}, /* ice blue */ 1422f27e2e3cSDerek J. Clark { 0, 0, 0}, /* dark */ 1423f27e2e3cSDerek J. Clark }; 1424f27e2e3cSDerek J. Clark u8 frame_count = ARRAY_SIZE(colors); 1425f27e2e3cSDerek J. Clark int f, z; 1426f27e2e3cSDerek J. Clark 1427f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->frame_lock) { 1428f27e2e3cSDerek J. Clark drvdata->rgb_frame_count = frame_count; 1429f27e2e3cSDerek J. Clark 1430f27e2e3cSDerek J. Clark for (f = 0; f < frame_count; f++) { 1431f27e2e3cSDerek J. Clark for (z = 0; z < 4; z++) { 1432f27e2e3cSDerek J. Clark drvdata->rgb_frames[f].zone[z] = colors[(z + f) % 4]; 1433f27e2e3cSDerek J. Clark drvdata->rgb_frames[f].zone[z + 4] = colors[(z - f + 6) % 4]; 1434f27e2e3cSDerek J. Clark } 1435f27e2e3cSDerek J. Clark drvdata->rgb_frames[f].zone[8] = colors[f]; 1436f27e2e3cSDerek J. Clark } 1437f27e2e3cSDerek J. Clark } 1438f27e2e3cSDerek J. Clark 1439f27e2e3cSDerek J. Clark return claw_write_rgb_state(drvdata); 1440f27e2e3cSDerek J. Clark } 1441f27e2e3cSDerek J. Clark 1442f27e2e3cSDerek J. Clark /* Apply current state to device */ 1443f27e2e3cSDerek J. Clark static int claw_apply_rgb_state(struct claw_drvdata *drvdata) 1444f27e2e3cSDerek J. Clark { 1445f27e2e3cSDerek J. Clark if (!drvdata->rgb_enabled) 1446f27e2e3cSDerek J. Clark return claw_apply_disabled(drvdata); 1447f27e2e3cSDerek J. Clark 1448f27e2e3cSDerek J. Clark switch (drvdata->rgb_effect) { 1449f27e2e3cSDerek J. Clark case CLAW_RGB_EFFECT_MONOCOLOR: 1450f27e2e3cSDerek J. Clark return claw_apply_monocolor(drvdata); 1451f27e2e3cSDerek J. Clark case CLAW_RGB_EFFECT_BREATHE: 1452f27e2e3cSDerek J. Clark return claw_apply_breathe(drvdata); 1453f27e2e3cSDerek J. Clark case CLAW_RGB_EFFECT_CHROMA: 1454f27e2e3cSDerek J. Clark return claw_apply_chroma(drvdata); 1455f27e2e3cSDerek J. Clark case CLAW_RGB_EFFECT_RAINBOW: 1456f27e2e3cSDerek J. Clark return claw_apply_rainbow(drvdata); 1457f27e2e3cSDerek J. Clark case CLAW_RGB_EFFECT_FROSTFIRE: 1458f27e2e3cSDerek J. Clark return claw_apply_frostfire(drvdata); 1459f27e2e3cSDerek J. Clark default: 1460f27e2e3cSDerek J. Clark dev_err(&drvdata->hdev->dev, "No supported rgb_effect selected\n"); 1461f27e2e3cSDerek J. Clark return -EINVAL; 1462f27e2e3cSDerek J. Clark } 1463f27e2e3cSDerek J. Clark } 1464f27e2e3cSDerek J. Clark 1465f27e2e3cSDerek J. Clark static void claw_rgb_queue_fn(struct work_struct *work) 1466f27e2e3cSDerek J. Clark { 1467f27e2e3cSDerek J. Clark struct delayed_work *dwork = container_of(work, struct delayed_work, work); 1468f27e2e3cSDerek J. Clark struct claw_drvdata *drvdata = container_of(dwork, struct claw_drvdata, rgb_queue); 1469f27e2e3cSDerek J. Clark int ret; 1470f27e2e3cSDerek J. Clark 1471f27e2e3cSDerek J. Clark if (!drvdata) 1472f27e2e3cSDerek J. Clark return; 1473f27e2e3cSDerek J. Clark 1474f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1475f27e2e3cSDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1476f27e2e3cSDerek J. Clark if (!smp_load_acquire(&drvdata->rgb_registered)) 1477f27e2e3cSDerek J. Clark return; 1478f27e2e3cSDerek J. Clark } 1479f27e2e3cSDerek J. Clark 1480f27e2e3cSDerek J. Clark ret = claw_apply_rgb_state(drvdata); 1481f27e2e3cSDerek J. Clark if (ret) 1482f27e2e3cSDerek J. Clark dev_err(&drvdata->hdev->dev, "Failed to apply RGB state: %d\n", ret); 1483f27e2e3cSDerek J. Clark } 1484f27e2e3cSDerek J. Clark 1485f27e2e3cSDerek J. Clark static ssize_t effect_store(struct device *dev, 1486f27e2e3cSDerek J. Clark struct device_attribute *attr, 1487f27e2e3cSDerek J. Clark const char *buf, size_t count) 1488f27e2e3cSDerek J. Clark { 1489f27e2e3cSDerek J. Clark struct led_classdev *led_cdev = dev_get_drvdata(dev); 1490f27e2e3cSDerek J. Clark struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev); 1491f27e2e3cSDerek J. Clark struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc); 1492f27e2e3cSDerek J. Clark int ret; 1493f27e2e3cSDerek J. Clark 1494f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1495f27e2e3cSDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1496f27e2e3cSDerek J. Clark if (!smp_load_acquire(&drvdata->rgb_registered)) 1497f27e2e3cSDerek J. Clark return -ENODEV; 1498f27e2e3cSDerek J. Clark } 1499f27e2e3cSDerek J. Clark 1500f27e2e3cSDerek J. Clark ret = sysfs_match_string(claw_rgb_effect_text, buf); 1501f27e2e3cSDerek J. Clark if (ret < 0) 1502f27e2e3cSDerek J. Clark return ret; 1503f27e2e3cSDerek J. Clark 1504f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->profile_lock) 1505f27e2e3cSDerek J. Clark drvdata->rgb_effect = ret; 1506f27e2e3cSDerek J. Clark 1507f27e2e3cSDerek J. Clark mod_delayed_work(system_wq, &drvdata->rgb_queue, msecs_to_jiffies(50)); 1508f27e2e3cSDerek J. Clark 1509f27e2e3cSDerek J. Clark return count; 1510f27e2e3cSDerek J. Clark } 1511f27e2e3cSDerek J. Clark 1512f27e2e3cSDerek J. Clark static ssize_t effect_show(struct device *dev, 1513f27e2e3cSDerek J. Clark struct device_attribute *attr, char *buf) 1514f27e2e3cSDerek J. Clark { 1515f27e2e3cSDerek J. Clark struct led_classdev *led_cdev = dev_get_drvdata(dev); 1516f27e2e3cSDerek J. Clark struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev); 1517f27e2e3cSDerek J. Clark struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc); 1518f27e2e3cSDerek J. Clark 1519f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1520f27e2e3cSDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1521f27e2e3cSDerek J. Clark if (!smp_load_acquire(&drvdata->rgb_registered)) 1522f27e2e3cSDerek J. Clark return -ENODEV; 1523f27e2e3cSDerek J. Clark } 1524f27e2e3cSDerek J. Clark 1525f27e2e3cSDerek J. Clark if (drvdata->rgb_effect >= ARRAY_SIZE(claw_rgb_effect_text)) 1526f27e2e3cSDerek J. Clark return -EINVAL; 1527f27e2e3cSDerek J. Clark 1528f27e2e3cSDerek J. Clark return sysfs_emit(buf, "%s\n", claw_rgb_effect_text[drvdata->rgb_effect]); 1529f27e2e3cSDerek J. Clark } 1530f27e2e3cSDerek J. Clark 1531f27e2e3cSDerek J. Clark static DEVICE_ATTR_RW(effect); 1532f27e2e3cSDerek J. Clark 1533f27e2e3cSDerek J. Clark static ssize_t effect_index_show(struct device *dev, 1534f27e2e3cSDerek J. Clark struct device_attribute *attr, char *buf) 1535f27e2e3cSDerek J. Clark { 1536f27e2e3cSDerek J. Clark int i, count = 0; 1537f27e2e3cSDerek J. Clark 1538f27e2e3cSDerek J. Clark for (i = 0; i < ARRAY_SIZE(claw_rgb_effect_text); i++) 1539f27e2e3cSDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", claw_rgb_effect_text[i]); 1540f27e2e3cSDerek J. Clark 1541f27e2e3cSDerek J. Clark if (count) 1542f27e2e3cSDerek J. Clark buf[count - 1] = '\n'; 1543f27e2e3cSDerek J. Clark 1544f27e2e3cSDerek J. Clark return count; 1545f27e2e3cSDerek J. Clark } 1546f27e2e3cSDerek J. Clark static DEVICE_ATTR_RO(effect_index); 1547f27e2e3cSDerek J. Clark 1548f27e2e3cSDerek J. Clark static ssize_t enabled_store(struct device *dev, 1549f27e2e3cSDerek J. Clark struct device_attribute *attr, 1550f27e2e3cSDerek J. Clark const char *buf, size_t count) 1551f27e2e3cSDerek J. Clark { 1552f27e2e3cSDerek J. Clark struct led_classdev *led_cdev = dev_get_drvdata(dev); 1553f27e2e3cSDerek J. Clark struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev); 1554f27e2e3cSDerek J. Clark struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc); 1555f27e2e3cSDerek J. Clark bool val; 1556f27e2e3cSDerek J. Clark int ret; 1557f27e2e3cSDerek J. Clark 1558f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1559f27e2e3cSDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1560f27e2e3cSDerek J. Clark if (!smp_load_acquire(&drvdata->rgb_registered)) 1561f27e2e3cSDerek J. Clark return -ENODEV; 1562f27e2e3cSDerek J. Clark } 1563f27e2e3cSDerek J. Clark 1564f27e2e3cSDerek J. Clark ret = kstrtobool(buf, &val); 1565f27e2e3cSDerek J. Clark if (ret) 1566f27e2e3cSDerek J. Clark return ret; 1567f27e2e3cSDerek J. Clark 1568f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->profile_lock) 1569f27e2e3cSDerek J. Clark drvdata->rgb_enabled = val; 1570f27e2e3cSDerek J. Clark 1571f27e2e3cSDerek J. Clark mod_delayed_work(system_wq, &drvdata->rgb_queue, msecs_to_jiffies(50)); 1572f27e2e3cSDerek J. Clark 1573f27e2e3cSDerek J. Clark return count; 1574f27e2e3cSDerek J. Clark } 1575f27e2e3cSDerek J. Clark 1576f27e2e3cSDerek J. Clark static ssize_t enabled_show(struct device *dev, 1577f27e2e3cSDerek J. Clark struct device_attribute *attr, char *buf) 1578f27e2e3cSDerek J. Clark { 1579f27e2e3cSDerek J. Clark struct led_classdev *led_cdev = dev_get_drvdata(dev); 1580f27e2e3cSDerek J. Clark struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev); 1581f27e2e3cSDerek J. Clark struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc); 1582f27e2e3cSDerek J. Clark 1583f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1584f27e2e3cSDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1585f27e2e3cSDerek J. Clark if (!smp_load_acquire(&drvdata->rgb_registered)) 1586f27e2e3cSDerek J. Clark return -ENODEV; 1587f27e2e3cSDerek J. Clark } 1588f27e2e3cSDerek J. Clark 1589f27e2e3cSDerek J. Clark return sysfs_emit(buf, "%s\n", drvdata->rgb_enabled ? "true" : "false"); 1590f27e2e3cSDerek J. Clark } 1591f27e2e3cSDerek J. Clark static DEVICE_ATTR_RW(enabled); 1592f27e2e3cSDerek J. Clark 1593f27e2e3cSDerek J. Clark static ssize_t enabled_index_show(struct device *dev, 1594f27e2e3cSDerek J. Clark struct device_attribute *attr, char *buf) 1595f27e2e3cSDerek J. Clark { 1596f27e2e3cSDerek J. Clark return sysfs_emit(buf, "true false\n"); 1597f27e2e3cSDerek J. Clark } 1598f27e2e3cSDerek J. Clark static DEVICE_ATTR_RO(enabled_index); 1599f27e2e3cSDerek J. Clark 1600f27e2e3cSDerek J. Clark static ssize_t speed_store(struct device *dev, struct device_attribute *attr, 1601f27e2e3cSDerek J. Clark const char *buf, size_t count) 1602f27e2e3cSDerek J. Clark { 1603f27e2e3cSDerek J. Clark struct led_classdev *led_cdev = dev_get_drvdata(dev); 1604f27e2e3cSDerek J. Clark struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev); 1605f27e2e3cSDerek J. Clark struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc); 1606f27e2e3cSDerek J. Clark unsigned int val, speed; 1607f27e2e3cSDerek J. Clark int ret; 1608f27e2e3cSDerek J. Clark 1609f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1610f27e2e3cSDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1611f27e2e3cSDerek J. Clark if (!smp_load_acquire(&drvdata->rgb_registered)) 1612f27e2e3cSDerek J. Clark return -ENODEV; 1613f27e2e3cSDerek J. Clark } 1614f27e2e3cSDerek J. Clark 1615f27e2e3cSDerek J. Clark ret = kstrtouint(buf, 10, &val); 1616f27e2e3cSDerek J. Clark if (ret) 1617f27e2e3cSDerek J. Clark return ret; 1618f27e2e3cSDerek J. Clark 1619f27e2e3cSDerek J. Clark if (val > 20) 1620f27e2e3cSDerek J. Clark return -EINVAL; 1621f27e2e3cSDerek J. Clark 1622f27e2e3cSDerek J. Clark /* 0 is fastest, invert value for intuitive userspace speed */ 1623f27e2e3cSDerek J. Clark speed = 20 - val; 1624f27e2e3cSDerek J. Clark 1625f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->profile_lock) 1626f27e2e3cSDerek J. Clark drvdata->rgb_speed = speed; 1627f27e2e3cSDerek J. Clark 1628f27e2e3cSDerek J. Clark mod_delayed_work(system_wq, &drvdata->rgb_queue, msecs_to_jiffies(50)); 1629f27e2e3cSDerek J. Clark 1630f27e2e3cSDerek J. Clark return count; 1631f27e2e3cSDerek J. Clark } 1632f27e2e3cSDerek J. Clark 1633f27e2e3cSDerek J. Clark static ssize_t speed_show(struct device *dev, struct device_attribute *attr, 1634f27e2e3cSDerek J. Clark char *buf) 1635f27e2e3cSDerek J. Clark { 1636f27e2e3cSDerek J. Clark struct led_classdev *led_cdev = dev_get_drvdata(dev); 1637f27e2e3cSDerek J. Clark struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev); 1638f27e2e3cSDerek J. Clark struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc); 1639f27e2e3cSDerek J. Clark u8 speed = 20 - drvdata->rgb_speed; 1640f27e2e3cSDerek J. Clark 1641f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1642f27e2e3cSDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1643f27e2e3cSDerek J. Clark if (!smp_load_acquire(&drvdata->rgb_registered)) 1644f27e2e3cSDerek J. Clark return -ENODEV; 1645f27e2e3cSDerek J. Clark } 1646f27e2e3cSDerek J. Clark 1647f27e2e3cSDerek J. Clark return sysfs_emit(buf, "%u\n", speed); 1648f27e2e3cSDerek J. Clark } 1649f27e2e3cSDerek J. Clark static DEVICE_ATTR_RW(speed); 1650f27e2e3cSDerek J. Clark 1651f27e2e3cSDerek J. Clark static ssize_t speed_range_show(struct device *dev, 1652f27e2e3cSDerek J. Clark struct device_attribute *attr, char *buf) 1653f27e2e3cSDerek J. Clark { 1654f27e2e3cSDerek J. Clark return sysfs_emit(buf, "0-20\n"); 1655f27e2e3cSDerek J. Clark } 1656f27e2e3cSDerek J. Clark static DEVICE_ATTR_RO(speed_range); 1657f27e2e3cSDerek J. Clark 1658f27e2e3cSDerek J. Clark static void claw_led_brightness_set(struct led_classdev *led_cdev, 1659f27e2e3cSDerek J. Clark enum led_brightness _brightness) 1660f27e2e3cSDerek J. Clark { 1661f27e2e3cSDerek J. Clark struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev); 1662f27e2e3cSDerek J. Clark struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc); 1663f27e2e3cSDerek J. Clark 1664f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1665f27e2e3cSDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1666f27e2e3cSDerek J. Clark if (!smp_load_acquire(&drvdata->rgb_registered)) 1667f27e2e3cSDerek J. Clark return; 1668f27e2e3cSDerek J. Clark } 1669f27e2e3cSDerek J. Clark 1670f27e2e3cSDerek J. Clark mod_delayed_work(system_wq, &drvdata->rgb_queue, msecs_to_jiffies(50)); 1671f27e2e3cSDerek J. Clark } 1672f27e2e3cSDerek J. Clark 1673f27e2e3cSDerek J. Clark static struct attribute *claw_rgb_attrs[] = { 1674f27e2e3cSDerek J. Clark &dev_attr_effect.attr, 1675f27e2e3cSDerek J. Clark &dev_attr_effect_index.attr, 1676f27e2e3cSDerek J. Clark &dev_attr_enabled.attr, 1677f27e2e3cSDerek J. Clark &dev_attr_enabled_index.attr, 1678f27e2e3cSDerek J. Clark &dev_attr_speed.attr, 1679f27e2e3cSDerek J. Clark &dev_attr_speed_range.attr, 1680f27e2e3cSDerek J. Clark NULL, 1681f27e2e3cSDerek J. Clark }; 1682f27e2e3cSDerek J. Clark 1683f27e2e3cSDerek J. Clark static const struct attribute_group claw_rgb_attr_group = { 1684f27e2e3cSDerek J. Clark .attrs = claw_rgb_attrs, 1685f27e2e3cSDerek J. Clark }; 1686f27e2e3cSDerek J. Clark 1687f27e2e3cSDerek J. Clark static struct mc_subled claw_rgb_subled_info[] = { 1688f27e2e3cSDerek J. Clark { 1689f27e2e3cSDerek J. Clark .color_index = LED_COLOR_ID_RED, 1690f27e2e3cSDerek J. Clark .channel = 0x1, 1691f27e2e3cSDerek J. Clark }, 1692f27e2e3cSDerek J. Clark { 1693f27e2e3cSDerek J. Clark .color_index = LED_COLOR_ID_GREEN, 1694f27e2e3cSDerek J. Clark .channel = 0x2, 1695f27e2e3cSDerek J. Clark }, 1696f27e2e3cSDerek J. Clark { 1697f27e2e3cSDerek J. Clark .color_index = LED_COLOR_ID_BLUE, 1698f27e2e3cSDerek J. Clark .channel = 0x3, 1699f27e2e3cSDerek J. Clark }, 1700f27e2e3cSDerek J. Clark }; 1701f27e2e3cSDerek J. Clark 1702a9517958SDerek J. Clark static void cfg_setup_fn(struct work_struct *work) 1703a9517958SDerek J. Clark { 1704a9517958SDerek J. Clark struct delayed_work *dwork = container_of(work, struct delayed_work, work); 1705a9517958SDerek J. Clark struct claw_drvdata *drvdata = container_of(dwork, struct claw_drvdata, cfg_setup); 1706f27e2e3cSDerek J. Clark bool gamepad_ready = false, rgb_ready = false, gp_registered, rgb_registered; 1707a9517958SDerek J. Clark int ret; 1708a9517958SDerek J. Clark 1709a9517958SDerek J. Clark ret = claw_hw_output_report(drvdata->hdev, CLAW_COMMAND_TYPE_READ_GAMEPAD_MODE, 1710a9517958SDerek J. Clark NULL, 0, 25); 1711a9517958SDerek J. Clark if (ret) { 1712a9517958SDerek J. Clark dev_err(&drvdata->hdev->dev, 1713f27e2e3cSDerek J. Clark "Failed to read gamepad mode: %d\n", ret); 1714f27e2e3cSDerek J. Clark goto prep_rgb; 1715a9517958SDerek J. Clark } 1716f27e2e3cSDerek J. Clark gamepad_ready = true; 1717f27e2e3cSDerek J. Clark 1718f27e2e3cSDerek J. Clark prep_rgb: 1719f27e2e3cSDerek J. Clark ret = claw_read_rgb_config(drvdata->hdev); 1720f27e2e3cSDerek J. Clark if (ret) { 1721f27e2e3cSDerek J. Clark dev_err(&drvdata->hdev->dev, 1722f27e2e3cSDerek J. Clark "Failed to read RGB config: %d\n", ret); 1723f27e2e3cSDerek J. Clark goto try_gamepad; 1724f27e2e3cSDerek J. Clark } 1725f27e2e3cSDerek J. Clark rgb_ready = true; 1726a9517958SDerek J. Clark 1727a9517958SDerek J. Clark /* Add sysfs attributes after we get the device state */ 1728f27e2e3cSDerek J. Clark try_gamepad: 1729f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) 1730f27e2e3cSDerek J. Clark /* Pairs with smp_store_release from below */ 1731f27e2e3cSDerek J. Clark gp_registered = smp_load_acquire(&drvdata->gp_registered); 1732f27e2e3cSDerek J. Clark 1733f27e2e3cSDerek J. Clark if (!gp_registered && gamepad_ready) { 1734a9517958SDerek J. Clark ret = device_add_group(&drvdata->hdev->dev, &claw_gamepad_attr_group); 1735a9517958SDerek J. Clark if (ret) { 1736a9517958SDerek J. Clark dev_err(&drvdata->hdev->dev, 1737f27e2e3cSDerek J. Clark "Failed to create gamepad attrs: %d\n", ret); 1738f27e2e3cSDerek J. Clark goto try_rgb; 1739a9517958SDerek J. Clark } 1740f27e2e3cSDerek J. Clark 1741f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1742a9517958SDerek J. Clark /* Pairs with smp_load_acquire in attribute show/store functions */ 1743a9517958SDerek J. Clark smp_store_release(&drvdata->gp_registered, true); 1744f27e2e3cSDerek J. Clark gp_registered = true; 1745f27e2e3cSDerek J. Clark } 1746f27e2e3cSDerek J. Clark } 1747a9517958SDerek J. Clark 1748f27e2e3cSDerek J. Clark try_rgb: 1749f27e2e3cSDerek J. Clark /* Add and enable RGB interface once we have the device state */ 1750f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) 1751f27e2e3cSDerek J. Clark /* Pairs with smp_store_release from below */ 1752f27e2e3cSDerek J. Clark rgb_registered = smp_load_acquire(&drvdata->rgb_registered); 1753f27e2e3cSDerek J. Clark 1754f27e2e3cSDerek J. Clark if (!rgb_registered && rgb_ready) { 1755f27e2e3cSDerek J. Clark ret = led_classdev_multicolor_register(&drvdata->hdev->dev, 1756f27e2e3cSDerek J. Clark &drvdata->led_mc); 1757f27e2e3cSDerek J. Clark if (ret) { 1758f27e2e3cSDerek J. Clark dev_err(&drvdata->hdev->dev, "Failed to create led device: %d\n", ret); 1759f27e2e3cSDerek J. Clark goto update_kobjects; 1760f27e2e3cSDerek J. Clark } 1761f27e2e3cSDerek J. Clark 1762f27e2e3cSDerek J. Clark ret = device_add_group(drvdata->led_mc.led_cdev.dev, &claw_rgb_attr_group); 1763f27e2e3cSDerek J. Clark if (ret) { 1764f27e2e3cSDerek J. Clark dev_err(&drvdata->hdev->dev, "Failed to create RGB attrs: %d\n", ret); 1765f27e2e3cSDerek J. Clark led_classdev_multicolor_unregister(&drvdata->led_mc); 1766f27e2e3cSDerek J. Clark goto update_kobjects; 1767f27e2e3cSDerek J. Clark } 1768f27e2e3cSDerek J. Clark 1769f27e2e3cSDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1770f27e2e3cSDerek J. Clark /* Pairs with smp_load_acquire in attribute show/store functions */ 1771f27e2e3cSDerek J. Clark smp_store_release(&drvdata->rgb_registered, true); 1772f27e2e3cSDerek J. Clark rgb_registered = true; 1773f27e2e3cSDerek J. Clark } 1774f27e2e3cSDerek J. Clark } 1775f27e2e3cSDerek J. Clark 1776f27e2e3cSDerek J. Clark update_kobjects: 1777f27e2e3cSDerek J. Clark if (gp_registered) 1778a9517958SDerek J. Clark kobject_uevent(&drvdata->hdev->dev.kobj, KOBJ_CHANGE); 1779f27e2e3cSDerek J. Clark if (rgb_registered) 1780f27e2e3cSDerek J. Clark kobject_uevent(&drvdata->led_mc.led_cdev.dev->kobj, KOBJ_CHANGE); 1781a9517958SDerek J. Clark } 1782a9517958SDerek J. Clark 1783a9517958SDerek J. Clark static void cfg_resume_fn(struct work_struct *work) 1784a9517958SDerek J. Clark { 1785a9517958SDerek J. Clark struct delayed_work *dwork = container_of(work, struct delayed_work, work); 1786a9517958SDerek J. Clark struct claw_drvdata *drvdata = container_of(dwork, struct claw_drvdata, cfg_resume); 1787a9517958SDerek J. Clark 1788a9517958SDerek J. Clark guard(spinlock_irqsave)(&drvdata->registration_lock); 1789a9517958SDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1790f27e2e3cSDerek J. Clark if (!smp_load_acquire(&drvdata->gp_registered) || 1791f27e2e3cSDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1792f27e2e3cSDerek J. Clark !smp_load_acquire(&drvdata->rgb_registered)) 1793a9517958SDerek J. Clark schedule_delayed_work(&drvdata->cfg_setup, msecs_to_jiffies(500)); 1794a9517958SDerek J. Clark } 1795a9517958SDerek J. Clark 179693ff13edSDerek J. Clark static void claw_features_supported(struct claw_drvdata *drvdata) 179793ff13edSDerek J. Clark { 179893ff13edSDerek J. Clark u8 major = (drvdata->bcd_device >> 8) & 0xff; 179993ff13edSDerek J. Clark u8 minor = drvdata->bcd_device & 0xff; 180093ff13edSDerek J. Clark 180193ff13edSDerek J. Clark if (major == 0x01) { 180293ff13edSDerek J. Clark drvdata->bmap_support = true; 1803f27e2e3cSDerek J. Clark if (minor >= 0x66) { 180493ff13edSDerek J. Clark drvdata->bmap_addr = button_mapping_addr_new; 1805*95547c53SDerek J. Clark drvdata->rumble_support = true; 1806f27e2e3cSDerek J. Clark drvdata->rgb_addr = rgb_addr_new; 1807f27e2e3cSDerek J. Clark } else { 180893ff13edSDerek J. Clark drvdata->bmap_addr = button_mapping_addr_old; 1809f27e2e3cSDerek J. Clark drvdata->rgb_addr = rgb_addr_old; 1810f27e2e3cSDerek J. Clark } 181193ff13edSDerek J. Clark return; 181293ff13edSDerek J. Clark } 181393ff13edSDerek J. Clark 181493ff13edSDerek J. Clark if ((major == 0x02 && minor >= 0x17) || major >= 0x03) { 181593ff13edSDerek J. Clark drvdata->bmap_support = true; 181693ff13edSDerek J. Clark drvdata->bmap_addr = button_mapping_addr_new; 1817*95547c53SDerek J. Clark drvdata->rumble_support = true; 1818f27e2e3cSDerek J. Clark drvdata->rgb_addr = rgb_addr_new; 181993ff13edSDerek J. Clark return; 182093ff13edSDerek J. Clark } 1821f27e2e3cSDerek J. Clark 1822f27e2e3cSDerek J. Clark drvdata->rgb_addr = rgb_addr_old; 182393ff13edSDerek J. Clark } 182493ff13edSDerek J. Clark 1825a9517958SDerek J. Clark static int claw_probe(struct hid_device *hdev, u8 ep) 1826a9517958SDerek J. Clark { 182793ff13edSDerek J. Clark struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 182893ff13edSDerek J. Clark struct usb_device *udev = interface_to_usbdev(intf); 1829a9517958SDerek J. Clark struct claw_drvdata *drvdata; 1830a9517958SDerek J. Clark int ret; 1831a9517958SDerek J. Clark 1832a9517958SDerek J. Clark drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); 1833a9517958SDerek J. Clark if (!drvdata) 1834a9517958SDerek J. Clark return -ENOMEM; 1835a9517958SDerek J. Clark 1836a9517958SDerek J. Clark drvdata->gamepad_mode = CLAW_GAMEPAD_MODE_XINPUT; 1837f27e2e3cSDerek J. Clark drvdata->rgb_enabled = true; 1838a9517958SDerek J. Clark drvdata->hdev = hdev; 1839a9517958SDerek J. Clark drvdata->ep = ep; 1840a9517958SDerek J. Clark 184193ff13edSDerek J. Clark /* Determine feature level from firmware version */ 184293ff13edSDerek J. Clark drvdata->bcd_device = le16_to_cpu(udev->descriptor.bcdDevice); 184393ff13edSDerek J. Clark claw_features_supported(drvdata); 184493ff13edSDerek J. Clark 184593ff13edSDerek J. Clark if (!drvdata->bmap_support) 184693ff13edSDerek J. Clark dev_dbg(&hdev->dev, "M-Key mapping is not supported. Update firmware to enable.\n"); 184793ff13edSDerek J. Clark 1848f27e2e3cSDerek J. Clark /* Device is hardwired and name is guaranteed to be unique */ 1849f27e2e3cSDerek J. Clark drvdata->led_mc.led_cdev.name = "msi_claw:rgb:joystick_rings"; 1850f27e2e3cSDerek J. Clark drvdata->led_mc.led_cdev.brightness = 0x50; 1851f27e2e3cSDerek J. Clark drvdata->led_mc.led_cdev.max_brightness = 0x64; 1852f27e2e3cSDerek J. Clark drvdata->led_mc.led_cdev.color = LED_COLOR_ID_RGB; 1853f27e2e3cSDerek J. Clark drvdata->led_mc.led_cdev.brightness_set = claw_led_brightness_set; 1854f27e2e3cSDerek J. Clark drvdata->led_mc.num_colors = 3; 1855f27e2e3cSDerek J. Clark drvdata->led_mc.subled_info = devm_kmemdup(&hdev->dev, claw_rgb_subled_info, 1856f27e2e3cSDerek J. Clark sizeof(claw_rgb_subled_info), GFP_KERNEL); 1857f27e2e3cSDerek J. Clark if (!drvdata->led_mc.subled_info) 1858f27e2e3cSDerek J. Clark return -ENOMEM; 1859f27e2e3cSDerek J. Clark 1860a9517958SDerek J. Clark mutex_init(&drvdata->cfg_mutex); 186193ff13edSDerek J. Clark mutex_init(&drvdata->profile_mutex); 186293ff13edSDerek J. Clark mutex_init(&drvdata->rom_mutex); 1863a9517958SDerek J. Clark spin_lock_init(&drvdata->registration_lock); 1864a9517958SDerek J. Clark spin_lock_init(&drvdata->cmd_lock); 1865a9517958SDerek J. Clark spin_lock_init(&drvdata->mode_lock); 186693ff13edSDerek J. Clark spin_lock_init(&drvdata->profile_lock); 1867f27e2e3cSDerek J. Clark spin_lock_init(&drvdata->frame_lock); 1868*95547c53SDerek J. Clark spin_lock_init(&drvdata->rumble_lock); 1869a9517958SDerek J. Clark init_completion(&drvdata->orphan_ack_complete); 1870a9517958SDerek J. Clark init_completion(&drvdata->send_cmd_complete); 1871a9517958SDerek J. Clark INIT_DELAYED_WORK(&drvdata->cfg_resume, &cfg_resume_fn); 1872a9517958SDerek J. Clark INIT_DELAYED_WORK(&drvdata->cfg_setup, &cfg_setup_fn); 1873f27e2e3cSDerek J. Clark INIT_DELAYED_WORK(&drvdata->rgb_queue, &claw_rgb_queue_fn); 1874a9517958SDerek J. Clark 1875a9517958SDerek J. Clark /* For control interface: open the HID transport for sending commands. */ 1876a9517958SDerek J. Clark ret = hid_hw_open(hdev); 1877a9517958SDerek J. Clark if (ret) 1878a9517958SDerek J. Clark return ret; 1879a9517958SDerek J. Clark 1880a9517958SDerek J. Clark hid_set_drvdata(hdev, drvdata); 1881a9517958SDerek J. Clark schedule_delayed_work(&drvdata->cfg_setup, msecs_to_jiffies(500)); 1882a9517958SDerek J. Clark 1883a9517958SDerek J. Clark return 0; 1884a9517958SDerek J. Clark } 1885a9517958SDerek J. Clark 1886a9517958SDerek J. Clark static int msi_probe(struct hid_device *hdev, const struct hid_device_id *id) 1887a9517958SDerek J. Clark { 1888a9517958SDerek J. Clark int ret; 1889a9517958SDerek J. Clark u8 ep; 1890a9517958SDerek J. Clark 1891a9517958SDerek J. Clark if (!hid_is_usb(hdev)) { 1892a9517958SDerek J. Clark ret = -ENODEV; 1893a9517958SDerek J. Clark goto err_probe; 1894a9517958SDerek J. Clark } 1895a9517958SDerek J. Clark 1896a9517958SDerek J. Clark ret = hid_parse(hdev); 1897a9517958SDerek J. Clark if (ret) 1898a9517958SDerek J. Clark goto err_probe; 1899a9517958SDerek J. Clark 1900a9517958SDerek J. Clark /* Set quirk to create separate input devices per HID application */ 1901a9517958SDerek J. Clark hdev->quirks |= HID_QUIRK_INPUT_PER_APP | HID_QUIRK_MULTI_INPUT; 1902a9517958SDerek J. Clark ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 1903a9517958SDerek J. Clark if (ret) 1904a9517958SDerek J. Clark goto err_probe; 1905a9517958SDerek J. Clark 1906a9517958SDerek J. Clark /* For non-control interfaces (keyboard/mouse), allow userspace to grab the devices. */ 1907a9517958SDerek J. Clark ret = get_endpoint_address(hdev); 1908a9517958SDerek J. Clark if (ret < 0) 1909a9517958SDerek J. Clark goto err_stop_hw; 1910a9517958SDerek J. Clark 1911a9517958SDerek J. Clark ep = ret; 1912a9517958SDerek J. Clark if (ep == CLAW_XINPUT_CFG_INTF_IN || ep == CLAW_DINPUT_CFG_INTF_IN) { 1913a9517958SDerek J. Clark ret = claw_probe(hdev, ep); 1914a9517958SDerek J. Clark if (ret) 1915a9517958SDerek J. Clark goto err_stop_hw; 1916a9517958SDerek J. Clark } 1917a9517958SDerek J. Clark 1918a9517958SDerek J. Clark return 0; 1919a9517958SDerek J. Clark 1920a9517958SDerek J. Clark err_stop_hw: 1921a9517958SDerek J. Clark hid_hw_stop(hdev); 1922a9517958SDerek J. Clark err_probe: 1923a9517958SDerek J. Clark return dev_err_probe(&hdev->dev, ret, "Failed to init device\n"); 1924a9517958SDerek J. Clark } 1925a9517958SDerek J. Clark 1926a9517958SDerek J. Clark static void claw_remove(struct hid_device *hdev) 1927a9517958SDerek J. Clark { 1928a9517958SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 1929a9517958SDerek J. Clark bool gp_registered; 1930f27e2e3cSDerek J. Clark bool rgb_registered; 1931a9517958SDerek J. Clark 1932a9517958SDerek J. Clark if (!drvdata) 1933a9517958SDerek J. Clark return; 1934a9517958SDerek J. Clark 1935a9517958SDerek J. Clark cancel_delayed_work_sync(&drvdata->cfg_resume); 1936a9517958SDerek J. Clark cancel_delayed_work_sync(&drvdata->cfg_setup); 1937a9517958SDerek J. Clark 1938a9517958SDerek J. Clark scoped_guard(spinlock_irqsave, &drvdata->registration_lock) { 1939a9517958SDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1940a9517958SDerek J. Clark gp_registered = smp_load_acquire(&drvdata->gp_registered); 1941a9517958SDerek J. Clark /* Pairs with smp_load_acquire in attribute show/store functions */ 1942a9517958SDerek J. Clark smp_store_release(&drvdata->gp_registered, false); 1943f27e2e3cSDerek J. Clark /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */ 1944f27e2e3cSDerek J. Clark rgb_registered = smp_load_acquire(&drvdata->rgb_registered); 1945f27e2e3cSDerek J. Clark /* Pairs with smp_load_acquire in attribute show/store functions */ 1946f27e2e3cSDerek J. Clark smp_store_release(&drvdata->rgb_registered, false); 1947a9517958SDerek J. Clark } 1948a9517958SDerek J. Clark 1949a9517958SDerek J. Clark if (gp_registered) 1950a9517958SDerek J. Clark device_remove_group(&hdev->dev, &claw_gamepad_attr_group); 1951a9517958SDerek J. Clark 1952f27e2e3cSDerek J. Clark if (rgb_registered) { 1953f27e2e3cSDerek J. Clark device_remove_group(drvdata->led_mc.led_cdev.dev, &claw_rgb_attr_group); 1954f27e2e3cSDerek J. Clark led_classdev_multicolor_unregister(&drvdata->led_mc); 1955f27e2e3cSDerek J. Clark } 1956f27e2e3cSDerek J. Clark cancel_delayed_work_sync(&drvdata->rgb_queue); 1957f27e2e3cSDerek J. Clark 1958a9517958SDerek J. Clark hid_hw_close(hdev); 1959a9517958SDerek J. Clark } 1960a9517958SDerek J. Clark 1961a9517958SDerek J. Clark static void msi_remove(struct hid_device *hdev) 1962a9517958SDerek J. Clark { 1963a9517958SDerek J. Clark int ret; 1964a9517958SDerek J. Clark u8 ep; 1965a9517958SDerek J. Clark 1966a9517958SDerek J. Clark /* Safe assumption. SET_INTERFACE ioctl can't be used while driver is bound */ 1967a9517958SDerek J. Clark ret = get_endpoint_address(hdev); 1968a9517958SDerek J. Clark if (ret <= 0) 1969a9517958SDerek J. Clark goto hw_stop; 1970a9517958SDerek J. Clark 1971a9517958SDerek J. Clark ep = ret; 1972a9517958SDerek J. Clark if (ep == CLAW_XINPUT_CFG_INTF_IN || ep == CLAW_DINPUT_CFG_INTF_IN) 1973a9517958SDerek J. Clark claw_remove(hdev); 1974a9517958SDerek J. Clark 1975a9517958SDerek J. Clark hw_stop: 1976a9517958SDerek J. Clark hid_hw_stop(hdev); 1977a9517958SDerek J. Clark } 1978a9517958SDerek J. Clark 1979a9517958SDerek J. Clark static int claw_resume(struct hid_device *hdev) 1980a9517958SDerek J. Clark { 1981a9517958SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 1982a9517958SDerek J. Clark 1983a9517958SDerek J. Clark if (!drvdata) 1984a9517958SDerek J. Clark return -ENODEV; 1985a9517958SDerek J. Clark 1986a9517958SDerek J. Clark /* MCU can take up to 500ms to be ready after resume */ 1987a9517958SDerek J. Clark schedule_delayed_work(&drvdata->cfg_resume, msecs_to_jiffies(500)); 1988a9517958SDerek J. Clark return 0; 1989a9517958SDerek J. Clark } 1990a9517958SDerek J. Clark 1991a9517958SDerek J. Clark static int msi_resume(struct hid_device *hdev) 1992a9517958SDerek J. Clark { 1993a9517958SDerek J. Clark int ret; 1994a9517958SDerek J. Clark u8 ep; 1995a9517958SDerek J. Clark 1996a9517958SDerek J. Clark /* Safe assumption. SET_INTERFACE ioctl can't be used while driver is bound */ 1997a9517958SDerek J. Clark ret = get_endpoint_address(hdev); 1998a9517958SDerek J. Clark if (ret <= 0) 1999a9517958SDerek J. Clark return 0; 2000a9517958SDerek J. Clark 2001a9517958SDerek J. Clark ep = ret; 2002a9517958SDerek J. Clark if (ep == CLAW_XINPUT_CFG_INTF_IN || ep == CLAW_DINPUT_CFG_INTF_IN) 2003a9517958SDerek J. Clark return claw_resume(hdev); 2004a9517958SDerek J. Clark 2005a9517958SDerek J. Clark return 0; 2006a9517958SDerek J. Clark } 2007a9517958SDerek J. Clark 2008a9517958SDerek J. Clark static int claw_suspend(struct hid_device *hdev) 2009a9517958SDerek J. Clark { 2010a9517958SDerek J. Clark struct claw_drvdata *drvdata = hid_get_drvdata(hdev); 2011a9517958SDerek J. Clark 2012a9517958SDerek J. Clark if (!drvdata) 2013a9517958SDerek J. Clark return -ENODEV; 2014a9517958SDerek J. Clark 2015a9517958SDerek J. Clark cancel_delayed_work_sync(&drvdata->cfg_resume); 2016a9517958SDerek J. Clark cancel_delayed_work_sync(&drvdata->cfg_setup); 2017f27e2e3cSDerek J. Clark cancel_delayed_work_sync(&drvdata->rgb_queue); 2018a9517958SDerek J. Clark 2019a9517958SDerek J. Clark return 0; 2020a9517958SDerek J. Clark } 2021a9517958SDerek J. Clark 2022a9517958SDerek J. Clark static int msi_suspend(struct hid_device *hdev, pm_message_t msg) 2023a9517958SDerek J. Clark { 2024a9517958SDerek J. Clark int ret; 2025a9517958SDerek J. Clark u8 ep; 2026a9517958SDerek J. Clark 2027a9517958SDerek J. Clark /* Safe assumption. SET_INTERFACE ioctl can't be used while driver is bound */ 2028a9517958SDerek J. Clark ret = get_endpoint_address(hdev); 2029a9517958SDerek J. Clark if (ret <= 0) 2030a9517958SDerek J. Clark return 0; 2031a9517958SDerek J. Clark 2032a9517958SDerek J. Clark ep = ret; 2033a9517958SDerek J. Clark if (ep == CLAW_XINPUT_CFG_INTF_IN || ep == CLAW_DINPUT_CFG_INTF_IN) 2034a9517958SDerek J. Clark return claw_suspend(hdev); 2035a9517958SDerek J. Clark 2036a9517958SDerek J. Clark return 0; 2037a9517958SDerek J. Clark } 2038a9517958SDerek J. Clark 2039a9517958SDerek J. Clark static const struct hid_device_id msi_devices[] = { 2040a9517958SDerek J. Clark { HID_USB_DEVICE(USB_VENDOR_ID_MSI_2, USB_DEVICE_ID_MSI_CLAW_XINPUT) }, 2041a9517958SDerek J. Clark { HID_USB_DEVICE(USB_VENDOR_ID_MSI_2, USB_DEVICE_ID_MSI_CLAW_DINPUT) }, 2042a9517958SDerek J. Clark { HID_USB_DEVICE(USB_VENDOR_ID_MSI_2, USB_DEVICE_ID_MSI_CLAW_DESKTOP) }, 2043a9517958SDerek J. Clark { HID_USB_DEVICE(USB_VENDOR_ID_MSI_2, USB_DEVICE_ID_MSI_CLAW_BIOS) }, 2044a9517958SDerek J. Clark { } 2045a9517958SDerek J. Clark }; 2046a9517958SDerek J. Clark MODULE_DEVICE_TABLE(hid, msi_devices); 2047a9517958SDerek J. Clark 2048a9517958SDerek J. Clark static struct hid_driver msi_driver = { 2049a9517958SDerek J. Clark .name = "hid-msi", 2050a9517958SDerek J. Clark .id_table = msi_devices, 2051a9517958SDerek J. Clark .raw_event = msi_raw_event, 2052a9517958SDerek J. Clark .probe = msi_probe, 2053a9517958SDerek J. Clark .remove = msi_remove, 2054a9517958SDerek J. Clark .resume = pm_ptr(msi_resume), 2055a9517958SDerek J. Clark .suspend = pm_ptr(msi_suspend), 2056a9517958SDerek J. Clark }; 2057a9517958SDerek J. Clark module_hid_driver(msi_driver); 2058a9517958SDerek J. Clark 2059a9517958SDerek J. Clark MODULE_LICENSE("GPL"); 2060a9517958SDerek J. Clark MODULE_AUTHOR("Denis Benato <denis.benato@linux.dev>"); 2061a9517958SDerek J. Clark MODULE_AUTHOR("Zhouwang Huang <honjow311@gmail.com>"); 2062a9517958SDerek J. Clark MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>"); 2063a9517958SDerek J. Clark MODULE_DESCRIPTION("HID driver for MSI Claw Handheld PC gamepads"); 2064