From 9fd3de7abd22d7e67a8757e3f67410302359fec7 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" <luke@ljones.dev> Date: Fri, 1 Dec 2023 16:57:19 +1300 Subject: [PATCH v14.7 4/4] HID: asus: add ROG Ally xpad settings - move ROG specific stuff to new .c - add a header for common parts - add xpad mode - add deadzones - add anti-deadzones - add gamepad button remapping - add gamepad mapping reset for xpad and wasd modes - add turbo mode for individual buttons - add joystick response curves - add vibration intensity settings - add calibration setting --- .../ABI/testing/sysfs-driver-hid-asus | 85 + drivers/hid/Makefile | 2 + drivers/hid/{hid-asus.c => hid-asus-core.c} | 71 +- drivers/hid/hid-asus-rog.c | 1469 +++++++++++++++++ drivers/hid/hid-asus-rog.h | 482 ++++++ drivers/hid/hid-asus.h | 58 + 6 files changed, 2126 insertions(+), 41 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-asus rename drivers/hid/{hid-asus.c => hid-asus-core.c} (96%) create mode 100644 drivers/hid/hid-asus-rog.c create mode 100644 drivers/hid/hid-asus-rog.h create mode 100644 drivers/hid/hid-asus.h diff --git a/Documentation/ABI/testing/sysfs-driver-hid-asus b/Documentation/ABI/testing/sysfs-driver-hid-asus new file mode 100644 index 000000000000..df5b0c5b0702 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-asus @@ -0,0 +1,85 @@ +What: /sys/bus/usb/devices/1-3:1.0/0003:0B05:1ABE.0001/gamepad_mode +Date: December 2023 +Contact: linux-input@vger.kernel.org +Description: Set the mode the ROG Ally xpad operates in: + - 1 = Game mode + - 2 = WASD mode + - 3 = Mouse mode + This setting applies instantly and applies settings that were previously changed + under that mode which are: + - deadzones + - anti-deadzones + - button mapping + - button turbo settings + - response curves + +What: /sys/bus/usb/devices/1-3:1.0/0003:0B05:1ABE.0001/apply +Date: December 2023 +Contact: linux-input@vger.kernel.org +Description: Apply the settings that have been stored in attributes so far. Because there are + many individual settings across a dozen packets this separation is required to + prevent spamming the MCU when userspace applications apply many changes at once. + +What: /sys/bus/usb/devices/1-3:1.0/0003:0B05:1ABE.0001/reset_btn_mapping +Date: December 2023 +Contact: linux-input@vger.kernel.org +Description: Reset a gamepad mode to its default button mapping. + +What: /sys/bus/usb/devices/1-3:1.0/0003:0B05:1ABE.0001/axis_<x/y/z>_<left/right>/deadzone +Date: December 2023 +Contact: linux-input@vger.kernel.org +Description: Set the inner and outer deadzones of joysticks and triggers. These settings are not + written to the MCU until `apply` is set. + - range 0-64 (corresponds to 0-100%) + +What: /sys/bus/usb/devices/1-3:1.0/0003:0B05:1ABE.0001/axis_<x/y/z>_<left/right>/deadzone_index +Date: December 2023 +Contact: linux-input@vger.kernel.org +Description: Descriptive labels for joystick deadzone array. + +What: /sys/bus/usb/devices/1-3:1.0/0003:0B05:1ABE.0001/axis_<x/y>_<left/right>/anti-deadzone +Date: December 2023 +Contact: linux-input@vger.kernel.org +Description: Set the joystick anti-deadzone feature: + - range 0-32 (corresponds to 0-50%) + +What: /sys/bus/usb/devices/1-3:1.0/0003:0B05:1ABE.0001/axis_<x/y/z>_<left/right>/calibration +Date: December 2023 +Contact: linux-input@vger.kernel.org +Description: Calibration values for the joysticks and trigger analogues. There are no default + values as the calibration is determined in userspace. + +What: /sys/bus/usb/devices/1-3:1.0/0003:0B05:1ABE.0001/axis_<x/y/z>_<left/right>/calibration_index +Date: December 2023 +Contact: linux-input@vger.kernel.org +Description: Descriptive labels for joystick and triggers calibration array. + +What: /sys/bus/usb/devices/1-3:1.0/0003:0B05:1ABE.0001/axis_<x/y>_<left/right>/rc_point<n> +Date: December 2023 +Contact: linux-input@vger.kernel.org +Description: Set the joystick response curve. There are 4 points available with 1 being the lowest + point and 4 being the highest point. + - range 0-64 (corresponds to 0-100%) + +What: /sys/bus/usb/devices/1-3:1.0/0003:0B05:1ABE.0001/axis_<x/y>_<left/right>/rc_point_index +Date: December 2023 +Contact: linux-input@vger.kernel.org +Description: Descriptive labels for joystick response curve points. + +What: /sys/bus/usb/devices/1-3:1.0/0003:0B05:1ABE.0001/btn_<label>/turbo +Date: December 2023 +Contact: linux-input@vger.kernel.org +Description: Set the turbo mode of the button: + - 0 = no turbo, a separate press and release is registered on press and release + - 1-16 = interval between presses if button held down in steps of 1000ms/16 + These settings are not written to the MCU until `apply` is set. + +What: /sys/bus/usb/devices/1-3:1.0/0003:0B05:1ABE.0001/vibration_intensity +Date: December 2023 +Contact: linux-input@vger.kernel.org +Description: Set the vibration intensity for left and right haptics. Applies instantly. + +What: /sys/bus/usb/devices/1-3:1.0/0003:0B05:1ABE.0001/vibration_intensity_index +Date: December 2023 +Contact: linux-input@vger.kernel.org +Description: Descriptive labels for index points of vibration_intensity. \ No newline at end of file diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 8a06d0f840bc..7c2436ed05d6 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -23,6 +23,8 @@ hid-logitech-$(CONFIG_LOGIWHEELS_FF) += hid-lg4ff.o hid-wiimote-y := hid-wiimote-core.o hid-wiimote-modules.o hid-wiimote-$(CONFIG_DEBUG_FS) += hid-wiimote-debug.o +hid-asus-y := hid-asus-core.o hid-asus-rog.o + obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_ACCUTOUCH) += hid-accutouch.o obj-$(CONFIG_HID_ALPS) += hid-alps.o diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus-core.c similarity index 96% rename from drivers/hid/hid-asus.c rename to drivers/hid/hid-asus-core.c index 3a1a6024d299..026705c43ee1 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus-core.c @@ -20,9 +20,8 @@ * Copyright (c) 2016 Frederik Wenigwieser <frederik.wenigwieser@gmail.com> */ -/* - */ - +#include <asm-generic/errno-base.h> +#include <asm-generic/errno.h> #include <linux/dmi.h> #include <linux/hid.h> #include <linux/module.h> @@ -32,6 +31,7 @@ #include <linux/power_supply.h> #include <linux/leds.h> +#include "hid-asus.h" #include "hid-ids.h" MODULE_AUTHOR("Yusuke Fujimaki <usk.fujimaki@gmail.com>"); @@ -47,10 +47,6 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); #define T100CHI_MOUSE_REPORT_ID 0x06 #define FEATURE_REPORT_ID 0x0d #define INPUT_REPORT_ID 0x5d -#define FEATURE_KBD_REPORT_ID 0x5a -#define FEATURE_KBD_REPORT_SIZE 16 -#define FEATURE_KBD_LED_REPORT_ID1 0x5d -#define FEATURE_KBD_LED_REPORT_ID2 0x5e #define SUPPORT_KBD_BACKLIGHT BIT(0) @@ -71,20 +67,6 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); #define BATTERY_STAT_CHARGING (1) #define BATTERY_STAT_FULL (2) -#define QUIRK_FIX_NOTEBOOK_REPORT BIT(0) -#define QUIRK_NO_INIT_REPORTS BIT(1) -#define QUIRK_SKIP_INPUT_MAPPING BIT(2) -#define QUIRK_IS_MULTITOUCH BIT(3) -#define QUIRK_NO_CONSUMER_USAGES BIT(4) -#define QUIRK_USE_KBD_BACKLIGHT BIT(5) -#define QUIRK_T100_KEYBOARD BIT(6) -#define QUIRK_T100CHI BIT(7) -#define QUIRK_G752_KEYBOARD BIT(8) -#define QUIRK_T90CHI BIT(9) -#define QUIRK_MEDION_E1239T BIT(10) -#define QUIRK_ROG_NKEY_KEYBOARD BIT(11) -#define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12) - #define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \ QUIRK_NO_CONSUMER_USAGES) @@ -113,22 +95,6 @@ struct asus_touchpad_info { int report_size; }; -struct asus_drvdata { - unsigned long quirks; - struct hid_device *hdev; - struct input_dev *input; - struct input_dev *tp_kbd_input; - struct asus_kbd_leds *kbd_backlight; - const struct asus_touchpad_info *tp; - bool enable_backlight; - struct power_supply *battery; - struct power_supply_desc battery_desc; - int battery_capacity; - int battery_stat; - bool battery_in_query; - unsigned long battery_next_query; -}; - static int asus_report_battery(struct asus_drvdata *, u8 *, int); static const struct asus_touchpad_info asus_i2c_tp = { @@ -329,6 +295,16 @@ static int asus_raw_event(struct hid_device *hdev, if (drvdata->battery && data[0] == BATTERY_REPORT_ID) return asus_report_battery(drvdata, data, size); + // TODO: remove after debugging + // if (data[0] == 0x5a || data[0] == 0x5d || data[0] == 0x5e){ + // for (int i = 0; i < size; i++) { + // if (i == 0) + // printk(KERN_INFO "GOT: %02x,", data[i]); + // else + // printk(KERN_CONT "%02x,", data[i]); + // } + // } + if (drvdata->tp && data[0] == INPUT_REPORT_ID) return asus_report_input(drvdata, data, size); @@ -365,7 +341,7 @@ static int asus_raw_event(struct hid_device *hdev, return 0; } -static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t buf_size) +int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t buf_size) { unsigned char *dmabuf; int ret; @@ -386,6 +362,13 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu return ret; } +int asus_kbd_get_report(struct hid_device *hdev, u8 *out_buf, size_t out_buf_size) +{ + return hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, out_buf, + out_buf_size, HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); +} + static int asus_kbd_init(struct hid_device *hdev, u8 report_id) { const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54, @@ -846,8 +829,8 @@ static int asus_input_mapping(struct hid_device *hdev, case 0xb2: asus_map_key_clear(KEY_PROG2); break; /* Fn+Left previous aura */ case 0xb3: asus_map_key_clear(KEY_PROG3); break; /* Fn+Left next aura */ case 0x6a: asus_map_key_clear(KEY_F13); break; /* Screenpad toggle */ - case 0x4b: asus_map_key_clear(KEY_F14); break; /* Arrows/Pg-Up/Dn toggle */ - case 0xa5: asus_map_key_clear(KEY_F15); break; /* ROG Ally left back */ + case 0x4b: asus_map_key_clear(KEY_F14); break; /* Arrows/Pg-Up/Dn toggle, Ally M1 */ + case 0xa5: asus_map_key_clear(KEY_F15); break; /* ROG Ally M2 */ case 0xa6: asus_map_key_clear(KEY_F16); break; /* ROG Ally QAM button */ case 0xa7: asus_map_key_clear(KEY_F17); break; /* ROG Ally ROG long-press */ case 0xa8: asus_map_key_clear(KEY_F18); break; /* ROG Ally ROG long-press-release */ @@ -1063,6 +1046,10 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) } } + /* all ROG devices have this HID interface but we will focus on Ally for now */ + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && hid_is_usb(hdev)) + rog_ally.probe(hdev, &rog_ally); + ret = hid_parse(hdev); if (ret) { hid_err(hdev, "Asus hid parse failed: %d\n", ret); @@ -1112,6 +1099,8 @@ static void asus_remove(struct hid_device *hdev) cancel_work_sync(&drvdata->kbd_backlight->work); } + rog_ally.remove(hdev, &rog_ally); + hid_hw_stop(hdev); } @@ -1245,7 +1234,7 @@ static const struct hid_device_id asus_devices[] = { QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_ALLY_XPAD }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD), QUIRK_ROG_CLAYMORE_II_KEYBOARD }, diff --git a/drivers/hid/hid-asus-rog.c b/drivers/hid/hid-asus-rog.c new file mode 100644 index 000000000000..94b7c986576c --- /dev/null +++ b/drivers/hid/hid-asus-rog.c @@ -0,0 +1,1469 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HID driver for Asus ROG laptops and Ally + * + * Copyright (c) 2023 Luke Jones <luke@ljones.dev> + */ + +#include <linux/hid.h> +#include <linux/types.h> +#include <linux/usb.h> + +#include "hid-asus.h" +#include "hid-asus-rog.h" + +/* ROG Ally has many settings related to the gamepad, all using the same n-key endpoint */ +struct asus_rog_ally { + enum xpad_mode mode; + /* + * index: [joysticks/triggers][left(2 bytes), right(2 bytes)] + * joysticks: 2 bytes: inner, outer + * triggers: 2 bytes: lower, upper + * min/max: 0-64 + */ + u8 deadzones[xpad_mode_mouse][2][4]; + /* + * index: left, right + * max: 64 + */ + u8 vibration_intensity[xpad_mode_mouse][2]; + /* + * index: [joysticks][2 byte stepping per point] + * - 4 points of 2 bytes each + * - byte 0 of pair = stick move % + * - byte 1 of pair = stick response % + * - min/max: 1-63 + */ + bool supports_response_curves; + u8 response_curve[xpad_mode_mouse][2][8]; + /* + * left = byte 0, right = byte 1 + */ + bool supports_anti_deadzones; + u8 anti_deadzones[xpad_mode_mouse][2]; + /* + * index: [mode][phys pair][b1, b1 secondary, b2, b2 secondary, blocks of 11] + */ + u8 key_mapping[xpad_mode_mouse][btn_pair_lt_rt][MAPPING_BLOCK_LEN]; + /* + * + */ + u8 turbo_btns[xpad_mode_mouse][TURBO_BLOCK_LEN]; + /* + */ + u32 js_calibrations[2][6]; + u32 tr_calibrations[2][2]; +}; + +static struct asus_rog_ally *__rog_ally_data(struct device *raw_dev) +{ + struct hid_device *hdev = to_hid_device(raw_dev); + return ((struct asus_drvdata *)hid_get_drvdata(hdev))->rog_ally_data; +} + +#define STR_TO_CODE_IF(_idx, _code, _label) \ + if (!strcmp(buf, _label)) \ + out[_idx] = _code; + +#define STR_TO_CODE_ELIF(_idx, _code, _label) else if (!strcmp(buf, _label)) out[_idx] = _code; + +/* writes the bytes for a requested key/function in to the out buffer */ +const static int __string_to_key_code(const char *buf, u8 *out, int out_len) +{ + u8 *save_buf; + + if (out_len != BTN_CODE_LEN) + return -EINVAL; + + save_buf = kzalloc(out_len, GFP_KERNEL); + if (!save_buf) + return -ENOMEM; + memcpy(save_buf, out, out_len); + memset(out, 0, out_len); // always clear before adjusting + + // Allow clearing + if (!strcmp(buf, " ") || !strcmp(buf, "\n")) + goto success; + + // set group xpad + out[0] = 0x01; + STR_TO_CODE_IF(1, 0x01, PAD_A) + STR_TO_CODE_ELIF(1, 0x02, PAD_B) + STR_TO_CODE_ELIF(1, 0x03, PAD_X) + STR_TO_CODE_ELIF(1, 0x04, PAD_Y) + STR_TO_CODE_ELIF(1, 0x05, PAD_LB) + STR_TO_CODE_ELIF(1, 0x06, PAD_RB) + STR_TO_CODE_ELIF(1, 0x07, PAD_LS) + STR_TO_CODE_ELIF(1, 0x08, PAD_RS) + STR_TO_CODE_ELIF(1, 0x09, PAD_DPAD_UP) + STR_TO_CODE_ELIF(1, 0x0a, PAD_DPAD_DOWN) + STR_TO_CODE_ELIF(1, 0x0b, PAD_DPAD_LEFT) + STR_TO_CODE_ELIF(1, 0x0c, PAD_DPAD_RIGHT) + STR_TO_CODE_ELIF(1, 0x11, PAD_VIEW) + STR_TO_CODE_ELIF(1, 0x12, PAD_MENU) + STR_TO_CODE_ELIF(1, 0x13, PAD_XBOX) + if (out[1]) + goto success; + + // set group keyboard + out[0] = 0x02; + STR_TO_CODE_IF(2, 0x8f, KB_M1) + STR_TO_CODE_ELIF(2, 0x8e, KB_M2) + + STR_TO_CODE_ELIF(2, 0x76, KB_ESC) + STR_TO_CODE_ELIF(2, 0x50, KB_F1) + STR_TO_CODE_ELIF(2, 0x60, KB_F2) + STR_TO_CODE_ELIF(2, 0x40, KB_F3) + STR_TO_CODE_ELIF(2, 0x0c, KB_F4) + STR_TO_CODE_ELIF(2, 0x03, KB_F5) + STR_TO_CODE_ELIF(2, 0x0b, KB_F6) + STR_TO_CODE_ELIF(2, 0x80, KB_F7) + STR_TO_CODE_ELIF(2, 0x0a, KB_F8) + STR_TO_CODE_ELIF(2, 0x01, KB_F9) + STR_TO_CODE_ELIF(2, 0x09, KB_F10) + STR_TO_CODE_ELIF(2, 0x78, KB_F11) + STR_TO_CODE_ELIF(2, 0x07, KB_F12) + STR_TO_CODE_ELIF(2, 0x10, KB_F14) + STR_TO_CODE_ELIF(2, 0x18, KB_F15) + + STR_TO_CODE_ELIF(2, 0x0e, KB_BACKTICK) + STR_TO_CODE_ELIF(2, 0x16, KB_1) + STR_TO_CODE_ELIF(2, 0x1e, KB_2) + STR_TO_CODE_ELIF(2, 0x26, KB_3) + STR_TO_CODE_ELIF(2, 0x25, KB_4) + STR_TO_CODE_ELIF(2, 0x2e, KB_5) + STR_TO_CODE_ELIF(2, 0x36, KB_6) + STR_TO_CODE_ELIF(2, 0x3d, KB_7) + STR_TO_CODE_ELIF(2, 0x3e, KB_8) + STR_TO_CODE_ELIF(2, 0x46, KB_9) + STR_TO_CODE_ELIF(2, 0x45, KB_0) + STR_TO_CODE_ELIF(2, 0x4e, KB_HYPHEN) + STR_TO_CODE_ELIF(2, 0x55, KB_EQUALS) + STR_TO_CODE_ELIF(2, 0x66, KB_BACKSPACE) + + STR_TO_CODE_ELIF(2, 0x0d, KB_TAB) + STR_TO_CODE_ELIF(2, 0x15, KB_Q) + STR_TO_CODE_ELIF(2, 0x1d, KB_W) + STR_TO_CODE_ELIF(2, 0x24, KB_E) + STR_TO_CODE_ELIF(2, 0x2d, KB_R) + STR_TO_CODE_ELIF(2, 0x2d, KB_T) + STR_TO_CODE_ELIF(2, 0x35, KB_Y) + STR_TO_CODE_ELIF(2, 0x3c, KB_U) + STR_TO_CODE_ELIF(2, 0x43, KB_I) + STR_TO_CODE_ELIF(2, 0x44, KB_O) + STR_TO_CODE_ELIF(2, 0x4d, KB_P) + STR_TO_CODE_ELIF(2, 0x54, KB_LBRACKET) + STR_TO_CODE_ELIF(2, 0x5b, KB_RBRACKET) + STR_TO_CODE_ELIF(2, 0x5d, KB_BACKSLASH) + + STR_TO_CODE_ELIF(2, 0x58, KB_CAPS) + STR_TO_CODE_ELIF(2, 0x1c, KB_A) + STR_TO_CODE_ELIF(2, 0x1b, KB_S) + STR_TO_CODE_ELIF(2, 0x23, KB_D) + STR_TO_CODE_ELIF(2, 0x2b, KB_F) + STR_TO_CODE_ELIF(2, 0x34, KB_G) + STR_TO_CODE_ELIF(2, 0x33, KB_H) + STR_TO_CODE_ELIF(2, 0x3b, KB_J) + STR_TO_CODE_ELIF(2, 0x42, KB_K) + STR_TO_CODE_ELIF(2, 0x4b, KB_L) + STR_TO_CODE_ELIF(2, 0x4c, KB_SEMI) + STR_TO_CODE_ELIF(2, 0x52, KB_QUOTE) + STR_TO_CODE_ELIF(2, 0x5a, KB_RET) + + STR_TO_CODE_ELIF(2, 0x88, KB_LSHIFT) + STR_TO_CODE_ELIF(2, 0x1a, KB_Z) + STR_TO_CODE_ELIF(2, 0x22, KB_X) + STR_TO_CODE_ELIF(2, 0x21, KB_C) + STR_TO_CODE_ELIF(2, 0x2a, KB_V) + STR_TO_CODE_ELIF(2, 0x32, KB_B) + STR_TO_CODE_ELIF(2, 0x31, KB_N) + STR_TO_CODE_ELIF(2, 0x3a, KB_M) + STR_TO_CODE_ELIF(2, 0x41, KB_COMMA) + STR_TO_CODE_ELIF(2, 0x49, KB_PERIOD) + STR_TO_CODE_ELIF(2, 0x4a, KB_FWDSLASH) + STR_TO_CODE_ELIF(2, 0x89, KB_RSHIFT) + + STR_TO_CODE_ELIF(2, 0x8c, KB_LCTL) + STR_TO_CODE_ELIF(2, 0x82, KB_META) + STR_TO_CODE_ELIF(2, 0xba, KB_LALT) + STR_TO_CODE_ELIF(2, 0x29, KB_SPACE) + STR_TO_CODE_ELIF(2, 0x8b, KB_RALT) + STR_TO_CODE_ELIF(2, 0x84, KB_MENU) + STR_TO_CODE_ELIF(2, 0x8d, KB_RCTL) + + STR_TO_CODE_ELIF(2, 0xc3, KB_PRNTSCN) + STR_TO_CODE_ELIF(2, 0x7e, KB_SCRLCK) + STR_TO_CODE_ELIF(2, 0x91, KB_PAUSE) + STR_TO_CODE_ELIF(2, 0xc2, KB_INS) + STR_TO_CODE_ELIF(2, 0x94, KB_HOME) + STR_TO_CODE_ELIF(2, 0x96, KB_PGUP) + STR_TO_CODE_ELIF(2, 0xc0, KB_DEL) + STR_TO_CODE_ELIF(2, 0x95, KB_END) + STR_TO_CODE_ELIF(2, 0x97, KB_PGDWN) + + STR_TO_CODE_ELIF(2, 0x99, KB_UP_ARROW) + STR_TO_CODE_ELIF(2, 0x98, KB_DOWN_ARROW) + STR_TO_CODE_ELIF(2, 0x91, KB_LEFT_ARROW) + STR_TO_CODE_ELIF(2, 0x9b, KB_RIGHT_ARROW) + + STR_TO_CODE_ELIF(2, 0x77, NUMPAD_LOCK) + STR_TO_CODE_ELIF(2, 0x90, NUMPAD_FWDSLASH) + STR_TO_CODE_ELIF(2, 0x7c, NUMPAD_ASTERISK) + STR_TO_CODE_ELIF(2, 0x7b, NUMPAD_HYPHEN) + STR_TO_CODE_ELIF(2, 0x70, NUMPAD_0) + STR_TO_CODE_ELIF(2, 0x69, NUMPAD_1) + STR_TO_CODE_ELIF(2, 0x72, NUMPAD_2) + STR_TO_CODE_ELIF(2, 0x7a, NUMPAD_3) + STR_TO_CODE_ELIF(2, 0x6b, NUMPAD_4) + STR_TO_CODE_ELIF(2, 0x73, NUMPAD_5) + STR_TO_CODE_ELIF(2, 0x74, NUMPAD_6) + STR_TO_CODE_ELIF(2, 0x6c, NUMPAD_7) + STR_TO_CODE_ELIF(2, 0x75, NUMPAD_8) + STR_TO_CODE_ELIF(2, 0x7d, NUMPAD_9) + STR_TO_CODE_ELIF(2, 0x79, NUMPAD_PLUS) + STR_TO_CODE_ELIF(2, 0x81, NUMPAD_ENTER) + STR_TO_CODE_ELIF(2, 0x71, NUMPAD_PERIOD) + if (out[2]) + goto success; + + out[0] = 0x03; + STR_TO_CODE_IF(4, 0x01, RAT_LCLICK) + STR_TO_CODE_ELIF(4, 0x02, RAT_RCLICK) + STR_TO_CODE_ELIF(4, 0x03, RAT_MCLICK) + STR_TO_CODE_ELIF(4, 0x04, RAT_WHEEL_UP) + STR_TO_CODE_ELIF(4, 0x05, RAT_WHEEL_DOWN) + if (out[4] != 0) + goto success; + + out[0] = 0x05; + STR_TO_CODE_IF(3, 0x16, MEDIA_SCREENSHOT) + STR_TO_CODE_ELIF(3, 0x19, MEDIA_SHOW_KEYBOARD) + STR_TO_CODE_ELIF(3, 0x1c, MEDIA_SHOW_DESKTOP) + STR_TO_CODE_ELIF(3, 0x1e, MEDIA_START_RECORDING) + STR_TO_CODE_ELIF(3, 0x01, MEDIA_MIC_OFF) + STR_TO_CODE_ELIF(3, 0x02, MEDIA_VOL_DOWN) + STR_TO_CODE_ELIF(3, 0x03, MEDIA_VOL_UP) + if (out[3]) + goto success; + + // restore bytes if invalid input + memcpy(out, save_buf, out_len); + kfree(save_buf); + return -EINVAL; + +success: + kfree(save_buf); + return 0; +} + +#define CODE_TO_STR_IF(_idx, _code, _label) \ + if (btn_block[_idx] == _code) \ + return _label; + +const static char *__btn_map_to_string(struct device *raw_dev, enum btn_pair pair, + enum btn_pair_side side, bool secondary) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + u8 *btn_block; + int offs; + + // TODO: this little block is common + offs = side ? MAPPING_BLOCK_LEN / 2 : 0; + offs = secondary ? offs + BTN_CODE_LEN : offs; + btn_block = rog_ally->key_mapping[rog_ally->mode - 1][pair - 1] + offs; + + if (btn_block[0] == 0x01) { + CODE_TO_STR_IF(1, 0x01, PAD_A) + CODE_TO_STR_IF(1, 0x02, PAD_B) + CODE_TO_STR_IF(1, 0x03, PAD_X) + CODE_TO_STR_IF(1, 0x04, PAD_Y) + CODE_TO_STR_IF(1, 0x05, PAD_LB) + CODE_TO_STR_IF(1, 0x06, PAD_RB) + CODE_TO_STR_IF(1, 0x07, PAD_LS) + CODE_TO_STR_IF(1, 0x08, PAD_RS) + CODE_TO_STR_IF(1, 0x09, PAD_DPAD_UP) + CODE_TO_STR_IF(1, 0x0a, PAD_DPAD_DOWN) + CODE_TO_STR_IF(1, 0x0b, PAD_DPAD_LEFT) + CODE_TO_STR_IF(1, 0x0c, PAD_DPAD_RIGHT) + CODE_TO_STR_IF(1, 0x11, PAD_VIEW) + CODE_TO_STR_IF(1, 0x12, PAD_MENU) + CODE_TO_STR_IF(1, 0x13, PAD_XBOX) + } + + if (btn_block[0] == 0x02) { + CODE_TO_STR_IF(2, 0x8f, KB_M1) + CODE_TO_STR_IF(2, 0x8e, KB_M2) + CODE_TO_STR_IF(2, 0x76, KB_ESC) + CODE_TO_STR_IF(2, 0x50, KB_F1) + CODE_TO_STR_IF(2, 0x60, KB_F2) + CODE_TO_STR_IF(2, 0x40, KB_F3) + CODE_TO_STR_IF(2, 0x0c, KB_F4) + CODE_TO_STR_IF(2, 0x03, KB_F5) + CODE_TO_STR_IF(2, 0x0b, KB_F6) + CODE_TO_STR_IF(2, 0x80, KB_F7) + CODE_TO_STR_IF(2, 0x0a, KB_F8) + CODE_TO_STR_IF(2, 0x01, KB_F9) + CODE_TO_STR_IF(2, 0x09, KB_F10) + CODE_TO_STR_IF(2, 0x78, KB_F11) + CODE_TO_STR_IF(2, 0x07, KB_F12) + CODE_TO_STR_IF(2, 0x10, KB_F14) + CODE_TO_STR_IF(2, 0x18, KB_F15) + + CODE_TO_STR_IF(2, 0x0e, KB_BACKTICK) + CODE_TO_STR_IF(2, 0x16, KB_1) + CODE_TO_STR_IF(2, 0x1e, KB_2) + CODE_TO_STR_IF(2, 0x26, KB_3) + CODE_TO_STR_IF(2, 0x25, KB_4) + CODE_TO_STR_IF(2, 0x2e, KB_5) + CODE_TO_STR_IF(2, 0x36, KB_6) + CODE_TO_STR_IF(2, 0x3d, KB_7) + CODE_TO_STR_IF(2, 0x3e, KB_8) + CODE_TO_STR_IF(2, 0x46, KB_9) + CODE_TO_STR_IF(2, 0x45, KB_0) + CODE_TO_STR_IF(2, 0x4e, KB_HYPHEN) + CODE_TO_STR_IF(2, 0x55, KB_EQUALS) + CODE_TO_STR_IF(2, 0x66, KB_BACKSPACE) + + CODE_TO_STR_IF(2, 0x0d, KB_TAB) + CODE_TO_STR_IF(2, 0x15, KB_Q) + CODE_TO_STR_IF(2, 0x1d, KB_W) + CODE_TO_STR_IF(2, 0x24, KB_E) + CODE_TO_STR_IF(2, 0x2d, KB_R) + CODE_TO_STR_IF(2, 0x2d, KB_T) + CODE_TO_STR_IF(2, 0x35, KB_Y) + CODE_TO_STR_IF(2, 0x3c, KB_U) + CODE_TO_STR_IF(2, 0x43, KB_I) + CODE_TO_STR_IF(2, 0x44, KB_O) + CODE_TO_STR_IF(2, 0x4d, KB_P) + CODE_TO_STR_IF(2, 0x54, KB_LBRACKET) + CODE_TO_STR_IF(2, 0x5b, KB_RBRACKET) + CODE_TO_STR_IF(2, 0x5d, KB_BACKSLASH) + + CODE_TO_STR_IF(2, 0x58, KB_CAPS) + CODE_TO_STR_IF(2, 0x1c, KB_A) + CODE_TO_STR_IF(2, 0x1b, KB_S) + CODE_TO_STR_IF(2, 0x23, KB_D) + CODE_TO_STR_IF(2, 0x2b, KB_F) + CODE_TO_STR_IF(2, 0x34, KB_G) + CODE_TO_STR_IF(2, 0x33, KB_H) + CODE_TO_STR_IF(2, 0x3b, KB_J) + CODE_TO_STR_IF(2, 0x42, KB_K) + CODE_TO_STR_IF(2, 0x4b, KB_L) + CODE_TO_STR_IF(2, 0x4c, KB_SEMI) + CODE_TO_STR_IF(2, 0x52, KB_QUOTE) + CODE_TO_STR_IF(2, 0x5a, KB_RET) + + CODE_TO_STR_IF(2, 0x88, KB_LSHIFT) + CODE_TO_STR_IF(2, 0x1a, KB_Z) + CODE_TO_STR_IF(2, 0x22, KB_X) + CODE_TO_STR_IF(2, 0x21, KB_C) + CODE_TO_STR_IF(2, 0x2a, KB_V) + CODE_TO_STR_IF(2, 0x32, KB_B) + CODE_TO_STR_IF(2, 0x31, KB_N) + CODE_TO_STR_IF(2, 0x3a, KB_M) + CODE_TO_STR_IF(2, 0x41, KB_COMMA) + CODE_TO_STR_IF(2, 0x49, KB_PERIOD) + CODE_TO_STR_IF(2, 0x4a, KB_FWDSLASH) + CODE_TO_STR_IF(2, 0x89, KB_RSHIFT) + + CODE_TO_STR_IF(2, 0x8c, KB_LCTL) + CODE_TO_STR_IF(2, 0x82, KB_META) + CODE_TO_STR_IF(2, 0xba, KB_LALT) + CODE_TO_STR_IF(2, 0x29, KB_SPACE) + CODE_TO_STR_IF(2, 0x8b, KB_RALT) + CODE_TO_STR_IF(2, 0x84, KB_MENU) + CODE_TO_STR_IF(2, 0x8d, KB_RCTL) + + CODE_TO_STR_IF(2, 0xc3, KB_PRNTSCN) + CODE_TO_STR_IF(2, 0x7e, KB_SCRLCK) + CODE_TO_STR_IF(2, 0x91, KB_PAUSE) + CODE_TO_STR_IF(2, 0xc2, KB_INS) + CODE_TO_STR_IF(2, 0x94, KB_HOME) + CODE_TO_STR_IF(2, 0x96, KB_PGUP) + CODE_TO_STR_IF(2, 0xc0, KB_DEL) + CODE_TO_STR_IF(2, 0x95, KB_END) + CODE_TO_STR_IF(2, 0x97, KB_PGDWN) + + CODE_TO_STR_IF(2, 0x99, KB_UP_ARROW) + CODE_TO_STR_IF(2, 0x98, KB_DOWN_ARROW) + CODE_TO_STR_IF(2, 0x91, KB_LEFT_ARROW) + CODE_TO_STR_IF(2, 0x9b, KB_RIGHT_ARROW) + + CODE_TO_STR_IF(2, 0x77, NUMPAD_LOCK) + CODE_TO_STR_IF(2, 0x90, NUMPAD_FWDSLASH) + CODE_TO_STR_IF(2, 0x7c, NUMPAD_ASTERISK) + CODE_TO_STR_IF(2, 0x7b, NUMPAD_HYPHEN) + CODE_TO_STR_IF(2, 0x70, NUMPAD_0) + CODE_TO_STR_IF(2, 0x69, NUMPAD_1) + CODE_TO_STR_IF(2, 0x72, NUMPAD_2) + CODE_TO_STR_IF(2, 0x7a, NUMPAD_3) + CODE_TO_STR_IF(2, 0x6b, NUMPAD_4) + CODE_TO_STR_IF(2, 0x73, NUMPAD_5) + CODE_TO_STR_IF(2, 0x74, NUMPAD_6) + CODE_TO_STR_IF(2, 0x6c, NUMPAD_7) + CODE_TO_STR_IF(2, 0x75, NUMPAD_8) + CODE_TO_STR_IF(2, 0x7d, NUMPAD_9) + CODE_TO_STR_IF(2, 0x79, NUMPAD_PLUS) + CODE_TO_STR_IF(2, 0x81, NUMPAD_ENTER) + CODE_TO_STR_IF(2, 0x71, NUMPAD_PERIOD) + } + + if (btn_block[0] == 0x03) { + CODE_TO_STR_IF(4, 0x01, RAT_LCLICK) + CODE_TO_STR_IF(4, 0x02, RAT_RCLICK) + CODE_TO_STR_IF(4, 0x03, RAT_MCLICK) + CODE_TO_STR_IF(4, 0x04, RAT_WHEEL_UP) + CODE_TO_STR_IF(4, 0x05, RAT_WHEEL_DOWN) + } + + if (btn_block[0] == 0x05) { + CODE_TO_STR_IF(3, 0x16, MEDIA_SCREENSHOT) + CODE_TO_STR_IF(3, 0x19, MEDIA_SHOW_KEYBOARD) + CODE_TO_STR_IF(3, 0x1c, MEDIA_SHOW_DESKTOP) + CODE_TO_STR_IF(3, 0x1e, MEDIA_START_RECORDING) + CODE_TO_STR_IF(3, 0x01, MEDIA_MIC_OFF) + CODE_TO_STR_IF(3, 0x02, MEDIA_VOL_DOWN) + CODE_TO_STR_IF(3, 0x03, MEDIA_VOL_UP) + } + + return ""; +} + +/* ASUS ROG Ally device specific attributes */ + +/* This should be called before any attempts to set device functions */ +static int __gamepad_check_ready(struct hid_device *hdev) +{ + u8 *hidbuf; + int ret, count; + + hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL); + if (!hidbuf) + return -ENOMEM; + + for (count = 0; count < 3; count++) { + hidbuf[0] = FEATURE_KBD_REPORT_ID; + hidbuf[1] = 0xD1; + hidbuf[2] = xpad_cmd_check_ready; + hidbuf[3] = 01; + ret = asus_kbd_set_report(hdev, hidbuf, + FEATURE_ROG_ALLY_REPORT_SIZE); + if (ret < 0) + hid_warn(hdev, "ROG Ally check failed set report: %d\n", ret); + + hidbuf[0] = hidbuf[1] = hidbuf[2] = hidbuf[3] = 0; + ret = asus_kbd_get_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + if (ret < 0) + hid_warn(hdev, "ROG Ally check failed get report: %d\n", ret); + + ret = hidbuf[2] == xpad_cmd_check_ready; + if (!ret) + hid_warn(hdev, "ROG Ally not ready, retry %d\n", count); + else + break; + msleep(2); // don't spam the entire loop in less than USB response time + } + + if (count == 3) + hid_err(hdev, "ROG Ally never responded with a ready\n"); + + kfree(hidbuf); + return ret; +} + +/********** BUTTON REMAPPING *********************************************************************/ +static void __btn_pair_to_pkt(struct device *raw_dev, enum btn_pair pair, u8 *out, int out_len) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + + out[0] = FEATURE_KBD_REPORT_ID; + out[1] = 0xD1; + out[2] = xpad_cmd_set_mapping; + out[3] = pair; + out[4] = 0x2c; //length + memcpy(&out[5], &rog_ally->key_mapping[rog_ally->mode - 1][pair - 1], MAPPING_BLOCK_LEN); +} + +/* Store the button setting in driver data. Does not apply to device until __gamepad_set_mapping */ +static int __gamepad_mapping_store(struct device *raw_dev, const char *buf, enum btn_pair pair, + int side, bool secondary) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + u8 *key_code; + int offs; + + offs = side ? MAPPING_BLOCK_LEN / 2 : 0; + offs = secondary ? offs + BTN_CODE_LEN : offs; + key_code = rog_ally->key_mapping[rog_ally->mode - 1][pair - 1] + offs; + + return __string_to_key_code(buf, key_code, BTN_CODE_LEN); +} + +/* Apply the mapping pair to the device */ +static int __gamepad_set_mapping(struct device *raw_dev, enum btn_pair pair) +{ + struct hid_device *hdev = to_hid_device(raw_dev); + u8 *hidbuf; + int ret; + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + return ret; + + hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL); + if (!hidbuf) + return -ENOMEM; + + __btn_pair_to_pkt(raw_dev, pair, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + kfree(hidbuf); + + return ret; +} + +static ssize_t btn_mapping_apply_store(struct device *raw_dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = __gamepad_write_all_to_mcu(raw_dev); + if (ret < 0) + return ret; + return count; +} +ALLY_DEVICE_ATTR_WO(btn_mapping_apply, apply); + +/********** BUTTON TURBO *************************************************************************/ +static int __gamepad_turbo_index(enum btn_pair pair, int side) +{ + return (pair - 1) * (2 * TURBO_BLOCK_STEP) + (side * TURBO_BLOCK_STEP); +}; + +static int __gamepad_turbo_show(struct device *raw_dev, enum btn_pair pair, int side) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + return rog_ally->turbo_btns[rog_ally->mode - 1][__gamepad_turbo_index(pair, side)]; +}; + +static int __gamepad_turbo_store(struct device *raw_dev, const char *buf, enum btn_pair pair, + int side) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + int ret, val; + + ret = kstrtoint(buf, 0, &val); + if (ret) + return ret; + if (val < 0 || val > 16) + return -EINVAL; + + rog_ally->turbo_btns[rog_ally->mode - 1][__gamepad_turbo_index(pair, side)] = val; + + return 0; +}; + +/* button map attributes, regular and macro*/ +ALLY_BTN_MAPPING(m2, btn_pair_m1_m2, btn_pair_side_left); +ALLY_BTN_MAPPING(m1, btn_pair_m1_m2, btn_pair_side_right); +ALLY_BTN_MAPPING(a, btn_pair_a_b, btn_pair_side_left); +ALLY_BTN_MAPPING(b, btn_pair_a_b, btn_pair_side_right); +ALLY_BTN_MAPPING(x, btn_pair_x_y, btn_pair_side_left); +ALLY_BTN_MAPPING(y, btn_pair_x_y, btn_pair_side_right); +ALLY_BTN_MAPPING(lb, btn_pair_lb_rb, btn_pair_side_left); +ALLY_BTN_MAPPING(rb, btn_pair_lb_rb, btn_pair_side_right); +ALLY_BTN_MAPPING(ls, btn_pair_ls_rs, btn_pair_side_left); +ALLY_BTN_MAPPING(rs, btn_pair_ls_rs, btn_pair_side_right); +ALLY_BTN_MAPPING(lt, btn_pair_lt_rt, btn_pair_side_left); +ALLY_BTN_MAPPING(rt, btn_pair_lt_rt, btn_pair_side_right); +ALLY_BTN_MAPPING(dpad_u, btn_pair_dpad_u_d, btn_pair_side_left); +ALLY_BTN_MAPPING(dpad_d, btn_pair_dpad_u_d, btn_pair_side_right); +ALLY_BTN_MAPPING(dpad_l, btn_pair_dpad_l_r, btn_pair_side_left); +ALLY_BTN_MAPPING(dpad_r, btn_pair_dpad_l_r, btn_pair_side_right); +ALLY_BTN_MAPPING(view, btn_pair_view_menu, btn_pair_side_left); +ALLY_BTN_MAPPING(menu, btn_pair_view_menu, btn_pair_side_right); + +static void __gamepad_mapping_xpad_default(struct asus_rog_ally *rog_ally) +{ + memcpy(&rog_ally->key_mapping[0][0], &XPAD_DEF1, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[0][1], &XPAD_DEF2, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[0][2], &XPAD_DEF3, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[0][3], &XPAD_DEF4, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[0][4], &XPAD_DEF5, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[0][5], &XPAD_DEF6, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[0][6], &XPAD_DEF7, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[0][7], &XPAD_DEF8, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[0][8], &XPAD_DEF9, MAPPING_BLOCK_LEN); +} + +static void __gamepad_mapping_wasd_default(struct asus_rog_ally *rog_ally) +{ + memcpy(&rog_ally->key_mapping[1][0], &WASD_DEF1, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[1][1], &WASD_DEF2, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[1][2], &WASD_DEF3, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[1][3], &WASD_DEF4, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[1][4], &WASD_DEF5, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[1][5], &WASD_DEF6, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[1][6], &WASD_DEF7, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[1][7], &WASD_DEF8, MAPPING_BLOCK_LEN); + memcpy(&rog_ally->key_mapping[1][8], &WASD_DEF9, MAPPING_BLOCK_LEN); +} + +static ssize_t btn_mapping_reset_store(struct device *raw_dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + switch (rog_ally->mode) { + case xpad_mode_game: + __gamepad_mapping_xpad_default(rog_ally); + break; + case xpad_mode_wasd: + __gamepad_mapping_wasd_default(rog_ally); + break; + default: + __gamepad_mapping_xpad_default(rog_ally); + break; + } + + return count; +} + +ALLY_DEVICE_ATTR_WO(btn_mapping_reset, reset_btn_mapping); + +/********** GAMEPAD MODE *************************************************************************/ +static ssize_t __gamepad_set_mode(struct device *raw_dev, int val) +{ + struct hid_device *hdev = to_hid_device(raw_dev); + u8 *hidbuf; + int ret; + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + return ret; + + hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL); + if (!hidbuf) + return -ENOMEM; + + hidbuf[0] = FEATURE_KBD_REPORT_ID; + hidbuf[1] = 0xD1; + hidbuf[2] = xpad_cmd_set_mode; + hidbuf[3] = 0x01; + hidbuf[4] = val; + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + goto report_fail; + + ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + if (ret < 0) + goto report_fail; + + ret = __gamepad_write_all_to_mcu(raw_dev); + if (ret < 0) + goto report_fail; + +report_fail: + kfree(hidbuf); + return ret; +} + +static ssize_t gamepad_mode_show(struct device *raw_dev, struct device_attribute *attr, char *buf) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + return sysfs_emit(buf, "%d\n", rog_ally->mode); +} + +static ssize_t gamepad_mode_store(struct device *raw_dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + int ret, val; + + ret = kstrtoint(buf, 0, &val); + if (ret) + return ret; + + if (val < xpad_mode_game || val > xpad_mode_mouse) + return -EINVAL; + + rog_ally->mode = val; + + ret = __gamepad_set_mode(raw_dev, val); + if (ret < 0) + return ret; + + return count; +} + +DEVICE_ATTR_RW(gamepad_mode); + +/********** VIBRATION INTENSITY ******************************************************************/ +static ssize_t gamepad_vibration_intensity_index_show(struct device *raw_dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "left right\n"); +} + +ALLY_DEVICE_ATTR_RO(gamepad_vibration_intensity_index, vibration_intensity_index); + +static ssize_t __gamepad_write_vibe_intensity_to_mcu(struct device *raw_dev) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + struct hid_device *hdev = to_hid_device(raw_dev); + u8 *hidbuf; + int ret; + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + return ret; + + hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL); + if (!hidbuf) + return -ENOMEM; + + hidbuf[0] = FEATURE_KBD_REPORT_ID; + hidbuf[1] = 0xD1; + hidbuf[2] = xpad_cmd_set_vibe_intensity; + hidbuf[3] = 0x02; // length + hidbuf[4] = rog_ally->vibration_intensity[rog_ally->mode - 1][btn_pair_side_left]; + hidbuf[5] = rog_ally->vibration_intensity[rog_ally->mode - 1][btn_pair_side_right]; + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + goto report_fail; + + ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + if (ret < 0) + goto report_fail; + +report_fail: + kfree(hidbuf); + return ret; +} + +static ssize_t gamepad_vibration_intensity_show(struct device *raw_dev, + struct device_attribute *attr, char *buf) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + return sysfs_emit(buf, "%d %d\n", + rog_ally->vibration_intensity[rog_ally->mode - 1][btn_pair_side_left], + rog_ally->vibration_intensity[rog_ally->mode - 1][btn_pair_side_right]); +} + +static ssize_t gamepad_vibration_intensity_store(struct device *raw_dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + u32 left, right; + int ret; + + if (sscanf(buf, "%d %d", &left, &right) != 2) + return -EINVAL; + + if (left > 64 || right > 64) + return -EINVAL; + + rog_ally->vibration_intensity[rog_ally->mode - 1][btn_pair_side_left] = left; + rog_ally->vibration_intensity[rog_ally->mode - 1][btn_pair_side_right] = right; + + ret = __gamepad_write_vibe_intensity_to_mcu(raw_dev); + if (ret < 0) + return ret; + + return count; +} + +ALLY_DEVICE_ATTR_RW(gamepad_vibration_intensity, vibration_intensity); + +/********** ROOT LEVEL ATTRS **********************************************************************/ +static struct attribute *gamepad_device_attrs[] = { &dev_attr_gamepad_mode.attr, + &dev_attr_btn_mapping_reset.attr, + &dev_attr_btn_mapping_apply.attr, + &dev_attr_gamepad_vibration_intensity.attr, + &dev_attr_gamepad_vibration_intensity_index.attr, + NULL }; + +static const struct attribute_group ally_controller_attr_group = { + .attrs = gamepad_device_attrs, +}; + +/********** ANALOGUE DEADZONES ********************************************************************/ +static ssize_t __gamepad_set_deadzones(struct device *raw_dev) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + struct hid_device *hdev = to_hid_device(raw_dev); + u8 *hidbuf; + int ret; + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + return ret; + + hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL); + if (!hidbuf) + return -ENOMEM; + + hidbuf[0] = FEATURE_KBD_REPORT_ID; + hidbuf[1] = 0xD1; + hidbuf[2] = xpad_cmd_set_js_dz; + hidbuf[3] = 0x04; // length + hidbuf[4] = rog_ally->deadzones[rog_ally->mode - 1][0][0]; + hidbuf[5] = rog_ally->deadzones[rog_ally->mode - 1][0][1]; + hidbuf[6] = rog_ally->deadzones[rog_ally->mode - 1][0][2]; + hidbuf[7] = rog_ally->deadzones[rog_ally->mode - 1][0][3]; + + ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + if (ret < 0) + goto end; + + hidbuf[2] = xpad_cmd_set_tr_dz; + hidbuf[4] = rog_ally->deadzones[rog_ally->mode - 1][1][0]; + hidbuf[5] = rog_ally->deadzones[rog_ally->mode - 1][1][1]; + hidbuf[6] = rog_ally->deadzones[rog_ally->mode - 1][1][2]; + hidbuf[7] = rog_ally->deadzones[rog_ally->mode - 1][1][3]; + + ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + if (ret < 0) + goto end; + +end: + kfree(hidbuf); + return ret; +} + +static ssize_t __gamepad_store_deadzones(struct device *raw_dev, enum xpad_axis axis, + const char *buf) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + int cmd, side, is_tr; + u32 inner, outer; + + if (sscanf(buf, "%d %d", &inner, &outer) != 2) + return -EINVAL; + + if (inner > 64 || outer > 64 || inner > outer) + return -EINVAL; + + is_tr = axis > xpad_axis_xy_right; + side = axis == xpad_axis_xy_right || axis == xpad_axis_z_right ? 2 : 0; + cmd = is_tr ? xpad_cmd_set_js_dz : xpad_cmd_set_tr_dz; + + rog_ally->deadzones[rog_ally->mode - 1][is_tr][side] = inner; + rog_ally->deadzones[rog_ally->mode - 1][is_tr][side + 1] = outer; + + return 0; +} + +static ssize_t axis_xyz_deadzone_index_show(struct device *raw_dev, struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "inner outer\n"); +} + +ALLY_DEVICE_ATTR_RO(axis_xyz_deadzone_index, deadzone_index); + +ALLY_AXIS_DEADZONE(xpad_axis_xy_left, deadzone); +ALLY_AXIS_DEADZONE(xpad_axis_xy_right, deadzone); +ALLY_AXIS_DEADZONE(xpad_axis_z_left, deadzone); +ALLY_AXIS_DEADZONE(xpad_axis_z_right, deadzone); + +/********** ANTI-DEADZONES ***********************************************************************/ +static ssize_t __gamepad_write_js_ADZ_to_mcu(struct device *raw_dev) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + struct hid_device *hdev = to_hid_device(raw_dev); + u8 *hidbuf; + int ret; + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + return ret; + + hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL); + if (!hidbuf) + return -ENOMEM; + + hidbuf[0] = FEATURE_KBD_REPORT_ID; + hidbuf[1] = 0xD1; + hidbuf[2] = xpad_cmd_set_adz; + hidbuf[3] = 0x02; // length + hidbuf[4] = rog_ally->anti_deadzones[rog_ally->mode - 1][btn_pair_side_left]; + hidbuf[5] = rog_ally->anti_deadzones[rog_ally->mode - 1][btn_pair_side_right]; + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + goto report_fail; + + ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + if (ret < 0) + goto report_fail; + +report_fail: + kfree(hidbuf); + return ret; +} + +static ssize_t __gamepad_js_ADZ_store(struct device *raw_dev, const char *buf, + enum btn_pair_side side) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + int ret, val; + + ret = kstrtoint(buf, 0, &val); + if (ret) + return ret; + + if (val < 0 || val > 32) + return -EINVAL; + + rog_ally->anti_deadzones[rog_ally->mode - 1][side] = val; + + return ret; +} + +static ssize_t xpad_axis_xy_left_ADZ_show(struct device *raw_dev, struct device_attribute *attr, + char *buf) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + return sysfs_emit(buf, "%d\n", + rog_ally->anti_deadzones[rog_ally->mode - 1][btn_pair_side_left]); +} + +static ssize_t xpad_axis_xy_left_ADZ_store(struct device *raw_dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = __gamepad_js_ADZ_store(raw_dev, buf, btn_pair_side_left); + if (ret) + return ret; + + return count; +} + +ALLY_DEVICE_ATTR_RW(xpad_axis_xy_left_ADZ, anti_deadzone); + +static ssize_t xpad_axis_xy_right_ADZ_show(struct device *raw_dev, struct device_attribute *attr, + char *buf) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + return sysfs_emit(buf, "%d\n", + rog_ally->anti_deadzones[rog_ally->mode - 1][btn_pair_side_right]); +} + +static ssize_t xpad_axis_xy_right_ADZ_store(struct device *raw_dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = __gamepad_js_ADZ_store(raw_dev, buf, btn_pair_side_right); + if (ret) + return ret; + + return count; +} + +ALLY_DEVICE_ATTR_RW(xpad_axis_xy_right_ADZ, anti_deadzone); + +/********** JS RESPONSE CURVES *******************************************************************/ +static ssize_t rc_point_index_show(struct device *raw_dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "move response\n"); +} + +ALLY_DEVICE_ATTR_RO(rc_point_index, rc_point_index); + +static ssize_t __gamepad_write_response_curves_to_mcu(struct device *raw_dev) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + struct hid_device *hdev = to_hid_device(raw_dev); + u8 *hidbuf; + int ret; + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + return ret; + + hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL); + if (!hidbuf) + return -ENOMEM; + + hidbuf[0] = FEATURE_KBD_REPORT_ID; + hidbuf[1] = 0xD1; + hidbuf[2] = xpad_cmd_set_response_curve; + hidbuf[3] = 0x09; // length + hidbuf[4] = 0x01; + memcpy(&hidbuf[5], &rog_ally->response_curve[rog_ally->mode - 1][btn_pair_side_left], 8); + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + goto report_fail; + + hidbuf[4] = 0x02; + memcpy(&hidbuf[5], &rog_ally->response_curve[rog_ally->mode - 1][btn_pair_side_right], 8); + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + goto report_fail; + + ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + if (ret < 0) + goto report_fail; + +report_fail: + kfree(hidbuf); + return ret; +} + +static ssize_t __gamepad_store_response_curve(struct device *raw_dev, const char *buf, + enum btn_pair_side side, int point) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + int idx = (point - 1) * 2; + u32 move, response; + + if (sscanf(buf, "%d %d", &move, &response) != 2) + return -EINVAL; + + if (move > 64 || response > 64) + return -EINVAL; + + rog_ally->response_curve[rog_ally->mode - 1][side][idx] = move; + rog_ally->response_curve[rog_ally->mode - 1][side][idx + 1] = response; + + return 0; +} + +ALLY_JS_RC_POINT(left, 1, rc_point_); +ALLY_JS_RC_POINT(left, 2, rc_point_); +ALLY_JS_RC_POINT(left, 3, rc_point_); +ALLY_JS_RC_POINT(left, 4, rc_point_); + +ALLY_JS_RC_POINT(right, 1, rc_point_); +ALLY_JS_RC_POINT(right, 2, rc_point_); +ALLY_JS_RC_POINT(right, 3, rc_point_); +ALLY_JS_RC_POINT(right, 4, rc_point_); + +/********** CALIBRATIONS *************************************************************************/ +static ssize_t __gamepad_write_cal_to_mcu(struct device *raw_dev, enum xpad_axis axis) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + struct hid_device *hdev = to_hid_device(raw_dev); + u8 *hidbuf; + u8 *c, side, pkt_len, data_len; + int ret, cal, checksum = 0; + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + return ret; + + hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL); + if (!hidbuf) + return -ENOMEM; + + side = axis == xpad_axis_xy_right || axis == xpad_axis_z_right ? 1 : 0; + pkt_len = axis > xpad_axis_xy_right ? 0x06 : 0x0E; + data_len = axis > xpad_axis_xy_right ? 2 : 6; + + hidbuf[0] = FEATURE_KBD_REPORT_ID; + hidbuf[1] = 0xD1; + hidbuf[2] = xpad_cmd_set_calibration; + hidbuf[3] = pkt_len; + hidbuf[4] = 0x01; // second command (set) + hidbuf[5] = axis; + c = &hidbuf[6]; // pointer + + for (size_t i = 0; i < data_len; i++) { + cal = rog_ally->js_calibrations[side][i]; + *c = (u8)((cal & 0xff00) >> 8); + checksum += *c; + c += 1; + *c = (u8)(cal & 0xff); + checksum += *c; + c += 1; + } + + hidbuf[6 + data_len * 2] = checksum; + + // TODO: debug if + printk("CAL: "); + for (size_t i = 0; i < 19; i++) { + printk(KERN_CONT "%02x,", hidbuf[i]); + } + + ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + if (ret < 0) + goto report_fail; + + memset(hidbuf, 0, FEATURE_ROG_ALLY_REPORT_SIZE); + hidbuf[0] = FEATURE_KBD_REPORT_ID; + hidbuf[1] = 0xD1; + hidbuf[2] = xpad_cmd_set_calibration; + hidbuf[3] = 0x01; // pkt len + hidbuf[4] = 0x03; // second command (set) + + ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + if (ret < 0) + goto report_fail; + +report_fail: + kfree(hidbuf); + return ret; +} + +static ssize_t __gamepad_cal_store(struct device *raw_dev, const char *buf, enum xpad_axis axis) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + u32 x_stable, x_min, x_max, y_stable, y_min, y_max, side; + + if (axis == xpad_axis_xy_left || axis == xpad_axis_xy_right) { + if (sscanf(buf, "%d %d %d %d %d %d", &x_stable, &x_min, &x_max, &y_stable, &y_min, + &y_max) != 6) + return -EINVAL; + //TODO: validate input + + side = axis == xpad_axis_xy_right || axis == xpad_axis_z_right ? 1 : 0; + /* stored in reverse order for easy copy to packet */ + rog_ally->js_calibrations[side][0] = y_stable; + rog_ally->js_calibrations[side][1] = y_min; + rog_ally->js_calibrations[side][2] = y_max; + rog_ally->js_calibrations[side][3] = x_stable; + rog_ally->js_calibrations[side][4] = x_min; + rog_ally->js_calibrations[side][5] = x_max; + + return __gamepad_write_cal_to_mcu(raw_dev, axis); + } else { + if (sscanf(buf, "%d %d", &x_stable, &x_max) != 2) + return -EINVAL; + //TODO: validate input + + side = axis == xpad_axis_xy_right || axis == xpad_axis_z_right ? 1 : 0; + /* stored in reverse order for easy copy to packet */ + rog_ally->tr_calibrations[side][0] = x_stable; + rog_ally->tr_calibrations[side][1] = x_max; + + return __gamepad_write_cal_to_mcu(raw_dev, axis); + } +} + +static ssize_t __gamepad_cal_show(struct device *raw_dev, char *buf, enum xpad_axis axis) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + int side = axis == xpad_axis_xy_right || axis == xpad_axis_z_right ? 1 : 0; + + if (axis == xpad_axis_xy_left || axis == xpad_axis_xy_right) { + return sysfs_emit(buf, "%d %d %d %d %d %d\n", rog_ally->js_calibrations[side][3], + rog_ally->js_calibrations[side][4], + rog_ally->js_calibrations[side][5], + rog_ally->js_calibrations[side][0], + rog_ally->js_calibrations[side][1], + rog_ally->js_calibrations[side][2]); + } else { + return sysfs_emit(buf, "%d %d\n", rog_ally->tr_calibrations[side][0], + rog_ally->tr_calibrations[side][1]); + } +} + +ALLY_CAL_ATTR(xpad_axis_xy_left_cal, xpad_axis_xy_left, calibration); +ALLY_CAL_ATTR(xpad_axis_xy_right_cal, xpad_axis_xy_right, calibration); +ALLY_CAL_ATTR(xpad_axis_z_left_cal, xpad_axis_z_left, calibration); +ALLY_CAL_ATTR(xpad_axis_z_right_cal, xpad_axis_z_right, calibration); + +static ssize_t xpad_axis_xy_cal_index_show(struct device *raw_dev, struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "x_stable x_min x_max y_stable y_min y_max\n"); +} + +ALLY_DEVICE_ATTR_RO(xpad_axis_xy_cal_index, calibration_index); + +static ssize_t xpad_axis_z_cal_index_show(struct device *raw_dev, struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "z_stable z_max\n"); +} + +ALLY_DEVICE_ATTR_RO(xpad_axis_z_cal_index, calibration_index); + +static ssize_t __gamepad_cal_reset(struct device *raw_dev, const char *buf, enum xpad_axis axis) +{ + struct hid_device *hdev = to_hid_device(raw_dev); + u8 *hidbuf; + u8 side; + int ret; + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + return ret; + + hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL); + if (!hidbuf) + return -ENOMEM; + + side = axis == xpad_axis_xy_right || axis == xpad_axis_z_right ? 1 : 0; + + hidbuf[0] = FEATURE_KBD_REPORT_ID; + hidbuf[1] = 0xD1; + hidbuf[2] = xpad_cmd_set_calibration; + hidbuf[3] = 0x02; // pkt len + hidbuf[4] = 0x02; // second command (reset) + hidbuf[5] = axis; + + ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + if (ret < 0) + goto report_fail; + + memset(hidbuf, 0, FEATURE_ROG_ALLY_REPORT_SIZE); + hidbuf[0] = FEATURE_KBD_REPORT_ID; + hidbuf[1] = 0xD1; + hidbuf[2] = xpad_cmd_set_calibration; + hidbuf[3] = 0x01; // pkt len + hidbuf[4] = 0x03; // second command (set) + + ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + if (ret < 0) + goto report_fail; + +report_fail: + kfree(hidbuf); + return ret; +} + +ALLY_CAL_RESET_ATTR(xpad_axis_xy_left_cal_reset, xpad_axis_xy_left, calibration_reset); +ALLY_CAL_RESET_ATTR(xpad_axis_xy_right_cal_reset, xpad_axis_xy_right, calibration_reset); +ALLY_CAL_RESET_ATTR(xpad_axis_z_left_cal_reset, xpad_axis_z_left, calibration_reset); +ALLY_CAL_RESET_ATTR(xpad_axis_z_right_cal_reset, xpad_axis_z_right, calibration_reset); + +static struct attribute *gamepad_axis_xy_left_attrs[] = { &dev_attr_xpad_axis_xy_left_deadzone.attr, + &dev_attr_axis_xyz_deadzone_index.attr, + &dev_attr_xpad_axis_xy_left_ADZ.attr, + &dev_attr_xpad_axis_xy_left_cal_reset.attr, + &dev_attr_xpad_axis_xy_left_cal.attr, + &dev_attr_xpad_axis_xy_cal_index.attr, + &dev_attr_rc_point_left_1.attr, + &dev_attr_rc_point_left_2.attr, + &dev_attr_rc_point_left_3.attr, + &dev_attr_rc_point_left_4.attr, + &dev_attr_rc_point_index.attr, + NULL }; +static const struct attribute_group ally_controller_axis_xy_left_attr_group = { + .name = "axis_xy_left", + .attrs = gamepad_axis_xy_left_attrs, +}; + +static struct attribute *gamepad_axis_xy_right_attrs[] = { + &dev_attr_xpad_axis_xy_right_deadzone.attr, + &dev_attr_axis_xyz_deadzone_index.attr, + &dev_attr_xpad_axis_xy_right_ADZ.attr, + &dev_attr_xpad_axis_xy_right_cal_reset.attr, + &dev_attr_xpad_axis_xy_right_cal.attr, + &dev_attr_xpad_axis_xy_cal_index.attr, + &dev_attr_rc_point_right_1.attr, + &dev_attr_rc_point_right_2.attr, + &dev_attr_rc_point_right_3.attr, + &dev_attr_rc_point_right_4.attr, + &dev_attr_rc_point_index.attr, + NULL +}; +static const struct attribute_group ally_controller_axis_xy_right_attr_group = { + .name = "axis_xy_right", + .attrs = gamepad_axis_xy_right_attrs, +}; + +static struct attribute *gamepad_axis_z_left_attrs[] = { + &dev_attr_xpad_axis_z_left_deadzone.attr, &dev_attr_axis_xyz_deadzone_index.attr, + &dev_attr_xpad_axis_z_left_cal.attr, &dev_attr_xpad_axis_z_cal_index.attr, + &dev_attr_xpad_axis_z_left_cal_reset.attr, NULL +}; +static const struct attribute_group ally_controller_axis_z_left_attr_group = { + .name = "axis_z_left", + .attrs = gamepad_axis_z_left_attrs, +}; + +static struct attribute *gamepad_axis_z_right_attrs[] = { + &dev_attr_xpad_axis_z_right_deadzone.attr, &dev_attr_axis_xyz_deadzone_index.attr, + &dev_attr_xpad_axis_z_right_cal.attr, &dev_attr_xpad_axis_z_cal_index.attr, + &dev_attr_xpad_axis_z_right_cal_reset.attr, NULL +}; +static const struct attribute_group ally_controller_axis_z_right_attr_group = { + .name = "axis_z_right", + .attrs = gamepad_axis_z_right_attrs, +}; + +static const struct attribute_group *gamepad_device_attr_groups[] = { + &ally_controller_attr_group, + &ally_controller_axis_xy_left_attr_group, + &ally_controller_axis_xy_right_attr_group, + &ally_controller_axis_z_left_attr_group, + &ally_controller_axis_z_right_attr_group, + &btn_mapping_m1_attr_group, + &btn_mapping_m2_attr_group, + &btn_mapping_a_attr_group, + &btn_mapping_b_attr_group, + &btn_mapping_x_attr_group, + &btn_mapping_y_attr_group, + &btn_mapping_lb_attr_group, + &btn_mapping_rb_attr_group, + &btn_mapping_ls_attr_group, + &btn_mapping_rs_attr_group, + &btn_mapping_dpad_u_attr_group, + &btn_mapping_dpad_d_attr_group, + &btn_mapping_dpad_l_attr_group, + &btn_mapping_dpad_r_attr_group, + &btn_mapping_view_attr_group, + &btn_mapping_menu_attr_group, + NULL +}; + +static int __gamepad_write_all_to_mcu(struct device *raw_dev) +{ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); + struct hid_device *hdev = to_hid_device(raw_dev); + u8 *hidbuf; + int ret = 0; + + ret = __gamepad_set_mapping(&hdev->dev, btn_pair_dpad_u_d); + if (ret < 0) + return ret; + ret = __gamepad_set_mapping(&hdev->dev, btn_pair_dpad_l_r); + if (ret < 0) + return ret; + ret = __gamepad_set_mapping(&hdev->dev, btn_pair_ls_rs); + if (ret < 0) + return ret; + ret = __gamepad_set_mapping(&hdev->dev, btn_pair_lb_rb); + if (ret < 0) + return ret; + ret = __gamepad_set_mapping(&hdev->dev, btn_pair_a_b); + if (ret < 0) + return ret; + ret = __gamepad_set_mapping(&hdev->dev, btn_pair_x_y); + if (ret < 0) + return ret; + ret = __gamepad_set_mapping(&hdev->dev, btn_pair_view_menu); + if (ret < 0) + return ret; + ret = __gamepad_set_mapping(&hdev->dev, btn_pair_m1_m2); + if (ret < 0) + return ret; + __gamepad_set_mapping(&hdev->dev, btn_pair_lt_rt); + if (ret < 0) + return ret; + __gamepad_set_deadzones(raw_dev); + if (ret < 0) + return ret; + __gamepad_write_js_ADZ_to_mcu(raw_dev); + if (ret < 0) + return ret; + __gamepad_write_vibe_intensity_to_mcu(raw_dev); + if (ret < 0) + return ret; + __gamepad_write_response_curves_to_mcu(raw_dev); + if (ret < 0) + return ret; + + ret = __gamepad_check_ready(hdev); + if (ret < 0) + return ret; + + /* set turbo */ + hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL); + if (!hidbuf) + return -ENOMEM; + hidbuf[0] = FEATURE_KBD_REPORT_ID; + hidbuf[1] = 0xD1; + hidbuf[2] = xpad_cmd_set_turbo; + hidbuf[3] = 0x20; // length + memcpy(&hidbuf[4], rog_ally->turbo_btns[rog_ally->mode - 1], TURBO_BLOCK_LEN); + ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE); + + kfree(hidbuf); + return ret; +} + +static int asus_rog_ally_probe(struct hid_device *hdev, const struct rog_ops *ops) +{ + struct asus_drvdata *drvdata = hid_get_drvdata(hdev); + int ret = 0; + + /* all ROG devices have this HID interface but we will focus on Ally for now */ + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && hid_is_usb(hdev)) { + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + if (intf->altsetting->desc.bInterfaceNumber == 0) { + hid_info(hdev, "Setting up ROG USB interface\n"); + /* initialise and set up USB, common to ROG */ + // TODO: + + /* initialise the Ally data */ + if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) { + hid_info(hdev, "Setting up ROG Ally interface\n"); + + drvdata->rog_ally_data = devm_kzalloc( + &hdev->dev, sizeof(*drvdata->rog_ally_data), GFP_KERNEL); + if (!drvdata->rog_ally_data) { + hid_err(hdev, "Can't alloc Asus ROG USB interface\n"); + ret = -ENOMEM; + goto err_stop_hw; + } + // TODO: move these to functions + drvdata->rog_ally_data->mode = xpad_mode_game; + for (int i = 0; i < xpad_mode_mouse; i++) { + drvdata->rog_ally_data->deadzones[i][0][1] = 64; + drvdata->rog_ally_data->deadzones[i][0][3] = 64; + drvdata->rog_ally_data->deadzones[i][1][1] = 64; + drvdata->rog_ally_data->deadzones[i][1][3] = 64; + + drvdata->rog_ally_data->response_curve[i][0][0] = 0x14; + drvdata->rog_ally_data->response_curve[i][0][1] = 0x14; + drvdata->rog_ally_data->response_curve[i][0][2] = 0x28; + drvdata->rog_ally_data->response_curve[i][0][3] = 0x28; + drvdata->rog_ally_data->response_curve[i][0][4] = 0x3c; + drvdata->rog_ally_data->response_curve[i][0][5] = 0x3c; + drvdata->rog_ally_data->response_curve[i][0][6] = 0x50; + drvdata->rog_ally_data->response_curve[i][0][7] = 0x50; + + drvdata->rog_ally_data->response_curve[i][1][0] = 0x14; + drvdata->rog_ally_data->response_curve[i][1][1] = 0x14; + drvdata->rog_ally_data->response_curve[i][1][2] = 0x28; + drvdata->rog_ally_data->response_curve[i][1][3] = 0x28; + drvdata->rog_ally_data->response_curve[i][1][4] = 0x3c; + drvdata->rog_ally_data->response_curve[i][1][5] = 0x3c; + drvdata->rog_ally_data->response_curve[i][1][6] = 0x50; + drvdata->rog_ally_data->response_curve[i][1][7] = 0x50; + + drvdata->rog_ally_data->vibration_intensity[i][0] = 64; + drvdata->rog_ally_data->vibration_intensity[i][1] = 64; + } + + /* ignore all errors for this as they are related to USB HID I/O */ + __gamepad_mapping_xpad_default(drvdata->rog_ally_data); + __gamepad_mapping_wasd_default(drvdata->rog_ally_data); + // these calls will never error so ignore the return + __gamepad_mapping_store(&hdev->dev, "kb_f14", btn_pair_m1_m2, + btn_pair_side_left, false); // M2 + __gamepad_mapping_store(&hdev->dev, "kb_f15", btn_pair_m1_m2, + btn_pair_side_right, false); // M1 + __gamepad_set_mapping(&hdev->dev, btn_pair_m1_m2); + __gamepad_set_mode(&hdev->dev, xpad_mode_game); + } + + if (sysfs_create_groups(&hdev->dev.kobj, gamepad_device_attr_groups)) + goto err_stop_hw; + } + } + + return 0; +err_stop_hw: + hid_hw_stop(hdev); + return ret; +} + +void asus_rog_ally_remove(struct hid_device *hdev, const struct rog_ops *ops) +{ + struct asus_drvdata *drvdata = hid_get_drvdata(hdev); + if (drvdata->rog_ally_data) { + __gamepad_set_mode(&hdev->dev, xpad_mode_mouse); + sysfs_remove_groups(&hdev->dev.kobj, gamepad_device_attr_groups); + } +} + +const struct rog_ops rog_ally = { + .probe = asus_rog_ally_probe, + .remove = asus_rog_ally_remove, +}; diff --git a/drivers/hid/hid-asus-rog.h b/drivers/hid/hid-asus-rog.h new file mode 100644 index 000000000000..efad0b041d5d --- /dev/null +++ b/drivers/hid/hid-asus-rog.h @@ -0,0 +1,482 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HID driver for Asus ROG laptops and Ally + * + * Copyright (c) 2023 Luke Jones <luke@ljones.dev> + */ + +/* data that is private to the hid-asus-rog module */ + +#include <linux/hid.h> +#include <linux/types.h> + +#define BTN_CODE_LEN 11 +#define MAPPING_BLOCK_LEN 44 + +#define TURBO_BLOCK_LEN 32 +#define TURBO_BLOCK_STEP 2 + +#define PAD_A "pad_a" +#define PAD_B "pad_b" +#define PAD_X "pad_x" +#define PAD_Y "pad_y" +#define PAD_LB "pad_lb" +#define PAD_RB "pad_rb" +#define PAD_LS "pad_ls" +#define PAD_RS "pad_rs" +#define PAD_DPAD_UP "pad_dpad_up" +#define PAD_DPAD_DOWN "pad_dpad_down" +#define PAD_DPAD_LEFT "pad_dpad_left" +#define PAD_DPAD_RIGHT "pad_dpad_right" +#define PAD_VIEW "pad_view" +#define PAD_MENU "pad_menu" +#define PAD_XBOX "pad_xbox" + +#define KB_M1 "kb_m1" +#define KB_M2 "kb_m2" +#define KB_ESC "kb_esc" +#define KB_F1 "kb_f1" +#define KB_F2 "kb_f2" +#define KB_F3 "kb_f3" +#define KB_F4 "kb_f4" +#define KB_F5 "kb_f5" +#define KB_F6 "kb_f6" +#define KB_F7 "kb_f7" +#define KB_F8 "kb_f8" +#define KB_F9 "kb_f9" +#define KB_F10 "kb_f10" +#define KB_F11 "kb_f11" +#define KB_F12 "kb_f12" +#define KB_F14 "kb_f14" +#define KB_F15 "kb_f15" + +#define KB_BACKTICK "kb_backtick" +#define KB_1 "kb_1" +#define KB_2 "kb_2" +#define KB_3 "kb_3" +#define KB_4 "kb_4" +#define KB_5 "kb_5" +#define KB_6 "kb_6" +#define KB_7 "kb_7" +#define KB_8 "kb_8" +#define KB_9 "kb_9" +#define KB_0 "kb_0" +#define KB_HYPHEN "kb_hyphen" +#define KB_EQUALS "kb_equals" +#define KB_BACKSPACE "kb_backspace" + +#define KB_TAB "kb_tab" +#define KB_Q "kb_q" +#define KB_W "kb_w" +#define KB_E "kb_e" +#define KB_R "kb_r" +#define KB_T "kb_t" +#define KB_Y "kb_y" +#define KB_U "kb_u" +#define KB_I "kb_i" +#define KB_O "kb_o" +#define KB_P "kb_p" +#define KB_LBRACKET "kb_lbracket" +#define KB_RBRACKET "kb_rbracket" +#define KB_BACKSLASH "kb_bkslash" + +#define KB_CAPS "kb_caps" +#define KB_A "kb_a" +#define KB_S "kb_s" +#define KB_D "kb_d" +#define KB_F "kb_f" +#define KB_G "kb_g" +#define KB_H "kb_h" +#define KB_J "kb_j" +#define KB_K "kb_k" +#define KB_L "kb_l" +#define KB_SEMI "kb_semicolon" +#define KB_QUOTE "kb_quote" +#define KB_RET "kb_enter" + +#define KB_LSHIFT "kb_lshift" +#define KB_Z "kb_z" +#define KB_X "kb_x" +#define KB_C "kb_c" +#define KB_V "kb_v" +#define KB_B "kb_b" +#define KB_N "kb_n" +#define KB_M "kb_m" +#define KB_COMMA "kb_comma" +#define KB_PERIOD "kb_period" +#define KB_FWDSLASH "kb_fwdslash" +#define KB_RSHIFT "kb_rshift" + +#define KB_LCTL "kb_lctl" +#define KB_META "kb_meta" +#define KB_LALT "kb_lalt" +#define KB_SPACE "kb_space" +#define KB_RALT "kb_ralt" +#define KB_MENU "kb_menu" +#define KB_RCTL "kb_rctl" + +#define KB_PRNTSCN "kb_prntscn" +#define KB_SCRLCK "kb_scrlck" +#define KB_PAUSE "kb_pause" +#define KB_INS "kb_ins" +#define KB_HOME "kb_home" +#define KB_PGUP "kb_pgup" +#define KB_DEL "kb_del" +#define KB_END "kb_end" +#define KB_PGDWN "kb_pgdwn" + +#define KB_UP_ARROW "kb_up_arrow" +#define KB_DOWN_ARROW "kb_down_arrow" +#define KB_LEFT_ARROW "kb_left_arrow" +#define KB_RIGHT_ARROW "kb_right_arrow" + +#define NUMPAD_LOCK "numpad_lock" +#define NUMPAD_FWDSLASH "numpad_fwdslash" +#define NUMPAD_ASTERISK "numpad_asterisk" +#define NUMPAD_HYPHEN "numpad_hyphen" +#define NUMPAD_0 "numpad_0" +#define NUMPAD_1 "numpad_1" +#define NUMPAD_2 "numpad_2" +#define NUMPAD_3 "numpad_3" +#define NUMPAD_4 "numpad_4" +#define NUMPAD_5 "numpad_5" +#define NUMPAD_6 "numpad_6" +#define NUMPAD_7 "numpad_7" +#define NUMPAD_8 "numpad_8" +#define NUMPAD_9 "numpad_9" +#define NUMPAD_PLUS "numpad_plus" +#define NUMPAD_ENTER "numpad_enter" +#define NUMPAD_PERIOD "numpad_." + +#define RAT_LCLICK "rat_lclick" +#define RAT_RCLICK "rat_rclick" +#define RAT_MCLICK "rat_mclick" +#define RAT_WHEEL_UP "rat_wheel_up" +#define RAT_WHEEL_DOWN "rat_wheel_down" + +#define MEDIA_SCREENSHOT "media_screenshot" +#define MEDIA_SHOW_KEYBOARD "media_show_keyboard" +#define MEDIA_SHOW_DESKTOP "media_show_desktop" +#define MEDIA_START_RECORDING "media_start_recording" +#define MEDIA_MIC_OFF "media_mic_off" +#define MEDIA_VOL_DOWN "media_vol_down" +#define MEDIA_VOL_UP "media_vol_up" + +/* required so we can have nested attributes with same name but different functions */ +#define ALLY_DEVICE_ATTR_RW(_name, _sysfs_name) \ + struct device_attribute dev_attr_##_name = \ + __ATTR(_sysfs_name, 0644, _name##_show, _name##_store) + +#define ALLY_DEVICE_ATTR_RO(_name, _sysfs_name) \ + struct device_attribute dev_attr_##_name = __ATTR(_sysfs_name, 0444, _name##_show, NULL) + +#define ALLY_DEVICE_ATTR_WO(_name, _sysfs_name) \ + struct device_attribute dev_attr_##_name = __ATTR(_sysfs_name, 0200, NULL, _name##_store) + +/* response curve macros */ +#define ALLY_RESP_CURVE_SHOW(_name, _point_n) \ + static ssize_t _name##_show(struct device *raw_dev, struct device_attribute *attr, \ + char *buf) \ + { \ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); \ + int idx = (_point_n - 1) * 2; \ + return sysfs_emit( \ + buf, "%d %d\n", \ + rog_ally->response_curve[rog_ally->mode][btn_pair_side_left][idx], \ + rog_ally->response_curve[rog_ally->mode][btn_pair_side_right][idx + 1]); \ + } + +#define ALLY_RESP_CURVE_STORE(_name, _side, _point_n) \ + static ssize_t _name##_store(struct device *raw_dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + int ret = __gamepad_store_response_curve(raw_dev, buf, btn_pair_side_##_side, \ + _point_n); \ + if (ret < 0) \ + return ret; \ + return count; \ + } + +/* _point_n must start at 1 */ +#define ALLY_JS_RC_POINT(_side, _point_n, _sysfs_label) \ + ALLY_RESP_CURVE_SHOW(rc_point_##_side##_##_point_n, _point_n); \ + ALLY_RESP_CURVE_STORE(rc_point_##_side##_##_point_n, _side, _point_n); \ + ALLY_DEVICE_ATTR_RW(rc_point_##_side##_##_point_n, _sysfs_label##_point_n); + +/* deadzone macros */ +#define ALLY_AXIS_DEADZONE_SHOW(_axis) \ + static ssize_t _axis##_deadzone_show(struct device *raw_dev, \ + struct device_attribute *attr, char *buf) \ + { \ + struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev); \ + int side, is_tr; \ + \ + is_tr = _axis > xpad_axis_xy_right; \ + side = _axis == xpad_axis_xy_right || _axis == xpad_axis_z_right ? 2 : 0; \ + \ + return sysfs_emit(buf, "%d %d\n", \ + rog_ally->deadzones[rog_ally->mode][is_tr][side], \ + rog_ally->deadzones[rog_ally->mode][is_tr][side + 1]); \ + } + +#define ALLY_AXIS_DEADZONE_STORE(_axis) \ + static ssize_t _axis##_deadzone_store(struct device *raw_dev, \ + struct device_attribute *attr, const char *buf, \ + size_t count) \ + { \ + int ret = __gamepad_store_deadzones(raw_dev, _axis, buf); \ + if (ret < 0) \ + return ret; \ + return count; \ + } + +#define ALLY_AXIS_DEADZONE(_axis, _sysfs_label) \ + ALLY_AXIS_DEADZONE_SHOW(_axis); \ + ALLY_AXIS_DEADZONE_STORE(_axis); \ + ALLY_DEVICE_ATTR_RW(_axis##_deadzone, _sysfs_label); + +/* button specific macros */ +#define ALLY_BTN_SHOW(_fname, _pair, _side, _secondary) \ + static ssize_t _fname##_show(struct device *raw_dev, struct device_attribute *attr, \ + char *buf) \ + { \ + return sysfs_emit(buf, "%s\n", \ + __btn_map_to_string(raw_dev, _pair, _side, _secondary)); \ + } + +#define ALLY_BTN_STORE(_fname, _pair, _side, _secondary) \ + static ssize_t _fname##_store(struct device *raw_dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + int ret = __gamepad_mapping_store(raw_dev, buf, _pair, _side, _secondary); \ + if (ret < 0) \ + return ret; \ + return count; \ + } + +#define ALLY_BTN_TURBO_SHOW(_fname, _pair, _side) \ + static ssize_t _fname##_turbo_show(struct device *raw_dev, struct device_attribute *attr, \ + char *buf) \ + { \ + return sysfs_emit(buf, "%d\n", __gamepad_turbo_show(raw_dev, _pair, _side)); \ + } + +#define ALLY_BTN_TURBO_STORE(_fname, _pair, _side) \ + static ssize_t _fname##_turbo_store(struct device *raw_dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + int ret = __gamepad_turbo_store(raw_dev, buf, _pair, _side); \ + if (ret < 0) \ + return ret; \ + return count; \ + } + +#define ALLY_BTN_ATTRS_GROUP(_name, _fname) \ + static struct attribute *_fname##_attrs[] = { &dev_attr_##_fname.attr, \ + &dev_attr_##_fname##_macro.attr, \ + &dev_attr_##_fname##_turbo.attr, NULL }; \ + static const struct attribute_group _fname##_attr_group = { \ + .name = __stringify(_name), \ + .attrs = _fname##_attrs, \ + }; + +#define ALLY_BTN_MAPPING(_fname, _pair, _side) \ + ALLY_BTN_SHOW(btn_mapping_##_fname, _pair, _side, false); \ + ALLY_BTN_STORE(btn_mapping_##_fname, _pair, _side, false); \ + \ + ALLY_BTN_SHOW(btn_mapping_##_fname##_macro, _pair, _side, true); \ + ALLY_BTN_STORE(btn_mapping_##_fname##_macro, _pair, _side, true); \ + \ + ALLY_BTN_TURBO_SHOW(btn_mapping_##_fname, _pair, _side); \ + ALLY_BTN_TURBO_STORE(btn_mapping_##_fname, _pair, _side); \ + \ + ALLY_DEVICE_ATTR_RW(btn_mapping_##_fname, remap); \ + ALLY_DEVICE_ATTR_RW(btn_mapping_##_fname##_macro, macro_remap); \ + ALLY_DEVICE_ATTR_RW(btn_mapping_##_fname##_turbo, turbo); \ + \ + ALLY_BTN_ATTRS_GROUP(btn_##_fname, btn_mapping_##_fname); + +/* calibration macros */ +#define ALLY_CAL_STORE(_fname, _axis) \ + static ssize_t _fname##_store(struct device *raw_dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + int ret = __gamepad_cal_store(raw_dev, buf, _axis); \ + if (ret < 0) \ + return ret; \ + return count; \ + }; + +#define ALLY_CAL_SHOW(_fname, _axis) \ + static ssize_t _fname##_show(struct device *raw_dev, struct device_attribute *attr, \ + char *buf) \ + { \ + return __gamepad_cal_show(raw_dev, buf, _axis); \ + } + +#define ALLY_CAL_ATTR(_fname, _axis, _sysfs_label) \ + ALLY_CAL_STORE(_fname, _axis); \ + ALLY_CAL_SHOW(_fname, _axis); \ + ALLY_DEVICE_ATTR_RW(_fname, _sysfs_label); + +#define ALLY_CAL_RESET_STORE(_fname, _axis) \ + static ssize_t _fname##_store(struct device *raw_dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + int ret = __gamepad_cal_reset(raw_dev, buf, _axis); \ + if (ret < 0) \ + return ret; \ + return count; \ + }; + +#define ALLY_CAL_RESET_ATTR(_fname, _axis, _sysfs_label) \ + ALLY_CAL_RESET_STORE(_fname, _axis); \ + ALLY_DEVICE_ATTR_WO(_fname, _sysfs_label); + +/* Default blocks for the xpad mode */ +static const u8 XPAD_DEF1[MAPPING_BLOCK_LEN] = { + 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x8c, 0x88, 0x76, 0x00, 0x00 +}; +static const u8 XPAD_DEF2[MAPPING_BLOCK_LEN] = { + 0x01, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x82, 0x23, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x0d, 0x00, 0x00, 0x00 +}; +static const u8 XPAD_DEF3[MAPPING_BLOCK_LEN] = { + 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const u8 XPAD_DEF4[MAPPING_BLOCK_LEN] = { + 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const u8 XPAD_DEF5[MAPPING_BLOCK_LEN] = { + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x31, 0x00, 0x00, 0x00 +}; +static const u8 XPAD_DEF6[MAPPING_BLOCK_LEN] = { + 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x82, 0x4d, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const u8 XPAD_DEF7[MAPPING_BLOCK_LEN] = { + 0x01, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const u8 XPAD_DEF8[MAPPING_BLOCK_LEN] = { + 0x02, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x8e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const u8 XPAD_DEF9[MAPPING_BLOCK_LEN] = { + 0x01, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* default blocks for the wasd mode */ +static const u8 WASD_DEF1[MAPPING_BLOCK_LEN] = { + 0x02, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x8c, 0x88, 0x76, 0x00, 0x00 +}; +static const u8 WASD_DEF2[MAPPING_BLOCK_LEN] = { + 0x02, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x82, 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x0d, 0x00, 0x00, 0x00 +}; +static const u8 WASD_DEF3[MAPPING_BLOCK_LEN] = { + 0x02, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const u8 WASD_DEF4[MAPPING_BLOCK_LEN] = { + 0x02, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const u8 WASD_DEF5[MAPPING_BLOCK_LEN] = { + 0x02, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x31, 0x00, 0x00, 0x00 +}; +static const u8 WASD_DEF6[MAPPING_BLOCK_LEN] = { + 0x02, 0x00, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x82, 0x4d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const u8 WASD_DEF7[MAPPING_BLOCK_LEN] = { + 0x01, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const u8 WASD_DEF8[MAPPING_BLOCK_LEN] = { + 0x02, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x8e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const u8 WASD_DEF9[MAPPING_BLOCK_LEN] = { + 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x88, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* + * the xpad_mode is used inside the mode setting packet and is used + * for indexing (xpad_mode - 1) + */ +enum xpad_mode { + xpad_mode_game = 0x01, + xpad_mode_wasd = 0x02, + xpad_mode_mouse = 0x03, +}; + +/* the xpad_cmd determines which feature is set or queried */ +enum xpad_cmd { + xpad_cmd_set_mode = 0x01, + xpad_cmd_set_mapping = 0x02, + xpad_cmd_set_js_dz = 0x04, /* deadzones */ + xpad_cmd_set_tr_dz = 0x05, /* deadzones */ + xpad_cmd_set_vibe_intensity = 0x06, + xpad_cmd_check_ready = 0x0A, + xpad_cmd_set_calibration = 0x0D, + xpad_cmd_set_turbo = 0x0F, + xpad_cmd_set_response_curve = 0x13, + xpad_cmd_set_adz = 0x18, +}; + +/* + * the xpad_mode is used in various set and query HID packets and is + * used for indexing (xpad_axis - 1) + */ +enum xpad_axis { + xpad_axis_xy_left = 0x01, + xpad_axis_xy_right = 0x02, + xpad_axis_z_left = 0x03, + xpad_axis_z_right = 0x04, +}; + +enum btn_pair { + btn_pair_dpad_u_d = 0x01, + btn_pair_dpad_l_r = 0x02, + btn_pair_ls_rs = 0x03, + btn_pair_lb_rb = 0x04, + btn_pair_a_b = 0x05, + btn_pair_x_y = 0x06, + btn_pair_view_menu = 0x07, + btn_pair_m1_m2 = 0x08, + btn_pair_lt_rt = 0x09, +}; + +enum btn_pair_side { + btn_pair_side_left = 0x00, + btn_pair_side_right = 0x01, +}; + +static int __gamepad_write_all_to_mcu(struct device *raw_dev); \ No newline at end of file diff --git a/drivers/hid/hid-asus.h b/drivers/hid/hid-asus.h new file mode 100644 index 000000000000..18317cad7110 --- /dev/null +++ b/drivers/hid/hid-asus.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HID driver for Asus ROG laptops and Ally + * + * Copyright (c) 2023 Luke Jones <luke@ljones.dev> + */ + +#include <linux/hid.h> +#include <linux/types.h> + +#define FEATURE_KBD_REPORT_ID 0x5a +#define FEATURE_KBD_REPORT_SIZE 16 +#define FEATURE_KBD_LED_REPORT_ID1 0x5d +#define FEATURE_KBD_LED_REPORT_ID2 0x5e +#define FEATURE_ROG_ALLY_REPORT_SIZE 64 + +#define QUIRK_FIX_NOTEBOOK_REPORT BIT(0) +#define QUIRK_NO_INIT_REPORTS BIT(1) +#define QUIRK_SKIP_INPUT_MAPPING BIT(2) +#define QUIRK_IS_MULTITOUCH BIT(3) +#define QUIRK_NO_CONSUMER_USAGES BIT(4) +#define QUIRK_USE_KBD_BACKLIGHT BIT(5) +#define QUIRK_T100_KEYBOARD BIT(6) +#define QUIRK_T100CHI BIT(7) +#define QUIRK_G752_KEYBOARD BIT(8) +#define QUIRK_T90CHI BIT(9) +#define QUIRK_MEDION_E1239T BIT(10) +#define QUIRK_ROG_NKEY_KEYBOARD BIT(11) +#define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12) +#define QUIRK_ROG_ALLY_XPAD BIT(13) + +struct asus_drvdata { + unsigned long quirks; + struct hid_device *hdev; + struct input_dev *input; + struct input_dev *tp_kbd_input; + struct asus_kbd_leds *kbd_backlight; + const struct asus_touchpad_info *tp; + bool enable_backlight; + struct power_supply *battery; + struct power_supply_desc battery_desc; + int battery_capacity; + int battery_stat; + bool battery_in_query; + unsigned long battery_next_query; + struct asus_rog_ally *rog_ally_data; +}; + +extern int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t buf_size); + +extern int asus_kbd_get_report(struct hid_device *hdev, u8 *out_buf, size_t out_buf_size); + +struct rog_ops { + int (*probe) (struct hid_device *hdev, const struct rog_ops *ops); + void (*remove) (struct hid_device *hdev, const struct rog_ops *ops); +}; + +extern const struct rog_ops rog_ally; \ No newline at end of file -- 2.43.0