input_common: Initial skeleton for custom joycon driver
This commit is contained in:
parent
475370c8f8
commit
d80e6c399b
8 changed files with 1786 additions and 3 deletions
494
src/input_common/helpers/joycon_protocol/joycon_types.h
Normal file
494
src/input_common/helpers/joycon_protocol/joycon_types.h
Normal file
|
@ -0,0 +1,494 @@
|
|||
// 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 = 60;
|
||||
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 DriverResult {
|
||||
Success,
|
||||
WrongReply,
|
||||
Timeout,
|
||||
UnsupportedControllerType,
|
||||
HandleInUse,
|
||||
ErrorReadingData,
|
||||
ErrorWritingData,
|
||||
NoDeviceDetected,
|
||||
InvalidHandle,
|
||||
NotSupported,
|
||||
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 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;
|
||||
};
|
||||
|
||||
} // namespace InputCommon::Joycon
|
Loading…
Add table
Add a link
Reference in a new issue