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