Skip to content

Commit 7a2089f

Browse files
committed
feat(class/gamepad): add gamepad device
Signed-off-by: sakumisu <[email protected]>
1 parent be5ba64 commit 7a2089f

File tree

6 files changed

+720
-0
lines changed

6 files changed

+720
-0
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ if(BL_SDK_BASE)
1010
set(CONFIG_CHERRYUSB_DEVICE_MSC 1)
1111
set(CONFIG_CHERRYUSB_DEVICE_AUDIO 1)
1212
set(CONFIG_CHERRYUSB_DEVICE_VIDEO 1)
13+
set(CONFIG_CHERRYUSB_DEVICE_GAMEPAD 1)
1314

1415
set(CONFIG_CHERRYUSB_HOST_CDC_ACM 1)
1516
set(CONFIG_CHERRYUSB_HOST_CDC_ECM 1)
@@ -182,6 +183,7 @@ elseif(HPM_SDK_BASE)
182183
set(CONFIG_CHERRYUSB_DEVICE_MSC 1)
183184
set(CONFIG_CHERRYUSB_DEVICE_AUDIO 1)
184185
set(CONFIG_CHERRYUSB_DEVICE_VIDEO 1)
186+
set(CONFIG_CHERRYUSB_DEVICE_GAMEPAD 1)
185187

186188
set(CONFIG_CHERRYUSB_HOST_CDC_ACM 1)
187189
set(CONFIG_CHERRYUSB_HOST_CDC_ECM 1)

cherryusb.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ list(
5151
${CMAKE_CURRENT_LIST_DIR}/class/vendor/net
5252
${CMAKE_CURRENT_LIST_DIR}/class/vendor/wifi
5353
${CMAKE_CURRENT_LIST_DIR}/class/aoa
54+
${CMAKE_CURRENT_LIST_DIR}/class/gamepad
5455
)
5556

5657
if(CONFIG_CHERRYUSB_DEVICE)
@@ -85,6 +86,9 @@ if(CONFIG_CHERRYUSB_DEVICE)
8586
if(CONFIG_CHERRYUSB_DEVICE_ADB)
8687
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/adb/usbd_adb.c)
8788
endif()
89+
if(CONFIG_CHERRYUSB_DEVICE_GAMEPAD)
90+
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/gamepad/usbd_gamepad.c)
91+
endif()
8892

8993
if(CONFIG_CHERRYUSB_DEVICE_FSDEV_ST)
9094
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/fsdev/usb_dc_fsdev.c)

class/gamepad/usb_gamepad.h

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
/*
2+
* Copyright (c) 2026, sakumisu
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#ifndef USB_GAMEPAD_H
7+
#define USB_GAMEPAD_H
8+
9+
#include "usb_hid.h"
10+
11+
/*
12+
* GAMEPAD BUTTON LAYOUT
13+
*
14+
* ____________________________ __
15+
* / [__L2__] [__R2__] \ |
16+
* / [__ L1 __] [__ R1 __] \ | Triggers
17+
* __/________________________________\__ __|
18+
* / _ \ |
19+
* / /\ __ (B4) \ |
20+
* / || __ |A1| __ _ _ \ | Main Pad
21+
* | <===DP===> |S1| |S2| (B3) -|- (B2)| |
22+
* \ || ¯¯ ¯¯ _ / |
23+
* /\ \/ / \ / \ (B1) /\ __|
24+
* / \________ | LS | ____ | RS | _______/ \ |
25+
* | / \ \___/ / \ \___/ / \ | | Sticks
26+
* | / \_____/ \_____/ \ | __|
27+
* | / L3 R3 \ |
28+
* \_____/ \_____/
29+
*
30+
* |________|______| |______|___________|
31+
* D-Pad Left Right Face
32+
* Stick Stick Buttons
33+
*
34+
* Extended: A2=Touchpad/Capture A3=Mute L4/R4=Paddles
35+
*/
36+
37+
// W3C Gamepad API standard button order
38+
// Bit position = W3C button index (trivial conversion: 1 << index)
39+
//
40+
// Gamepad XInput Switch PS3/4/5 DInput
41+
// ------ ------ ------ ------- ------
42+
43+
// Face buttons (right cluster)
44+
#define USB_GAMEPAD_BUTTON_B1 (1 << 0) // A B Cross 2
45+
#define USB_GAMEPAD_BUTTON_B2 (1 << 1) // B A Circle 3
46+
#define USB_GAMEPAD_BUTTON_B3 (1 << 2) // X Y Square 1
47+
#define USB_GAMEPAD_BUTTON_B4 (1 << 3) // Y X Triangle 4
48+
49+
// Shoulder buttons
50+
#define USB_GAMEPAD_BUTTON_L1 (1 << 4) // LB L L1 5
51+
#define USB_GAMEPAD_BUTTON_R1 (1 << 5) // RB R R1 6
52+
#define USB_GAMEPAD_BUTTON_L2 (1 << 6) // LT ZL L2 7
53+
#define USB_GAMEPAD_BUTTON_R2 (1 << 7) // RT ZR R2 8
54+
55+
// Center cluster
56+
#define USB_GAMEPAD_BUTTON_S1 (1 << 8) // Back - Select 9
57+
#define USB_GAMEPAD_BUTTON_S2 (1 << 9) // Start + Start 10
58+
59+
// Stick clicks
60+
#define USB_GAMEPAD_BUTTON_L3 (1 << 10) // LS LS L3 11
61+
#define USB_GAMEPAD_BUTTON_R3 (1 << 11) // RS RS R3 12
62+
63+
// D-pad
64+
#define USB_GAMEPAD_BUTTON_DU (1 << 12) // D-Up D-Up D-Up Hat
65+
#define USB_GAMEPAD_BUTTON_DD (1 << 13) // D-Down D-Down D-Down Hat
66+
#define USB_GAMEPAD_BUTTON_DL (1 << 14) // D-Left D-Left D-Left Hat
67+
#define USB_GAMEPAD_BUTTON_DR (1 << 15) // D-Right D-Right D-Right Hat
68+
69+
// Auxiliary
70+
#define USB_GAMEPAD_BUTTON_A1 (1 << 16) // Guide Home PS 13
71+
#define USB_GAMEPAD_BUTTON_A2 (1 << 17) // - Capture Touchpad 14
72+
#define USB_GAMEPAD_BUTTON_A3 (1 << 18) // - - Mute -
73+
#define USB_GAMEPAD_BUTTON_A4 (1 << 19) // - - - -
74+
75+
// Paddles (extended)
76+
#define USB_GAMEPAD_BUTTON_L4 (1 << 20) // P1 - - -
77+
#define USB_GAMEPAD_BUTTON_R4 (1 << 21) // P2 - - -
78+
79+
#define XINPUT_VID 0x045E // Microsoft
80+
#define XINPUT_PID 0x028E // Xbox 360 Controller
81+
#define XINPUT_BCD_DEVICE 0x0114 // v1.14
82+
83+
/* XInput (Xbox 360) USB */
84+
85+
// XInput Interface Class/Subclass/Protocol
86+
#define XINPUT_INTERFACE_CLASS 0xFF
87+
#define XINPUT_INTERFACE_SUBCLASS 0x5D
88+
#define XINPUT_INTERFACE_PROTOCOL 0x01
89+
90+
#define XINPUT_BUTTON_MASK_UP (1U << 0)
91+
#define XINPUT_BUTTON_MASK_DOWN (1U << 1)
92+
#define XINPUT_BUTTON_MASK_LEFT (1U << 2)
93+
#define XINPUT_BUTTON_MASK_RIGHT (1U << 3)
94+
#define XINPUT_BUTTON_MASK_START (1U << 4)
95+
#define XINPUT_BUTTON_MASK_BACK (1U << 5)
96+
#define XINPUT_BUTTON_MASK_L3 (1U << 6)
97+
#define XINPUT_BUTTON_MASK_R3 (1U << 7)
98+
#define XINPUT_BUTTON_MASK_LB (1U << 8)
99+
#define XINPUT_BUTTON_MASK_RB (1U << 9)
100+
#define XINPUT_BUTTON_MASK_GUIDE (1U << 10)
101+
//#define XINPUT_BUTTON_MASK_UNUSED (1U << 11)
102+
#define XINPUT_BUTTON_MASK_A (1U << 12)
103+
#define XINPUT_BUTTON_MASK_B (1U << 13)
104+
#define XINPUT_BUTTON_MASK_X (1U << 14)
105+
#define XINPUT_BUTTON_MASK_Y (1U << 15)
106+
107+
// LED patterns for report_id 0x01
108+
#define XINPUT_LED_OFF 0x00
109+
#define XINPUT_LED_BLINK 0x01
110+
#define XINPUT_LED_FLASH_1 0x02
111+
#define XINPUT_LED_FLASH_2 0x03
112+
#define XINPUT_LED_FLASH_3 0x04
113+
#define XINPUT_LED_FLASH_4 0x05
114+
#define XINPUT_LED_ON_1 0x06
115+
#define XINPUT_LED_ON_2 0x07
116+
#define XINPUT_LED_ON_3 0x08
117+
#define XINPUT_LED_ON_4 0x09
118+
#define XINPUT_LED_ROTATE 0x0A
119+
#define XINPUT_LED_BLINK_SLOW 0x0B
120+
#define XINPUT_LED_BLINK_SLOW_1 0x0C
121+
#define XINPUT_LED_BLINK_SLOW_2 0x0D
122+
123+
struct xinput_in_report {
124+
uint8_t report_id; /* Always 0x00 */
125+
uint8_t report_size; /* Always 0x14 (20) */
126+
uint16_t buttons; /* DPAD, Start, Back, L3, R3, LB, RB, Guide, A, B, X, Y */
127+
uint8_t lt; /* Left trigger (0-255) */
128+
uint8_t rt; /* Right trigger (0-255) */
129+
int16_t lx; /* Left stick X (-32768 to 32767) */
130+
int16_t ly; /* Left stick Y (-32768 to 32767) */
131+
int16_t rx; /* Right stick X (-32768 to 32767) */
132+
int16_t ry; /* Right stick Y (-32768 to 32767) */
133+
uint8_t reserved[6]; /* Reserved/padding */
134+
} __PACKED;
135+
136+
struct xinput_out_report {
137+
uint8_t report_id; // 0x00 = rumble, 0x01 = LED
138+
uint8_t report_size; // 0x08
139+
uint8_t led; // LED pattern (0x00 for rumble)
140+
uint8_t rumble_l; // Left motor (large, 0-255)
141+
uint8_t rumble_r; // Right motor (small, 0-255)
142+
uint8_t reserved[3]; // Padding
143+
} __PACKED;
144+
145+
// clang-format off
146+
#define XINPUT_DESCRIPTOR_LEN (9 + 16 + 7 + 7)
147+
148+
#define XINPUT_DESCRIPTOR_INIT(bInterfaceNumber, out_ep, in_ep) \
149+
USB_INTERFACE_DESCRIPTOR_INIT(bInterfaceNumber, 0x00, 0x02, 0xff, 0x5d, 0x01, 0x00), /* XInput proprietary descriptor (0x21) */ \
150+
16, 0x21, 0x00, 0x01, 0x01, 0x24, in_ep, 0x14, 0x03, 0x00, 0x03, 0x13, out_ep, 0x00, 0x03, 0x00, \
151+
USB_ENDPOINT_DESCRIPTOR_INIT(in_ep, 0x03, 32, 0x01), \
152+
USB_ENDPOINT_DESCRIPTOR_INIT(out_ep, 0x03, 32, 0x08)
153+
// clang-format on
154+
155+
#define SWITCH_VID 0x0F0D // 0x057E Nintendo Pro Controller
156+
#define SWITCH_PID 0x0092 // 0x2009
157+
#define SWITCH_BCD_DEVICE 0x0100 // v1.00
158+
159+
// Button masks (16-bit)
160+
#define SWITCH_MASK_Y (1U << 0)
161+
#define SWITCH_MASK_B (1U << 1)
162+
#define SWITCH_MASK_A (1U << 2)
163+
#define SWITCH_MASK_X (1U << 3)
164+
#define SWITCH_MASK_L (1U << 4)
165+
#define SWITCH_MASK_R (1U << 5)
166+
#define SWITCH_MASK_ZL (1U << 6)
167+
#define SWITCH_MASK_ZR (1U << 7)
168+
#define SWITCH_MASK_MINUS (1U << 8)
169+
#define SWITCH_MASK_PLUS (1U << 9)
170+
#define SWITCH_MASK_L3 (1U << 10)
171+
#define SWITCH_MASK_R3 (1U << 11)
172+
#define SWITCH_MASK_HOME (1U << 12)
173+
#define SWITCH_MASK_CAPTURE (1U << 13)
174+
175+
// D-pad / Hat switch values
176+
#define SWITCH_HAT_UP 0x00
177+
#define SWITCH_HAT_UP_RIGHT 0x01
178+
#define SWITCH_HAT_RIGHT 0x02
179+
#define SWITCH_HAT_DOWN_RIGHT 0x03
180+
#define SWITCH_HAT_DOWN 0x04
181+
#define SWITCH_HAT_DOWN_LEFT 0x05
182+
#define SWITCH_HAT_LEFT 0x06
183+
#define SWITCH_HAT_UP_LEFT 0x07
184+
#define SWITCH_HAT_CENTER 0x08
185+
186+
// Analog stick range
187+
#define SWITCH_JOYSTICK_MIN 0x00
188+
#define SWITCH_JOYSTICK_MID 0x80
189+
#define SWITCH_JOYSTICK_MAX 0xFF
190+
191+
struct switch_in_report {
192+
uint16_t buttons; // 16 button bits
193+
uint8_t hat; // D-pad (hat switch, 0-8)
194+
uint8_t lx; // Left stick X (0-255, 128 = center)
195+
uint8_t ly; // Left stick Y (0-255, 128 = center)
196+
uint8_t rx; // Right stick X (0-255, 128 = center)
197+
uint8_t ry; // Right stick Y (0-255, 128 = center)
198+
uint8_t vendor; // Vendor-specific byte
199+
} __PACKED;
200+
201+
struct switch_out_report {
202+
uint8_t data[8]; // Vendor-specific rumble data
203+
} __PACKED;
204+
205+
#define HID_SWITCH_REPORT_DESC_SIZE 86
206+
207+
// clang-format off
208+
#define SWITCH_DESCRIPTOR_LEN HID_CUSTOM_INOUT_DESCRIPTOR_LEN
209+
210+
#define SWITCH_DESCRIPTOR_INIT(bInterfaceNumber, out_ep, in_ep) \
211+
HID_CUSTOM_INOUT_DESCRIPTOR_INIT(bInterfaceNumber, 0x00, HID_SWITCH_REPORT_DESC_SIZE, out_ep, in_ep, 64, 0x01)
212+
// clang-format on
213+
214+
struct usb_gamepad_report {
215+
uint32_t buttons;
216+
uint8_t lt;
217+
uint8_t rt;
218+
uint8_t lx;
219+
uint8_t ly;
220+
uint8_t rx;
221+
uint8_t ry;
222+
};
223+
224+
#endif /* USB_GAMEPAD_H */

0 commit comments

Comments
 (0)