aboutsummaryrefslogtreecommitdiff
//Unofficial driver for the official Valve/HTC Vive hardware.
//
//Based off of https://github.com/collabora/OSVR-Vive-Libre
// Originally Copyright 2016 Philipp Zabel
// Originally Copyright 2016 Lubosz Sarnecki <lubosz.sarnecki@collabora.co.uk>
// Originally Copyright (C) 2013 Fredrik Hultin
// Originally Copyright (C) 2013 Jakob Bornecrantz
//
//But, re-written as best as I can to get it put under an open souce license instead of a forced-source license.
//If there are portions of the code too similar to the original, I would like to know  so they can be re-written.
//All MIT/x11 Licensed Code in this file may be relicensed freely under the GPL or LGPL licenses.

#include <survive.h>
#include <jsmn.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <os_generic.h>
#if !defined(__FreeBSD__) && !defined(__APPLE__)
#include <malloc.h> // for alloca
#endif

#include "json_helpers.h"
#include "survive_default_devices.h"
#include "survive_config.h"

#ifdef HIDAPI
#if defined(WINDOWS) || defined(WIN32) || defined (_WIN32)
#include <windows.h>
#undef WCHAR_MAX
#endif
#include <hidapi.h>
#else
#ifdef __FreeBSD__
#include <libusb.h>
#else
#include <libusb-1.0/libusb.h>
#endif
#endif


struct SurviveViveData;

const short vidpids[] = {
	0x0bb4, 0x2c87, 0, //Valve HMD Button and face proximity sensor
	0x28de, 0x2000, 0, //Valve HMD IMU & Lighthouse Sensors
	0x28de, 0x2101, 0, //Valve Watchman
	0x28de, 0x2101, 1, //Valve Watchman
	0x28de, 0x2022, 0, //HTC Tracker
	0x28de, 0x2300, 0, //HTC Tracker 2018
	0x28de, 0x2012, 0, //Valve Watchman, USB connected
#ifdef HIDAPI
	0x28de, 0x2000, 1, //Valve HMD lighthouse(B) (only used on HIDAPI, for lightcap)
	0x28de, 0x2022, 1, //HTC Tracker (only used on HIDAPI, for lightcap)
	0x28de, 0x2300, 1, //HTC Tracker 2018 (only used on HIDAPI, for lightcap)
	0x28de, 0x2012, 1, //Valve Watchman, USB connected (only used on HIDAPI, for lightcap)

	0x28de, 0x2000, 2, //Valve HMD lighthouse(B) (only used on HIDAPI, for lightcap)
	0x28de, 0x2022, 2, //HTC Tracker (only used on HIDAPI, for lightcap)
	0x28de, 0x2300, 2, //HTC Tracker 2018 (only used on HIDAPI, for lightcap)
	0x28de, 0x2012, 2, //Valve Watchman, USB connected (only used on HIDAPI, for lightcap)

#endif
}; //length MAX_USB_INTERFACES*2

const char * devnames[] = {
	"HMD",
	"HMD IMU & LH",
	"Watchman 1",
	"Watchman 2",
	"Tracker 0",
	"Tracker 1",
	"Wired Watchman 1",
#ifdef HIDAPI
	"HMD Lightcap",
	"Tracker 0 Lightcap",
	"Tracker 1 Lightcap",
	"Wired Watchman 1 Lightcap",

	"HMD Buttons",
	"Tracker 0 Buttons",
	"Tracker 1 Buttons",
	"Wired Watchman 1 Buttons",
#endif
}; //length MAX_USB_INTERFACES


enum {
    USB_DEV_HMD = 0,
    USB_DEV_HMD_IMU_LH,
    USB_DEV_WATCHMAN1,
    USB_DEV_WATCHMAN2,
    USB_DEV_TRACKER0,
    USB_DEV_TRACKER1,
    USB_DEV_W_WATCHMAN1,    // Wired Watchman attached via USB
#ifdef HIDAPI
    USB_DEV_HMD_IMU_LHB,
    USB_DEV_TRACKER0_LIGHTCAP,
    USB_DEV_TRACKER1_LIGHTCAP,
    USB_DEV_W_WATCHMAN1_LIGHTCAP,

    USB_DEV_HMD_BUTTONS,
    USB_DEV_TRACKER0_BUTTONS,
    USB_DEV_TRACKER1_BUTTONS,
    USB_DEV_W_WATCHMAN1_BUTTONS,
#endif
    MAX_USB_DEVS
};

enum {
    USB_IF_HMD = 0,
    USB_IF_HMD_IMU_LH,
    USB_IF_WATCHMAN1,
    USB_IF_WATCHMAN2,
    USB_IF_TRACKER0,
    USB_IF_TRACKER1,
    USB_IF_W_WATCHMAN1,

    USB_IF_LIGHTCAP,
    USB_IF_TRACKER0_LIGHTCAP,
    USB_IF_TRACKER1_LIGHTCAP,
    USB_IF_W_WATCHMAN1_LIGHTCAP,

    USB_IF_HMD_BUTTONS,
    USB_IF_TRACKER0_BUTTONS,
    USB_IF_TRACKER1_BUTTONS,
    USB_IF_W_WATCHMAN1_BUTTONS,
    MAX_INTERFACES
};

typedef struct SurviveUSBInterface SurviveUSBInterface;
typedef struct SurviveViveData SurviveViveData;

typedef void (*usb_callback)( SurviveUSBInterface * ti );

#ifdef HIDAPI
#define USBHANDLE hid_device *
#else
#define USBHANDLE libusb_device_handle *
#endif

struct SurviveUSBInterface
{
	SurviveViveData * sv;
	SurviveContext * ctx;

#ifdef HIDAPI
	USBHANDLE uh;
#else
	struct libusb_transfer * transfer;
#endif
	SurviveObject * assoc_obj;
	int actual_len;
	uint8_t buffer[INTBUFFSIZE];
	usb_callback cb;
	int which_interface_am_i;	//for indexing into uiface
	const char * hname;			//human-readable names
	size_t packet_count;
};

struct SurviveViveData
{
	SurviveContext * ctx;

	SurviveUSBInterface uiface[MAX_INTERFACES];

	USBHANDLE udev[MAX_USB_DEVS];
#ifdef HIDAPI
	og_thread_t servicethread[MAX_USB_DEVS];
#else
	struct libusb_context* usbctx;
	size_t read_count;
#endif
};

#ifdef HIDAPI
og_mutex_t      GlobalRXUSBMutx;
#endif

void survive_data_cb( SurviveUSBInterface * si );

//USB Subsystem 
void survive_usb_close( SurviveContext * t );
int survive_usb_init( SurviveViveData * sv, SurviveObject * hmd, SurviveObject *wm0, SurviveObject * wm1, SurviveObject * tr0 , SurviveObject * tr1, SurviveObject * ww0 );
int survive_usb_poll( SurviveContext * ctx );
int survive_get_config( char ** config, SurviveViveData * ctx, int devno, int iface, int send_extra_magic );
int survive_vive_send_magic(SurviveContext * ctx, void * drv, int magic_code, void * data, int datalen );

#ifdef HIDAPI
void * HAPIReceiver( void * v )
{

	SurviveUSBInterface * iface = v;
	USBHANDLE * hp = &iface->uh;
	
	while( (iface->actual_len = hid_read( *hp, iface->buffer, sizeof( iface->buffer ) )) > 0 )
	{
		//if( iface->actual_len  == 52 ) continue;
		OGLockMutex( GlobalRXUSBMutx );
#if 0
		printf( "%d %d: ", iface->which_interface_am_i, iface->actual_len );
		int i;
		for( i = 0; i < iface->actual_len; i++ )
		{
			printf( "%02x ", iface->buffer[i] );
		}
		printf("\n" );
#endif
		survive_data_cb( iface );
		OGUnlockMutex( GlobalRXUSBMutx );
	}
	//XXX TODO: Mark device as failed.
	*hp = 0;
	return 0;
}

#else
static void handle_transfer(struct libusb_transfer* transfer)
{
	SurviveUSBInterface * iface = transfer->user_data;
	SurviveContext * ctx = iface->ctx;

	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 );
	iface->packet_count++;

	if( libusb_submit_transfer(transfer) )
	{
		SV_ERROR( "Error resubmitting transfer for %s", iface->hname );
		SV_KILL();
	}
}
#endif


static int AttachInterface( SurviveViveData * sv, SurviveObject * assocobj, int which_interface_am_i, USBHANDLE devh, int endpoint, usb_callback cb, const char * hname )
{
	SurviveContext * ctx = sv->ctx;
	SurviveUSBInterface * iface = &sv->uiface[which_interface_am_i];
	iface->ctx = ctx;
	iface->sv = sv;
	iface->which_interface_am_i = which_interface_am_i;
	iface->assoc_obj = assocobj;
	iface->hname = hname;
	iface->cb = cb;

#ifdef HIDAPI
	//What do here?
	iface->uh = devh;
	sv->servicethread[which_interface_am_i] = OGCreateThread( HAPIReceiver, iface );
	OGUSleep(100000);
#else
	struct libusb_transfer * tx = iface->transfer = libusb_alloc_transfer(0);
	//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", 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, %s)", hname, rc, libusb_error_name(rc));
		return 6;
	}
#endif
	return 0;
}

/*
static 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" );
}*/

//XXX TODO: Redo this subsystem for setting/updating feature reports.


#ifdef HIDAPI

static inline int update_feature_report(USBHANDLE dev, uint16_t iface, uint8_t * data, int datalen )
{
	int r = hid_send_feature_report( dev, data, datalen );
//	printf( "HUR: (%p) %d (%d) [%d]\n", dev, r, datalen, data[0] );
	return r;
}
static inline int getupdate_feature_report(USBHANDLE dev, uint16_t iface, uint8_t * data, size_t datalen ) 
{
	int r = hid_get_feature_report( dev, data, datalen );
//	printf( "HGR: (%p) %d (%d) (%d)\n", dev, r, datalen, data[0] );
	if( r == -1 ) return -9; //Pretend it's not a critical error
	return r;
}

#else
	
static inline int update_feature_report(libusb_device_handle* dev, uint16_t interface, uint8_t * data, int datalen ) {
//	int xfer;
//	int r = libusb_interrupt_transfer(dev, 0x01, data, datalen, &xfer, 1000);
//	printf( "XFER: %d / R: %d\n", xfer, r );
//	return xfer;
	return libusb_control_transfer(dev, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
		0x09, 0x300 | data[0], interface, data, datalen, 1000 );
}


static inline int getupdate_feature_report(libusb_device_handle* dev, uint16_t interface, uint8_t * data, int datalen ) {

	int ret = libusb_control_transfer(dev, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN,
		0x01, 0x300 | data[0], interface, data, datalen, 1000 );
	if( ret == -9 ) return -9;
	if (ret < 0)
		return -1;
	return ret;
}

#endif

static inline int hid_get_feature_report_timeout(USBHANDLE device, uint16_t iface, unsigned char *buf, size_t len )
{
	int ret;
	uint8_t i = 0;
    for (i = 0; i < 50; i++)
	{
        ret = getupdate_feature_report(device, iface, buf, len);
		if( ret != -9 && ( ret != -1 || errno != EPIPE ) ) return ret;
		OGUSleep( 1000 );
	}

	return -1;
}

int survive_usb_init( SurviveViveData * sv, SurviveObject * hmd, SurviveObject *wm0, SurviveObject * wm1, SurviveObject * tr0, SurviveObject * tr1, SurviveObject * ww0 )
{
	SurviveContext * ctx = sv->ctx;
	const char *blacklist = survive_configs(ctx, "blacklist-devs", SC_GET, "-");
	SV_INFO("Blacklisting %s", blacklist);

#ifdef HIDAPI
	SV_INFO( "Vive starting in HIDAPI mode." );
	if( !GlobalRXUSBMutx )
	{
		GlobalRXUSBMutx = OGCreateMutex();
		OGLockMutex( GlobalRXUSBMutx );
	}
	int res, i;
	res = hid_init();
	if( res )
	{
		SV_ERROR( "Could not setup hidapi." );
		return res;
	}
	
	for( i = 0; i < MAX_USB_DEVS; i++ )
	{
		if (strstr(blacklist, devnames[i]))
			continue;
		int enumid = vidpids[i*3+2];
		int vendor_id = vidpids[i*3+0];
		int product_id = vidpids[i*3+1];
		struct hid_device_info * devs = hid_enumerate(vendor_id, product_id);
		struct hid_device_info * cur_dev = devs;
		const char *path_to_open = NULL;
		hid_device *handle = NULL;
		int menum = 0;
		
		cur_dev = devs;
		while (cur_dev) {
			if (cur_dev->vendor_id == vendor_id &&
				cur_dev->product_id == product_id)
			{
				if( cur_dev->interface_number == enumid ||
					cur_dev->interface_number == -1 && menum == enumid)
				{
					path_to_open = cur_dev->path;
					break;
				}
				menum++;
			}
			cur_dev = cur_dev->next;
		}

		if (path_to_open) {
			handle = hid_open_path(path_to_open);
		}

		hid_free_enumeration(devs);

		if( !handle )
		{
			SV_INFO( "Warning: Could not find vive device %04x:%04x", vendor_id, product_id );
			continue;
		}
	
		// Read the Serial Number String
		wchar_t wstr[255];

		res = hid_get_serial_number_string(handle, wstr, 255);
		printf("Found %s. ", devnames[i]);
		wprintf(L"Serial Number String: (%d) %s for %04x:%04x@%d  (Dev: %p)\n", wstr[0], wstr,vendor_id, product_id, menum, handle);
		
		sv->udev[i] = handle;

	}

#else
	SV_INFO( "Vive starting in libusb mode." );

	int r = libusb_init( &sv->usbctx );
	if( r )
	{
		SV_ERROR("libusb fault %d (%s)\n", r, libusb_error_name(r));
		return r;
	}

	int i;
	int16_t j;
	libusb_device** devs;
	int ret = libusb_get_device_list(sv->usbctx, &devs);

	if( ret < 0 )
	{
		SV_ERROR("Couldn't get list of USB devices %d (%s)", ret, libusb_error_name(ret));
		return ret;
	}


	//Open all interfaces.
	for( i = 0; i < MAX_USB_DEVS; i++ )
	{
		if (strstr(blacklist, devnames[i]))
			continue;
		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--;
			}
		}

		if( d == 0 )
		{
			SV_INFO( "Did not find device %s (%04x:%04x.%d)", devnames[i], vid, pid, which );
			sv->udev[i] = 0;
			continue;
		}

		struct libusb_config_descriptor *conf;
		ret = libusb_get_config_descriptor(d, 0, &conf);
		if( ret )
			continue;
		ret = libusb_open(d, &sv->udev[i]);

		if( !sv->udev[i] || ret )
		{
			SV_ERROR("Error: cannot open device \"%s\" with vid/pid %04x:%04x error %d (%s)", devnames[i], vid, pid,
					 ret, libusb_error_name(ret));
			return -5;
		}

		libusb_set_auto_detach_kernel_driver( sv->udev[i], 1 );
		for (j = 0; j < conf->bNumInterfaces; j++ )
		{
#if 0
		    if (libusb_kernel_driver_active(sv->udev[i], j) == 1) {
		        ret = libusb_detach_kernel_driver(sv->udev[i], j);
		        if (ret != LIBUSB_SUCCESS) {
		            SV_ERROR("Failed to unclaim interface %d for device %s "
				     "from the kernel. %d (%s)", j, devnames[i], ret, libusb_error_name(ret) );
		            libusb_free_config_descriptor(conf);
		            libusb_close(sv->udev[i]);
		            continue;
		        }
		    }
#endif

			if( libusb_claim_interface(sv->udev[i], j) )
			{
				SV_ERROR( "Could not claim interface %d of %s", j, devnames[i] );
				return -9;
			}
		}

		SV_INFO( "Successfully enumerated %s (%d, %d)", devnames[i], did, conf->bNumInterfaces );

		usleep(100000);
	}
	
	libusb_free_device_list( devs, 1 );
#endif
	//Add the drivers - this must happen BEFORE we actually attach interfaces.
	if (sv->udev[USB_DEV_HMD_IMU_LH]) {
		survive_add_object(ctx, hmd);
	}
	if (sv->udev[USB_DEV_WATCHMAN1]) {
		survive_add_object(ctx, wm0);
	}
	if (sv->udev[USB_DEV_WATCHMAN2]) {
		survive_add_object(ctx, wm1);
	}
	if (sv->udev[USB_DEV_TRACKER0]) {
		survive_add_object(ctx, tr0);
	}
	if (sv->udev[USB_DEV_TRACKER1]) {
		survive_add_object(ctx, tr1);
	}
	if (sv->udev[USB_DEV_W_WATCHMAN1]) {
		survive_add_object(ctx, ww0);
	}

	if( sv->udev[USB_DEV_HMD] && AttachInterface( sv, hmd, USB_IF_HMD,        sv->udev[USB_DEV_HMD],        0x81, survive_data_cb, "Mainboard" ) ) { return -6; }
	if( sv->udev[USB_DEV_HMD_IMU_LH] && AttachInterface( sv, hmd, USB_IF_HMD_IMU_LH, sv->udev[USB_DEV_HMD_IMU_LH], 0x81, survive_data_cb, "Lighthouse" ) ) { return -7; }
	if( sv->udev[USB_DEV_WATCHMAN1] && AttachInterface( sv, wm0, USB_IF_WATCHMAN1,  sv->udev[USB_DEV_WATCHMAN1],  0x81, survive_data_cb, "Watchman 1" ) ) { return -8; }
	if( sv->udev[USB_DEV_WATCHMAN2] && AttachInterface( sv, wm1, USB_IF_WATCHMAN2, sv->udev[USB_DEV_WATCHMAN2], 0x81, survive_data_cb, "Watchman 2")) { return -9; }
	if( sv->udev[USB_DEV_TRACKER0] && AttachInterface( sv, tr0, USB_IF_TRACKER0, sv->udev[USB_DEV_TRACKER0], 0x81, survive_data_cb, "Tracker 1")) { return -10; }
	if( sv->udev[USB_DEV_TRACKER1] && AttachInterface( sv, tr1, USB_IF_TRACKER1, sv->udev[USB_DEV_TRACKER1], 0x81, survive_data_cb, "Tracker 2")) { return -10; }
	if( sv->udev[USB_DEV_W_WATCHMAN1] && AttachInterface( sv, ww0, USB_IF_W_WATCHMAN1, sv->udev[USB_DEV_W_WATCHMAN1], 0x81, survive_data_cb, "Wired Watchman 1")) { return -11; }
#ifdef HIDAPI
	//Tricky: use other interface for actual lightcap.  XXX THIS IS NOT YET RIGHT!!!
	if( sv->udev[USB_DEV_HMD_IMU_LHB] && AttachInterface( sv, hmd, USB_IF_LIGHTCAP, sv->udev[USB_DEV_HMD_IMU_LHB], 0x82, survive_data_cb, "Lightcap")) { return -12; }

	// This is a HACK!  But it works.  Need to investigate further
	sv->uiface[USB_DEV_TRACKER0_LIGHTCAP].actual_len = 64;
	sv->uiface[USB_DEV_TRACKER1_LIGHTCAP].actual_len = 64;
	if( sv->udev[USB_DEV_TRACKER0_LIGHTCAP] && AttachInterface( sv, tr0, USB_IF_TRACKER0_LIGHTCAP, sv->udev[USB_DEV_TRACKER0_LIGHTCAP], 0x82, survive_data_cb, "Tracker 1 Lightcap")) { return -13; }
	if( sv->udev[USB_DEV_TRACKER1_LIGHTCAP] && AttachInterface( sv, tr1, USB_IF_TRACKER1_LIGHTCAP, sv->udev[USB_DEV_TRACKER1_LIGHTCAP], 0x82, survive_data_cb, "Tracker 2 Lightcap")) { return -13; }

	if( sv->udev[USB_DEV_W_WATCHMAN1_LIGHTCAP] && AttachInterface( sv, ww0, USB_IF_W_WATCHMAN1_LIGHTCAP, sv->udev[USB_DEV_W_WATCHMAN1_LIGHTCAP], 0x82, survive_data_cb, "Wired Watchman 1 Lightcap")) { return -13; }


	if (sv->udev[USB_DEV_TRACKER0_BUTTONS] && AttachInterface(sv, tr0, USB_IF_TRACKER0_BUTTONS, sv->udev[USB_DEV_TRACKER0_BUTTONS], 0x83, survive_data_cb, "Tracker 1 Buttons")) { return -13; }
	if (sv->udev[USB_DEV_TRACKER1_BUTTONS] && AttachInterface(sv, tr1, USB_IF_TRACKER1_BUTTONS, sv->udev[USB_DEV_TRACKER1_BUTTONS], 0x83, survive_data_cb, "Tracker 2 Buttons")) { return -13; }
	if (sv->udev[USB_DEV_W_WATCHMAN1_BUTTONS] && AttachInterface(sv, ww0, USB_IF_W_WATCHMAN1_BUTTONS, sv->udev[USB_DEV_W_WATCHMAN1_BUTTONS], 0x83, survive_data_cb, "Wired Watchman 1 BUTTONS")) { return -13; }

#else
	if( sv->udev[USB_DEV_HMD_IMU_LH] && AttachInterface( sv, hmd, USB_IF_LIGHTCAP, sv->udev[USB_DEV_HMD_IMU_LH], 0x82, survive_data_cb, "Lightcap")) { return -12; }
	if( sv->udev[USB_DEV_TRACKER0] && AttachInterface( sv, tr0, USB_IF_TRACKER0_LIGHTCAP, sv->udev[USB_DEV_TRACKER0], 0x82, survive_data_cb, "Tracker 0 Lightcap")) { return -13; }
	if( sv->udev[USB_DEV_TRACKER1] && AttachInterface( sv, tr1, USB_IF_TRACKER1_LIGHTCAP, sv->udev[USB_DEV_TRACKER1], 0x82, survive_data_cb, "Tracker 1 Lightcap")) { return -13; }
	if( sv->udev[USB_DEV_W_WATCHMAN1] && AttachInterface( sv, ww0, USB_IF_W_WATCHMAN1_LIGHTCAP, sv->udev[USB_DEV_W_WATCHMAN1], 0x82, survive_data_cb, "Wired Watchman 1 Lightcap")) { return -13; }
#endif
	SV_INFO( "All enumerated devices attached." );

	survive_vive_send_magic(ctx, sv, 1, 0, 0 );

	//libUSB initialized.  Continue.
	return 0;
}

int survive_vive_send_magic(SurviveContext * ctx, void * drv, int magic_code, void * data, int datalen )
{
	int r;
	SurviveViveData * sv = drv;

	//XXX TODO: Handle haptics, etc.
	int turnon = magic_code;


	if( turnon )
	{
		//From actual steam.
		if (sv->udev[USB_DEV_HMD])
		{
			static uint8_t vive_magic_power_on[64] = {  0x04, 0x78, 0x29, 0x38,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01 };
			r = update_feature_report( sv->udev[USB_DEV_HMD], 0, vive_magic_power_on, sizeof( vive_magic_power_on ) );
			if( r != sizeof( vive_magic_power_on ) ) return 5;
		}

		if (sv->udev[USB_DEV_HMD_IMU_LH])
		{
			static uint8_t vive_magic_enable_lighthouse[5] = { 0x04 };
			r = update_feature_report( sv->udev[USB_DEV_HMD_IMU_LH], 0, vive_magic_enable_lighthouse, sizeof( vive_magic_enable_lighthouse ) );
			if( r != sizeof( vive_magic_enable_lighthouse ) ) return 5;

			static uint8_t vive_magic_enable_lighthouse2[5] = { 0x07, 0x02 };  //Switch to 0x25 mode (able to get more light updates)
			r = update_feature_report( sv->udev[USB_DEV_HMD_IMU_LH], 0, vive_magic_enable_lighthouse2, sizeof( vive_magic_enable_lighthouse2 ) );
			if( r != sizeof( vive_magic_enable_lighthouse2 ) ) return 5;
		}

		if (sv->udev[USB_DEV_W_WATCHMAN1])
		{
			static uint8_t vive_magic_power_on[5] = { 0x04 };
			r = update_feature_report( sv->udev[USB_DEV_W_WATCHMAN1], 0, vive_magic_power_on, sizeof( vive_magic_power_on ) );
			if( r != sizeof( vive_magic_power_on ) ) return 5;
		}

#ifdef HIDAPI
		if (sv->udev[USB_DEV_W_WATCHMAN1_LIGHTCAP])
		{
			static uint8_t vive_magic_enable_lighthouse[5] = { 0x04 };
			r = update_feature_report( sv->udev[USB_DEV_W_WATCHMAN1_LIGHTCAP], 0, vive_magic_enable_lighthouse, sizeof( vive_magic_enable_lighthouse ) );
			if( r != sizeof( vive_magic_enable_lighthouse ) ) return 5;

			static uint8_t vive_magic_enable_lighthouse2[5] = { 0x07, 0x02 };  //Switch to 0x25 mode (able to get more light updates)
			r = update_feature_report( sv->udev[USB_DEV_W_WATCHMAN1_LIGHTCAP], 0, vive_magic_enable_lighthouse2, sizeof( vive_magic_enable_lighthouse2 ) );
			if( r != sizeof( vive_magic_enable_lighthouse2 ) ) return 5;
		}

#else
		if (sv->udev[USB_DEV_W_WATCHMAN1])
		{
			static uint8_t vive_magic_enable_lighthouse[5] = { 0x04 };
			r = update_feature_report( sv->udev[USB_DEV_W_WATCHMAN1], 0, vive_magic_enable_lighthouse, sizeof( vive_magic_enable_lighthouse ) );
			if( r != sizeof( vive_magic_enable_lighthouse ) ) return 5;

			static uint8_t vive_magic_enable_lighthouse2[5] = { 0x07, 0x02 };  //Switch to 0x25 mode (able to get more light updates)
			r = update_feature_report( sv->udev[USB_DEV_W_WATCHMAN1], 0, vive_magic_enable_lighthouse2, sizeof( vive_magic_enable_lighthouse2 ) );
			if( r != sizeof( vive_magic_enable_lighthouse2 ) ) return 5;
		}

#endif

		if (sv->udev[USB_DEV_TRACKER0])
		{
			static uint8_t vive_magic_power_on[5] = { 0x04 };
			r = update_feature_report( sv->udev[USB_DEV_TRACKER0], 0, vive_magic_power_on, sizeof( vive_magic_power_on ) );
			if( r != sizeof( vive_magic_power_on ) ) return 5;
		}
//#ifdef HIDAPI
//		if (sv->udev[USB_DEV_TRACKER0_LIGHTCAP])
//		{
//			static uint8_t vive_magic_enable_lighthouse[5] = { 0x04 };
//			r = update_feature_report( sv->udev[USB_DEV_TRACKER0_LIGHTCAP], 0, vive_magic_enable_lighthouse, sizeof( vive_magic_enable_lighthouse ) );
//			if( r != sizeof( vive_magic_enable_lighthouse ) ) return 5;
//
//			static uint8_t vive_magic_enable_lighthouse2[5] = { 0x07, 0x02 };  //Switch to 0x25 mode (able to get more light updates)
//			r = update_feature_report( sv->udev[USB_DEV_TRACKER0_LIGHTCAP], 0, vive_magic_enable_lighthouse2, sizeof( vive_magic_enable_lighthouse2 ) );
//			if( r != sizeof( vive_magic_enable_lighthouse2 ) ) return 5;
//		}
//#else		
		if (sv->udev[USB_DEV_TRACKER0])
		{
			static uint8_t vive_magic_enable_lighthouse[5] = { 0x04 };
			r = update_feature_report( sv->udev[USB_DEV_TRACKER0], 0, vive_magic_enable_lighthouse, sizeof( vive_magic_enable_lighthouse ) );
			if( r != sizeof( vive_magic_enable_lighthouse ) ) return 5;

			static uint8_t vive_magic_enable_lighthouse2[5] = { 0x07, 0x02 };  //Switch to 0x25 mode (able to get more light updates)
			r = update_feature_report( sv->udev[USB_DEV_TRACKER0], 0, vive_magic_enable_lighthouse2, sizeof( vive_magic_enable_lighthouse2 ) );
			if( r != sizeof( vive_magic_enable_lighthouse2 ) ) return 5;
		}

//#endif

		if (sv->udev[USB_DEV_TRACKER1])
		{
			static uint8_t vive_magic_power_on[5] = { 0x04 };
			r = update_feature_report( sv->udev[USB_DEV_TRACKER1], 0, vive_magic_power_on, sizeof( vive_magic_power_on ) );
			if( r != sizeof( vive_magic_power_on ) ) return 5;
		}
		if (sv->udev[USB_DEV_TRACKER1])
		{
			static uint8_t vive_magic_enable_lighthouse[5] = { 0x04 };
			r = update_feature_report( sv->udev[USB_DEV_TRACKER1], 0, vive_magic_enable_lighthouse, sizeof( vive_magic_enable_lighthouse ) );
			if( r != sizeof( vive_magic_enable_lighthouse ) ) return 5;

			static uint8_t vive_magic_enable_lighthouse2[5] = { 0x07, 0x02 };  //Switch to 0x25 mode (able to get more light updates)
			r = update_feature_report( sv->udev[USB_DEV_TRACKER1], 0, vive_magic_enable_lighthouse2, sizeof( vive_magic_enable_lighthouse2 ) );
			if( r != sizeof( vive_magic_enable_lighthouse2 ) ) return 5;
		}

#if 0		
		for (int j=0; j < 40; j++)
		{
			for (int i = 0; i < 0x1; i++)
			{
				//uint8_t vive_controller_haptic_pulse[64] = { 0xff, 0x8f, 0x7, 0 /*right*/, 0xFF /*period on*/, 0xFF/*period on*/, 0xFF/*period off*/, 0xFF/*period off*/, 0xFF /* repeat Count */, 0xFF /* repeat count */ };
				//uint8_t vive_controller_haptic_pulse[64] = { 0xff, 0x8f, 0x07, 0x00, 0xf4, 0x01, 0xb5, 0xa2, 0x01, 0x00 }; // data taken from Nairol's captures
				uint8_t vive_controller_haptic_pulse[64] = { 0xff, 0x8f, 0x07, 0x00, 0xf4, 0x01, 0xb5, 0xa2, 0x01* j, 0x00 }; 
				r = update_feature_report( sv->udev[USB_DEV_WATCHMAN1], 0, vive_controller_haptic_pulse, sizeof( vive_controller_haptic_pulse ) );
				r = getupdate_feature_report(sv->udev[USB_DEV_WATCHMAN1], 0, vive_controller_haptic_pulse, sizeof(vive_controller_haptic_pulse));
				SV_INFO("UCR: %d", r);
				if (r != sizeof(vive_controller_haptic_pulse)) printf("HAPTIC FAILED **************************\n"); // return 5;
				OGUSleep(5000);
			}

			OGUSleep(20000);
		}


#endif

#if 0
		//// working code to turn off a wireless controller:
		//{
		//	uint8_t vive_controller_off[64] = { 0xff, 0x9f, 0x04, 'o', 'f', 'f', '!' };
		//	//r = update_feature_report( sv->udev[USB_DEV_WATCHMAN1], 0, vive_controller_haptic_pulse, sizeof( vive_controller_haptic_pulse ) );
		//	r = update_feature_report(sv->udev[USB_DEV_WATCHMAN1], 0, vive_controller_off, sizeof(vive_controller_off));
		//	SV_INFO("UCR: %d", r);
		//	if (r != sizeof(vive_controller_off)) printf("OFF FAILED **************************\n"); // return 5;
		//	OGUSleep(1000);
		//}
#endif
		//if (sv->udev[USB_DEV_TRACKER0])
		//{
		//	static uint8_t vive_magic_power_on[64] = {  0x04, 0x78, 0x29, 0x38 };
		//	r = update_feature_report( sv->udev[USB_DEV_TRACKER0], 0, vive_magic_power_on, sizeof( vive_magic_power_on ) );
		//	if( r != sizeof( vive_magic_power_on ) ) return 5;
		//}

		SV_INFO( "Powered unit on." );
	}
	else
	{

		static uint8_t vive_magic_power_off1[] = {
			0x04, 0x78, 0x29, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
			0x30, 0x05, 0x77, 0x00, 0x30, 0x05, 0x77, 0x00, 0x6c, 0x4d, 0x37, 0x65, 0x40,
			0xf9, 0x33, 0x00, 0x04, 0xf8, 0xa3, 0x04, 0x04, 0x00, 0x00, 0x00, 0x70, 0xb0,
			0x72, 0x00, 0xf4, 0xf7, 0xa3, 0x04, 0x7c, 0xf8, 0x33, 0x00, 0x0c, 0xf8, 0xa3,
			0x04, 0x0a, 0x6e, 0x29, 0x65, 0x24, 0xf9, 0x33, 0x00, 0x00, 0x00, 0x00,
		};

		static uint8_t vive_magic_power_off2[] = {
			0x04, 0x78, 0x29, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
			0x30, 0x05, 0x77, 0x00, 0xe4, 0xf7, 0x33, 0x00, 0xe4, 0xf7, 0x33, 0x00, 0x60,
			0x6e, 0x72, 0x00, 0xb4, 0xf7, 0x33, 0x00, 0x04, 0x00, 0x00, 0x00, 0x70, 0xb0,
			0x72, 0x00, 0x90, 0xf7, 0x33, 0x00, 0x7c, 0xf8, 0x33, 0x00, 0xd0, 0xf7, 0x33,
			0x00, 0x3c, 0x68, 0x29, 0x65, 0x24, 0xf9, 0x33, 0x00, 0x00, 0x00, 0x00,
		};

//		r = update_feature_report( sv->udev[USB_DEV_HMD], 0, vive_magic_power_off1, sizeof( vive_magic_power_off1 ) );
//		SV_INFO( "UCR: %d", r );
//		if( r != sizeof( vive_magic_power_off1 ) ) return 5;

		if (sv->udev[USB_DEV_HMD])
		{
			r = update_feature_report( sv->udev[USB_DEV_HMD], 0, vive_magic_power_off2, sizeof( vive_magic_power_off2 ) );
			SV_INFO( "UCR: %d", r );
			if( r != sizeof( vive_magic_power_off2 ) ) return 5;
		}
	}
	return 0;
}

int survive_vive_send_haptic(SurviveObject * so, uint8_t reserved, uint16_t pulseHigh, uint16_t pulseLow, uint16_t repeatCount)
{
	SurviveViveData *sv = (SurviveViveData*)so->driver;
	SurviveContext * ctx = so->ctx;

	if (NULL == sv)
	{
		return -500;
	}

	int r;
	uint8_t vive_controller_haptic_pulse[64] = { 
		0xff, 0x8f, 0x07, 0x00, 
		pulseHigh & 0xff00 >> 8,  pulseHigh & 0xff,
		pulseLow & 0xff00 >> 8,  pulseLow & 0xff,
		repeatCount & 0xff00 >> 8,  repeatCount & 0xff,
	};

	r = update_feature_report(sv->udev[USB_DEV_WATCHMAN1], 0, vive_controller_haptic_pulse, sizeof(vive_controller_haptic_pulse));
	r = getupdate_feature_report(sv->udev[USB_DEV_WATCHMAN1], 0, vive_controller_haptic_pulse, sizeof(vive_controller_haptic_pulse));
	//SV_INFO("UCR: %d", r);
	if (r != sizeof(vive_controller_haptic_pulse)) 
	{
		SV_ERROR("HAPTIC FAILED **************************\n"); 
		return -1;
	}

	return 0;

	//for (int j = 0; j < 40; j++)
	//{
	//	for (int i = 0; i < 0x1; i++)
	//	{
	//		//uint8_t vive_controller_haptic_pulse[64] = { 0xff, 0x8f, 0x7, 0 /*right*/, 0xFF /*period on*/, 0xFF/*period on*/, 0xFF/*period off*/, 0xFF/*period off*/, 0xFF /* repeat Count */, 0xFF /* repeat count */ };
	//		//uint8_t vive_controller_haptic_pulse[64] = { 0xff, 0x8f, 0x07, 0x00, 0xf4, 0x01, 0xb5, 0xa2, 0x01, 0x00 }; // data taken from Nairol's captures
	//		uint8_t vive_controller_haptic_pulse[64] = { 0xff, 0x8f, 0x07, 0x00, 0xf4, 0x01, 0xb5, 0xa2, 0x01 * j, 0x00 };
	//		r = update_feature_report(sv->udev[USB_DEV_WATCHMAN1], 0, vive_controller_haptic_pulse, sizeof(vive_controller_haptic_pulse));
	//		r = getupdate_feature_report(sv->udev[USB_DEV_WATCHMAN1], 0, vive_controller_haptic_pulse, sizeof(vive_controller_haptic_pulse));
	//		if (r != sizeof(vive_controller_haptic_pulse)) printf("HAPTIC FAILED **************************\n"); // return 5;
	//		OGUSleep(5000);
	//	}

	//	OGUSleep(20000);
	//}

	////OGUSleep(5000);
	//return 0;
}

void survive_vive_usb_close( SurviveViveData * sv )
{
	int i;
#ifdef HIDAPI
	for( i = 0; i < MAX_USB_DEVS; i++ )
	{
		if( sv->udev[i] )
			hid_close( sv->udev[i] );
	}
	for( i = 0; i < MAX_USB_DEVS; i++ )
	{
		OGJoinThread( sv->servicethread[i] );
	}
	//This is global, don't do it on account of other tasks.
	//hid_exit();

#else
	for( i = 0; i < MAX_USB_DEVS; i++ )
	{
	    libusb_close( sv->udev[i] );
	}
    libusb_exit(sv->usbctx);	
#endif
}

int survive_vive_usb_poll( SurviveContext * ctx, void * v )
{
#ifdef HIDAPI
	OGUnlockMutex( GlobalRXUSBMutx );
	OGUSleep( 100 );
	OGUnlockMutex( GlobalRXUSBMutx );
	return 0;
#else
	SurviveViveData * sv = v;
	sv->read_count++;
	int r = libusb_handle_events( sv->usbctx );
	if( r )
	{
		SurviveContext * ctx = sv->ctx;
		SV_ERROR("Libusb poll failed. %d (%s)", r, libusb_error_name(r));
	}
	return r;
#endif
	return 0;
}


int survive_get_config( char ** config, SurviveViveData * sv, int devno, int iface, int send_extra_magic )
{
	SurviveContext * ctx = sv->ctx;
	int ret, count = 0, size = 0;
	uint8_t cfgbuff[64];
	uint8_t compressed_data[8192];
	uint8_t uncompressed_data[65536];
	USBHANDLE dev = sv->udev[devno];

	if( send_extra_magic )
	{
		uint8_t cfgbuffwide[65];

		memset( cfgbuffwide, 0, sizeof( cfgbuff ) );
		cfgbuffwide[0] = 0x01;
		ret = hid_get_feature_report_timeout( dev, iface, cfgbuffwide, sizeof( cfgbuffwide ) );
		OGUSleep(1000);

		int k;
		
		uint8_t cfgbuff_send[64] = { 0xff, 0x83 };
			
		#ifdef HIDAPI
		//XXX TODO WRITEME
		for( k = 0; k < 10; k++ )
		{
			OGUSleep( 1000);
		}
	
		#else
		//Switch mode to pull config?
		for( k = 0; k < 10; k++ )
		{
			update_feature_report( dev, iface, cfgbuff_send, 64 );
			OGUSleep(1000);
		}
		#endif

		cfgbuffwide[0] = 0xff;
		ret = hid_get_feature_report_timeout( dev, iface, cfgbuffwide, sizeof( cfgbuffwide ) );
		OGUSleep(1000);
	}

	// Send Report 16 to prepare the device for reading config info
	memset( cfgbuff, 0, sizeof( cfgbuff ) );
	cfgbuff[0] = 0x10;
	if( ( ret = hid_get_feature_report_timeout( dev, iface, cfgbuff, sizeof( cfgbuff ) ) ) < 0 )
	{
		SV_INFO( "Could not get survive config data for device %d:%d", devno, iface );
		return -1;
	}


	// Now do a bunch of Report 17 until there are no bytes left
	cfgbuff[1] = 0xaa;
	cfgbuff[0] = 0x11;
	do
	{
		if( (ret = hid_get_feature_report_timeout(dev, iface, cfgbuff, sizeof( cfgbuff ) ) ) < 0 )
		{
			SV_INFO( "Could not read config data (after first packet) on device %d:%d (count: %d)\n", devno, iface, count );
			return -2;
		}

		size = cfgbuff[1];

		if( !size ) break;

		if( size > 62 )
		{
			SV_INFO( "Too much data (%d) on packet from config for device %d:%d (count: %d)", size, devno, iface, count );
			return -3;
		}

		if( count + size >= sizeof( compressed_data ) )
		{
			SV_INFO( "Configuration length too long %d:%d (count: %d)", devno, iface, count );
			return -4;
		}

        memcpy( &compressed_data[count], cfgbuff + 2, size );
		count += size;
	} while( 1 );

	if( count == 0 )
	{
		SV_INFO( "Empty configuration for %d:%d", devno, iface );
		return -5;
	}

	SV_INFO( "Got config data length %d", count );
	
	int len = survive_simple_inflate( ctx, compressed_data, count, uncompressed_data, sizeof(uncompressed_data)-1 );
	if( len <= 0 )
	{
		SV_INFO( "Error: data for config descriptor %d:%d is bad. (%d)", devno, iface, len );
		return -5;
	}

	*config = malloc( len + 1 );
	memcpy( *config, uncompressed_data, len );

	char fstname[128];
	sprintf( fstname, "calinfo/%d.json", devno );
	FILE * f = fopen( fstname, "wb" );
	fwrite( uncompressed_data, len, 1, f );
	fclose( f );

	return len;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

#define POP1  (*(readdata++))

#ifndef _MSC_VER
struct __attribute__((__packed__)) unaligned_16_t {
    int16_t v;
};
struct __attribute__((__packed__)) unaligned_32_t {
    int32_t v;
};
struct __attribute__((__packed__)) unaligned_u16_t {
    uint16_t v;
};
struct __attribute__((__packed__)) unaligned_u32_t {
    uint32_t v;
};
#else
struct unaligned_16_t {
	int16_t v;
};
struct unaligned_32_t {
	int32_t v;
};
struct unaligned_u16_t {
	uint16_t v;
};
struct unaligned_u32_t {
	uint32_t v;
};
#endif

#define POP2  ((((( struct unaligned_u16_t*)((readdata+=2)-2))))->v)
#define POP4  ((((( struct unaligned_u32_t*)((readdata+=4)-4))))->v)

void calibrate_acc(SurviveObject* so, FLT* agm) {
	if (so->acc_bias != NULL) {
		agm[0] -= so->acc_bias[0];
		agm[1] -= so->acc_bias[1];
		agm[2] -= so->acc_bias[2];
	}

	if (so->acc_scale != NULL) {
		agm[0] *= so->acc_scale[0];
		agm[1] *= so->acc_scale[1];
		agm[2] *= so->acc_scale[2];
	}

	quatrotatevector(agm, so->relative_imu_pose.Rot, agm);
}

void calibrate_gyro(SurviveObject* so, FLT* agm) {
	if (so->gyro_bias != NULL) {
		agm[0] -= so->gyro_bias[0];
		agm[1] -= so->gyro_bias[1];
		agm[2] -= so->gyro_bias[2];
	}

	if (so->gyro_scale != NULL) {
		agm[0] *= so->gyro_scale[0];
		agm[1] *= so->gyro_scale[1];
		agm[2] *= so->gyro_scale[2];
	}

	quatrotatevector(agm, so->relative_imu_pose.Rot, agm);
}

typedef struct 
{
	// could use a bitfield here, but since this data is short-lived,
	// the space savings probably isn't worth the processing overhead.
	uint8_t pressedButtonsValid;
	uint8_t triggerOfBatteryValid;
	uint8_t batteryChargeValid;
	uint8_t hardwareIdValid;
	uint8_t touchpadHorizontalValid;
	uint8_t touchpadVerticalValid;
	uint8_t triggerHighResValid;

	uint32_t pressedButtons;
	uint16_t triggerOrBattery;
	uint8_t  batteryCharge;
	uint32_t hardwareId;
	uint16_t touchpadHorizontal;
	uint16_t touchpadVertical;
	uint16_t triggerHighRes;
} buttonEvent;

void incrementAndPostButtonQueue(SurviveContext *ctx)
{
	ButtonQueueEntry *entry = &(ctx->buttonQueue.entry[ctx->buttonQueue.nextWriteIndex]);

	if ((ctx->buttonQueue.nextWriteIndex+1)% BUTTON_QUEUE_MAX_LEN == ctx->buttonQueue.nextReadIndex)
	{
		// There's not enough space to write this entry.  Clear it out and move along
		//printf("Button Buffer Full\n");
		memset(entry, 0, sizeof(ButtonQueueEntry));
		return;
	}
	entry->isPopulated = 1;
	ctx->buttonQueue.nextWriteIndex++;
	// if we've exceeded the size of the buffer, loop around to the beginning.
	if (ctx->buttonQueue.nextWriteIndex >= BUTTON_QUEUE_MAX_LEN)
	{
		ctx->buttonQueue.nextWriteIndex = 0;
	}
	OGUnlockSema(ctx->buttonQueue.buttonservicesem);

	// clear out any old data in the entry so we always start with a clean slate.
	entry = &(ctx->buttonQueue.entry[ctx->buttonQueue.nextWriteIndex]);
	memset(entry, 0, sizeof(ButtonQueueEntry));
}

// important!  This must be the only place that we're posting to the buttonEntryQueue
// if that ever needs to be changed, you will have to add locking so that only one
// thread is posting at a time.
void registerButtonEvent(SurviveObject *so, buttonEvent *event)
{
	ButtonQueueEntry *entry = &(so->ctx->buttonQueue.entry[so->ctx->buttonQueue.nextWriteIndex]);

	memset(entry, 0, sizeof(ButtonQueueEntry));

	entry->so = so;
	if (event->pressedButtonsValid)
	{
		//printf("trigger %8.8x\n", event->triggerHighRes);
		for (int a=0; a < 16; a++)
		{
			if (((event->pressedButtons) & (1<<a)) != ((so->buttonmask) & (1<<a)))
			{
				// Hey, the button did something
				if (event->pressedButtons & (1 << a))
				{
					// it went down
					entry->eventType = BUTTON_EVENT_BUTTON_DOWN;
				}
				else
				{
					// it went up
					entry->eventType = BUTTON_EVENT_BUTTON_UP;
				}
				entry->buttonId = a;
				if (entry->buttonId == 0)
				{
					// this fixes 2 issues.  First, is the a button id of 0 indicates no button pressed.
					// second is that the trigger shows up as button 0 coming from the wireless controller,
					// but we infer it from the position on the wired controller.  On the wired, we treat it
					// as buttonId 24 (look further down in this function)
					entry->buttonId = 24;
				}
				incrementAndPostButtonQueue(so->ctx);
				entry = &(so->ctx->buttonQueue.entry[so->ctx->buttonQueue.nextWriteIndex]);
			}
		}
		// if the trigger button is depressed & it wasn't before
		if ((((event->pressedButtons) & (0xff000000)) == 0xff000000) &&
			((so->buttonmask) & (0xff000000)) != 0xff000000)
		{
			entry->eventType = BUTTON_EVENT_BUTTON_DOWN;
			entry->buttonId = 24;
			incrementAndPostButtonQueue(so->ctx);
			entry = &(so->ctx->buttonQueue.entry[so->ctx->buttonQueue.nextWriteIndex]);
		}
		// if the trigger button isn't depressed but it was before
		else if ((((event->pressedButtons) & (0xff000000)) != 0xff000000) &&
			((so->buttonmask) & (0xff000000)) == 0xff000000)
		{
			entry->eventType = BUTTON_EVENT_BUTTON_UP;
			entry->buttonId = 24;
			incrementAndPostButtonQueue(so->ctx);
			entry = &(so->ctx->buttonQueue.entry[so->ctx->buttonQueue.nextWriteIndex]);
		}
	}
	if (event->triggerHighResValid)
	{
		if (so->axis1 != event->triggerHighRes)
		{
			entry->eventType = BUTTON_EVENT_AXIS_CHANGED;
			entry->axis1Id = 1;
			entry->axis1Val = event->triggerHighRes;
			incrementAndPostButtonQueue(so->ctx);
			entry = &(so->ctx->buttonQueue.entry[so->ctx->buttonQueue.nextWriteIndex]);

		}
	}
	if ((event->touchpadHorizontalValid) && (event->touchpadVerticalValid))
	{
		if ((so->axis2 != event->touchpadHorizontal) ||
			(so->axis3 != event->touchpadVertical))
		{
			entry->eventType = BUTTON_EVENT_AXIS_CHANGED;
			entry->axis1Id = 2;
			entry->axis1Val = event->touchpadHorizontal;
			entry->axis2Id = 3;
			entry->axis2Val = event->touchpadVertical;
			incrementAndPostButtonQueue(so->ctx);
			entry = &(so->ctx->buttonQueue.entry[so->ctx->buttonQueue.nextWriteIndex]);

		}
	}

	if (event->pressedButtonsValid)
	{
		so->buttonmask = event->pressedButtons;
	}
	if (event->batteryChargeValid)
	{
		so->charge = event->batteryCharge;
	}
	if (event->touchpadHorizontalValid)
	{
		so->axis2 = event->touchpadHorizontal;
	}
	if (event->touchpadVerticalValid)
	{
		so->axis3 = event->touchpadVertical;
	}
	if (event->triggerHighResValid)
	{
		so->axis1 = event->triggerHighRes;
	}
}

uint8_t isPopulated;  //probably can remove this given the semaphore in the parent struct.   helps with debugging
uint8_t eventType;
uint8_t buttonId;
uint8_t axis1Id;
uint16_t axis1Val;
uint8_t axis2Id;
uint16_t axis2Val;
SurviveObject *so;


static void handle_watchman( SurviveObject * w, uint8_t * readdata )
{
	uint8_t startread[29];
	memcpy( startread, readdata, 29 );

#if 0
	printf( "DAT:     " );
		for(int i = 0; i < 29; i++ )
		{
			printf( "%02x ", readdata[i] );
		}
		printf("\n");
#endif

	uint8_t time1 = POP1;
	uint8_t qty = POP1;
	uint8_t time2 = POP1;
	uint8_t type = POP1;


	qty-=2;
	int propset = 0;
	int doimu = 0;

	if( (type & 0xf0) == 0xf0 )
	{
		buttonEvent bEvent;
		memset(&bEvent, 0, sizeof(bEvent));

		propset |= 4;
		//printf( "%02x %02x %02x %02x\n", qty, type, time1, time2 );
		type &= ~0x10;

		if( type & 0x01 )
		{
			
			qty-=1;
			bEvent.pressedButtonsValid = 1;
			bEvent.pressedButtons = POP1;

			//printf("buttonmask is %d\n", w->buttonmask);
			type &= ~0x01;
		}
		if( type & 0x04 ) 
		{
			qty-=1;
			bEvent.triggerHighResValid = 1;
			bEvent.triggerHighRes = ( POP1 ) * 128;
			type &= ~0x04;
		}
		if( type & 0x02 )
		{
			qty-=4;
			bEvent.touchpadHorizontalValid = 1;
			bEvent.touchpadVerticalValid = 1;

			bEvent.touchpadHorizontal = POP2;
			bEvent.touchpadVertical = POP2;
			type &= ~0x02;
		}

		if (bEvent.pressedButtonsValid || bEvent.triggerHighResValid || bEvent.touchpadHorizontalValid)
		{
			registerButtonEvent(w, &bEvent);
		}


		//XXX TODO: Is this correct?  It looks SO WACKY
		type &= 0x7f;
		if( type == 0x68 ) doimu = 1;
		type &= 0x0f;
		if( type == 0x00 && qty ) { type = POP1; qty--; }
	}

	if( type == 0xe1 )
	{
		propset |= 1;
		w->charging = readdata[0]>>7;
		w->charge = POP1&0x7f; qty--;
		w->ison = 1; 
		if( qty )
		{
			qty--;
			type = POP1; //IMU usually follows.
		}
	}

	if( ( ( type & 0xe8 ) == 0xe8 ) || doimu ) //Hmm, this looks kind of yucky... we can get e8's that are accelgyro's but, cleared by first propset.
	{
		propset |= 2;
		FLT agm[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
		int j;
		for (j = 0; j < 6; j++)
			agm[j] = (int16_t)(readdata[j * 2 + 1] | (readdata[j * 2 + 2] << 8));
		calibrate_acc(w, agm);
		calibrate_gyro(w, agm+3);
		w->ctx->imuproc( w, 3, agm, (time1<<24)|(time2<<16)|readdata[0], 0 );
		readdata += 13; qty -= 13;

		type &= ~0xe8;
		if( qty )
		{
			qty--;
			type = POP1;
		}
	}


	if( qty )
	{
		qty++;
		readdata--;
		*readdata = type; //Put 'type' back on stack.
		uint8_t * mptr = readdata + qty-3-1; //-3 for timecode, -1 to 

#ifdef DEBUG_WATCHMAN
		printf( "_%s ", w->codename);
		for(int i = 0; i < qty; i++ )
		{
			printf( "%02x ", readdata[i] );
		}
		printf("\n");
#endif


		uint32_t mytime = (mptr[3] << 16)|(mptr[2] << 8)|(mptr[1] << 0);

		uint32_t times[20];
		const int nrtime = sizeof(times)/sizeof(uint32_t);
		int timecount = 0;
		int leds;
		int fault = 0;

		///Handle uint32_tifying (making sure we keep it incrementing)
		uint32_t llt = w->last_lighttime;
		uint32_t imumsb = time1<<24;
		mytime |= imumsb;

		//Compare mytime to llt

		int diff = mytime - llt;
		if( diff < -0x1000000 )
			mytime += 0x1000000;
		else if( diff > 0x100000 )
			mytime -= 0x1000000;

		w->last_lighttime = mytime;

		times[timecount++] = mytime;
#ifdef DEBUG_WATCHMAN
		printf( "_%s Packet Start Time: %d\n", w->codename, mytime );
#endif	

		//First, pull off the times, starting with the current time, then all the delta times going backwards.
		{
			while( mptr - readdata > (timecount>>1) )
			{
				uint32_t arcane_value = 0;
				//ArcanePop (Pop off values from the back, forward, checking if the MSB is set)
				do
				{
					uint8_t ap = *(mptr--);
					arcane_value |= (ap&0x7f);
					if( ap & 0x80 )  break;
					arcane_value <<= 7;
				} while(1);
				times[timecount++] = (mytime -= arcane_value);
#ifdef DEBUG_WATCHMAN
				printf( "_%s Time: %d  newtime: %d\n", w->codename, arcane_value, mytime );
#endif
			}

			leds = timecount>>1;
			//Check that the # of sensors at the beginning match the # of parameters we would expect.
			if( timecount & 1 ) { fault = 1; goto end; }				//Inordinal LED count
			if( leds != mptr - readdata + 1 ) { fault = 2; goto end; }	//LED Count does not line up with parameters
		}


		LightcapElement les[10];
		int lese = 0; //les's end


		//Second, go through all LEDs and extract the lightevent from them. 
		{
			uint8_t *marked;
			marked = alloca(nrtime);
			memset( marked, 0, nrtime );
			int i, parpl = 0;
			timecount--;
			int timepl = 0;

			//This works, but usually returns the values in reverse end-time order.
			for( i = 0; i < leds; i++ )
			{
				int led = readdata[i];
				int adv = led & 0x07;
				led >>= 3;

				while( marked[timepl] ) timepl++;

#ifdef DEBUG_WATCHMAN
				int i;
				printf( "TP %d   TC: %d : ", timepl, timecount );
				for( i = 0; i < nrtime; i++ )
				{
					printf( "%d", marked[i] );
				}
				printf( "\n" );
#endif

				if( timepl > timecount ) { fault = 3; goto end; }         //Ran off max of list.
				uint32_t endtime = times[timepl++];

				int end = timepl + adv;
				if( end > timecount ) { fault = 4; goto end; } //end referencing off list
				if( marked[end] > 0 ) { fault = 5; goto end; } //Already marked trying to be used.
				uint32_t starttime = times[end];
				marked[end] = 1;

				//Insert all lighting things into a sorted list.  This list will be
				//reverse sorted, but that is to minimize operations.  To read it
				//in sorted order simply read it back backwards.
				//Use insertion sort, since we should most of the time, be in order.
				LightcapElement * le = &les[lese++];
				le->sensor_id = led;

				if( (uint32_t)(endtime - starttime) > 65535 ) { fault = 6; goto end; } //Length of pulse dumb.
				le->length = endtime - starttime;
				le->timestamp = starttime;

#ifdef DEBUG_WATCHMAN
				printf( "_%s Event: %d %d %d-%d\n", w->codename, led, le->length, endtime, starttime );
#endif
				int swap = lese-2;
				while( swap >= 0 && les[swap].timestamp < les[swap+1].timestamp )
				{
					LightcapElement l;
					memcpy( &l, &les[swap], sizeof( l ) );
					memcpy( &les[swap], &les[swap+1], sizeof( l ) );
					memcpy( &les[swap+1], &l, sizeof( l ) );
					swap--;
				}
			}
		}

		int i;
		for( i = lese-1; i >= 0; i-- )
		{
			//printf( "%d: %d [%d]\n", les[i].sensor_id, les[i].length, les[i].timestamp );
			handle_lightcap( w, &les[i] );
		}

		return;
	end:
		{
			SurviveContext * ctx = w->ctx;
			SV_INFO( "Light decoding fault: %d", fault );
		}
	}
}

static inline uint16_t read_buffer16(uint8_t *readdata, int idx) {
	uint16_t rtn;
	memcpy(&rtn, readdata + idx, sizeof(uint16_t));
	return rtn;
}
static inline uint32_t read_buffer32(uint8_t *readdata, int idx) {
	uint32_t rtn;
	memcpy(&rtn, readdata + idx, sizeof(uint32_t));
	return rtn;
}

void survive_data_cb( SurviveUSBInterface * si )
{
	int size = si->actual_len;
	SurviveContext * ctx = si->ctx;

	int iface = si->which_interface_am_i;
	SurviveObject * obj = si->assoc_obj;
	uint8_t * readdata = si->buffer;

	int id = POP1;
//	printf( "%16s Size: %2d ID: %d / %d\n", si->hname, size, id, iface );


#if 0
	if(  si->which_interface_am_i == 5 )
	{
		int i;
		printf( "%16s: %d: %d: %d: ", si->hname, id, size, sizeof(LightcapElement) );
		for( i = 0; i < size-1; i++ )
		{
			printf( "%02x ", readdata[i] );
		}
		printf( "\n" );
		
	}
#endif 
	switch( si->which_interface_am_i )
	{
	case USB_IF_HMD:
	{
		SurviveObject * headset = obj;
		readdata+=2;
		headset->buttonmask = POP1;		//Lens
		headset->axis2 = POP2;			//Lens Separation
		readdata+=2;
		headset->buttonmask |= POP1;	//Button
		readdata+=3;
		readdata++;						//Proxchange, No change = 0, Decrease = 1, Increase = 2
		readdata++;
		headset->axis3 = POP2;			//Proximity  	<< how close to face are you?  Less than 80 = not on face.
		headset->axis1 = POP2;			//IPD   		<< what is this?
		headset->ison = 1;
		break;
	}
	case USB_IF_HMD_IMU_LH:
	case USB_IF_W_WATCHMAN1:
	case USB_IF_TRACKER0:
	case USB_IF_TRACKER1:
	{
		int i;
		//printf( "%d -> ", size );
		for( i = 0; i < 3; i++ )
		{
            struct unaligned_16_t * acceldata = (struct unaligned_16_t *)readdata;
			readdata += 12;
			uint32_t timecode = POP4;
			uint8_t code = POP1;
			//printf( "%d ", code );
			int8_t cd = code - obj->oldcode;

			if( cd > 0 )
			{
				obj->oldcode = code;

				//XXX XXX BIG TODO!!! Actually recal gyro data.
				FLT agm[9] = {acceldata[0].v,
							  acceldata[1].v,
							  acceldata[2].v,
							  acceldata[3].v,
							  acceldata[4].v,
							  acceldata[5].v,
							  0,
							  0,
							  0};

				calibrate_acc(obj, agm);
				calibrate_gyro(obj, agm + 3);
				ctx->imuproc( obj, 3, agm, timecode, code );
			}
		}
		if (id != 32)
		{
			int a=0; // set breakpoint here
		}
		//DONE OK.
		break;
	}
	case USB_IF_WATCHMAN1:
	case USB_IF_WATCHMAN2:
	{
		SurviveObject * w = obj;
		if( id == 35 )
		{
			handle_watchman( w, readdata);
		}
		else if( id == 36 )
		{
			handle_watchman( w, readdata);
			handle_watchman( w, readdata+29 );
		}
		else if( id == 38 )
		{
			w->ison = 0; // turning off
		}
		else
		{
			SV_INFO( "Unknown watchman code %d\n", id );
		}
		break;
	}
	case USB_IF_LIGHTCAP:
	case USB_IF_TRACKER1_LIGHTCAP:
	{
		int i;
		for( i = 0; i < 9; i++ )
		{
			LightcapElement le;
			le.sensor_id = POP1;
			le.length = POP2;
			le.timestamp = POP4;
			if( le.sensor_id > 0xfd ) continue;
			handle_lightcap( obj, &le );
		}		
		break;
	}
    case USB_IF_W_WATCHMAN1_LIGHTCAP:
    case USB_IF_TRACKER0_LIGHTCAP:
    {
        int i=0;
            for( i = 0; i < 7; i++ )
            {
                    unsigned short *sensorId = (unsigned short *)readdata;
                    unsigned short *length = (unsigned short *)(&(readdata[2]));
                    unsigned long *time = (unsigned long *)(&(readdata[4]));
                    LightcapElement le;
                    le.sensor_id = (uint8_t)POP2;
                    le.length = POP2;
                    le.timestamp = POP4;
                    if( le.sensor_id > 0xfd ) continue;  //
                    handle_lightcap( obj, &le );
            }
            break;
    
            if (id != 33)
            {
                    int a = 0; // breakpoint here
            }
    }
	case USB_IF_TRACKER0_BUTTONS:
	case USB_IF_TRACKER1_BUTTONS:
	case USB_IF_W_WATCHMAN1_BUTTONS:
	{
		if (1 == id)
		{
			//0x00	uint8	1	reportID	HID report identifier(= 1)
			//0x02	uint16	2	reportType(? )	0x0B04: Ping(every second) / 0x3C01 : User input
			//0x04	uint32	4	reportCount	Counter that increases with every report
			//0x08	uint32	4	pressedButtons	Bit field, see below for individual buttons
			//0x0C	uint16	2	triggerOrBattery	Analog trigger value(user input) / Battery voltage ? (ping)
			//0x0E	uint8	1	batteryCharge	Bit 7 : Charging / Bit 6..0 : Battery charge in percent
			//0x10	uint32	4	hardwareID	Hardware ID(user input) / 0x00000000 (ping)
			//0x14	int16	2	touchpadHorizontal	Horizontal thumb position(Left : -32768 / Right : 32767)
			//0x16	int16	2	touchpadVertical	Vertical thumb position(Bottom : -32768 / Top : 32767)
			//0x18 ? 2 ? unknown
			//0x1A	uint16	2	triggerHighRes	Analog trigger value with higher resolution
			//0x1C ? 24 ? unknown
			//0x34	uint16	2	triggerRawMaybe	Analog trigger value, maybe raw sensor data
			//0x36 ? 8 ? unknown
			//0x3E	uint8	1	someBitFieldMaybe	0x00 : ping / 0x64 : user input
			//0x3F ? 1 ? unknown

			//typedef struct
			//{
			//	//uint8_t reportId;
			//	uint16_t reportType;
			//	uint32_t reportCount;
			//	uint32_t pressedButtons;
			//	uint16_t  triggerOrBattery;
			//	uint8_t batteryCharge;
			//	uint32_t hardwareId;
			//	int16_t  touchpadHorizontal;
			//	int16_t  touchpadVertical;
			//	uint16_t unknown1;
			//	uint16_t triggerHighRes;
			//	uint8_t  unknown2;
			//	uint8_t  unknown3;
			//	uint8_t  unknown4;
			//	uint16_t triggerRaw;
			//	uint8_t  unknown5;
			//	uint8_t  unknown6; // maybe some bitfield?
			//	uint8_t  unknown7;
			//} usb_buttons_raw;

			//usb_buttons_raw *raw = (usb_buttons_raw*) readdata;
			if (read_buffer16(readdata, 0) == 0x100) {
				buttonEvent bEvent;
				memset(&bEvent, 0, sizeof(bEvent));

				bEvent.pressedButtonsValid = 1;
				bEvent.pressedButtons = read_buffer32(readdata, 0x7);
				bEvent.triggerHighResValid = 1;
				//bEvent.triggerHighRes = raw->triggerHighRes; 
				//bEvent.triggerHighRes = (raw->pressedButtons & 0xff000000) >> 24; // this seems to provide the same data at 2x the resolution as above
				//bEvent.triggerHighRes = raw->triggerRaw;

				bEvent.triggerHighRes = read_buffer16(readdata, 0x19);
				bEvent.touchpadHorizontalValid = 1;
				//bEvent.touchpadHorizontal = raw->touchpadHorizontal;
				bEvent.touchpadHorizontal = read_buffer16(readdata, 0x13);
				bEvent.touchpadVerticalValid = 1;
				//bEvent.touchpadVertical = raw->touchpadVertical;
				bEvent.touchpadVertical = read_buffer16(readdata, 0x15);

				//printf("%4.4x\n", bEvent.triggerHighRes);
				registerButtonEvent(obj, &bEvent);

				//printf("Buttons: %8.8x\n", raw->pressedButtons);
			}
			int a = 0;
		}
		else
		{
			int a = 0;// breakpoint here
		}
	}
	default:
	{
		int a = 0; // breakpoint here
	}

	}
}










///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////



static int LoadConfig( SurviveViveData * sv, SurviveObject * so, int devno, int iface, int extra_magic )
{
	SurviveContext * ctx = sv->ctx;
	char * ct0conf = 0;
	int len = survive_get_config( &ct0conf, sv, devno, iface, extra_magic );
	SV_INFO( "Loading config: %d", len );

	if( len < 0 )
	{
		survive_remove_object(ctx, so);
		return len;
	}

	{
	  char raw_fname[100];
	  sprintf( raw_fname, "%s_config.json", so->codename );
	  FILE * f = fopen( raw_fname, "w" );
	  fwrite( ct0conf, strlen(ct0conf), 1, f );
	  fclose( f );
	}

	return so->ctx->configfunction(so, ct0conf, len);
}


int survive_vive_close( SurviveContext * ctx, void * driver )
{
	SurviveViveData * sv = driver;
 
	survive_vive_usb_close( sv );
	return 0;
}

int DriverRegHTCVive( SurviveContext * ctx )
{
	const char *playback_dir = survive_configs(ctx, "playback", SC_GET, "");
	if(strlen(playback_dir) != 0) {
	  SV_INFO("Playback is active; disabling USB driver");
	  return 0;
	}

	SurviveViveData * sv = calloc(1, sizeof(SurviveViveData) );
	SurviveObject * hmd = survive_create_hmd(ctx, "HTC", sv);
	SurviveObject * wm0 = survive_create_wm0(ctx, "HTC", sv, 0);
	SurviveObject * wm1 = survive_create_wm1(ctx, "HTC", sv, 0);
	SurviveObject * tr0 = survive_create_tr0(ctx, "HTC", sv);
	SurviveObject * tr1 = survive_create_tr1(ctx, "HTC", sv);
	SurviveObject * ww0 = survive_create_ww0(ctx, "HTC", sv);

	sv->ctx = ctx;
	
	#ifdef _WIN32
		CreateDirectoryA("calinfo", NULL);
	#elif defined WINDOWS
		mkdir( "calinfo" );
	#else
		mkdir( "calinfo", 0755 );
	#endif

	//USB must happen last.
	if (survive_usb_init(sv, hmd, wm0, wm1, tr0, tr1, ww0)) {
			// TODO: Cleanup any libUSB stuff sitting around.
			goto fail_gracefully;
	}

	if( sv->udev[USB_DEV_HMD_IMU_LH] ||
	    sv->udev[USB_DEV_WATCHMAN1]  ||
	    sv->udev[USB_DEV_WATCHMAN2]  ||
	    sv->udev[USB_DEV_TRACKER0]   ||
	    sv->udev[USB_DEV_TRACKER1]   ||
	    sv->udev[USB_DEV_W_WATCHMAN1] ) {
	  survive_add_driver( ctx, sv, survive_vive_usb_poll, survive_vive_close, survive_vive_send_magic );
	} else {
	  SV_INFO("No USB devices detected");
	}

	//Next, pull out the config stuff.
	if (sv->udev[USB_DEV_HMD_IMU_LH] && LoadConfig(sv, hmd, USB_DEV_HMD_IMU_LH, 0, 0)) {
		SV_INFO("HMD config issue.");
	}
	if( sv->udev[USB_DEV_WATCHMAN1] && LoadConfig( sv, wm0, USB_DEV_WATCHMAN1, 0, 1 )) { SV_INFO( "Watchman 0 config issue." ); }
	if( sv->udev[USB_DEV_WATCHMAN2] && LoadConfig( sv, wm1, USB_DEV_WATCHMAN2, 0, 1 )) { SV_INFO( "Watchman 1 config issue." ); }
	if( sv->udev[USB_DEV_TRACKER0]  && LoadConfig( sv, tr0, USB_DEV_TRACKER0, 0, 0 )) { SV_INFO( "Tracker 0 config issue." ); }
	if( sv->udev[USB_DEV_TRACKER1]  && LoadConfig( sv, tr1, USB_DEV_TRACKER1, 0, 0 )) { SV_INFO( "Tracker 1 config issue." ); }
	if( sv->udev[USB_DEV_W_WATCHMAN1]  && LoadConfig( sv, ww0, USB_DEV_W_WATCHMAN1, 0, 0 )) { SV_INFO( "Wired Watchman 0 config issue." ); }


	return 0;
fail_gracefully:
	free( hmd );
	free( wm0 );
	free( wm1 );
	free( tr0 );
	free( tr1 );
	free( ww0 );
	survive_vive_usb_close( sv );
	free( sv );
	return -1;
}

REGISTER_LINKTIME( DriverRegHTCVive );