1 //=================== Copyright Valve Corporation, All rights reserved. =======
2 //
3 // Purpose: A wrapper implementing "HID" API for Android
4 //
5 // This layer glues the hidapi API to Android's USB and BLE stack.
6 //
7 //=============================================================================
8
9 #include <jni.h>
10 #include <android/log.h>
11 #include <pthread.h>
12 #include <errno.h> // For ETIMEDOUT and ECONNRESET
13 #include <stdlib.h> // For malloc() and free()
14 #include <string.h> // For memcpy()
15
16 #define TAG "hidapi"
17
18 // Have error log always available
19 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
20
21 #ifdef DEBUG
22 #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
23 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
24 #else
25 #define LOGV(...)
26 #define LOGD(...)
27 #endif
28
29 #define SDL_JAVA_PREFIX org_libsdl_app
30 #define CONCAT1(prefix, class, function) CONCAT2(prefix, class, function)
31 #define CONCAT2(prefix, class, function) Java_ ## prefix ## _ ## class ## _ ## function
32 #define HID_DEVICE_MANAGER_JAVA_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, HIDDeviceManager, function)
33
34 #include "../hidapi/hidapi.h"
35
36 typedef uint32_t uint32;
37 typedef uint64_t uint64;
38
39
40 struct hid_device_
41 {
42 int m_nId;
43 int m_nDeviceRefCount;
44 };
45
46 static JavaVM *g_JVM;
47 static pthread_key_t g_ThreadKey;
48
49 template<class T>
50 class hid_device_ref
51 {
52 public:
hid_device_ref(T * pObject=nullptr)53 hid_device_ref( T *pObject = nullptr ) : m_pObject( nullptr )
54 {
55 SetObject( pObject );
56 }
57
hid_device_ref(const hid_device_ref & rhs)58 hid_device_ref( const hid_device_ref &rhs ) : m_pObject( nullptr )
59 {
60 SetObject( rhs.GetObject() );
61 }
62
~hid_device_ref()63 ~hid_device_ref()
64 {
65 SetObject( nullptr );
66 }
67
SetObject(T * pObject)68 void SetObject( T *pObject )
69 {
70 if ( m_pObject && m_pObject->DecrementRefCount() == 0 )
71 {
72 delete m_pObject;
73 }
74
75 m_pObject = pObject;
76
77 if ( m_pObject )
78 {
79 m_pObject->IncrementRefCount();
80 }
81 }
82
operator =(T * pObject)83 hid_device_ref &operator =( T *pObject )
84 {
85 SetObject( pObject );
86 return *this;
87 }
88
operator =(const hid_device_ref & rhs)89 hid_device_ref &operator =( const hid_device_ref &rhs )
90 {
91 SetObject( rhs.GetObject() );
92 return *this;
93 }
94
GetObject() const95 T *GetObject() const
96 {
97 return m_pObject;
98 }
99
operator ->() const100 T* operator->() const
101 {
102 return m_pObject;
103 }
104
105 operator bool() const
106 {
107 return ( m_pObject != nullptr );
108 }
109
110 private:
111 T *m_pObject;
112 };
113
114 class hid_mutex_guard
115 {
116 public:
hid_mutex_guard(pthread_mutex_t * pMutex)117 hid_mutex_guard( pthread_mutex_t *pMutex ) : m_pMutex( pMutex )
118 {
119 pthread_mutex_lock( m_pMutex );
120 }
~hid_mutex_guard()121 ~hid_mutex_guard()
122 {
123 pthread_mutex_unlock( m_pMutex );
124 }
125
126 private:
127 pthread_mutex_t *m_pMutex;
128 };
129
130 class hid_buffer
131 {
132 public:
hid_buffer()133 hid_buffer() : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
134 {
135 }
136
hid_buffer(const uint8_t * pData,size_t nSize)137 hid_buffer( const uint8_t *pData, size_t nSize ) : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
138 {
139 assign( pData, nSize );
140 }
141
~hid_buffer()142 ~hid_buffer()
143 {
144 delete[] m_pData;
145 }
146
assign(const uint8_t * pData,size_t nSize)147 void assign( const uint8_t *pData, size_t nSize )
148 {
149 if ( nSize > m_nAllocated )
150 {
151 delete[] m_pData;
152 m_pData = new uint8_t[ nSize ];
153 m_nAllocated = nSize;
154 }
155
156 m_nSize = nSize;
157 memcpy( m_pData, pData, nSize );
158 }
159
clear()160 void clear()
161 {
162 m_nSize = 0;
163 }
164
size() const165 size_t size() const
166 {
167 return m_nSize;
168 }
169
data() const170 const uint8_t *data() const
171 {
172 return m_pData;
173 }
174
175 private:
176 uint8_t *m_pData;
177 size_t m_nSize;
178 size_t m_nAllocated;
179 };
180
181 class hid_buffer_pool
182 {
183 public:
hid_buffer_pool()184 hid_buffer_pool() : m_nSize( 0 ), m_pHead( nullptr ), m_pTail( nullptr ), m_pFree( nullptr )
185 {
186 }
187
~hid_buffer_pool()188 ~hid_buffer_pool()
189 {
190 clear();
191
192 while ( m_pFree )
193 {
194 hid_buffer_entry *pEntry = m_pFree;
195 m_pFree = m_pFree->m_pNext;
196 delete pEntry;
197 }
198 }
199
size() const200 size_t size() const { return m_nSize; }
201
front() const202 const hid_buffer &front() const { return m_pHead->m_buffer; }
203
pop_front()204 void pop_front()
205 {
206 hid_buffer_entry *pEntry = m_pHead;
207 if ( pEntry )
208 {
209 m_pHead = pEntry->m_pNext;
210 if ( !m_pHead )
211 {
212 m_pTail = nullptr;
213 }
214 pEntry->m_pNext = m_pFree;
215 m_pFree = pEntry;
216 --m_nSize;
217 }
218 }
219
emplace_back(const uint8_t * pData,size_t nSize)220 void emplace_back( const uint8_t *pData, size_t nSize )
221 {
222 hid_buffer_entry *pEntry;
223
224 if ( m_pFree )
225 {
226 pEntry = m_pFree;
227 m_pFree = m_pFree->m_pNext;
228 }
229 else
230 {
231 pEntry = new hid_buffer_entry;
232 }
233 pEntry->m_pNext = nullptr;
234
235 if ( m_pTail )
236 {
237 m_pTail->m_pNext = pEntry;
238 }
239 else
240 {
241 m_pHead = pEntry;
242 }
243 m_pTail = pEntry;
244
245 pEntry->m_buffer.assign( pData, nSize );
246 ++m_nSize;
247 }
248
clear()249 void clear()
250 {
251 while ( size() > 0 )
252 {
253 pop_front();
254 }
255 }
256
257 private:
258 struct hid_buffer_entry
259 {
260 hid_buffer m_buffer;
261 hid_buffer_entry *m_pNext;
262 };
263
264 size_t m_nSize;
265 hid_buffer_entry *m_pHead;
266 hid_buffer_entry *m_pTail;
267 hid_buffer_entry *m_pFree;
268 };
269
NewByteArray(JNIEnv * env,const uint8_t * pData,size_t nDataLen)270 static jbyteArray NewByteArray( JNIEnv* env, const uint8_t *pData, size_t nDataLen )
271 {
272 jbyteArray array = env->NewByteArray( nDataLen );
273 jbyte *pBuf = env->GetByteArrayElements( array, NULL );
274 memcpy( pBuf, pData, nDataLen );
275 env->ReleaseByteArrayElements( array, pBuf, 0 );
276
277 return array;
278 }
279
CreateStringFromJString(JNIEnv * env,const jstring & sString)280 static char *CreateStringFromJString( JNIEnv *env, const jstring &sString )
281 {
282 size_t nLength = env->GetStringUTFLength( sString );
283 const char *pjChars = env->GetStringUTFChars( sString, NULL );
284 char *psString = (char*)malloc( nLength + 1 );
285 memcpy( psString, pjChars, nLength );
286 psString[ nLength ] = '\0';
287 env->ReleaseStringUTFChars( sString, pjChars );
288 return psString;
289 }
290
CreateWStringFromJString(JNIEnv * env,const jstring & sString)291 static wchar_t *CreateWStringFromJString( JNIEnv *env, const jstring &sString )
292 {
293 size_t nLength = env->GetStringLength( sString );
294 const jchar *pjChars = env->GetStringChars( sString, NULL );
295 wchar_t *pwString = (wchar_t*)malloc( ( nLength + 1 ) * sizeof( wchar_t ) );
296 wchar_t *pwChars = pwString;
297 for ( size_t iIndex = 0; iIndex < nLength; ++iIndex )
298 {
299 pwChars[ iIndex ] = pjChars[ iIndex ];
300 }
301 pwString[ nLength ] = '\0';
302 env->ReleaseStringChars( sString, pjChars );
303 return pwString;
304 }
305
CreateWStringFromWString(const wchar_t * pwSrc)306 static wchar_t *CreateWStringFromWString( const wchar_t *pwSrc )
307 {
308 size_t nLength = wcslen( pwSrc );
309 wchar_t *pwString = (wchar_t*)malloc( ( nLength + 1 ) * sizeof( wchar_t ) );
310 memcpy( pwString, pwSrc, nLength * sizeof( wchar_t ) );
311 pwString[ nLength ] = '\0';
312 return pwString;
313 }
314
CopyHIDDeviceInfo(const hid_device_info * pInfo)315 static hid_device_info *CopyHIDDeviceInfo( const hid_device_info *pInfo )
316 {
317 hid_device_info *pCopy = new hid_device_info;
318 *pCopy = *pInfo;
319 pCopy->path = strdup( pInfo->path );
320 pCopy->product_string = CreateWStringFromWString( pInfo->product_string );
321 pCopy->manufacturer_string = CreateWStringFromWString( pInfo->manufacturer_string );
322 pCopy->serial_number = CreateWStringFromWString( pInfo->serial_number );
323 return pCopy;
324 }
325
FreeHIDDeviceInfo(hid_device_info * pInfo)326 static void FreeHIDDeviceInfo( hid_device_info *pInfo )
327 {
328 free( pInfo->path );
329 free( pInfo->serial_number );
330 free( pInfo->manufacturer_string );
331 free( pInfo->product_string );
332 delete pInfo;
333 }
334
335 static jclass g_HIDDeviceManagerCallbackClass;
336 static jobject g_HIDDeviceManagerCallbackHandler;
337 static jmethodID g_midHIDDeviceManagerOpen;
338 static jmethodID g_midHIDDeviceManagerSendOutputReport;
339 static jmethodID g_midHIDDeviceManagerSendFeatureReport;
340 static jmethodID g_midHIDDeviceManagerGetFeatureReport;
341 static jmethodID g_midHIDDeviceManagerClose;
342
get_timespec_ms(const struct timespec & ts)343 static uint64_t get_timespec_ms( const struct timespec &ts )
344 {
345 return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
346 }
347
348 class CHIDDevice
349 {
350 public:
CHIDDevice(int nDeviceID,hid_device_info * pInfo)351 CHIDDevice( int nDeviceID, hid_device_info *pInfo )
352 {
353 m_nId = nDeviceID;
354 m_pInfo = pInfo;
355
356 // The Bluetooth Steam Controller needs special handling
357 const int VALVE_USB_VID = 0x28DE;
358 const int D0G_BLE2_PID = 0x1106;
359 if ( pInfo->vendor_id == VALVE_USB_VID && pInfo->product_id == D0G_BLE2_PID )
360 {
361 m_bIsBLESteamController = true;
362 }
363 }
364
~CHIDDevice()365 ~CHIDDevice()
366 {
367 FreeHIDDeviceInfo( m_pInfo );
368
369 // Note that we don't delete m_pDevice, as the app may still have a reference to it
370 }
371
IncrementRefCount()372 int IncrementRefCount()
373 {
374 int nValue;
375 pthread_mutex_lock( &m_refCountLock );
376 nValue = ++m_nRefCount;
377 pthread_mutex_unlock( &m_refCountLock );
378 return nValue;
379 }
380
DecrementRefCount()381 int DecrementRefCount()
382 {
383 int nValue;
384 pthread_mutex_lock( &m_refCountLock );
385 nValue = --m_nRefCount;
386 pthread_mutex_unlock( &m_refCountLock );
387 return nValue;
388 }
389
GetId()390 int GetId()
391 {
392 return m_nId;
393 }
394
GetDeviceInfo()395 const hid_device_info *GetDeviceInfo()
396 {
397 return m_pInfo;
398 }
399
GetDevice()400 hid_device *GetDevice()
401 {
402 return m_pDevice;
403 }
404
ExceptionCheck(JNIEnv * env,const char * pszMethodName)405 void ExceptionCheck( JNIEnv *env, const char *pszMethodName )
406 {
407 if ( env->ExceptionCheck() )
408 {
409 // Get our exception
410 jthrowable jExcept = env->ExceptionOccurred();
411
412 // Clear the exception so we can call JNI again
413 env->ExceptionClear();
414
415 // Get our exception message
416 jclass jExceptClass = env->GetObjectClass( jExcept );
417 jmethodID jMessageMethod = env->GetMethodID( jExceptClass, "getMessage", "()Ljava/lang/String;" );
418 jstring jMessage = (jstring)( env->CallObjectMethod( jExcept, jMessageMethod ) );
419 const char *pszMessage = env->GetStringUTFChars( jMessage, NULL );
420
421 // ...and log it.
422 LOGE( "CHIDDevice::%s threw an exception: %s", pszMethodName, pszMessage );
423
424 // Cleanup
425 env->ReleaseStringUTFChars( jMessage, pszMessage );
426 env->DeleteLocalRef( jMessage );
427 env->DeleteLocalRef( jExceptClass );
428 env->DeleteLocalRef( jExcept );
429 }
430 }
431
BOpen()432 bool BOpen()
433 {
434 // Make sure thread is attached to JVM/env
435 JNIEnv *env;
436 g_JVM->AttachCurrentThread( &env, NULL );
437 pthread_setspecific( g_ThreadKey, (void*)env );
438
439 if ( !g_HIDDeviceManagerCallbackHandler )
440 {
441 LOGV( "Device open without callback handler" );
442 return false;
443 }
444
445 m_bIsWaitingForOpen = false;
446 m_bOpenResult = env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerOpen, m_nId );
447 ExceptionCheck( env, "BOpen" );
448
449 if ( m_bIsWaitingForOpen )
450 {
451 hid_mutex_guard cvl( &m_cvLock );
452
453 const int OPEN_TIMEOUT_SECONDS = 60;
454 struct timespec ts, endtime;
455 clock_gettime( CLOCK_REALTIME, &ts );
456 endtime = ts;
457 endtime.tv_sec += OPEN_TIMEOUT_SECONDS;
458 do
459 {
460 if ( pthread_cond_timedwait( &m_cv, &m_cvLock, &endtime ) != 0 )
461 {
462 break;
463 }
464 }
465 while ( m_bIsWaitingForOpen && get_timespec_ms( ts ) < get_timespec_ms( endtime ) );
466 }
467
468 if ( !m_bOpenResult )
469 {
470 if ( m_bIsWaitingForOpen )
471 {
472 LOGV( "Device open failed - timed out waiting for device permission" );
473 }
474 else
475 {
476 LOGV( "Device open failed" );
477 }
478 return false;
479 }
480
481 m_pDevice = new hid_device;
482 m_pDevice->m_nId = m_nId;
483 m_pDevice->m_nDeviceRefCount = 1;
484 LOGD("Creating device %d (%p), refCount = 1\n", m_pDevice->m_nId, m_pDevice);
485 return true;
486 }
487
SetOpenPending()488 void SetOpenPending()
489 {
490 m_bIsWaitingForOpen = true;
491 }
492
SetOpenResult(bool bResult)493 void SetOpenResult( bool bResult )
494 {
495 if ( m_bIsWaitingForOpen )
496 {
497 m_bOpenResult = bResult;
498 m_bIsWaitingForOpen = false;
499 pthread_cond_signal( &m_cv );
500 }
501 }
502
ProcessInput(const uint8_t * pBuf,size_t nBufSize)503 void ProcessInput( const uint8_t *pBuf, size_t nBufSize )
504 {
505 hid_mutex_guard l( &m_dataLock );
506
507 size_t MAX_REPORT_QUEUE_SIZE = 16;
508 if ( m_vecData.size() >= MAX_REPORT_QUEUE_SIZE )
509 {
510 m_vecData.pop_front();
511 }
512 m_vecData.emplace_back( pBuf, nBufSize );
513 }
514
GetInput(unsigned char * data,size_t length)515 int GetInput( unsigned char *data, size_t length )
516 {
517 hid_mutex_guard l( &m_dataLock );
518
519 if ( m_vecData.size() == 0 )
520 {
521 // LOGV( "hid_read_timeout no data available" );
522 return 0;
523 }
524
525 const hid_buffer &buffer = m_vecData.front();
526 size_t nDataLen = buffer.size() > length ? length : buffer.size();
527 if ( m_bIsBLESteamController )
528 {
529 data[0] = 0x03;
530 memcpy( data + 1, buffer.data(), nDataLen );
531 ++nDataLen;
532 }
533 else
534 {
535 memcpy( data, buffer.data(), nDataLen );
536 }
537 m_vecData.pop_front();
538
539 // LOGV("Read %u bytes", nDataLen);
540 // LOGV("%02x %02x %02x %02x %02x %02x %02x %02x ....",
541 // data[0], data[1], data[2], data[3],
542 // data[4], data[5], data[6], data[7]);
543
544 return nDataLen;
545 }
546
SendOutputReport(const unsigned char * pData,size_t nDataLen)547 int SendOutputReport( const unsigned char *pData, size_t nDataLen )
548 {
549 // Make sure thread is attached to JVM/env
550 JNIEnv *env;
551 g_JVM->AttachCurrentThread( &env, NULL );
552 pthread_setspecific( g_ThreadKey, (void*)env );
553
554 int nRet = -1;
555 if ( g_HIDDeviceManagerCallbackHandler )
556 {
557 jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
558 nRet = env->CallIntMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerSendOutputReport, m_nId, pBuf );
559 ExceptionCheck( env, "SendOutputReport" );
560 env->DeleteLocalRef( pBuf );
561 }
562 else
563 {
564 LOGV( "SendOutputReport without callback handler" );
565 }
566 return nRet;
567 }
568
SendFeatureReport(const unsigned char * pData,size_t nDataLen)569 int SendFeatureReport( const unsigned char *pData, size_t nDataLen )
570 {
571 // Make sure thread is attached to JVM/env
572 JNIEnv *env;
573 g_JVM->AttachCurrentThread( &env, NULL );
574 pthread_setspecific( g_ThreadKey, (void*)env );
575
576 int nRet = -1;
577 if ( g_HIDDeviceManagerCallbackHandler )
578 {
579 jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
580 nRet = env->CallIntMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerSendFeatureReport, m_nId, pBuf );
581 ExceptionCheck( env, "SendFeatureReport" );
582 env->DeleteLocalRef( pBuf );
583 }
584 else
585 {
586 LOGV( "SendFeatureReport without callback handler" );
587 }
588 return nRet;
589 }
590
ProcessFeatureReport(const uint8_t * pBuf,size_t nBufSize)591 void ProcessFeatureReport( const uint8_t *pBuf, size_t nBufSize )
592 {
593 hid_mutex_guard cvl( &m_cvLock );
594 if ( m_bIsWaitingForFeatureReport )
595 {
596 m_featureReport.assign( pBuf, nBufSize );
597
598 m_bIsWaitingForFeatureReport = false;
599 m_nFeatureReportError = 0;
600 pthread_cond_signal( &m_cv );
601 }
602 }
603
GetFeatureReport(unsigned char * pData,size_t nDataLen)604 int GetFeatureReport( unsigned char *pData, size_t nDataLen )
605 {
606 // Make sure thread is attached to JVM/env
607 JNIEnv *env;
608 g_JVM->AttachCurrentThread( &env, NULL );
609 pthread_setspecific( g_ThreadKey, (void*)env );
610
611 if ( !g_HIDDeviceManagerCallbackHandler )
612 {
613 LOGV( "GetFeatureReport without callback handler" );
614 return -1;
615 }
616
617 {
618 hid_mutex_guard cvl( &m_cvLock );
619 if ( m_bIsWaitingForFeatureReport )
620 {
621 LOGV( "Get feature report already ongoing... bail" );
622 return -1; // Read already ongoing, we currently do not serialize, TODO
623 }
624 m_bIsWaitingForFeatureReport = true;
625 }
626
627 jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
628 int nRet = env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerGetFeatureReport, m_nId, pBuf ) ? 0 : -1;
629 ExceptionCheck( env, "GetFeatureReport" );
630 env->DeleteLocalRef( pBuf );
631 if ( nRet < 0 )
632 {
633 LOGV( "GetFeatureReport failed" );
634 m_bIsWaitingForFeatureReport = false;
635 return -1;
636 }
637
638 {
639 hid_mutex_guard cvl( &m_cvLock );
640 if ( m_bIsWaitingForFeatureReport )
641 {
642 LOGV("=== Going to sleep" );
643 // Wait in CV until we are no longer waiting for a feature report.
644 const int FEATURE_REPORT_TIMEOUT_SECONDS = 2;
645 struct timespec ts, endtime;
646 clock_gettime( CLOCK_REALTIME, &ts );
647 endtime = ts;
648 endtime.tv_sec += FEATURE_REPORT_TIMEOUT_SECONDS;
649 do
650 {
651 if ( pthread_cond_timedwait( &m_cv, &m_cvLock, &endtime ) != 0 )
652 {
653 break;
654 }
655 }
656 while ( m_bIsWaitingForFeatureReport && get_timespec_ms( ts ) < get_timespec_ms( endtime ) );
657
658 // We are back
659 if ( m_bIsWaitingForFeatureReport )
660 {
661 m_nFeatureReportError = -ETIMEDOUT;
662 m_bIsWaitingForFeatureReport = false;
663 }
664 LOGV( "=== Got feature report err=%d", m_nFeatureReportError );
665 if ( m_nFeatureReportError != 0 )
666 {
667 return m_nFeatureReportError;
668 }
669 }
670
671 size_t uBytesToCopy = m_featureReport.size() > nDataLen ? nDataLen : m_featureReport.size();
672 memcpy( pData, m_featureReport.data(), uBytesToCopy );
673 m_featureReport.clear();
674 LOGV( "=== Got %u bytes", uBytesToCopy );
675
676 return uBytesToCopy;
677 }
678 }
679
Close(bool bDeleteDevice)680 void Close( bool bDeleteDevice )
681 {
682 // Make sure thread is attached to JVM/env
683 JNIEnv *env;
684 g_JVM->AttachCurrentThread( &env, NULL );
685 pthread_setspecific( g_ThreadKey, (void*)env );
686
687 if ( g_HIDDeviceManagerCallbackHandler )
688 {
689 env->CallVoidMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerClose, m_nId );
690 ExceptionCheck( env, "Close" );
691 }
692
693 hid_mutex_guard dataLock( &m_dataLock );
694 m_vecData.clear();
695
696 // Clean and release pending feature report reads
697 hid_mutex_guard cvLock( &m_cvLock );
698 m_featureReport.clear();
699 m_bIsWaitingForFeatureReport = false;
700 m_nFeatureReportError = -ECONNRESET;
701 pthread_cond_broadcast( &m_cv );
702
703 if ( bDeleteDevice )
704 {
705 delete m_pDevice;
706 m_pDevice = nullptr;
707 }
708 }
709
710 private:
711 pthread_mutex_t m_refCountLock = PTHREAD_MUTEX_INITIALIZER;
712 int m_nRefCount = 0;
713 int m_nId = 0;
714 hid_device_info *m_pInfo = nullptr;
715 hid_device *m_pDevice = nullptr;
716 bool m_bIsBLESteamController = false;
717
718 pthread_mutex_t m_dataLock = PTHREAD_MUTEX_INITIALIZER; // This lock has to be held to access m_vecData
719 hid_buffer_pool m_vecData;
720
721 // For handling get_feature_report
722 pthread_mutex_t m_cvLock = PTHREAD_MUTEX_INITIALIZER; // This lock has to be held to access any variables below
723 pthread_cond_t m_cv = PTHREAD_COND_INITIALIZER;
724 bool m_bIsWaitingForOpen = false;
725 bool m_bOpenResult = false;
726 bool m_bIsWaitingForFeatureReport = false;
727 int m_nFeatureReportError = 0;
728 hid_buffer m_featureReport;
729
730 public:
731 hid_device_ref<CHIDDevice> next;
732 };
733
734 class CHIDDevice;
735 static pthread_mutex_t g_DevicesMutex = PTHREAD_MUTEX_INITIALIZER;
736 static pthread_mutex_t g_DevicesRefCountMutex = PTHREAD_MUTEX_INITIALIZER;
737 static hid_device_ref<CHIDDevice> g_Devices;
738
FindDevice(int nDeviceId)739 static hid_device_ref<CHIDDevice> FindDevice( int nDeviceId )
740 {
741 hid_device_ref<CHIDDevice> pDevice;
742
743 hid_mutex_guard l( &g_DevicesMutex );
744 for ( pDevice = g_Devices; pDevice; pDevice = pDevice->next )
745 {
746 if ( pDevice->GetId() == nDeviceId )
747 {
748 break;
749 }
750 }
751 return pDevice;
752 }
753
ThreadDestroyed(void * value)754 static void ThreadDestroyed(void* value)
755 {
756 /* The thread is being destroyed, detach it from the Java VM and set the g_ThreadKey value to NULL as required */
757 JNIEnv *env = (JNIEnv*) value;
758 if (env != NULL) {
759 g_JVM->DetachCurrentThread();
760 pthread_setspecific(g_ThreadKey, NULL);
761 }
762 }
763
764
765 extern "C"
766 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz);
767
768 extern "C"
769 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz);
770
771 extern "C"
772 JNIEXPORT 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 );
773
774 extern "C"
775 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID);
776
777 extern "C"
778 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened);
779
780 extern "C"
781 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID);
782
783 extern "C"
784 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
785
786 extern "C"
787 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
788
789
790 extern "C"
HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)791 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz )
792 {
793 LOGV( "HIDDeviceRegisterCallback()");
794
795 env->GetJavaVM( &g_JVM );
796
797 /*
798 * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
799 * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
800 */
801 if (pthread_key_create(&g_ThreadKey, ThreadDestroyed) != 0) {
802 __android_log_print(ANDROID_LOG_ERROR, TAG, "Error initializing pthread key");
803 }
804
805 if ( g_HIDDeviceManagerCallbackHandler != NULL )
806 {
807 env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
808 g_HIDDeviceManagerCallbackClass = NULL;
809 env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
810 g_HIDDeviceManagerCallbackHandler = NULL;
811 }
812
813 g_HIDDeviceManagerCallbackHandler = env->NewGlobalRef( thiz );
814 jclass objClass = env->GetObjectClass( thiz );
815 if ( objClass )
816 {
817 g_HIDDeviceManagerCallbackClass = reinterpret_cast< jclass >( env->NewGlobalRef( objClass ) );
818 g_midHIDDeviceManagerOpen = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "openDevice", "(I)Z" );
819 if ( !g_midHIDDeviceManagerOpen )
820 {
821 __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing openDevice" );
822 }
823 g_midHIDDeviceManagerSendOutputReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "sendOutputReport", "(I[B)I" );
824 if ( !g_midHIDDeviceManagerSendOutputReport )
825 {
826 __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing sendOutputReport" );
827 }
828 g_midHIDDeviceManagerSendFeatureReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "sendFeatureReport", "(I[B)I" );
829 if ( !g_midHIDDeviceManagerSendFeatureReport )
830 {
831 __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing sendFeatureReport" );
832 }
833 g_midHIDDeviceManagerGetFeatureReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "getFeatureReport", "(I[B)Z" );
834 if ( !g_midHIDDeviceManagerGetFeatureReport )
835 {
836 __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing getFeatureReport" );
837 }
838 g_midHIDDeviceManagerClose = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "closeDevice", "(I)V" );
839 if ( !g_midHIDDeviceManagerClose )
840 {
841 __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing closeDevice" );
842 }
843 env->DeleteLocalRef( objClass );
844 }
845 }
846
847 extern "C"
HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)848 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz)
849 {
850 LOGV("HIDDeviceReleaseCallback");
851 if ( env->IsSameObject( thiz, g_HIDDeviceManagerCallbackHandler ) )
852 {
853 env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
854 g_HIDDeviceManagerCallbackClass = NULL;
855 env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
856 g_HIDDeviceManagerCallbackHandler = NULL;
857 }
858 }
859
860 extern "C"
HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)861 JNIEXPORT 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 )
862 {
863 LOGV( "HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface );
864
865 hid_device_info *pInfo = new hid_device_info;
866 memset( pInfo, 0, sizeof( *pInfo ) );
867 pInfo->path = CreateStringFromJString( env, sIdentifier );
868 pInfo->vendor_id = nVendorId;
869 pInfo->product_id = nProductId;
870 pInfo->serial_number = CreateWStringFromJString( env, sSerialNumber );
871 pInfo->release_number = nReleaseNumber;
872 pInfo->manufacturer_string = CreateWStringFromJString( env, sManufacturer );
873 pInfo->product_string = CreateWStringFromJString( env, sProduct );
874 pInfo->interface_number = nInterface;
875 pInfo->interface_class = nInterfaceClass;
876 pInfo->interface_subclass = nInterfaceSubclass;
877 pInfo->interface_protocol = nInterfaceProtocol;
878
879 hid_device_ref<CHIDDevice> pDevice( new CHIDDevice( nDeviceID, pInfo ) );
880
881 hid_mutex_guard l( &g_DevicesMutex );
882 hid_device_ref<CHIDDevice> pLast, pCurr;
883 for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->next )
884 {
885 continue;
886 }
887 if ( pLast )
888 {
889 pLast->next = pDevice;
890 }
891 else
892 {
893 g_Devices = pDevice;
894 }
895 }
896
897 extern "C"
HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)898 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID)
899 {
900 LOGV( "HIDDeviceOpenPending() id=%d\n", nDeviceID );
901 hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
902 if ( pDevice )
903 {
904 pDevice->SetOpenPending();
905 }
906 }
907
908 extern "C"
HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)909 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened)
910 {
911 LOGV( "HIDDeviceOpenResult() id=%d, result=%s\n", nDeviceID, bOpened ? "true" : "false" );
912 hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
913 if ( pDevice )
914 {
915 pDevice->SetOpenResult( bOpened );
916 }
917 }
918
919 extern "C"
HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)920 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID)
921 {
922 LOGV( "HIDDeviceDisconnected() id=%d\n", nDeviceID );
923 hid_device_ref<CHIDDevice> pDevice;
924 {
925 hid_mutex_guard l( &g_DevicesMutex );
926 hid_device_ref<CHIDDevice> pLast, pCurr;
927 for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->next )
928 {
929 if ( pCurr->GetId() == nDeviceID )
930 {
931 pDevice = pCurr;
932
933 if ( pLast )
934 {
935 pLast->next = pCurr->next;
936 }
937 else
938 {
939 g_Devices = pCurr->next;
940 }
941 }
942 }
943 }
944 if ( pDevice )
945 {
946 pDevice->Close( false );
947 }
948 }
949
950 extern "C"
HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)951 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
952 {
953 jbyte *pBuf = env->GetByteArrayElements(value, NULL);
954 jsize nBufSize = env->GetArrayLength(value);
955
956 // LOGV( "HIDDeviceInput() id=%d len=%u\n", nDeviceID, nBufSize );
957 hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
958 if ( pDevice )
959 {
960 pDevice->ProcessInput( reinterpret_cast< const uint8_t* >( pBuf ), nBufSize );
961 }
962
963 env->ReleaseByteArrayElements(value, pBuf, 0);
964 }
965
966 extern "C"
HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)967 JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
968 {
969 jbyte *pBuf = env->GetByteArrayElements(value, NULL);
970 jsize nBufSize = env->GetArrayLength(value);
971
972 LOGV( "HIDDeviceFeatureReport() id=%d len=%u\n", nDeviceID, nBufSize );
973 hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
974 if ( pDevice )
975 {
976 pDevice->ProcessFeatureReport( reinterpret_cast< const uint8_t* >( pBuf ), nBufSize );
977 }
978
979 env->ReleaseByteArrayElements(value, pBuf, 0);
980 }
981
982 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
983 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
984 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
985
986 extern "C"
987 {
988
hid_init(void)989 int hid_init(void)
990 {
991 return 0;
992 }
993
hid_enumerate(unsigned short vendor_id,unsigned short product_id)994 struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
995 {
996 struct hid_device_info *root = NULL;
997 hid_mutex_guard l( &g_DevicesMutex );
998 for ( hid_device_ref<CHIDDevice> pDevice = g_Devices; pDevice; pDevice = pDevice->next )
999 {
1000 const hid_device_info *info = pDevice->GetDeviceInfo();
1001 if ( ( vendor_id == 0 && product_id == 0 ) ||
1002 ( vendor_id == info->vendor_id && product_id == info->product_id ) )
1003 {
1004 hid_device_info *dev = CopyHIDDeviceInfo( info );
1005 dev->next = root;
1006 root = dev;
1007 }
1008 }
1009 return root;
1010 }
1011
hid_free_enumeration(struct hid_device_info * devs)1012 void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
1013 {
1014 while ( devs )
1015 {
1016 struct hid_device_info *next = devs->next;
1017 FreeHIDDeviceInfo( devs );
1018 devs = next;
1019 }
1020 }
1021
hid_open(unsigned short vendor_id,unsigned short product_id,const wchar_t * serial_number)1022 HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
1023 {
1024 // TODO: Implement
1025 return NULL;
1026 }
1027
hid_open_path(const char * path,int bExclusive)1028 HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bExclusive)
1029 {
1030 LOGV( "hid_open_path( %s )", path );
1031
1032 hid_device_ref< CHIDDevice > pDevice;
1033 {
1034 hid_mutex_guard r( &g_DevicesRefCountMutex );
1035 hid_mutex_guard l( &g_DevicesMutex );
1036 for ( hid_device_ref<CHIDDevice> pCurr = g_Devices; pCurr; pCurr = pCurr->next )
1037 {
1038 if ( strcmp( pCurr->GetDeviceInfo()->path, path ) == 0 )
1039 {
1040 hid_device *pValue = pCurr->GetDevice();
1041 if ( pValue )
1042 {
1043 ++pValue->m_nDeviceRefCount;
1044 LOGD("Incrementing device %d (%p), refCount = %d\n", pValue->m_nId, pValue, pValue->m_nDeviceRefCount);
1045 return pValue;
1046 }
1047
1048 // Hold a shared pointer to the controller for the duration
1049 pDevice = pCurr;
1050 break;
1051 }
1052 }
1053 }
1054 if ( pDevice && pDevice->BOpen() )
1055 {
1056 return pDevice->GetDevice();
1057 }
1058 return NULL;
1059 }
1060
hid_write(hid_device * device,const unsigned char * data,size_t length)1061 int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length)
1062 {
1063 if ( device )
1064 {
1065 LOGV( "hid_write id=%d length=%u", device->m_nId, length );
1066 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1067 if ( pDevice )
1068 {
1069 return pDevice->SendOutputReport( data, length );
1070 }
1071 }
1072 return -1; // Controller was disconnected
1073 }
1074
1075 // TODO: Implement timeout?
hid_read_timeout(hid_device * device,unsigned char * data,size_t length,int milliseconds)1076 int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
1077 {
1078 if ( device )
1079 {
1080 // LOGV( "hid_read_timeout id=%d length=%u timeout=%d", device->m_nId, length, milliseconds );
1081 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1082 if ( pDevice )
1083 {
1084 return pDevice->GetInput( data, length );
1085 }
1086 LOGV( "controller was disconnected" );
1087 }
1088 return -1; // Controller was disconnected
1089 }
1090
1091 // TODO: Implement blocking
hid_read(hid_device * device,unsigned char * data,size_t length)1092 int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length)
1093 {
1094 LOGV( "hid_read id=%d length=%u", device->m_nId, length );
1095 return hid_read_timeout( device, data, length, 0 );
1096 }
1097
1098 // TODO: Implement?
hid_set_nonblocking(hid_device * device,int nonblock)1099 int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock)
1100 {
1101 return -1;
1102 }
1103
hid_send_feature_report(hid_device * device,const unsigned char * data,size_t length)1104 int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length)
1105 {
1106 if ( device )
1107 {
1108 LOGV( "hid_send_feature_report id=%d length=%u", device->m_nId, length );
1109 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1110 if ( pDevice )
1111 {
1112 return pDevice->SendFeatureReport( data, length );
1113 }
1114 }
1115 return -1; // Controller was disconnected
1116 }
1117
1118
1119 // Synchronous operation. Will block until completed.
hid_get_feature_report(hid_device * device,unsigned char * data,size_t length)1120 int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length)
1121 {
1122 if ( device )
1123 {
1124 LOGV( "hid_get_feature_report id=%d length=%u", device->m_nId, length );
1125 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1126 if ( pDevice )
1127 {
1128 return pDevice->GetFeatureReport( data, length );
1129 }
1130 }
1131 return -1; // Controller was disconnected
1132 }
1133
1134
hid_close(hid_device * device)1135 void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device)
1136 {
1137 if ( device )
1138 {
1139 LOGV( "hid_close id=%d", device->m_nId );
1140 hid_mutex_guard r( &g_DevicesRefCountMutex );
1141 LOGD("Decrementing device %d (%p), refCount = %d\n", device->m_nId, device, device->m_nDeviceRefCount - 1);
1142 if ( --device->m_nDeviceRefCount == 0 )
1143 {
1144 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1145 if ( pDevice )
1146 {
1147 pDevice->Close( true );
1148 }
1149 else
1150 {
1151 delete device;
1152 }
1153 LOGD("Deleted device %p\n", device);
1154 }
1155 }
1156 }
1157
hid_get_manufacturer_string(hid_device * device,wchar_t * string,size_t maxlen)1158 int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen)
1159 {
1160 if ( device )
1161 {
1162 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1163 if ( pDevice )
1164 {
1165 wcsncpy( string, pDevice->GetDeviceInfo()->manufacturer_string, maxlen );
1166 return 0;
1167 }
1168 }
1169 return -1;
1170 }
1171
hid_get_product_string(hid_device * device,wchar_t * string,size_t maxlen)1172 int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen)
1173 {
1174 if ( device )
1175 {
1176 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1177 if ( pDevice )
1178 {
1179 wcsncpy( string, pDevice->GetDeviceInfo()->product_string, maxlen );
1180 return 0;
1181 }
1182 }
1183 return -1;
1184 }
1185
hid_get_serial_number_string(hid_device * device,wchar_t * string,size_t maxlen)1186 int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen)
1187 {
1188 if ( device )
1189 {
1190 hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
1191 if ( pDevice )
1192 {
1193 wcsncpy( string, pDevice->GetDeviceInfo()->serial_number, maxlen );
1194 return 0;
1195 }
1196 }
1197 return -1;
1198 }
1199
hid_get_indexed_string(hid_device * device,int string_index,wchar_t * string,size_t maxlen)1200 int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen)
1201 {
1202 return -1;
1203 }
1204
hid_error(hid_device * device)1205 HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device)
1206 {
1207 return NULL;
1208 }
1209
hid_exit(void)1210 int hid_exit(void)
1211 {
1212 return 0;
1213 }
1214
1215 }
1216