sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

hid.cpp (31905B)


      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:
     53 	hid_device_ref( T *pObject = nullptr ) : m_pObject( nullptr )
     54 	{
     55 		SetObject( pObject );
     56 	}
     57 
     58 	hid_device_ref( const hid_device_ref &rhs ) : m_pObject( nullptr )
     59 	{
     60 		SetObject( rhs.GetObject() );
     61 	}
     62 
     63 	~hid_device_ref()
     64 	{
     65 		SetObject( nullptr );
     66 	}
     67 
     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 
     83 	hid_device_ref &operator =( T *pObject )
     84 	{
     85 		SetObject( pObject );
     86 		return *this;
     87 	}
     88 
     89 	hid_device_ref &operator =( const hid_device_ref &rhs )
     90 	{
     91 		SetObject( rhs.GetObject() );
     92 		return *this;
     93 	}
     94 
     95 	T *GetObject() const
     96 	{
     97 		return m_pObject;
     98 	}
     99 
    100 	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:
    117 	hid_mutex_guard( pthread_mutex_t *pMutex ) : m_pMutex( pMutex )
    118 	{
    119 		pthread_mutex_lock( m_pMutex );
    120 	}
    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:
    133 	hid_buffer() : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
    134 	{
    135 	}
    136 
    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 
    142 	~hid_buffer()
    143 	{
    144 		delete[] m_pData;
    145 	}
    146 
    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 
    160 	void clear()
    161 	{
    162 		m_nSize = 0;
    163 	}
    164 
    165 	size_t size() const
    166 	{
    167 		return m_nSize;
    168 	}
    169 
    170 	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:
    184 	hid_buffer_pool() : m_nSize( 0 ), m_pHead( nullptr ), m_pTail( nullptr ), m_pFree( nullptr )
    185 	{
    186 	}
    187 
    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 
    200 	size_t size() const { return m_nSize; }
    201 
    202 	const hid_buffer &front() const { return m_pHead->m_buffer; }
    203 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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:
    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 
    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 
    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 
    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 
    390 	int GetId()
    391 	{
    392 		return m_nId;
    393 	}
    394 
    395 	const hid_device_info *GetDeviceInfo()
    396 	{
    397 		return m_pInfo;
    398 	}
    399 
    400 	hid_device *GetDevice()
    401 	{
    402 		return m_pDevice;
    403 	}
    404 
    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 
    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 
    488 	void SetOpenPending()
    489 	{
    490 		m_bIsWaitingForOpen = true;
    491 	}
    492 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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"
    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"
    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"
    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"
    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"
    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"
    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"
    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"
    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 
    989 int hid_init(void)
    990 {
    991 	return 0;
    992 }
    993 
    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 
   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 
   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 
   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 
   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?
   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
   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?
   1099 int  HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock)
   1100 {
   1101 	return -1;
   1102 }
   1103 
   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.
   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 
   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 
   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 
   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 
   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 
   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 
   1205 HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device)
   1206 {
   1207 	return NULL;
   1208 }
   1209 
   1210 int hid_exit(void)
   1211 {
   1212 	return 0;
   1213 }
   1214 
   1215 }