|
@@ -0,0 +1,218 @@
|
|
|
|
|
+/*
|
|
|
|
|
+ * Copyright (c) 2026, sakumisu
|
|
|
|
|
+ *
|
|
|
|
|
+ * SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
+ */
|
|
|
|
|
+#include "usbd_core.h"
|
|
|
|
|
+#include "usbd_hid.h"
|
|
|
|
|
+#include "usbd_gamepad.h"
|
|
|
|
|
+
|
|
|
|
|
+extern int hid_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len);
|
|
|
|
|
+
|
|
|
|
|
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t gamepad_report_buffer[64];
|
|
|
|
|
+
|
|
|
|
|
+static int xinput_vendor_class_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
|
|
|
|
|
+{
|
|
|
|
|
+ struct xinput_in_report xinput_report;
|
|
|
|
|
+
|
|
|
|
|
+ memset(&xinput_report, 0, sizeof(xinput_report));
|
|
|
|
|
+ xinput_report.report_size = 20;
|
|
|
|
|
+
|
|
|
|
|
+ memcpy(*data, &xinput_report, sizeof(xinput_report));
|
|
|
|
|
+ *len = sizeof(xinput_report);
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+int usbd_gamepad_xinput_send_report(uint8_t ep, struct usb_gamepad_report *report)
|
|
|
|
|
+{
|
|
|
|
|
+ struct xinput_in_report *xinput_report;
|
|
|
|
|
+
|
|
|
|
|
+ xinput_report = (struct xinput_in_report *)gamepad_report_buffer;
|
|
|
|
|
+ memset(xinput_report, 0, sizeof(xinput_report));
|
|
|
|
|
+ xinput_report->report_size = 20;
|
|
|
|
|
+
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_DU)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_UP;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_DD)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_DOWN;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_DL)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_LEFT;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_DR)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_RIGHT;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_S2)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_START;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_S1)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_BACK;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_L3)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_L3;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_R3)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_R3;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_L1)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_LB;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_R1)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_RB;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_A1)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_GUIDE;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_B1)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_A;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_B2)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_B;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_B3)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_X;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_B4)
|
|
|
|
|
+ xinput_report->buttons |= XINPUT_BUTTON_MASK_Y;
|
|
|
|
|
+
|
|
|
|
|
+ // Analog triggers (0-255), fall back to digital if analog is 0 but button pressed
|
|
|
|
|
+ xinput_report->lt = report->lt;
|
|
|
|
|
+ xinput_report->rt = report->rt;
|
|
|
|
|
+ if (xinput_report->lt == 0 && (report->buttons & USB_GAMEPAD_BUTTON_L2))
|
|
|
|
|
+ xinput_report->lt = 0xFF;
|
|
|
|
|
+ if (xinput_report->rt == 0 && (report->buttons & USB_GAMEPAD_BUTTON_R2))
|
|
|
|
|
+ xinput_report->rt = 0xFF;
|
|
|
|
|
+
|
|
|
|
|
+ return usbd_ep_start_write(0, ep, gamepad_report_buffer, sizeof(struct xinput_in_report));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Convert gamepad dpad mask to switch hat value
|
|
|
|
|
+static uint8_t convert_dpad_to_switch_hat(uint32_t buttons)
|
|
|
|
|
+{
|
|
|
|
|
+ // Joypad uses active-high (1 = pressed)
|
|
|
|
|
+ uint8_t up = (buttons & USB_GAMEPAD_BUTTON_DU) ? 1 : 0;
|
|
|
|
|
+ uint8_t down = (buttons & USB_GAMEPAD_BUTTON_DD) ? 1 : 0;
|
|
|
|
|
+ uint8_t left = (buttons & USB_GAMEPAD_BUTTON_DL) ? 1 : 0;
|
|
|
|
|
+ uint8_t right = (buttons & USB_GAMEPAD_BUTTON_DR) ? 1 : 0;
|
|
|
|
|
+
|
|
|
|
|
+ if (up && right)
|
|
|
|
|
+ return SWITCH_HAT_UP_RIGHT;
|
|
|
|
|
+ if (up && left)
|
|
|
|
|
+ return SWITCH_HAT_UP_LEFT;
|
|
|
|
|
+ if (down && right)
|
|
|
|
|
+ return SWITCH_HAT_DOWN_RIGHT;
|
|
|
|
|
+ if (down && left)
|
|
|
|
|
+ return SWITCH_HAT_DOWN_LEFT;
|
|
|
|
|
+ if (up)
|
|
|
|
|
+ return SWITCH_HAT_UP;
|
|
|
|
|
+ if (down)
|
|
|
|
|
+ return SWITCH_HAT_DOWN;
|
|
|
|
|
+ if (left)
|
|
|
|
|
+ return SWITCH_HAT_LEFT;
|
|
|
|
|
+ if (right)
|
|
|
|
|
+ return SWITCH_HAT_RIGHT;
|
|
|
|
|
+
|
|
|
|
|
+ return SWITCH_HAT_CENTER;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+int usbd_gamepad_switch_send_report(uint8_t ep, struct usb_gamepad_report *report)
|
|
|
|
|
+{
|
|
|
|
|
+ struct switch_in_report *switch_report;
|
|
|
|
|
+
|
|
|
|
|
+ switch_report = (struct switch_in_report *)gamepad_report_buffer;
|
|
|
|
|
+ memset(switch_report, 0, sizeof(switch_report));
|
|
|
|
|
+
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_S1)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_MINUS;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_S2)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_PLUS;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_L1)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_L;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_R1)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_R;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_L2)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_ZL;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_R2)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_ZR;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_L3)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_L3;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_R3)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_R3;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_A1)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_HOME;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_A2)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_CAPTURE;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_B1)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_B;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_B2)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_A;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_B3)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_Y;
|
|
|
|
|
+ if (report->buttons & USB_GAMEPAD_BUTTON_B4)
|
|
|
|
|
+ switch_report->buttons |= SWITCH_MASK_X;
|
|
|
|
|
+
|
|
|
|
|
+ switch_report->hat = convert_dpad_to_switch_hat(report->buttons);
|
|
|
|
|
+
|
|
|
|
|
+ // Analog sticks (HID convention: 0=up, 255=down - no inversion needed)
|
|
|
|
|
+ switch_report->lx = report->lx;
|
|
|
|
|
+ switch_report->ly = report->ly;
|
|
|
|
|
+ switch_report->rx = report->rx;
|
|
|
|
|
+ switch_report->ry = report->ry;
|
|
|
|
|
+
|
|
|
|
|
+ switch_report->vendor = 0;
|
|
|
|
|
+
|
|
|
|
|
+ return usbd_ep_start_write(0, ep, gamepad_report_buffer, sizeof(struct switch_in_report));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+struct usbd_interface *usbd_gamepad_xinput_init_intf(struct usbd_interface *intf)
|
|
|
|
|
+{
|
|
|
|
|
+ intf->class_interface_handler = NULL;
|
|
|
|
|
+ intf->class_endpoint_handler = NULL;
|
|
|
|
|
+ intf->vendor_handler = xinput_vendor_class_request_handler;
|
|
|
|
|
+ intf->notify_handler = NULL;
|
|
|
|
|
+
|
|
|
|
|
+ return intf;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static const uint8_t hid_switch_report_desc[HID_SWITCH_REPORT_DESC_SIZE] = {
|
|
|
|
|
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
|
|
|
|
+ 0x09, 0x05, // Usage (Game Pad)
|
|
|
|
|
+ 0xA1, 0x01, // Collection (Application)
|
|
|
|
|
+ 0x15, 0x00, // Logical Minimum (0)
|
|
|
|
|
+ 0x25, 0x01, // Logical Maximum (1)
|
|
|
|
|
+ 0x35, 0x00, // Physical Minimum (0)
|
|
|
|
|
+ 0x45, 0x01, // Physical Maximum (1)
|
|
|
|
|
+ 0x75, 0x01, // Report Size (1)
|
|
|
|
|
+ 0x95, 0x10, // Report Count (16)
|
|
|
|
|
+ 0x05, 0x09, // Usage Page (Button)
|
|
|
|
|
+ 0x19, 0x01, // Usage Minimum (Button 1)
|
|
|
|
|
+ 0x29, 0x10, // Usage Maximum (Button 16)
|
|
|
|
|
+ 0x81, 0x02, // Input (Data,Var,Abs)
|
|
|
|
|
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
|
|
|
|
+ 0x25, 0x07, // Logical Maximum (7)
|
|
|
|
|
+ 0x46, 0x3B, 0x01, // Physical Maximum (315)
|
|
|
|
|
+ 0x75, 0x04, // Report Size (4)
|
|
|
|
|
+ 0x95, 0x01, // Report Count (1)
|
|
|
|
|
+ 0x65, 0x14, // Unit (Eng Rot:Angular Pos)
|
|
|
|
|
+ 0x09, 0x39, // Usage (Hat switch)
|
|
|
|
|
+ 0x81, 0x42, // Input (Data,Var,Abs,Null)
|
|
|
|
|
+ 0x65, 0x00, // Unit (None)
|
|
|
|
|
+ 0x95, 0x01, // Report Count (1)
|
|
|
|
|
+ 0x81, 0x01, // Input (Const) - 4-bit padding
|
|
|
|
|
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
|
|
|
|
|
+ 0x46, 0xFF, 0x00, // Physical Maximum (255)
|
|
|
|
|
+ 0x09, 0x30, // Usage (X) - Left Stick X
|
|
|
|
|
+ 0x09, 0x31, // Usage (Y) - Left Stick Y
|
|
|
|
|
+ 0x09, 0x32, // Usage (Z) - Right Stick X
|
|
|
|
|
+ 0x09, 0x35, // Usage (Rz) - Right Stick Y
|
|
|
|
|
+ 0x75, 0x08, // Report Size (8)
|
|
|
|
|
+ 0x95, 0x04, // Report Count (4)
|
|
|
|
|
+ 0x81, 0x02, // Input (Data,Var,Abs)
|
|
|
|
|
+ 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined)
|
|
|
|
|
+ 0x09, 0x20, // Usage (0x20)
|
|
|
|
|
+ 0x95, 0x01, // Report Count (1)
|
|
|
|
|
+ 0x81, 0x02, // Input (Data,Var,Abs) - Vendor byte
|
|
|
|
|
+ 0x0A, 0x21, 0x26, // Usage (0x2621)
|
|
|
|
|
+ 0x95, 0x08, // Report Count (8)
|
|
|
|
|
+ 0x91, 0x02, // Output (Data,Var,Abs) - Rumble
|
|
|
|
|
+ 0xC0, // End Collection
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+struct usbd_interface *usbd_gamepad_switch_init_intf(struct usbd_interface *intf)
|
|
|
|
|
+{
|
|
|
|
|
+ intf->class_interface_handler = hid_class_interface_request_handler;
|
|
|
|
|
+ intf->class_endpoint_handler = NULL;
|
|
|
|
|
+ intf->vendor_handler = NULL;
|
|
|
|
|
+ intf->notify_handler = NULL;
|
|
|
|
|
+
|
|
|
|
|
+ intf->hid_report_descriptor = hid_switch_report_desc;
|
|
|
|
|
+ intf->hid_report_descriptor_len = HID_SWITCH_REPORT_DESC_SIZE;
|
|
|
|
|
+ return intf;
|
|
|
|
|
+}
|