Pārlūkot izejas kodu

Added missing file from previous commit

Sam Lantinga 5 gadi atpakaļ
vecāks
revīzija
0641711e9f

+ 1174 - 0
src/joystick/hidapi/SDL_hidapi_steam.c

@@ -0,0 +1,1174 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include "SDL_hints.h"
+#include "SDL_log.h"
+#include "SDL_events.h"
+#include "SDL_timer.h"
+#include "SDL_joystick.h"
+#include "SDL_gamecontroller.h"
+#include "../SDL_sysjoystick.h"
+#include "SDL_hidapijoystick_c.h"
+
+
+
+#ifdef SDL_JOYSTICK_HIDAPI_STEAM
+
+/*****************************************************************************************************/
+
+#include <stdint.h>
+
+typedef enum
+{
+    false,
+    true
+} bool;
+
+typedef uint32_t uint32;
+typedef uint64_t uint64;
+    
+#include "steam/controller_constants.h"
+#include "steam/controller_structs.h"
+
+typedef struct SteamControllerStateInternal_t
+{
+    // Controller Type for this Controller State
+    uint32 eControllerType;
+
+    // If packet num matches that on your prior call, then the controller state hasn't been changed since 
+    // your last call and there is no need to process it
+    uint32 unPacketNum;
+    
+    // bit flags for each of the buttons
+    uint64 ulButtons;
+    
+    // Left pad coordinates
+    short sLeftPadX;
+    short sLeftPadY;
+    
+    // Right pad coordinates
+    short sRightPadX;
+    short sRightPadY;
+
+    // Center pad coordinates
+    short sCenterPadX;
+    short sCenterPadY;
+    
+    // Left analog stick coordinates
+    short sLeftStickX;
+    short sLeftStickY;
+
+    // Right analog stick coordinates
+    short sRightStickX;
+    short sRightStickY;
+    
+    unsigned short sTriggerL;
+    unsigned short sTriggerR;
+    
+    short sAccelX;
+    short sAccelY;
+    short sAccelZ;
+    
+    short sGyroX;
+    short sGyroY;
+    short sGyroZ;
+    
+    float sGyroQuatW;
+    float sGyroQuatX;
+    float sGyroQuatY;
+    float sGyroQuatZ;
+    
+    short sGyroSteeringAngle;
+    
+    unsigned short sBatteryLevel;
+
+    // Pressure sensor data.
+    unsigned short sPressurePadLeft;
+    unsigned short sPressurePadRight;
+    
+    unsigned short sPressureBumperLeft;
+    unsigned short sPressureBumperRight;
+    
+    // Internal state data
+    short sPrevLeftPad[2];
+    short sPrevLeftStick[2];
+} SteamControllerStateInternal_t;
+
+
+/* Defines for ulButtons in SteamControllerStateInternal_t */
+#define STEAM_RIGHT_TRIGGER_MASK            0x00000001
+#define STEAM_LEFT_TRIGGER_MASK             0x00000002
+#define STEAM_RIGHT_BUMPER_MASK             0x00000004
+#define STEAM_LEFT_BUMPER_MASK              0x00000008
+#define STEAM_BUTTON_0_MASK                 0x00000010    /* Y */
+#define STEAM_BUTTON_1_MASK                 0x00000020    /* B */
+#define STEAM_BUTTON_2_MASK                 0x00000040    /* X */
+#define STEAM_BUTTON_3_MASK                 0x00000080    /* A */
+#define STEAM_TOUCH_0_MASK                  0x00000100    /* DPAD UP */
+#define STEAM_TOUCH_1_MASK                  0x00000200    /* DPAD RIGHT */
+#define STEAM_TOUCH_2_MASK                  0x00000400    /* DPAD LEFT */
+#define STEAM_TOUCH_3_MASK                  0x00000800    /* DPAD DOWN */
+#define STEAM_BUTTON_MENU_MASK              0x00001000    /* SELECT */
+#define STEAM_BUTTON_STEAM_MASK             0x00002000    /* GUIDE */
+#define STEAM_BUTTON_ESCAPE_MASK            0x00004000    /* START */
+#define STEAM_BUTTON_BACK_LEFT_MASK         0x00008000
+#define STEAM_BUTTON_BACK_RIGHT_MASK        0x00010000
+#define STEAM_BUTTON_LEFTPAD_CLICKED_MASK   0x00020000
+#define STEAM_BUTTON_RIGHTPAD_CLICKED_MASK  0x00040000
+#define STEAM_LEFTPAD_FINGERDOWN_MASK       0x00080000
+#define STEAM_RIGHTPAD_FINGERDOWN_MASK      0x00100000
+#define STEAM_JOYSTICK_BUTTON_MASK            0x00400000
+#define STEAM_LEFTPAD_AND_JOYSTICK_MASK        0x00800000
+
+
+// Look for report version 0x0001, type WIRELESS (3), length >= 1 byte
+#define D0G_IS_VALID_WIRELESS_EVENT(data, len)    ((len) >= 5 && (data)[0] == 1 && (data)[1] == 0 && (data)[2] == 3 && (data)[3] >= 1)
+#define D0G_GET_WIRELESS_EVENT_TYPE(data)        ((data)[4])
+#define D0G_WIRELESS_DISCONNECTED    1
+#define D0G_WIRELESS_ESTABLISHED    2
+#define D0G_WIRELESS_NEWLYPAIRED    3
+
+#define D0G_IS_WIRELESS_DISCONNECT(data, len)    ( D0G_IS_VALID_WIRELESS_EVENT(data,len) && D0G_GET_WIRELESS_EVENT_TYPE(data) == D0G_WIRELESS_DISCONNECTED )
+
+#define MAX_REPORT_SEGMENT_PAYLOAD_SIZE    18
+/*
+ * SteamControllerPacketAssembler has to be used when reading output repots from controllers.
+ */
+typedef struct
+{
+    uint8_t uBuffer[ MAX_REPORT_SEGMENT_PAYLOAD_SIZE * 8 + 1 ];
+    int nExpectedSegmentNumber;
+    bool bIsBle;
+} SteamControllerPacketAssembler;
+
+
+#undef clamp
+#define clamp(val, min, max) (((val) > (max)) ? (max) : (((val) < (min)) ? (min) : (val)))
+
+#undef offsetof
+#define offsetof(s,m)    (size_t)&(((s *)0)->m)
+
+#ifdef DEBUG_STEAM_CONTROLLER
+#define DPRINTF(format, ...) printf(format, ##__VA_ARGS__)
+#define HEXDUMP(ptr, len) hexdump(ptr, len)
+#else
+#define DPRINTF(format, ...)
+#define HEXDUMP(ptr, len)
+#endif
+#define printf  SDL_Log
+
+#define MAX_REPORT_SEGMENT_SIZE        ( MAX_REPORT_SEGMENT_PAYLOAD_SIZE + 2 )
+#define CALC_REPORT_SEGMENT_NUM(index)  ( ( index / MAX_REPORT_SEGMENT_PAYLOAD_SIZE ) & 0x07 )
+#define REPORT_SEGMENT_DATA_FLAG    0x80
+#define REPORT_SEGMENT_LAST_FLAG    0x40
+#define BLE_REPORT_NUMBER        0x03
+
+#define STEAMCONTROLLER_TRIGGER_MAX_ANALOG 26000
+
+// Enable mouse mode when using the Steam Controller locally
+#undef ENABLE_MOUSE_MODE
+
+
+// Wireless firmware quirk: the firmware intentionally signals "failure" when performing
+// SET_FEATURE / GET_FEATURE when it actually means "pending radio round-trip". The only
+// way to make SET_FEATURE / GET_FEATURE work is to loop several times with a sleep. If
+// it takes more than 50ms to get the response for SET_FEATURE / GET_FEATURE, we assume
+// that the controller has failed.
+#define RADIO_WORKAROUND_SLEEP_ATTEMPTS 50
+#define RADIO_WORKAROUND_SLEEP_DURATION_US 500
+
+// This was defined by experimentation. 2000 seemed to work but to give that extra bit of margin, set to 3ms.
+#define CONTROLLER_CONFIGURATION_DELAY_US 3000
+
+static uint8_t GetSegmentHeader( int nSegmentNumber, bool bLastPacket )
+{
+    uint8_t header = REPORT_SEGMENT_DATA_FLAG;
+    header |= nSegmentNumber;
+    if ( bLastPacket )
+        header |= REPORT_SEGMENT_LAST_FLAG;
+    
+    return header;
+}
+
+static void hexdump( const uint8_t *ptr, int len )
+{
+    int i;
+    for ( i = 0; i < len ; ++i )
+        printf("%02x ", ptr[i]);
+    printf("\n");
+}
+
+static void ResetSteamControllerPacketAssembler( SteamControllerPacketAssembler *pAssembler )
+{
+    memset( pAssembler->uBuffer, 0, sizeof( pAssembler->uBuffer ) );
+    pAssembler->nExpectedSegmentNumber = 0;
+}
+
+static void InitializeSteamControllerPacketAssembler( SteamControllerPacketAssembler *pAssembler )
+{
+    /* We only support BLE devices right now */
+    pAssembler->bIsBle = true;
+    ResetSteamControllerPacketAssembler( pAssembler );
+}
+
+// Returns:
+//     <0 on error
+//     0 on not ready
+//     Complete packet size on completion
+static int WriteSegmentToSteamControllerPacketAssembler( SteamControllerPacketAssembler *pAssembler, const uint8_t *pSegment, int nSegmentLength )
+{
+    if ( pAssembler->bIsBle )
+    {
+        HEXDUMP( pSegment, nSegmentLength );
+
+        if ( pSegment[ 0 ] != BLE_REPORT_NUMBER )
+        {
+            // We may get keyboard/mouse input events until controller stops sending them
+            return 0;
+        }
+        
+        if ( nSegmentLength != MAX_REPORT_SEGMENT_SIZE )
+        {
+            printf( "Bad segment size! %d\n", (int)nSegmentLength );
+            hexdump( pSegment, nSegmentLength );
+            ResetSteamControllerPacketAssembler( pAssembler );
+            return -1;
+        }
+        
+        uint8_t uSegmentHeader = pSegment[ 1 ];
+        DPRINTF("GOT PACKET HEADER = 0x%x\n", uSegmentHeader);
+        
+        if ( ( uSegmentHeader & REPORT_SEGMENT_DATA_FLAG ) == 0 )
+        {
+            // We get empty segments, just ignore them
+            return 0;
+        }
+        
+        int nSegmentNumber = uSegmentHeader & 0x07;
+        if ( nSegmentNumber != pAssembler->nExpectedSegmentNumber )
+        {
+            ResetSteamControllerPacketAssembler( pAssembler );
+            
+            if ( nSegmentNumber )
+            {
+                // This happens occasionally
+                DPRINTF("Bad segment number, got %d, expected %d\n",
+                    nSegmentNumber, pAssembler->nExpectedSegmentNumber );
+                return -1;
+            }
+        }
+        
+        memcpy( pAssembler->uBuffer + nSegmentNumber * MAX_REPORT_SEGMENT_PAYLOAD_SIZE,
+               pSegment + 2, // ignore header and report number
+               MAX_REPORT_SEGMENT_PAYLOAD_SIZE );
+        
+        if ( uSegmentHeader & REPORT_SEGMENT_LAST_FLAG )
+        {
+            pAssembler->nExpectedSegmentNumber = 0;
+            return ( nSegmentNumber + 1 ) * MAX_REPORT_SEGMENT_PAYLOAD_SIZE;
+        }
+        
+        pAssembler->nExpectedSegmentNumber++;
+    }
+    else
+    {
+        // Just pass through
+        memcpy( pAssembler->uBuffer,
+               pSegment,
+               nSegmentLength );
+        return nSegmentLength;
+    }
+    
+    return 0;
+}
+
+#define BLE_MAX_READ_RETRIES    8
+
+static int SetFeatureReport( hid_device *dev, unsigned char uBuffer[65], int nActualDataLen )
+{
+    DPRINTF("SetFeatureReport %p %p %d\n", dev, uBuffer, nActualDataLen);
+    int nRet = -1;
+    bool bBle = true; // only wireless/BLE for now, though macOS could do wired in the future
+    
+    if ( bBle )
+    {
+        if ( nActualDataLen < 1 )
+            return -1;
+        
+        int nSegmentNumber = 0;
+        uint8_t uPacketBuffer[ MAX_REPORT_SEGMENT_SIZE ];
+        
+        // Skip report number in data
+        unsigned char *pBufferPtr = uBuffer + 1;
+        nActualDataLen--;
+        
+        while ( nActualDataLen > 0 )
+        {
+            int nBytesInPacket = nActualDataLen > MAX_REPORT_SEGMENT_PAYLOAD_SIZE ? MAX_REPORT_SEGMENT_PAYLOAD_SIZE : nActualDataLen;
+            
+            nActualDataLen -= nBytesInPacket;
+
+            // Construct packet
+            memset( uPacketBuffer, 0, sizeof( uPacketBuffer ) );
+            uPacketBuffer[ 0 ] = BLE_REPORT_NUMBER;
+            uPacketBuffer[ 1 ] = GetSegmentHeader( nSegmentNumber, nActualDataLen == 0 );
+            memcpy( &uPacketBuffer[ 2 ], pBufferPtr, nBytesInPacket );
+            
+            pBufferPtr += nBytesInPacket;
+            nSegmentNumber++;
+            
+            nRet = hid_send_feature_report( dev, uPacketBuffer, sizeof( uPacketBuffer ) );
+            DPRINTF("SetFeatureReport() ret = %d\n", nRet);
+        }
+    }
+    
+    return nRet;
+}
+
+static int GetFeatureReport( hid_device *dev, unsigned char uBuffer[65] )
+{
+    DPRINTF("GetFeatureReport( %p %p )\n", dev, uBuffer );
+    int nRet = -1;
+    bool bBle = true;
+
+    if ( bBle )
+    {
+        SteamControllerPacketAssembler assembler;
+        InitializeSteamControllerPacketAssembler( &assembler );
+        
+        int nRetries = 0;
+        uint8_t uSegmentBuffer[ MAX_REPORT_SEGMENT_SIZE ];
+        while( nRetries < BLE_MAX_READ_RETRIES )
+        {
+            memset( uSegmentBuffer, 0, sizeof( uSegmentBuffer ) );
+            uSegmentBuffer[ 0 ] = BLE_REPORT_NUMBER;
+            nRet = hid_get_feature_report( dev, uSegmentBuffer, sizeof( uSegmentBuffer ) );
+            DPRINTF( "GetFeatureReport ble ret=%d\n", nRet );
+            HEXDUMP( uSegmentBuffer, nRet );
+            
+            // Zero retry counter if we got data
+            if ( nRet > 2 && ( uSegmentBuffer[ 1 ] & REPORT_SEGMENT_DATA_FLAG ) )
+                nRetries = 0;
+            else
+                nRetries++;
+            
+            if ( nRet > 0 )
+            {
+                int nPacketLength = WriteSegmentToSteamControllerPacketAssembler( &assembler,
+                                                                                 uSegmentBuffer,
+                                                                                 nRet );
+                
+                if ( nPacketLength > 0 && nPacketLength < 65 )
+                {
+                    // Leave space for "report number"
+                    uBuffer[ 0 ] = 0;
+                    memcpy( uBuffer + 1, assembler.uBuffer, nPacketLength );
+                    return nPacketLength;
+                }
+            }
+            
+            
+        }
+        printf("Could not get a full ble packet after %d retries\n", nRetries );
+        return -1;
+    }
+    
+    return nRet;
+}
+
+static int ReadResponse( hid_device *dev, uint8_t uBuffer[65], int nExpectedResponse )
+{
+    DPRINTF("ReadResponse( %p %p %d )\n", dev, uBuffer, nExpectedResponse );
+    int nRet = GetFeatureReport( dev, uBuffer );
+
+    if ( nRet < 0 )
+        return nRet;
+    
+    DPRINTF("ReadResponse got %d bytes of data: ", nRet );
+    HEXDUMP( uBuffer, nRet );
+    
+    if ( uBuffer[1] != nExpectedResponse )
+        return -1;
+    
+    return nRet;
+}
+
+//---------------------------------------------------------------------------
+// Reset steam controller (unmap buttons and pads) and re-fetch capability bits
+//---------------------------------------------------------------------------
+static bool ResetSteamController( hid_device *dev, bool bSuppressErrorSpew )
+{
+    DPRINTF( "ResetSteamController hid=%p\n", dev );
+    // Firmware quirk: Set Feature and Get Feature requests always require a 65-byte buffer.
+    unsigned char buf[65];
+    int res = -1;
+    
+    buf[0] = 0;
+    buf[1] = ID_GET_ATTRIBUTES_VALUES;
+    res = SetFeatureReport( dev, buf, 2 );
+    if ( res < 0 )
+    {
+        if ( !bSuppressErrorSpew )
+            printf( "GET_ATTRIBUTES_VALUES failed for controller %p\n", dev );
+        return false;
+    }
+    
+    // Retrieve GET_ATTRIBUTES_VALUES result
+    // Wireless controller endpoints without a connected controller will return nAttrs == 0
+    res = ReadResponse( dev, buf, ID_GET_ATTRIBUTES_VALUES );
+    if ( res < 0 || buf[1] != ID_GET_ATTRIBUTES_VALUES )
+    {
+        HEXDUMP(buf, res);
+        if ( !bSuppressErrorSpew )
+            printf( "Bad GET_ATTRIBUTES_VALUES response for controller %p\n", dev );
+        return false;
+    }
+    
+    int nAttributesLength = buf[ 2 ];
+    if ( nAttributesLength > res )
+    {
+        if ( !bSuppressErrorSpew )
+            printf( "Bad GET_ATTRIBUTES_VALUES response for controller %p\n", dev );
+        return false;
+    }
+    
+    // Clear digital button mappings
+    buf[0] = 0;
+    buf[1] = ID_CLEAR_DIGITAL_MAPPINGS;
+    res = SetFeatureReport( dev, buf, 2 );
+    if ( res < 0 )
+    {
+        if ( !bSuppressErrorSpew )
+            printf( "CLEAR_DIGITAL_MAPPINGS failed for controller %p\n", dev );
+        return false;
+    }
+    
+    // Reset the default settings
+    memset( buf, 0, 65 );
+    buf[1] = ID_LOAD_DEFAULT_SETTINGS;
+    buf[2] = 0;
+    res = SetFeatureReport( dev, buf, 3 );
+    if ( res < 0 )
+    {
+        if ( !bSuppressErrorSpew )
+            printf( "LOAD_DEFAULT_SETTINGS failed for controller %p\n", dev );
+        return false;
+    }
+    
+    // Apply custom settings - clear trackpad modes (cancel mouse emulation), etc
+    int nSettings = 0;
+#define ADD_SETTING(SETTING, VALUE)    \
+buf[3+nSettings*3] = SETTING;    \
+buf[3+nSettings*3+1] = ((uint16_t)VALUE)&0xFF; \
+buf[3+nSettings*3+2] = ((uint16_t)VALUE)>>8; \
+++nSettings;
+    
+    memset( buf, 0, 65 );
+    buf[1] = ID_SET_SETTINGS_VALUES;
+    ADD_SETTING( SETTING_WIRELESS_PACKET_VERSION, 2 );
+    ADD_SETTING( SETTING_LEFT_TRACKPAD_MODE, TRACKPAD_NONE );
+#ifdef ENABLE_MOUSE_MODE
+    ADD_SETTING( SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_ABSOLUTE_MOUSE );
+    ADD_SETTING( SETTING_SMOOTH_ABSOLUTE_MOUSE, 1 );
+    ADD_SETTING( SETTING_MOMENTUM_MAXIMUM_VELOCITY, 20000 );    // [0-20000] default 8000
+    ADD_SETTING( SETTING_MOMENTUM_DECAY_AMMOUNT, 50 );        // [0-50] default 5
+#else
+    ADD_SETTING( SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_NONE );
+    ADD_SETTING( SETTING_SMOOTH_ABSOLUTE_MOUSE, 0 );
+#endif
+    buf[2] = nSettings*3;
+    
+    res = SetFeatureReport( dev, buf, 3+nSettings*3 );
+    if ( res < 0 )
+    {
+        if ( !bSuppressErrorSpew )
+            printf( "SET_SETTINGS failed for controller %p\n", dev );
+        return false;
+    }
+    
+#ifdef ENABLE_MOUSE_MODE
+    // Wait for ID_CLEAR_DIGITAL_MAPPINGS to be processed on the controller
+    bool bMappingsCleared = false;
+    int iRetry;
+    for ( iRetry = 0; iRetry < 2; ++iRetry )
+    {
+        memset( buf, 0, 65 );
+        buf[1] = ID_GET_DIGITAL_MAPPINGS;
+        buf[2] = 1; // one byte - requesting from index 0
+        buf[3] = 0;
+        res = SetFeatureReport( dev, buf, 4 );
+        if ( res < 0 )
+        {
+            printf( "GET_DIGITAL_MAPPINGS failed for controller %p\n", dev );
+            return false;
+        }
+        
+        res = ReadResponse( dev, buf, ID_GET_DIGITAL_MAPPINGS );
+        if ( res < 0 || buf[1] != ID_GET_DIGITAL_MAPPINGS )
+        {
+            printf( "Bad GET_DIGITAL_MAPPINGS response for controller %p\n", dev );
+            return false;
+        }
+        
+        // If the length of the digital mappings result is not 1 (index byte, no mappings) then clearing hasn't executed
+        if ( buf[2] == 1 && buf[3] == 0xFF )
+        {
+            bMappingsCleared = true;
+            break;
+        }
+        usleep( CONTROLLER_CONFIGURATION_DELAY_US );
+    }
+    
+    if ( !bMappingsCleared && !bSuppressErrorSpew )
+    {
+        printf( "Warning: CLEAR_DIGITAL_MAPPINGS never completed for controller %p\n", dev );
+    }
+    
+    // Set our new mappings
+    memset( buf, 0, 65 );
+    buf[1] = ID_SET_DIGITAL_MAPPINGS;
+    buf[2] = 6; // 2 settings x 3 bytes
+    buf[3] = IO_DIGITAL_BUTTON_RIGHT_TRIGGER;
+    buf[4] = DEVICE_MOUSE;
+    buf[5] = MOUSE_BTN_LEFT;
+    buf[6] = IO_DIGITAL_BUTTON_LEFT_TRIGGER;
+    buf[7] = DEVICE_MOUSE;
+    buf[8] = MOUSE_BTN_RIGHT;
+    
+    res = SetFeatureReport( dev, buf, 9 );
+    if ( res < 0 )
+    {
+        if ( !bSuppressErrorSpew )
+            printf( "SET_DIGITAL_MAPPINGS failed for controller %p\n", dev );
+        return false;
+    }
+#endif // ENABLE_MOUSE_MODE
+    
+    return true;
+}
+
+
+//---------------------------------------------------------------------------
+// Read from a Steam Controller
+//---------------------------------------------------------------------------
+static int ReadSteamController( hid_device *dev, uint8_t *pData, int nDataSize )
+{
+    memset( pData, 0, nDataSize );
+    pData[ 0 ] = BLE_REPORT_NUMBER; // hid_read will also overwrite this with the same value, 0x03
+    return hid_read( dev, pData, nDataSize );
+}
+
+
+//---------------------------------------------------------------------------
+// Close a Steam Controller
+//---------------------------------------------------------------------------
+static void CloseSteamController( hid_device *dev )
+{
+    // Switch the Steam Controller back to lizard mode so it works with the OS
+    unsigned char buf[65];
+    int nSettings = 0;
+    
+    // Reset digital button mappings
+    memset( buf, 0, 65 );
+    buf[1] = ID_SET_DEFAULT_DIGITAL_MAPPINGS;
+    SetFeatureReport( dev, buf, 2 );
+
+    // Reset the default settings
+    memset( buf, 0, 65 );
+    buf[1] = ID_LOAD_DEFAULT_SETTINGS;
+    buf[2] = 0;
+    SetFeatureReport( dev, buf, 3 );
+
+    // Reset mouse mode for lizard mode
+    memset( buf, 0, 65 );
+    buf[1] = ID_SET_SETTINGS_VALUES;
+    ADD_SETTING( SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_ABSOLUTE_MOUSE );
+    buf[2] = nSettings*3;
+    SetFeatureReport( dev, buf, 3+nSettings*3 );
+}
+
+
+//---------------------------------------------------------------------------
+// Scale and clamp values to a range
+//---------------------------------------------------------------------------
+static float RemapValClamped( float val, float A, float B, float C, float D)
+{
+    if ( A == B )
+    {
+        return ( val - B ) >= 0.0f ? D : C;
+    }
+    else
+    {
+        float cVal = (val - A) / (B - A);
+        cVal = clamp( cVal, 0.0f, 1.0f );
+
+        return C + (D - C) * cVal;
+    }
+}
+
+
+//---------------------------------------------------------------------------
+// Rotate the pad coordinates
+//---------------------------------------------------------------------------
+static void RotatePad( int *pX, int *pY, float flAngleInRad )
+{
+    short int origX = *pX, origY = *pY;
+
+    *pX = (int)( cosf( flAngleInRad ) * origX - sinf( flAngleInRad ) * origY );
+    *pY = (int)( sinf( flAngleInRad ) * origX + cosf( flAngleInRad ) * origY );
+}
+static void RotatePadShort( short *pX, short *pY, float flAngleInRad )
+{
+    short int origX = *pX, origY = *pY;
+
+    *pX = (short)( cosf( flAngleInRad ) * origX - sinf( flAngleInRad ) * origY );
+    *pY = (short)( sinf( flAngleInRad ) * origX + cosf( flAngleInRad ) * origY );
+}
+
+
+//---------------------------------------------------------------------------
+// Format the first part of the state packet
+//---------------------------------------------------------------------------
+static void FormatStatePacketUntilGyro( SteamControllerStateInternal_t *pState, ValveControllerStatePacket_t *pStatePacket )
+{
+    memset(pState, 0, offsetof(SteamControllerStateInternal_t, sBatteryLevel));
+
+    //pState->eControllerType = m_eControllerType;
+    pState->eControllerType = 2; // k_eControllerType_SteamController;
+    pState->unPacketNum = pStatePacket->unPacketNum;
+
+    // We have a chunk of trigger data in the packet format here, so zero it out afterwards
+    memcpy(&pState->ulButtons, &pStatePacket->ButtonTriggerData.ulButtons, 8);
+    pState->ulButtons &= ~0xFFFF000000LL;
+
+    // The firmware uses this bit to tell us what kind of data is packed into the left two axises
+    if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK)
+    {
+        // Finger-down bit not set; "left pad" is actually trackpad
+        pState->sLeftPadX = pState->sPrevLeftPad[0] = pStatePacket->sLeftPadX;
+        pState->sLeftPadY = pState->sPrevLeftPad[1] = pStatePacket->sLeftPadY;
+
+        if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_AND_JOYSTICK_MASK)
+        {
+            // The controller is interleaving both stick and pad data, both are active
+            pState->sLeftStickX = pState->sPrevLeftStick[0];
+            pState->sLeftStickY = pState->sPrevLeftStick[1];
+        }
+        else
+        {
+            // The stick is not active
+            pState->sPrevLeftStick[0] = 0;
+            pState->sPrevLeftStick[1] = 0;
+        }
+    }
+    else
+    {
+        // Finger-down bit not set; "left pad" is actually joystick
+
+        // XXX there's a firmware bug where sometimes padX is 0 and padY is a large number (acutally the battery voltage)
+        // If that happens skip this packet and report last frames stick
+/*
+        if ( m_eControllerType == k_eControllerType_SteamControllerV2 && pStatePacket->sLeftPadY > 900 )
+        {
+            pState->sLeftStickX = pState->sPrevLeftStick[0];
+            pState->sLeftStickY = pState->sPrevLeftStick[1];
+        }
+        else
+*/
+        {
+            pState->sPrevLeftStick[0] = pState->sLeftStickX = pStatePacket->sLeftPadX;
+            pState->sPrevLeftStick[1] = pState->sLeftStickY = pStatePacket->sLeftPadY;
+        }
+/*
+        if (m_eControllerType == k_eControllerType_SteamControllerV2)
+        {
+            UpdateV2JoystickCap(&state);
+        }
+*/
+
+        if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_AND_JOYSTICK_MASK)
+        {
+            // The controller is interleaving both stick and pad data, both are active
+            pState->sLeftPadX = pState->sPrevLeftPad[0];
+            pState->sLeftPadY = pState->sPrevLeftPad[1];
+        }
+        else
+        {
+            // The trackpad is not active
+            pState->sPrevLeftPad[0] = 0;
+            pState->sPrevLeftPad[1] = 0;
+
+            // Old controllers send trackpad click for joystick button when trackpad is not active
+            if (pState->ulButtons & STEAM_BUTTON_LEFTPAD_CLICKED_MASK)
+            {
+                pState->ulButtons &= ~STEAM_BUTTON_LEFTPAD_CLICKED_MASK;
+                pState->ulButtons |= STEAM_JOYSTICK_BUTTON_MASK;
+            }
+        }
+    }
+
+    // Fingerdown bit indicates if the packed left axis data was joystick or pad,
+    // but if we are interleaving both, the left finger is definitely on the pad.
+    if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_AND_JOYSTICK_MASK)
+        pState->ulButtons |= STEAM_LEFTPAD_FINGERDOWN_MASK;
+
+    pState->sRightPadX = pStatePacket->sRightPadX;
+    pState->sRightPadY = pStatePacket->sRightPadY;
+
+    int nLeftPadX = pState->sLeftPadX;
+    int nLeftPadY = pState->sLeftPadY;
+    int nRightPadX = pState->sRightPadX;
+    int nRightPadY = pState->sRightPadY;
+
+    // 15 degrees in rad
+    const float flRotationAngle = 0.261799f;
+
+    RotatePad(&nLeftPadX, &nLeftPadY, -flRotationAngle);
+    RotatePad(&nRightPadX, &nRightPadY, flRotationAngle);
+
+    int nPadOffset;
+    if (pState->ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK)
+        nPadOffset = 1000;
+    else
+        nPadOffset = 0;
+
+    pState->sLeftPadX = clamp(nLeftPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
+    pState->sLeftPadY = clamp(nLeftPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
+
+    nPadOffset = 0;
+    if (pState->ulButtons & STEAM_RIGHTPAD_FINGERDOWN_MASK)
+        nPadOffset = 1000;
+    else
+        nPadOffset = 0;
+
+    pState->sRightPadX = clamp(nRightPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
+    pState->sRightPadY = clamp(nRightPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16);
+
+    pState->sTriggerL = (unsigned short)RemapValClamped( (pStatePacket->ButtonTriggerData.Triggers.nLeft << 7) | pStatePacket->ButtonTriggerData.Triggers.nLeft, 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16 );
+    pState->sTriggerR = (unsigned short)RemapValClamped( (pStatePacket->ButtonTriggerData.Triggers.nRight << 7) | pStatePacket->ButtonTriggerData.Triggers.nRight, 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16 );
+}
+
+
+//---------------------------------------------------------------------------
+// Update Steam Controller state from a BLE data packet, returns true if it parsed data
+//---------------------------------------------------------------------------
+static bool UpdateBLESteamControllerState( const uint8_t *pData, int nDataSize, SteamControllerStateInternal_t *pState )
+{
+    const float flRotationAngle = 0.261799f;
+    uint32_t ucOptionDataMask;
+
+    pState->unPacketNum++;
+    ucOptionDataMask = ( *pData++ & 0xF0 );
+    ucOptionDataMask |= (uint32_t)(*pData++) << 8;
+    if ( ucOptionDataMask & k_EBLEButtonChunk1 )
+    {
+        memcpy( &pState->ulButtons, pData, 3 );
+        pData += 3;
+    }
+    if ( ucOptionDataMask & k_EBLEButtonChunk2 )
+    {
+        // The middle 2 bytes of the button bits over the wire are triggers when over the wire and non-SC buttons in the internal controller state packet
+        pState->sTriggerL = (unsigned short)RemapValClamped( ( pData[ 0 ] << 7 ) | pData[ 0 ], 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16 );
+        pState->sTriggerR = (unsigned short)RemapValClamped( ( pData[ 1 ] << 7 ) | pData[ 1 ], 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16 );
+        pData += 2;
+    }
+    if ( ucOptionDataMask & k_EBLEButtonChunk3 )
+    {
+        uint8_t *pButtonByte = (uint8_t *)&pState->ulButtons;
+        pButtonByte[ 5 ] = *pData++;
+        pButtonByte[ 6 ] = *pData++;
+        pButtonByte[ 7 ] = *pData++;
+    }
+    if ( ucOptionDataMask & k_EBLELeftJoystickChunk )
+    {
+        // This doesn't handle any of the special headcrab stuff for raw joystick which is OK for now since that FW doesn't support
+        // this protocol yet either
+        int nLength = sizeof( pState->sLeftStickX ) + sizeof( pState->sLeftStickY );
+        memcpy( &pState->sLeftStickX, pData, nLength );
+        pData += nLength;
+    }
+    if ( ucOptionDataMask & k_EBLELeftTrackpadChunk )
+    {
+        int nLength = sizeof( pState->sLeftPadX ) + sizeof( pState->sLeftPadY );
+        int nPadOffset;
+        memcpy( &pState->sLeftPadX, pData, nLength );
+        if ( pState->ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK )
+            nPadOffset = 1000;
+        else
+            nPadOffset = 0;
+
+        RotatePadShort( &pState->sLeftPadX, &pState->sLeftPadY, -flRotationAngle );
+        pState->sLeftPadX = clamp( pState->sLeftPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16 );
+        pState->sLeftPadY = clamp( pState->sLeftPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16 );
+        pData += nLength;
+    }
+    if ( ucOptionDataMask & k_EBLERightTrackpadChunk )
+    {
+        int nLength = sizeof( pState->sRightPadX ) + sizeof( pState->sRightPadY );
+        int nPadOffset = 0;
+
+        memcpy( &pState->sRightPadX, pData, nLength );
+
+        if ( pState->ulButtons & STEAM_RIGHTPAD_FINGERDOWN_MASK )
+            nPadOffset = 1000;
+        else
+            nPadOffset = 0;
+
+        RotatePadShort( &pState->sRightPadX, &pState->sRightPadY, flRotationAngle );
+        pState->sRightPadX = clamp( pState->sRightPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16 );
+        pState->sRightPadY = clamp( pState->sRightPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16 );
+        pData += nLength;
+    }
+    if ( ucOptionDataMask & k_EBLEIMUAccelChunk )
+    {
+        int nLength = sizeof( pState->sAccelX ) + sizeof( pState->sAccelY ) + sizeof( pState->sAccelZ );
+        memcpy( &pState->sAccelX, pData, nLength );
+        pData += nLength;
+    }
+    if ( ucOptionDataMask & k_EBLEIMUGyroChunk )
+    {
+        int nLength = sizeof( pState->sAccelX ) + sizeof( pState->sAccelY ) + sizeof( pState->sAccelZ );
+        memcpy( &pState->sGyroX, pData, nLength );
+        pData += nLength;
+    }
+    if ( ucOptionDataMask & k_EBLEIMUQuatChunk )
+    {
+        int nLength = sizeof( pState->sGyroQuatW ) + sizeof( pState->sGyroQuatX ) + sizeof( pState->sGyroQuatY ) + sizeof( pState->sGyroQuatZ );
+        memcpy( &pState->sGyroQuatW, pData, nLength );
+        pData += nLength;
+    }
+    return true;
+}
+
+
+//---------------------------------------------------------------------------
+// Update Steam Controller state from a data packet, returns true if it parsed data
+//---------------------------------------------------------------------------
+static bool UpdateSteamControllerState( const uint8_t *pData, int nDataSize, SteamControllerStateInternal_t *pState )
+{
+    ValveInReport_t *pInReport = (ValveInReport_t*)pData;
+
+    if ( pInReport->header.unReportVersion != k_ValveInReportMsgVersion )
+    {
+        if ( ( pData[ 0 ] & 0x0F ) == k_EBLEReportState )
+        {
+            return UpdateBLESteamControllerState( pData, nDataSize, pState );
+        }
+        return false;
+    }
+
+    if ( ( pInReport->header.ucType != ID_CONTROLLER_STATE ) &&
+         ( pInReport->header.ucType != ID_CONTROLLER_BLE_STATE ) )
+    {
+        return false;
+    }
+
+    if ( pInReport->header.ucType == ID_CONTROLLER_STATE )
+    {
+        ValveControllerStatePacket_t *pStatePacket = &pInReport->payload.controllerState;
+
+        // No new data to process; indicate that we received a state packet, but otherwise do nothing.
+        if ( pState->unPacketNum == pStatePacket->unPacketNum )
+            return true;
+
+        FormatStatePacketUntilGyro( pState, pStatePacket );
+
+        pState->sAccelX = pStatePacket->sAccelX;
+        pState->sAccelY = pStatePacket->sAccelY;
+        pState->sAccelZ = pStatePacket->sAccelZ;
+
+        pState->sGyroQuatW = pStatePacket->sGyroQuatW;
+        pState->sGyroQuatX = pStatePacket->sGyroQuatX;
+        pState->sGyroQuatY = pStatePacket->sGyroQuatY;
+        pState->sGyroQuatZ = pStatePacket->sGyroQuatZ;
+
+        pState->sGyroX = pStatePacket->sGyroX;
+        pState->sGyroY = pStatePacket->sGyroY;
+        pState->sGyroZ = pStatePacket->sGyroZ;
+
+    }
+    else if ( pInReport->header.ucType == ID_CONTROLLER_BLE_STATE )
+    {
+        ValveControllerBLEStatePacket_t *pBLEStatePacket = &pInReport->payload.controllerBLEState;
+        ValveControllerStatePacket_t *pStatePacket = &pInReport->payload.controllerState;
+
+        // No new data to process; indicate that we received a state packet, but otherwise do nothing.
+        if ( pState->unPacketNum == pStatePacket->unPacketNum )
+            return true;
+
+        FormatStatePacketUntilGyro( pState, pStatePacket );
+
+        switch ( pBLEStatePacket->ucGyroDataType )
+        {
+        case 1:
+            pState->sGyroQuatW = (( float ) pBLEStatePacket->sGyro[0]);
+            pState->sGyroQuatX = (( float ) pBLEStatePacket->sGyro[1]);
+            pState->sGyroQuatY = (( float ) pBLEStatePacket->sGyro[2]);
+            pState->sGyroQuatZ = (( float ) pBLEStatePacket->sGyro[3]);
+            break;
+
+        case 2:
+            pState->sAccelX = pBLEStatePacket->sGyro[0];
+            pState->sAccelY = pBLEStatePacket->sGyro[1];
+            pState->sAccelZ = pBLEStatePacket->sGyro[2];
+            break;
+
+        case 3:
+            pState->sGyroX = pBLEStatePacket->sGyro[0];
+            pState->sGyroY = pBLEStatePacket->sGyro[1];
+            pState->sGyroZ = pBLEStatePacket->sGyro[2];
+            break;
+
+        default:
+            break;
+        }
+    }
+
+    return true;
+}
+
+/*****************************************************************************************************/
+
+typedef struct {
+    SteamControllerPacketAssembler m_assembler;
+    SteamControllerStateInternal_t m_state;
+    SteamControllerStateInternal_t m_last_state;
+} SDL_DriverSteam_Context;
+
+
+static SDL_bool
+HIDAPI_DriverSteam_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
+{
+    return SDL_IsJoystickSteamController(vendor_id, product_id);
+}
+
+static const char *
+HIDAPI_DriverSteam_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
+{
+    return "Steam Controller";
+}
+
+static SDL_bool
+HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device)
+{
+    return HIDAPI_JoystickConnected(device, NULL);
+}
+
+static int
+HIDAPI_DriverSteam_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
+{
+    return -1;
+}
+
+static void
+HIDAPI_DriverSteam_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
+{
+}
+
+static SDL_bool
+HIDAPI_DriverSteam_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
+{
+    SDL_DriverSteam_Context *ctx;
+
+    ctx = (SDL_DriverSteam_Context *)SDL_calloc(1, sizeof(*ctx));
+    if (!ctx) {
+        SDL_OutOfMemory();
+        goto error;
+    }
+    device->context = ctx;
+
+    device->dev = hid_open_path(device->path, 0);
+    if (!device->dev) {
+        SDL_SetError("Couldn't open %s", device->path);
+        goto error;
+    }
+
+    if (!ResetSteamController(device->dev, false)) {
+        goto error;
+    }
+
+    InitializeSteamControllerPacketAssembler(&ctx->m_assembler);
+
+    /* Initialize the joystick capabilities */
+    joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
+    joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
+
+    return SDL_TRUE;
+
+error:
+    if (device->dev) {
+        hid_close(device->dev);
+        device->dev = NULL;
+    }
+    if (device->context) {
+        SDL_free(device->context);
+        device->context = NULL;
+    }
+    return SDL_FALSE;
+}
+
+static int
+HIDAPI_DriverSteam_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    /* You should use the full Steam Input API for rumble support */
+    return SDL_Unsupported();
+}
+
+static SDL_bool
+HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device)
+{
+    SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
+    SDL_Joystick *joystick = NULL;
+
+    if (device->num_joysticks > 0) {
+        joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
+    }
+    if (!joystick) {
+        return SDL_FALSE;
+    }
+
+    for (;;)
+    {
+        uint8_t data[128];
+        int r, nPacketLength;
+        const Uint8 *pPacket;
+
+        r = ReadSteamController(device->dev, data, sizeof(data));
+        if (r == 0)
+        {
+            break;
+        }
+
+        nPacketLength = 0;
+        if (r > 0) {
+            nPacketLength = WriteSegmentToSteamControllerPacketAssembler(&ctx->m_assembler, data, r);
+        }
+
+        pPacket = ctx->m_assembler.uBuffer;
+
+        if (nPacketLength > 0 && UpdateSteamControllerState(pPacket, nPacketLength, &ctx->m_state)) {
+            if (ctx->m_state.ulButtons != ctx->m_last_state.ulButtons) {
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A,
+                    (ctx->m_state.ulButtons & STEAM_BUTTON_3_MASK) ? SDL_PRESSED : SDL_RELEASED);
+
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B,
+                    (ctx->m_state.ulButtons & STEAM_BUTTON_1_MASK) ? SDL_PRESSED : SDL_RELEASED);
+
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X,
+                    (ctx->m_state.ulButtons & STEAM_BUTTON_2_MASK) ? SDL_PRESSED : SDL_RELEASED);
+
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y,
+                    (ctx->m_state.ulButtons & STEAM_BUTTON_0_MASK) ? SDL_PRESSED : SDL_RELEASED);
+
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
+                    (ctx->m_state.ulButtons & STEAM_LEFT_BUMPER_MASK) ? SDL_PRESSED : SDL_RELEASED);
+
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
+                    (ctx->m_state.ulButtons & STEAM_RIGHT_BUMPER_MASK) ? SDL_PRESSED : SDL_RELEASED);
+
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK,
+                    (ctx->m_state.ulButtons & STEAM_BUTTON_MENU_MASK) ? SDL_PRESSED : SDL_RELEASED);
+
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START,
+                    (ctx->m_state.ulButtons & STEAM_BUTTON_ESCAPE_MASK) ? SDL_PRESSED : SDL_RELEASED);
+
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE,
+                    (ctx->m_state.ulButtons & STEAM_BUTTON_STEAM_MASK) ? SDL_PRESSED : SDL_RELEASED);
+
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK,
+                    (ctx->m_state.ulButtons & STEAM_JOYSTICK_BUTTON_MASK) ? SDL_PRESSED : SDL_RELEASED);
+            }
+            {
+                /* Minimum distance from center of pad to register a direction */
+                const int kPadDeadZone = 10000;
+
+                /* Pad coordinates are like math grid coordinates: negative is bottom left */
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP,
+                    (ctx->m_state.sLeftPadY > kPadDeadZone) ? SDL_PRESSED : SDL_RELEASED);
+
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN,
+                    (ctx->m_state.sLeftPadY < -kPadDeadZone) ? SDL_PRESSED : SDL_RELEASED);
+
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT,
+                    (ctx->m_state.sLeftPadX < -kPadDeadZone) ? SDL_PRESSED : SDL_RELEASED);
+
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
+                    (ctx->m_state.sLeftPadX > kPadDeadZone) ? SDL_PRESSED : SDL_RELEASED);
+            }
+
+            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ctx->m_state.sTriggerL * 2 - 32768);
+            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ctx->m_state.sTriggerR * 2 - 32768);
+
+            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, ctx->m_state.sLeftStickX);
+            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~ctx->m_state.sLeftStickY);
+
+            ctx->m_last_state = ctx->m_state;
+        }
+
+        if (r <= 0) {
+            /* Failed to read from controller */
+            HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
+            return SDL_FALSE;
+        }
+    }
+    return SDL_TRUE;
+}
+
+static void
+HIDAPI_DriverSteam_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
+{
+    CloseSteamController(device->dev);
+    hid_close(device->dev);
+    device->dev = NULL;
+
+    SDL_free(device->context);
+    device->context = NULL;
+}
+
+static void
+HIDAPI_DriverSteam_FreeDevice(SDL_HIDAPI_Device *device)
+{
+}
+
+SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam =
+{
+    SDL_HINT_JOYSTICK_HIDAPI_STEAM,
+    SDL_TRUE,
+    HIDAPI_DriverSteam_IsSupportedDevice,
+    HIDAPI_DriverSteam_GetDeviceName,
+    HIDAPI_DriverSteam_InitDevice,
+    HIDAPI_DriverSteam_GetDevicePlayerIndex,
+    HIDAPI_DriverSteam_SetDevicePlayerIndex,
+    HIDAPI_DriverSteam_UpdateDevice,
+    HIDAPI_DriverSteam_OpenJoystick,
+    HIDAPI_DriverSteam_RumbleJoystick,
+    HIDAPI_DriverSteam_CloseJoystick,
+    HIDAPI_DriverSteam_FreeDevice
+};
+
+#endif /* SDL_JOYSTICK_HIDAPI_STEAM */
+
+#endif /* SDL_JOYSTICK_HIDAPI */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 484 - 0
src/joystick/hidapi/steam/controller_constants.h

@@ -0,0 +1,484 @@
+//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
+//
+// Purpose: Defines constants used to communicate with Valve controllers.
+//
+//==================================================================================================
+
+#ifndef _CONTROLLER_CONSTANTS_
+#define _CONTROLLER_CONSTANTS_
+
+#include "controller_structs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FEATURE_REPORT_SIZE	64
+
+#define VALVE_USB_VID		0x28DE
+
+// Frame update rate (in ms).
+#define FAST_SCAN_INTERVAL  6
+#define SLOW_SCAN_INTERVAL  9
+
+// Contains each of the USB PIDs for Valve controllers (only add to this enum and never change the order)
+enum ValveControllerPID
+{
+	BASTILLE_PID              = 0x2202,
+	CHELL_PID                 = 0x1101,
+	D0G_PID                   = 0x1102,
+	ELI_PID                   = 0x1103,
+	FREEMAN_PID               = 0x1104,
+	D0G_BLE_PID				  = 0x1105,
+	D0G_BLE2_PID			  = 0x1106,
+	D0GGLE_PID                = 0x1142,
+};
+
+// This enum contains all of the messages exchanged between the host and the target (only add to this enum and never change the order)
+enum FeatureReportMessageIDs
+{
+	ID_SET_DIGITAL_MAPPINGS              = 0x80,
+	ID_CLEAR_DIGITAL_MAPPINGS            = 0x81,
+	ID_GET_DIGITAL_MAPPINGS              = 0x82,
+	ID_GET_ATTRIBUTES_VALUES             = 0x83,
+	ID_GET_ATTRIBUTE_LABEL               = 0x84,
+	ID_SET_DEFAULT_DIGITAL_MAPPINGS      = 0x85,
+	ID_FACTORY_RESET                     = 0x86,
+	ID_SET_SETTINGS_VALUES               = 0x87,
+	ID_CLEAR_SETTINGS_VALUES             = 0x88,
+	ID_GET_SETTINGS_VALUES               = 0x89,
+	ID_GET_SETTING_LABEL                 = 0x8A,
+	ID_GET_SETTINGS_MAXS                 = 0x8B,
+	ID_GET_SETTINGS_DEFAULTS             = 0x8C,
+	ID_SET_CONTROLLER_MODE               = 0x8D,
+	ID_LOAD_DEFAULT_SETTINGS             = 0x8E,
+	ID_TRIGGER_HAPTIC_PULSE              = 0x8F,
+	ID_TURN_OFF_CONTROLLER               = 0x9F,
+
+	ID_GET_DEVICE_INFO                   = 0xA1,
+	
+	ID_CALIBRATE_TRACKPADS               = 0xA7,
+	ID_RESERVED_0                        = 0xA8,
+	ID_SET_SERIAL_NUMBER                 = 0xA9,
+	ID_GET_TRACKPAD_CALIBRATION          = 0xAA,
+	ID_GET_TRACKPAD_FACTORY_CALIBRATION  = 0xAB,
+	ID_GET_TRACKPAD_RAW_DATA             = 0xAC,
+	ID_ENABLE_PAIRING                    = 0xAD,
+	ID_GET_STRING_ATTRIBUTE              = 0xAE,
+	ID_RADIO_ERASE_RECORDS               = 0xAF,
+	ID_RADIO_WRITE_RECORD                = 0xB0,
+	ID_SET_DONGLE_SETTING                = 0xB1,
+	ID_DONGLE_DISCONNECT_DEVICE          = 0xB2,
+	ID_DONGLE_COMMIT_DEVICE              = 0xB3,
+	ID_DONGLE_GET_WIRELESS_STATE         = 0xB4,
+	ID_CALIBRATE_GYRO                    = 0xB5,
+	ID_PLAY_AUDIO                        = 0xB6,
+	ID_AUDIO_UPDATE_START                = 0xB7,
+	ID_AUDIO_UPDATE_DATA                 = 0xB8,
+	ID_AUDIO_UPDATE_COMPLETE             = 0xB9,
+	ID_GET_CHIPID                        = 0xBA,
+
+	ID_CALIBRATE_JOYSTICK                = 0xBF,
+	ID_CALIBRATE_ANALOG_TRIGGERS         = 0xC0,
+	ID_SET_AUDIO_MAPPING                 = 0xC1,
+	ID_CHECK_GYRO_FW_LOAD                = 0xC2,
+	ID_CALIBRATE_ANALOG                  = 0xC3,
+	ID_DONGLE_GET_CONNECTED_SLOTS        = 0xC4,
+};
+
+
+// Enumeration of all wireless dongle events
+typedef enum WirelessEventTypes
+{
+	WIRELESS_EVENT_DISCONNECT	= 1,
+	WIRELESS_EVENT_CONNECT		= 2,
+	WIRELESS_EVENT_PAIR			= 3,
+} EWirelessEventType;
+
+
+// Enumeration of generic digital inputs - not all of these will be supported on all controllers (only add to this enum and never change the order)
+typedef enum
+{
+	IO_DIGITAL_BUTTON_NONE = -1,
+	IO_DIGITAL_BUTTON_RIGHT_TRIGGER,
+	IO_DIGITAL_BUTTON_LEFT_TRIGGER,
+	IO_DIGITAL_BUTTON_1,
+	IO_DIGITAL_BUTTON_Y=IO_DIGITAL_BUTTON_1,
+	IO_DIGITAL_BUTTON_2,
+	IO_DIGITAL_BUTTON_B=IO_DIGITAL_BUTTON_2,
+	IO_DIGITAL_BUTTON_3,
+	IO_DIGITAL_BUTTON_X=IO_DIGITAL_BUTTON_3,
+	IO_DIGITAL_BUTTON_4,
+	IO_DIGITAL_BUTTON_A=IO_DIGITAL_BUTTON_4,
+	IO_DIGITAL_BUTTON_RIGHT_BUMPER,
+	IO_DIGITAL_BUTTON_LEFT_BUMPER,
+	IO_DIGITAL_BUTTON_LEFT_JOYSTICK_CLICK,
+	IO_DIGITAL_BUTTON_ESCAPE,
+	IO_DIGITAL_BUTTON_STEAM,
+	IO_DIGITAL_BUTTON_MENU,
+	IO_DIGITAL_STICK_UP,
+	IO_DIGITAL_STICK_DOWN,
+	IO_DIGITAL_STICK_LEFT,
+	IO_DIGITAL_STICK_RIGHT,
+	IO_DIGITAL_TOUCH_1,
+	IO_DIGITAL_BUTTON_UP=IO_DIGITAL_TOUCH_1,
+	IO_DIGITAL_TOUCH_2,
+	IO_DIGITAL_BUTTON_RIGHT=IO_DIGITAL_TOUCH_2,
+	IO_DIGITAL_TOUCH_3,
+	IO_DIGITAL_BUTTON_LEFT=IO_DIGITAL_TOUCH_3,
+	IO_DIGITAL_TOUCH_4,
+	IO_DIGITAL_BUTTON_DOWN=IO_DIGITAL_TOUCH_4,
+	IO_DIGITAL_BUTTON_BACK_LEFT,
+	IO_DIGITAL_BUTTON_BACK_RIGHT,
+	IO_DIGITAL_LEFT_TRACKPAD_N,
+	IO_DIGITAL_LEFT_TRACKPAD_NE,
+	IO_DIGITAL_LEFT_TRACKPAD_E,
+	IO_DIGITAL_LEFT_TRACKPAD_SE,
+	IO_DIGITAL_LEFT_TRACKPAD_S,
+	IO_DIGITAL_LEFT_TRACKPAD_SW,
+	IO_DIGITAL_LEFT_TRACKPAD_W,
+	IO_DIGITAL_LEFT_TRACKPAD_NW,
+	IO_DIGITAL_RIGHT_TRACKPAD_N,
+	IO_DIGITAL_RIGHT_TRACKPAD_NE,
+	IO_DIGITAL_RIGHT_TRACKPAD_E,
+	IO_DIGITAL_RIGHT_TRACKPAD_SE,
+	IO_DIGITAL_RIGHT_TRACKPAD_S,
+	IO_DIGITAL_RIGHT_TRACKPAD_SW,
+	IO_DIGITAL_RIGHT_TRACKPAD_W,
+	IO_DIGITAL_RIGHT_TRACKPAD_NW,
+	IO_DIGITAL_LEFT_TRACKPAD_DOUBLE_TAP,
+	IO_DIGITAL_RIGHT_TRACKPAD_DOUBLE_TAP,
+	IO_DIGITAL_LEFT_TRACKPAD_OUTER_RADIUS,
+	IO_DIGITAL_RIGHT_TRACKPAD_OUTER_RADIUS,
+	IO_DIGITAL_LEFT_TRACKPAD_CLICK,
+	IO_DIGITAL_RIGHT_TRACKPAD_CLICK,
+	IO_DIGITAL_BATTERY_LOW,
+	IO_DIGITAL_LEFT_TRIGGER_THRESHOLD,
+	IO_DIGITAL_RIGHT_TRIGGER_THRESHOLD,
+	IO_DIGITAL_BUTTON_BACK_LEFT2,
+	IO_DIGITAL_BUTTON_BACK_RIGHT2,
+	IO_DIGITAL_BUTTON_ALWAYS_ON,
+	IO_DIGITAL_BUTTON_ANCILLARY_1,
+	IO_DIGITAL_BUTTON_MACRO_0,
+	IO_DIGITAL_BUTTON_MACRO_1,
+	IO_DIGITAL_BUTTON_MACRO_2,
+	IO_DIGITAL_BUTTON_MACRO_3,
+	IO_DIGITAL_BUTTON_MACRO_4,
+	IO_DIGITAL_BUTTON_MACRO_5,
+	IO_DIGITAL_BUTTON_MACRO_6,
+	IO_DIGITAL_BUTTON_MACRO_7,
+	IO_DIGITAL_BUTTON_MACRO_1FINGER,
+	IO_DIGITAL_BUTTON_MACRO_2FINGER,
+	IO_DIGITAL_COUNT
+} DigitalIO ;
+
+// Enumeration of generic analog inputs - not all of these will be supported on all controllers (only add to this enum and never change the order)
+typedef enum 
+{
+	IO_ANALOG_LEFT_STICK_X,
+	IO_ANALOG_LEFT_STICK_Y,
+	IO_ANALOG_RIGHT_STICK_X,
+	IO_ANALOG_RIGHT_STICK_Y,
+	IO_ANALOG_LEFT_TRIGGER,
+	IO_ANALOG_RIGHT_TRIGGER,
+	IO_MOUSE1_X,
+	IO_MOUSE1_Y,
+	IO_MOUSE1_Z,
+	IO_ACCEL_X,
+	IO_ACCEL_Y,
+	IO_ACCEL_Z,
+	IO_GYRO_X,
+	IO_GYRO_Y,
+	IO_GYRO_Z,
+	IO_GYRO_QUAT_W,
+	IO_GYRO_QUAT_X,
+	IO_GYRO_QUAT_Y,
+	IO_GYRO_QUAT_Z,
+	IO_GYRO_STEERING_VEC,
+	IO_RAW_TRIGGER_LEFT,
+	IO_RAW_TRIGGER_RIGHT,
+	IO_RAW_JOYSTICK_X,
+	IO_RAW_JOYSTICK_Y,
+	IO_GYRO_TILT_VEC,
+	IO_ANALOG_COUNT
+} AnalogIO;
+
+
+// Contains list of all types of devices that the controller emulates (only add to this enum and never change the order)
+enum DeviceTypes
+{
+	DEVICE_KEYBOARD,
+	DEVICE_MOUSE,
+	DEVICE_GAMEPAD,
+	DEVICE_MODE_ADJUST,
+	DEVICE_COUNT
+};
+
+// Scan codes for HID keyboards 
+enum HIDKeyboardKeys
+{
+	KEY_INVALID,
+	KEY_FIRST = 0x04,
+	KEY_A = KEY_FIRST, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, 
+	KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2, 
+	KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_RETURN, KEY_ESCAPE, KEY_BACKSPACE, KEY_TAB, KEY_SPACE, KEY_DASH, KEY_EQUALS, KEY_LEFT_BRACKET,
+	KEY_RIGHT_BRACKET, KEY_BACKSLASH, KEY_UNUSED1, KEY_SEMICOLON, KEY_SINGLE_QUOTE, KEY_BACK_TICK, KEY_COMMA, KEY_PERIOD, KEY_FORWARD_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6,
+	KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_PRINT_SCREEN, KEY_SCROLL_LOCK, KEY_BREAK, KEY_INSERT, KEY_HOME, KEY_PAGE_UP, KEY_DELETE, KEY_END, KEY_PAGE_DOWN, KEY_RIGHT_ARROW,
+	KEY_LEFT_ARROW, KEY_DOWN_ARROW, KEY_UP_ARROW, KEY_NUM_LOCK, KEY_KEYPAD_FORWARD_SLASH, KEY_KEYPAD_ASTERISK, KEY_KEYPAD_DASH, KEY_KEYPAD_PLUS, KEY_KEYPAD_ENTER, KEY_KEYPAD_1, KEY_KEYPAD_2, KEY_KEYPAD_3, KEY_KEYPAD_4, KEY_KEYPAD_5, KEY_KEYPAD_6, KEY_KEYPAD_7,
+	KEY_KEYPAD_8, KEY_KEYPAD_9, KEY_KEYPAD_0, KEY_KEYPAD_PERIOD,
+	KEY_LALT,
+    KEY_LSHIFT,
+    KEY_LWIN,
+    KEY_LCONTROL,
+    KEY_RALT,
+    KEY_RSHIFT,
+    KEY_RWIN,
+    KEY_RCONTROL,
+	KEY_VOLUP,
+	KEY_VOLDOWN,
+	KEY_MUTE,
+	KEY_PLAY,
+	KEY_STOP,
+	KEY_NEXT,
+	KEY_PREV,
+    KEY_LAST = KEY_PREV
+};
+
+enum ModifierMasks
+{
+  KEY_LCONTROL_MASK = (1<<0),
+  KEY_LSHIFT_MASK = (1<<1),
+  KEY_LALT_MASK = (1<<2),
+  KEY_LWIN_MASK = (1<<3),
+  KEY_RCONTROL_MASK = (1<<4),
+  KEY_RSHIFT_MASK = (1<<5),
+  KEY_RALT_MASK = (1<<6),
+  KEY_RWIN_MASK = (1<<7)
+};
+
+// Standard mouse buttons as specified in the HID mouse spec
+enum MouseButtons
+{
+	MOUSE_BTN_LEFT,
+	MOUSE_BTN_RIGHT,
+	MOUSE_BTN_MIDDLE,
+	MOUSE_BTN_BACK,
+	MOUSE_BTN_FORWARD,
+	MOUSE_SCROLL_UP,
+	MOUSE_SCROLL_DOWN,
+	MOUSE_BTN_COUNT
+};
+
+// Gamepad buttons
+enum GamepadButtons
+{
+	GAMEPAD_BTN_TRIGGER_LEFT=1, 
+	GAMEPAD_BTN_TRIGGER_RIGHT,
+	GAMEPAD_BTN_A,
+	GAMEPAD_BTN_B,
+	GAMEPAD_BTN_Y,
+	GAMEPAD_BTN_X,
+	GAMEPAD_BTN_SHOULDER_LEFT,
+	GAMEPAD_BTN_SHOULDER_RIGHT,
+	GAMEPAD_BTN_LEFT_JOYSTICK,
+	GAMEPAD_BTN_RIGHT_JOYSTICK,
+	GAMEPAD_BTN_START,
+	GAMEPAD_BTN_SELECT,
+	GAMEPAD_BTN_STEAM,
+	GAMEPAD_BTN_DPAD_UP,
+	GAMEPAD_BTN_DPAD_DOWN,
+	GAMEPAD_BTN_DPAD_LEFT,
+	GAMEPAD_BTN_DPAD_RIGHT,
+	GAMEPAD_BTN_LSTICK_UP,
+	GAMEPAD_BTN_LSTICK_DOWN,
+	GAMEPAD_BTN_LSTICK_LEFT,
+	GAMEPAD_BTN_LSTICK_RIGHT,
+	GAMEPAD_BTN_RSTICK_UP,
+	GAMEPAD_BTN_RSTICK_DOWN,
+	GAMEPAD_BTN_RSTICK_LEFT,
+	GAMEPAD_BTN_RSTICK_RIGHT,
+	GAMEPAD_BTN_COUNT
+};
+
+// Mode adjust
+enum ModeAdjustModes
+{
+	MODE_ADJUST_SENSITITY=1,
+	MODE_ADJUST_LEFT_PAD_SECONDARY_MODE,
+	MODE_ADJUST_RIGHT_PAD_SECONDARY_MODE,
+	MODE_ADJUST_COUNT
+};
+
+// Read-only attributes of controllers (only add to this enum and never change the order)
+typedef enum
+{
+	ATTRIB_UNIQUE_ID,
+	ATTRIB_PRODUCT_ID,
+	ATTRIB_PRODUCT_REVISON,											// deprecated
+	ATTRIB_CAPABILITIES = ATTRIB_PRODUCT_REVISON,	// intentional aliasing
+	ATTRIB_FIRMWARE_VERSION,										// deprecated
+	ATTRIB_FIRMWARE_BUILD_TIME,
+	ATTRIB_RADIO_FIRMWARE_BUILD_TIME,
+	ATTRIB_RADIO_DEVICE_ID0,
+	ATTRIB_RADIO_DEVICE_ID1,
+	ATTRIB_DONGLE_FIRMWARE_BUILD_TIME,
+	ATTRIB_BOARD_REVISION,
+	ATTRIB_BOOTLOADER_BUILD_TIME,
+	ATTRIB_CONNECTION_INTERVAL_IN_US,
+	ATTRIB_COUNT
+} ControllerAttributes;
+
+// Read-only string attributes of controllers (only add to this enum and never change the order)
+typedef enum
+{
+	ATTRIB_STR_BOARD_SERIAL,
+	ATTRIB_STR_UNIT_SERIAL,
+	ATTRIB_STR_COUNT
+} ControllerStringAttributes;
+
+typedef enum
+{
+	STATUS_CODE_NORMAL,
+	STATUS_CODE_CRITICAL_BATTERY,
+	STATUS_CODE_GYRO_INIT_ERROR,
+} ControllerStatusEventCodes;
+
+typedef enum
+{
+	STATUS_STATE_LOW_BATTERY=0,
+} ControllerStatusStateFlags;
+
+typedef enum {
+	TRACKPAD_ABSOLUTE_MOUSE,
+	TRACKPAD_RELATIVE_MOUSE,
+	TRACKPAD_DPAD_FOUR_WAY_DISCRETE,
+	TRACKPAD_DPAD_FOUR_WAY_OVERLAP,
+	TRACKPAD_DPAD_EIGHT_WAY,
+	TRACKPAD_RADIAL_MODE,
+	TRACKPAD_ABSOLUTE_DPAD,
+	TRACKPAD_NONE,
+	TRACKPAD_GESTURE_KEYBOARD,
+	TRACKPAD_NUM_MODES
+} TrackpadDPadMode;
+
+// Read-write controller settings (only add to this enum and never change the order)
+typedef enum 
+{
+	SETTING_MOUSE_SENSITIVITY,
+	SETTING_MOUSE_ACCELERATION,
+	SETTING_TRACKBALL_ROTATION_ANGLE,
+	SETTING_HAPTIC_INTENSITY,
+	SETTING_LEFT_GAMEPAD_STICK_ENABLED,
+	SETTING_RIGHT_GAMEPAD_STICK_ENABLED,
+	SETTING_USB_DEBUG_MODE,
+	SETTING_LEFT_TRACKPAD_MODE,
+	SETTING_RIGHT_TRACKPAD_MODE,
+	SETTING_MOUSE_POINTER_ENABLED,
+	SETTING_DPAD_DEADZONE,
+	SETTING_MINIMUM_MOMENTUM_VEL,
+	SETTING_MOMENTUM_DECAY_AMMOUNT,
+	SETTING_TRACKPAD_RELATIVE_MODE_TICKS_PER_PIXEL,
+	SETTING_HAPTIC_INCREMENT,
+	SETTING_DPAD_ANGLE_SIN,
+	SETTING_DPAD_ANGLE_COS,
+	SETTING_MOMENTUM_VERTICAL_DIVISOR,
+	SETTING_MOMENTUM_MAXIMUM_VELOCITY,
+	SETTING_TRACKPAD_Z_ON,
+	SETTING_TRACKPAD_Z_OFF,
+	SETTING_SENSITIVY_SCALE_AMMOUNT,
+	SETTING_LEFT_TRACKPAD_SECONDARY_MODE,
+	SETTING_RIGHT_TRACKPAD_SECONDARY_MODE,
+	SETTING_SMOOTH_ABSOLUTE_MOUSE,
+	SETTING_STEAMBUTTON_POWEROFF_TIME,
+	SETTING_UNUSED_1,
+	SETTING_TRACKPAD_OUTER_RADIUS,
+	SETTING_TRACKPAD_Z_ON_LEFT,
+	SETTING_TRACKPAD_Z_OFF_LEFT,
+	SETTING_TRACKPAD_OUTER_SPIN_VEL,
+	SETTING_TRACKPAD_OUTER_SPIN_RADIUS,
+	SETTING_TRACKPAD_OUTER_SPIN_HORIZONTAL_ONLY,
+	SETTING_TRACKPAD_RELATIVE_MODE_DEADZONE,
+	SETTING_TRACKPAD_RELATIVE_MODE_MAX_VEL,
+	SETTING_TRACKPAD_RELATIVE_MODE_INVERT_Y,
+	SETTING_TRACKPAD_DOUBLE_TAP_BEEP_ENABLED,
+	SETTING_TRACKPAD_DOUBLE_TAP_BEEP_PERIOD,
+	SETTING_TRACKPAD_DOUBLE_TAP_BEEP_COUNT,
+	SETTING_TRACKPAD_OUTER_RADIUS_RELEASE_ON_TRANSITION,
+	SETTING_RADIAL_MODE_ANGLE,
+	SETTING_HAPTIC_INTENSITY_MOUSE_MODE,
+	SETTING_LEFT_DPAD_REQUIRES_CLICK,
+	SETTING_RIGHT_DPAD_REQUIRES_CLICK,
+	SETTING_LED_BASELINE_BRIGHTNESS,
+	SETTING_LED_USER_BRIGHTNESS,
+	SETTING_ENABLE_RAW_JOYSTICK,
+	SETTING_ENABLE_FAST_SCAN,
+	SETTING_GYRO_MODE,
+	SETTING_WIRELESS_PACKET_VERSION,
+	SETTING_SLEEP_INACTIVITY_TIMEOUT,
+	SETTING_COUNT,
+	
+	// This is a special setting value use for callbacks and should not be set/get explicitly.
+	SETTING_ALL=0xFF
+} ControllerSettings;
+
+typedef enum
+{
+	SETTING_DEFAULT,
+	SETTING_MIN,
+	SETTING_MAX,
+	SETTING_DEFAULTMINMAXCOUNT
+} SettingDefaultMinMax;
+
+// Bitmask that define which IMU features to enable.
+typedef enum
+{
+	SETTING_GYRO_MODE_OFF				= 0x0000,
+	SETTING_GYRO_MODE_STEERING			= 0x0001,
+	SETTING_GYRO_MODE_TILT				= 0x0002,
+	SETTING_GYRO_MODE_SEND_ORIENTATION	= 0x0004,
+	SETTING_GYRO_MODE_SEND_RAW_ACCEL	= 0x0008,
+	SETTING_GYRO_MODE_SEND_RAW_GYRO		= 0x0010,
+} SettingGyroMode;
+
+// Bitmask for haptic pulse flags
+typedef enum
+{
+	HAPTIC_PULSE_NORMAL					= 0x0000,
+	HAPTIC_PULSE_HIGH_PRIORITY			= 0x0001,
+	HAPTIC_PULSE_VERY_HIGH_PRIORITY		= 0x0002,
+} SettingHapticPulseFlags;
+
+typedef struct
+{
+	// default,min,max in this array in that order
+	short defaultminmax[SETTING_DEFAULTMINMAXCOUNT]; 
+} SettingValueRange_t;
+
+// below is from controller_constants.c which should be compiled into any code that uses this
+extern const SettingValueRange_t g_DefaultSettingValues[SETTING_COUNT];
+
+// Read-write settings for dongle (only add to this enum and never change the order)
+typedef enum 
+{
+	DONGLE_SETTING_MOUSE_KEYBOARD_ENABLED,
+	DONGLE_SETTING_COUNT,
+} DongleSettings;
+
+typedef enum
+{
+	AUDIO_STARTUP		= 0,
+	AUDIO_SHUTDOWN		= 1,
+	AUDIO_PAIR			= 2,
+	AUDIO_PAIR_SUCCESS	= 3,
+	AUDIO_IDENTIFY		= 4,
+	AUDIO_LIZARDMODE	= 5,
+	AUDIO_NORMALMODE	= 6,
+
+	AUDIO_MAX_SLOT      = 15
+} ControllerAudio;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _CONTROLLER_CONSTANTS_H

+ 255 - 0
src/joystick/hidapi/steam/controller_structs.h

@@ -0,0 +1,255 @@
+//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
+//
+// Purpose: Defines methods and structures used to communicate with Valve controllers.
+//
+//==================================================================================================
+#ifndef _CONTROLLER_STRUCTS_
+#define _CONTROLLER_STRUCTS_
+
+#pragma pack(1)
+
+// Roll this version forward anytime that you are breaking compatibility of existing
+// message types within ValveInReport_t or the header itself.  Hopefully this should
+// be super rare and instead you shoudl just add new message payloads to the union,
+// or just add fields to the end of existing payload structs which is expected to be 
+// safe in all code consuming these as they should just consume/copy upto the prior size 
+// they were aware of when processing.
+#define k_ValveInReportMsgVersion 0x01
+
+typedef enum
+{
+	ID_CONTROLLER_STATE = 1,
+	ID_CONTROLLER_DEBUG = 2,
+	ID_CONTROLLER_WIRELESS = 3,
+	ID_CONTROLLER_STATUS = 4,
+	ID_CONTROLLER_DEBUG2 = 5,
+	ID_CONTROLLER_SECONDARY_STATE = 6,
+	ID_CONTROLLER_BLE_STATE = 7,
+	ID_CONTROLLER_MSG_COUNT
+} ValveInReportMessageIDs; 
+
+typedef struct 
+{
+	unsigned short unReportVersion;
+	
+	unsigned char ucType;
+	unsigned char ucLength;
+	
+} ValveInReportHeader_t;
+
+// State payload
+typedef struct 
+{
+	// If packet num matches that on your prior call, then the controller state hasn't been changed since 
+	// your last call and there is no need to process it
+	uint32 unPacketNum;
+	
+	// Button bitmask and trigger data.
+	union
+	{
+		uint64 ulButtons;
+		struct
+		{
+			unsigned char _pad0[3];
+			unsigned char nLeft;
+			unsigned char nRight;
+			unsigned char _pad1[3];
+		} Triggers;
+	} ButtonTriggerData;
+	
+	// Left pad coordinates
+	short sLeftPadX;
+	short sLeftPadY;
+	
+	// Right pad coordinates
+	short sRightPadX;
+	short sRightPadY;
+	
+	// This is redundant, packed above, but still sent over wired
+	unsigned short sTriggerL;
+	unsigned short sTriggerR;
+
+	// FIXME figure out a way to grab this stuff over wireless
+	short sAccelX;
+	short sAccelY;
+	short sAccelZ;
+	
+	short sGyroX;
+	short sGyroY;
+	short sGyroZ;
+	
+	short sGyroQuatW;
+	short sGyroQuatX;
+	short sGyroQuatY;
+	short sGyroQuatZ;
+
+} ValveControllerStatePacket_t;
+
+// BLE State payload this has to be re-formatted from the normal state because BLE controller shows up as 
+//a HID device and we don't want to send all the optional parts of the message. Keep in sync with struct above.
+typedef struct
+{
+	// If packet num matches that on your prior call, then the controller state hasn't been changed since 
+	// your last call and there is no need to process it
+	uint32 unPacketNum;
+
+	// Button bitmask and trigger data.
+	union
+	{
+		uint64 ulButtons;
+		struct
+		{
+			unsigned char _pad0[3];
+			unsigned char nLeft;
+			unsigned char nRight;
+			unsigned char _pad1[3];
+		} Triggers;
+	} ButtonTriggerData;
+
+	// Left pad coordinates
+	short sLeftPadX;
+	short sLeftPadY;
+
+	// Right pad coordinates
+	short sRightPadX;
+	short sRightPadY;
+
+	//This mimcs how the dongle reconstitutes HID packets, there will be 0-4 shorts depending on gyro mode
+	unsigned char ucGyroDataType; //TODO could maybe find some unused bits in the button field for this info (is only 2bits)
+	short sGyro[4];
+
+} ValveControllerBLEStatePacket_t;
+
+// Define a payload for reporting debug information
+typedef struct
+{
+	// Left pad coordinates
+	short sLeftPadX;
+	short sLeftPadY;
+
+	// Right pad coordinates
+	short sRightPadX;
+	short sRightPadY;
+
+	// Left mouse deltas
+	short sLeftPadMouseDX;
+	short sLeftPadMouseDY;
+
+	// Right mouse deltas
+	short sRightPadMouseDX;
+	short sRightPadMouseDY;
+	
+	// Left mouse filtered deltas
+	short sLeftPadMouseFilteredDX;
+	short sLeftPadMouseFilteredDY;
+
+	// Right mouse filtered deltas
+	short sRightPadMouseFilteredDX;
+	short sRightPadMouseFilteredDY;
+	
+	// Pad Z values
+	unsigned char ucLeftZ;
+	unsigned char ucRightZ;
+	
+	// FingerPresent
+	unsigned char ucLeftFingerPresent;
+	unsigned char ucRightFingerPresent;
+	
+	// Timestamps
+	unsigned char ucLeftTimestamp;
+	unsigned char ucRightTimestamp;
+	
+	// Double tap state
+	unsigned char ucLeftTapState;
+	unsigned char ucRightTapState;
+	
+	unsigned int unDigitalIOStates0;
+	unsigned int unDigitalIOStates1;
+	
+} ValveControllerDebugPacket_t;
+
+typedef struct
+{
+	unsigned char ucPadNum;
+	unsigned char ucPad[3]; // need Data to be word aligned
+	short Data[20];
+	unsigned short unNoise;
+} ValveControllerTrackpadImage_t;
+
+typedef struct
+{
+	unsigned char ucPadNum;
+	unsigned char ucOffset;
+	unsigned char ucPad[2]; // need Data to be word aligned
+	short rgData[28];
+} ValveControllerRawTrackpadImage_t;
+
+// Payload for wireless metadata
+typedef struct 
+{
+	unsigned char ucEventType;
+} SteamControllerWirelessEvent_t;
+
+typedef struct 
+{
+	// Current packet number.
+    unsigned int unPacketNum;
+	
+	// Event codes and state information.
+    unsigned short sEventCode;
+    unsigned short unStateFlags;
+
+    // Current battery voltage (mV).
+    unsigned short sBatteryVoltage;
+	
+	// Current battery level (0-100).
+	unsigned char ucBatteryLevel;
+} SteamControllerStatusEvent_t;
+
+typedef struct
+{
+	ValveInReportHeader_t header;
+	
+	union
+	{
+		ValveControllerStatePacket_t controllerState;
+		ValveControllerBLEStatePacket_t controllerBLEState;
+		ValveControllerDebugPacket_t debugState;
+		ValveControllerTrackpadImage_t padImage;
+		ValveControllerRawTrackpadImage_t rawPadImage;
+		SteamControllerWirelessEvent_t wirelessEvent;
+		SteamControllerStatusEvent_t statusEvent;
+	} payload;
+	
+} ValveInReport_t;
+
+
+// Enumeration for BLE packet protocol
+enum EBLEPacketReportNums
+{
+	// Skipping past 2-3 because they are escape characters in Uart protocol
+	k_EBLEReportState = 4,
+	k_EBLEReportStatus = 5,
+};
+
+
+// Enumeration of data chunks in BLE state packets
+enum EBLEOptionDataChunksBitmask
+{
+	// First byte uppper nibble
+	k_EBLEButtonChunk1 = 0x10,
+	k_EBLEButtonChunk2 = 0x20,
+	k_EBLEButtonChunk3 = 0x40,
+	k_EBLELeftJoystickChunk = 0x80,
+
+	// Second full byte
+	k_EBLELeftTrackpadChunk = 0x100,
+	k_EBLERightTrackpadChunk = 0x200,
+	k_EBLEIMUAccelChunk = 0x400,
+	k_EBLEIMUGyroChunk = 0x800,
+	k_EBLEIMUQuatChunk = 0x1000,
+};
+
+#pragma pack()
+
+#endif // _CONTROLLER_STRUCTS