6161#define SENSOR_INTERVAL_APEX5_WIRED_RATE_HZ 970
6262#define SENSOR_INTERVAL_APEX5_WIRED_NS (SDL_NS_PER_SECOND / SENSOR_INTERVAL_APEX5_WIRED_RATE_HZ)
6363
64- #define FLYDIGI_ACQUIRE_CONTROLLER_HEARTBEAT_TIME 1000 * 60
65-
66- #define FLYDIGI_V1_CMD_REPORT_ID 0x05
67- #define FLYDIGI_V1_HAPTIC_COMMAND 0x0F
68- #define FLYDIGI_V1_GET_INFO_COMMAND 0xEC
69-
70- #define FLYDIGI_V2_CMD_REPORT_ID 0x03
71- #define FLYDIGI_V2_MAGIC1 0x5A
72- #define FLYDIGI_V2_MAGIC2 0xA5
73- #define FLYDIGI_V2_GET_INFO_COMMAND 0x01
74- #define FLYDIGI_V2_HAPTIC_COMMAND 0x12
75- #define FLYDIGI_V2_ACQUIRE_CONTROLLER_COMMAND 0x1c
64+ #define FLYDIGI_ACQUIRE_CONTROLLER_HEARTBEAT_TIME 1000 * 30
65+
66+ #define FLYDIGI_V1_CMD_REPORT_ID 0x05
67+ #define FLYDIGI_V1_HAPTIC_COMMAND 0x0F
68+ #define FLYDIGI_V1_GET_INFO_COMMAND 0xEC
69+
70+ #define FLYDIGI_V2_CMD_REPORT_ID 0x03
71+ #define FLYDIGI_V2_MAGIC1 0x5A
72+ #define FLYDIGI_V2_MAGIC2 0xA5
73+ #define FLYDIGI_V2_GET_INFO_COMMAND 0x01
74+ #define FLYDIGI_V2_GET_STATUS_COMMAND 0x10
75+ #define FLYDIGI_V2_SET_STATUS_COMMAND 0x11
76+ #define FLYDIGI_V2_HAPTIC_COMMAND 0x12
77+ #define FLYDIGI_V2_ACQUIRE_CONTROLLER_COMMAND 0x1C
78+ #define FLYDIGI_V2_INPUT_REPORT 0xEF
7679
7780#define LOAD16 (A , B ) (Sint16)((Uint16)(A) | (((Uint16)(B)) << 8))
7881
@@ -89,7 +92,8 @@ typedef struct
8992 Uint64 sensor_timestamp_step_ns ; // Based on observed rate of receipt of IMU sensor packets.
9093 float accelScale ;
9194 float gyroScale ;
92- Uint64 last_heartbeat ;
95+ Uint64 next_heartbeat ;
96+ Uint64 last_packet ;
9397 Uint8 last_state [USB_PACKET_LENGTH ];
9498} SDL_DriverFlydigi_Context ;
9599
@@ -198,58 +202,78 @@ static bool GetReply(SDL_HIDAPI_Device* device, Uint8 command, Uint8* data, size
198202
199203static bool SDL_HIDAPI_Flydigi_SendAcquireRequest (SDL_HIDAPI_Device * device , bool acquire )
200204{
201- const Uint8 acquireControllerCmd [] = {
205+ const Uint8 acquireControllerCmd [32 ] = {
202206 FLYDIGI_V2_CMD_REPORT_ID ,
203207 FLYDIGI_V2_MAGIC1 ,
204208 FLYDIGI_V2_MAGIC2 ,
205209 FLYDIGI_V2_ACQUIRE_CONTROLLER_COMMAND ,
206210 23 ,
207211 acquire ? 1 : 0 ,
208- 83 , 68 , 76 , 0
212+ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
209213 };
214+
215+ if (acquire ) {
216+ const char * name = SDL_GetAppMetadataProperty (SDL_PROP_APP_METADATA_NAME_STRING );
217+ SDL_assert (name != NULL );
218+ SDL_strlcpy ((char * )& acquireControllerCmd [6 ], name , sizeof (acquireControllerCmd ) - 6 );
219+ }
210220 if (SDL_hid_write (device -> dev , acquireControllerCmd , sizeof (acquireControllerCmd )) < 0 ) {
211221 return SDL_SetError ("Couldn't send acquire command" );
212222 }
213223 return true;
214224}
215225
226+ static bool HIDAPI_DriverFlydigi_HandleAcquireResponse (Uint8 * data , int size )
227+ {
228+ if (data [5 ] != 1 ) {
229+ return SDL_SetError ("Controller acquiring has been disabled" );
230+ }
231+ return true;
232+ }
233+
216234static bool HIDAPI_DriverFlydigi_InitControllerV2 (SDL_HIDAPI_Device * device )
217235{
218236 SDL_DriverFlydigi_Context * ctx = (SDL_DriverFlydigi_Context * )device -> context ;
219237
238+ Uint8 data [USB_PACKET_LENGTH ];
220239 const Uint8 query_info [] = { FLYDIGI_V2_CMD_REPORT_ID , FLYDIGI_V2_MAGIC1 , FLYDIGI_V2_MAGIC2 , FLYDIGI_V2_GET_INFO_COMMAND , 2 , 0 };
221240 if (SDL_hid_write (device -> dev , query_info , sizeof (query_info )) < 0 ) {
222241 return SDL_SetError ("Couldn't query controller info" );
223242 }
243+ if (!GetReply (device , FLYDIGI_V2_GET_INFO_COMMAND , data , sizeof (data ))) {
244+ return SDL_SetError ("Couldn't get controller info" );
245+ }
224246
225- Uint8 data [USB_PACKET_LENGTH ];
226- if (GetReply (device , FLYDIGI_V2_GET_INFO_COMMAND , data , sizeof (data ))) {
227- ctx -> deviceID = data [6 ];
228- ctx -> firmware_version = LOAD16 (data [17 ], data [16 ]);
229-
230- switch (data [7 ]) {
231- case 1 :
232- // Wired connection
233- ctx -> wireless = false;
234- break ;
235- case 2 :
236- // Wireless connection
237- ctx -> wireless = true;
238- break ;
239- default :
240- break ;
241- }
247+ // Check the firmware version
248+ ctx -> firmware_version = LOAD16 (data [17 ], data [16 ]);
249+ if (ctx -> firmware_version < 0x7031 ) {
250+ return SDL_SetError ("Unsupported firmware version" );
242251 }
243252
244- ctx -> last_heartbeat = SDL_GetTicks ();
253+ switch (data [7 ]) {
254+ case 1 :
255+ // Wired connection
256+ ctx -> wireless = false;
257+ break ;
258+ case 2 :
259+ // Wireless connection
260+ ctx -> wireless = true;
261+ break ;
262+ default :
263+ break ;
264+ }
265+ ctx -> deviceID = data [6 ];
245266
246- if (!SDL_HIDAPI_Flydigi_SendAcquireRequest (device , true)) {
247- return false;
267+ // See whether we can acquire the controller
268+ const Uint8 query_status [] = { FLYDIGI_V2_CMD_REPORT_ID , FLYDIGI_V2_MAGIC1 , FLYDIGI_V2_MAGIC2 , FLYDIGI_V2_GET_STATUS_COMMAND };
269+ if (SDL_hid_write (device -> dev , query_status , sizeof (query_status )) < 0 ) {
270+ return SDL_SetError ("Couldn't query controller status" );
248271 }
249- if (!GetReply (device , FLYDIGI_V2_ACQUIRE_CONTROLLER_COMMAND , data , sizeof (data ))) {
250- return SDL_SetError ("Controller acquiring is not supported " );
272+ if (!GetReply (device , FLYDIGI_V2_GET_STATUS_COMMAND , data , sizeof (data ))) {
273+ return SDL_SetError ("Couldn't get controller status " );
251274 }
252- if (data [6 ] != 1 ) {
275+ if (data [10 ] != 1 ) {
276+ // Click "Allow third-party apps to take over mappings" in the FlyDigi Space Station app
253277 return SDL_SetError ("Controller acquiring is disabled" );
254278 }
255279 return true;
@@ -341,14 +365,10 @@ static void HIDAPI_DriverFlydigi_UpdateDeviceIdentity(SDL_HIDAPI_Device *device)
341365 case SDL_FLYDIGI_APEX5 :
342366 HIDAPI_SetDeviceName (device , "Flydigi Apex 5" );
343367 ctx -> has_lmrm = true;
344-
345- // The Apex 5 gyro values are only usable on firmware 7.0.3.0 and newer
346- if (ctx -> firmware_version >= 0x7030 ) {
347- ctx -> sensors_supported = true;
348- ctx -> accelScale = SDL_STANDARD_GRAVITY / 4096.0f ;
349- ctx -> gyroScale = DEG2RAD (2000.0f );
350- ctx -> sensor_timestamp_step_ns = ctx -> wireless ? SENSOR_INTERVAL_APEX5_DONGLE_NS : SENSOR_INTERVAL_APEX5_WIRED_NS ;
351- }
368+ ctx -> sensors_supported = true;
369+ ctx -> accelScale = SDL_STANDARD_GRAVITY / 4096.0f ;
370+ ctx -> gyroScale = DEG2RAD (2000.0f );
371+ ctx -> sensor_timestamp_step_ns = ctx -> wireless ? SENSOR_INTERVAL_APEX5_DONGLE_NS : SENSOR_INTERVAL_APEX5_WIRED_NS ;
352372 break ;
353373 case SDL_FLYDIGI_VADER2 :
354374 // The Vader 2 controller has sensors, but they're only reported when gyro mouse is enabled
@@ -503,10 +523,6 @@ static void HIDAPI_DriverFlydigi_HandleStatePacketV1(SDL_Joystick *joystick, SDL
503523{
504524 Sint16 axis ;
505525 Uint64 timestamp = SDL_GetTicksNS ();
506- if (data [0 ] != 0x04 || data [1 ] != 0xFE ) {
507- // We don't know how to handle this report
508- return ;
509- }
510526
511527 Uint8 extra_button_index = SDL_GAMEPAD_NUM_BASE_FLYDIGI_BUTTONS ;
512528
@@ -632,21 +648,22 @@ static void HIDAPI_DriverFlydigi_HandleStatePacketV1(SDL_Joystick *joystick, SDL
632648 SDL_memcpy (ctx -> last_state , data , SDL_min (size , sizeof (ctx -> last_state )));
633649}
634650
651+ static bool HIDAPI_DriverFlydigi_HandlePacketV1 (SDL_Joystick * joystick , SDL_DriverFlydigi_Context * ctx , Uint8 * data , int size )
652+ {
653+ if (data [0 ] != 0x04 || data [1 ] != 0xFE ) {
654+ // We don't know how to handle this report, ignore it
655+ return true;
656+ }
657+
658+ HIDAPI_DriverFlydigi_HandleStatePacketV1 (joystick , ctx , data , size );
659+ return true;
660+ }
661+
635662static void HIDAPI_DriverFlydigi_HandleStatePacketV2 (SDL_Joystick * joystick , SDL_DriverFlydigi_Context * ctx , Uint8 * data , int size )
636663{
637664 Sint16 axis ;
638665 Uint64 timestamp = SDL_GetTicksNS ();
639666
640- if (size > 0 && data [0 ] != 0x5A ) {
641- // If first byte is not 0x5A, it must be REPORT_ID, we need to remove it.
642- ++ data ;
643- -- size ;
644- }
645- if (size < 31 || data [0 ] != 0x5A || data [1 ] != 0xA5 || data [2 ] != 0xEF ) {
646- // We don't know how to handle this report
647- return ;
648- }
649-
650667 Uint8 extra_button_index = SDL_GAMEPAD_NUM_BASE_FLYDIGI_BUTTONS ;
651668
652669 if (ctx -> last_state [11 ] != data [11 ]) {
@@ -766,42 +783,85 @@ static void HIDAPI_DriverFlydigi_HandleStatePacketV2(SDL_Joystick *joystick, SDL
766783 SDL_memcpy (ctx -> last_state , data , SDL_min (size , sizeof (ctx -> last_state )));
767784}
768785
786+ static bool HIDAPI_DriverFlydigi_HandlePacketV2 (SDL_Joystick * joystick , SDL_DriverFlydigi_Context * ctx , Uint8 * data , int size )
787+ {
788+ if (size > 0 && data [0 ] != 0x5A ) {
789+ // If first byte is not 0x5A, it must be REPORT_ID, we need to remove it.
790+ ++ data ;
791+ -- size ;
792+ }
793+ if (size < 31 || data [0 ] != FLYDIGI_V2_MAGIC1 || data [1 ] != FLYDIGI_V2_MAGIC2 ) {
794+ // We don't know how to handle this report, ignore it
795+ return true;
796+ }
797+
798+ switch (data [2 ]) {
799+ case FLYDIGI_V2_ACQUIRE_CONTROLLER_COMMAND :
800+ if (!HIDAPI_DriverFlydigi_HandleAcquireResponse (data , size )) {
801+ return false;
802+ }
803+ break ;
804+ case FLYDIGI_V2_INPUT_REPORT :
805+ HIDAPI_DriverFlydigi_HandleStatePacketV2 (joystick , ctx , data , size );
806+ break ;
807+ default :
808+ // We don't recognize this command, ignore it
809+ break ;
810+ }
811+ return true;
812+ }
813+
769814static bool HIDAPI_DriverFlydigi_UpdateDevice (SDL_HIDAPI_Device * device )
770815{
771816 SDL_DriverFlydigi_Context * ctx = (SDL_DriverFlydigi_Context * )device -> context ;
772817 SDL_Joystick * joystick = NULL ;
773818 Uint8 data [USB_PACKET_LENGTH ];
774819 int size = 0 ;
820+ Uint64 now = SDL_GetTicks ();
775821
776822 if (device -> num_joysticks > 0 ) {
777823 joystick = SDL_GetJoystickFromID (device -> joysticks [0 ]);
778- } else {
779- return false;
780824 }
781825
782- if (device -> vendor_id == USB_VENDOR_FLYDIGI_V2 ) {
783- Uint64 now = SDL_GetTicks ();
784- if (now >= (ctx -> last_heartbeat + FLYDIGI_ACQUIRE_CONTROLLER_HEARTBEAT_TIME )) {
826+ if (device -> vendor_id == USB_VENDOR_FLYDIGI_V2 && joystick ) {
827+ if (!ctx -> next_heartbeat || now >= ctx -> next_heartbeat ) {
785828 SDL_HIDAPI_Flydigi_SendAcquireRequest (device , true);
786- ctx -> last_heartbeat = now ;
829+ ctx -> next_heartbeat = now + FLYDIGI_ACQUIRE_CONTROLLER_HEARTBEAT_TIME ;
787830 }
788831 }
789832
790833 while ((size = SDL_hid_read_timeout (device -> dev , data , sizeof (data ), 0 )) > 0 ) {
791834#ifdef DEBUG_FLYDIGI_PROTOCOL
792835 HIDAPI_DumpPacket ("Flydigi packet: size = %d" , data , size );
793836#endif
837+ ctx -> last_packet = now ;
838+
794839 if (!joystick ) {
795840 continue ;
796841 }
842+
797843 if (device -> vendor_id == USB_VENDOR_FLYDIGI_V1 ) {
798- HIDAPI_DriverFlydigi_HandleStatePacketV1 (joystick , ctx , data , size );
844+ if (!HIDAPI_DriverFlydigi_HandlePacketV1 (joystick , ctx , data , size )) {
845+ HIDAPI_JoystickDisconnected (device , device -> joysticks [0 ]);
846+ return false;
847+ }
799848 } else {
800- HIDAPI_DriverFlydigi_HandleStatePacketV2 (joystick , ctx , data , size );
849+ if (!HIDAPI_DriverFlydigi_HandlePacketV2 (joystick , ctx , data , size )) {
850+ HIDAPI_JoystickDisconnected (device , device -> joysticks [0 ]);
851+ return false;
852+ }
853+ }
854+ }
855+
856+ if (device -> vendor_id == USB_VENDOR_FLYDIGI_V2 ) {
857+ // If we haven't gotten a packet in a while, check to make sure we can still acquire it
858+ const int INPUT_TIMEOUT_MS = 100 ;
859+ if (now >= (ctx -> last_packet + INPUT_TIMEOUT_MS )) {
860+ ctx -> next_heartbeat = now ;
801861 }
802862 }
803863
804- if (size < 0 ) {
864+ if (size < 0 && device -> num_joysticks > 0 ) {
805865 // Read error, device is disconnected
806866 HIDAPI_JoystickDisconnected (device , device -> joysticks [0 ]);
807867 }
@@ -810,7 +870,9 @@ static bool HIDAPI_DriverFlydigi_UpdateDevice(SDL_HIDAPI_Device *device)
810870
811871static void HIDAPI_DriverFlydigi_CloseJoystick (SDL_HIDAPI_Device * device , SDL_Joystick * joystick )
812872{
813- SDL_HIDAPI_Flydigi_SendAcquireRequest (device , false);
873+ // Don't unacquire the controller, someone else might be using it too.
874+ // The controller will automatically unacquire itself after a little while
875+ //SDL_HIDAPI_Flydigi_SendAcquireRequest(device, false);
814876}
815877
816878static void HIDAPI_DriverFlydigi_FreeDevice (SDL_HIDAPI_Device * device )
0 commit comments