184910c45SDerek J. Clark // SPDX-License-Identifier: GPL-2.0-or-later 284910c45SDerek J. Clark /* 384910c45SDerek J. Clark * HID driver for OneXPlayer gamepad configuration devices. 484910c45SDerek J. Clark * 584910c45SDerek J. Clark * Copyright (c) 2026 Valve Corporation 684910c45SDerek J. Clark */ 784910c45SDerek J. Clark 884910c45SDerek J. Clark #include <linux/array_size.h> 984910c45SDerek J. Clark #include <linux/cleanup.h> 1084910c45SDerek J. Clark #include <linux/delay.h> 1184910c45SDerek J. Clark #include <linux/dev_printk.h> 1284910c45SDerek J. Clark #include <linux/device.h> 13252c4bf1SDerek J. Clark #include <linux/dmi.h> 1484910c45SDerek J. Clark #include <linux/hid.h> 1584910c45SDerek J. Clark #include <linux/jiffies.h> 1684910c45SDerek J. Clark #include <linux/kstrtox.h> 1784910c45SDerek J. Clark #include <linux/led-class-multicolor.h> 1884910c45SDerek J. Clark #include <linux/mutex.h> 1984910c45SDerek J. Clark #include <linux/sysfs.h> 2084910c45SDerek J. Clark #include <linux/types.h> 2184910c45SDerek J. Clark #include <linux/workqueue.h> 2284910c45SDerek J. Clark 2384910c45SDerek J. Clark #include "hid-ids.h" 2484910c45SDerek J. Clark 2584910c45SDerek J. Clark #define OXP_PACKET_SIZE 64 2684910c45SDerek J. Clark 2784910c45SDerek J. Clark #define GEN1_MESSAGE_ID 0xff 28252c4bf1SDerek J. Clark #define GEN2_MESSAGE_ID 0x3f 2984910c45SDerek J. Clark 3084910c45SDerek J. Clark #define GEN1_USAGE_PAGE 0xff01 31252c4bf1SDerek J. Clark #define GEN2_USAGE_PAGE 0xff00 3284910c45SDerek J. Clark 3384910c45SDerek J. Clark enum oxp_function_index { 3484910c45SDerek J. Clark OXP_FID_GEN1_RGB_SET = 0x07, 3584910c45SDerek J. Clark OXP_FID_GEN1_RGB_REPLY = 0x0f, 362f424f28SDerek J. Clark OXP_FID_GEN2_TOGGLE_MODE = 0xb2, 3799bde1dfSDerek J. Clark OXP_FID_GEN2_RUMBLE_SET = 0xb3, 38e4c850a6SDerek J. Clark OXP_FID_GEN2_KEY_STATE = 0xb4, 39252c4bf1SDerek J. Clark OXP_FID_GEN2_STATUS_EVENT = 0xb8, 4084910c45SDerek J. Clark }; 4184910c45SDerek J. Clark 42e4c850a6SDerek J. Clark #define OXP_MAPPING_GAMEPAD 0x01 43e4c850a6SDerek J. Clark #define OXP_MAPPING_KEYBOARD 0x02 44e4c850a6SDerek J. Clark 45e4c850a6SDerek J. Clark struct oxp_button_data { 46e4c850a6SDerek J. Clark u8 mode; 47e4c850a6SDerek J. Clark u8 index; 48e4c850a6SDerek J. Clark u8 key_id; 49e4c850a6SDerek J. Clark u8 padding[2]; 50e4c850a6SDerek J. Clark } __packed; 51e4c850a6SDerek J. Clark 52e4c850a6SDerek J. Clark struct oxp_button_entry { 53e4c850a6SDerek J. Clark struct oxp_button_data data; 54e4c850a6SDerek J. Clark const char *name; 55e4c850a6SDerek J. Clark }; 56e4c850a6SDerek J. Clark 57e4c850a6SDerek J. Clark static const struct oxp_button_entry oxp_button_table[] = { 58e4c850a6SDerek J. Clark /* Gamepad Buttons */ 59e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x01 }, "BTN_A" }, 60e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x02 }, "BTN_B" }, 61e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x03 }, "BTN_X" }, 62e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x04 }, "BTN_Y" }, 63e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x05 }, "BTN_LB" }, 64e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x06 }, "BTN_RB" }, 65e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x07 }, "BTN_LT" }, 66e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x08 }, "BTN_RT" }, 67e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x09 }, "BTN_START" }, 68e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x0a }, "BTN_SELECT" }, 69e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x0b }, "BTN_L3" }, 70e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x0c }, "BTN_R3" }, 71e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x0d }, "DPAD_UP" }, 72e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x0e }, "DPAD_DOWN" }, 73e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x0f }, "DPAD_LEFT" }, 74e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x10 }, "DPAD_RIGHT" }, 75e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x11 }, "JOY_L_UP" }, 76e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x12 }, "JOY_L_UP_RIGHT" }, 77e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x13 }, "JOY_L_RIGHT" }, 78e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x14 }, "JOY_L_DOWN_RIGHT" }, 79e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x15 }, "JOY_L_DOWN" }, 80e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x16 }, "JOY_L_DOWN_LEFT" }, 81e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x17 }, "JOY_L_LEFT" }, 82e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x18 }, "JOY_L_UP_LEFT" }, 83e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x19 }, "JOY_R_UP" }, 84e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x1a }, "JOY_R_UP_RIGHT" }, 85e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x1b }, "JOY_R_RIGHT" }, 86e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x1c }, "JOY_R_DOWN_RIGHT" }, 87e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x1d }, "JOY_R_DOWN" }, 88e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x1e }, "JOY_R_DOWN_LEFT" }, 89e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x1f }, "JOY_R_LEFT" }, 90e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x20 }, "JOY_R_UP_LEFT" }, 91e4c850a6SDerek J. Clark { { OXP_MAPPING_GAMEPAD, 0x22 }, "BTN_GUIDE" }, 92e4c850a6SDerek J. Clark /* Keyboard Keys */ 93e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x5a }, "KEY_F1" }, 94e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x5b }, "KEY_F2" }, 95e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x5c }, "KEY_F3" }, 96e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x5d }, "KEY_F4" }, 97e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x5e }, "KEY_F5" }, 98e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x5f }, "KEY_F6" }, 99e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x60 }, "KEY_F7" }, 100e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x61 }, "KEY_F8" }, 101e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x62 }, "KEY_F9" }, 102e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x63 }, "KEY_F10" }, 103e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x64 }, "KEY_F11" }, 104e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x65 }, "KEY_F12" }, 105e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x66 }, "KEY_F13" }, 106e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x67 }, "KEY_F14" }, 107e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x68 }, "KEY_F15" }, 108e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x69 }, "KEY_F16" }, 109e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x6a }, "KEY_F17" }, 110e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x6b }, "KEY_F18" }, 111e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x6c }, "KEY_F19" }, 112e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x6d }, "KEY_F20" }, 113e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x6e }, "KEY_F21" }, 114e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x6f }, "KEY_F22" }, 115e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x70 }, "KEY_F23" }, 116e4c850a6SDerek J. Clark { { OXP_MAPPING_KEYBOARD, 0x01, 0x71 }, "KEY_F24" }, 117e4c850a6SDerek J. Clark }; 118e4c850a6SDerek J. Clark 119e4c850a6SDerek J. Clark enum oxp_joybutton_index { 120e4c850a6SDerek J. Clark BUTTON_A = 0x01, 121e4c850a6SDerek J. Clark BUTTON_B, 122e4c850a6SDerek J. Clark BUTTON_X, 123e4c850a6SDerek J. Clark BUTTON_Y, 124e4c850a6SDerek J. Clark BUTTON_LB, 125e4c850a6SDerek J. Clark BUTTON_RB, 126e4c850a6SDerek J. Clark BUTTON_LT, 127e4c850a6SDerek J. Clark BUTTON_RT, 128e4c850a6SDerek J. Clark BUTTON_START, 129e4c850a6SDerek J. Clark BUTTON_SELECT, 130e4c850a6SDerek J. Clark BUTTON_L3, 131e4c850a6SDerek J. Clark BUTTON_R3, 132e4c850a6SDerek J. Clark BUTTON_DUP, 133e4c850a6SDerek J. Clark BUTTON_DDOWN, 134e4c850a6SDerek J. Clark BUTTON_DLEFT, 135e4c850a6SDerek J. Clark BUTTON_DRIGHT, 136e4c850a6SDerek J. Clark BUTTON_M1 = 0x22, 137e4c850a6SDerek J. Clark BUTTON_M2, 138e4c850a6SDerek J. Clark /* These are unused currently, reserved for future devices */ 139e4c850a6SDerek J. Clark BUTTON_M3, 140e4c850a6SDerek J. Clark BUTTON_M4, 141e4c850a6SDerek J. Clark BUTTON_M5, 142e4c850a6SDerek J. Clark BUTTON_M6, 143e4c850a6SDerek J. Clark }; 144e4c850a6SDerek J. Clark 145e4c850a6SDerek J. Clark struct oxp_button_idx { 146e4c850a6SDerek J. Clark enum oxp_joybutton_index button_idx; 147e4c850a6SDerek J. Clark u8 mapping_idx; 148e4c850a6SDerek J. Clark } __packed; 149e4c850a6SDerek J. Clark 150e4c850a6SDerek J. Clark struct oxp_bmap_page_1 { 151e4c850a6SDerek J. Clark struct oxp_button_idx btn_a; 152e4c850a6SDerek J. Clark struct oxp_button_idx btn_b; 153e4c850a6SDerek J. Clark struct oxp_button_idx btn_x; 154e4c850a6SDerek J. Clark struct oxp_button_idx btn_y; 155e4c850a6SDerek J. Clark struct oxp_button_idx btn_lb; 156e4c850a6SDerek J. Clark struct oxp_button_idx btn_rb; 157e4c850a6SDerek J. Clark struct oxp_button_idx btn_lt; 158e4c850a6SDerek J. Clark struct oxp_button_idx btn_rt; 159e4c850a6SDerek J. Clark struct oxp_button_idx btn_start; 160e4c850a6SDerek J. Clark } __packed; 161e4c850a6SDerek J. Clark 162e4c850a6SDerek J. Clark struct oxp_bmap_page_2 { 163e4c850a6SDerek J. Clark struct oxp_button_idx btn_select; 164e4c850a6SDerek J. Clark struct oxp_button_idx btn_l3; 165e4c850a6SDerek J. Clark struct oxp_button_idx btn_r3; 166e4c850a6SDerek J. Clark struct oxp_button_idx btn_dup; 167e4c850a6SDerek J. Clark struct oxp_button_idx btn_ddown; 168e4c850a6SDerek J. Clark struct oxp_button_idx btn_dleft; 169e4c850a6SDerek J. Clark struct oxp_button_idx btn_dright; 170e4c850a6SDerek J. Clark struct oxp_button_idx btn_m1; 171e4c850a6SDerek J. Clark struct oxp_button_idx btn_m2; 172e4c850a6SDerek J. Clark } __packed; 173e4c850a6SDerek J. Clark 17484910c45SDerek J. Clark static struct oxp_hid_cfg { 17584910c45SDerek J. Clark struct delayed_work oxp_rgb_queue; 176e4c850a6SDerek J. Clark struct delayed_work oxp_btn_queue; 177e4c850a6SDerek J. Clark struct oxp_bmap_page_1 *bmap_1; 178e4c850a6SDerek J. Clark struct oxp_bmap_page_2 *bmap_2; 1792f424f28SDerek J. Clark struct delayed_work oxp_mcu_init; 18084910c45SDerek J. Clark struct led_classdev_mc *led_mc; 18184910c45SDerek J. Clark struct hid_device *hdev; 18284910c45SDerek J. Clark struct mutex cfg_mutex; /*ensure single synchronous output report*/ 18384910c45SDerek J. Clark u8 rgb_brightness; 1842f424f28SDerek J. Clark u8 gamepad_mode; 18599bde1dfSDerek J. Clark u8 rumble_intensity; 18684910c45SDerek J. Clark u8 rgb_effect; 18784910c45SDerek J. Clark u8 rgb_speed; 18884910c45SDerek J. Clark u8 rgb_en; 18984910c45SDerek J. Clark } drvdata; 19084910c45SDerek J. Clark 191e4c850a6SDerek J. Clark #define OXP_FILL_PAGE_SLOT(page, btn) \ 192e4c850a6SDerek J. Clark { .button_idx = (page)->btn.button_idx, \ 193e4c850a6SDerek J. Clark .mapping_idx = (page)->btn.mapping_idx } 194e4c850a6SDerek J. Clark 1952f424f28SDerek J. Clark enum oxp_gamepad_mode_index { 1962f424f28SDerek J. Clark OXP_GP_MODE_XINPUT = 0x00, 1972f424f28SDerek J. Clark OXP_GP_MODE_DEBUG = 0x03, 1982f424f28SDerek J. Clark }; 1992f424f28SDerek J. Clark 2002f424f28SDerek J. Clark static const char *const oxp_gamepad_mode_text[] = { 2012f424f28SDerek J. Clark [OXP_GP_MODE_XINPUT] = "xinput", 2022f424f28SDerek J. Clark [OXP_GP_MODE_DEBUG] = "debug", 2032f424f28SDerek J. Clark }; 2042f424f28SDerek J. Clark 20584910c45SDerek J. Clark enum oxp_feature_en_index { 20684910c45SDerek J. Clark OXP_FEAT_DISABLED, 20784910c45SDerek J. Clark OXP_FEAT_ENABLED, 20884910c45SDerek J. Clark }; 20984910c45SDerek J. Clark 21084910c45SDerek J. Clark static const char *const oxp_feature_en_text[] = { 21184910c45SDerek J. Clark [OXP_FEAT_DISABLED] = "false", 21284910c45SDerek J. Clark [OXP_FEAT_ENABLED] = "true", 21384910c45SDerek J. Clark }; 21484910c45SDerek J. Clark 21584910c45SDerek J. Clark enum oxp_rgb_effect_index { 21684910c45SDerek J. Clark OXP_UNKNOWN, 21784910c45SDerek J. Clark OXP_EFFECT_AURORA, 21884910c45SDerek J. Clark OXP_EFFECT_BIRTHDAY, 21984910c45SDerek J. Clark OXP_EFFECT_FLOWING, 22084910c45SDerek J. Clark OXP_EFFECT_CHROMA_1, 22184910c45SDerek J. Clark OXP_EFFECT_NEON, 22284910c45SDerek J. Clark OXP_EFFECT_CHROMA_2, 22384910c45SDerek J. Clark OXP_EFFECT_DREAMY, 22484910c45SDerek J. Clark OXP_EFFECT_WARM, 22584910c45SDerek J. Clark OXP_EFFECT_CYBERPUNK, 22684910c45SDerek J. Clark OXP_EFFECT_SEA, 22784910c45SDerek J. Clark OXP_EFFECT_SUNSET, 22884910c45SDerek J. Clark OXP_EFFECT_COLORFUL, 22984910c45SDerek J. Clark OXP_EFFECT_MONSTER, 23084910c45SDerek J. Clark OXP_EFFECT_GREEN, 23184910c45SDerek J. Clark OXP_EFFECT_BLUE, 23284910c45SDerek J. Clark OXP_EFFECT_YELLOW, 23384910c45SDerek J. Clark OXP_EFFECT_TEAL, 23484910c45SDerek J. Clark OXP_EFFECT_PURPLE, 23584910c45SDerek J. Clark OXP_EFFECT_FOGGY, 23684910c45SDerek J. Clark OXP_EFFECT_MONO_LIST, /* placeholder for effect_index_show */ 23784910c45SDerek J. Clark }; 23884910c45SDerek J. Clark 23984910c45SDerek J. Clark /* These belong to rgb_effect_index, but we want to hide them from 24084910c45SDerek J. Clark * rgb_effect_text 24184910c45SDerek J. Clark */ 24284910c45SDerek J. Clark 24384910c45SDerek J. Clark #define OXP_GET_PROPERTY 0xfc 24484910c45SDerek J. Clark #define OXP_SET_PROPERTY 0xfd 24584910c45SDerek J. Clark #define OXP_EFFECT_MONO_TRUE 0xfe /* actual index for monocolor */ 24684910c45SDerek J. Clark 24784910c45SDerek J. Clark static const char *const oxp_rgb_effect_text[] = { 24884910c45SDerek J. Clark [OXP_UNKNOWN] = "unknown", 24984910c45SDerek J. Clark [OXP_EFFECT_AURORA] = "aurora", 25084910c45SDerek J. Clark [OXP_EFFECT_BIRTHDAY] = "birthday_cake", 25184910c45SDerek J. Clark [OXP_EFFECT_FLOWING] = "flowing_light", 25284910c45SDerek J. Clark [OXP_EFFECT_CHROMA_1] = "chroma_popping", 25384910c45SDerek J. Clark [OXP_EFFECT_NEON] = "neon", 25484910c45SDerek J. Clark [OXP_EFFECT_CHROMA_2] = "chroma_breathing", 25584910c45SDerek J. Clark [OXP_EFFECT_DREAMY] = "dreamy", 25684910c45SDerek J. Clark [OXP_EFFECT_WARM] = "warm_sun", 25784910c45SDerek J. Clark [OXP_EFFECT_CYBERPUNK] = "cyberpunk", 25884910c45SDerek J. Clark [OXP_EFFECT_SEA] = "sea_foam", 25984910c45SDerek J. Clark [OXP_EFFECT_SUNSET] = "sunset_afterglow", 26084910c45SDerek J. Clark [OXP_EFFECT_COLORFUL] = "colorful", 26184910c45SDerek J. Clark [OXP_EFFECT_MONSTER] = "monster_woke", 26284910c45SDerek J. Clark [OXP_EFFECT_GREEN] = "green_breathing", 26384910c45SDerek J. Clark [OXP_EFFECT_BLUE] = "blue_breathing", 26484910c45SDerek J. Clark [OXP_EFFECT_YELLOW] = "yellow_breathing", 26584910c45SDerek J. Clark [OXP_EFFECT_TEAL] = "teal_breathing", 26684910c45SDerek J. Clark [OXP_EFFECT_PURPLE] = "purple_breathing", 26784910c45SDerek J. Clark [OXP_EFFECT_FOGGY] = "foggy_haze", 26884910c45SDerek J. Clark [OXP_EFFECT_MONO_LIST] = "monocolor", 26984910c45SDerek J. Clark }; 27084910c45SDerek J. Clark 27199bde1dfSDerek J. Clark enum oxp_rumble_side_index { 27299bde1dfSDerek J. Clark OXP_RUMBLE_LEFT = 0x00, 27399bde1dfSDerek J. Clark OXP_RUMBLE_RIGHT, 27499bde1dfSDerek J. Clark }; 27599bde1dfSDerek J. Clark 27684910c45SDerek J. Clark struct oxp_gen_1_rgb_report { 27784910c45SDerek J. Clark u8 report_id; 27884910c45SDerek J. Clark u8 message_id; 27984910c45SDerek J. Clark u8 padding_2[2]; 28084910c45SDerek J. Clark u8 effect; 28184910c45SDerek J. Clark u8 enabled; 28284910c45SDerek J. Clark u8 speed; 28384910c45SDerek J. Clark u8 brightness; 28484910c45SDerek J. Clark u8 red; 28584910c45SDerek J. Clark u8 green; 28684910c45SDerek J. Clark u8 blue; 28784910c45SDerek J. Clark } __packed; 28884910c45SDerek J. Clark 289252c4bf1SDerek J. Clark struct oxp_gen_2_rgb_report { 290252c4bf1SDerek J. Clark u8 report_id; 291252c4bf1SDerek J. Clark u8 header_id; 292252c4bf1SDerek J. Clark u8 padding_2; 293252c4bf1SDerek J. Clark u8 message_id; 294252c4bf1SDerek J. Clark u8 padding_4[2]; 295252c4bf1SDerek J. Clark u8 enabled; 296252c4bf1SDerek J. Clark u8 speed; 297252c4bf1SDerek J. Clark u8 brightness; 298252c4bf1SDerek J. Clark u8 red; 299252c4bf1SDerek J. Clark u8 green; 300252c4bf1SDerek J. Clark u8 blue; 301252c4bf1SDerek J. Clark u8 padding_12[3]; 302252c4bf1SDerek J. Clark u8 effect; 303252c4bf1SDerek J. Clark } __packed; 304252c4bf1SDerek J. Clark 305e4c850a6SDerek J. Clark struct oxp_attr { 306e4c850a6SDerek J. Clark u8 index; 307e4c850a6SDerek J. Clark }; 308e4c850a6SDerek J. Clark 30984910c45SDerek J. Clark static u16 get_usage_page(struct hid_device *hdev) 31084910c45SDerek J. Clark { 31184910c45SDerek J. Clark return hdev->collection[0].usage >> 16; 31284910c45SDerek J. Clark } 31384910c45SDerek J. Clark 31484910c45SDerek J. Clark static int oxp_hid_raw_event_gen_1(struct hid_device *hdev, 31584910c45SDerek J. Clark struct hid_report *report, u8 *data, 31684910c45SDerek J. Clark int size) 31784910c45SDerek J. Clark { 31884910c45SDerek J. Clark struct led_classdev_mc *led_mc = drvdata.led_mc; 31984910c45SDerek J. Clark struct oxp_gen_1_rgb_report *rgb_rep; 32084910c45SDerek J. Clark 32184910c45SDerek J. Clark if (data[1] != OXP_FID_GEN1_RGB_REPLY) 32284910c45SDerek J. Clark return 0; 32384910c45SDerek J. Clark 32484910c45SDerek J. Clark rgb_rep = (struct oxp_gen_1_rgb_report *)data; 32584910c45SDerek J. Clark /* Ensure we save monocolor as the list value */ 32684910c45SDerek J. Clark drvdata.rgb_effect = rgb_rep->effect == OXP_EFFECT_MONO_TRUE ? 32784910c45SDerek J. Clark OXP_EFFECT_MONO_LIST : 32884910c45SDerek J. Clark rgb_rep->effect; 32984910c45SDerek J. Clark drvdata.rgb_speed = rgb_rep->speed; 33084910c45SDerek J. Clark drvdata.rgb_en = rgb_rep->enabled == 0 ? OXP_FEAT_DISABLED : 33184910c45SDerek J. Clark OXP_FEAT_ENABLED; 33284910c45SDerek J. Clark drvdata.rgb_brightness = rgb_rep->brightness; 33384910c45SDerek J. Clark led_mc->led_cdev.brightness = rgb_rep->brightness / 4 * 33484910c45SDerek J. Clark led_mc->led_cdev.max_brightness; 33584910c45SDerek J. Clark /* If monocolor had less than 100% brightness on the previous boot, 33684910c45SDerek J. Clark * there will be no reliable way to determine the real intensity. 33784910c45SDerek J. Clark * Since intensity scaling is used with a hardware brightness set at max, 33884910c45SDerek J. Clark * our brightness will always look like 100%. Use the last set value to 33984910c45SDerek J. Clark * prevent successive boots from lowering the brightness further. 34084910c45SDerek J. Clark * Brightness will be "wrong" but the effect will remain the same visually. 34184910c45SDerek J. Clark */ 34284910c45SDerek J. Clark led_mc->subled_info[0].intensity = rgb_rep->red; 34384910c45SDerek J. Clark led_mc->subled_info[1].intensity = rgb_rep->green; 34484910c45SDerek J. Clark led_mc->subled_info[2].intensity = rgb_rep->blue; 34584910c45SDerek J. Clark 34684910c45SDerek J. Clark return 0; 34784910c45SDerek J. Clark } 34884910c45SDerek J. Clark 3492f424f28SDerek J. Clark static int oxp_gen_2_property_out(enum oxp_function_index fid, u8 *data, u8 data_size); 350e4c850a6SDerek J. Clark static int oxp_set_buttons(void); 35199bde1dfSDerek J. Clark static int oxp_rumble_intensity_set(u8 intensity); 3522f424f28SDerek J. Clark 3532f424f28SDerek J. Clark static void oxp_mcu_init_fn(struct work_struct *work) 3542f424f28SDerek J. Clark { 3552f424f28SDerek J. Clark u8 gp_mode_data[3] = { OXP_GP_MODE_DEBUG, 0x01, 0x02 }; 3562f424f28SDerek J. Clark int ret; 3572f424f28SDerek J. Clark 358e4c850a6SDerek J. Clark /* Re-apply the button mapping */ 359e4c850a6SDerek J. Clark ret = oxp_set_buttons(); 360e4c850a6SDerek J. Clark if (ret) 361e4c850a6SDerek J. Clark dev_err(&drvdata.hdev->dev, 362e4c850a6SDerek J. Clark "Error: Failed to set button mapping: %i\n", ret); 363e4c850a6SDerek J. Clark 3642f424f28SDerek J. Clark /* Cycle the gamepad mode */ 3652f424f28SDerek J. Clark ret = oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, gp_mode_data, 3); 3662f424f28SDerek J. Clark if (ret) 3672f424f28SDerek J. Clark dev_err(&drvdata.hdev->dev, 3682f424f28SDerek J. Clark "Error: Failed to set gamepad mode: %i\n", ret); 3692f424f28SDerek J. Clark 3702f424f28SDerek J. Clark /* Remainder only applies for xinput mode */ 3712f424f28SDerek J. Clark if (drvdata.gamepad_mode == OXP_GP_MODE_DEBUG) 3722f424f28SDerek J. Clark return; 3732f424f28SDerek J. Clark 3742f424f28SDerek J. Clark gp_mode_data[0] = OXP_GP_MODE_XINPUT; 3752f424f28SDerek J. Clark ret = oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, gp_mode_data, 3); 3762f424f28SDerek J. Clark if (ret) 3772f424f28SDerek J. Clark dev_err(&drvdata.hdev->dev, 3782f424f28SDerek J. Clark "Error: Failed to set gamepad mode: %i\n", ret); 37999bde1dfSDerek J. Clark 38099bde1dfSDerek J. Clark /* Set vibration level */ 38199bde1dfSDerek J. Clark ret = oxp_rumble_intensity_set(drvdata.rumble_intensity); 38299bde1dfSDerek J. Clark if (ret) 38399bde1dfSDerek J. Clark dev_err(&drvdata.hdev->dev, 38499bde1dfSDerek J. Clark "Error: Failed to set rumble intensity: %i\n", ret); 3852f424f28SDerek J. Clark } 3862f424f28SDerek J. Clark 387252c4bf1SDerek J. Clark static int oxp_hid_raw_event_gen_2(struct hid_device *hdev, 388252c4bf1SDerek J. Clark struct hid_report *report, u8 *data, 389252c4bf1SDerek J. Clark int size) 390252c4bf1SDerek J. Clark { 391252c4bf1SDerek J. Clark struct led_classdev_mc *led_mc = drvdata.led_mc; 392252c4bf1SDerek J. Clark struct oxp_gen_2_rgb_report *rgb_rep; 393252c4bf1SDerek J. Clark 394252c4bf1SDerek J. Clark if (data[0] != OXP_FID_GEN2_STATUS_EVENT) 395252c4bf1SDerek J. Clark return 0; 396252c4bf1SDerek J. Clark 3972f424f28SDerek J. Clark /* Sent ~6s after resume event, indicating the MCU has fully reset. 3982f424f28SDerek J. Clark * Re-apply our settings after this has been received. 3992f424f28SDerek J. Clark */ 4002f424f28SDerek J. Clark if (data[3] == OXP_EFFECT_MONO_TRUE) { 401*8bb7c0fdSMarco Crivellari mod_delayed_work(system_dfl_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50)); 4022f424f28SDerek J. Clark return 0; 4032f424f28SDerek J. Clark } 4042f424f28SDerek J. Clark 405252c4bf1SDerek J. Clark if (data[3] != OXP_GET_PROPERTY) 406252c4bf1SDerek J. Clark return 0; 407252c4bf1SDerek J. Clark 408252c4bf1SDerek J. Clark rgb_rep = (struct oxp_gen_2_rgb_report *)data; 409252c4bf1SDerek J. Clark /* Ensure we save monocolor as the list value */ 410252c4bf1SDerek J. Clark drvdata.rgb_effect = rgb_rep->effect == OXP_EFFECT_MONO_TRUE ? 411252c4bf1SDerek J. Clark OXP_EFFECT_MONO_LIST : 412252c4bf1SDerek J. Clark rgb_rep->effect; 413252c4bf1SDerek J. Clark drvdata.rgb_speed = rgb_rep->speed; 414252c4bf1SDerek J. Clark drvdata.rgb_en = rgb_rep->enabled == 0 ? OXP_FEAT_DISABLED : 415252c4bf1SDerek J. Clark OXP_FEAT_ENABLED; 416252c4bf1SDerek J. Clark drvdata.rgb_brightness = rgb_rep->brightness; 417252c4bf1SDerek J. Clark led_mc->led_cdev.brightness = rgb_rep->brightness / 4 * 418252c4bf1SDerek J. Clark led_mc->led_cdev.max_brightness; 419252c4bf1SDerek J. Clark /* If monocolor had less than 100% brightness on the previous boot, 420252c4bf1SDerek J. Clark * there will be no reliable way to determine the real intensity. 421252c4bf1SDerek J. Clark * Since intensity scaling is used with a hardware brightness set at max, 422252c4bf1SDerek J. Clark * our brightness will always look like 100%. Use the last set value to 423252c4bf1SDerek J. Clark * prevent successive boots from lowering the brightness further. 424252c4bf1SDerek J. Clark * Brightness will be "wrong" but the effect will remain the same visually. 425252c4bf1SDerek J. Clark */ 426252c4bf1SDerek J. Clark led_mc->subled_info[0].intensity = rgb_rep->red; 427252c4bf1SDerek J. Clark led_mc->subled_info[1].intensity = rgb_rep->green; 428252c4bf1SDerek J. Clark led_mc->subled_info[2].intensity = rgb_rep->blue; 429252c4bf1SDerek J. Clark 430252c4bf1SDerek J. Clark return 0; 431252c4bf1SDerek J. Clark } 432252c4bf1SDerek J. Clark 43384910c45SDerek J. Clark static int oxp_hid_raw_event(struct hid_device *hdev, struct hid_report *report, 43484910c45SDerek J. Clark u8 *data, int size) 43584910c45SDerek J. Clark { 43684910c45SDerek J. Clark u16 up = get_usage_page(hdev); 43784910c45SDerek J. Clark 43884910c45SDerek J. Clark dev_dbg(&hdev->dev, "raw event data: [%*ph]\n", OXP_PACKET_SIZE, data); 43984910c45SDerek J. Clark 44084910c45SDerek J. Clark switch (up) { 44184910c45SDerek J. Clark case GEN1_USAGE_PAGE: 44284910c45SDerek J. Clark return oxp_hid_raw_event_gen_1(hdev, report, data, size); 443252c4bf1SDerek J. Clark case GEN2_USAGE_PAGE: 444252c4bf1SDerek J. Clark return oxp_hid_raw_event_gen_2(hdev, report, data, size); 44584910c45SDerek J. Clark default: 44684910c45SDerek J. Clark break; 44784910c45SDerek J. Clark } 44884910c45SDerek J. Clark 44984910c45SDerek J. Clark return 0; 45084910c45SDerek J. Clark } 45184910c45SDerek J. Clark 45284910c45SDerek J. Clark static int mcu_property_out(u8 *header, size_t header_size, u8 *data, 45384910c45SDerek J. Clark size_t data_size, u8 *footer, size_t footer_size) 45484910c45SDerek J. Clark { 45584910c45SDerek J. Clark unsigned char *dmabuf __free(kfree) = kzalloc(OXP_PACKET_SIZE, GFP_KERNEL); 45684910c45SDerek J. Clark int ret; 45784910c45SDerek J. Clark 45884910c45SDerek J. Clark if (!dmabuf) 45984910c45SDerek J. Clark return -ENOMEM; 46084910c45SDerek J. Clark 46184910c45SDerek J. Clark if (header_size + data_size + footer_size > OXP_PACKET_SIZE) 46284910c45SDerek J. Clark return -EINVAL; 46384910c45SDerek J. Clark 46484910c45SDerek J. Clark guard(mutex)(&drvdata.cfg_mutex); 46584910c45SDerek J. Clark memcpy(dmabuf, header, header_size); 46684910c45SDerek J. Clark memcpy(dmabuf + header_size, data, data_size); 46784910c45SDerek J. Clark if (footer_size) 46884910c45SDerek J. Clark memcpy(dmabuf + OXP_PACKET_SIZE - footer_size, footer, footer_size); 46984910c45SDerek J. Clark 47084910c45SDerek J. Clark dev_dbg(&drvdata.hdev->dev, "raw data: [%*ph]\n", OXP_PACKET_SIZE, dmabuf); 47184910c45SDerek J. Clark 47284910c45SDerek J. Clark ret = hid_hw_output_report(drvdata.hdev, dmabuf, OXP_PACKET_SIZE); 47384910c45SDerek J. Clark if (ret < 0) 47484910c45SDerek J. Clark return ret; 47584910c45SDerek J. Clark 47684910c45SDerek J. Clark /* MCU takes 200ms to be ready for another command. */ 47784910c45SDerek J. Clark msleep(200); 47884910c45SDerek J. Clark return ret == OXP_PACKET_SIZE ? 0 : -EIO; 47984910c45SDerek J. Clark } 48084910c45SDerek J. Clark 48184910c45SDerek J. Clark static int oxp_gen_1_property_out(enum oxp_function_index fid, u8 *data, 48284910c45SDerek J. Clark u8 data_size) 48384910c45SDerek J. Clark { 48484910c45SDerek J. Clark u8 header[] = { fid, GEN1_MESSAGE_ID }; 48584910c45SDerek J. Clark size_t header_size = ARRAY_SIZE(header); 48684910c45SDerek J. Clark 48784910c45SDerek J. Clark return mcu_property_out(header, header_size, data, data_size, NULL, 0); 48884910c45SDerek J. Clark } 48984910c45SDerek J. Clark 490252c4bf1SDerek J. Clark static int oxp_gen_2_property_out(enum oxp_function_index fid, u8 *data, 491252c4bf1SDerek J. Clark u8 data_size) 492252c4bf1SDerek J. Clark { 493252c4bf1SDerek J. Clark u8 header[] = { fid, GEN2_MESSAGE_ID, 0x01 }; 494252c4bf1SDerek J. Clark u8 footer[] = { GEN2_MESSAGE_ID, fid }; 495252c4bf1SDerek J. Clark size_t header_size = ARRAY_SIZE(header); 496252c4bf1SDerek J. Clark size_t footer_size = ARRAY_SIZE(footer); 497252c4bf1SDerek J. Clark 498252c4bf1SDerek J. Clark return mcu_property_out(header, header_size, data, data_size, footer, 499252c4bf1SDerek J. Clark footer_size); 500252c4bf1SDerek J. Clark } 501252c4bf1SDerek J. Clark 5022f424f28SDerek J. Clark static ssize_t gamepad_mode_store(struct device *dev, 5032f424f28SDerek J. Clark struct device_attribute *attr, const char *buf, 5042f424f28SDerek J. Clark size_t count) 5052f424f28SDerek J. Clark { 5062f424f28SDerek J. Clark u16 up = get_usage_page(drvdata.hdev); 5072f424f28SDerek J. Clark u8 data[3] = { 0x00, 0x01, 0x02 }; 5082f424f28SDerek J. Clark int ret = -EINVAL; 5092f424f28SDerek J. Clark int i; 5102f424f28SDerek J. Clark 5112f424f28SDerek J. Clark if (up != GEN2_USAGE_PAGE) 5122f424f28SDerek J. Clark return ret; 5132f424f28SDerek J. Clark 5142f424f28SDerek J. Clark for (i = 0; i < ARRAY_SIZE(oxp_gamepad_mode_text); i++) { 5152f424f28SDerek J. Clark if (oxp_gamepad_mode_text[i] && sysfs_streq(buf, oxp_gamepad_mode_text[i])) { 5162f424f28SDerek J. Clark ret = i; 5172f424f28SDerek J. Clark break; 5182f424f28SDerek J. Clark } 5192f424f28SDerek J. Clark } 5202f424f28SDerek J. Clark if (ret < 0) 5212f424f28SDerek J. Clark return ret; 5222f424f28SDerek J. Clark 5232f424f28SDerek J. Clark data[0] = ret; 5242f424f28SDerek J. Clark 5252f424f28SDerek J. Clark ret = oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, data, 3); 5262f424f28SDerek J. Clark if (ret) 5272f424f28SDerek J. Clark return ret; 5282f424f28SDerek J. Clark 5292f424f28SDerek J. Clark drvdata.gamepad_mode = data[0]; 5302f424f28SDerek J. Clark 53199bde1dfSDerek J. Clark if (drvdata.gamepad_mode == OXP_GP_MODE_DEBUG) 53299bde1dfSDerek J. Clark return count; 53399bde1dfSDerek J. Clark 53499bde1dfSDerek J. Clark /* Re-apply rumble settings as switching gamepad mode will override */ 53599bde1dfSDerek J. Clark ret = oxp_rumble_intensity_set(drvdata.rumble_intensity); 53699bde1dfSDerek J. Clark if (ret) 53799bde1dfSDerek J. Clark return ret; 53899bde1dfSDerek J. Clark 5392f424f28SDerek J. Clark return count; 5402f424f28SDerek J. Clark } 5412f424f28SDerek J. Clark 5422f424f28SDerek J. Clark static ssize_t gamepad_mode_show(struct device *dev, 5432f424f28SDerek J. Clark struct device_attribute *attr, char *buf) 5442f424f28SDerek J. Clark { 5452f424f28SDerek J. Clark return sysfs_emit(buf, "%s\n", oxp_gamepad_mode_text[drvdata.gamepad_mode]); 5462f424f28SDerek J. Clark } 5472f424f28SDerek J. Clark static DEVICE_ATTR_RW(gamepad_mode); 5482f424f28SDerek J. Clark 5492f424f28SDerek J. Clark static ssize_t gamepad_mode_index_show(struct device *dev, 5502f424f28SDerek J. Clark struct device_attribute *attr, 5512f424f28SDerek J. Clark char *buf) 5522f424f28SDerek J. Clark { 5532f424f28SDerek J. Clark ssize_t count = 0; 5542f424f28SDerek J. Clark unsigned int i; 5552f424f28SDerek J. Clark 5562f424f28SDerek J. Clark for (i = 0; i < ARRAY_SIZE(oxp_gamepad_mode_text); i++) { 5572f424f28SDerek J. Clark if (!oxp_gamepad_mode_text[i] || 5582f424f28SDerek J. Clark oxp_gamepad_mode_text[i][0] == '\0') 5592f424f28SDerek J. Clark continue; 5602f424f28SDerek J. Clark 5612f424f28SDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", oxp_gamepad_mode_text[i]); 5622f424f28SDerek J. Clark } 5632f424f28SDerek J. Clark 5642f424f28SDerek J. Clark if (count) 5652f424f28SDerek J. Clark buf[count - 1] = '\n'; 5662f424f28SDerek J. Clark 5672f424f28SDerek J. Clark return count; 5682f424f28SDerek J. Clark } 5692f424f28SDerek J. Clark static DEVICE_ATTR_RO(gamepad_mode_index); 5702f424f28SDerek J. Clark 571e4c850a6SDerek J. Clark static void oxp_set_defaults_bmap_1(struct oxp_bmap_page_1 *bmap) 572e4c850a6SDerek J. Clark { 573e4c850a6SDerek J. Clark bmap->btn_a.button_idx = BUTTON_A; 574e4c850a6SDerek J. Clark bmap->btn_a.mapping_idx = 0; 575e4c850a6SDerek J. Clark bmap->btn_b.button_idx = BUTTON_B; 576e4c850a6SDerek J. Clark bmap->btn_b.mapping_idx = 1; 577e4c850a6SDerek J. Clark bmap->btn_x.button_idx = BUTTON_X; 578e4c850a6SDerek J. Clark bmap->btn_x.mapping_idx = 2; 579e4c850a6SDerek J. Clark bmap->btn_y.button_idx = BUTTON_Y; 580e4c850a6SDerek J. Clark bmap->btn_y.mapping_idx = 3; 581e4c850a6SDerek J. Clark bmap->btn_lb.button_idx = BUTTON_LB; 582e4c850a6SDerek J. Clark bmap->btn_lb.mapping_idx = 4; 583e4c850a6SDerek J. Clark bmap->btn_rb.button_idx = BUTTON_RB; 584e4c850a6SDerek J. Clark bmap->btn_rb.mapping_idx = 5; 585e4c850a6SDerek J. Clark bmap->btn_lt.button_idx = BUTTON_LT; 586e4c850a6SDerek J. Clark bmap->btn_lt.mapping_idx = 6; 587e4c850a6SDerek J. Clark bmap->btn_rt.button_idx = BUTTON_RT; 588e4c850a6SDerek J. Clark bmap->btn_rt.mapping_idx = 7; 589e4c850a6SDerek J. Clark bmap->btn_start.button_idx = BUTTON_START; 590e4c850a6SDerek J. Clark bmap->btn_start.mapping_idx = 8; 591e4c850a6SDerek J. Clark } 592e4c850a6SDerek J. Clark 593e4c850a6SDerek J. Clark static void oxp_set_defaults_bmap_2(struct oxp_bmap_page_2 *bmap) 594e4c850a6SDerek J. Clark { 595e4c850a6SDerek J. Clark bmap->btn_select.button_idx = BUTTON_SELECT; 596e4c850a6SDerek J. Clark bmap->btn_select.mapping_idx = 9; 597e4c850a6SDerek J. Clark bmap->btn_l3.button_idx = BUTTON_L3; 598e4c850a6SDerek J. Clark bmap->btn_l3.mapping_idx = 10; 599e4c850a6SDerek J. Clark bmap->btn_r3.button_idx = BUTTON_R3; 600e4c850a6SDerek J. Clark bmap->btn_r3.mapping_idx = 11; 601e4c850a6SDerek J. Clark bmap->btn_dup.button_idx = BUTTON_DUP; 602e4c850a6SDerek J. Clark bmap->btn_dup.mapping_idx = 12; 603e4c850a6SDerek J. Clark bmap->btn_ddown.button_idx = BUTTON_DDOWN; 604e4c850a6SDerek J. Clark bmap->btn_ddown.mapping_idx = 13; 605e4c850a6SDerek J. Clark bmap->btn_dleft.button_idx = BUTTON_DLEFT; 606e4c850a6SDerek J. Clark bmap->btn_dleft.mapping_idx = 14; 607e4c850a6SDerek J. Clark bmap->btn_dright.button_idx = BUTTON_DRIGHT; 608e4c850a6SDerek J. Clark bmap->btn_dright.mapping_idx = 15; 609e4c850a6SDerek J. Clark bmap->btn_m1.button_idx = BUTTON_M1; 610e4c850a6SDerek J. Clark bmap->btn_m1.mapping_idx = 48; /* KEY_F15 */ 611e4c850a6SDerek J. Clark bmap->btn_m2.button_idx = BUTTON_M2; 612e4c850a6SDerek J. Clark bmap->btn_m2.mapping_idx = 49; /* KEY_F16 */ 613e4c850a6SDerek J. Clark } 614e4c850a6SDerek J. Clark 615e4c850a6SDerek J. Clark static void oxp_page_fill_data(char *buf, const struct oxp_button_idx *buttons, 616e4c850a6SDerek J. Clark size_t len) 617e4c850a6SDerek J. Clark { 618e4c850a6SDerek J. Clark size_t offset_increment = sizeof(u8) + sizeof(struct oxp_button_idx); 619e4c850a6SDerek J. Clark size_t offset = 5; 620e4c850a6SDerek J. Clark unsigned int i; 621e4c850a6SDerek J. Clark 622e4c850a6SDerek J. Clark for (i = 0; i < len; i++, offset += offset_increment) { 623e4c850a6SDerek J. Clark buf[offset] = (u8)buttons[i].button_idx; 624e4c850a6SDerek J. Clark memcpy(buf + offset + 1, 625e4c850a6SDerek J. Clark &oxp_button_table[buttons[i].mapping_idx].data, 626e4c850a6SDerek J. Clark sizeof(struct oxp_button_data)); 627e4c850a6SDerek J. Clark } 628e4c850a6SDerek J. Clark } 629e4c850a6SDerek J. Clark 630e4c850a6SDerek J. Clark static int oxp_set_buttons(void) 631e4c850a6SDerek J. Clark { 632e4c850a6SDerek J. Clark u8 page_1[59] = { 0x02, 0x38, 0x20, 0x01, 0x01 }; 633e4c850a6SDerek J. Clark u8 page_2[59] = { 0x02, 0x38, 0x20, 0x02, 0x01 }; 634e4c850a6SDerek J. Clark u16 up = get_usage_page(drvdata.hdev); 635e4c850a6SDerek J. Clark int ret; 636e4c850a6SDerek J. Clark 637e4c850a6SDerek J. Clark if (up != GEN2_USAGE_PAGE) 638e4c850a6SDerek J. Clark return -EINVAL; 639e4c850a6SDerek J. Clark 640e4c850a6SDerek J. Clark const struct oxp_button_idx p1[] = { 641e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_a), 642e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_b), 643e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_x), 644e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_y), 645e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_lb), 646e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_rb), 647e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_lt), 648e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_rt), 649e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_start), 650e4c850a6SDerek J. Clark }; 651e4c850a6SDerek J. Clark 652e4c850a6SDerek J. Clark const struct oxp_button_idx p2[] = { 653e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_select), 654e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_l3), 655e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_r3), 656e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_dup), 657e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_ddown), 658e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_dleft), 659e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_dright), 660e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_m1), 661e4c850a6SDerek J. Clark OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_m2), 662e4c850a6SDerek J. Clark }; 663e4c850a6SDerek J. Clark 664e4c850a6SDerek J. Clark oxp_page_fill_data(page_1, p1, ARRAY_SIZE(p1)); 665e4c850a6SDerek J. Clark oxp_page_fill_data(page_2, p2, ARRAY_SIZE(p2)); 666e4c850a6SDerek J. Clark 667e4c850a6SDerek J. Clark ret = oxp_gen_2_property_out(OXP_FID_GEN2_KEY_STATE, page_1, ARRAY_SIZE(page_1)); 668e4c850a6SDerek J. Clark if (ret) 669e4c850a6SDerek J. Clark return ret; 670e4c850a6SDerek J. Clark 671e4c850a6SDerek J. Clark return oxp_gen_2_property_out(OXP_FID_GEN2_KEY_STATE, page_2, ARRAY_SIZE(page_2)); 672e4c850a6SDerek J. Clark } 673e4c850a6SDerek J. Clark 674e4c850a6SDerek J. Clark static void oxp_reset_buttons(void) 675e4c850a6SDerek J. Clark { 676e4c850a6SDerek J. Clark oxp_set_defaults_bmap_1(drvdata.bmap_1); 677e4c850a6SDerek J. Clark oxp_set_defaults_bmap_2(drvdata.bmap_2); 678e4c850a6SDerek J. Clark } 679e4c850a6SDerek J. Clark 680e4c850a6SDerek J. Clark static ssize_t reset_buttons_store(struct device *dev, 681e4c850a6SDerek J. Clark struct device_attribute *attr, const char *buf, 682e4c850a6SDerek J. Clark size_t count) 683e4c850a6SDerek J. Clark { 684e4c850a6SDerek J. Clark int val, ret; 685e4c850a6SDerek J. Clark 686e4c850a6SDerek J. Clark ret = kstrtoint(buf, 10, &val); 687e4c850a6SDerek J. Clark if (ret) 688e4c850a6SDerek J. Clark return ret; 689e4c850a6SDerek J. Clark 690e4c850a6SDerek J. Clark if (val != 1) 691e4c850a6SDerek J. Clark return -EINVAL; 692e4c850a6SDerek J. Clark 693e4c850a6SDerek J. Clark oxp_reset_buttons(); 694e4c850a6SDerek J. Clark ret = oxp_set_buttons(); 695e4c850a6SDerek J. Clark if (ret) 696e4c850a6SDerek J. Clark return ret; 697e4c850a6SDerek J. Clark 698e4c850a6SDerek J. Clark return count; 699e4c850a6SDerek J. Clark } 700e4c850a6SDerek J. Clark static DEVICE_ATTR_WO(reset_buttons); 701e4c850a6SDerek J. Clark 702e4c850a6SDerek J. Clark static void oxp_btn_queue_fn(struct work_struct *work) 703e4c850a6SDerek J. Clark { 704e4c850a6SDerek J. Clark int ret; 705e4c850a6SDerek J. Clark 706e4c850a6SDerek J. Clark ret = oxp_set_buttons(); 707e4c850a6SDerek J. Clark if (ret) 708e4c850a6SDerek J. Clark dev_err(&drvdata.hdev->dev, 709e4c850a6SDerek J. Clark "Error: Failed to write button mapping: %i\n", ret); 710e4c850a6SDerek J. Clark } 711e4c850a6SDerek J. Clark 712e4c850a6SDerek J. Clark static int oxp_button_idx_from_str(const char *buf) 713e4c850a6SDerek J. Clark { 714e4c850a6SDerek J. Clark int i; 715e4c850a6SDerek J. Clark 716e4c850a6SDerek J. Clark for (i = 0; i < ARRAY_SIZE(oxp_button_table); i++) 717e4c850a6SDerek J. Clark if (sysfs_streq(buf, oxp_button_table[i].name)) 718e4c850a6SDerek J. Clark return i; 719e4c850a6SDerek J. Clark 720e4c850a6SDerek J. Clark return -EINVAL; 721e4c850a6SDerek J. Clark } 722e4c850a6SDerek J. Clark 723e4c850a6SDerek J. Clark static ssize_t map_button_store(struct device *dev, 724e4c850a6SDerek J. Clark struct device_attribute *attr, const char *buf, 725e4c850a6SDerek J. Clark size_t count, u8 index) 726e4c850a6SDerek J. Clark { 727e4c850a6SDerek J. Clark int idx; 728e4c850a6SDerek J. Clark 729e4c850a6SDerek J. Clark idx = oxp_button_idx_from_str(buf); 730e4c850a6SDerek J. Clark if (idx < 0) 731e4c850a6SDerek J. Clark return idx; 732e4c850a6SDerek J. Clark 733e4c850a6SDerek J. Clark switch (index) { 734e4c850a6SDerek J. Clark case BUTTON_A: 735e4c850a6SDerek J. Clark drvdata.bmap_1->btn_a.mapping_idx = idx; 736e4c850a6SDerek J. Clark break; 737e4c850a6SDerek J. Clark case BUTTON_B: 738e4c850a6SDerek J. Clark drvdata.bmap_1->btn_b.mapping_idx = idx; 739e4c850a6SDerek J. Clark break; 740e4c850a6SDerek J. Clark case BUTTON_X: 741e4c850a6SDerek J. Clark drvdata.bmap_1->btn_x.mapping_idx = idx; 742e4c850a6SDerek J. Clark break; 743e4c850a6SDerek J. Clark case BUTTON_Y: 744e4c850a6SDerek J. Clark drvdata.bmap_1->btn_y.mapping_idx = idx; 745e4c850a6SDerek J. Clark break; 746e4c850a6SDerek J. Clark case BUTTON_LB: 747e4c850a6SDerek J. Clark drvdata.bmap_1->btn_lb.mapping_idx = idx; 748e4c850a6SDerek J. Clark break; 749e4c850a6SDerek J. Clark case BUTTON_RB: 750e4c850a6SDerek J. Clark drvdata.bmap_1->btn_rb.mapping_idx = idx; 751e4c850a6SDerek J. Clark break; 752e4c850a6SDerek J. Clark case BUTTON_LT: 753e4c850a6SDerek J. Clark drvdata.bmap_1->btn_lt.mapping_idx = idx; 754e4c850a6SDerek J. Clark break; 755e4c850a6SDerek J. Clark case BUTTON_RT: 756e4c850a6SDerek J. Clark drvdata.bmap_1->btn_rt.mapping_idx = idx; 757e4c850a6SDerek J. Clark break; 758e4c850a6SDerek J. Clark case BUTTON_START: 759e4c850a6SDerek J. Clark drvdata.bmap_1->btn_start.mapping_idx = idx; 760e4c850a6SDerek J. Clark break; 761e4c850a6SDerek J. Clark case BUTTON_SELECT: 762e4c850a6SDerek J. Clark drvdata.bmap_2->btn_select.mapping_idx = idx; 763e4c850a6SDerek J. Clark break; 764e4c850a6SDerek J. Clark case BUTTON_L3: 765e4c850a6SDerek J. Clark drvdata.bmap_2->btn_l3.mapping_idx = idx; 766e4c850a6SDerek J. Clark break; 767e4c850a6SDerek J. Clark case BUTTON_R3: 768e4c850a6SDerek J. Clark drvdata.bmap_2->btn_r3.mapping_idx = idx; 769e4c850a6SDerek J. Clark break; 770e4c850a6SDerek J. Clark case BUTTON_DUP: 771e4c850a6SDerek J. Clark drvdata.bmap_2->btn_dup.mapping_idx = idx; 772e4c850a6SDerek J. Clark break; 773e4c850a6SDerek J. Clark case BUTTON_DDOWN: 774e4c850a6SDerek J. Clark drvdata.bmap_2->btn_ddown.mapping_idx = idx; 775e4c850a6SDerek J. Clark break; 776e4c850a6SDerek J. Clark case BUTTON_DLEFT: 777e4c850a6SDerek J. Clark drvdata.bmap_2->btn_dleft.mapping_idx = idx; 778e4c850a6SDerek J. Clark break; 779e4c850a6SDerek J. Clark case BUTTON_DRIGHT: 780e4c850a6SDerek J. Clark drvdata.bmap_2->btn_dright.mapping_idx = idx; 781e4c850a6SDerek J. Clark break; 782e4c850a6SDerek J. Clark case BUTTON_M1: 783e4c850a6SDerek J. Clark drvdata.bmap_2->btn_m1.mapping_idx = idx; 784e4c850a6SDerek J. Clark break; 785e4c850a6SDerek J. Clark case BUTTON_M2: 786e4c850a6SDerek J. Clark drvdata.bmap_2->btn_m2.mapping_idx = idx; 787e4c850a6SDerek J. Clark break; 788e4c850a6SDerek J. Clark default: 789e4c850a6SDerek J. Clark return -EINVAL; 790e4c850a6SDerek J. Clark } 791*8bb7c0fdSMarco Crivellari mod_delayed_work(system_dfl_wq, &drvdata.oxp_btn_queue, msecs_to_jiffies(50)); 792e4c850a6SDerek J. Clark return count; 793e4c850a6SDerek J. Clark } 794e4c850a6SDerek J. Clark 795e4c850a6SDerek J. Clark static ssize_t map_button_show(struct device *dev, 796e4c850a6SDerek J. Clark struct device_attribute *attr, char *buf, 797e4c850a6SDerek J. Clark u8 index) 798e4c850a6SDerek J. Clark { 799e4c850a6SDerek J. Clark u8 i; 800e4c850a6SDerek J. Clark 801e4c850a6SDerek J. Clark switch (index) { 802e4c850a6SDerek J. Clark case BUTTON_A: 803e4c850a6SDerek J. Clark i = drvdata.bmap_1->btn_a.mapping_idx; 804e4c850a6SDerek J. Clark break; 805e4c850a6SDerek J. Clark case BUTTON_B: 806e4c850a6SDerek J. Clark i = drvdata.bmap_1->btn_b.mapping_idx; 807e4c850a6SDerek J. Clark break; 808e4c850a6SDerek J. Clark case BUTTON_X: 809e4c850a6SDerek J. Clark i = drvdata.bmap_1->btn_x.mapping_idx; 810e4c850a6SDerek J. Clark break; 811e4c850a6SDerek J. Clark case BUTTON_Y: 812e4c850a6SDerek J. Clark i = drvdata.bmap_1->btn_y.mapping_idx; 813e4c850a6SDerek J. Clark break; 814e4c850a6SDerek J. Clark case BUTTON_LB: 815e4c850a6SDerek J. Clark i = drvdata.bmap_1->btn_lb.mapping_idx; 816e4c850a6SDerek J. Clark break; 817e4c850a6SDerek J. Clark case BUTTON_RB: 818e4c850a6SDerek J. Clark i = drvdata.bmap_1->btn_rb.mapping_idx; 819e4c850a6SDerek J. Clark break; 820e4c850a6SDerek J. Clark case BUTTON_LT: 821e4c850a6SDerek J. Clark i = drvdata.bmap_1->btn_lt.mapping_idx; 822e4c850a6SDerek J. Clark break; 823e4c850a6SDerek J. Clark case BUTTON_RT: 824e4c850a6SDerek J. Clark i = drvdata.bmap_1->btn_rt.mapping_idx; 825e4c850a6SDerek J. Clark break; 826e4c850a6SDerek J. Clark case BUTTON_START: 827e4c850a6SDerek J. Clark i = drvdata.bmap_1->btn_start.mapping_idx; 828e4c850a6SDerek J. Clark break; 829e4c850a6SDerek J. Clark case BUTTON_SELECT: 830e4c850a6SDerek J. Clark i = drvdata.bmap_2->btn_select.mapping_idx; 831e4c850a6SDerek J. Clark break; 832e4c850a6SDerek J. Clark case BUTTON_L3: 833e4c850a6SDerek J. Clark i = drvdata.bmap_2->btn_l3.mapping_idx; 834e4c850a6SDerek J. Clark break; 835e4c850a6SDerek J. Clark case BUTTON_R3: 836e4c850a6SDerek J. Clark i = drvdata.bmap_2->btn_r3.mapping_idx; 837e4c850a6SDerek J. Clark break; 838e4c850a6SDerek J. Clark case BUTTON_DUP: 839e4c850a6SDerek J. Clark i = drvdata.bmap_2->btn_dup.mapping_idx; 840e4c850a6SDerek J. Clark break; 841e4c850a6SDerek J. Clark case BUTTON_DDOWN: 842e4c850a6SDerek J. Clark i = drvdata.bmap_2->btn_ddown.mapping_idx; 843e4c850a6SDerek J. Clark break; 844e4c850a6SDerek J. Clark case BUTTON_DLEFT: 845e4c850a6SDerek J. Clark i = drvdata.bmap_2->btn_dleft.mapping_idx; 846e4c850a6SDerek J. Clark break; 847e4c850a6SDerek J. Clark case BUTTON_DRIGHT: 848e4c850a6SDerek J. Clark i = drvdata.bmap_2->btn_dright.mapping_idx; 849e4c850a6SDerek J. Clark break; 850e4c850a6SDerek J. Clark case BUTTON_M1: 851e4c850a6SDerek J. Clark i = drvdata.bmap_2->btn_m1.mapping_idx; 852e4c850a6SDerek J. Clark break; 853e4c850a6SDerek J. Clark case BUTTON_M2: 854e4c850a6SDerek J. Clark i = drvdata.bmap_2->btn_m2.mapping_idx; 855e4c850a6SDerek J. Clark break; 856e4c850a6SDerek J. Clark default: 857e4c850a6SDerek J. Clark return -EINVAL; 858e4c850a6SDerek J. Clark } 859e4c850a6SDerek J. Clark 860e4c850a6SDerek J. Clark if (i >= ARRAY_SIZE(oxp_button_table)) 861e4c850a6SDerek J. Clark return -EINVAL; 862e4c850a6SDerek J. Clark 863e4c850a6SDerek J. Clark return sysfs_emit(buf, "%s\n", oxp_button_table[i].name); 864e4c850a6SDerek J. Clark } 865e4c850a6SDerek J. Clark 866e4c850a6SDerek J. Clark static ssize_t button_mapping_options_show(struct device *dev, 867e4c850a6SDerek J. Clark struct device_attribute *attr, char *buf) 868e4c850a6SDerek J. Clark { 869e4c850a6SDerek J. Clark ssize_t count = 0; 870e4c850a6SDerek J. Clark unsigned int i; 871e4c850a6SDerek J. Clark 872e4c850a6SDerek J. Clark for (i = 0; i < ARRAY_SIZE(oxp_button_table); i++) 873e4c850a6SDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", oxp_button_table[i].name); 874e4c850a6SDerek J. Clark 875e4c850a6SDerek J. Clark if (count) 876e4c850a6SDerek J. Clark buf[count - 1] = '\n'; 877e4c850a6SDerek J. Clark 878e4c850a6SDerek J. Clark return count; 879e4c850a6SDerek J. Clark } 880e4c850a6SDerek J. Clark static DEVICE_ATTR_RO(button_mapping_options); 881e4c850a6SDerek J. Clark 88299bde1dfSDerek J. Clark static int oxp_rumble_intensity_set(u8 intensity) 88399bde1dfSDerek J. Clark { 88499bde1dfSDerek J. Clark u8 header[15] = { 0x02, 0x38, 0x02, 0xe3, 0x39, 0xe3, 0x39, 0xe3, 88599bde1dfSDerek J. Clark 0x39, 0x01, intensity, 0x05, 0xe3, 0x39, 0xe3 }; 88699bde1dfSDerek J. Clark u8 footer[9] = { 0x39, 0xe3, 0x39, 0xe3, 0xe3, 0x02, 0x04, 0x39, 0x39 }; 88799bde1dfSDerek J. Clark size_t footer_size = ARRAY_SIZE(footer); 88899bde1dfSDerek J. Clark size_t header_size = ARRAY_SIZE(header); 88999bde1dfSDerek J. Clark u8 data[59] = { 0x0 }; 89099bde1dfSDerek J. Clark size_t data_size = ARRAY_SIZE(data); 89199bde1dfSDerek J. Clark 89299bde1dfSDerek J. Clark memcpy(data, header, header_size); 89399bde1dfSDerek J. Clark memcpy(data + data_size - footer_size, footer, footer_size); 89499bde1dfSDerek J. Clark 89599bde1dfSDerek J. Clark return oxp_gen_2_property_out(OXP_FID_GEN2_RUMBLE_SET, data, data_size); 89699bde1dfSDerek J. Clark } 89799bde1dfSDerek J. Clark 89899bde1dfSDerek J. Clark static ssize_t rumble_intensity_store(struct device *dev, 89999bde1dfSDerek J. Clark struct device_attribute *attr, const char *buf, 90099bde1dfSDerek J. Clark size_t count) 90199bde1dfSDerek J. Clark { 90299bde1dfSDerek J. Clark int ret; 90399bde1dfSDerek J. Clark u8 val; 90499bde1dfSDerek J. Clark 90599bde1dfSDerek J. Clark ret = kstrtou8(buf, 10, &val); 90699bde1dfSDerek J. Clark if (ret) 90799bde1dfSDerek J. Clark return ret; 90899bde1dfSDerek J. Clark 90999bde1dfSDerek J. Clark if (val < 0 || val > 5) 91099bde1dfSDerek J. Clark return -EINVAL; 91199bde1dfSDerek J. Clark 91299bde1dfSDerek J. Clark ret = oxp_rumble_intensity_set(val); 91399bde1dfSDerek J. Clark if (ret) 91499bde1dfSDerek J. Clark return ret; 91599bde1dfSDerek J. Clark 91699bde1dfSDerek J. Clark drvdata.rumble_intensity = val; 91799bde1dfSDerek J. Clark 91899bde1dfSDerek J. Clark return count; 91999bde1dfSDerek J. Clark } 92099bde1dfSDerek J. Clark 92199bde1dfSDerek J. Clark static ssize_t rumble_intensity_show(struct device *dev, 92299bde1dfSDerek J. Clark struct device_attribute *attr, char *buf) 92399bde1dfSDerek J. Clark { 92499bde1dfSDerek J. Clark return sysfs_emit(buf, "%i\n", drvdata.rumble_intensity); 92599bde1dfSDerek J. Clark } 92699bde1dfSDerek J. Clark static DEVICE_ATTR_RW(rumble_intensity); 92799bde1dfSDerek J. Clark 92899bde1dfSDerek J. Clark static ssize_t rumble_intensity_range_show(struct device *dev, 92999bde1dfSDerek J. Clark struct device_attribute *attr, char *buf) 93099bde1dfSDerek J. Clark { 93199bde1dfSDerek J. Clark return sysfs_emit(buf, "0-5\n"); 93299bde1dfSDerek J. Clark } 93399bde1dfSDerek J. Clark static DEVICE_ATTR_RO(rumble_intensity_range); 93499bde1dfSDerek J. Clark 935e4c850a6SDerek J. Clark #define OXP_DEVICE_ATTR_RW(_name, _group) \ 936e4c850a6SDerek J. Clark static ssize_t _name##_store(struct device *dev, \ 937e4c850a6SDerek J. Clark struct device_attribute *attr, \ 938e4c850a6SDerek J. Clark const char *buf, size_t count) \ 939e4c850a6SDerek J. Clark { \ 940e4c850a6SDerek J. Clark return _group##_store(dev, attr, buf, count, _name.index); \ 941e4c850a6SDerek J. Clark } \ 942e4c850a6SDerek J. Clark static ssize_t _name##_show(struct device *dev, \ 943e4c850a6SDerek J. Clark struct device_attribute *attr, char *buf) \ 944e4c850a6SDerek J. Clark { \ 945e4c850a6SDerek J. Clark return _group##_show(dev, attr, buf, _name.index); \ 946e4c850a6SDerek J. Clark } \ 947e4c850a6SDerek J. Clark static DEVICE_ATTR_RW(_name) 948e4c850a6SDerek J. Clark 949e4c850a6SDerek J. Clark static struct oxp_attr button_a = { BUTTON_A }; 950e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_a, map_button); 951e4c850a6SDerek J. Clark 952e4c850a6SDerek J. Clark static struct oxp_attr button_b = { BUTTON_B }; 953e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_b, map_button); 954e4c850a6SDerek J. Clark 955e4c850a6SDerek J. Clark static struct oxp_attr button_x = { BUTTON_X }; 956e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_x, map_button); 957e4c850a6SDerek J. Clark 958e4c850a6SDerek J. Clark static struct oxp_attr button_y = { BUTTON_Y }; 959e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_y, map_button); 960e4c850a6SDerek J. Clark 961e4c850a6SDerek J. Clark static struct oxp_attr button_lb = { BUTTON_LB }; 962e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_lb, map_button); 963e4c850a6SDerek J. Clark 964e4c850a6SDerek J. Clark static struct oxp_attr button_rb = { BUTTON_RB }; 965e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_rb, map_button); 966e4c850a6SDerek J. Clark 967e4c850a6SDerek J. Clark static struct oxp_attr button_lt = { BUTTON_LT }; 968e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_lt, map_button); 969e4c850a6SDerek J. Clark 970e4c850a6SDerek J. Clark static struct oxp_attr button_rt = { BUTTON_RT }; 971e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_rt, map_button); 972e4c850a6SDerek J. Clark 973e4c850a6SDerek J. Clark static struct oxp_attr button_start = { BUTTON_START }; 974e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_start, map_button); 975e4c850a6SDerek J. Clark 976e4c850a6SDerek J. Clark static struct oxp_attr button_select = { BUTTON_SELECT }; 977e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_select, map_button); 978e4c850a6SDerek J. Clark 979e4c850a6SDerek J. Clark static struct oxp_attr button_l3 = { BUTTON_L3 }; 980e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_l3, map_button); 981e4c850a6SDerek J. Clark 982e4c850a6SDerek J. Clark static struct oxp_attr button_r3 = { BUTTON_R3 }; 983e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_r3, map_button); 984e4c850a6SDerek J. Clark 985e4c850a6SDerek J. Clark static struct oxp_attr button_d_up = { BUTTON_DUP }; 986e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_d_up, map_button); 987e4c850a6SDerek J. Clark 988e4c850a6SDerek J. Clark static struct oxp_attr button_d_down = { BUTTON_DDOWN }; 989e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_d_down, map_button); 990e4c850a6SDerek J. Clark 991e4c850a6SDerek J. Clark static struct oxp_attr button_d_left = { BUTTON_DLEFT }; 992e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_d_left, map_button); 993e4c850a6SDerek J. Clark 994e4c850a6SDerek J. Clark static struct oxp_attr button_d_right = { BUTTON_DRIGHT }; 995e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_d_right, map_button); 996e4c850a6SDerek J. Clark 997e4c850a6SDerek J. Clark static struct oxp_attr button_m1 = { BUTTON_M1 }; 998e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_m1, map_button); 999e4c850a6SDerek J. Clark 1000e4c850a6SDerek J. Clark static struct oxp_attr button_m2 = { BUTTON_M2 }; 1001e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_m2, map_button); 1002e4c850a6SDerek J. Clark 10032f424f28SDerek J. Clark static struct attribute *oxp_cfg_attrs[] = { 1004e4c850a6SDerek J. Clark &dev_attr_button_a.attr, 1005e4c850a6SDerek J. Clark &dev_attr_button_b.attr, 1006e4c850a6SDerek J. Clark &dev_attr_button_d_down.attr, 1007e4c850a6SDerek J. Clark &dev_attr_button_d_left.attr, 1008e4c850a6SDerek J. Clark &dev_attr_button_d_right.attr, 1009e4c850a6SDerek J. Clark &dev_attr_button_d_up.attr, 1010e4c850a6SDerek J. Clark &dev_attr_button_l3.attr, 1011e4c850a6SDerek J. Clark &dev_attr_button_lb.attr, 1012e4c850a6SDerek J. Clark &dev_attr_button_lt.attr, 1013e4c850a6SDerek J. Clark &dev_attr_button_m1.attr, 1014e4c850a6SDerek J. Clark &dev_attr_button_m2.attr, 1015e4c850a6SDerek J. Clark &dev_attr_button_mapping_options.attr, 1016e4c850a6SDerek J. Clark &dev_attr_button_r3.attr, 1017e4c850a6SDerek J. Clark &dev_attr_button_rb.attr, 1018e4c850a6SDerek J. Clark &dev_attr_button_rt.attr, 1019e4c850a6SDerek J. Clark &dev_attr_button_select.attr, 1020e4c850a6SDerek J. Clark &dev_attr_button_start.attr, 1021e4c850a6SDerek J. Clark &dev_attr_button_x.attr, 1022e4c850a6SDerek J. Clark &dev_attr_button_y.attr, 10232f424f28SDerek J. Clark &dev_attr_gamepad_mode.attr, 10242f424f28SDerek J. Clark &dev_attr_gamepad_mode_index.attr, 1025e4c850a6SDerek J. Clark &dev_attr_reset_buttons.attr, 102699bde1dfSDerek J. Clark &dev_attr_rumble_intensity.attr, 102799bde1dfSDerek J. Clark &dev_attr_rumble_intensity_range.attr, 10282f424f28SDerek J. Clark NULL, 10292f424f28SDerek J. Clark }; 10302f424f28SDerek J. Clark 10312f424f28SDerek J. Clark static const struct attribute_group oxp_cfg_attrs_group = { 10322f424f28SDerek J. Clark .attrs = oxp_cfg_attrs, 10332f424f28SDerek J. Clark }; 10342f424f28SDerek J. Clark 103584910c45SDerek J. Clark static int oxp_rgb_status_store(u8 enabled, u8 speed, u8 brightness) 103684910c45SDerek J. Clark { 103784910c45SDerek J. Clark u16 up = get_usage_page(drvdata.hdev); 103884910c45SDerek J. Clark u8 *data; 103984910c45SDerek J. Clark 104084910c45SDerek J. Clark /* Always default to max brightness and use intensity scaling when in 104184910c45SDerek J. Clark * monocolor mode. 104284910c45SDerek J. Clark */ 104384910c45SDerek J. Clark switch (up) { 104484910c45SDerek J. Clark case GEN1_USAGE_PAGE: 104584910c45SDerek J. Clark data = (u8[4]) { OXP_SET_PROPERTY, enabled, speed, brightness }; 104684910c45SDerek J. Clark if (drvdata.rgb_effect == OXP_EFFECT_MONO_LIST) 104784910c45SDerek J. Clark data[3] = 0x04; 104884910c45SDerek J. Clark return oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, 4); 1049252c4bf1SDerek J. Clark case GEN2_USAGE_PAGE: 1050252c4bf1SDerek J. Clark data = (u8[6]) { OXP_SET_PROPERTY, 0x00, 0x02, enabled, speed, brightness }; 1051252c4bf1SDerek J. Clark if (drvdata.rgb_effect == OXP_EFFECT_MONO_LIST) 1052252c4bf1SDerek J. Clark data[5] = 0x04; 1053252c4bf1SDerek J. Clark return oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, 6); 105484910c45SDerek J. Clark default: 105584910c45SDerek J. Clark return -ENODEV; 105684910c45SDerek J. Clark } 105784910c45SDerek J. Clark } 105884910c45SDerek J. Clark 105984910c45SDerek J. Clark static ssize_t oxp_rgb_status_show(void) 106084910c45SDerek J. Clark { 106184910c45SDerek J. Clark u16 up = get_usage_page(drvdata.hdev); 106284910c45SDerek J. Clark u8 *data; 106384910c45SDerek J. Clark 106484910c45SDerek J. Clark switch (up) { 106584910c45SDerek J. Clark case GEN1_USAGE_PAGE: 106684910c45SDerek J. Clark data = (u8[1]) { OXP_GET_PROPERTY }; 106784910c45SDerek J. Clark return oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, 1); 1068252c4bf1SDerek J. Clark case GEN2_USAGE_PAGE: 1069252c4bf1SDerek J. Clark data = (u8[3]) { OXP_GET_PROPERTY, 0x00, 0x02 }; 1070252c4bf1SDerek J. Clark return oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, 3); 107184910c45SDerek J. Clark default: 107284910c45SDerek J. Clark return -ENODEV; 107384910c45SDerek J. Clark } 107484910c45SDerek J. Clark } 107584910c45SDerek J. Clark 107684910c45SDerek J. Clark static int oxp_rgb_color_set(void) 107784910c45SDerek J. Clark { 107884910c45SDerek J. Clark u8 max_br = drvdata.led_mc->led_cdev.max_brightness; 107984910c45SDerek J. Clark u8 br = drvdata.led_mc->led_cdev.brightness; 108084910c45SDerek J. Clark u16 up = get_usage_page(drvdata.hdev); 108184910c45SDerek J. Clark u8 green, red, blue; 108284910c45SDerek J. Clark size_t size; 108384910c45SDerek J. Clark u8 *data; 108484910c45SDerek J. Clark int i; 108584910c45SDerek J. Clark 108684910c45SDerek J. Clark red = br * drvdata.led_mc->subled_info[0].intensity / max_br; 108784910c45SDerek J. Clark green = br * drvdata.led_mc->subled_info[1].intensity / max_br; 108884910c45SDerek J. Clark blue = br * drvdata.led_mc->subled_info[2].intensity / max_br; 108984910c45SDerek J. Clark 109084910c45SDerek J. Clark switch (up) { 109184910c45SDerek J. Clark case GEN1_USAGE_PAGE: 109284910c45SDerek J. Clark size = 55; 109384910c45SDerek J. Clark data = (u8[55]) { OXP_EFFECT_MONO_TRUE }; 109484910c45SDerek J. Clark 109584910c45SDerek J. Clark for (i = 0; i < (size - 1) / 3; i++) { 109684910c45SDerek J. Clark data[3 * i + 1] = red; 109784910c45SDerek J. Clark data[3 * i + 2] = green; 109884910c45SDerek J. Clark data[3 * i + 3] = blue; 109984910c45SDerek J. Clark } 110084910c45SDerek J. Clark return oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, size); 1101252c4bf1SDerek J. Clark case GEN2_USAGE_PAGE: 1102252c4bf1SDerek J. Clark size = 57; 1103252c4bf1SDerek J. Clark data = (u8[57]) { OXP_EFFECT_MONO_TRUE, 0x00, 0x02 }; 1104252c4bf1SDerek J. Clark 1105252c4bf1SDerek J. Clark for (i = 1; i < size / 3; i++) { 1106252c4bf1SDerek J. Clark data[3 * i] = red; 1107252c4bf1SDerek J. Clark data[3 * i + 1] = green; 1108252c4bf1SDerek J. Clark data[3 * i + 2] = blue; 1109252c4bf1SDerek J. Clark } 1110252c4bf1SDerek J. Clark return oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, size); 111184910c45SDerek J. Clark default: 111284910c45SDerek J. Clark return -ENODEV; 111384910c45SDerek J. Clark } 111484910c45SDerek J. Clark } 111584910c45SDerek J. Clark 111684910c45SDerek J. Clark static int oxp_rgb_effect_set(u8 effect) 111784910c45SDerek J. Clark { 111884910c45SDerek J. Clark u16 up = get_usage_page(drvdata.hdev); 111984910c45SDerek J. Clark u8 *data; 112084910c45SDerek J. Clark int ret; 112184910c45SDerek J. Clark 112284910c45SDerek J. Clark switch (effect) { 112384910c45SDerek J. Clark case OXP_EFFECT_AURORA: 112484910c45SDerek J. Clark case OXP_EFFECT_BIRTHDAY: 112584910c45SDerek J. Clark case OXP_EFFECT_FLOWING: 112684910c45SDerek J. Clark case OXP_EFFECT_CHROMA_1: 112784910c45SDerek J. Clark case OXP_EFFECT_NEON: 112884910c45SDerek J. Clark case OXP_EFFECT_CHROMA_2: 112984910c45SDerek J. Clark case OXP_EFFECT_DREAMY: 113084910c45SDerek J. Clark case OXP_EFFECT_WARM: 113184910c45SDerek J. Clark case OXP_EFFECT_CYBERPUNK: 113284910c45SDerek J. Clark case OXP_EFFECT_SEA: 113384910c45SDerek J. Clark case OXP_EFFECT_SUNSET: 113484910c45SDerek J. Clark case OXP_EFFECT_COLORFUL: 113584910c45SDerek J. Clark case OXP_EFFECT_MONSTER: 113684910c45SDerek J. Clark case OXP_EFFECT_GREEN: 113784910c45SDerek J. Clark case OXP_EFFECT_BLUE: 113884910c45SDerek J. Clark case OXP_EFFECT_YELLOW: 113984910c45SDerek J. Clark case OXP_EFFECT_TEAL: 114084910c45SDerek J. Clark case OXP_EFFECT_PURPLE: 114184910c45SDerek J. Clark case OXP_EFFECT_FOGGY: 114284910c45SDerek J. Clark switch (up) { 114384910c45SDerek J. Clark case GEN1_USAGE_PAGE: 114484910c45SDerek J. Clark data = (u8[1]) { effect }; 114584910c45SDerek J. Clark ret = oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, 1); 114684910c45SDerek J. Clark break; 1147252c4bf1SDerek J. Clark case GEN2_USAGE_PAGE: 1148252c4bf1SDerek J. Clark data = (u8[3]) { effect, 0x00, 0x02 }; 1149252c4bf1SDerek J. Clark ret = oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, 3); 1150252c4bf1SDerek J. Clark break; 115184910c45SDerek J. Clark default: 115284910c45SDerek J. Clark ret = -ENODEV; 115384910c45SDerek J. Clark } 115484910c45SDerek J. Clark break; 115584910c45SDerek J. Clark case OXP_EFFECT_MONO_LIST: 115684910c45SDerek J. Clark ret = oxp_rgb_color_set(); 115784910c45SDerek J. Clark break; 115884910c45SDerek J. Clark default: 115984910c45SDerek J. Clark return -EINVAL; 116084910c45SDerek J. Clark } 116184910c45SDerek J. Clark 116284910c45SDerek J. Clark if (ret) 116384910c45SDerek J. Clark return ret; 116484910c45SDerek J. Clark 116584910c45SDerek J. Clark drvdata.rgb_effect = effect; 116684910c45SDerek J. Clark 116784910c45SDerek J. Clark return 0; 116884910c45SDerek J. Clark } 116984910c45SDerek J. Clark 117084910c45SDerek J. Clark static ssize_t enabled_store(struct device *dev, struct device_attribute *attr, 117184910c45SDerek J. Clark const char *buf, size_t count) 117284910c45SDerek J. Clark { 117384910c45SDerek J. Clark int ret; 117484910c45SDerek J. Clark u8 val; 117584910c45SDerek J. Clark 117684910c45SDerek J. Clark ret = sysfs_match_string(oxp_feature_en_text, buf); 117784910c45SDerek J. Clark if (ret < 0) 117884910c45SDerek J. Clark return ret; 117984910c45SDerek J. Clark val = ret; 118084910c45SDerek J. Clark 118184910c45SDerek J. Clark ret = oxp_rgb_status_store(val, drvdata.rgb_speed, 118284910c45SDerek J. Clark drvdata.rgb_brightness); 118384910c45SDerek J. Clark if (ret) 118484910c45SDerek J. Clark return ret; 118584910c45SDerek J. Clark 118684910c45SDerek J. Clark drvdata.rgb_en = val; 118784910c45SDerek J. Clark return count; 118884910c45SDerek J. Clark } 118984910c45SDerek J. Clark 119084910c45SDerek J. Clark static ssize_t enabled_show(struct device *dev, struct device_attribute *attr, 119184910c45SDerek J. Clark char *buf) 119284910c45SDerek J. Clark { 119384910c45SDerek J. Clark int ret; 119484910c45SDerek J. Clark 119584910c45SDerek J. Clark ret = oxp_rgb_status_show(); 119684910c45SDerek J. Clark if (ret) 119784910c45SDerek J. Clark return ret; 119884910c45SDerek J. Clark 119984910c45SDerek J. Clark if (drvdata.rgb_en >= ARRAY_SIZE(oxp_feature_en_text)) 120084910c45SDerek J. Clark return -EINVAL; 120184910c45SDerek J. Clark 120284910c45SDerek J. Clark return sysfs_emit(buf, "%s\n", oxp_feature_en_text[drvdata.rgb_en]); 120384910c45SDerek J. Clark } 120484910c45SDerek J. Clark static DEVICE_ATTR_RW(enabled); 120584910c45SDerek J. Clark 120684910c45SDerek J. Clark static ssize_t enabled_index_show(struct device *dev, 120784910c45SDerek J. Clark struct device_attribute *attr, char *buf) 120884910c45SDerek J. Clark { 120984910c45SDerek J. Clark size_t count = 0; 121084910c45SDerek J. Clark unsigned int i; 121184910c45SDerek J. Clark 121284910c45SDerek J. Clark for (i = 0; i < ARRAY_SIZE(oxp_feature_en_text); i++) 121384910c45SDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", oxp_feature_en_text[i]); 121484910c45SDerek J. Clark 121584910c45SDerek J. Clark if (count) 121684910c45SDerek J. Clark buf[count - 1] = '\n'; 121784910c45SDerek J. Clark 121884910c45SDerek J. Clark return count; 121984910c45SDerek J. Clark } 122084910c45SDerek J. Clark static DEVICE_ATTR_RO(enabled_index); 122184910c45SDerek J. Clark 122284910c45SDerek J. Clark static ssize_t effect_store(struct device *dev, struct device_attribute *attr, 122384910c45SDerek J. Clark const char *buf, size_t count) 122484910c45SDerek J. Clark { 122584910c45SDerek J. Clark int ret; 122684910c45SDerek J. Clark u8 val; 122784910c45SDerek J. Clark 122884910c45SDerek J. Clark ret = sysfs_match_string(oxp_rgb_effect_text, buf); 122984910c45SDerek J. Clark if (ret < 0) 123084910c45SDerek J. Clark return ret; 123184910c45SDerek J. Clark 123284910c45SDerek J. Clark val = ret; 123384910c45SDerek J. Clark 123484910c45SDerek J. Clark ret = oxp_rgb_status_store(drvdata.rgb_en, drvdata.rgb_speed, 123584910c45SDerek J. Clark drvdata.rgb_brightness); 123684910c45SDerek J. Clark if (ret) 123784910c45SDerek J. Clark return ret; 123884910c45SDerek J. Clark 123984910c45SDerek J. Clark ret = oxp_rgb_effect_set(val); 124084910c45SDerek J. Clark if (ret) 124184910c45SDerek J. Clark return ret; 124284910c45SDerek J. Clark 124384910c45SDerek J. Clark return count; 124484910c45SDerek J. Clark } 124584910c45SDerek J. Clark 124684910c45SDerek J. Clark static ssize_t effect_show(struct device *dev, struct device_attribute *attr, 124784910c45SDerek J. Clark char *buf) 124884910c45SDerek J. Clark { 124984910c45SDerek J. Clark int ret; 125084910c45SDerek J. Clark 125184910c45SDerek J. Clark ret = oxp_rgb_status_show(); 125284910c45SDerek J. Clark if (ret) 125384910c45SDerek J. Clark return ret; 125484910c45SDerek J. Clark 125584910c45SDerek J. Clark if (drvdata.rgb_effect >= ARRAY_SIZE(oxp_rgb_effect_text)) 125684910c45SDerek J. Clark return -EINVAL; 125784910c45SDerek J. Clark 125884910c45SDerek J. Clark return sysfs_emit(buf, "%s\n", oxp_rgb_effect_text[drvdata.rgb_effect]); 125984910c45SDerek J. Clark } 126084910c45SDerek J. Clark 126184910c45SDerek J. Clark static DEVICE_ATTR_RW(effect); 126284910c45SDerek J. Clark 126384910c45SDerek J. Clark static ssize_t effect_index_show(struct device *dev, 126484910c45SDerek J. Clark struct device_attribute *attr, char *buf) 126584910c45SDerek J. Clark { 126684910c45SDerek J. Clark size_t count = 0; 126784910c45SDerek J. Clark unsigned int i; 126884910c45SDerek J. Clark 126984910c45SDerek J. Clark for (i = 1; i < ARRAY_SIZE(oxp_rgb_effect_text); i++) 127084910c45SDerek J. Clark count += sysfs_emit_at(buf, count, "%s ", oxp_rgb_effect_text[i]); 127184910c45SDerek J. Clark 127284910c45SDerek J. Clark if (count) 127384910c45SDerek J. Clark buf[count - 1] = '\n'; 127484910c45SDerek J. Clark 127584910c45SDerek J. Clark return count; 127684910c45SDerek J. Clark } 127784910c45SDerek J. Clark static DEVICE_ATTR_RO(effect_index); 127884910c45SDerek J. Clark 127984910c45SDerek J. Clark static ssize_t speed_store(struct device *dev, struct device_attribute *attr, 128084910c45SDerek J. Clark const char *buf, size_t count) 128184910c45SDerek J. Clark { 128284910c45SDerek J. Clark int ret; 128384910c45SDerek J. Clark u8 val; 128484910c45SDerek J. Clark 128584910c45SDerek J. Clark ret = kstrtou8(buf, 10, &val); 128684910c45SDerek J. Clark if (ret) 128784910c45SDerek J. Clark return ret; 128884910c45SDerek J. Clark 128984910c45SDerek J. Clark if (val > 9) 129084910c45SDerek J. Clark return -EINVAL; 129184910c45SDerek J. Clark 129284910c45SDerek J. Clark ret = oxp_rgb_status_store(drvdata.rgb_en, val, drvdata.rgb_brightness); 129384910c45SDerek J. Clark if (ret) 129484910c45SDerek J. Clark return ret; 129584910c45SDerek J. Clark 129684910c45SDerek J. Clark drvdata.rgb_speed = val; 129784910c45SDerek J. Clark return count; 129884910c45SDerek J. Clark } 129984910c45SDerek J. Clark 130084910c45SDerek J. Clark static ssize_t speed_show(struct device *dev, struct device_attribute *attr, 130184910c45SDerek J. Clark char *buf) 130284910c45SDerek J. Clark { 130384910c45SDerek J. Clark int ret; 130484910c45SDerek J. Clark 130584910c45SDerek J. Clark ret = oxp_rgb_status_show(); 130684910c45SDerek J. Clark if (ret) 130784910c45SDerek J. Clark return ret; 130884910c45SDerek J. Clark 130984910c45SDerek J. Clark if (drvdata.rgb_speed > 9) 131084910c45SDerek J. Clark return -EINVAL; 131184910c45SDerek J. Clark 131284910c45SDerek J. Clark return sysfs_emit(buf, "%hhu\n", drvdata.rgb_speed); 131384910c45SDerek J. Clark } 131484910c45SDerek J. Clark static DEVICE_ATTR_RW(speed); 131584910c45SDerek J. Clark 131684910c45SDerek J. Clark static ssize_t speed_range_show(struct device *dev, 131784910c45SDerek J. Clark struct device_attribute *attr, char *buf) 131884910c45SDerek J. Clark { 131984910c45SDerek J. Clark return sysfs_emit(buf, "0-9\n"); 132084910c45SDerek J. Clark } 132184910c45SDerek J. Clark static DEVICE_ATTR_RO(speed_range); 132284910c45SDerek J. Clark 132384910c45SDerek J. Clark static void oxp_rgb_queue_fn(struct work_struct *work) 132484910c45SDerek J. Clark { 132584910c45SDerek J. Clark unsigned int max_brightness = drvdata.led_mc->led_cdev.max_brightness; 132684910c45SDerek J. Clark unsigned int brightness = drvdata.led_mc->led_cdev.brightness; 132784910c45SDerek J. Clark u8 val = 4 * brightness / max_brightness; 132884910c45SDerek J. Clark int ret; 132984910c45SDerek J. Clark 133084910c45SDerek J. Clark if (drvdata.rgb_brightness != val) { 133184910c45SDerek J. Clark ret = oxp_rgb_status_store(drvdata.rgb_en, drvdata.rgb_speed, val); 133284910c45SDerek J. Clark if (ret) 133384910c45SDerek J. Clark dev_err(drvdata.led_mc->led_cdev.dev, 133484910c45SDerek J. Clark "Error: Failed to write RGB Status: %i\n", ret); 133584910c45SDerek J. Clark 133684910c45SDerek J. Clark drvdata.rgb_brightness = val; 133784910c45SDerek J. Clark } 133884910c45SDerek J. Clark 133984910c45SDerek J. Clark if (drvdata.rgb_effect != OXP_EFFECT_MONO_LIST) 134084910c45SDerek J. Clark return; 134184910c45SDerek J. Clark 134284910c45SDerek J. Clark ret = oxp_rgb_effect_set(drvdata.rgb_effect); 134384910c45SDerek J. Clark if (ret) 134484910c45SDerek J. Clark dev_err(drvdata.led_mc->led_cdev.dev, "Error: Failed to write RGB color: %i\n", 134584910c45SDerek J. Clark ret); 134684910c45SDerek J. Clark } 134784910c45SDerek J. Clark 134884910c45SDerek J. Clark static void oxp_rgb_brightness_set(struct led_classdev *led_cdev, 134984910c45SDerek J. Clark enum led_brightness brightness) 135084910c45SDerek J. Clark { 135184910c45SDerek J. Clark led_cdev->brightness = brightness; 1352*8bb7c0fdSMarco Crivellari mod_delayed_work(system_dfl_wq, &drvdata.oxp_rgb_queue, msecs_to_jiffies(50)); 135384910c45SDerek J. Clark } 135484910c45SDerek J. Clark 135584910c45SDerek J. Clark static struct attribute *oxp_rgb_attrs[] = { 135684910c45SDerek J. Clark &dev_attr_effect.attr, 135784910c45SDerek J. Clark &dev_attr_effect_index.attr, 135884910c45SDerek J. Clark &dev_attr_enabled.attr, 135984910c45SDerek J. Clark &dev_attr_enabled_index.attr, 136084910c45SDerek J. Clark &dev_attr_speed.attr, 136184910c45SDerek J. Clark &dev_attr_speed_range.attr, 136284910c45SDerek J. Clark NULL, 136384910c45SDerek J. Clark }; 136484910c45SDerek J. Clark 136584910c45SDerek J. Clark static const struct attribute_group oxp_rgb_attr_group = { 136684910c45SDerek J. Clark .attrs = oxp_rgb_attrs, 136784910c45SDerek J. Clark }; 136884910c45SDerek J. Clark 136984910c45SDerek J. Clark static struct mc_subled oxp_rgb_subled_info[] = { 137084910c45SDerek J. Clark { 137184910c45SDerek J. Clark .color_index = LED_COLOR_ID_RED, 137284910c45SDerek J. Clark .intensity = 0x24, 137384910c45SDerek J. Clark .channel = 0x1, 137484910c45SDerek J. Clark }, 137584910c45SDerek J. Clark { 137684910c45SDerek J. Clark .color_index = LED_COLOR_ID_GREEN, 137784910c45SDerek J. Clark .intensity = 0x22, 137884910c45SDerek J. Clark .channel = 0x2, 137984910c45SDerek J. Clark }, 138084910c45SDerek J. Clark { 138184910c45SDerek J. Clark .color_index = LED_COLOR_ID_BLUE, 138284910c45SDerek J. Clark .intensity = 0x99, 138384910c45SDerek J. Clark .channel = 0x3, 138484910c45SDerek J. Clark }, 138584910c45SDerek J. Clark }; 138684910c45SDerek J. Clark 138784910c45SDerek J. Clark static struct led_classdev_mc oxp_cdev_rgb = { 138884910c45SDerek J. Clark .led_cdev = { 138984910c45SDerek J. Clark .name = "oxp:rgb:joystick_rings", 139084910c45SDerek J. Clark .color = LED_COLOR_ID_RGB, 139184910c45SDerek J. Clark .brightness = 0x64, 139284910c45SDerek J. Clark .max_brightness = 0x64, 139384910c45SDerek J. Clark .brightness_set = oxp_rgb_brightness_set, 139484910c45SDerek J. Clark }, 139584910c45SDerek J. Clark .num_colors = ARRAY_SIZE(oxp_rgb_subled_info), 139684910c45SDerek J. Clark .subled_info = oxp_rgb_subled_info, 139784910c45SDerek J. Clark }; 139884910c45SDerek J. Clark 1399252c4bf1SDerek J. Clark struct quirk_entry { 1400252c4bf1SDerek J. Clark bool hybrid_mcu; 1401252c4bf1SDerek J. Clark }; 1402252c4bf1SDerek J. Clark 1403252c4bf1SDerek J. Clark static struct quirk_entry quirk_hybrid_mcu = { 1404252c4bf1SDerek J. Clark .hybrid_mcu = true, 1405252c4bf1SDerek J. Clark }; 1406252c4bf1SDerek J. Clark 1407252c4bf1SDerek J. Clark static const struct dmi_system_id oxp_hybrid_mcu_list[] = { 1408252c4bf1SDerek J. Clark { 1409252c4bf1SDerek J. Clark .ident = "OneXPlayer Apex", 1410252c4bf1SDerek J. Clark .matches = { 1411252c4bf1SDerek J. Clark DMI_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), 1412252c4bf1SDerek J. Clark DMI_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER APEX"), 1413252c4bf1SDerek J. Clark }, 1414252c4bf1SDerek J. Clark .driver_data = &quirk_hybrid_mcu, 1415252c4bf1SDerek J. Clark }, 1416252c4bf1SDerek J. Clark { 1417252c4bf1SDerek J. Clark .ident = "OneXPlayer G1 AMD", 1418252c4bf1SDerek J. Clark .matches = { 1419252c4bf1SDerek J. Clark DMI_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), 1420252c4bf1SDerek J. Clark DMI_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER G1 A"), 1421252c4bf1SDerek J. Clark }, 1422252c4bf1SDerek J. Clark .driver_data = &quirk_hybrid_mcu, 1423252c4bf1SDerek J. Clark }, 1424252c4bf1SDerek J. Clark { 1425252c4bf1SDerek J. Clark .ident = "OneXPlayer G1 Intel", 1426252c4bf1SDerek J. Clark .matches = { 1427252c4bf1SDerek J. Clark DMI_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), 1428252c4bf1SDerek J. Clark DMI_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER G1 i"), 1429252c4bf1SDerek J. Clark }, 1430252c4bf1SDerek J. Clark .driver_data = &quirk_hybrid_mcu, 1431252c4bf1SDerek J. Clark }, 1432252c4bf1SDerek J. Clark {}, 1433252c4bf1SDerek J. Clark }; 1434252c4bf1SDerek J. Clark 1435252c4bf1SDerek J. Clark static bool oxp_hybrid_mcu_device(void) 1436252c4bf1SDerek J. Clark { 1437252c4bf1SDerek J. Clark const struct dmi_system_id *dmi_id; 1438252c4bf1SDerek J. Clark struct quirk_entry *quirks; 1439252c4bf1SDerek J. Clark 1440252c4bf1SDerek J. Clark dmi_id = dmi_first_match(oxp_hybrid_mcu_list); 1441252c4bf1SDerek J. Clark if (!dmi_id) 1442252c4bf1SDerek J. Clark return false; 1443252c4bf1SDerek J. Clark 1444252c4bf1SDerek J. Clark quirks = dmi_id->driver_data; 1445252c4bf1SDerek J. Clark 1446252c4bf1SDerek J. Clark return quirks->hybrid_mcu; 1447252c4bf1SDerek J. Clark } 1448252c4bf1SDerek J. Clark 144984910c45SDerek J. Clark static int oxp_cfg_probe(struct hid_device *hdev, u16 up) 145084910c45SDerek J. Clark { 1451e4c850a6SDerek J. Clark struct oxp_bmap_page_1 *bmap_1; 1452e4c850a6SDerek J. Clark struct oxp_bmap_page_2 *bmap_2; 145384910c45SDerek J. Clark int ret; 145484910c45SDerek J. Clark 145584910c45SDerek J. Clark hid_set_drvdata(hdev, &drvdata); 145684910c45SDerek J. Clark mutex_init(&drvdata.cfg_mutex); 145784910c45SDerek J. Clark drvdata.hdev = hdev; 1458252c4bf1SDerek J. Clark 1459252c4bf1SDerek J. Clark if (up == GEN2_USAGE_PAGE && oxp_hybrid_mcu_device()) 1460252c4bf1SDerek J. Clark goto skip_rgb; 1461252c4bf1SDerek J. Clark 146284910c45SDerek J. Clark drvdata.led_mc = &oxp_cdev_rgb; 146384910c45SDerek J. Clark 146484910c45SDerek J. Clark INIT_DELAYED_WORK(&drvdata.oxp_rgb_queue, oxp_rgb_queue_fn); 146584910c45SDerek J. Clark ret = devm_led_classdev_multicolor_register(&hdev->dev, &oxp_cdev_rgb); 146684910c45SDerek J. Clark if (ret) 146784910c45SDerek J. Clark return dev_err_probe(&hdev->dev, ret, 146884910c45SDerek J. Clark "Failed to create RGB device\n"); 146984910c45SDerek J. Clark 147084910c45SDerek J. Clark ret = devm_device_add_group(drvdata.led_mc->led_cdev.dev, 147184910c45SDerek J. Clark &oxp_rgb_attr_group); 147284910c45SDerek J. Clark if (ret) 147384910c45SDerek J. Clark return dev_err_probe(drvdata.led_mc->led_cdev.dev, ret, 147484910c45SDerek J. Clark "Failed to create RGB configuration attributes\n"); 147584910c45SDerek J. Clark 147684910c45SDerek J. Clark ret = oxp_rgb_status_show(); 147784910c45SDerek J. Clark if (ret) 147884910c45SDerek J. Clark dev_warn(drvdata.led_mc->led_cdev.dev, 147984910c45SDerek J. Clark "Failed to query RGB initial state: %i\n", ret); 148084910c45SDerek J. Clark 14812f424f28SDerek J. Clark /* Below features are only implemented in gen 2 */ 14822f424f28SDerek J. Clark if (up != GEN2_USAGE_PAGE) 14832f424f28SDerek J. Clark return 0; 14842f424f28SDerek J. Clark 1485252c4bf1SDerek J. Clark skip_rgb: 1486e4c850a6SDerek J. Clark bmap_1 = devm_kzalloc(&hdev->dev, sizeof(struct oxp_bmap_page_1), GFP_KERNEL); 1487e4c850a6SDerek J. Clark if (!bmap_1) 1488e4c850a6SDerek J. Clark return dev_err_probe(&hdev->dev, -ENOMEM, 1489e4c850a6SDerek J. Clark "Unable to allocate button map page 1\n"); 1490e4c850a6SDerek J. Clark 1491e4c850a6SDerek J. Clark bmap_2 = devm_kzalloc(&hdev->dev, sizeof(struct oxp_bmap_page_2), GFP_KERNEL); 1492e4c850a6SDerek J. Clark if (!bmap_2) 1493e4c850a6SDerek J. Clark return dev_err_probe(&hdev->dev, -ENOMEM, 1494e4c850a6SDerek J. Clark "Unable to allocate button map page 2\n"); 1495e4c850a6SDerek J. Clark 1496e4c850a6SDerek J. Clark drvdata.bmap_1 = bmap_1; 1497e4c850a6SDerek J. Clark drvdata.bmap_2 = bmap_2; 1498e4c850a6SDerek J. Clark oxp_reset_buttons(); 1499e4c850a6SDerek J. Clark INIT_DELAYED_WORK(&drvdata.oxp_btn_queue, oxp_btn_queue_fn); 1500e4c850a6SDerek J. Clark 15012f424f28SDerek J. Clark drvdata.gamepad_mode = OXP_GP_MODE_XINPUT; 150299bde1dfSDerek J. Clark drvdata.rumble_intensity = 5; 15032f424f28SDerek J. Clark 15042f424f28SDerek J. Clark INIT_DELAYED_WORK(&drvdata.oxp_mcu_init, oxp_mcu_init_fn); 1505*8bb7c0fdSMarco Crivellari mod_delayed_work(system_dfl_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50)); 15062f424f28SDerek J. Clark 15072f424f28SDerek J. Clark ret = devm_device_add_group(&hdev->dev, &oxp_cfg_attrs_group); 15082f424f28SDerek J. Clark if (ret) 15092f424f28SDerek J. Clark return dev_err_probe(&hdev->dev, ret, 15102f424f28SDerek J. Clark "Failed to attach configuration attributes\n"); 15112f424f28SDerek J. Clark 151284910c45SDerek J. Clark return 0; 151384910c45SDerek J. Clark } 151484910c45SDerek J. Clark 151584910c45SDerek J. Clark static int oxp_hid_probe(struct hid_device *hdev, 151684910c45SDerek J. Clark const struct hid_device_id *id) 151784910c45SDerek J. Clark { 151884910c45SDerek J. Clark int ret; 151984910c45SDerek J. Clark u16 up; 152084910c45SDerek J. Clark 152184910c45SDerek J. Clark ret = hid_parse(hdev); 152284910c45SDerek J. Clark if (ret) 152384910c45SDerek J. Clark return dev_err_probe(&hdev->dev, ret, "Failed to parse HID device\n"); 152484910c45SDerek J. Clark 152584910c45SDerek J. Clark ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 152684910c45SDerek J. Clark if (ret) 152784910c45SDerek J. Clark return dev_err_probe(&hdev->dev, ret, "Failed to start HID device\n"); 152884910c45SDerek J. Clark 152984910c45SDerek J. Clark ret = hid_hw_open(hdev); 153084910c45SDerek J. Clark if (ret) { 153184910c45SDerek J. Clark hid_hw_stop(hdev); 153284910c45SDerek J. Clark return dev_err_probe(&hdev->dev, ret, "Failed to open HID device\n"); 153384910c45SDerek J. Clark } 153484910c45SDerek J. Clark 153584910c45SDerek J. Clark up = get_usage_page(hdev); 153684910c45SDerek J. Clark dev_dbg(&hdev->dev, "Got usage page %04x\n", up); 153784910c45SDerek J. Clark 153884910c45SDerek J. Clark switch (up) { 153984910c45SDerek J. Clark case GEN1_USAGE_PAGE: 1540252c4bf1SDerek J. Clark case GEN2_USAGE_PAGE: 154184910c45SDerek J. Clark ret = oxp_cfg_probe(hdev, up); 154284910c45SDerek J. Clark if (ret) { 154384910c45SDerek J. Clark hid_hw_close(hdev); 154484910c45SDerek J. Clark hid_hw_stop(hdev); 154584910c45SDerek J. Clark } 154684910c45SDerek J. Clark 154784910c45SDerek J. Clark return ret; 154884910c45SDerek J. Clark default: 154984910c45SDerek J. Clark return 0; 155084910c45SDerek J. Clark } 155184910c45SDerek J. Clark } 155284910c45SDerek J. Clark 155384910c45SDerek J. Clark static void oxp_hid_remove(struct hid_device *hdev) 155484910c45SDerek J. Clark { 155584910c45SDerek J. Clark cancel_delayed_work(&drvdata.oxp_rgb_queue); 1556e4c850a6SDerek J. Clark cancel_delayed_work(&drvdata.oxp_btn_queue); 15572f424f28SDerek J. Clark cancel_delayed_work(&drvdata.oxp_mcu_init); 155884910c45SDerek J. Clark hid_hw_close(hdev); 155984910c45SDerek J. Clark hid_hw_stop(hdev); 156084910c45SDerek J. Clark } 156184910c45SDerek J. Clark 156284910c45SDerek J. Clark static const struct hid_device_id oxp_devices[] = { 156384910c45SDerek J. Clark { HID_USB_DEVICE(USB_VENDOR_ID_CRSC, USB_DEVICE_ID_ONEXPLAYER_GEN1) }, 1564252c4bf1SDerek J. Clark { HID_USB_DEVICE(USB_VENDOR_ID_WCH, USB_DEVICE_ID_ONEXPLAYER_GEN2) }, 156584910c45SDerek J. Clark {} 156684910c45SDerek J. Clark }; 156784910c45SDerek J. Clark 156884910c45SDerek J. Clark MODULE_DEVICE_TABLE(hid, oxp_devices); 156984910c45SDerek J. Clark static struct hid_driver hid_oxp = { 157084910c45SDerek J. Clark .name = "hid-oxp", 157184910c45SDerek J. Clark .id_table = oxp_devices, 157284910c45SDerek J. Clark .probe = oxp_hid_probe, 157384910c45SDerek J. Clark .remove = oxp_hid_remove, 157484910c45SDerek J. Clark .raw_event = oxp_hid_raw_event, 157584910c45SDerek J. Clark }; 157684910c45SDerek J. Clark module_hid_driver(hid_oxp); 157784910c45SDerek J. Clark 157884910c45SDerek J. Clark MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>"); 157984910c45SDerek J. Clark MODULE_DESCRIPTION("Driver for OneXPlayer HID Interfaces"); 158084910c45SDerek J. Clark MODULE_LICENSE("GPL"); 1581