summaryrefslogtreecommitdiffstats
path: root/src/input_common/helpers/joycon_protocol/joycon_types.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/helpers/joycon_protocol/joycon_types.h')
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h612
1 files changed, 612 insertions, 0 deletions
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
new file mode 100644
index 000000000..e2d47349f
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -0,0 +1,612 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
+// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
+// https://github.com/CTCaer/jc_toolkit
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
+
+#pragma once
+
+#include <array>
+#include <functional>
+#include <SDL_hidapi.h>
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace InputCommon::Joycon {
+constexpr u32 MaxErrorCount = 50;
+constexpr u32 MaxBufferSize = 368;
+constexpr u32 MaxResponseSize = 49;
+constexpr u32 MaxSubCommandResponseSize = 64;
+constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40};
+
+using MacAddress = std::array<u8, 6>;
+using SerialNumber = std::array<u8, 15>;
+
+enum class ControllerType {
+ None,
+ Left,
+ Right,
+ Pro,
+ Grip,
+ Dual,
+};
+
+enum class PadAxes {
+ LeftStickX,
+ LeftStickY,
+ RightStickX,
+ RightStickY,
+ Undefined,
+};
+
+enum class PadMotion {
+ LeftMotion,
+ RightMotion,
+ Undefined,
+};
+
+enum class PadButton : u32 {
+ Down = 0x000001,
+ Up = 0x000002,
+ Right = 0x000004,
+ Left = 0x000008,
+ LeftSR = 0x000010,
+ LeftSL = 0x000020,
+ L = 0x000040,
+ ZL = 0x000080,
+ Y = 0x000100,
+ X = 0x000200,
+ B = 0x000400,
+ A = 0x000800,
+ RightSR = 0x001000,
+ RightSL = 0x002000,
+ R = 0x004000,
+ ZR = 0x008000,
+ Minus = 0x010000,
+ Plus = 0x020000,
+ StickR = 0x040000,
+ StickL = 0x080000,
+ Home = 0x100000,
+ Capture = 0x200000,
+};
+
+enum class PasivePadButton : u32 {
+ Down_A = 0x0001,
+ Right_X = 0x0002,
+ Left_B = 0x0004,
+ Up_Y = 0x0008,
+ SL = 0x0010,
+ SR = 0x0020,
+ Minus = 0x0100,
+ Plus = 0x0200,
+ StickL = 0x0400,
+ StickR = 0x0800,
+ Home = 0x1000,
+ Capture = 0x2000,
+ L_R = 0x4000,
+ ZL_ZR = 0x8000,
+};
+
+enum class OutputReport : u8 {
+ RUMBLE_AND_SUBCMD = 0x01,
+ FW_UPDATE_PKT = 0x03,
+ RUMBLE_ONLY = 0x10,
+ MCU_DATA = 0x11,
+ USB_CMD = 0x80,
+};
+
+enum class InputReport : u8 {
+ SUBCMD_REPLY = 0x21,
+ STANDARD_FULL_60HZ = 0x30,
+ NFC_IR_MODE_60HZ = 0x31,
+ SIMPLE_HID_MODE = 0x3F,
+ INPUT_USB_RESPONSE = 0x81,
+};
+
+enum class FeatureReport : u8 {
+ Last_SUBCMD = 0x02,
+ OTA_GW_UPGRADE = 0x70,
+ SETUP_MEM_READ = 0x71,
+ MEM_READ = 0x72,
+ ERASE_MEM_SECTOR = 0x73,
+ MEM_WRITE = 0x74,
+ LAUNCH = 0x75,
+};
+
+enum class SubCommand : u8 {
+ STATE = 0x00,
+ MANUAL_BT_PAIRING = 0x01,
+ REQ_DEV_INFO = 0x02,
+ SET_REPORT_MODE = 0x03,
+ TRIGGERS_ELAPSED = 0x04,
+ GET_PAGE_LIST_STATE = 0x05,
+ SET_HCI_STATE = 0x06,
+ RESET_PAIRING_INFO = 0x07,
+ LOW_POWER_MODE = 0x08,
+ SPI_FLASH_READ = 0x10,
+ SPI_FLASH_WRITE = 0x11,
+ RESET_MCU = 0x20,
+ SET_MCU_CONFIG = 0x21,
+ SET_MCU_STATE = 0x22,
+ SET_PLAYER_LIGHTS = 0x30,
+ GET_PLAYER_LIGHTS = 0x31,
+ SET_HOME_LIGHT = 0x38,
+ ENABLE_IMU = 0x40,
+ SET_IMU_SENSITIVITY = 0x41,
+ WRITE_IMU_REG = 0x42,
+ READ_IMU_REG = 0x43,
+ ENABLE_VIBRATION = 0x48,
+ GET_REGULATED_VOLTAGE = 0x50,
+ SET_EXTERNAL_CONFIG = 0x58,
+ UNKNOWN_RINGCON = 0x59,
+ UNKNOWN_RINGCON2 = 0x5A,
+ UNKNOWN_RINGCON3 = 0x5C,
+};
+
+enum class UsbSubCommand : u8 {
+ CONN_STATUS = 0x01,
+ HADSHAKE = 0x02,
+ BAUDRATE_3M = 0x03,
+ NO_TIMEOUT = 0x04,
+ EN_TIMEOUT = 0x05,
+ RESET = 0x06,
+ PRE_HANDSHAKE = 0x91,
+ SEND_UART = 0x92,
+};
+
+enum class CalMagic : u8 {
+ USR_MAGIC_0 = 0xB2,
+ USR_MAGIC_1 = 0xA1,
+ USRR_MAGI_SIZE = 2,
+};
+
+enum class CalAddr {
+ SERIAL_NUMBER = 0X6000,
+ DEVICE_TYPE = 0X6012,
+ COLOR_EXIST = 0X601B,
+ FACT_LEFT_DATA = 0X603d,
+ FACT_RIGHT_DATA = 0X6046,
+ COLOR_DATA = 0X6050,
+ FACT_IMU_DATA = 0X6020,
+ USER_LEFT_MAGIC = 0X8010,
+ USER_LEFT_DATA = 0X8012,
+ USER_RIGHT_MAGIC = 0X801B,
+ USER_RIGHT_DATA = 0X801D,
+ USER_IMU_MAGIC = 0X8026,
+ USER_IMU_DATA = 0X8028,
+};
+
+enum class ReportMode : u8 {
+ ACTIVE_POLLING_NFC_IR_CAMERA_DATA = 0x00,
+ ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01,
+ ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02,
+ ACTIVE_POLLING_IR_CAMERA_DATA = 0x03,
+ MCU_UPDATE_STATE = 0x23,
+ STANDARD_FULL_60HZ = 0x30,
+ NFC_IR_MODE_60HZ = 0x31,
+ SIMPLE_HID_MODE = 0x3F,
+};
+
+enum class GyroSensitivity : u8 {
+ DPS250,
+ DPS500,
+ DPS1000,
+ DPS2000, // Default
+};
+
+enum class AccelerometerSensitivity : u8 {
+ G8, // Default
+ G4,
+ G2,
+ G16,
+};
+
+enum class GyroPerformance : u8 {
+ HZ833,
+ HZ208, // Default
+};
+
+enum class AccelerometerPerformance : u8 {
+ HZ200,
+ HZ100, // Default
+};
+
+enum class MCUCommand : u8 {
+ ConfigureMCU = 0x21,
+ ConfigureIR = 0x23,
+};
+
+enum class MCUSubCommand : u8 {
+ SetMCUMode = 0x0,
+ SetDeviceMode = 0x1,
+ ReadDeviceMode = 0x02,
+ WriteDeviceRegisters = 0x4,
+};
+
+enum class MCUMode : u8 {
+ Suspend = 0,
+ Standby = 1,
+ Ringcon = 3,
+ NFC = 4,
+ IR = 5,
+ MaybeFWUpdate = 6,
+};
+
+enum class MCURequest : u8 {
+ GetMCUStatus = 1,
+ GetNFCData = 2,
+ GetIRData = 3,
+};
+
+enum class MCUReport : u8 {
+ Empty = 0x00,
+ StateReport = 0x01,
+ IRData = 0x03,
+ BusyInitializing = 0x0b,
+ IRStatus = 0x13,
+ IRRegisters = 0x1b,
+ NFCState = 0x2a,
+ NFCReadData = 0x3a,
+ EmptyAwaitingCmd = 0xff,
+};
+
+enum class MCUPacketFlag : u8 {
+ MorePacketsRemaining = 0x00,
+ LastCommandPacket = 0x08,
+};
+
+enum class NFCReadCommand : u8 {
+ CancelAll = 0x00,
+ StartPolling = 0x01,
+ StopPolling = 0x02,
+ StartWaitingRecieve = 0x04,
+ Ntag = 0x06,
+ Mifare = 0x0F,
+};
+
+enum class NFCTagType : u8 {
+ AllTags = 0x00,
+ Ntag215 = 0x01,
+};
+
+enum class NFCPages {
+ Block0 = 0,
+ Block45 = 45,
+ Block135 = 135,
+ Block231 = 231,
+};
+
+enum class NFCStatus : u8 {
+ LastPackage = 0x04,
+ TagLost = 0x07,
+};
+
+enum class IrsMode : u8 {
+ None = 0x02,
+ Moment = 0x03,
+ Dpd = 0x04,
+ Clustering = 0x06,
+ ImageTransfer = 0x07,
+ Silhouette = 0x08,
+ TeraImage = 0x09,
+ SilhouetteTeraImage = 0x0A,
+};
+
+enum class IrsResolution {
+ Size320x240,
+ Size160x120,
+ Size80x60,
+ Size40x30,
+ Size20x15,
+ None,
+};
+
+enum class IrsResolutionCode : u8 {
+ Size320x240 = 0x00, // Full pixel array
+ Size160x120 = 0x50, // Sensor Binning [2 X 2]
+ Size80x60 = 0x64, // Sensor Binning [4 x 2] and Skipping [1 x 2]
+ Size40x30 = 0x69, // Sensor Binning [4 x 2] and Skipping [2 x 4]
+ Size20x15 = 0x6A, // Sensor Binning [4 x 2] and Skipping [4 x 4]
+};
+
+// Size of image divided by 300
+enum class IrsFragments : u8 {
+ Size20x15 = 0x00,
+ Size40x30 = 0x03,
+ Size80x60 = 0x0f,
+ Size160x120 = 0x3f,
+ Size320x240 = 0xFF,
+};
+
+enum class IrLeds : u8 {
+ BrightAndDim = 0x00,
+ Bright = 0x20,
+ Dim = 0x10,
+ None = 0x30,
+};
+
+enum class IrExLedFilter : u8 {
+ Disabled = 0x00,
+ Enabled = 0x03,
+};
+
+enum class IrImageFlip : u8 {
+ Normal = 0x00,
+ Inverted = 0x02,
+};
+
+enum class IrRegistersAddress : u16 {
+ UpdateTime = 0x0400,
+ FinalizeConfig = 0x0700,
+ LedFilter = 0x0e00,
+ Leds = 0x1000,
+ LedIntensitiyMSB = 0x1100,
+ LedIntensitiyLSB = 0x1200,
+ ImageFlip = 0x2d00,
+ Resolution = 0x2e00,
+ DigitalGainLSB = 0x2e01,
+ DigitalGainMSB = 0x2f01,
+ ExposureLSB = 0x3001,
+ ExposureMSB = 0x3101,
+ ExposureTime = 0x3201,
+ WhitePixelThreshold = 0x4301,
+ DenoiseSmoothing = 0x6701,
+ DenoiseEdge = 0x6801,
+ DenoiseColor = 0x6901,
+};
+
+enum class DriverResult {
+ Success,
+ WrongReply,
+ Timeout,
+ UnsupportedControllerType,
+ HandleInUse,
+ ErrorReadingData,
+ ErrorWritingData,
+ NoDeviceDetected,
+ InvalidHandle,
+ NotSupported,
+ Disabled,
+ Unknown,
+};
+
+struct MotionSensorCalibration {
+ s16 offset;
+ s16 scale;
+};
+
+struct MotionCalibration {
+ std::array<MotionSensorCalibration, 3> accelerometer;
+ std::array<MotionSensorCalibration, 3> gyro;
+};
+
+// Basic motion data containing data from the sensors and a timestamp in microseconds
+struct MotionData {
+ float gyro_x{};
+ float gyro_y{};
+ float gyro_z{};
+ float accel_x{};
+ float accel_y{};
+ float accel_z{};
+ u64 delta_timestamp{};
+};
+
+struct JoyStickAxisCalibration {
+ u16 max{1};
+ u16 min{1};
+ u16 center{0};
+};
+
+struct JoyStickCalibration {
+ JoyStickAxisCalibration x;
+ JoyStickAxisCalibration y;
+};
+
+struct RingCalibration {
+ s16 default_value;
+ s16 max_value;
+ s16 min_value;
+};
+
+struct Color {
+ u32 body;
+ u32 buttons;
+ u32 left_grip;
+ u32 right_grip;
+};
+
+struct Battery {
+ union {
+ u8 raw{};
+
+ BitField<0, 4, u8> unknown;
+ BitField<4, 1, u8> charging;
+ BitField<5, 3, u8> status;
+ };
+};
+
+struct VibrationValue {
+ f32 low_amplitude;
+ f32 low_frequency;
+ f32 high_amplitude;
+ f32 high_frequency;
+};
+
+struct JoyconHandle {
+ SDL_hid_device* handle = nullptr;
+ u8 packet_counter{};
+};
+
+struct MCUConfig {
+ MCUCommand command;
+ MCUSubCommand sub_command;
+ MCUMode mode;
+ INSERT_PADDING_BYTES(0x22);
+ u8 crc;
+};
+static_assert(sizeof(MCUConfig) == 0x26, "MCUConfig is an invalid size");
+
+#pragma pack(push, 1)
+struct InputReportPassive {
+ InputReport report_mode;
+ u16 button_input;
+ u8 stick_state;
+ std::array<u8, 10> unknown_data;
+};
+static_assert(sizeof(InputReportPassive) == 0xE, "InputReportPassive is an invalid size");
+
+struct InputReportActive {
+ InputReport report_mode;
+ u8 packet_id;
+ Battery battery_status;
+ std::array<u8, 3> button_input;
+ std::array<u8, 3> left_stick_state;
+ std::array<u8, 3> right_stick_state;
+ u8 vibration_code;
+ std::array<s16, 6 * 2> motion_input;
+ INSERT_PADDING_BYTES(0x2);
+ s16 ring_input;
+};
+static_assert(sizeof(InputReportActive) == 0x29, "InputReportActive is an invalid size");
+
+struct InputReportNfcIr {
+ InputReport report_mode;
+ u8 packet_id;
+ Battery battery_status;
+ std::array<u8, 3> button_input;
+ std::array<u8, 3> left_stick_state;
+ std::array<u8, 3> right_stick_state;
+ u8 vibration_code;
+ std::array<s16, 6 * 2> motion_input;
+ INSERT_PADDING_BYTES(0x4);
+};
+static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size");
+#pragma pack(pop)
+
+struct IMUCalibration {
+ std::array<s16, 3> accelerometer_offset;
+ std::array<s16, 3> accelerometer_scale;
+ std::array<s16, 3> gyroscope_offset;
+ std::array<s16, 3> gyroscope_scale;
+};
+static_assert(sizeof(IMUCalibration) == 0x18, "IMUCalibration is an invalid size");
+
+struct NFCReadBlock {
+ u8 start;
+ u8 end;
+};
+static_assert(sizeof(NFCReadBlock) == 0x2, "NFCReadBlock is an invalid size");
+
+struct NFCReadBlockCommand {
+ u8 block_count{};
+ std::array<NFCReadBlock, 4> blocks{};
+};
+static_assert(sizeof(NFCReadBlockCommand) == 0x9, "NFCReadBlockCommand is an invalid size");
+
+struct NFCReadCommandData {
+ u8 unknown;
+ u8 uuid_length;
+ u8 unknown_2;
+ std::array<u8, 6> uid;
+ NFCTagType tag_type;
+ NFCReadBlockCommand read_block;
+};
+static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size");
+
+struct NFCPollingCommandData {
+ u8 enable_mifare;
+ u8 unknown_1;
+ u8 unknown_2;
+ u8 unknown_3;
+ u8 unknown_4;
+};
+static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size");
+
+struct NFCRequestState {
+ MCUSubCommand sub_command;
+ NFCReadCommand command_argument;
+ u8 packet_id;
+ INSERT_PADDING_BYTES(0x1);
+ MCUPacketFlag packet_flag;
+ u8 data_length;
+ union {
+ std::array<u8, 0x1F> raw_data;
+ NFCReadCommandData nfc_read;
+ NFCPollingCommandData nfc_polling;
+ };
+ u8 crc;
+};
+static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size");
+
+struct IrsConfigure {
+ MCUCommand command;
+ MCUSubCommand sub_command;
+ IrsMode irs_mode;
+ IrsFragments number_of_fragments;
+ u16 mcu_major_version;
+ u16 mcu_minor_version;
+ INSERT_PADDING_BYTES(0x1D);
+ u8 crc;
+};
+static_assert(sizeof(IrsConfigure) == 0x26, "IrsConfigure is an invalid size");
+
+#pragma pack(push, 1)
+struct IrsRegister {
+ IrRegistersAddress address;
+ u8 value;
+};
+static_assert(sizeof(IrsRegister) == 0x3, "IrsRegister is an invalid size");
+
+struct IrsWriteRegisters {
+ MCUCommand command;
+ MCUSubCommand sub_command;
+ u8 number_of_registers;
+ std::array<IrsRegister, 9> registers;
+ INSERT_PADDING_BYTES(0x7);
+ u8 crc;
+};
+static_assert(sizeof(IrsWriteRegisters) == 0x26, "IrsWriteRegisters is an invalid size");
+#pragma pack(pop)
+
+struct FirmwareVersion {
+ u8 major;
+ u8 minor;
+};
+static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
+
+struct DeviceInfo {
+ FirmwareVersion firmware;
+ MacAddress mac_address;
+};
+static_assert(sizeof(DeviceInfo) == 0x8, "DeviceInfo is an invalid size");
+
+struct MotionStatus {
+ bool is_enabled;
+ u64 delta_time;
+ GyroSensitivity gyro_sensitivity;
+ AccelerometerSensitivity accelerometer_sensitivity;
+};
+
+struct RingStatus {
+ bool is_enabled;
+ s16 default_value;
+ s16 max_value;
+ s16 min_value;
+};
+
+struct JoyconCallbacks {
+ std::function<void(Battery)> on_battery_data;
+ std::function<void(Color)> on_color_data;
+ std::function<void(int, bool)> on_button_data;
+ std::function<void(int, f32)> on_stick_data;
+ std::function<void(int, const MotionData&)> on_motion_data;
+ std::function<void(f32)> on_ring_data;
+ std::function<void(const std::vector<u8>&)> on_amiibo_data;
+ std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data;
+};
+
+} // namespace InputCommon::Joycon