Imported Upstream version 2.0.14
[platform/upstream/SDL.git] / src / joystick / hidapi / SDL_hidapi_gamecube.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_haptic.h"
29 #include "SDL_joystick.h"
30 #include "SDL_gamecontroller.h"
31 #include "../../SDL_hints_c.h"
32 #include "../SDL_sysjoystick.h"
33 #include "SDL_hidapijoystick_c.h"
34 #include "SDL_hidapi_rumble.h"
35
36
37 #ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE
38
39 #define MAX_CONTROLLERS 4
40
41 typedef struct {
42     SDL_JoystickID joysticks[MAX_CONTROLLERS];
43     Uint8 wireless[MAX_CONTROLLERS];
44     Uint8 min_axis[MAX_CONTROLLERS*SDL_CONTROLLER_AXIS_MAX];
45     Uint8 max_axis[MAX_CONTROLLERS*SDL_CONTROLLER_AXIS_MAX];
46     Uint8 rumbleAllowed[MAX_CONTROLLERS];
47     Uint8 rumble[1+MAX_CONTROLLERS];
48     /* Without this variable, hid_write starts to lag a TON */
49     SDL_bool rumbleUpdate;
50     SDL_bool m_bUseButtonLabels;
51 } SDL_DriverGameCube_Context;
52
53 static SDL_bool
54 HIDAPI_DriverGameCube_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)
55 {
56     if (vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER) {
57         /* Nintendo Co., Ltd.  Wii U GameCube Controller Adapter */
58         return SDL_TRUE;
59     }
60     return SDL_FALSE;
61 }
62
63 static const char *
64 HIDAPI_DriverGameCube_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
65 {
66     return "Nintendo GameCube Controller";
67 }
68
69 static void
70 ResetAxisRange(SDL_DriverGameCube_Context *ctx, int joystick_index)
71 {
72     SDL_memset(&ctx->min_axis[joystick_index*SDL_CONTROLLER_AXIS_MAX], 128-88, SDL_CONTROLLER_AXIS_MAX);
73     SDL_memset(&ctx->max_axis[joystick_index*SDL_CONTROLLER_AXIS_MAX], 128+88, SDL_CONTROLLER_AXIS_MAX);
74
75     /* Trigger axes may have a higher resting value */
76     ctx->min_axis[joystick_index*SDL_CONTROLLER_AXIS_MAX+SDL_CONTROLLER_AXIS_TRIGGERLEFT] = 40;
77     ctx->min_axis[joystick_index*SDL_CONTROLLER_AXIS_MAX+SDL_CONTROLLER_AXIS_TRIGGERRIGHT] = 40;
78 }
79
80 static float fsel(float fComparand, float fValGE, float fLT)
81 {
82     return fComparand >= 0 ? fValGE : fLT;
83 }
84
85 static float RemapVal(float val, float A, float B, float C, float D)
86 {
87     if (A == B) {
88         return fsel(val - B , D , C);
89     }
90     if (val < A) {
91         val = A;
92     }
93     if (val > B) {
94         val = B;
95     }
96     return C + (D - C) * (val - A) / (B - A);
97 }
98
99 static void SDLCALL SDL_GameControllerButtonReportingHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
100 {
101     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)userdata;
102     ctx->m_bUseButtonLabels = SDL_GetStringBoolean(hint, SDL_TRUE);
103 }
104
105 static Uint8 RemapButton(SDL_DriverGameCube_Context *ctx, Uint8 button)
106 {
107     if (!ctx->m_bUseButtonLabels) {
108         /* Use button positions */
109         switch (button) {
110         case SDL_CONTROLLER_BUTTON_B:
111             return SDL_CONTROLLER_BUTTON_X;
112         case SDL_CONTROLLER_BUTTON_X:
113             return SDL_CONTROLLER_BUTTON_B;
114         default:
115             break;
116         }
117     }
118     return button;
119 }
120
121 static SDL_bool
122 HIDAPI_DriverGameCube_InitDevice(SDL_HIDAPI_Device *device)
123 {
124     SDL_DriverGameCube_Context *ctx;
125     Uint8 packet[37];
126     Uint8 *curSlot;
127     Uint8 i;
128     int size;
129     Uint8 initMagic = 0x13;
130     Uint8 rumbleMagic = 0x11;
131
132     ctx = (SDL_DriverGameCube_Context *)SDL_calloc(1, sizeof(*ctx));
133     if (!ctx) {
134         SDL_OutOfMemory();
135         return SDL_FALSE;
136     }
137
138     device->dev = hid_open_path(device->path, 0);
139     if (!device->dev) {
140         SDL_free(ctx);
141         SDL_SetError("Couldn't open %s", device->path);
142         return SDL_FALSE;
143     }
144     device->context = ctx;
145
146     ctx->joysticks[0] = -1;
147     ctx->joysticks[1] = -1;
148     ctx->joysticks[2] = -1;
149     ctx->joysticks[3] = -1;
150     ctx->rumble[0] = rumbleMagic;
151
152     /* This is all that's needed to initialize the device. Really! */
153     if (hid_write(device->dev, &initMagic, sizeof(initMagic)) != sizeof(initMagic)) {
154         SDL_SetError("Couldn't initialize WUP-028");
155         goto error;
156     }
157
158     /* Wait for the adapter to initialize */
159     SDL_Delay(10);
160
161     /* Add all the applicable joysticks */
162     while ((size = hid_read_timeout(device->dev, packet, sizeof(packet), 0)) > 0) {
163         if (size < 37 || packet[0] != 0x21) {
164             continue; /* Nothing to do yet...? */
165         }
166
167         /* Go through all 4 slots */
168         curSlot = packet + 1;
169         for (i = 0; i < MAX_CONTROLLERS; i += 1, curSlot += 9) {
170             ctx->wireless[i] = (curSlot[0] & 0x20) != 0;
171
172             /* Only allow rumble if the adapter's second USB cable is connected */
173             ctx->rumbleAllowed[i] = (curSlot[0] & 0x04) != 0 && !ctx->wireless[i];
174
175             if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */
176                 if (ctx->joysticks[i] == -1) {
177                     ResetAxisRange(ctx, i);
178                     HIDAPI_JoystickConnected(device, &ctx->joysticks[i]);
179                 }
180             } else {
181                 if (ctx->joysticks[i] != -1) {
182                     HIDAPI_JoystickDisconnected(device, ctx->joysticks[i]);
183                     ctx->joysticks[i] = -1;
184                 }
185                 continue;
186             }
187         }
188     }
189
190     SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS,
191                         SDL_GameControllerButtonReportingHintChanged, ctx);
192
193     return SDL_TRUE;
194
195 error:
196     if (device->dev) {
197         hid_close(device->dev);
198         device->dev = NULL;
199     }
200     if (device->context) {
201         SDL_free(device->context);
202         device->context = NULL;
203     }
204     return SDL_FALSE;
205 }
206
207 static int
208 HIDAPI_DriverGameCube_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
209 {
210     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
211     Uint8 i;
212
213     for (i = 0; i < 4; ++i) {
214         if (instance_id == ctx->joysticks[i]) {
215             return i;
216         }
217     }
218     return -1;
219 }
220
221 static void
222 HIDAPI_DriverGameCube_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
223 {
224 }
225
226 static SDL_bool
227 HIDAPI_DriverGameCube_UpdateDevice(SDL_HIDAPI_Device *device)
228 {
229     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
230     SDL_Joystick *joystick;
231     Uint8 packet[37];
232     Uint8 *curSlot;
233     Uint8 i;
234     Sint16 axis_value;
235     int size;
236
237     /* Read input packet */
238     while ((size = hid_read_timeout(device->dev, packet, sizeof(packet), 0)) > 0) {
239         if (size < 37 || packet[0] != 0x21) {
240             continue; /* Nothing to do right now...? */
241         }
242
243         /* Go through all 4 slots */
244         curSlot = packet + 1;
245         for (i = 0; i < MAX_CONTROLLERS; i += 1, curSlot += 9) {
246             ctx->wireless[i] = (curSlot[0] & 0x20) != 0;
247
248             /* Only allow rumble if the adapter's second USB cable is connected */
249             ctx->rumbleAllowed[i] = (curSlot[0] & 0x04) != 0 && !ctx->wireless[i];
250
251             if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */
252                 if (ctx->joysticks[i] == -1) {
253                     ResetAxisRange(ctx, i);
254                     HIDAPI_JoystickConnected(device, &ctx->joysticks[i]);
255                 }
256                 joystick = SDL_JoystickFromInstanceID(ctx->joysticks[i]);
257
258                 /* Hasn't been opened yet, skip */
259                 if (joystick == NULL) {
260                     continue;
261                 }
262             } else {
263                 if (ctx->joysticks[i] != -1) {
264                     HIDAPI_JoystickDisconnected(device, ctx->joysticks[i]);
265                     ctx->joysticks[i] = -1;
266                 }
267                 continue;
268             }
269
270             #define READ_BUTTON(off, flag, button) \
271                 SDL_PrivateJoystickButton( \
272                     joystick, \
273                     RemapButton(ctx, button), \
274                     (curSlot[off] & flag) ? SDL_PRESSED : SDL_RELEASED \
275                 );
276             READ_BUTTON(1, 0x01, 0) /* A */
277             READ_BUTTON(1, 0x04, 1) /* B */
278             READ_BUTTON(1, 0x02, 2) /* X */
279             READ_BUTTON(1, 0x08, 3) /* Y */
280             READ_BUTTON(1, 0x10, 4) /* DPAD_LEFT */
281             READ_BUTTON(1, 0x20, 5) /* DPAD_RIGHT */
282             READ_BUTTON(1, 0x40, 6) /* DPAD_DOWN */
283             READ_BUTTON(1, 0x80, 7) /* DPAD_UP */
284             READ_BUTTON(2, 0x01, 8) /* START */
285             READ_BUTTON(2, 0x02, 9) /* RIGHTSHOULDER */
286             /* These two buttons are for the bottoms of the analog triggers.
287              * More than likely, you're going to want to read the axes instead!
288              * -flibit
289              */
290             READ_BUTTON(2, 0x04, 10) /* TRIGGERRIGHT */
291             READ_BUTTON(2, 0x08, 11) /* TRIGGERLEFT */
292             #undef READ_BUTTON
293
294             #define READ_AXIS(off, axis) \
295                 if (axis < SDL_CONTROLLER_AXIS_TRIGGERLEFT) \
296                 if (curSlot[off] < ctx->min_axis[i*SDL_CONTROLLER_AXIS_MAX+axis]) ctx->min_axis[i*SDL_CONTROLLER_AXIS_MAX+axis] = curSlot[off]; \
297                 if (curSlot[off] > ctx->max_axis[i*SDL_CONTROLLER_AXIS_MAX+axis]) ctx->max_axis[i*SDL_CONTROLLER_AXIS_MAX+axis] = curSlot[off]; \
298                 axis_value = (Sint16)(RemapVal(curSlot[off], ctx->min_axis[i*SDL_CONTROLLER_AXIS_MAX+axis], ctx->max_axis[i*SDL_CONTROLLER_AXIS_MAX+axis], SDL_MIN_SINT16, SDL_MAX_SINT16)); \
299                 SDL_PrivateJoystickAxis( \
300                     joystick, \
301                     axis, axis_value \
302                 );
303             READ_AXIS(3, SDL_CONTROLLER_AXIS_LEFTX)
304             READ_AXIS(4, SDL_CONTROLLER_AXIS_LEFTY)
305             READ_AXIS(5, SDL_CONTROLLER_AXIS_RIGHTX)
306             READ_AXIS(6, SDL_CONTROLLER_AXIS_RIGHTY)
307             READ_AXIS(7, SDL_CONTROLLER_AXIS_TRIGGERLEFT)
308             READ_AXIS(8, SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
309             #undef READ_AXIS
310         }
311     }
312
313     /* Write rumble packet */
314     if (ctx->rumbleUpdate) {
315         SDL_HIDAPI_SendRumble(device, ctx->rumble, sizeof(ctx->rumble));
316         ctx->rumbleUpdate = SDL_FALSE;
317     }
318
319     /* If we got here, nothing bad happened! */
320     return SDL_TRUE;
321 }
322
323 static SDL_bool
324 HIDAPI_DriverGameCube_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
325 {
326     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
327     Uint8 i;
328     for (i = 0; i < MAX_CONTROLLERS; i += 1) {
329         if (joystick->instance_id == ctx->joysticks[i]) {
330             joystick->nbuttons = 12;
331             joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
332             joystick->epowerlevel = ctx->wireless[i] ? SDL_JOYSTICK_POWER_UNKNOWN : SDL_JOYSTICK_POWER_WIRED;
333             return SDL_TRUE;
334         }
335     }
336     return SDL_FALSE; /* Should never get here! */
337 }
338
339 static int
340 HIDAPI_DriverGameCube_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
341 {
342     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
343     Uint8 i, val;
344     for (i = 0; i < MAX_CONTROLLERS; i += 1) {
345         if (joystick->instance_id == ctx->joysticks[i]) {
346             if (ctx->wireless[i]) {
347                 return SDL_SetError("Ninteno GameCube WaveBird controllers do not support rumble");
348             }
349             if (!ctx->rumbleAllowed[i]) {
350                 return SDL_SetError("Second USB cable for WUP-028 not connected");
351             }
352             val = (low_frequency_rumble > 0 || high_frequency_rumble > 0);
353             if (val != ctx->rumble[i + 1]) {
354                 ctx->rumble[i + 1] = val;
355                 ctx->rumbleUpdate = SDL_TRUE;
356             }
357             return 0;
358         }
359     }
360
361     /* Should never get here! */
362     SDL_SetError("Couldn't find joystick");
363     return -1;
364 }
365
366 static int
367 HIDAPI_DriverGameCube_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
368 {
369     return SDL_Unsupported();
370 }
371
372 static SDL_bool
373 HIDAPI_DriverGameCube_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
374 {
375     return SDL_FALSE;
376 }
377
378 static int
379 HIDAPI_DriverGameCube_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
380 {
381     return SDL_Unsupported();
382 }
383
384 static int
385 HIDAPI_DriverGameCube_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
386 {
387     return SDL_Unsupported();
388 }
389
390 static void
391 HIDAPI_DriverGameCube_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
392 {
393     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
394
395     /* Stop rumble activity */
396     if (ctx->rumbleUpdate) {
397         SDL_HIDAPI_SendRumble(device, ctx->rumble, sizeof(ctx->rumble));
398         ctx->rumbleUpdate = SDL_FALSE;
399     }
400 }
401
402 static void
403 HIDAPI_DriverGameCube_FreeDevice(SDL_HIDAPI_Device *device)
404 {
405     SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
406
407     hid_close(device->dev);
408     device->dev = NULL;
409
410     SDL_DelHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS,
411                         SDL_GameControllerButtonReportingHintChanged, ctx);
412
413     SDL_free(device->context);
414     device->context = NULL;
415 }
416
417 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube =
418 {
419     SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE,
420     SDL_TRUE,
421     HIDAPI_DriverGameCube_IsSupportedDevice,
422     HIDAPI_DriverGameCube_GetDeviceName,
423     HIDAPI_DriverGameCube_InitDevice,
424     HIDAPI_DriverGameCube_GetDevicePlayerIndex,
425     HIDAPI_DriverGameCube_SetDevicePlayerIndex,
426     HIDAPI_DriverGameCube_UpdateDevice,
427     HIDAPI_DriverGameCube_OpenJoystick,
428     HIDAPI_DriverGameCube_RumbleJoystick,
429     HIDAPI_DriverGameCube_RumbleJoystickTriggers,
430     HIDAPI_DriverGameCube_HasJoystickLED,
431     HIDAPI_DriverGameCube_SetJoystickLED,
432     HIDAPI_DriverGameCube_SetJoystickSensorsEnabled,
433     HIDAPI_DriverGameCube_CloseJoystick,
434     HIDAPI_DriverGameCube_FreeDevice,
435 };
436
437 #endif /* SDL_JOYSTICK_HIDAPI_GAMECUBE */
438
439 #endif /* SDL_JOYSTICK_HIDAPI */
440
441 /* vi: set ts=4 sw=4 expandtab: */