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