Imported Upstream version 2.0.14
[platform/upstream/SDL.git] / src / joystick / hidapi / SDL_hidapi_xbox360.c
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 /* Define this if you want to log all packets from the controller */
38 /*#define DEBUG_XBOX_PROTOCOL*/
39
40
41 typedef struct {
42     Uint8 last_state[USB_PACKET_LENGTH];
43 } SDL_DriverXbox360_Context;
44
45 #if defined(__MACOSX__)
46 static SDL_bool
47 IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
48 {
49     /* Check to see if it's the Xbox One S or Xbox One Elite Series 2 in Bluetooth mode */
50     if (vendor_id == USB_VENDOR_MICROSOFT) {
51         if (product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH ||
52             product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH ||
53             product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH) {
54             return SDL_TRUE;
55         }
56     }
57     return SDL_FALSE;
58 }
59 #endif
60
61 static SDL_bool
62 HIDAPI_DriverXbox360_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)
63 {
64     const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */
65
66     if (vendor_id == USB_VENDOR_NVIDIA) {
67         /* This is the NVIDIA Shield controller which doesn't talk Xbox controller protocol */
68         return SDL_FALSE;
69     }
70     if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == 0x0291 || product_id == 0x0719)) ||
71         (type == SDL_CONTROLLER_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) {
72         /* This is the wireless dongle, which talks a different protocol */
73         return SDL_FALSE;
74     }
75     if (interface_number > 0) {
76         /* This is the chatpad or other input interface, not the Xbox 360 interface */
77         return SDL_FALSE;
78     }
79 #if defined(__MACOSX__) || defined(__WIN32__)
80     if (vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x028e && version == 1) {
81         /* This is the Steam Virtual Gamepad, which isn't supported by this driver */
82         return SDL_FALSE;
83     }
84 #endif
85 #if defined(__MACOSX__)
86     /* Wired Xbox One controllers are handled by this driver, interfacing with
87        the 360Controller driver available from:
88        https://github.com/360Controller/360Controller/releases
89
90        Bluetooth Xbox One controllers are handled by the SDL Xbox One driver
91     */
92     if (IsBluetoothXboxOneController(vendor_id, product_id)) {
93         return SDL_FALSE;
94     }
95     return (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE) ? SDL_TRUE : SDL_FALSE;
96 #else
97     return (type == SDL_CONTROLLER_TYPE_XBOX360) ? SDL_TRUE : SDL_FALSE;
98 #endif
99 }
100
101 static const char *
102 HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
103 {
104     return NULL;
105 }
106
107 static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
108 {
109     Uint8 mode = 0x02 + slot;
110     const Uint8 led_packet[] = { 0x01, 0x03, mode };
111
112     if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
113         return SDL_FALSE;
114     }
115     return SDL_TRUE;
116 }
117
118 static SDL_bool
119 HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device)
120 {
121     return HIDAPI_JoystickConnected(device, NULL);
122 }
123
124 static int
125 HIDAPI_DriverXbox360_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
126 {
127     return -1;
128 }
129
130 static void
131 HIDAPI_DriverXbox360_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
132 {
133     if (!device->dev) {
134         return;
135     }
136     SetSlotLED(device->dev, (player_index % 4));
137 }
138
139 static SDL_bool
140 HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
141 {
142     SDL_DriverXbox360_Context *ctx;
143     int player_index;
144
145     ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx));
146     if (!ctx) {
147         SDL_OutOfMemory();
148         return SDL_FALSE;
149     }
150
151     device->dev = hid_open_path(device->path, 0);
152     if (!device->dev) {
153         SDL_SetError("Couldn't open %s", device->path);
154         SDL_free(ctx);
155         return SDL_FALSE;
156     }
157     device->context = ctx;
158
159     /* Set the controller LED */
160     player_index = SDL_JoystickGetPlayerIndex(joystick);
161     if (player_index >= 0) {
162         SetSlotLED(device->dev, (player_index % 4));
163     }
164
165     /* Initialize the joystick capabilities */
166     joystick->nbuttons = 15;
167     joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
168     joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
169
170     return SDL_TRUE;
171 }
172
173 static int
174 HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
175 {
176 #ifdef __MACOSX__
177     if (IsBluetoothXboxOneController(device->vendor_id, device->product_id)) {
178         Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00 };
179
180         rumble_packet[4] = (low_frequency_rumble >> 8);
181         rumble_packet[5] = (high_frequency_rumble >> 8);
182
183         if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
184             return SDL_SetError("Couldn't send rumble packet");
185         }
186     } else {
187         /* On Mac OS X the 360Controller driver uses this short report,
188            and we need to prefix it with a magic token so hidapi passes it through untouched
189          */
190         Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 };
191
192         rumble_packet[6+2] = (low_frequency_rumble >> 8);
193         rumble_packet[6+3] = (high_frequency_rumble >> 8);
194
195         if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
196             return SDL_SetError("Couldn't send rumble packet");
197         }
198     }
199 #else
200     Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
201
202     rumble_packet[3] = (low_frequency_rumble >> 8);
203     rumble_packet[4] = (high_frequency_rumble >> 8);
204
205     if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
206         return SDL_SetError("Couldn't send rumble packet");
207     }
208 #endif
209     return 0;
210 }
211
212 static int
213 HIDAPI_DriverXbox360_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
214 {
215     return SDL_Unsupported();
216 }
217
218 static SDL_bool
219 HIDAPI_DriverXbox360_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
220 {
221     /* Doesn't have an RGB LED, so don't return true here */
222     return SDL_FALSE;
223 }
224
225 static int
226 HIDAPI_DriverXbox360_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
227 {
228     return SDL_Unsupported();
229 }
230
231 static int
232 HIDAPI_DriverXbox360_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
233 {
234     return SDL_Unsupported();
235 }
236
237 static void
238 HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
239 {
240     Sint16 axis;
241 #ifdef __MACOSX__
242     const SDL_bool invert_y_axes = SDL_FALSE;
243 #else
244     const SDL_bool invert_y_axes = SDL_TRUE;
245 #endif
246
247     if (ctx->last_state[2] != data[2]) {
248         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
249         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
250         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
251         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
252         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
253         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
254         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
255         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
256     }
257
258     if (ctx->last_state[3] != data[3]) {
259         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
260         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
261         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
262         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
263         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
264         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
265         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
266     }
267
268     axis = ((int)data[4] * 257) - 32768;
269     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
270     axis = ((int)data[5] * 257) - 32768;
271     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
272     axis = *(Sint16*)(&data[6]);
273     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
274     axis = *(Sint16*)(&data[8]);
275     if (invert_y_axes) {
276         axis = ~axis;
277     }
278     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
279     axis = *(Sint16*)(&data[10]);
280     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
281     axis = *(Sint16*)(&data[12]);
282     if (invert_y_axes) {
283         axis = ~axis;
284     }
285     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
286
287     SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
288 }
289
290 static SDL_bool
291 HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
292 {
293     SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
294     SDL_Joystick *joystick = NULL;
295     Uint8 data[USB_PACKET_LENGTH];
296     int size = 0;
297
298     if (device->num_joysticks > 0) {
299         joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
300     }
301     if (!joystick) {
302         return SDL_FALSE;
303     }
304
305     while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
306 #ifdef DEBUG_XBOX_PROTOCOL
307         HIDAPI_DumpPacket("Xbox 360 packet: size = %d", data, size);
308 #endif
309         if (data[0] == 0x00) {
310             HIDAPI_DriverXbox360_HandleStatePacket(joystick, ctx, data, size);
311         }
312     }
313
314     if (size < 0) {
315         /* Read error, device is disconnected */
316         HIDAPI_JoystickDisconnected(device, joystick->instance_id);
317     }
318     return (size >= 0);
319 }
320
321 static void
322 HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
323 {
324     if (device->dev) {
325         hid_close(device->dev);
326         device->dev = NULL;
327     }
328
329     SDL_free(device->context);
330     device->context = NULL;
331 }
332
333 static void
334 HIDAPI_DriverXbox360_FreeDevice(SDL_HIDAPI_Device *device)
335 {
336 }
337
338 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
339 {
340     SDL_HINT_JOYSTICK_HIDAPI_XBOX,
341     SDL_TRUE,
342     HIDAPI_DriverXbox360_IsSupportedDevice,
343     HIDAPI_DriverXbox360_GetDeviceName,
344     HIDAPI_DriverXbox360_InitDevice,
345     HIDAPI_DriverXbox360_GetDevicePlayerIndex,
346     HIDAPI_DriverXbox360_SetDevicePlayerIndex,
347     HIDAPI_DriverXbox360_UpdateDevice,
348     HIDAPI_DriverXbox360_OpenJoystick,
349     HIDAPI_DriverXbox360_RumbleJoystick,
350     HIDAPI_DriverXbox360_RumbleJoystickTriggers,
351     HIDAPI_DriverXbox360_HasJoystickLED,
352     HIDAPI_DriverXbox360_SetJoystickLED,
353     HIDAPI_DriverXbox360_SetJoystickSensorsEnabled,
354     HIDAPI_DriverXbox360_CloseJoystick,
355     HIDAPI_DriverXbox360_FreeDevice,
356 };
357
358 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
359
360 #endif /* SDL_JOYSTICK_HIDAPI */
361
362 /* vi: set ts=4 sw=4 expandtab: */