Skip to content

Commit 184f6fa

Browse files
endriftslouken
authored andcommitted
switch2: Read calibration data
1 parent d15e531 commit 184f6fa

File tree

1 file changed

+228
-73
lines changed

1 file changed

+228
-73
lines changed

src/joystick/hidapi/SDL_hidapi_switch2.c

Lines changed: 228 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,128 @@
3232

3333
#ifdef SDL_JOYSTICK_HIDAPI_SWITCH2
3434

35+
typedef struct
36+
{
37+
Uint16 neutral;
38+
Uint16 max;
39+
Uint16 min;
40+
} Switch2_AxisCalibration;
41+
42+
typedef struct
43+
{
44+
Switch2_AxisCalibration x;
45+
Switch2_AxisCalibration y;
46+
} Switch2_StickCalibration;
47+
3548
typedef struct
3649
{
3750
SDL_LibUSBContext *libusb;
3851
libusb_device_handle *device_handle;
3952
bool interface_claimed;
4053
Uint8 interface_number;
41-
Uint8 bulk_endpoint;
54+
Uint8 out_endpoint;
55+
Uint8 in_endpoint;
56+
57+
Switch2_StickCalibration left_stick;
58+
Switch2_StickCalibration right_stick;
59+
Uint8 left_trigger_max;
60+
Uint8 right_trigger_max;
4261
} SDL_DriverSwitch2_Context;
4362

63+
static void ParseStickCalibration(Switch2_StickCalibration *stick_data, const Uint8 *data)
64+
{
65+
stick_data->x.neutral = data[0];
66+
stick_data->x.neutral |= (data[1] & 0x0F) << 8;
67+
68+
stick_data->y.neutral = data[1] >> 4;
69+
stick_data->y.neutral |= data[2] << 4;
70+
71+
stick_data->x.max = data[3];
72+
stick_data->x.max |= (data[4] & 0x0F) << 8;
73+
74+
stick_data->y.max = data[4] >> 4;
75+
stick_data->y.max |= data[5] << 4;
76+
77+
stick_data->x.min = data[6];
78+
stick_data->x.min |= (data[7] & 0x0F) << 8;
79+
80+
stick_data->y.min = data[7] >> 4;
81+
stick_data->y.min |= data[8] << 4;
82+
}
83+
84+
static int SendBulkData(SDL_DriverSwitch2_Context *ctx, const Uint8 *data, unsigned size)
85+
{
86+
int transferred;
87+
int res = ctx->libusb->bulk_transfer(ctx->device_handle,
88+
ctx->out_endpoint,
89+
(Uint8 *)data,
90+
size,
91+
&transferred,
92+
1000);
93+
if (res < 0) {
94+
return res;
95+
}
96+
return transferred;
97+
}
98+
99+
static int RecvBulkData(SDL_DriverSwitch2_Context *ctx, Uint8 *data, unsigned size)
100+
{
101+
int transferred;
102+
int total_transferred = 0;
103+
int res;
104+
105+
while (size > 0) {
106+
unsigned current_read = size;
107+
if (current_read > 64) {
108+
current_read = 64;
109+
}
110+
res = ctx->libusb->bulk_transfer(ctx->device_handle,
111+
ctx->in_endpoint,
112+
data,
113+
current_read,
114+
&transferred,
115+
100);
116+
if (res < 0) {
117+
return res;
118+
}
119+
total_transferred += transferred;
120+
size -= transferred;
121+
data += current_read;
122+
if ((unsigned) transferred < current_read) {
123+
break;
124+
}
125+
}
126+
127+
return total_transferred;
128+
}
129+
130+
static void MapJoystickAxis(Uint64 timestamp, SDL_Joystick *joystick, int axis, const Switch2_AxisCalibration *calib, float value)
131+
{
132+
Sint16 mapped_value;
133+
if (calib && calib->neutral && calib->min && calib->max) {
134+
value -= calib->neutral;
135+
if (value < 0) {
136+
value /= calib->min;
137+
} else {
138+
value /= calib->max;
139+
}
140+
mapped_value = (Sint16) SDL_clamp(value * SDL_MAX_SINT16, SDL_MIN_SINT16, SDL_MAX_SINT16);
141+
} else {
142+
mapped_value = (Sint16) HIDAPI_RemapVal(value, 0, 4096, SDL_MIN_SINT16, SDL_MAX_SINT16);
143+
}
144+
SDL_SendJoystickAxis(timestamp, joystick, axis, mapped_value);
145+
}
146+
147+
static void MapTriggerAxis(Uint64 timestamp, SDL_Joystick *joystick, int axis, Uint8 max, float value)
148+
{
149+
Sint16 mapped_value = (Sint16) HIDAPI_RemapVal(
150+
SDL_clamp((value - max) / (232.f - max), 0, 1),
151+
0, 1,
152+
SDL_MIN_SINT16, SDL_MAX_SINT16
153+
);
154+
SDL_SendJoystickAxis(timestamp, joystick, axis, mapped_value);
155+
}
156+
44157
static void HIDAPI_DriverSwitch2_RegisterHints(SDL_HintCallback callback, void *userdata)
45158
{
46159
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH2, callback, userdata);
@@ -75,9 +188,11 @@ static bool HIDAPI_DriverSwitch2_InitBluetooth(SDL_HIDAPI_Device *device)
75188
return SDL_SetError("Nintendo Switch2 controllers not supported over Bluetooth");
76189
}
77190

78-
static bool FindBulkOutEndpoint(SDL_LibUSBContext *libusb, libusb_device_handle *handle, Uint8 *bInterfaceNumber, Uint8 *bEndpointAddress)
191+
static bool FindBulkEndpoints(SDL_LibUSBContext *libusb, libusb_device_handle *handle, Uint8 *bInterfaceNumber, Uint8 *out_endpoint, Uint8 *in_endpoint)
79192
{
80193
struct libusb_config_descriptor *config;
194+
int found = 0;
195+
81196
if (libusb->get_config_descriptor(libusb->get_device(handle), 0, &config) != 0) {
82197
return false;
83198
}
@@ -89,12 +204,20 @@ static bool FindBulkOutEndpoint(SDL_LibUSBContext *libusb, libusb_device_handle
89204
if (altsetting->bInterfaceNumber == 1) {
90205
for (int k = 0; k < altsetting->bNumEndpoints; k++) {
91206
const struct libusb_endpoint_descriptor *ep = &altsetting->endpoint[k];
92-
if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK && (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) {
93-
207+
if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK) {
94208
*bInterfaceNumber = altsetting->bInterfaceNumber;
95-
*bEndpointAddress = ep->bEndpointAddress;
96-
libusb->free_config_descriptor(config);
97-
return true;
209+
if ((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) {
210+
*out_endpoint = ep->bEndpointAddress;
211+
found |= 1;
212+
}
213+
if ((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) {
214+
*in_endpoint = ep->bEndpointAddress;
215+
found |= 2;
216+
}
217+
if (found == 3) {
218+
libusb->free_config_descriptor(config);
219+
return true;
220+
}
98221
}
99222
}
100223
}
@@ -117,8 +240,8 @@ static bool HIDAPI_DriverSwitch2_InitUSB(SDL_HIDAPI_Device *device)
117240
return SDL_SetError("Couldn't get libusb device handle");
118241
}
119242

120-
if (!FindBulkOutEndpoint(ctx->libusb, ctx->device_handle, &ctx->interface_number, &ctx->bulk_endpoint)) {
121-
return SDL_SetError("Couldn't find bulk endpoint");
243+
if (!FindBulkEndpoints(ctx->libusb, ctx->device_handle, &ctx->interface_number, &ctx->out_endpoint, &ctx->in_endpoint)) {
244+
return SDL_SetError("Couldn't find bulk endpoints");
122245
}
123246

124247
int res = ctx->libusb->claim_interface(ctx->device_handle, ctx->interface_number);
@@ -127,35 +250,75 @@ static bool HIDAPI_DriverSwitch2_InitUSB(SDL_HIDAPI_Device *device)
127250
}
128251
ctx->interface_claimed = true;
129252

130-
const unsigned char DEFAULT_REPORT_DATA[] = {
131-
0x03, 0x91, 0x00, 0x0d, 0x00, 0x08,
132-
0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
253+
const unsigned char INIT_DATA[] = {
254+
0x03, 0x91, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00,
255+
0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
133256
};
134257
const unsigned char SET_LED_DATA[] = {
135-
0x09, 0x91, 0x00, 0x07, 0x00, 0x08,
136-
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
258+
0x09, 0x91, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00,
259+
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
260+
};
261+
unsigned char flash_read_command[] = {
262+
0x02, 0x91, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00,
263+
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00
137264
};
265+
unsigned char calibration_data[0x50] = {0};
138266

139-
int transferred;
140-
res = ctx->libusb->bulk_transfer(ctx->device_handle,
141-
ctx->bulk_endpoint,
142-
(unsigned char *)DEFAULT_REPORT_DATA,
143-
sizeof(DEFAULT_REPORT_DATA),
144-
&transferred,
145-
1000);
267+
res = SendBulkData(ctx, INIT_DATA, sizeof(INIT_DATA));
146268
if (res < 0) {
147-
return SDL_SetError("Couldn't set report data: %d\n", res);
269+
return SDL_SetError("Couldn't send initialization data: %d\n", res);
148270
}
271+
RecvBulkData(ctx, calibration_data, 0x40);
149272

150-
res = ctx->libusb->bulk_transfer(ctx->device_handle,
151-
ctx->bulk_endpoint,
152-
(unsigned char *)SET_LED_DATA,
153-
sizeof(SET_LED_DATA),
154-
&transferred,
155-
1000);
273+
res = SendBulkData(ctx, SET_LED_DATA, sizeof(SET_LED_DATA));
156274
if (res < 0) {
157275
return SDL_SetError("Couldn't set LED data: %d\n", res);
158276
}
277+
RecvBulkData(ctx, calibration_data, 0x40);
278+
279+
flash_read_command[12] = 0x80;
280+
res = SendBulkData(ctx, flash_read_command, sizeof(flash_read_command));
281+
if (res < 0) {
282+
SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res);
283+
} else {
284+
res = RecvBulkData(ctx, calibration_data, sizeof(calibration_data));
285+
if (res < 0) {
286+
SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res);
287+
} else {
288+
ParseStickCalibration(&ctx->left_stick, &calibration_data[0x38]);
289+
}
290+
}
291+
292+
293+
flash_read_command[12] = 0xC0;
294+
res = SendBulkData(ctx, flash_read_command, sizeof(flash_read_command));
295+
if (res < 0) {
296+
SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res);
297+
} else {
298+
res = RecvBulkData(ctx, calibration_data, sizeof(calibration_data));
299+
if (res < 0) {
300+
SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res);
301+
} else {
302+
ParseStickCalibration(&ctx->right_stick, &calibration_data[0x38]);
303+
}
304+
}
305+
306+
if (device->product_id == USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER) {
307+
flash_read_command[12] = 0x40;
308+
flash_read_command[13] = 0x31;
309+
res = SendBulkData(ctx, flash_read_command, sizeof(flash_read_command));
310+
if (res < 0) {
311+
SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res);
312+
} else {
313+
res = RecvBulkData(ctx, calibration_data, sizeof(calibration_data));
314+
if (res < 0) {
315+
SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res);
316+
} else {
317+
ctx->left_trigger_max = calibration_data[0x10];
318+
ctx->right_trigger_max = calibration_data[0x11];
319+
}
320+
}
321+
}
159322

160323
return true;
161324
}
@@ -205,6 +368,8 @@ static void HIDAPI_DriverSwitch2_SetDevicePlayerIndex(SDL_HIDAPI_Device *device,
205368

206369
static bool HIDAPI_DriverSwitch2_UpdateDevice(SDL_HIDAPI_Device *device)
207370
{
371+
SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context;
372+
208373
const struct {
209374
int byte;
210375
unsigned char mask;
@@ -258,66 +423,52 @@ static bool HIDAPI_DriverSwitch2_UpdateDevice(SDL_HIDAPI_Device *device)
258423
(packet[buttons[i].byte] & buttons[i].mask) != 0
259424
);
260425
}
261-
SDL_SendJoystickAxis(
426+
427+
MapJoystickAxis(
262428
timestamp,
263429
joystick,
264430
SDL_GAMEPAD_AXIS_LEFTX,
265-
(Sint16) HIDAPI_RemapVal(
266-
(float) (packet[6] | ((packet[7] & 0x0F) << 8)),
267-
0,
268-
4096,
269-
SDL_MIN_SINT16,
270-
SDL_MAX_SINT16
271-
)
431+
&ctx->left_stick.x,
432+
(float) (packet[6] | ((packet[7] & 0x0F) << 8))
272433
);
273-
SDL_SendJoystickAxis(
434+
MapJoystickAxis(
274435
timestamp,
275436
joystick,
276437
SDL_GAMEPAD_AXIS_LEFTY,
277-
(Sint16) HIDAPI_RemapVal(
278-
(float) ((packet[7] >> 4) | (packet[8] << 4)),
279-
0,
280-
4096,
281-
SDL_MIN_SINT16,
282-
SDL_MAX_SINT16
283-
)
438+
&ctx->left_stick.y,
439+
(float) ((packet[7] >> 4) | (packet[8] << 4))
284440
);
285-
SDL_SendJoystickAxis(
441+
MapJoystickAxis(
286442
timestamp,
287443
joystick,
288444
SDL_GAMEPAD_AXIS_RIGHTX,
289-
(Sint16) HIDAPI_RemapVal(
290-
(float) (packet[9] | ((packet[10] & 0x0F) << 8)),
291-
0,
292-
4096,
293-
SDL_MIN_SINT16,
294-
SDL_MAX_SINT16
295-
)
445+
&ctx->right_stick.x,
446+
(float) (packet[9] | ((packet[10] & 0x0F) << 8))
296447
);
297-
SDL_SendJoystickAxis(
448+
MapJoystickAxis(
298449
timestamp,
299450
joystick,
300451
SDL_GAMEPAD_AXIS_RIGHTY,
301-
(Sint16) HIDAPI_RemapVal(
302-
(float) ((packet[10] >> 4) | (packet[11] << 4)),
303-
0,
304-
4096,
305-
SDL_MIN_SINT16,
306-
SDL_MAX_SINT16
307-
)
308-
);
309-
SDL_SendJoystickAxis(
310-
timestamp,
311-
joystick,
312-
SDL_GAMEPAD_AXIS_LEFT_TRIGGER,
313-
(Sint16) HIDAPI_RemapVal(packet[13], 0, 255, SDL_MIN_SINT16, SDL_MAX_SINT16)
314-
);
315-
SDL_SendJoystickAxis(
316-
timestamp,
317-
joystick,
318-
SDL_GAMEPAD_AXIS_RIGHT_TRIGGER,
319-
(Sint16) HIDAPI_RemapVal(packet[14], 0, 255, SDL_MIN_SINT16, SDL_MAX_SINT16)
452+
&ctx->right_stick.y,
453+
(float) ((packet[10] >> 4) | (packet[11] << 4))
320454
);
455+
456+
if (device->product_id == USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER) {
457+
MapTriggerAxis(
458+
timestamp,
459+
joystick,
460+
SDL_GAMEPAD_AXIS_LEFT_TRIGGER,
461+
ctx->left_trigger_max,
462+
packet[13]
463+
);
464+
MapTriggerAxis(
465+
timestamp,
466+
joystick,
467+
SDL_GAMEPAD_AXIS_RIGHT_TRIGGER,
468+
ctx->right_trigger_max,
469+
packet[14]
470+
);
471+
}
321472
}
322473
return true;
323474
}
@@ -326,7 +477,11 @@ static bool HIDAPI_DriverSwitch2_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy
326477
{
327478
// Initialize the joystick capabilities
328479
joystick->nbuttons = 21;
329-
joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
480+
if (device->product_id == USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER) {
481+
joystick->naxes = 6;
482+
} else {
483+
joystick->naxes = 4;
484+
}
330485

331486
return true;
332487
}

0 commit comments

Comments
 (0)