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+
3548typedef 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+
44157static 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
206369static 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