slouken@12088
|
1 |
/*
|
slouken@12088
|
2 |
Simple DirectMedia Layer
|
slouken@12503
|
3 |
Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
|
slouken@12088
|
4 |
|
slouken@12088
|
5 |
This software is provided 'as-is', without any express or implied
|
slouken@12088
|
6 |
warranty. In no event will the authors be held liable for any damages
|
slouken@12088
|
7 |
arising from the use of this software.
|
slouken@12088
|
8 |
|
slouken@12088
|
9 |
Permission is granted to anyone to use this software for any purpose,
|
slouken@12088
|
10 |
including commercial applications, and to alter it and redistribute it
|
slouken@12088
|
11 |
freely, subject to the following restrictions:
|
slouken@12088
|
12 |
|
slouken@12088
|
13 |
1. The origin of this software must not be misrepresented; you must not
|
slouken@12088
|
14 |
claim that you wrote the original software. If you use this software
|
slouken@12088
|
15 |
in a product, an acknowledgment in the product documentation would be
|
slouken@12088
|
16 |
appreciated but is not required.
|
slouken@12088
|
17 |
2. Altered source versions must be plainly marked as such, and must not be
|
slouken@12088
|
18 |
misrepresented as being the original software.
|
slouken@12088
|
19 |
3. This notice may not be removed or altered from any source distribution.
|
slouken@12088
|
20 |
*/
|
slouken@12097
|
21 |
/* This driver supports both simplified reports and the extended input reports enabled by Steam.
|
slouken@12097
|
22 |
Code and logic contributed by Valve Corporation under the SDL zlib license.
|
slouken@12097
|
23 |
*/
|
slouken@12088
|
24 |
#include "../../SDL_internal.h"
|
slouken@12088
|
25 |
|
slouken@12088
|
26 |
#ifdef SDL_JOYSTICK_HIDAPI
|
slouken@12088
|
27 |
|
slouken@12088
|
28 |
#include "SDL_hints.h"
|
slouken@12088
|
29 |
#include "SDL_log.h"
|
slouken@12088
|
30 |
#include "SDL_events.h"
|
slouken@12088
|
31 |
#include "SDL_timer.h"
|
slouken@12088
|
32 |
#include "SDL_joystick.h"
|
slouken@12088
|
33 |
#include "SDL_gamecontroller.h"
|
slouken@12088
|
34 |
#include "../SDL_sysjoystick.h"
|
slouken@12088
|
35 |
#include "SDL_hidapijoystick_c.h"
|
slouken@12088
|
36 |
|
slouken@12088
|
37 |
|
slouken@12088
|
38 |
#ifdef SDL_JOYSTICK_HIDAPI_PS4
|
slouken@12088
|
39 |
|
slouken@12088
|
40 |
#define SONY_USB_VID 0x054C
|
slouken@12088
|
41 |
#define SONY_DS4_PID 0x05C4
|
slouken@12088
|
42 |
#define SONY_DS4_DONGLE_PID 0x0BA0
|
slouken@12088
|
43 |
#define SONY_DS4_SLIM_PID 0x09CC
|
slouken@12088
|
44 |
|
slouken@12172
|
45 |
#define RAZER_USB_VID 0x1532
|
slouken@12172
|
46 |
#define RAZER_PANTHERA_PID 0X0401
|
slouken@12176
|
47 |
#define RAZER_PANTHERA_EVO_PID 0x1008
|
slouken@12172
|
48 |
|
slouken@12088
|
49 |
#define USB_PACKET_LENGTH 64
|
slouken@12088
|
50 |
|
slouken@12088
|
51 |
#define VOLUME_CHECK_INTERVAL_MS (10 * 1000)
|
slouken@12088
|
52 |
|
slouken@12088
|
53 |
typedef enum
|
slouken@12088
|
54 |
{
|
slouken@12088
|
55 |
k_EPS4ReportIdUsbState = 1,
|
slouken@12088
|
56 |
k_EPS4ReportIdUsbEffects = 5,
|
slouken@12088
|
57 |
k_EPS4ReportIdBluetoothState = 17,
|
slouken@12088
|
58 |
k_EPS4ReportIdBluetoothEffects = 17,
|
slouken@12088
|
59 |
k_EPS4ReportIdDisconnectMessage = 226,
|
slouken@12088
|
60 |
} EPS4ReportId;
|
slouken@12088
|
61 |
|
slouken@12088
|
62 |
typedef enum
|
slouken@12088
|
63 |
{
|
slouken@12088
|
64 |
k_ePS4FeatureReportIdGyroCalibration_USB = 0x02,
|
slouken@12088
|
65 |
k_ePS4FeatureReportIdGyroCalibration_BT = 0x05,
|
slouken@12088
|
66 |
k_ePS4FeatureReportIdSerialNumber = 0x12,
|
slouken@12088
|
67 |
} EPS4FeatureReportID;
|
slouken@12088
|
68 |
|
slouken@12088
|
69 |
typedef struct
|
slouken@12088
|
70 |
{
|
slouken@12088
|
71 |
Uint8 ucLeftJoystickX;
|
slouken@12088
|
72 |
Uint8 ucLeftJoystickY;
|
slouken@12088
|
73 |
Uint8 ucRightJoystickX;
|
slouken@12088
|
74 |
Uint8 ucRightJoystickY;
|
slouken@12088
|
75 |
Uint8 rgucButtonsHatAndCounter[ 3 ];
|
slouken@12088
|
76 |
Uint8 ucTriggerLeft;
|
slouken@12088
|
77 |
Uint8 ucTriggerRight;
|
slouken@12088
|
78 |
Uint8 _rgucPad0[ 3 ];
|
slouken@12088
|
79 |
Sint16 sGyroX;
|
slouken@12088
|
80 |
Sint16 sGyroY;
|
slouken@12088
|
81 |
Sint16 sGyroZ;
|
slouken@12088
|
82 |
Sint16 sAccelX;
|
slouken@12088
|
83 |
Sint16 sAccelY;
|
slouken@12088
|
84 |
Sint16 sAccelZ;
|
slouken@12088
|
85 |
Uint8 _rgucPad1[ 5 ];
|
slouken@12088
|
86 |
Uint8 ucBatteryLevel;
|
slouken@12088
|
87 |
Uint8 _rgucPad2[ 4 ];
|
slouken@12088
|
88 |
Uint8 ucTrackpadCounter1;
|
slouken@12088
|
89 |
Uint8 rgucTrackpadData1[ 3 ];
|
slouken@12088
|
90 |
Uint8 ucTrackpadCounter2;
|
slouken@12088
|
91 |
Uint8 rgucTrackpadData2[ 3 ];
|
slouken@12088
|
92 |
} PS4StatePacket_t;
|
slouken@12088
|
93 |
|
slouken@12088
|
94 |
typedef struct
|
slouken@12088
|
95 |
{
|
slouken@12088
|
96 |
Uint8 ucRumbleRight;
|
slouken@12088
|
97 |
Uint8 ucRumbleLeft;
|
slouken@12088
|
98 |
Uint8 ucLedRed;
|
slouken@12088
|
99 |
Uint8 ucLedGreen;
|
slouken@12088
|
100 |
Uint8 ucLedBlue;
|
slouken@12088
|
101 |
Uint8 ucLedDelayOn;
|
slouken@12088
|
102 |
Uint8 ucLedDelayOff;
|
slouken@12088
|
103 |
Uint8 _rgucPad0[ 8 ];
|
slouken@12088
|
104 |
Uint8 ucVolumeLeft;
|
slouken@12088
|
105 |
Uint8 ucVolumeRight;
|
slouken@12088
|
106 |
Uint8 ucVolumeMic;
|
slouken@12088
|
107 |
Uint8 ucVolumeSpeaker;
|
slouken@12088
|
108 |
} DS4EffectsState_t;
|
slouken@12088
|
109 |
|
slouken@12088
|
110 |
typedef struct {
|
flibitijibibo@12641
|
111 |
SDL_JoystickID joystickID;
|
slouken@12088
|
112 |
SDL_bool is_dongle;
|
slouken@12088
|
113 |
SDL_bool is_bluetooth;
|
slouken@12088
|
114 |
SDL_bool audio_supported;
|
slouken@12163
|
115 |
SDL_bool rumble_supported;
|
slouken@12088
|
116 |
Uint8 volume;
|
slouken@12088
|
117 |
Uint32 last_volume_check;
|
slouken@12088
|
118 |
Uint32 rumble_expiration;
|
slouken@12088
|
119 |
PS4StatePacket_t last_state;
|
slouken@12088
|
120 |
} SDL_DriverPS4_Context;
|
slouken@12088
|
121 |
|
slouken@12088
|
122 |
|
slouken@12088
|
123 |
/* Public domain CRC implementation adapted from:
|
slouken@12088
|
124 |
http://home.thep.lu.se/~bjorn/crc/crc32_simple.c
|
slouken@12088
|
125 |
*/
|
slouken@12088
|
126 |
static Uint32 crc32_for_byte(Uint32 r)
|
slouken@12088
|
127 |
{
|
slouken@12088
|
128 |
int i;
|
slouken@12088
|
129 |
for(i = 0; i < 8; ++i) {
|
slouken@12088
|
130 |
r = (r & 1? 0: (Uint32)0xEDB88320L) ^ r >> 1;
|
slouken@12088
|
131 |
}
|
slouken@12088
|
132 |
return r ^ (Uint32)0xFF000000L;
|
slouken@12088
|
133 |
}
|
slouken@12088
|
134 |
|
slouken@12088
|
135 |
static Uint32 crc32(Uint32 crc, const void *data, int count)
|
slouken@12088
|
136 |
{
|
slouken@12088
|
137 |
int i;
|
slouken@12088
|
138 |
for(i = 0; i < count; ++i) {
|
slouken@12088
|
139 |
crc = crc32_for_byte((Uint8)crc ^ ((const Uint8*)data)[i]) ^ crc >> 8;
|
slouken@12088
|
140 |
}
|
slouken@12088
|
141 |
return crc;
|
slouken@12088
|
142 |
}
|
slouken@12088
|
143 |
|
sezeroz@12251
|
144 |
#if defined(__WIN32__) && defined(HAVE_ENDPOINTVOLUME_H)
|
slouken@12088
|
145 |
#include "../../core/windows/SDL_windows.h"
|
slouken@12088
|
146 |
|
sezeroz@12120
|
147 |
#ifndef NTDDI_VISTA
|
sezeroz@12120
|
148 |
#define NTDDI_VISTA 0x06000000
|
sezeroz@12120
|
149 |
#endif
|
sezeroz@12120
|
150 |
#ifndef _WIN32_WINNT_VISTA
|
sezeroz@12120
|
151 |
#define _WIN32_WINNT_VISTA 0x0600
|
sezeroz@12120
|
152 |
#endif
|
sezeroz@12120
|
153 |
|
slouken@12088
|
154 |
/* Define Vista for the Audio related includes below to work */
|
slouken@12088
|
155 |
#undef NTDDI_VERSION
|
slouken@12088
|
156 |
#define NTDDI_VERSION NTDDI_VISTA
|
slouken@12088
|
157 |
#undef _WIN32_WINNT
|
slouken@12088
|
158 |
#define _WIN32_WINNT _WIN32_WINNT_VISTA
|
slouken@12088
|
159 |
#define COBJMACROS
|
sezeroz@12101
|
160 |
#include <mmdeviceapi.h>
|
sezeroz@12101
|
161 |
#include <audioclient.h>
|
sezeroz@12101
|
162 |
#include <endpointvolume.h>
|
slouken@12088
|
163 |
|
slouken@12088
|
164 |
#undef DEFINE_GUID
|
slouken@12088
|
165 |
#define DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
|
slouken@12245
|
166 |
DEFINE_GUID(SDL_CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E);
|
slouken@12245
|
167 |
DEFINE_GUID(SDL_IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6);
|
slouken@12245
|
168 |
DEFINE_GUID(SDL_IID_IAudioEndpointVolume, 0x5CDF2C82, 0x841E, 0x4546, 0x97, 0x22, 0x0C, 0xF7, 0x40, 0x78, 0x22, 0x9A);
|
slouken@12088
|
169 |
#endif
|
slouken@12088
|
170 |
|
slouken@12088
|
171 |
|
slouken@12088
|
172 |
|
slouken@12088
|
173 |
static float GetSystemVolume(void)
|
slouken@12088
|
174 |
{
|
slouken@12088
|
175 |
float volume = -1.0f; /* Return this if we can't get system volume */
|
slouken@12088
|
176 |
|
sezeroz@12251
|
177 |
#if defined(__WIN32__) && defined(HAVE_ENDPOINTVOLUME_H)
|
slouken@12088
|
178 |
HRESULT hr = WIN_CoInitialize();
|
slouken@12088
|
179 |
if (SUCCEEDED(hr)) {
|
slouken@12088
|
180 |
IMMDeviceEnumerator *pEnumerator;
|
slouken@12088
|
181 |
|
slouken@12088
|
182 |
/* This should gracefully fail on XP and succeed on everything Vista and above */
|
slouken@12245
|
183 |
hr = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &SDL_IID_IMMDeviceEnumerator, (LPVOID*)&pEnumerator);
|
slouken@12088
|
184 |
if (SUCCEEDED(hr)) {
|
slouken@12088
|
185 |
IMMDevice *pDevice;
|
slouken@12088
|
186 |
|
slouken@12088
|
187 |
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eConsole, &pDevice);
|
slouken@12088
|
188 |
if (SUCCEEDED(hr)) {
|
slouken@12088
|
189 |
IAudioEndpointVolume *pEndpointVolume;
|
slouken@12088
|
190 |
|
slouken@12245
|
191 |
hr = IMMDevice_Activate(pDevice, &SDL_IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (LPVOID*)&pEndpointVolume);
|
slouken@12088
|
192 |
if (SUCCEEDED(hr)) {
|
slouken@12088
|
193 |
IAudioEndpointVolume_GetMasterVolumeLevelScalar(pEndpointVolume, &volume);
|
slouken@12088
|
194 |
IUnknown_Release(pEndpointVolume);
|
slouken@12088
|
195 |
}
|
slouken@12088
|
196 |
IUnknown_Release(pDevice);
|
slouken@12088
|
197 |
}
|
slouken@12088
|
198 |
IUnknown_Release(pEnumerator);
|
slouken@12088
|
199 |
}
|
slouken@12088
|
200 |
WIN_CoUninitialize();
|
slouken@12088
|
201 |
}
|
slouken@12088
|
202 |
#endif /* __WIN32__ */
|
slouken@12088
|
203 |
|
slouken@12088
|
204 |
return volume;
|
slouken@12088
|
205 |
}
|
slouken@12088
|
206 |
|
slouken@12088
|
207 |
static uint8_t GetPlaystationVolumeFromFloat(float fVolume)
|
slouken@12088
|
208 |
{
|
slouken@12088
|
209 |
const int k_nVolumeFitRatio = 15;
|
slouken@12088
|
210 |
const int k_nVolumeFitOffset = 9;
|
slouken@12088
|
211 |
float fVolLog;
|
slouken@12088
|
212 |
|
slouken@12088
|
213 |
if (fVolume > 1.0f || fVolume < 0.0f) {
|
slouken@12088
|
214 |
fVolume = 0.30f;
|
slouken@12088
|
215 |
}
|
slouken@12088
|
216 |
fVolLog = SDL_logf(fVolume * 100);
|
slouken@12088
|
217 |
|
slouken@12088
|
218 |
return (Uint8)((fVolLog * k_nVolumeFitRatio) + k_nVolumeFitOffset);
|
slouken@12088
|
219 |
}
|
slouken@12088
|
220 |
|
slouken@12088
|
221 |
static SDL_bool
|
slouken@12168
|
222 |
HIDAPI_DriverPS4_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
|
slouken@12088
|
223 |
{
|
slouken@12088
|
224 |
return SDL_IsJoystickPS4(vendor_id, product_id);
|
slouken@12088
|
225 |
}
|
slouken@12088
|
226 |
|
slouken@12088
|
227 |
static const char *
|
slouken@12088
|
228 |
HIDAPI_DriverPS4_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
|
slouken@12088
|
229 |
{
|
slouken@12088
|
230 |
if (vendor_id == SONY_USB_VID) {
|
slouken@12088
|
231 |
return "PS4 Controller";
|
slouken@12088
|
232 |
}
|
slouken@12088
|
233 |
return NULL;
|
slouken@12088
|
234 |
}
|
slouken@12088
|
235 |
|
slouken@12088
|
236 |
static SDL_bool ReadFeatureReport(hid_device *dev, Uint8 report_id, Uint8 *data, size_t size)
|
slouken@12088
|
237 |
{
|
slouken@12088
|
238 |
Uint8 report[USB_PACKET_LENGTH + 1];
|
slouken@12088
|
239 |
|
slouken@12088
|
240 |
SDL_memset(report, 0, sizeof(report));
|
slouken@12088
|
241 |
report[0] = report_id;
|
slouken@12088
|
242 |
if (hid_get_feature_report(dev, report, sizeof(report)) < 0) {
|
slouken@12088
|
243 |
return SDL_FALSE;
|
slouken@12088
|
244 |
}
|
slouken@12088
|
245 |
SDL_memcpy(data, report, SDL_min(size, sizeof(report)));
|
slouken@12088
|
246 |
return SDL_TRUE;
|
slouken@12088
|
247 |
}
|
slouken@12088
|
248 |
|
slouken@12088
|
249 |
static SDL_bool CheckUSBConnected(hid_device *dev)
|
slouken@12088
|
250 |
{
|
slouken@12088
|
251 |
int i;
|
slouken@12088
|
252 |
Uint8 data[16];
|
slouken@12088
|
253 |
|
slouken@12088
|
254 |
/* This will fail if we're on Bluetooth */
|
slouken@12088
|
255 |
if (ReadFeatureReport(dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data))) {
|
slouken@12088
|
256 |
for (i = 0; i < sizeof(data); ++i) {
|
slouken@12088
|
257 |
if (data[i] != 0x00) {
|
slouken@12088
|
258 |
return SDL_TRUE;
|
slouken@12088
|
259 |
}
|
slouken@12088
|
260 |
}
|
slouken@12088
|
261 |
/* Maybe the dongle without a connected controller? */
|
slouken@12088
|
262 |
}
|
slouken@12088
|
263 |
return SDL_FALSE;
|
slouken@12088
|
264 |
}
|
slouken@12088
|
265 |
|
slouken@12172
|
266 |
static SDL_bool HIDAPI_DriverPS4_CanRumble(Uint16 vendor_id, Uint16 product_id)
|
slouken@12172
|
267 |
{
|
slouken@12172
|
268 |
/* The Razer Panthera fight stick hangs when trying to rumble */
|
slouken@12176
|
269 |
if (vendor_id == RAZER_USB_VID &&
|
slouken@12176
|
270 |
(product_id == RAZER_PANTHERA_PID || product_id == RAZER_PANTHERA_EVO_PID)) {
|
slouken@12172
|
271 |
return SDL_FALSE;
|
slouken@12172
|
272 |
}
|
slouken@12172
|
273 |
return SDL_TRUE;
|
slouken@12172
|
274 |
}
|
slouken@12172
|
275 |
|
slouken@12088
|
276 |
static SDL_bool
|
flibitijibibo@12641
|
277 |
HIDAPI_DriverPS4_InitDriver(SDL_HIDAPI_DriverData *context, Uint16 vendor_id, Uint16 product_id, int *num_joysticks)
|
slouken@12088
|
278 |
{
|
slouken@12088
|
279 |
SDL_DriverPS4_Context *ctx;
|
slouken@12088
|
280 |
|
slouken@12088
|
281 |
ctx = (SDL_DriverPS4_Context *)SDL_calloc(1, sizeof(*ctx));
|
slouken@12088
|
282 |
if (!ctx) {
|
slouken@12088
|
283 |
SDL_OutOfMemory();
|
slouken@12088
|
284 |
return SDL_FALSE;
|
slouken@12088
|
285 |
}
|
flibitijibibo@12641
|
286 |
context->context = ctx;
|
slouken@12088
|
287 |
|
slouken@12088
|
288 |
/* Check for type of connection */
|
slouken@12088
|
289 |
ctx->is_dongle = (vendor_id == SONY_USB_VID && product_id == SONY_DS4_DONGLE_PID);
|
slouken@12088
|
290 |
if (ctx->is_dongle) {
|
slouken@12088
|
291 |
ctx->is_bluetooth = SDL_FALSE;
|
slouken@12088
|
292 |
} else if (vendor_id == SONY_USB_VID) {
|
flibitijibibo@12641
|
293 |
ctx->is_bluetooth = !CheckUSBConnected(context->device);
|
slouken@12088
|
294 |
} else {
|
slouken@12088
|
295 |
/* Third party controllers appear to all be wired */
|
slouken@12088
|
296 |
ctx->is_bluetooth = SDL_FALSE;
|
slouken@12088
|
297 |
}
|
slouken@12088
|
298 |
#ifdef DEBUG_PS4
|
slouken@12088
|
299 |
SDL_Log("PS4 dongle = %s, bluetooth = %s\n", ctx->is_dongle ? "TRUE" : "FALSE", ctx->is_bluetooth ? "TRUE" : "FALSE");
|
slouken@12088
|
300 |
#endif
|
slouken@12088
|
301 |
|
slouken@12088
|
302 |
/* Check to see if audio is supported */
|
slouken@12088
|
303 |
if (vendor_id == SONY_USB_VID &&
|
slouken@12088
|
304 |
(product_id == SONY_DS4_SLIM_PID || product_id == SONY_DS4_DONGLE_PID )) {
|
slouken@12088
|
305 |
ctx->audio_supported = SDL_TRUE;
|
slouken@12088
|
306 |
}
|
slouken@12088
|
307 |
|
slouken@12172
|
308 |
if (HIDAPI_DriverPS4_CanRumble(vendor_id, product_id)) {
|
slouken@12172
|
309 |
if (ctx->is_bluetooth) {
|
slouken@12172
|
310 |
ctx->rumble_supported = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, SDL_FALSE);
|
slouken@12172
|
311 |
} else {
|
slouken@12172
|
312 |
ctx->rumble_supported = SDL_TRUE;
|
slouken@12172
|
313 |
}
|
slouken@12163
|
314 |
}
|
slouken@12163
|
315 |
|
flibitijibibo@12641
|
316 |
ctx->joystickID = SDL_GetNextJoystickInstanceID();
|
flibitijibibo@12641
|
317 |
*num_joysticks += 1;
|
flibitijibibo@12641
|
318 |
SDL_PrivateJoystickAdded(ctx->joystickID);
|
flibitijibibo@12641
|
319 |
|
flibitijibibo@12641
|
320 |
return SDL_TRUE;
|
flibitijibibo@12641
|
321 |
}
|
flibitijibibo@12641
|
322 |
|
flibitijibibo@12641
|
323 |
static void
|
flibitijibibo@12641
|
324 |
HIDAPI_DriverPS4_QuitDriver(SDL_HIDAPI_DriverData *context, SDL_bool send_event, int *num_joysticks)
|
flibitijibibo@12641
|
325 |
{
|
flibitijibibo@12641
|
326 |
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context->context;
|
flibitijibibo@12641
|
327 |
|
flibitijibibo@12641
|
328 |
*num_joysticks -= 1;
|
flibitijibibo@12641
|
329 |
if (send_event) {
|
flibitijibibo@12641
|
330 |
SDL_PrivateJoystickRemoved(ctx->joystickID);
|
flibitijibibo@12641
|
331 |
}
|
flibitijibibo@12641
|
332 |
SDL_free(context->context);
|
flibitijibibo@12641
|
333 |
}
|
flibitijibibo@12641
|
334 |
|
flibitijibibo@12641
|
335 |
static int
|
flibitijibibo@12641
|
336 |
HIDAPI_DriverPS4_NumJoysticks(SDL_HIDAPI_DriverData *context)
|
flibitijibibo@12641
|
337 |
{
|
flibitijibibo@12641
|
338 |
return 1;
|
flibitijibibo@12641
|
339 |
}
|
flibitijibibo@12641
|
340 |
|
zack@12787
|
341 |
static int
|
zack@12787
|
342 |
HIDAPI_DriverPS4_PlayerIndexForIndex(SDL_HIDAPI_DriverData *context, int index)
|
zack@12787
|
343 |
{
|
zack@12787
|
344 |
return -1;
|
zack@12787
|
345 |
}
|
zack@12787
|
346 |
|
flibitijibibo@12641
|
347 |
static SDL_JoystickID
|
flibitijibibo@12641
|
348 |
HIDAPI_DriverPS4_InstanceIDForIndex(SDL_HIDAPI_DriverData *context, int index)
|
flibitijibibo@12641
|
349 |
{
|
flibitijibibo@12641
|
350 |
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context->context;
|
flibitijibibo@12641
|
351 |
return ctx->joystickID;
|
flibitijibibo@12641
|
352 |
}
|
flibitijibibo@12641
|
353 |
|
flibitijibibo@12641
|
354 |
static int HIDAPI_DriverPS4_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
|
flibitijibibo@12641
|
355 |
|
flibitijibibo@12641
|
356 |
static SDL_bool
|
flibitijibibo@12641
|
357 |
HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick)
|
flibitijibibo@12641
|
358 |
{
|
slouken@12088
|
359 |
/* Initialize LED and effect state */
|
flibitijibibo@12641
|
360 |
HIDAPI_DriverPS4_Rumble(context, joystick, 0, 0, 0);
|
slouken@12088
|
361 |
|
slouken@12088
|
362 |
/* Initialize the joystick capabilities */
|
slouken@12790
|
363 |
joystick->nbuttons = 16;
|
slouken@12088
|
364 |
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
|
slouken@12088
|
365 |
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
|
slouken@12088
|
366 |
|
slouken@12088
|
367 |
return SDL_TRUE;
|
slouken@12088
|
368 |
}
|
slouken@12088
|
369 |
|
slouken@12088
|
370 |
static int
|
flibitijibibo@12641
|
371 |
HIDAPI_DriverPS4_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
|
slouken@12088
|
372 |
{
|
flibitijibibo@12641
|
373 |
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context->context;
|
slouken@12088
|
374 |
DS4EffectsState_t *effects;
|
slouken@12088
|
375 |
Uint8 data[78];
|
slouken@12088
|
376 |
int report_size, offset;
|
slouken@12088
|
377 |
|
slouken@12163
|
378 |
if (!ctx->rumble_supported) {
|
slouken@12163
|
379 |
return SDL_Unsupported();
|
slouken@12163
|
380 |
}
|
slouken@12163
|
381 |
|
slouken@12088
|
382 |
/* In order to send rumble, we have to send a complete effect packet */
|
slouken@12088
|
383 |
SDL_memset(data, 0, sizeof(data));
|
slouken@12088
|
384 |
|
slouken@12088
|
385 |
if (ctx->is_bluetooth) {
|
slouken@12088
|
386 |
data[0] = k_EPS4ReportIdBluetoothEffects;
|
slouken@12088
|
387 |
data[1] = 0xC0 | 0x04; /* Magic value HID + CRC, also sets interval to 4ms for samples */
|
slouken@12088
|
388 |
data[3] = 0x03; /* 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval */
|
slouken@12088
|
389 |
|
slouken@12088
|
390 |
report_size = 78;
|
slouken@12088
|
391 |
offset = 6;
|
slouken@12088
|
392 |
} else {
|
slouken@12088
|
393 |
data[0] = k_EPS4ReportIdUsbEffects;
|
slouken@12088
|
394 |
data[1] = 0x07; /* Magic value */
|
slouken@12088
|
395 |
|
slouken@12088
|
396 |
report_size = 32;
|
slouken@12088
|
397 |
offset = 4;
|
slouken@12088
|
398 |
}
|
slouken@12088
|
399 |
effects = (DS4EffectsState_t *)&data[offset];
|
slouken@12088
|
400 |
|
slouken@12088
|
401 |
effects->ucRumbleLeft = (low_frequency_rumble >> 8);
|
slouken@12088
|
402 |
effects->ucRumbleRight = (high_frequency_rumble >> 8);
|
slouken@12088
|
403 |
|
slouken@12088
|
404 |
effects->ucLedRed = 0;
|
slouken@12088
|
405 |
effects->ucLedGreen = 0;
|
slouken@12088
|
406 |
effects->ucLedBlue = 80;
|
slouken@12088
|
407 |
|
slouken@12088
|
408 |
if (ctx->audio_supported) {
|
slouken@12088
|
409 |
Uint32 now = SDL_GetTicks();
|
slouken@12088
|
410 |
if (!ctx->last_volume_check ||
|
slouken@12088
|
411 |
SDL_TICKS_PASSED(now, ctx->last_volume_check + VOLUME_CHECK_INTERVAL_MS)) {
|
slouken@12088
|
412 |
ctx->volume = GetPlaystationVolumeFromFloat(GetSystemVolume());
|
slouken@12088
|
413 |
ctx->last_volume_check = now;
|
slouken@12088
|
414 |
}
|
slouken@12088
|
415 |
|
slouken@12088
|
416 |
effects->ucVolumeRight = ctx->volume;
|
slouken@12088
|
417 |
effects->ucVolumeLeft = ctx->volume;
|
slouken@12088
|
418 |
effects->ucVolumeSpeaker = ctx->volume;
|
slouken@12088
|
419 |
effects->ucVolumeMic = 0xFF;
|
slouken@12088
|
420 |
}
|
slouken@12088
|
421 |
|
slouken@12088
|
422 |
if (ctx->is_bluetooth) {
|
slouken@12088
|
423 |
/* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */
|
slouken@12088
|
424 |
Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */
|
slouken@12088
|
425 |
Uint32 unCRC;
|
slouken@12088
|
426 |
unCRC = crc32(0, &ubHdr, 1);
|
slouken@12088
|
427 |
unCRC = crc32(unCRC, data, (Uint32)(report_size - sizeof(unCRC)));
|
slouken@12088
|
428 |
SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
|
slouken@12088
|
429 |
}
|
slouken@12088
|
430 |
|
flibitijibibo@12641
|
431 |
if (hid_write(context->device, data, report_size) != report_size) {
|
slouken@12088
|
432 |
return SDL_SetError("Couldn't send rumble packet");
|
slouken@12088
|
433 |
}
|
slouken@12088
|
434 |
|
slouken@12088
|
435 |
if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
|
slouken@12088
|
436 |
ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
|
slouken@12088
|
437 |
} else {
|
slouken@12088
|
438 |
ctx->rumble_expiration = 0;
|
slouken@12088
|
439 |
}
|
slouken@12088
|
440 |
return 0;
|
slouken@12088
|
441 |
}
|
slouken@12088
|
442 |
|
slouken@12088
|
443 |
static void
|
slouken@12088
|
444 |
HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet)
|
slouken@12088
|
445 |
{
|
slouken@12088
|
446 |
Sint16 axis;
|
slouken@12088
|
447 |
|
slouken@12088
|
448 |
if (ctx->last_state.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) {
|
slouken@12088
|
449 |
{
|
slouken@12088
|
450 |
Uint8 data = (packet->rgucButtonsHatAndCounter[0] >> 4);
|
slouken@12088
|
451 |
|
slouken@12088
|
452 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
slouken@12088
|
453 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
slouken@12088
|
454 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
slouken@12088
|
455 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
slouken@12088
|
456 |
}
|
slouken@12088
|
457 |
{
|
slouken@12088
|
458 |
Uint8 data = (packet->rgucButtonsHatAndCounter[0] & 0x0F);
|
slouken@12088
|
459 |
SDL_bool dpad_up = SDL_FALSE;
|
slouken@12088
|
460 |
SDL_bool dpad_down = SDL_FALSE;
|
slouken@12088
|
461 |
SDL_bool dpad_left = SDL_FALSE;
|
slouken@12088
|
462 |
SDL_bool dpad_right = SDL_FALSE;
|
slouken@12088
|
463 |
|
slouken@12088
|
464 |
switch (data) {
|
slouken@12088
|
465 |
case 0:
|
slouken@12088
|
466 |
dpad_up = SDL_TRUE;
|
slouken@12088
|
467 |
break;
|
slouken@12088
|
468 |
case 1:
|
slouken@12088
|
469 |
dpad_up = SDL_TRUE;
|
slouken@12088
|
470 |
dpad_right = SDL_TRUE;
|
slouken@12088
|
471 |
break;
|
slouken@12088
|
472 |
case 2:
|
slouken@12088
|
473 |
dpad_right = SDL_TRUE;
|
slouken@12088
|
474 |
break;
|
slouken@12088
|
475 |
case 3:
|
slouken@12088
|
476 |
dpad_right = SDL_TRUE;
|
slouken@12088
|
477 |
dpad_down = SDL_TRUE;
|
slouken@12088
|
478 |
break;
|
slouken@12088
|
479 |
case 4:
|
slouken@12088
|
480 |
dpad_down = SDL_TRUE;
|
slouken@12088
|
481 |
break;
|
slouken@12088
|
482 |
case 5:
|
slouken@12088
|
483 |
dpad_left = SDL_TRUE;
|
slouken@12088
|
484 |
dpad_down = SDL_TRUE;
|
slouken@12088
|
485 |
break;
|
slouken@12088
|
486 |
case 6:
|
slouken@12088
|
487 |
dpad_left = SDL_TRUE;
|
slouken@12088
|
488 |
break;
|
slouken@12088
|
489 |
case 7:
|
slouken@12088
|
490 |
dpad_up = SDL_TRUE;
|
slouken@12088
|
491 |
dpad_left = SDL_TRUE;
|
slouken@12088
|
492 |
break;
|
slouken@12088
|
493 |
default:
|
slouken@12088
|
494 |
break;
|
slouken@12088
|
495 |
}
|
slouken@12088
|
496 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
|
slouken@12088
|
497 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
|
slouken@12088
|
498 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
|
slouken@12088
|
499 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
|
slouken@12088
|
500 |
}
|
slouken@12088
|
501 |
}
|
slouken@12088
|
502 |
|
slouken@12088
|
503 |
if (ctx->last_state.rgucButtonsHatAndCounter[1] != packet->rgucButtonsHatAndCounter[1]) {
|
slouken@12088
|
504 |
Uint8 data = packet->rgucButtonsHatAndCounter[1];
|
slouken@12088
|
505 |
|
slouken@12088
|
506 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
slouken@12088
|
507 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
slouken@12088
|
508 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
|
slouken@12088
|
509 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
|
slouken@12088
|
510 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
|
slouken@12088
|
511 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
|
slouken@12088
|
512 |
}
|
slouken@12088
|
513 |
|
slouken@12088
|
514 |
if (ctx->last_state.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) {
|
slouken@12088
|
515 |
Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03);
|
slouken@12088
|
516 |
|
slouken@12088
|
517 |
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
slouken@12790
|
518 |
SDL_PrivateJoystickButton(joystick, 15, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
slouken@12088
|
519 |
}
|
slouken@12088
|
520 |
|
slouken@12088
|
521 |
axis = ((int)packet->ucTriggerLeft * 257) - 32768;
|
slouken@12088
|
522 |
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
|
slouken@12088
|
523 |
axis = ((int)packet->ucTriggerRight * 257) - 32768;
|
slouken@12088
|
524 |
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
|
slouken@12088
|
525 |
axis = ((int)packet->ucLeftJoystickX * 257) - 32768;
|
slouken@12088
|
526 |
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
|
slouken@12088
|
527 |
axis = ((int)packet->ucLeftJoystickY * 257) - 32768;
|
slouken@12088
|
528 |
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
|
slouken@12088
|
529 |
axis = ((int)packet->ucRightJoystickX * 257) - 32768;
|
slouken@12088
|
530 |
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
|
slouken@12088
|
531 |
axis = ((int)packet->ucRightJoystickY * 257) - 32768;
|
slouken@12088
|
532 |
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
|
slouken@12088
|
533 |
|
slouken@12088
|
534 |
if (packet->ucBatteryLevel & 0x10) {
|
slouken@12088
|
535 |
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
|
slouken@12088
|
536 |
} else {
|
slouken@12088
|
537 |
/* Battery level ranges from 0 to 10 */
|
slouken@12088
|
538 |
int level = (packet->ucBatteryLevel & 0xF);
|
slouken@12088
|
539 |
if (level == 0) {
|
slouken@12088
|
540 |
joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
|
slouken@12088
|
541 |
} else if (level <= 2) {
|
slouken@12088
|
542 |
joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
|
slouken@12088
|
543 |
} else if (level <= 7) {
|
slouken@12088
|
544 |
joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
|
slouken@12088
|
545 |
} else {
|
slouken@12088
|
546 |
joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
|
slouken@12088
|
547 |
}
|
slouken@12088
|
548 |
}
|
slouken@12088
|
549 |
|
slouken@12088
|
550 |
SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
|
slouken@12088
|
551 |
}
|
slouken@12088
|
552 |
|
slouken@12111
|
553 |
static SDL_bool
|
flibitijibibo@12641
|
554 |
HIDAPI_DriverPS4_UpdateDriver(SDL_HIDAPI_DriverData *context, int *num_joysticks)
|
slouken@12088
|
555 |
{
|
flibitijibibo@12641
|
556 |
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context->context;
|
flibitijibibo@12641
|
557 |
SDL_Joystick *joystick = SDL_JoystickFromInstanceID(ctx->joystickID);
|
slouken@12088
|
558 |
Uint8 data[USB_PACKET_LENGTH];
|
slouken@12088
|
559 |
int size;
|
slouken@12088
|
560 |
|
flibitijibibo@12641
|
561 |
if (joystick == NULL) {
|
flibitijibibo@12641
|
562 |
return SDL_TRUE; /* Nothing to do right now! */
|
flibitijibibo@12641
|
563 |
}
|
flibitijibibo@12641
|
564 |
|
flibitijibibo@12641
|
565 |
while ((size = hid_read_timeout(context->device, data, sizeof(data), 0)) > 0) {
|
slouken@12088
|
566 |
switch (data[0]) {
|
slouken@12088
|
567 |
case k_EPS4ReportIdUsbState:
|
flibitijibibo@12641
|
568 |
HIDAPI_DriverPS4_HandleStatePacket(joystick, context->device, ctx, (PS4StatePacket_t *)&data[1]);
|
slouken@12088
|
569 |
break;
|
slouken@12088
|
570 |
case k_EPS4ReportIdBluetoothState:
|
slouken@12088
|
571 |
/* Bluetooth state packets have two additional bytes at the beginning */
|
flibitijibibo@12641
|
572 |
HIDAPI_DriverPS4_HandleStatePacket(joystick, context->device, ctx, (PS4StatePacket_t *)&data[3]);
|
slouken@12088
|
573 |
break;
|
slouken@12088
|
574 |
default:
|
slouken@12088
|
575 |
#ifdef DEBUG_JOYSTICK
|
slouken@12088
|
576 |
SDL_Log("Unknown PS4 packet: 0x%.2x\n", data[0]);
|
slouken@12088
|
577 |
#endif
|
slouken@12088
|
578 |
break;
|
slouken@12088
|
579 |
}
|
slouken@12088
|
580 |
}
|
slouken@12088
|
581 |
|
slouken@12088
|
582 |
if (ctx->rumble_expiration) {
|
slouken@12088
|
583 |
Uint32 now = SDL_GetTicks();
|
slouken@12088
|
584 |
if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
|
flibitijibibo@12641
|
585 |
HIDAPI_DriverPS4_Rumble(context, joystick, 0, 0, 0);
|
slouken@12088
|
586 |
}
|
slouken@12088
|
587 |
}
|
slouken@12111
|
588 |
|
slouken@12163
|
589 |
return (size >= 0);
|
slouken@12088
|
590 |
}
|
slouken@12088
|
591 |
|
slouken@12088
|
592 |
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 =
|
slouken@12088
|
593 |
{
|
slouken@12088
|
594 |
SDL_HINT_JOYSTICK_HIDAPI_PS4,
|
slouken@12088
|
595 |
SDL_TRUE,
|
slouken@12088
|
596 |
HIDAPI_DriverPS4_IsSupportedDevice,
|
slouken@12088
|
597 |
HIDAPI_DriverPS4_GetDeviceName,
|
flibitijibibo@12641
|
598 |
HIDAPI_DriverPS4_InitDriver,
|
flibitijibibo@12641
|
599 |
HIDAPI_DriverPS4_QuitDriver,
|
flibitijibibo@12641
|
600 |
HIDAPI_DriverPS4_UpdateDriver,
|
flibitijibibo@12641
|
601 |
HIDAPI_DriverPS4_NumJoysticks,
|
zack@12787
|
602 |
HIDAPI_DriverPS4_PlayerIndexForIndex,
|
flibitijibibo@12641
|
603 |
HIDAPI_DriverPS4_InstanceIDForIndex,
|
flibitijibibo@12641
|
604 |
HIDAPI_DriverPS4_OpenJoystick,
|
flibitijibibo@12641
|
605 |
HIDAPI_DriverPS4_Rumble
|
slouken@12088
|
606 |
};
|
slouken@12088
|
607 |
|
slouken@12088
|
608 |
#endif /* SDL_JOYSTICK_HIDAPI_PS4 */
|
slouken@12088
|
609 |
|
slouken@12088
|
610 |
#endif /* SDL_JOYSTICK_HIDAPI */
|
slouken@12088
|
611 |
|
slouken@12088
|
612 |
/* vi: set ts=4 sw=4 expandtab: */
|