switch-pico/switch_pro_descriptors.h

513 lines
16 KiB
C

/*
* Switch Pro controller descriptor and data definitions.
* Copied from the GP2040-CE project to mirror the behaviour of its
* SwitchProDriver without pulling in the rest of the codebase.
*/
#pragma once
#include <stdint.h>
#define SWITCH_PRO_ENDPOINT_SIZE 64
// HAT report (4 bits)
#define SWITCH_PRO_HAT_UP 0x00
#define SWITCH_PRO_HAT_UPRIGHT 0x01
#define SWITCH_PRO_HAT_RIGHT 0x02
#define SWITCH_PRO_HAT_DOWNRIGHT 0x03
#define SWITCH_PRO_HAT_DOWN 0x04
#define SWITCH_PRO_HAT_DOWNLEFT 0x05
#define SWITCH_PRO_HAT_LEFT 0x06
#define SWITCH_PRO_HAT_UPLEFT 0x07
#define SWITCH_PRO_HAT_NOTHING 0x08
#define SWITCH_PRO_MASK_ZR (1U << 7)
#define SWITCH_PRO_MASK_R (1U << 6)
#define SWITCH_PRO_MASK_A (1U << 3)
#define SWITCH_PRO_MASK_B (1U << 2)
#define SWITCH_PRO_MASK_X (1U << 1)
#define SWITCH_PRO_MASK_Y 1U
#define SWITCH_PRO_MASK_CAPTURE (1U << 5)
#define SWITCH_PRO_MASK_HOME (1U << 4)
#define SWITCH_PRO_MASK_L3 (1U << 3)
#define SWITCH_PRO_MASK_R3 (1U << 2)
#define SWITCH_PRO_MASK_PLUS (1U << 1)
#define SWITCH_PRO_MASK_MINUS 1U
#define SWITCH_PRO_MASK_ZL (1U << 7)
#define SWITCH_PRO_MASK_L (1U << 6)
#define SWITCH_PRO_JOYSTICK_MIN 0x0000
#define SWITCH_PRO_JOYSTICK_MID 0x7FFF
#define SWITCH_PRO_JOYSTICK_MAX 0xFFFF
typedef enum {
REPORT_OUTPUT_00 = 0x00,
REPORT_FEATURE = 0x01,
REPORT_OUTPUT_10 = 0x10,
REPORT_OUTPUT_21 = 0x21,
REPORT_OUTPUT_30 = 0x30,
REPORT_CONFIGURATION = 0x80,
REPORT_USB_INPUT_81 = 0x81,
} SwitchReportID;
typedef enum {
IDENTIFY = 0x01,
HANDSHAKE,
BAUD_RATE,
DISABLE_USB_TIMEOUT,
ENABLE_USB_TIMEOUT
} SwitchOutputSubtypes;
typedef enum {
GET_CONTROLLER_STATE = 0x00,
BLUETOOTH_PAIR_REQUEST = 0x01,
REQUEST_DEVICE_INFO = 0x02,
SET_MODE = 0x03,
TRIGGER_BUTTONS = 0x04,
SET_SHIPMENT = 0x08,
SPI_READ = 0x10,
SET_NFC_IR_CONFIG = 0x21,
SET_NFC_IR_STATE = 0x22,
SET_PLAYER_LIGHTS = 0x30,
GET_PLAYER_LIGHTS = 0x31,
COMMAND_UNKNOWN_33 = 0x33,
SET_HOME_LIGHT = 0x38,
TOGGLE_IMU = 0x40,
IMU_SENSITIVITY = 0x41,
READ_IMU = 0x43,
ENABLE_VIBRATION = 0x48,
GET_VOLTAGE = 0x50,
} SwitchCommands;
typedef struct {
uint8_t data[3];
void setX(uint16_t x) {
data[0] = x & 0xFF;
data[1] = (data[1] & 0xF0) | ((x >> 8) & 0x0F);
}
void setY(uint16_t y) {
data[1] = (data[1] & 0x0F) | ((y & 0x0F) << 4);
data[2] = (y >> 4) & 0xFF;
}
uint16_t getX() {
return static_cast<uint16_t>(data[0]) | ((data[1] & 0x0F) << 8);
}
uint16_t getY() {
return static_cast<uint16_t>((data[1] >> 4)) | (data[2] << 4);
}
} SwitchAnalog;
// left and right calibration are stored differently for some reason, so two structs
typedef struct {
uint8_t data[9];
void getMin(uint16_t& x, uint16_t& y) const { packCalib(6, x, y); }
void getCenter(uint16_t& x, uint16_t& y) const { packCalib(3, x, y); }
void getMax(uint16_t& x, uint16_t& y) const { packCalib(0, x, y); }
void getRealMin(uint16_t& x, uint16_t& y) const {
uint16_t minX, minY;
uint16_t cenX, cenY;
getMin(minX, minY);
getCenter(cenX, cenY);
x = cenX - minX;
y = cenY - minY;
}
void getRealMax(uint16_t& x, uint16_t& y) const {
uint16_t maxX, maxY;
uint16_t cenX, cenY;
getMax(maxX, maxY);
getCenter(cenX, cenY);
x = cenX + maxX;
y = cenY + maxY;
}
void packCalib(uint8_t offset, uint16_t& x, uint16_t& y) const {
x = static_cast<uint16_t>(data[offset]) | ((data[offset + 1] & 0x0F) << 8);
y = static_cast<uint16_t>(data[offset + 2] << 4) | (data[offset + 1] >> 4);
}
} SwitchLeftCalibration;
typedef struct {
uint8_t data[9];
void getMin(uint16_t& x, uint16_t& y) const { packCalib(3, x, y); }
void getCenter(uint16_t& x, uint16_t& y) const { packCalib(0, x, y); }
void getMax(uint16_t& x, uint16_t& y) const { packCalib(6, x, y); }
void getRealMin(uint16_t& x, uint16_t& y) const {
uint16_t minX, minY;
uint16_t cenX, cenY;
getMin(minX, minY);
getCenter(cenX, cenY);
x = cenX - minX;
y = cenY - minY;
}
void getRealMax(uint16_t& x, uint16_t& y) const {
uint16_t maxX, maxY;
uint16_t cenX, cenY;
getMax(maxX, maxY);
getCenter(cenX, cenY);
x = cenX + maxX;
y = cenY + maxY;
}
void packCalib(uint8_t offset, uint16_t& x, uint16_t& y) const {
x = static_cast<uint16_t>(data[offset]) | ((data[offset + 1] & 0x0F) << 8);
y = static_cast<uint16_t>(data[offset + 2] << 4) | (data[offset + 1] >> 4);
}
} SwitchRightCalibration;
typedef struct
{
uint8_t red;
uint8_t green;
uint8_t blue;
} SwitchColorDefinition;
typedef struct __attribute((packed, aligned(1)))
{
uint8_t serialNumber[16];
uint8_t unknown00[2];
uint8_t deviceType;
uint8_t unknown01; // usually 0xA0
uint8_t unknown02[7];
uint8_t colorInfo; // 0 = default colors
uint8_t unknown03[4];
uint8_t motionCalibration[24];
uint8_t unknown04[5];
SwitchLeftCalibration leftStickCalibration;
SwitchRightCalibration rightStickCalibration;
uint8_t unknown08;
SwitchColorDefinition bodyColor;
SwitchColorDefinition buttonColor;
SwitchColorDefinition leftGripColor;
SwitchColorDefinition rightGripColor;
uint8_t unknown06[37];
uint8_t motionHorizontalOffsets[6];
uint8_t stickParams1[17];
uint8_t stickParams2[17];
uint8_t unknown07[0xE57];
} SwitchFactoryConfig;
typedef struct __attribute((packed, aligned(1)))
{
uint8_t unknown00[16];
uint8_t leftCalibrationMagic[2];
SwitchLeftCalibration leftCalibration;
uint8_t rightCalibrationMagic[2];
SwitchRightCalibration rightCalibration;
uint8_t motionCalibrationMagic[2];
uint8_t motionCalibration[24];
} SwitchUserCalibration;
typedef struct __attribute((packed, aligned(1)))
{
uint8_t connectionInfo : 4;
uint8_t batteryLevel : 4;
// byte 00
uint8_t buttonY : 1;
uint8_t buttonX : 1;
uint8_t buttonB : 1;
uint8_t buttonA : 1;
uint8_t buttonRightSR : 1;
uint8_t buttonRightSL : 1;
uint8_t buttonR : 1;
uint8_t buttonZR : 1;
// byte 01
uint8_t buttonMinus : 1;
uint8_t buttonPlus : 1;
uint8_t buttonThumbR : 1;
uint8_t buttonThumbL : 1;
uint8_t buttonHome : 1;
uint8_t buttonCapture : 1;
uint8_t dummy : 1;
uint8_t chargingGrip : 1;
// byte 02
uint8_t dpadDown : 1;
uint8_t dpadUp : 1;
uint8_t dpadRight : 1;
uint8_t dpadLeft : 1;
uint8_t buttonLeftSL : 1;
uint8_t buttonLeftSR : 1;
uint8_t buttonL : 1;
uint8_t buttonZL : 1;
SwitchAnalog leftStick;
SwitchAnalog rightStick;
} SwitchInputReport;
typedef struct __attribute((packed, aligned(1)))
{
uint8_t reportID;
uint8_t timestamp;
SwitchInputReport inputs;
uint8_t rumbleReport;
uint8_t imuData[36];
uint8_t padding[15];
} SwitchProReport;
typedef enum {
SWITCH_TYPE_LEFT_JOYCON = 0x01,
SWITCH_TYPE_RIGHT_JOYCON,
SWITCH_TYPE_PRO_CONTROLLER,
SWITCH_TYPE_FAMICOM_LEFT_JOYCON = 0x07,
SWITCH_TYPE_FAMICOM_RIGHT_JOYCON = 0x08,
SWITCH_TYPE_NES_LEFT_JOYCON = 0x09,
SWITCH_TYPE_NES_RIGHT_JOYCON = 0x0A,
SWITCH_TYPE_SNES = 0x0B,
SWITCH_TYPE_N64 = 0x0C,
} SwitchControllerType;
typedef struct {
uint8_t majorVersion;
uint8_t minorVersion;
uint8_t controllerType;
uint8_t unknown00;
uint8_t macAddress[6];
uint8_t unknown01;
uint8_t storedColors;
} SwitchDeviceInfo;
typedef struct
{
uint16_t buttons;
uint8_t hat;
uint8_t lx;
uint8_t ly;
uint8_t rx;
uint8_t ry;
} SwitchProOutReport;
static const uint8_t switch_pro_string_language[] = { 0x09, 0x04 };
static const uint8_t switch_pro_string_manufacturer[] = "Nintendo Co., Ltd.";
static const uint8_t switch_pro_string_product[] = "Pro Controller";
// static const uint8_t switch_pro_string_manufacturer[] = "Open Stick Community";
// static const uint8_t switch_pro_string_product[] = "GP2040-CE (Pro Controller)";
static const uint8_t switch_pro_string_version[] = "000000000001";
static const uint8_t *switch_pro_string_descriptors[] __attribute__((unused)) =
{
switch_pro_string_language,
switch_pro_string_manufacturer,
switch_pro_string_product,
switch_pro_string_version
};
static const uint8_t switch_pro_device_descriptor[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x00, 0x02, // bcdUSB 2.00
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 64
0x7E, 0x05, // idVendor 0x057E
0x09, 0x20, // idProduct 0x2009
0x10, 0x02, // bcdDevice 4.10
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x03, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t switch_pro_hid_descriptor[] =
{
0x09, // bLength
0x21, // bDescriptorType (HID)
0x11, 0x01, // bcdHID 1.11
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType[0] (HID)
0xCB, 0x00, // wDescriptorLength[0] 86
};
static const uint8_t switch_pro_configuration_descriptor[] =
{
0x09, // bLength
0x02, // bDescriptorType (Configuration)
0x29, 0x00, // wTotalLength 41
0x01, // bNumInterfaces 1
0x01, // bConfigurationValue
0x00, // iConfiguration (String Index)
0xA0, // bmAttributes Remote Wakeup
0xFA, // bMaxPower 500mA
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x00, // bInterfaceNumber 0
0x00, // bAlternateSetting
0x02, // bNumEndpoints 2
0x03, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface (String Index)
0x09, // bLength
0x21, // bDescriptorType (HID)
0x11, 0x01, // bcdHID 1.11
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType[0] (HID)
0xCB, 0x00, // wDescriptorLength[0] 203
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x81, // bEndpointAddress (IN/D2H)
0x03, // bmAttributes (Interrupt)
0x40, 0x00, // wMaxPacketSize 64
0x08, // bInterval 8 (unit depends on device speed)
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x01, // bEndpointAddress (OUT/H2D)
0x03, // bmAttributes (Interrupt)
0x40, 0x00, // wMaxPacketSize 64
0x08, // bInterval 8 (unit depends on device speed)
};
static const uint8_t switch_pro_report_descriptor[] =
{
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x15, 0x00, // Logical Minimum (0)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Application)
0x85, 0x30, // Report ID (48)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0A, // Usage Maximum (0x0A)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0A, // Report Count (10)
0x55, 0x00, // Unit Exponent (0)
0x65, 0x00, // Unit (None)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x09, // Usage Page (Button)
0x19, 0x0B, // Usage Minimum (0x0B)
0x29, 0x0E, // Usage Maximum (0x0E)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01, // Report Size (1)
0x95, 0x02, // Report Count (2)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x0B, 0x01, 0x00, 0x01, 0x00, // Usage (0x010001)
0xA1, 0x00, // Collection (Physical)
0x0B, 0x30, 0x00, 0x01, 0x00, // Usage (0x010030)
0x0B, 0x31, 0x00, 0x01, 0x00, // Usage (0x010031)
0x0B, 0x32, 0x00, 0x01, 0x00, // Usage (0x010032)
0x0B, 0x35, 0x00, 0x01, 0x00, // Usage (0x010035)
0x15, 0x00, // Logical Minimum (0)
0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534)
0x75, 0x10, // Report Size (16)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x0B, 0x39, 0x00, 0x01, 0x00, // Usage (0x010039)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x07, // Logical Maximum (7)
0x35, 0x00, // Physical Minimum (0)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x09, // Usage Page (Button)
0x19, 0x0F, // Usage Minimum (0x0F)
0x29, 0x12, // Usage Maximum (0x12)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x08, // Report Size (8)
0x95, 0x34, // Report Count (52)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x85, 0x21, // Report ID (33)
0x09, 0x01, // Usage (0x01)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x85, 0x81, // Report ID (-127)
0x09, 0x02, // Usage (0x02)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x85, 0x01, // Report ID (1)
0x09, 0x03, // Usage (0x03)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
0x85, 0x10, // Report ID (16)
0x09, 0x04, // Usage (0x04)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
0x85, 0x80, // Report ID (-128)
0x09, 0x05, // Usage (0x05)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
0x85, 0x82, // Report ID (-126)
0x09, 0x06, // Usage (0x06)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
0xC0, // End Collection
};