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