summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/hidapi/android
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/hidapi/android
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/hidapi/android')
-rw-r--r--contrib/SDL-3.2.8/src/hidapi/android/hid.cpp1477
-rw-r--r--contrib/SDL-3.2.8/src/hidapi/android/hid.h39
2 files changed, 1516 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/hidapi/android/hid.cpp b/contrib/SDL-3.2.8/src/hidapi/android/hid.cpp
new file mode 100644
index 0000000..887390e
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/hidapi/android/hid.cpp
@@ -0,0 +1,1477 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 2022 Valve Corporation
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// Purpose: A wrapper implementing "HID" API for Android
24//
25// This layer glues the hidapi API to Android's USB and BLE stack.
26
27#include "hid.h"
28
29// Common to stub version and non-stub version of functions
30#include <jni.h>
31#include <android/log.h>
32
33#define TAG "hidapi"
34
35// Have error log always available
36#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
37
38#ifdef DEBUG
39#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
40#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
41#else
42#define LOGV(...)
43#define LOGD(...)
44#endif
45
46#define SDL_JAVA_PREFIX org_libsdl_app
47#define CONCAT1(prefix, class, function) CONCAT2(prefix, class, function)
48#define CONCAT2(prefix, class, function) Java_ ## prefix ## _ ## class ## _ ## function
49#define HID_DEVICE_MANAGER_JAVA_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, HIDDeviceManager, function)
50
51
52#ifndef SDL_HIDAPI_DISABLED
53
54extern "C" {
55#include "../SDL_hidapi_c.h"
56}
57#include "../../core/android/SDL_android.h"
58
59#define hid_close PLATFORM_hid_close
60#define hid_device PLATFORM_hid_device
61#define hid_device_ PLATFORM_hid_device_
62#define hid_enumerate PLATFORM_hid_enumerate
63#define hid_error PLATFORM_hid_error
64#define hid_exit PLATFORM_hid_exit
65#define hid_free_enumeration PLATFORM_hid_free_enumeration
66#define hid_get_device_info PLATFORM_hid_get_device_info
67#define hid_get_feature_report PLATFORM_hid_get_feature_report
68#define hid_get_indexed_string PLATFORM_hid_get_indexed_string
69#define hid_get_input_report PLATFORM_hid_get_input_report
70#define hid_get_manufacturer_string PLATFORM_hid_get_manufacturer_string
71#define hid_get_product_string PLATFORM_hid_get_product_string
72#define hid_get_report_descriptor PLATFORM_hid_get_report_descriptor
73#define hid_get_serial_number_string PLATFORM_hid_get_serial_number_string
74#define hid_init PLATFORM_hid_init
75#define hid_open_path PLATFORM_hid_open_path
76#define hid_open PLATFORM_hid_open
77#define hid_read PLATFORM_hid_read
78#define hid_read_timeout PLATFORM_hid_read_timeout
79#define hid_send_feature_report PLATFORM_hid_send_feature_report
80#define hid_set_nonblocking PLATFORM_hid_set_nonblocking
81#define hid_version PLATFORM_hid_version
82#define hid_version_str PLATFORM_hid_version_str
83#define hid_write PLATFORM_hid_write
84
85#include <pthread.h>
86#include <errno.h> // For ETIMEDOUT and ECONNRESET
87#include <stdlib.h> // For malloc() and free()
88
89#include "../hidapi/hidapi.h"
90
91typedef uint32_t uint32;
92typedef uint64_t uint64;
93
94
95struct hid_device_
96{
97 int m_nId;
98 int m_nDeviceRefCount;
99};
100
101template<class T>
102class hid_device_ref
103{
104public:
105 hid_device_ref( T *pObject = nullptr ) : m_pObject( nullptr )
106 {
107 SetObject( pObject );
108 }
109
110 hid_device_ref( const hid_device_ref &rhs ) : m_pObject( nullptr )
111 {
112 SetObject( rhs.GetObject() );
113 }
114
115 ~hid_device_ref()
116 {
117 SetObject( nullptr );
118 }
119
120 void SetObject( T *pObject )
121 {
122 if ( m_pObject && m_pObject->DecrementRefCount() == 0 )
123 {
124 delete m_pObject;
125 }
126
127 m_pObject = pObject;
128
129 if ( m_pObject )
130 {
131 m_pObject->IncrementRefCount();
132 }
133 }
134
135 hid_device_ref &operator =( T *pObject )
136 {
137 SetObject( pObject );
138 return *this;
139 }
140
141 hid_device_ref &operator =( const hid_device_ref &rhs )
142 {
143 SetObject( rhs.GetObject() );
144 return *this;
145 }
146
147 T *GetObject() const
148 {
149 return m_pObject;
150 }
151
152 T* operator->() const
153 {
154 return m_pObject;
155 }
156
157 operator bool() const
158 {
159 return ( m_pObject != nullptr );
160 }
161
162private:
163 T *m_pObject;
164};
165
166class hid_mutex_guard
167{
168public:
169 hid_mutex_guard( pthread_mutex_t *pMutex ) : m_pMutex( pMutex )
170 {
171 pthread_mutex_lock( m_pMutex );
172 }
173 ~hid_mutex_guard()
174 {
175 pthread_mutex_unlock( m_pMutex );
176 }
177
178private:
179 pthread_mutex_t *m_pMutex;
180};
181
182class hid_buffer
183{
184public:
185 hid_buffer() : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
186 {
187 }
188
189 hid_buffer( const uint8_t *pData, size_t nSize ) : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
190 {
191 assign( pData, nSize );
192 }
193
194 ~hid_buffer()
195 {
196 delete[] m_pData;
197 }
198
199 void assign( const uint8_t *pData, size_t nSize )
200 {
201 if ( nSize > m_nAllocated )
202 {
203 delete[] m_pData;
204 m_pData = new uint8_t[ nSize ];
205 m_nAllocated = nSize;
206 }
207
208 m_nSize = nSize;
209 SDL_memcpy( m_pData, pData, nSize );
210 }
211
212 void clear()
213 {
214 m_nSize = 0;
215 }
216
217 size_t size() const
218 {
219 return m_nSize;
220 }
221
222 const uint8_t *data() const
223 {
224 return m_pData;
225 }
226
227private:
228 uint8_t *m_pData;
229 size_t m_nSize;
230 size_t m_nAllocated;
231};
232
233class hid_buffer_pool
234{
235public:
236 hid_buffer_pool() : m_nSize( 0 ), m_pHead( nullptr ), m_pTail( nullptr ), m_pFree( nullptr )
237 {
238 }
239
240 ~hid_buffer_pool()
241 {
242 clear();
243
244 while ( m_pFree )
245 {
246 hid_buffer_entry *pEntry = m_pFree;
247 m_pFree = m_pFree->m_pNext;
248 delete pEntry;
249 }
250 }
251
252 size_t size() const { return m_nSize; }
253
254 const hid_buffer &front() const { return m_pHead->m_buffer; }
255
256 void pop_front()
257 {
258 hid_buffer_entry *pEntry = m_pHead;
259 if ( pEntry )
260 {
261 m_pHead = pEntry->m_pNext;
262 if ( !m_pHead )
263 {
264 m_pTail = nullptr;
265 }
266 pEntry->m_pNext = m_pFree;
267 m_pFree = pEntry;
268 --m_nSize;
269 }
270 }
271
272 void emplace_back( const uint8_t *pData, size_t nSize )
273 {
274 hid_buffer_entry *pEntry;
275
276 if ( m_pFree )
277 {
278 pEntry = m_pFree;
279 m_pFree = m_pFree->m_pNext;
280 }
281 else
282 {
283 pEntry = new hid_buffer_entry;
284 }
285 pEntry->m_pNext = nullptr;
286
287 if ( m_pTail )
288 {
289 m_pTail->m_pNext = pEntry;
290 }
291 else
292 {
293 m_pHead = pEntry;
294 }
295 m_pTail = pEntry;
296
297 pEntry->m_buffer.assign( pData, nSize );
298 ++m_nSize;
299 }
300
301 void clear()
302 {
303 while ( size() > 0 )
304 {
305 pop_front();
306 }
307 }
308
309private:
310 struct hid_buffer_entry
311 {
312 hid_buffer m_buffer;
313 hid_buffer_entry *m_pNext;
314 };
315
316 size_t m_nSize;
317 hid_buffer_entry *m_pHead;
318 hid_buffer_entry *m_pTail;
319 hid_buffer_entry *m_pFree;
320};
321
322static jbyteArray NewByteArray( JNIEnv* env, const uint8_t *pData, size_t nDataLen )
323{
324 jbyteArray array = env->NewByteArray( (jsize)nDataLen );
325 jbyte *pBuf = env->GetByteArrayElements( array, NULL );
326 SDL_memcpy( pBuf, pData, nDataLen );
327 env->ReleaseByteArrayElements( array, pBuf, 0 );
328
329 return array;
330}
331
332static char *CreateStringFromJString( JNIEnv *env, const jstring &sString )
333{
334 size_t nLength = env->GetStringUTFLength( sString );
335 const char *pjChars = env->GetStringUTFChars( sString, NULL );
336 char *psString = (char*)malloc( nLength + 1 );
337 SDL_memcpy( psString, pjChars, nLength );
338 psString[ nLength ] = '\0';
339 env->ReleaseStringUTFChars( sString, pjChars );
340 return psString;
341}
342
343static wchar_t *CreateWStringFromJString( JNIEnv *env, const jstring &sString )
344{
345 size_t nLength = env->GetStringLength( sString );
346 const jchar *pjChars = env->GetStringChars( sString, NULL );
347 wchar_t *pwString = (wchar_t*)malloc( ( nLength + 1 ) * sizeof( wchar_t ) );
348 wchar_t *pwChars = pwString;
349 for ( size_t iIndex = 0; iIndex < nLength; ++iIndex )
350 {
351 pwChars[ iIndex ] = pjChars[ iIndex ];
352 }
353 pwString[ nLength ] = '\0';
354 env->ReleaseStringChars( sString, pjChars );
355 return pwString;
356}
357
358static wchar_t *CreateWStringFromWString( const wchar_t *pwSrc )
359{
360 size_t nLength = SDL_wcslen( pwSrc );
361 wchar_t *pwString = (wchar_t*)malloc( ( nLength + 1 ) * sizeof( wchar_t ) );
362 SDL_memcpy( pwString, pwSrc, nLength * sizeof( wchar_t ) );
363 pwString[ nLength ] = '\0';
364 return pwString;
365}
366
367static hid_device_info *CopyHIDDeviceInfo( const hid_device_info *pInfo )
368{
369 hid_device_info *pCopy = new hid_device_info;
370 *pCopy = *pInfo;
371 pCopy->path = SDL_strdup( pInfo->path );
372 pCopy->product_string = CreateWStringFromWString( pInfo->product_string );
373 pCopy->manufacturer_string = CreateWStringFromWString( pInfo->manufacturer_string );
374 pCopy->serial_number = CreateWStringFromWString( pInfo->serial_number );
375 return pCopy;
376}
377
378static void FreeHIDDeviceInfo( hid_device_info *pInfo )
379{
380 free( pInfo->path );
381 free( pInfo->serial_number );
382 free( pInfo->manufacturer_string );
383 free( pInfo->product_string );
384 delete pInfo;
385}
386
387static jclass g_HIDDeviceManagerCallbackClass;
388static jobject g_HIDDeviceManagerCallbackHandler;
389static jmethodID g_midHIDDeviceManagerInitialize;
390static jmethodID g_midHIDDeviceManagerOpen;
391static jmethodID g_midHIDDeviceManagerWriteReport;
392static jmethodID g_midHIDDeviceManagerReadReport;
393static jmethodID g_midHIDDeviceManagerClose;
394static bool g_initialized = false;
395
396static uint64_t get_timespec_ms( const struct timespec &ts )
397{
398 return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
399}
400
401static void ExceptionCheck( JNIEnv *env, const char *pszClassName, const char *pszMethodName )
402{
403 if ( env->ExceptionCheck() )
404 {
405 // Get our exception
406 jthrowable jExcept = env->ExceptionOccurred();
407
408 // Clear the exception so we can call JNI again
409 env->ExceptionClear();
410
411 // Get our exception message
412 jclass jExceptClass = env->GetObjectClass( jExcept );
413 jmethodID jMessageMethod = env->GetMethodID( jExceptClass, "getMessage", "()Ljava/lang/String;" );
414 jstring jMessage = (jstring)( env->CallObjectMethod( jExcept, jMessageMethod ) );
415 const char *pszMessage = env->GetStringUTFChars( jMessage, NULL );
416
417 // ...and log it.
418 LOGE( "%s%s%s threw an exception: %s",
419 pszClassName ? pszClassName : "",
420 pszClassName ? "::" : "",
421 pszMethodName, pszMessage );
422
423 // Cleanup
424 env->ReleaseStringUTFChars( jMessage, pszMessage );
425 env->DeleteLocalRef( jMessage );
426 env->DeleteLocalRef( jExceptClass );
427 env->DeleteLocalRef( jExcept );
428 }
429}
430
431class CHIDDevice
432{
433public:
434 CHIDDevice( int nDeviceID, hid_device_info *pInfo )
435 {
436 m_nId = nDeviceID;
437 m_pInfo = pInfo;
438
439 // The Bluetooth Steam Controller needs special handling
440 const int VALVE_USB_VID = 0x28DE;
441 const int D0G_BLE2_PID = 0x1106;
442 if ( pInfo->vendor_id == VALVE_USB_VID && pInfo->product_id == D0G_BLE2_PID )
443 {
444 m_bIsBLESteamController = true;
445 }
446 }
447
448 ~CHIDDevice()
449 {
450 FreeHIDDeviceInfo( m_pInfo );
451
452 // Note that we don't delete m_pDevice, as the app may still have a reference to it
453 }
454
455 int IncrementRefCount()
456 {
457 int nValue;
458 pthread_mutex_lock( &m_refCountLock );
459 nValue = ++m_nRefCount;
460 pthread_mutex_unlock( &m_refCountLock );
461 return nValue;
462 }
463
464 int DecrementRefCount()
465 {
466 int nValue;
467 pthread_mutex_lock( &m_refCountLock );
468 nValue = --m_nRefCount;
469 pthread_mutex_unlock( &m_refCountLock );
470 return nValue;
471 }
472
473 int GetId()
474 {
475 return m_nId;
476 }
477
478 hid_device_info *GetDeviceInfo()
479 {
480 return m_pInfo;
481 }
482
483 hid_device *GetDevice()
484 {
485 return m_pDevice;
486 }
487
488 void ExceptionCheck( JNIEnv *env, const char *pszMethodName )
489 {
490 ::ExceptionCheck( env, "CHIDDevice", pszMethodName );
491 }
492
493 bool BOpen()
494 {
495 JNIEnv *env = (JNIEnv *)SDL_GetAndroidJNIEnv();
496
497 if ( !g_HIDDeviceManagerCallbackHandler )
498 {
499 LOGV( "Device open without callback handler" );
500 return false;
501 }
502
503 if ( m_bIsWaitingForOpen )
504 {
505 SDL_SetError( "Waiting for permission" );
506 return false;
507 }
508
509 if ( !m_bOpenResult )
510 {
511 m_bOpenResult = env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerOpen, m_nId );
512 ExceptionCheck( env, "BOpen" );
513
514 if ( m_bIsWaitingForOpen )
515 {
516 LOGV( "Device open waiting for permission" );
517 SDL_SetError( "Waiting for permission" );
518 m_bWasOpenPending = true;
519 return false;
520 }
521
522 if ( !m_bOpenResult )
523 {
524 LOGV( "Device open failed" );
525 SDL_SetError( "Device open failed" );
526 return false;
527 }
528 }
529
530 m_pDevice = new hid_device;
531 m_pDevice->m_nId = m_nId;
532 m_pDevice->m_nDeviceRefCount = 1;
533 LOGD("Creating device %d (%p), refCount = 1\n", m_pDevice->m_nId, m_pDevice);
534
535 return true;
536 }
537
538 void SetOpenPending()
539 {
540 m_bIsWaitingForOpen = true;
541 }
542
543 bool BOpenPending() const
544 {
545 return m_bIsWaitingForOpen;
546 }
547
548 void SetWasOpenPending( bool bState )
549 {
550 m_bWasOpenPending = bState;
551 }
552
553 bool BWasOpenPending() const
554 {
555 return m_bWasOpenPending;
556 }
557
558 void SetOpenResult( bool bResult )
559 {
560 if ( m_bIsWaitingForOpen )
561 {
562 m_bOpenResult = bResult;
563 m_bIsWaitingForOpen = false;
564
565 if ( m_bOpenResult )
566 {
567 LOGV( "Device open succeeded" );
568 }
569 else
570 {
571 LOGV( "Device open failed" );
572 }
573 }
574 }
575
576 bool BOpenResult() const
577 {
578 return m_bOpenResult;
579 }
580
581 void ProcessInput( const uint8_t *pBuf, size_t nBufSize )
582 {
583 hid_mutex_guard l( &m_dataLock );
584
585 size_t MAX_REPORT_QUEUE_SIZE = 16;
586 if ( m_vecData.size() >= MAX_REPORT_QUEUE_SIZE )
587 {
588 m_vecData.pop_front();
589 }
590 m_vecData.emplace_back( pBuf, nBufSize );
591 }
592
593 int GetInput( unsigned char *data, size_t length )
594 {
595 hid_mutex_guard l( &m_dataLock );
596
597 if ( m_vecData.size() == 0 )
598 {
599// LOGV( "hid_read_timeout no data available" );
600 return 0;
601 }
602
603 const hid_buffer &buffer = m_vecData.front();
604 size_t nDataLen = buffer.size() > length ? length : buffer.size();
605 if ( m_bIsBLESteamController )
606 {
607 data[0] = 0x03;
608 SDL_memcpy( data + 1, buffer.data(), nDataLen );
609 ++nDataLen;
610 }
611 else
612 {
613 SDL_memcpy( data, buffer.data(), nDataLen );
614 }
615 m_vecData.pop_front();
616
617// LOGV("Read %u bytes", nDataLen);
618// LOGV("%02x %02x %02x %02x %02x %02x %02x %02x ....",
619// data[0], data[1], data[2], data[3],
620// data[4], data[5], data[6], data[7]);
621
622 return (int)nDataLen;
623 }
624
625 int WriteReport( const unsigned char *pData, size_t nDataLen, bool bFeature )
626 {
627 JNIEnv *env = (JNIEnv *)SDL_GetAndroidJNIEnv();
628
629 if ( !g_HIDDeviceManagerCallbackHandler )
630 {
631 LOGV( "WriteReport without callback handler" );
632 return -1;
633 }
634
635 jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
636 int nRet = env->CallIntMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerWriteReport, m_nId, pBuf, bFeature );
637 ExceptionCheck( env, "WriteReport" );
638 env->DeleteLocalRef( pBuf );
639 return nRet;
640 }
641
642 void ProcessReportResponse( const uint8_t *pBuf, size_t nBufSize )
643 {
644 hid_mutex_guard cvl( &m_cvLock );
645 if ( m_bIsWaitingForReportResponse )
646 {
647 m_reportResponse.assign( pBuf, nBufSize );
648
649 m_bIsWaitingForReportResponse = false;
650 m_nReportResponseError = 0;
651 pthread_cond_signal( &m_cv );
652 }
653 }
654
655 int ReadReport( unsigned char *pData, size_t nDataLen, bool bFeature )
656 {
657 JNIEnv *env = (JNIEnv *)SDL_GetAndroidJNIEnv();
658
659 if ( !g_HIDDeviceManagerCallbackHandler )
660 {
661 LOGV( "ReadReport without callback handler" );
662 return -1;
663 }
664
665 {
666 hid_mutex_guard cvl( &m_cvLock );
667 if ( m_bIsWaitingForReportResponse )
668 {
669 LOGV( "Get feature report already ongoing... bail" );
670 return -1; // Read already ongoing, we currently do not serialize, TODO
671 }
672 m_bIsWaitingForReportResponse = true;
673 }
674
675 jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
676 int nRet = env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerReadReport, m_nId, pBuf, bFeature ) ? 0 : -1;
677 ExceptionCheck( env, "ReadReport" );
678 env->DeleteLocalRef( pBuf );
679 if ( nRet < 0 )
680 {
681 LOGV( "ReadReport failed" );
682 m_bIsWaitingForReportResponse = false;
683 return -1;
684 }
685
686 {
687 hid_mutex_guard cvl( &m_cvLock );
688 if ( m_bIsWaitingForReportResponse )
689 {
690 LOGV("=== Going to sleep" );
691 // Wait in CV until we are no longer waiting for a feature report.
692 const int FEATURE_REPORT_TIMEOUT_SECONDS = 2;
693 struct timespec ts, endtime;
694 clock_gettime( CLOCK_REALTIME, &ts );
695 endtime = ts;
696 endtime.tv_sec += FEATURE_REPORT_TIMEOUT_SECONDS;
697 do
698 {
699 if ( pthread_cond_timedwait( &m_cv, &m_cvLock, &endtime ) != 0 )
700 {
701 break;
702 }
703 }
704 while ( m_bIsWaitingForReportResponse && get_timespec_ms( ts ) < get_timespec_ms( endtime ) );
705
706 // We are back
707 if ( m_bIsWaitingForReportResponse )
708 {
709 m_nReportResponseError = -ETIMEDOUT;
710 m_bIsWaitingForReportResponse = false;
711 }
712 LOGV( "=== Got feature report err=%d", m_nReportResponseError );
713 if ( m_nReportResponseError != 0 )
714 {
715 return m_nReportResponseError;
716 }
717 }
718
719 size_t uBytesToCopy = m_reportResponse.size() > nDataLen ? nDataLen : m_reportResponse.size();
720 SDL_memcpy( pData, m_reportResponse.data(), uBytesToCopy );
721 m_reportResponse.clear();
722 LOGV( "=== Got %zu bytes", uBytesToCopy );
723
724 return (int)uBytesToCopy;
725 }
726 }
727
728 void Close( bool bDeleteDevice )
729 {
730 JNIEnv *env = (JNIEnv *)SDL_GetAndroidJNIEnv();
731
732 if ( g_HIDDeviceManagerCallbackHandler )
733 {
734 if ( !m_bIsWaitingForOpen && m_bOpenResult )
735 {
736 env->CallVoidMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerClose, m_nId );
737 ExceptionCheck( env, "Close" );
738 }
739 }
740
741 hid_mutex_guard dataLock( &m_dataLock );
742 m_vecData.clear();
743
744 // Clean and release pending feature report reads
745 hid_mutex_guard cvLock( &m_cvLock );
746 m_reportResponse.clear();
747 m_bIsWaitingForReportResponse = false;
748 m_nReportResponseError = -ECONNRESET;
749 pthread_cond_broadcast( &m_cv );
750
751 m_bOpenResult = false;
752
753 if ( bDeleteDevice )
754 {
755 delete m_pDevice;
756 m_pDevice = nullptr;
757 }
758 }
759
760private:
761 pthread_mutex_t m_refCountLock = PTHREAD_MUTEX_INITIALIZER;
762 int m_nRefCount = 0;
763 int m_nId = 0;
764 hid_device_info *m_pInfo = nullptr;
765 hid_device *m_pDevice = nullptr;
766 bool m_bIsBLESteamController = false;
767
768 pthread_mutex_t m_dataLock = PTHREAD_MUTEX_INITIALIZER; // This lock has to be held to access m_vecData
769 hid_buffer_pool m_vecData;
770
771 // For handling get_feature_report
772 pthread_mutex_t m_cvLock = PTHREAD_MUTEX_INITIALIZER; // This lock has to be held to access any variables below
773 pthread_cond_t m_cv = PTHREAD_COND_INITIALIZER;
774 bool m_bIsWaitingForOpen = false;
775 bool m_bWasOpenPending = false;
776 bool m_bOpenResult = false;
777 bool m_bIsWaitingForReportResponse = false;
778 int m_nReportResponseError = 0;
779 hid_buffer m_reportResponse;
780
781public:
782 hid_device_ref<CHIDDevice> next;
783};
784
785class CHIDDevice;
786static pthread_mutex_t g_DevicesMutex = PTHREAD_MUTEX_INITIALIZER;
787static pthread_mutex_t g_DevicesRefCountMutex = PTHREAD_MUTEX_INITIALIZER;
788static hid_device_ref<CHIDDevice> g_Devices;
789
790static hid_device_ref<CHIDDevice> FindDevice( int nDeviceId )
791{
792 hid_device_ref<CHIDDevice> pDevice;
793
794 hid_mutex_guard l( &g_DevicesMutex );
795 for ( pDevice = g_Devices; pDevice; pDevice = pDevice->next )
796 {
797 if ( pDevice->GetId() == nDeviceId )
798 {
799 break;
800 }
801 }
802 return pDevice;
803}
804
805
806extern "C"
807JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz);
808
809extern "C"
810JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz);
811
812extern "C"
813JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol, bool bBluetooth );
814
815extern "C"
816JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID);
817
818extern "C"
819JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened);
820
821extern "C"
822JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID);
823
824extern "C"
825JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
826
827extern "C"
828JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReportResponse)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
829
830
831extern "C"
832JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz )
833{
834 LOGV( "HIDDeviceRegisterCallback()");
835
836 if ( g_HIDDeviceManagerCallbackHandler != NULL )
837 {
838 env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
839 g_HIDDeviceManagerCallbackClass = NULL;
840 env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
841 g_HIDDeviceManagerCallbackHandler = NULL;
842 }
843
844 g_HIDDeviceManagerCallbackHandler = env->NewGlobalRef( thiz );
845 jclass objClass = env->GetObjectClass( thiz );
846 if ( objClass )
847 {
848 g_HIDDeviceManagerCallbackClass = reinterpret_cast< jclass >( env->NewGlobalRef( objClass ) );
849 g_midHIDDeviceManagerInitialize = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "initialize", "(ZZ)Z" );
850 if ( !g_midHIDDeviceManagerInitialize )
851 {
852 __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing initialize" );
853 }
854 g_midHIDDeviceManagerOpen = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "openDevice", "(I)Z" );
855 if ( !g_midHIDDeviceManagerOpen )
856 {
857 __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing openDevice" );
858 }
859 g_midHIDDeviceManagerWriteReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "writeReport", "(I[BZ)I" );
860 if ( !g_midHIDDeviceManagerWriteReport )
861 {
862 __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing writeReport" );
863 }
864 g_midHIDDeviceManagerReadReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "readReport", "(I[BZ)Z" );
865 if ( !g_midHIDDeviceManagerReadReport )
866 {
867 __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing getFeatureReport" );
868 }
869 g_midHIDDeviceManagerClose = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "closeDevice", "(I)V" );
870 if ( !g_midHIDDeviceManagerClose )
871 {
872 __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing closeDevice" );
873 }
874 env->DeleteLocalRef( objClass );
875 }
876}
877
878extern "C"
879JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz)
880{
881 LOGV("HIDDeviceReleaseCallback");
882 if ( env->IsSameObject( thiz, g_HIDDeviceManagerCallbackHandler ) )
883 {
884 env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
885 g_HIDDeviceManagerCallbackClass = NULL;
886 env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
887 g_HIDDeviceManagerCallbackHandler = NULL;
888 g_initialized = false;
889 }
890}
891
892extern "C"
893JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol, bool bBluetooth )
894{
895 LOGV( "HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface );
896
897 hid_device_info *pInfo = new hid_device_info;
898 SDL_memset( pInfo, 0, sizeof( *pInfo ) );
899 pInfo->path = CreateStringFromJString( env, sIdentifier );
900 pInfo->vendor_id = nVendorId;
901 pInfo->product_id = nProductId;
902 pInfo->serial_number = CreateWStringFromJString( env, sSerialNumber );
903 pInfo->release_number = nReleaseNumber;
904 pInfo->manufacturer_string = CreateWStringFromJString( env, sManufacturer );
905 pInfo->product_string = CreateWStringFromJString( env, sProduct );
906 pInfo->interface_number = nInterface;
907 pInfo->interface_class = nInterfaceClass;
908 pInfo->interface_subclass = nInterfaceSubclass;
909 pInfo->interface_protocol = nInterfaceProtocol;
910 if ( bBluetooth )
911 {
912 pInfo->bus_type = HID_API_BUS_BLUETOOTH;
913 }
914 else
915 {
916 pInfo->bus_type = HID_API_BUS_USB;
917 }
918
919 hid_device_ref<CHIDDevice> pDevice( new CHIDDevice( nDeviceID, pInfo ) );
920
921 hid_mutex_guard l( &g_DevicesMutex );
922 hid_device_ref<CHIDDevice> pLast, pCurr;
923 for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->next )
924 {
925 continue;
926 }
927 if ( pLast )
928 {
929 pLast->next = pDevice;
930 }
931 else
932 {
933 g_Devices = pDevice;
934 }
935}
936
937extern "C"
938JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID)
939{
940 LOGV( "HIDDeviceOpenPending() id=%d\n", nDeviceID );
941 hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
942 if ( pDevice )
943 {
944 pDevice->SetOpenPending();
945 }
946}
947
948extern "C"
949JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened)
950{
951 LOGV( "HIDDeviceOpenResult() id=%d, result=%s\n", nDeviceID, bOpened ? "true" : "false" );
952 hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
953 if ( pDevice )
954 {
955 pDevice->SetOpenResult( bOpened );
956 }
957}
958
959extern "C"
960JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID)
961{
962 LOGV( "HIDDeviceDisconnected() id=%d\n", nDeviceID );
963 hid_device_ref<CHIDDevice> pDevice;
964 {
965 hid_mutex_guard l( &g_DevicesMutex );
966 hid_device_ref<CHIDDevice> pLast, pCurr;
967 for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->next )
968 {
969 if ( pCurr->GetId() == nDeviceID )
970 {
971 pDevice = pCurr;
972
973 if ( pLast )
974 {
975 pLast->next = pCurr->next;
976 }
977 else
978 {
979 g_Devices = pCurr->next;
980 }
981 }
982 }
983 }
984 if ( pDevice )
985 {
986 pDevice->Close( false );
987 }
988}
989
990extern "C"
991JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
992{
993 jbyte *pBuf = env->GetByteArrayElements(value, NULL);
994 jsize nBufSize = env->GetArrayLength(value);
995
996// LOGV( "HIDDeviceInput() id=%d len=%u\n", nDeviceID, nBufSize );
997 hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
998 if ( pDevice )
999 {
1000 pDevice->ProcessInput( reinterpret_cast< const uint8_t* >( pBuf ), nBufSize );
1001 }
1002
1003 env->ReleaseByteArrayElements(value, pBuf, 0);
1004}
1005
1006extern "C"
1007JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReportResponse)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
1008{
1009 jbyte *pBuf = env->GetByteArrayElements(value, NULL);
1010 jsize nBufSize = env->GetArrayLength(value);
1011
1012 LOGV( "HIDDeviceReportResponse() id=%d len=%u\n", nDeviceID, nBufSize );
1013 hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
1014 if ( pDevice )
1015 {
1016 pDevice->ProcessReportResponse( reinterpret_cast< const uint8_t* >( pBuf ), nBufSize );
1017 }
1018
1019 env->ReleaseByteArrayElements(value, pBuf, 0);
1020}
1021
1022//////////////////////////////////////////////////////////////////////////////////////////////////////////////
1023//////////////////////////////////////////////////////////////////////////////////////////////////////////////
1024//////////////////////////////////////////////////////////////////////////////////////////////////////////////
1025
1026extern "C"
1027{
1028
1029static void SDLCALL RequestBluetoothPermissionCallback( void *userdata, const char *permission, bool granted )
1030{
1031 SDL_Log( "Bluetooth permission %s", granted ? "granted" : "denied" );
1032
1033 if ( granted && g_HIDDeviceManagerCallbackHandler )
1034 {
1035 JNIEnv *env = (JNIEnv *)SDL_GetAndroidJNIEnv();
1036
1037 env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerInitialize, false, true );
1038 }
1039}
1040
1041int hid_init(void)
1042{
1043 if ( !g_initialized && g_HIDDeviceManagerCallbackHandler )
1044 {
1045 // HIDAPI doesn't work well with Android < 4.3
1046 if ( SDL_GetAndroidSDKVersion() >= 18 )
1047 {
1048 JNIEnv *env = (JNIEnv *)SDL_GetAndroidJNIEnv();
1049
1050 env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerInitialize, true, false );
1051
1052 // Bluetooth is currently only used for Steam Controllers, so check that hint
1053 // before initializing Bluetooth, which will prompt the user for permission.
1054 if ( SDL_GetHintBoolean( SDL_HINT_JOYSTICK_HIDAPI_STEAM, false ) )
1055 {
1056 if ( SDL_GetAndroidSDKVersion() < 31 )
1057 {
1058 env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerInitialize, false, true );
1059 }
1060 else
1061 {
1062 SDL_Log( "Requesting Bluetooth permission" );
1063 SDL_RequestAndroidPermission( "android.permission.BLUETOOTH_CONNECT", RequestBluetoothPermissionCallback, NULL );
1064 }
1065 }
1066 ExceptionCheck( env, NULL, "hid_init" );
1067 }
1068 g_initialized = true; // Regardless of result, so it's only called once
1069 }
1070 return 0;
1071}
1072
1073struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
1074{
1075 struct hid_device_info *root = NULL;
1076
1077 hid_mutex_guard l( &g_DevicesMutex );
1078 for ( hid_device_ref<CHIDDevice> pDevice = g_Devices; pDevice; pDevice = pDevice->next )
1079 {
1080 // Don't enumerate devices that are currently being opened, we'll re-enumerate them when we're done
1081 // Make sure we skip them at least once, so they get removed and then re-added to the caller's device list
1082 if ( pDevice->BWasOpenPending() )
1083 {
1084 // Don't enumerate devices that failed to open, otherwise the application might try to keep prompting for access
1085 if ( !pDevice->BOpenPending() && pDevice->BOpenResult() )
1086 {
1087 pDevice->SetWasOpenPending( false );
1088 }
1089 continue;
1090 }
1091
1092 const hid_device_info *info = pDevice->GetDeviceInfo();
1093
1094 /* See if there are any devices we should skip in enumeration */
1095 if (SDL_HIDAPI_ShouldIgnoreDevice(HID_API_BUS_UNKNOWN, info->vendor_id, info->product_id, 0, 0)) {
1096 continue;
1097 }
1098
1099 if ( ( vendor_id == 0x0 || info->vendor_id == vendor_id ) &&
1100 ( product_id == 0x0 || info->product_id == product_id ) )
1101 {
1102 hid_device_info *dev = CopyHIDDeviceInfo( info );
1103 dev->next = root;
1104 root = dev;
1105 }
1106 }
1107 return root;
1108}
1109
1110void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
1111{
1112 while ( devs )
1113 {
1114 struct hid_device_info *next = devs->next;
1115 FreeHIDDeviceInfo( devs );
1116 devs = next;
1117 }
1118}
1119
1120HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
1121{
1122 // TODO: Implement
1123 return NULL;
1124}
1125
1126HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
1127{
1128 LOGV( "hid_open_path( %s )", path );
1129
1130 hid_device_ref< CHIDDevice > pDevice;
1131 {
1132 hid_mutex_guard r( &g_DevicesRefCountMutex );
1133 hid_mutex_guard l( &g_DevicesMutex );
1134 for ( hid_device_ref<CHIDDevice> pCurr = g_Devices; pCurr; pCurr = pCurr->next )
1135 {
1136 if ( SDL_strcmp( pCurr->GetDeviceInfo()->path, path ) == 0 )
1137 {
1138 hid_device *pValue = pCurr->GetDevice();
1139 if ( pValue )
1140 {
1141 ++pValue->m_nDeviceRefCount;
1142 LOGD("Incrementing device %d (%p), refCount = %d\n", pValue->m_nId, pValue, pValue->m_nDeviceRefCount);
1143 return pValue;
1144 }
1145
1146 // Hold a shared pointer to the controller for the duration
1147 pDevice = pCurr;
1148 break;
1149 }
1150 }
1151 }
1152 if ( !pDevice )
1153 {
1154 SDL_SetError( "Couldn't find device with path %s", path );
1155 return NULL;
1156 }
1157 if ( pDevice->BOpen() )
1158 {
1159 return pDevice->GetDevice();
1160 }
1161 return NULL;
1162}
1163
1164int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length)
1165{
1166 if ( device )
1167 {
1168// LOGV( "hid_write id=%d length=%zu", device->m_nId, length );
1169 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1170 if ( pDevice )
1171 {
1172 return pDevice->WriteReport( data, length, false );
1173 }
1174 }
1175 return -1; // Controller was disconnected
1176}
1177
1178static uint32_t getms()
1179{
1180 struct timeval now;
1181
1182 gettimeofday(&now, NULL);
1183 return (uint32_t)(now.tv_sec * 1000 + now.tv_usec / 1000);
1184}
1185
1186static void delayms(uint32_t ms)
1187{
1188 int was_error;
1189
1190 struct timespec elapsed, tv;
1191
1192 /* Set the timeout interval */
1193 elapsed.tv_sec = ms / 1000;
1194 elapsed.tv_nsec = (ms % 1000) * 1000000;
1195 do {
1196 errno = 0;
1197
1198 tv.tv_sec = elapsed.tv_sec;
1199 tv.tv_nsec = elapsed.tv_nsec;
1200 was_error = nanosleep(&tv, &elapsed);
1201 } while (was_error && (errno == EINTR));
1202}
1203
1204int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
1205{
1206 if ( device )
1207 {
1208// LOGV( "hid_read_timeout id=%d length=%u timeout=%d", device->m_nId, length, milliseconds );
1209 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1210 if ( pDevice )
1211 {
1212 int nResult = pDevice->GetInput( data, length );
1213 if ( nResult == 0 && milliseconds > 0 )
1214 {
1215 uint32_t start = getms();
1216 do
1217 {
1218 delayms( 1 );
1219 nResult = pDevice->GetInput( data, length );
1220 } while ( nResult == 0 && ( getms() - start ) < milliseconds );
1221 }
1222 return nResult;
1223 }
1224 LOGV( "controller was disconnected" );
1225 }
1226 return -1; // Controller was disconnected
1227}
1228
1229// TODO: Implement blocking
1230int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length)
1231{
1232// LOGV( "hid_read id=%d length=%zu", device->m_nId, length );
1233 return hid_read_timeout( device, data, length, 0 );
1234}
1235
1236// TODO: Implement?
1237int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock)
1238{
1239 return -1;
1240}
1241
1242int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length)
1243{
1244 if ( device )
1245 {
1246 LOGV( "hid_send_feature_report id=%d length=%zu", device->m_nId, length );
1247 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1248 if ( pDevice )
1249 {
1250 return pDevice->WriteReport( data, length, true );
1251 }
1252 }
1253 return -1; // Controller was disconnected
1254}
1255
1256
1257// Synchronous operation. Will block until completed.
1258int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length)
1259{
1260 if ( device )
1261 {
1262 LOGV( "hid_get_feature_report id=%d length=%zu", device->m_nId, length );
1263 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1264 if ( pDevice )
1265 {
1266 return pDevice->ReadReport( data, length, true );
1267 }
1268 }
1269 return -1; // Controller was disconnected
1270}
1271
1272
1273// Synchronous operation. Will block until completed.
1274int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *device, unsigned char *data, size_t length)
1275{
1276 if ( device )
1277 {
1278 LOGV( "hid_get_input_report id=%d length=%zu", device->m_nId, length );
1279 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1280 if ( pDevice )
1281 {
1282 return pDevice->ReadReport( data, length, false );
1283 }
1284 }
1285 return -1; // Controller was disconnected
1286}
1287
1288
1289void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device)
1290{
1291 if ( device )
1292 {
1293 LOGV( "hid_close id=%d", device->m_nId );
1294 hid_mutex_guard r( &g_DevicesRefCountMutex );
1295 LOGD("Decrementing device %d (%p), refCount = %d\n", device->m_nId, device, device->m_nDeviceRefCount - 1);
1296 if ( --device->m_nDeviceRefCount == 0 )
1297 {
1298 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1299 if ( pDevice )
1300 {
1301 pDevice->Close( true );
1302 }
1303 else
1304 {
1305 delete device;
1306 }
1307 LOGD("Deleted device %p\n", device);
1308 }
1309 }
1310}
1311
1312int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen)
1313{
1314 if ( device )
1315 {
1316 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1317 if ( pDevice )
1318 {
1319 wcsncpy( string, pDevice->GetDeviceInfo()->manufacturer_string, maxlen );
1320 return 0;
1321 }
1322 }
1323 return -1;
1324}
1325
1326int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen)
1327{
1328 if ( device )
1329 {
1330 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1331 if ( pDevice )
1332 {
1333 wcsncpy( string, pDevice->GetDeviceInfo()->product_string, maxlen );
1334 return 0;
1335 }
1336 }
1337 return -1;
1338}
1339
1340int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen)
1341{
1342 if ( device )
1343 {
1344 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1345 if ( pDevice )
1346 {
1347 wcsncpy( string, pDevice->GetDeviceInfo()->serial_number, maxlen );
1348 return 0;
1349 }
1350 }
1351 return -1;
1352}
1353
1354int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen)
1355{
1356 return -1;
1357}
1358
1359struct hid_device_info *hid_get_device_info(hid_device *device)
1360{
1361 if ( device )
1362 {
1363 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1364 if ( pDevice )
1365 {
1366 return pDevice->GetDeviceInfo();
1367 }
1368 }
1369 return NULL;
1370}
1371
1372int hid_get_report_descriptor(hid_device *device, unsigned char *buf, size_t buf_size)
1373{
1374 // Not implemented
1375 return -1;
1376}
1377
1378HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device)
1379{
1380 return NULL;
1381}
1382
1383int hid_exit(void)
1384{
1385 return 0;
1386}
1387
1388}
1389
1390#else
1391
1392extern "C"
1393JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz);
1394
1395extern "C"
1396JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz);
1397
1398extern "C"
1399JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol, bool bBluetooth );
1400
1401extern "C"
1402JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID);
1403
1404extern "C"
1405JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened);
1406
1407extern "C"
1408JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID);
1409
1410extern "C"
1411JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
1412
1413extern "C"
1414JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReportResponse)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
1415
1416
1417extern "C"
1418JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz )
1419{
1420 LOGV("Stub HIDDeviceRegisterCallback()");
1421}
1422
1423extern "C"
1424JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz)
1425{
1426 LOGV("Stub HIDDeviceReleaseCallback()");
1427}
1428
1429extern "C"
1430JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol, bool bBluetooth )
1431{
1432 LOGV("Stub HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface);
1433}
1434
1435extern "C"
1436JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID)
1437{
1438 LOGV("Stub HIDDeviceOpenPending() id=%d\n", nDeviceID);
1439}
1440
1441extern "C"
1442JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened)
1443{
1444 LOGV("Stub HIDDeviceOpenResult() id=%d, result=%s\n", nDeviceID, bOpened ? "true" : "false");
1445}
1446
1447extern "C"
1448JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID)
1449{
1450 LOGV("Stub HIDDeviceDisconnected() id=%d\n", nDeviceID);
1451}
1452
1453extern "C"
1454JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
1455{
1456 LOGV("Stub HIDDeviceInput() id=%d len=%u\n", nDeviceID, nBufSize);
1457}
1458
1459extern "C"
1460JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReportResponse)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
1461{
1462 LOGV("Stub HIDDeviceReportResponse() id=%d len=%u\n", nDeviceID, nBufSize);
1463}
1464
1465#endif /* SDL_HIDAPI_DISABLED */
1466
1467extern "C"
1468JNINativeMethod HIDDeviceManager_tab[8] = {
1469 { "HIDDeviceRegisterCallback", "()V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback) },
1470 { "HIDDeviceReleaseCallback", "()V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback) },
1471 { "HIDDeviceConnected", "(ILjava/lang/String;IILjava/lang/String;ILjava/lang/String;Ljava/lang/String;IIIIZ)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected) },
1472 { "HIDDeviceOpenPending", "(I)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending) },
1473 { "HIDDeviceOpenResult", "(IZ)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult) },
1474 { "HIDDeviceDisconnected", "(I)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected) },
1475 { "HIDDeviceInputReport", "(I[B)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport) },
1476 { "HIDDeviceReportResponse", "(I[B)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReportResponse) }
1477};
diff --git a/contrib/SDL-3.2.8/src/hidapi/android/hid.h b/contrib/SDL-3.2.8/src/hidapi/android/hid.h
new file mode 100644
index 0000000..5e6253b
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/hidapi/android/hid.h
@@ -0,0 +1,39 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 2022 Valve Corporation
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
22// Purpose: Exporting table containing HIDDeviceManager native methods
23
24#ifndef SDL_android_hid_h_
25#define SDL_android_hid_h_
26
27#include <jni.h>
28
29#ifdef __cplusplus
30extern "C" {
31#endif
32
33extern JNINativeMethod HIDDeviceManager_tab[8];
34
35#ifdef __cplusplus
36}
37#endif
38
39#endif