1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 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_timer.h"
28 #include "SDL_joystick.h"
29 #include "SDL_gamecontroller.h"
30 #include "../SDL_sysjoystick.h"
31 #include "SDL_hidapijoystick_c.h"
32 #include "SDL_hidapi_rumble.h"
33
34
35 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
36
37
38 typedef struct {
39 SDL_bool connected;
40 Uint8 last_state[USB_PACKET_LENGTH];
41 } SDL_DriverXbox360W_Context;
42
43
44 static SDL_bool
HIDAPI_DriverXbox360W_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)45 HIDAPI_DriverXbox360W_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)
46 {
47 const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */
48
49 if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == 0x0291 || product_id == 0x02a9 || product_id == 0x0719)) ||
50 (type == SDL_CONTROLLER_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) {
51 return SDL_TRUE;
52 }
53 return SDL_FALSE;
54 }
55
56 static const char *
HIDAPI_DriverXbox360W_GetDeviceName(Uint16 vendor_id,Uint16 product_id)57 HIDAPI_DriverXbox360W_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
58 {
59 return "Xbox 360 Wireless Controller";
60 }
61
SetSlotLED(hid_device * dev,Uint8 slot)62 static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
63 {
64 Uint8 mode = 0x02 + slot;
65 const Uint8 led_packet[] = { 0x00, 0x00, 0x08, (0x40 + (mode % 0x0e)), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
66
67 if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
68 return SDL_FALSE;
69 }
70 return SDL_TRUE;
71 }
72
73 static void
UpdatePowerLevel(SDL_Joystick * joystick,Uint8 level)74 UpdatePowerLevel(SDL_Joystick *joystick, Uint8 level)
75 {
76 float normalized_level = (float)level / 255.0f;
77
78 if (normalized_level <= 0.05f) {
79 joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
80 } else if (normalized_level <= 0.20f) {
81 joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
82 } else if (normalized_level <= 0.70f) {
83 joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
84 } else {
85 joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
86 }
87 }
88
89 static SDL_bool
HIDAPI_DriverXbox360W_InitDevice(SDL_HIDAPI_Device * device)90 HIDAPI_DriverXbox360W_InitDevice(SDL_HIDAPI_Device *device)
91 {
92 SDL_DriverXbox360W_Context *ctx;
93
94 /* Requests controller presence information from the wireless dongle */
95 const Uint8 init_packet[] = { 0x08, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
96
97 ctx = (SDL_DriverXbox360W_Context *)SDL_calloc(1, sizeof(*ctx));
98 if (!ctx) {
99 SDL_OutOfMemory();
100 return SDL_FALSE;
101 }
102
103 device->dev = hid_open_path(device->path, 0);
104 if (!device->dev) {
105 SDL_free(ctx);
106 SDL_SetError("Couldn't open %s", device->path);
107 return SDL_FALSE;
108 }
109 device->context = ctx;
110
111 if (hid_write(device->dev, init_packet, sizeof(init_packet)) != sizeof(init_packet)) {
112 SDL_SetError("Couldn't write init packet");
113 return SDL_FALSE;
114 }
115
116 return SDL_TRUE;
117 }
118
119 static int
HIDAPI_DriverXbox360W_GetDevicePlayerIndex(SDL_HIDAPI_Device * device,SDL_JoystickID instance_id)120 HIDAPI_DriverXbox360W_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
121 {
122 return -1;
123 }
124
125 static void
HIDAPI_DriverXbox360W_SetDevicePlayerIndex(SDL_HIDAPI_Device * device,SDL_JoystickID instance_id,int player_index)126 HIDAPI_DriverXbox360W_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
127 {
128 SetSlotLED(device->dev, (player_index % 4));
129 }
130
131 static SDL_bool
HIDAPI_DriverXbox360W_OpenJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick)132 HIDAPI_DriverXbox360W_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
133 {
134 SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context;
135
136 SDL_zeroa(ctx->last_state);
137
138 /* Initialize the joystick capabilities */
139 joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
140 joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
141 joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN;
142
143 return SDL_TRUE;
144 }
145
146 static int
HIDAPI_DriverXbox360W_RumbleJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,Uint16 low_frequency_rumble,Uint16 high_frequency_rumble)147 HIDAPI_DriverXbox360W_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
148 {
149 Uint8 rumble_packet[] = { 0x00, 0x01, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
150
151 rumble_packet[5] = (low_frequency_rumble >> 8);
152 rumble_packet[6] = (high_frequency_rumble >> 8);
153
154 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
155 return SDL_SetError("Couldn't send rumble packet");
156 }
157 return 0;
158 }
159
160 static void
HIDAPI_DriverXbox360W_HandleStatePacket(SDL_Joystick * joystick,hid_device * dev,SDL_DriverXbox360W_Context * ctx,Uint8 * data,int size)161 HIDAPI_DriverXbox360W_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360W_Context *ctx, Uint8 *data, int size)
162 {
163 Sint16 axis;
164 const SDL_bool invert_y_axes = SDL_TRUE;
165
166 if (ctx->last_state[2] != data[2]) {
167 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
168 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
169 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
170 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
171 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
172 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
173 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
174 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
175 }
176
177 if (ctx->last_state[3] != data[3]) {
178 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
179 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
180 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
181 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
182 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
183 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
184 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
185 }
186
187 axis = ((int)data[4] * 257) - 32768;
188 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
189 axis = ((int)data[5] * 257) - 32768;
190 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
191 axis = *(Sint16*)(&data[6]);
192 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
193 axis = *(Sint16*)(&data[8]);
194 if (invert_y_axes) {
195 axis = ~axis;
196 }
197 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
198 axis = *(Sint16*)(&data[10]);
199 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
200 axis = *(Sint16*)(&data[12]);
201 if (invert_y_axes) {
202 axis = ~axis;
203 }
204 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
205
206 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
207 }
208
209 static SDL_bool
HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device * device)210 HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device *device)
211 {
212 SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context;
213 SDL_Joystick *joystick = NULL;
214 Uint8 data[USB_PACKET_LENGTH];
215 int size;
216
217 if (device->num_joysticks > 0) {
218 joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
219 }
220
221 while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
222 if (size == 2 && data[0] == 0x08) {
223 SDL_bool connected = (data[1] & 0x80) ? SDL_TRUE : SDL_FALSE;
224 #ifdef DEBUG_JOYSTICK
225 SDL_Log("Connected = %s\n", connected ? "TRUE" : "FALSE");
226 #endif
227 if (connected != ctx->connected) {
228 ctx->connected = connected;
229
230 if (connected) {
231 SDL_JoystickID joystickID;
232
233 HIDAPI_JoystickConnected(device, &joystickID, SDL_FALSE);
234
235 } else if (device->num_joysticks > 0) {
236 HIDAPI_JoystickDisconnected(device, device->joysticks[0], SDL_FALSE);
237 }
238 }
239 } else if (size == 29 && data[0] == 0x00 && data[1] == 0x0f && data[2] == 0x00 && data[3] == 0xf0) {
240 /* Serial number is data[7-13] */
241 #ifdef DEBUG_JOYSTICK
242 SDL_Log("Battery status (initial): %d\n", data[17]);
243 #endif
244 if (joystick) {
245 UpdatePowerLevel(joystick, data[17]);
246 }
247 } else if (size == 29 && data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x00 && data[3] == 0x13) {
248 #ifdef DEBUG_JOYSTICK
249 SDL_Log("Battery status: %d\n", data[4]);
250 #endif
251 if (joystick) {
252 UpdatePowerLevel(joystick, data[4]);
253 }
254 } else if (size == 29 && data[0] == 0x00 && (data[1] & 0x01) == 0x01) {
255 if (joystick) {
256 HIDAPI_DriverXbox360W_HandleStatePacket(joystick, device->dev, ctx, data+4, size-4);
257 }
258 }
259 }
260
261 if (joystick) {
262 if (size < 0) {
263 /* Read error, device is disconnected */
264 HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE);
265 }
266 }
267 return (size >= 0);
268 }
269
270 static void
HIDAPI_DriverXbox360W_CloseJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick)271 HIDAPI_DriverXbox360W_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
272 {
273 }
274
275 static void
HIDAPI_DriverXbox360W_FreeDevice(SDL_HIDAPI_Device * device)276 HIDAPI_DriverXbox360W_FreeDevice(SDL_HIDAPI_Device *device)
277 {
278 hid_close(device->dev);
279 device->dev = NULL;
280
281 SDL_free(device->context);
282 device->context = NULL;
283 }
284
285 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360W =
286 {
287 SDL_HINT_JOYSTICK_HIDAPI_XBOX,
288 SDL_TRUE,
289 HIDAPI_DriverXbox360W_IsSupportedDevice,
290 HIDAPI_DriverXbox360W_GetDeviceName,
291 HIDAPI_DriverXbox360W_InitDevice,
292 HIDAPI_DriverXbox360W_GetDevicePlayerIndex,
293 HIDAPI_DriverXbox360W_SetDevicePlayerIndex,
294 HIDAPI_DriverXbox360W_UpdateDevice,
295 HIDAPI_DriverXbox360W_OpenJoystick,
296 HIDAPI_DriverXbox360W_RumbleJoystick,
297 HIDAPI_DriverXbox360W_CloseJoystick,
298 HIDAPI_DriverXbox360W_FreeDevice,
299 NULL
300 };
301
302 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
303
304 #endif /* SDL_JOYSTICK_HIDAPI */
305
306 /* vi: set ts=4 sw=4 expandtab: */
307