1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22
23 #ifdef SDL_JOYSTICK_HIDAPI
24
25 #include "SDL_hints.h"
26 #include "SDL_events.h"
27 #include "SDL_joystick.h"
28 #include "SDL_gamecontroller.h"
29 #include "../SDL_sysjoystick.h"
30 #include "SDL_hidapijoystick_c.h"
31 #include "SDL_hidapi_rumble.h"
32
33
34 #ifdef SDL_JOYSTICK_HIDAPI_STADIA
35
36 /* Define this if you want to log all packets from the controller */
37 /*#define DEBUG_STADIA_PROTOCOL*/
38
39 enum
40 {
41 SDL_CONTROLLER_BUTTON_STADIA_SHARE = 15,
42 SDL_CONTROLLER_BUTTON_STADIA_GOOGLE_ASSISTANT,
43 SDL_CONTROLLER_NUM_STADIA_BUTTONS,
44 };
45
46 typedef struct {
47 Uint8 last_state[USB_PACKET_LENGTH];
48 } SDL_DriverStadia_Context;
49
50
51 static SDL_bool
HIDAPI_DriverStadia_IsSupportedDevice(const char * name,SDL_GameControllerType type,Uint16 vendor_id,Uint16 product_id,Uint16 version,int interface_number,int interface_class,int interface_subclass,int interface_protocol)52 HIDAPI_DriverStadia_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
53 {
54 return (type == SDL_CONTROLLER_TYPE_GOOGLE_STADIA) ? SDL_TRUE : SDL_FALSE;
55 }
56
57 static const char *
HIDAPI_DriverStadia_GetDeviceName(Uint16 vendor_id,Uint16 product_id)58 HIDAPI_DriverStadia_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
59 {
60 return "Google Stadia Controller";
61 }
62
63 static SDL_bool
HIDAPI_DriverStadia_InitDevice(SDL_HIDAPI_Device * device)64 HIDAPI_DriverStadia_InitDevice(SDL_HIDAPI_Device *device)
65 {
66 return HIDAPI_JoystickConnected(device, NULL);
67 }
68
69 static int
HIDAPI_DriverStadia_GetDevicePlayerIndex(SDL_HIDAPI_Device * device,SDL_JoystickID instance_id)70 HIDAPI_DriverStadia_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
71 {
72 return -1;
73 }
74
75 static void
HIDAPI_DriverStadia_SetDevicePlayerIndex(SDL_HIDAPI_Device * device,SDL_JoystickID instance_id,int player_index)76 HIDAPI_DriverStadia_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
77 {
78 }
79
80 static SDL_bool
HIDAPI_DriverStadia_OpenJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick)81 HIDAPI_DriverStadia_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
82 {
83 SDL_DriverStadia_Context *ctx;
84
85 ctx = (SDL_DriverStadia_Context *)SDL_calloc(1, sizeof(*ctx));
86 if (!ctx) {
87 SDL_OutOfMemory();
88 return SDL_FALSE;
89 }
90
91 device->dev = SDL_hid_open_path(device->path, 0);
92 if (!device->dev) {
93 SDL_SetError("Couldn't open %s", device->path);
94 SDL_free(ctx);
95 return SDL_FALSE;
96 }
97 device->context = ctx;
98
99 /* Initialize the joystick capabilities */
100 joystick->nbuttons = SDL_CONTROLLER_NUM_STADIA_BUTTONS;
101 joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
102 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
103
104 return SDL_TRUE;
105 }
106
107 static int
HIDAPI_DriverStadia_RumbleJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,Uint16 low_frequency_rumble,Uint16 high_frequency_rumble)108 HIDAPI_DriverStadia_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
109 {
110 Uint8 rumble_packet[] = { 0x05, 0x00, 0x00, 0x00, 0x00 };
111
112 rumble_packet[1] = (low_frequency_rumble & 0xFF);
113 rumble_packet[2] = (low_frequency_rumble >> 8);
114 rumble_packet[3] = (high_frequency_rumble & 0xFF);
115 rumble_packet[4] = (high_frequency_rumble >> 8);
116
117 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
118 return SDL_SetError("Couldn't send rumble packet");
119 }
120 return 0;
121 }
122
123 static int
HIDAPI_DriverStadia_RumbleJoystickTriggers(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,Uint16 left_rumble,Uint16 right_rumble)124 HIDAPI_DriverStadia_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
125 {
126 return SDL_Unsupported();
127 }
128
129 static Uint32
HIDAPI_DriverStadia_GetJoystickCapabilities(SDL_HIDAPI_Device * device,SDL_Joystick * joystick)130 HIDAPI_DriverStadia_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
131 {
132 return SDL_JOYCAP_RUMBLE;
133 }
134
135 static int
HIDAPI_DriverStadia_SetJoystickLED(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,Uint8 red,Uint8 green,Uint8 blue)136 HIDAPI_DriverStadia_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
137 {
138 return SDL_Unsupported();
139 }
140
141 static int
HIDAPI_DriverStadia_SendJoystickEffect(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,const void * data,int size)142 HIDAPI_DriverStadia_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
143 {
144 return SDL_Unsupported();
145 }
146
147 static int
HIDAPI_DriverStadia_SetJoystickSensorsEnabled(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,SDL_bool enabled)148 HIDAPI_DriverStadia_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
149 {
150 return SDL_Unsupported();
151 }
152
153 static void
HIDAPI_DriverStadia_HandleStatePacket(SDL_Joystick * joystick,SDL_DriverStadia_Context * ctx,Uint8 * data,int size)154 HIDAPI_DriverStadia_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverStadia_Context *ctx, Uint8 *data, int size)
155 {
156 Sint16 axis;
157
158 // The format is the same but the original FW will send 10 bytes and January '21 FW update will send 11
159 if (size < 10 || data[0] != 0x03) {
160 /* We don't know how to handle this report */
161 return;
162 }
163
164 if (ctx->last_state[1] != data[1]) {
165 SDL_bool dpad_up = SDL_FALSE;
166 SDL_bool dpad_down = SDL_FALSE;
167 SDL_bool dpad_left = SDL_FALSE;
168 SDL_bool dpad_right = SDL_FALSE;
169
170 switch (data[1]) {
171 case 0:
172 dpad_up = SDL_TRUE;
173 break;
174 case 1:
175 dpad_up = SDL_TRUE;
176 dpad_right = SDL_TRUE;
177 break;
178 case 2:
179 dpad_right = SDL_TRUE;
180 break;
181 case 3:
182 dpad_right = SDL_TRUE;
183 dpad_down = SDL_TRUE;
184 break;
185 case 4:
186 dpad_down = SDL_TRUE;
187 break;
188 case 5:
189 dpad_left = SDL_TRUE;
190 dpad_down = SDL_TRUE;
191 break;
192 case 6:
193 dpad_left = SDL_TRUE;
194 break;
195 case 7:
196 dpad_up = SDL_TRUE;
197 dpad_left = SDL_TRUE;
198 break;
199 default:
200 break;
201 }
202 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
203 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
204 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
205 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
206 }
207
208 if (ctx->last_state[2] != data[2]) {
209 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
210 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
211 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
212 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
213 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_STADIA_SHARE, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
214 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_STADIA_GOOGLE_ASSISTANT, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
215 }
216
217 if (ctx->last_state[3] != data[3]) {
218 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
219 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
220 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
221 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
222 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
223 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
224 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
225 }
226
227 #define READ_STICK_AXIS(offset) \
228 (data[offset] == 0x80 ? 0 : \
229 (Sint16)HIDAPI_RemapVal((float)((int)data[offset] - 0x80), 0x01 - 0x80, 0xff - 0x80, SDL_MIN_SINT16, SDL_MAX_SINT16))
230 {
231 axis = READ_STICK_AXIS(4);
232 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
233 axis = READ_STICK_AXIS(5);
234 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
235 axis = READ_STICK_AXIS(6);
236 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
237 axis = READ_STICK_AXIS(7);
238 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
239 }
240 #undef READ_STICK_AXIS
241
242 #define READ_TRIGGER_AXIS(offset) \
243 (Sint16)(((int)data[offset] * 257) - 32768)
244 {
245 axis = READ_TRIGGER_AXIS(8);
246 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
247 axis = READ_TRIGGER_AXIS(9);
248 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
249 }
250 #undef READ_TRIGGER_AXIS
251
252 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
253 }
254
255 static SDL_bool
HIDAPI_DriverStadia_UpdateDevice(SDL_HIDAPI_Device * device)256 HIDAPI_DriverStadia_UpdateDevice(SDL_HIDAPI_Device *device)
257 {
258 SDL_DriverStadia_Context *ctx = (SDL_DriverStadia_Context *)device->context;
259 SDL_Joystick *joystick = NULL;
260 Uint8 data[USB_PACKET_LENGTH];
261 int size = 0;
262
263 if (device->num_joysticks > 0) {
264 joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
265 }
266 if (!joystick) {
267 return SDL_FALSE;
268 }
269
270 while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
271 #ifdef DEBUG_STADIA_PROTOCOL
272 HIDAPI_DumpPacket("Google Stadia packet: size = %d", data, size);
273 #endif
274 HIDAPI_DriverStadia_HandleStatePacket(joystick, ctx, data, size);
275 }
276
277 if (size < 0) {
278 /* Read error, device is disconnected */
279 HIDAPI_JoystickDisconnected(device, joystick->instance_id);
280 }
281 return (size >= 0);
282 }
283
284 static void
HIDAPI_DriverStadia_CloseJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick)285 HIDAPI_DriverStadia_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
286 {
287 SDL_LockMutex(device->dev_lock);
288 {
289 if (device->dev) {
290 SDL_hid_close(device->dev);
291 device->dev = NULL;
292 }
293
294 SDL_free(device->context);
295 device->context = NULL;
296 }
297 SDL_UnlockMutex(device->dev_lock);
298 }
299
300 static void
HIDAPI_DriverStadia_FreeDevice(SDL_HIDAPI_Device * device)301 HIDAPI_DriverStadia_FreeDevice(SDL_HIDAPI_Device *device)
302 {
303 }
304
305 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverStadia =
306 {
307 SDL_HINT_JOYSTICK_HIDAPI_STADIA,
308 SDL_TRUE,
309 SDL_TRUE,
310 HIDAPI_DriverStadia_IsSupportedDevice,
311 HIDAPI_DriverStadia_GetDeviceName,
312 HIDAPI_DriverStadia_InitDevice,
313 HIDAPI_DriverStadia_GetDevicePlayerIndex,
314 HIDAPI_DriverStadia_SetDevicePlayerIndex,
315 HIDAPI_DriverStadia_UpdateDevice,
316 HIDAPI_DriverStadia_OpenJoystick,
317 HIDAPI_DriverStadia_RumbleJoystick,
318 HIDAPI_DriverStadia_RumbleJoystickTriggers,
319 HIDAPI_DriverStadia_GetJoystickCapabilities,
320 HIDAPI_DriverStadia_SetJoystickLED,
321 HIDAPI_DriverStadia_SendJoystickEffect,
322 HIDAPI_DriverStadia_SetJoystickSensorsEnabled,
323 HIDAPI_DriverStadia_CloseJoystick,
324 HIDAPI_DriverStadia_FreeDevice,
325 };
326
327 #endif /* SDL_JOYSTICK_HIDAPI_STADIA */
328
329 #endif /* SDL_JOYSTICK_HIDAPI */
330
331 /* vi: set ts=4 sw=4 expandtab: */
332