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