From 8388ca81fbc6c0a840e81f6fd805b897d378556b Mon Sep 17 00:00:00 2001 From: cnlohr Date: Tue, 29 Nov 2016 03:03:34 -0500 Subject: Add a basic vive driver. Gotta figure out how to turn it on. --- Makefile | 16 +++ include/survive.h | 11 ++ src/os_generic.c | 336 +++++++++++++++++++++++++++++++++++++++++++++++++ src/os_generic.h | 76 +++++++++++ src/survive.c | 28 +++++ src/survive_internal.h | 63 ++++++++++ src/survive_usb.c | 189 ++++++++++++++++++++++++++++ test.c | 22 ++++ 8 files changed, 741 insertions(+) create mode 100644 Makefile create mode 100644 include/survive.h create mode 100644 src/os_generic.c create mode 100644 src/os_generic.h create mode 100644 src/survive.c create mode 100644 src/survive_internal.h create mode 100644 src/survive_usb.c create mode 100644 test.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b213a03 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +all : test + +CFLAGS:=-Iinclude -fPIC -g +LDFLAGS:=-lpthread -lusb-1.0 + +test : test.c lib/libsurvive.so + gcc -o $@ $^ $(LDFLAGS) $(CFLAGS) + +lib/libsurvive.so : src/os_generic.o src/survive.o src/survive_usb.o + gcc -o $@ $^ $(LDFLAGS) -shared + +clean : + rm -rf *.o src/*.o *~ src/*~ test libsurvive.so + + + diff --git a/include/survive.h b/include/survive.h new file mode 100644 index 0000000..67ab55d --- /dev/null +++ b/include/survive.h @@ -0,0 +1,11 @@ +#ifndef _SURVIVE_H +#define _SURVIVE_H + +struct SurviveContext; + +struct SurviveContext * survive_init( ); +void survive_close( struct SurviveContext * ctx ); +int survive_poll(); + +#endif + diff --git a/src/os_generic.c b/src/os_generic.c new file mode 100644 index 0000000..2429ad9 --- /dev/null +++ b/src/os_generic.c @@ -0,0 +1,336 @@ +#include "os_generic.h" + +#ifdef USE_WINDOWS + +#include + +void OGSleep( int is ) +{ + Sleep( is*1000 ); +} + +void OGUSleep( int ius ) +{ + Sleep( ius/1000 ); +} + +double OGGetAbsoluteTime() +{ + static LARGE_INTEGER lpf; + LARGE_INTEGER li; + + if( !lpf.QuadPart ) + { + QueryPerformanceFrequency( &lpf ); + } + + QueryPerformanceCounter( &li ); + return (double)li.QuadPart / (double)lpf.QuadPart; +} + + +double OGGetFileTime( const char * file ) +{ + FILETIME ft; + + HANDLE h = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + + if( h==INVALID_HANDLE_VALUE ) + return -1; + + GetFileTime( h, 0, 0, &ft ); + + CloseHandle( h ); + + return ft.dwHighDateTime + ft.dwLowDateTime; +} + + +og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter ) +{ + return (og_thread_t)CreateThread( 0, 0, (LPTHREAD_START_ROUTINE)routine, parameter, 0, 0 ); +} + +void * OGJoinThread( og_thread_t ot ) +{ + WaitForSingleObject( ot, INFINITE ); + CloseHandle( ot ); +} + +void OGCancelThread( og_thread_t ot ) +{ + CloseHandle( ot ); +} + +og_mutex_t OGCreateMutex() +{ + return CreateMutex( 0, 0, 0 ); +} + +void OGLockMutex( og_mutex_t om ) +{ + WaitForSingleObject(om, INFINITE); +} + +void OGUnlockMutex( og_mutex_t om ) +{ + ReleaseMutex(om); +} + +void OGDeleteMutex( og_mutex_t om ) +{ + CloseHandle( om ); +} + + + +og_sema_t OGCreateSema() +{ + HANDLE sem = CreateSemaphore( 0, 0, 32767, 0 ); + return (og_sema_t)sem; +} + +int OGGetSema( og_sema_t os ) +{ + typedef LONG NTSTATUS; + HANDLE sem = (HANDLE)os; + typedef NTSTATUS (NTAPI *_NtQuerySemaphore)( + HANDLE SemaphoreHandle, + DWORD SemaphoreInformationClass, /* Would be SEMAPHORE_INFORMATION_CLASS */ + PVOID SemaphoreInformation, /* but this is to much to dump here */ + ULONG SemaphoreInformationLength, + PULONG ReturnLength OPTIONAL + ); + + typedef struct _SEMAPHORE_BASIC_INFORMATION { + ULONG CurrentCount; + ULONG MaximumCount; + } SEMAPHORE_BASIC_INFORMATION; + + + static _NtQuerySemaphore NtQuerySemaphore; + SEMAPHORE_BASIC_INFORMATION BasicInfo; + NTSTATUS Status; + + if( !NtQuerySemaphore ) + { + NtQuerySemaphore = (_NtQuerySemaphore)GetProcAddress (GetModuleHandle ("ntdll.dll"), "NtQuerySemaphore"); + if( !NtQuerySemaphore ) + { + return -1; + } + } + + + Status = NtQuerySemaphore (sem, 0 /*SemaphoreBasicInformation*/, + &BasicInfo, sizeof (SEMAPHORE_BASIC_INFORMATION), NULL); + + if (Status == ERROR_SUCCESS) + { + return BasicInfo.CurrentCount; + } + + return -2; +} + +void OGLockSema( og_sema_t os ) +{ + WaitForSingleObject( (HANDLE)os, INFINITE ); +} + +void OGUnlockSema( og_sema_t os ) +{ + ReleaseSemaphore( (HANDLE)os, 1, 0 ); +} + +void OGDeleteSema( og_sema_t os ) +{ + CloseHandle( os ); +} + +#else + +#define _GNU_SOURCE + + +#include +#include +#include +#include +#include +#include + + +pthread_mutex_t g_RawMutexStart = PTHREAD_MUTEX_INITIALIZER; + +void OGSleep( int is ) +{ + sleep( is ); +} + +void OGUSleep( int ius ) +{ + usleep( ius ); +} + +double OGGetAbsoluteTime() +{ + struct timeval tv; + gettimeofday( &tv, 0 ); + return ((double)tv.tv_usec)/1000000. + (tv.tv_sec); +} + +double OGGetFileTime( const char * file ) +{ + struct stat buff; + + int r = stat( file, &buff ); + + if( r < 0 ) + { + return -1; + } + + return buff.st_mtime; +} + + + +og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter ) +{ + pthread_t * ret = malloc( sizeof( pthread_t ) ); + int r = pthread_create( ret, 0, routine, parameter ); + if( r ) + { + free( ret ); + return 0; + } + return (og_thread_t)ret; +} + +void * OGJoinThread( og_thread_t ot ) +{ + void * retval; + if( !ot ) + { + return 0; + } + pthread_join( *(pthread_t*)ot, &retval ); + free( ot ); + return retval; +} + +void OGCancelThread( og_thread_t ot ) +{ + if( !ot ) + { + return; + } + pthread_cancel( *(pthread_t*)ot ); + free( ot ); +} + +og_mutex_t OGCreateMutex() +{ + pthread_mutexattr_t mta; + og_mutex_t r = malloc( sizeof( pthread_mutex_t ) ); + + pthread_mutexattr_init(&mta); + pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init( (pthread_mutex_t *)r, &mta ); + + return r; +} + +void OGLockMutex( og_mutex_t om ) +{ + if( !om ) + { + return; + } + pthread_mutex_lock( (pthread_mutex_t*)om ); +} + +void OGUnlockMutex( og_mutex_t om ) +{ + if( !om ) + { + return; + } + pthread_mutex_unlock( (pthread_mutex_t*)om ); +} + +void OGDeleteMutex( og_mutex_t om ) +{ + if( !om ) + { + return; + } + + pthread_mutex_destroy( (pthread_mutex_t*)om ); + free( om ); +} + + + + +og_sema_t OGCreateSema() +{ + sem_t * sem = malloc( sizeof( sem_t ) ); + sem_init( sem, 0, 0 ); + return (og_sema_t)sem; +} + +int OGGetSema( og_sema_t os ) +{ + int valp; + sem_getvalue( os, &valp ); + return valp; +} + + +void OGLockSema( og_sema_t os ) +{ + sem_wait( os ); +} + +void OGUnlockSema( og_sema_t os ) +{ + sem_post( os ); +} + +void OGDeleteSema( og_sema_t os ) +{ + sem_destroy( os ); + free(os); +} + + + +#endif + +//Date Stamp: 2012-02-15 + +/* + Copyright (c) 2011-2012 <>< Charles Lohr + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of this file. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + diff --git a/src/os_generic.h b/src/os_generic.h new file mode 100644 index 0000000..7ce22f2 --- /dev/null +++ b/src/os_generic.h @@ -0,0 +1,76 @@ +#ifndef _OS_GENERIC_H +#define _OS_GENERIC_H + +#ifdef WIN32 +#define USE_WINDOWS +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +//Things that shouldn't be macro'd +double OGGetAbsoluteTime(); +void OGSleep( int is ); +void OGUSleep( int ius ); +double OGGetFileTime( const char * file ); + +//Threads and Mutices +typedef void* og_thread_t; +typedef void* og_mutex_t; +typedef void* og_sema_t; + +og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter ); +void * OGJoinThread( og_thread_t ot ); +void OGCancelThread( og_thread_t ot ); + +//Always a recrusive mutex. +og_mutex_t OGCreateMutex(); +void OGLockMutex( og_mutex_t om ); +void OGUnlockMutex( og_mutex_t om ); +void OGDeleteMutex( og_mutex_t om ); + +//Always a semaphore +og_sema_t OGCreateSema(); //Create a semaphore, comes locked initially. NOTE: Max count is 32767 +void OGLockSema( og_sema_t os ); +int OGGetSema( og_sema_t os ); //if <0 there was a failure. +void OGUnlockSema( og_sema_t os ); +void OGDeleteSema( og_sema_t os ); + +#ifdef __cplusplus +}; +#endif + + + +#endif + + +//Date Stamp: 2012-02-15 + +/* + NOTE: Portions (namely the top section) are part of headers from other + sources. + + Copyright (c) 2011-2012 <>< Charles Lohr + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of this file. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + diff --git a/src/survive.c b/src/survive.c new file mode 100644 index 0000000..0ae5aef --- /dev/null +++ b/src/survive.c @@ -0,0 +1,28 @@ +#include +#include "survive_internal.h" +#include +#include + +struct SurviveContext * survive_init( ) +{ + int r = 0; + struct SurviveContext * ret = calloc( 1, sizeof( struct SurviveContext ) ); + if( r = survive_usb_init( ret ) ) + { + return 0; + } + + return ret; +} + + +void survive_close( struct SurviveContext * ctx ) +{ + survive_usb_close( ctx ); +} + +int survive_poll( struct SurviveContext * ctx ) +{ + survive_usb_poll( ctx ); +} + diff --git a/src/survive_internal.h b/src/survive_internal.h new file mode 100644 index 0000000..b29b25b --- /dev/null +++ b/src/survive_internal.h @@ -0,0 +1,63 @@ +#ifndef _SURVIVE_INTERNAL_H +#define _SURVIVE_INTERNAL_H + +#include +#include +#include +#include + +#define SV_INFO( x... ) printf( x ) +#define SV_ERROR( x... ) fprintf( stderr, x ) + +//XXX TODO This one needs to be rewritten. +#define SV_KILL() exit(0) + +#define USB_DEV_HMD 0 +#define USB_DEV_LIGHTHOUSE 1 +#define USB_DEV_WATCHMAN1 2 +#define USB_DEV_WATCHMAN2 3 +#define MAX_USB_DEVS 4 + + +#define USB_IF_HMD 0 +#define USB_IF_LIGHTHOUSE 1 +#define USB_IF_WATCHMAN1 2 +#define USB_IF_WATCHMAN2 3 +#define USB_IF_LIGHTCAP 4 +#define MAX_INTERFACES 5 + +#define INTBUFFSIZE 64 + +struct SurviveContext; +struct SurviveUSBInterface; + + +//XXX TODO: Roll this into the main structure. + +typedef void (*usb_callback)( struct SurviveUSBInterface * ti ); + +struct SurviveUSBInterface +{ + struct libusb_transfer * transfer; + struct SurviveContext * ctx; + int actual_len; + uint8_t buffer[INTBUFFSIZE]; + usb_callback cb; + int which_interface_am_i; + const char * hname; //human names +}; + +struct SurviveContext +{ + struct libusb_context* usbctx; + struct libusb_device_handle * udev[MAX_USB_DEVS]; + struct SurviveUSBInterface uiface[MAX_INTERFACES]; +}; + +void survive_usb_close( struct SurviveContext * t ); +int survive_usb_init( struct SurviveContext * t ); +int survive_usb_poll( struct SurviveContext * ctx ); + +#endif + + diff --git a/src/survive_usb.c b/src/survive_usb.c new file mode 100644 index 0000000..704b4b5 --- /dev/null +++ b/src/survive_usb.c @@ -0,0 +1,189 @@ +#include "survive_internal.h" +#include +#include + +const short vidpids[] = { + 0x0bb4, 0x2c87, 0, //The main HTC HMD device + 0x28de, 0x2000, 0, //Valve lighthouse + 0x28de, 0x2101, 0, //Valve Watchman + 0x28de, 0x2101, 1, //Valve Watchman +}; //length MAX_USB_INTERFACES*2 + +const char * devnames[] = { + "HMD", + "Lighthouse", + "Watchman 1", + "Watchman 2", +}; //length MAX_USB_INTERFACES + + +static void handle_transfer(struct libusb_transfer* transfer) { + struct SurviveUSBInterface * iface = transfer->user_data; + + if( transfer->status != LIBUSB_TRANSFER_COMPLETED ) + { + SV_ERROR("Transfer problem %d with %s", transfer->status, iface->hname ); + SV_KILL(); + return; + } + + iface->actual_len = transfer->actual_length; + iface->cb( iface ); + + if( libusb_submit_transfer(transfer) ) + { + SV_ERROR( "Error resubmitting transfer for %s\n", iface->hname ); + SV_KILL(); + } +} + + + +static int AttachInterface( struct SurviveContext * t, int which_interface_am_i, libusb_device_handle * devh, int endpoint, usb_callback cb, const char * hname ) +{ + struct SurviveUSBInterface * iface = &t->uiface[which_interface_am_i]; + iface->ctx = t; + iface->which_interface_am_i = which_interface_am_i; + iface->hname = hname; + struct libusb_transfer * tx = iface->transfer = libusb_alloc_transfer(0); + iface->cb = cb; + printf( "%p %d %p %p\n", iface, which_interface_am_i, tx, devh ); + + if (!iface->transfer) + { + SV_ERROR( "Error: failed on libusb_alloc_transfer for %s\n", hname ); + return 4; + } + + libusb_fill_interrupt_transfer( tx, devh, endpoint, iface->buffer, INTBUFFSIZE, handle_transfer, iface, 0); + + int rc = libusb_submit_transfer( tx ); + if( rc ) + { + SV_ERROR( "Error: Could not submit transfer for %s (Code %d)\n", hname, rc ); + return 6; + } + + return 0; +} + + +void debug_cb( struct SurviveUSBInterface * si ) +{ + int i; + int len = si->actual_len; + printf( "%16s: %d: ", si->hname, len ); + for( i = 0; i < len; i++ ) + { + printf( "%02x ", si->buffer[i] ); + } + printf( "\n" ); +} + +int survive_usb_init( struct SurviveContext * t ) +{ + int r = libusb_init( &t->usbctx ); + if( r ) + { + SV_ERROR( "libusb fault %d\n", r ); + return r; + } + + int i; + libusb_device** devs; + int ret = libusb_get_device_list(t->usbctx, &devs); + + if( ret < 0 ) + { + SV_ERROR( "Couldn't get list of USB devices %d\n", ret ); + return ret; + } + + + //Open all interfaces. + for( i = 0; i < MAX_USB_DEVS; i++ ) + { + libusb_device * d; + int vid = vidpids[i*3+0]; + int pid = vidpids[i*3+1]; + int which = vidpids[i*3+2]; + + int did; + for( did = 0; d = devs[did]; did++ ) + { + struct libusb_device_descriptor desc; + + int ret = libusb_get_device_descriptor( d, &desc); + if (ret < 0) { + continue; + } + + if( desc.idVendor == vid && desc.idProduct == pid) + { + if( which == 0 ) break; + which--; + } + } + + struct libusb_config_descriptor *conf; + ret = libusb_get_config_descriptor(d, 0, &conf); + if( ret ) + continue; + + ret = libusb_open(d, &t->udev[i]); + + if( !t->udev[i] || ret ) + { + SV_ERROR( "Error: cannot open device \"%s\" with vid/pid %04x:%04x\n", devnames[i], vid, pid ); + return -5; + } + + libusb_set_auto_detach_kernel_driver( t->udev[i], 1 ); + + for (int j = 0; j < conf->bNumInterfaces; j++ ) + { + if( libusb_claim_interface(t->udev[i], j) ) + { + SV_ERROR( "Could not claim interface %d of %s\n", j, devnames[i] ); + return -9; + } + } + + SV_INFO( "Successfully enumerated %s (%d, %d)\n", devnames[i], did, conf->bNumInterfaces ); + } + libusb_free_device_list( devs, 1 ); + + if( AttachInterface( t, USB_IF_HMD, t->udev[USB_DEV_HMD], 0x81, debug_cb, "Mainboard" ) ) { return -6; } + if( AttachInterface( t, USB_IF_LIGHTHOUSE, t->udev[USB_DEV_LIGHTHOUSE], 0x81, debug_cb, "Lighthouse" ) ) { return -6; } + if( AttachInterface( t, USB_IF_WATCHMAN1, t->udev[USB_DEV_WATCHMAN1], 0x81, debug_cb, "Watchman 1" ) ) { return -6; } + if( AttachInterface( t, USB_IF_WATCHMAN2, t->udev[USB_DEV_WATCHMAN2], 0x81, debug_cb, "Watchman 2" ) ) { return -6; } + if( AttachInterface( t, USB_IF_LIGHTCAP, t->udev[USB_DEV_LIGHTHOUSE], 0x82, debug_cb, "Lightcap" ) ) { return -6; } + + SV_INFO( "All devices attached.\n" ); + + + //libUSB initialized. Continue. + return 0; +} + + +void survive_usb_close( struct SurviveContext * t ) +{ + int i; + for( i = 0; i < MAX_USB_DEVS; i++ ) + { + libusb_close( t->udev[i] ); + } + libusb_exit(t->usbctx); +} + +int survive_usb_poll( struct SurviveContext * ctx ) +{ + int r = libusb_handle_events( ctx->usbctx ); + if( r ) + { + SV_ERROR( "Libusb poll failed.\n" ); + } + return r; +} + diff --git a/test.c b/test.c new file mode 100644 index 0000000..b7f9627 --- /dev/null +++ b/test.c @@ -0,0 +1,22 @@ +#include +#include + + +#include + +int main() +{ + struct SurviveContext * ctx = survive_init( &ctx ); + + if( !ctx ) + { + fprintf( stderr, "Fatal.\n" ); + return 1; + } + + while(survive_poll(ctx) == 0) + { + + } +} + -- cgit v1.2.3