10 #include <android/log.h> 19 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) 22 #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__) 23 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) 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) 34 #include "../hidapi/hidapi.h" 70 if ( m_pObject && m_pObject->DecrementRefCount() == 0 )
79 m_pObject->IncrementRefCount();
105 operator bool()
const 107 return ( m_pObject !=
nullptr );
119 pthread_mutex_lock( m_pMutex );
123 pthread_mutex_unlock( m_pMutex );
133 hid_buffer() : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
137 hid_buffer(
const uint8_t *pData,
size_t nSize ) : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
139 assign( pData, nSize );
149 if ( nSize > m_nAllocated )
152 m_pData =
new uint8_t[ nSize ];
153 m_nAllocated = nSize;
157 memcpy( m_pData, pData, nSize );
184 hid_buffer_pool() : m_nSize( 0 ), m_pHead( nullptr ), m_pTail( nullptr ), m_pFree( nullptr )
200 size_t size()
const {
return m_nSize; }
272 jbyteArray
array = env->NewByteArray( nDataLen );
273 jbyte *pBuf = env->GetByteArrayElements( array,
NULL );
274 memcpy( pBuf, pData, nDataLen );
275 env->ReleaseByteArrayElements( array, pBuf, 0 );
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 );
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 )
299 pwChars[ iIndex ] = pjChars[ iIndex ];
301 pwString[ nLength ] =
'\0';
302 env->ReleaseStringChars( sString, pjChars );
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';
319 pCopy->
path = strdup( pInfo->
path );
345 return (
uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
357 const int VALVE_USB_VID = 0x28DE;
358 const int D0G_BLE2_PID = 0x1106;
361 m_bIsBLESteamController =
true;
375 pthread_mutex_lock( &m_refCountLock );
376 nValue = ++m_nRefCount;
377 pthread_mutex_unlock( &m_refCountLock );
384 pthread_mutex_lock( &m_refCountLock );
385 nValue = --m_nRefCount;
386 pthread_mutex_unlock( &m_refCountLock );
407 if ( env->ExceptionCheck() )
410 jthrowable jExcept = env->ExceptionOccurred();
413 env->ExceptionClear();
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 );
422 LOGE(
"CHIDDevice::%s threw an exception: %s", pszMethodName, pszMessage );
425 env->ReleaseStringUTFChars( jMessage, pszMessage );
426 env->DeleteLocalRef( jMessage );
427 env->DeleteLocalRef( jExceptClass );
428 env->DeleteLocalRef( jExcept );
436 g_JVM->AttachCurrentThread( &env,
NULL );
439 m_bIsWaitingForOpen =
false;
441 ExceptionCheck( env,
"BOpen" );
443 if ( m_bIsWaitingForOpen )
447 const int OPEN_TIMEOUT_SECONDS = 60;
448 struct timespec ts, endtime;
449 clock_gettime( CLOCK_REALTIME, &ts );
451 endtime.tv_sec += OPEN_TIMEOUT_SECONDS;
454 if ( pthread_cond_timedwait( &m_cv, &m_cvLock, &endtime ) != 0 )
462 if ( !m_bOpenResult )
464 if ( m_bIsWaitingForOpen )
466 LOGV(
"Device open failed - timed out waiting for device permission" );
470 LOGV(
"Device open failed" );
475 m_pDevice =
new hid_device;
476 m_pDevice->m_nId =
m_nId;
477 m_pDevice->m_nDeviceRefCount = 1;
478 LOGD(
"Creating device %d (%p), refCount = 1\n", m_pDevice->m_nId, m_pDevice);
484 m_bIsWaitingForOpen =
true;
489 if ( m_bIsWaitingForOpen )
491 m_bOpenResult = bResult;
492 m_bIsWaitingForOpen =
false;
493 pthread_cond_signal( &m_cv );
501 size_t MAX_REPORT_QUEUE_SIZE = 16;
502 if ( m_vecData.size() >= MAX_REPORT_QUEUE_SIZE )
504 m_vecData.pop_front();
506 m_vecData.emplace_back( pBuf, nBufSize );
513 if ( m_vecData.size() == 0 )
520 size_t nDataLen = buffer.
size() > length ?
length : buffer.
size();
521 if ( m_bIsBLESteamController )
531 m_vecData.pop_front();
545 g_JVM->AttachCurrentThread( &env,
NULL );
550 ExceptionCheck( env,
"SendOutputReport" );
552 env->DeleteLocalRef( pBuf );
560 g_JVM->AttachCurrentThread( &env,
NULL );
565 ExceptionCheck( env,
"SendFeatureReport" );
566 env->DeleteLocalRef( pBuf );
573 if ( m_bIsWaitingForFeatureReport )
575 m_featureReport.assign( pBuf, nBufSize );
577 m_bIsWaitingForFeatureReport =
false;
578 m_nFeatureReportError = 0;
579 pthread_cond_signal( &m_cv );
587 g_JVM->AttachCurrentThread( &env,
NULL );
592 if ( m_bIsWaitingForFeatureReport )
594 LOGV(
"Get feature report already ongoing... bail" );
597 m_bIsWaitingForFeatureReport =
true;
602 ExceptionCheck( env,
"GetFeatureReport" );
603 env->DeleteLocalRef( pBuf );
606 LOGV(
"GetFeatureReport failed" );
607 m_bIsWaitingForFeatureReport =
false;
613 if ( m_bIsWaitingForFeatureReport )
615 LOGV(
"=== Going to sleep" );
617 const int FEATURE_REPORT_TIMEOUT_SECONDS = 2;
618 struct timespec ts, endtime;
619 clock_gettime( CLOCK_REALTIME, &ts );
621 endtime.tv_sec += FEATURE_REPORT_TIMEOUT_SECONDS;
624 if ( pthread_cond_timedwait( &m_cv, &m_cvLock, &endtime ) != 0 )
632 if ( m_bIsWaitingForFeatureReport )
634 m_nFeatureReportError = -ETIMEDOUT;
635 m_bIsWaitingForFeatureReport =
false;
637 LOGV(
"=== Got feature report err=%d", m_nFeatureReportError );
638 if ( m_nFeatureReportError != 0 )
640 return m_nFeatureReportError;
644 size_t uBytesToCopy = m_featureReport.size() > nDataLen ? nDataLen : m_featureReport.size();
645 memcpy( pData, m_featureReport.data(), uBytesToCopy );
646 m_featureReport.clear();
647 LOGV(
"=== Got %u bytes", uBytesToCopy );
657 g_JVM->AttachCurrentThread( &env,
NULL );
661 ExceptionCheck( env,
"Close" );
668 m_featureReport.clear();
669 m_bIsWaitingForFeatureReport =
false;
670 m_nFeatureReportError = -ECONNRESET;
671 pthread_cond_broadcast( &m_cv );
681 pthread_mutex_t m_refCountLock = PTHREAD_MUTEX_INITIALIZER;
685 hid_device *m_pDevice =
nullptr;
686 bool m_bIsBLESteamController =
false;
688 pthread_mutex_t m_dataLock = PTHREAD_MUTEX_INITIALIZER;
692 pthread_mutex_t m_cvLock = PTHREAD_MUTEX_INITIALIZER;
693 pthread_cond_t m_cv = PTHREAD_COND_INITIALIZER;
694 bool m_bIsWaitingForOpen =
false;
695 bool m_bOpenResult =
false;
696 bool m_bIsWaitingForFeatureReport =
false;
697 int m_nFeatureReportError = 0;
714 for ( pDevice = g_Devices; pDevice; pDevice = pDevice->
next )
716 if ( pDevice->
GetId() == nDeviceId )
727 JNIEnv *env = (JNIEnv*) value;
729 g_JVM->DetachCurrentThread();
742 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 );
763 LOGV(
"HIDDeviceRegisterCallback()");
765 env->GetJavaVM( &
g_JVM );
772 __android_log_print(ANDROID_LOG_ERROR,
TAG,
"Error initializing pthread key");
784 jclass objClass = env->GetObjectClass( thiz );
791 __android_log_print(ANDROID_LOG_ERROR,
TAG,
"HIDDeviceRegisterCallback: callback class missing openDevice" );
796 __android_log_print(ANDROID_LOG_ERROR,
TAG,
"HIDDeviceRegisterCallback: callback class missing sendOutputReport" );
801 __android_log_print(ANDROID_LOG_ERROR,
TAG,
"HIDDeviceRegisterCallback: callback class missing sendFeatureReport" );
806 __android_log_print(ANDROID_LOG_ERROR,
TAG,
"HIDDeviceRegisterCallback: callback class missing getFeatureReport" );
811 __android_log_print(ANDROID_LOG_ERROR,
TAG,
"HIDDeviceRegisterCallback: callback class missing closeDevice" );
813 env->DeleteLocalRef( objClass );
820 LOGV(
"HIDDeviceReleaseCallback");
831 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 )
833 LOGV(
"HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface );
836 memset( pInfo, 0,
sizeof( *pInfo ) );
850 for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->
next )
856 pLast->
next = pDevice;
867 LOGV(
"HIDDeviceOpenPending() id=%d\n", nDeviceID );
878 LOGV(
"HIDDeviceOpenResult() id=%d, result=%s\n", nDeviceID, bOpened ?
"true" :
"false" );
889 LOGV(
"HIDDeviceDisconnected() id=%d\n", nDeviceID );
894 for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->
next )
896 if ( pCurr->GetId() == nDeviceID )
906 g_Devices = pCurr->
next;
913 pDevice->
Close(
false );
920 jbyte *pBuf = env->GetByteArrayElements(
value,
NULL);
921 jsize nBufSize = env->GetArrayLength(
value);
927 pDevice->
ProcessInput( reinterpret_cast< const uint8_t* >( pBuf ), nBufSize );
930 env->ReleaseByteArrayElements(
value, pBuf, 0);
936 jbyte *pBuf = env->GetByteArrayElements(
value,
NULL);
937 jsize nBufSize = env->GetArrayLength(
value);
939 LOGV(
"HIDDeviceFeatureReport() id=%d len=%u\n", nDeviceID, nBufSize );
946 env->ReleaseByteArrayElements(
value, pBuf, 0);
968 if ( ( vendor_id == 0 && product_id == 0 ) ||
997 LOGV(
"hid_open_path( %s )", path );
1005 if ( strcmp( pCurr->GetDeviceInfo()->path,
path ) == 0 )
1007 hid_device *pValue = pCurr->GetDevice();
1010 ++pValue->m_nDeviceRefCount;
1011 LOGD(
"Incrementing device %d (%p), refCount = %d\n", pValue->m_nId, pValue, pValue->m_nDeviceRefCount);
1021 if ( pDevice && pDevice->
BOpen() )
1030 LOGV(
"hid_write id=%d length=%u", device->m_nId, length );
1046 return pDevice->
GetInput( data, length );
1048 LOGV(
"controller was disconnected" );
1055 LOGV(
"hid_read id=%d length=%u", device->m_nId, length );
1067 LOGV(
"hid_send_feature_report id=%d length=%u", device->m_nId, length );
1080 LOGV(
"hid_get_feature_report id=%d length=%u", device->m_nId, length );
1092 LOGV(
"hid_close id=%d", device->m_nId );
1094 LOGD(
"Decrementing device %d (%p), refCount = %d\n", device->m_nId, device, device->m_nDeviceRefCount - 1);
1095 if ( --device->m_nDeviceRefCount == 0 )
1100 pDevice->
Close(
true );
1106 LOGD(
"Deleted device %p\n", device);
#define HID_DEVICE_MANAGER_JAVA_INTERFACE(function)
HID_API_EXPORT const wchar_t *HID_API_CALL hid_error(hid_device *device)
Get a string describing the last error which occurred.
int GetInput(unsigned char *data, size_t length)
static jmethodID g_midHIDDeviceManagerOpen
GLdouble GLdouble GLdouble r
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE() HIDDeviceReleaseCallback(JNIEnv *env, jobject thiz)
static jmethodID g_midHIDDeviceManagerGetFeatureReport
const uint8_t * data() const
void assign(const uint8_t *pData, size_t nSize)
hid_buffer(const uint8_t *pData, size_t nSize)
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
Free an enumeration Linked List.
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE() HIDDeviceOpenPending(JNIEnv *env, jobject thiz, int nDeviceID)
const hid_device_info * GetDeviceInfo()
void ExceptionCheck(JNIEnv *env, const char *pszMethodName)
unsigned long long uint64_t
hid_buffer_entry * m_pHead
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE() HIDDeviceRegisterCallback(JNIEnv *env, jobject thiz)
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
hid_buffer m_featureReport
static jobject g_HIDDeviceManagerCallbackHandler
static jmethodID g_midHIDDeviceManagerSendOutputReport
hid_device_ref< CHIDDevice > next
const hid_buffer & front() const
wchar_t * manufacturer_string
int SendFeatureReport(const unsigned char *pData, size_t nDataLen)
hid_buffer_entry * m_pTail
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length)
Write an Output report to a HID device.
static pthread_mutex_t g_DevicesRefCountMutex
static jmethodID g_midHIDDeviceManagerClose
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen)
Get a string from a HID device, based on its string index.
hid_device_ref(T *pObject=nullptr)
static pthread_mutex_t g_DevicesMutex
static SDL_AudioDeviceID device
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
Read an Input report from a HID device with timeout.
static hid_device_ref< CHIDDevice > g_Devices
struct hid_device_info * next
void Close(bool bDeleteDevice)
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock)
Set the device handle to be non-blocking.
hid_buffer_entry * m_pNext
void ProcessFeatureReport(const uint8_t *pBuf, size_t nBufSize)
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device)
Close a HID device.
static jmethodID g_midHIDDeviceManagerSendFeatureReport
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen)
Get The Product String from a HID device.
int GetFeatureReport(unsigned char *pData, size_t nDataLen)
unsigned short product_id
static void ThreadDestroyed(void *value)
pthread_mutex_t * m_pMutex
static void FreeHIDDeviceInfo(hid_device_info *pInfo)
CHIDDevice(int nDeviceID, hid_device_info *pInfo)
int hid_exit(void)
Finalize the HIDAPI library.
static char * CreateStringFromJString(JNIEnv *env, const jstring &sString)
HID_API_EXPORT hid_device *HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
Open a HID device using a Vendor ID (VID), Product ID (PID) and optionally a serial number...
static hid_device_info * CopyHIDDeviceInfo(const hid_device_info *pInfo)
GLsizei const GLfloat * value
void emplace_back(const uint8_t *pData, size_t nSize)
hid_buffer_entry * m_pFree
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length)
Get a feature report from a HID device.
void SetObject(T *pObject)
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen)
Get The Serial Number String from a HID device.
#define HID_API_EXPORT_CALL
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE() HIDDeviceInputReport(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE() HIDDeviceDisconnected(JNIEnv *env, jobject thiz, int nDeviceID)
int SendOutputReport(const unsigned char *pData, size_t nDataLen)
hid_device_ref(const hid_device_ref &rhs)
void SetOpenResult(bool bResult)
hid_buffer_pool m_vecData
int hid_init(void)
Initialize the HIDAPI library.
static pthread_key_t g_ThreadKey
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE() HIDDeviceOpenResult(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened)
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE() HIDDeviceFeatureReport(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
static uint64_t get_timespec_ms(const struct timespec &ts)
void ProcessInput(const uint8_t *pBuf, size_t nBufSize)
static jbyteArray NewByteArray(JNIEnv *env, const uint8_t *pData, size_t nDataLen)
HID_API_EXPORT hid_device *HID_API_CALL hid_open_path(const char *path, int bExclusive)
Open a HID device by its path name.
GLsizei const GLchar *const * path
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length)
Send a Feature report to the device.
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen)
Get The Manufacturer String from a HID device.
GLuint GLsizei GLsizei * length
static jclass g_HIDDeviceManagerCallbackClass
static wchar_t * CreateWStringFromWString(const wchar_t *pwSrc)
static hid_device_ref< CHIDDevice > FindDevice(int nDeviceId)
struct hid_device_info HID_API_EXPORT *HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
Enumerate the HID Devices.
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)
unsigned short release_number
static wchar_t * CreateWStringFromJString(JNIEnv *env, const jstring &sString)
hid_mutex_guard(pthread_mutex_t *pMutex)
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length)
Read an Input report from a HID device.