linux/drivers/hid/hid-nintendo.c

// SPDX-License-Identifier: GPL-2.0+
/*
 * HID driver for Nintendo Switch Joy-Cons and Pro Controllers
 *
 * Copyright (c) 2019-2021 Daniel J. Ogorchock <[email protected]>
 * Portions Copyright (c) 2020 Nadia Holmquist Pedersen <[email protected]>
 * Copyright (c) 2022 Emily Strickland <[email protected]>
 * Copyright (c) 2023 Ryan McClelland <[email protected]>
 *
 * The following resources/projects were referenced for this driver:
 *   https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
 *   https://gitlab.com/pjranki/joycon-linux-kernel (Peter Rankin)
 *   https://github.com/FrotBot/SwitchProConLinuxUSB
 *   https://github.com/MTCKC/ProconXInput
 *   https://github.com/Davidobot/BetterJoyForCemu
 *   hid-wiimote kernel hid driver
 *   hid-logitech-hidpp driver
 *   hid-sony driver
 *
 * This driver supports the Nintendo Switch Joy-Cons and Pro Controllers. The
 * Pro Controllers can either be used over USB or Bluetooth.
 *
 * This driver also incorporates support for Nintendo Switch Online controllers
 * for the NES, SNES, Sega Genesis, and N64.
 *
 * The driver will retrieve the factory calibration info from the controllers,
 * so little to no user calibration should be required.
 *
 */

#include "hid-ids.h"
#include <asm/unaligned.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/hid.h>
#include <linux/idr.h>
#include <linux/input.h>
#include <linux/jiffies.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <linux/spinlock.h>

/*
 * Reference the url below for the following HID report defines:
 * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
 */

/* Output Reports */
#define JC_OUTPUT_RUMBLE_AND_SUBCMD
#define JC_OUTPUT_FW_UPDATE_PKT
#define JC_OUTPUT_RUMBLE_ONLY
#define JC_OUTPUT_MCU_DATA
#define JC_OUTPUT_USB_CMD

/* Subcommand IDs */
#define JC_SUBCMD_STATE
#define JC_SUBCMD_MANUAL_BT_PAIRING
#define JC_SUBCMD_REQ_DEV_INFO
#define JC_SUBCMD_SET_REPORT_MODE
#define JC_SUBCMD_TRIGGERS_ELAPSED
#define JC_SUBCMD_GET_PAGE_LIST_STATE
#define JC_SUBCMD_SET_HCI_STATE
#define JC_SUBCMD_RESET_PAIRING_INFO
#define JC_SUBCMD_LOW_POWER_MODE
#define JC_SUBCMD_SPI_FLASH_READ
#define JC_SUBCMD_SPI_FLASH_WRITE
#define JC_SUBCMD_RESET_MCU
#define JC_SUBCMD_SET_MCU_CONFIG
#define JC_SUBCMD_SET_MCU_STATE
#define JC_SUBCMD_SET_PLAYER_LIGHTS
#define JC_SUBCMD_GET_PLAYER_LIGHTS
#define JC_SUBCMD_SET_HOME_LIGHT
#define JC_SUBCMD_ENABLE_IMU
#define JC_SUBCMD_SET_IMU_SENSITIVITY
#define JC_SUBCMD_WRITE_IMU_REG
#define JC_SUBCMD_READ_IMU_REG
#define JC_SUBCMD_ENABLE_VIBRATION
#define JC_SUBCMD_GET_REGULATED_VOLTAGE

/* Input Reports */
#define JC_INPUT_BUTTON_EVENT
#define JC_INPUT_SUBCMD_REPLY
#define JC_INPUT_IMU_DATA
#define JC_INPUT_MCU_DATA
#define JC_INPUT_USB_RESPONSE

/* Feature Reports */
#define JC_FEATURE_LAST_SUBCMD
#define JC_FEATURE_OTA_FW_UPGRADE
#define JC_FEATURE_SETUP_MEM_READ
#define JC_FEATURE_MEM_READ
#define JC_FEATURE_ERASE_MEM_SECTOR
#define JC_FEATURE_MEM_WRITE
#define JC_FEATURE_LAUNCH

/* USB Commands */
#define JC_USB_CMD_CONN_STATUS
#define JC_USB_CMD_HANDSHAKE
#define JC_USB_CMD_BAUDRATE_3M
#define JC_USB_CMD_NO_TIMEOUT
#define JC_USB_CMD_EN_TIMEOUT
#define JC_USB_RESET
#define JC_USB_PRE_HANDSHAKE
#define JC_USB_SEND_UART

/* Magic value denoting presence of user calibration */
#define JC_CAL_USR_MAGIC_0
#define JC_CAL_USR_MAGIC_1
#define JC_CAL_USR_MAGIC_SIZE

/* SPI storage addresses of user calibration data */
#define JC_CAL_USR_LEFT_MAGIC_ADDR
#define JC_CAL_USR_LEFT_DATA_ADDR
#define JC_CAL_USR_LEFT_DATA_END
#define JC_CAL_USR_RIGHT_MAGIC_ADDR
#define JC_CAL_USR_RIGHT_DATA_ADDR
#define JC_CAL_STICK_DATA_SIZE

/* SPI storage addresses of factory calibration data */
#define JC_CAL_FCT_DATA_LEFT_ADDR
#define JC_CAL_FCT_DATA_RIGHT_ADDR

/* SPI storage addresses of IMU factory calibration data */
#define JC_IMU_CAL_FCT_DATA_ADDR
#define JC_IMU_CAL_FCT_DATA_END
#define JC_IMU_CAL_DATA_SIZE
/* SPI storage addresses of IMU user calibration data */
#define JC_IMU_CAL_USR_MAGIC_ADDR
#define JC_IMU_CAL_USR_DATA_ADDR

/* The raw analog joystick values will be mapped in terms of this magnitude */
#define JC_MAX_STICK_MAG
#define JC_STICK_FUZZ
#define JC_STICK_FLAT

/* Hat values for pro controller's d-pad */
#define JC_MAX_DPAD_MAG
#define JC_DPAD_FUZZ
#define JC_DPAD_FLAT

/* Under most circumstances IMU reports are pushed every 15ms; use as default */
#define JC_IMU_DFLT_AVG_DELTA_MS
/* How many samples to sum before calculating average IMU report delta */
#define JC_IMU_SAMPLES_PER_DELTA_AVG
/* Controls how many dropped IMU packets at once trigger a warning message */
#define JC_IMU_DROPPED_PKT_WARNING

/*
 * The controller's accelerometer has a sensor resolution of 16bits and is
 * configured with a range of +-8000 milliGs. Therefore, the resolution can be
 * calculated thus: (2^16-1)/(8000 * 2) = 4.096 digits per milliG
 * Resolution per G (rather than per millliG): 4.096 * 1000 = 4096 digits per G
 * Alternatively: 1/4096 = .0002441 Gs per digit
 */
#define JC_IMU_MAX_ACCEL_MAG
#define JC_IMU_ACCEL_RES_PER_G
#define JC_IMU_ACCEL_FUZZ
#define JC_IMU_ACCEL_FLAT

/*
 * The controller's gyroscope has a sensor resolution of 16bits and is
 * configured with a range of +-2000 degrees/second.
 * Digits per dps: (2^16 -1)/(2000*2) = 16.38375
 * dps per digit: 16.38375E-1 = .0610
 *
 * STMicro recommends in the datasheet to add 15% to the dps/digit. This allows
 * the full sensitivity range to be saturated without clipping. This yields more
 * accurate results, so it's the technique this driver uses.
 * dps per digit (corrected): .0610 * 1.15 = .0702
 * digits per dps (corrected): .0702E-1 = 14.247
 *
 * Now, 14.247 truncating to 14 loses a lot of precision, so we rescale the
 * min/max range by 1000.
 */
#define JC_IMU_PREC_RANGE_SCALE
/* Note: change mag and res_per_dps if prec_range_scale is ever altered */
#define JC_IMU_MAX_GYRO_MAG
#define JC_IMU_GYRO_RES_PER_DPS
#define JC_IMU_GYRO_FUZZ
#define JC_IMU_GYRO_FLAT

/* frequency/amplitude tables for rumble */
struct joycon_rumble_freq_data {};

struct joycon_rumble_amp_data {};

#if IS_ENABLED(CONFIG_NINTENDO_FF)
/*
 * These tables are from
 * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
 */
static const struct joycon_rumble_freq_data joycon_rumble_frequencies[] =;

#define joycon_max_rumble_amp
static const struct joycon_rumble_amp_data joycon_rumble_amplitudes[] =;
static const u16 JC_RUMBLE_DFLT_LOW_FREQ =;
static const u16 JC_RUMBLE_DFLT_HIGH_FREQ =;
static const unsigned short JC_RUMBLE_ZERO_AMP_PKT_CNT =;
#endif /* IS_ENABLED(CONFIG_NINTENDO_FF) */
static const u16 JC_RUMBLE_PERIOD_MS =;

/* States for controller state machine */
enum joycon_ctlr_state {};

/* Controller type received as part of device info */
enum joycon_ctlr_type {};

struct joycon_stick_cal {};

struct joycon_imu_cal {};

/*
 * All the controller's button values are stored in a u32.
 * They can be accessed with bitwise ANDs.
 */
#define JC_BTN_Y
#define JC_BTN_X
#define JC_BTN_B
#define JC_BTN_A
#define JC_BTN_SR_R
#define JC_BTN_SL_R
#define JC_BTN_R
#define JC_BTN_ZR
#define JC_BTN_MINUS
#define JC_BTN_PLUS
#define JC_BTN_RSTICK
#define JC_BTN_LSTICK
#define JC_BTN_HOME
#define JC_BTN_CAP
#define JC_BTN_DOWN
#define JC_BTN_UP
#define JC_BTN_RIGHT
#define JC_BTN_LEFT
#define JC_BTN_SR_L
#define JC_BTN_SL_L
#define JC_BTN_L
#define JC_BTN_ZL

struct joycon_ctlr_button_mapping {};

/*
 * D-pad is configured as buttons for the left Joy-Con only!
 */
static const struct joycon_ctlr_button_mapping left_joycon_button_mappings[] =;

/*
 * The unused *right*-side triggers become the SL/SR triggers for the *left*
 * Joy-Con, if and only if we're not using a charging grip.
 */
static const struct joycon_ctlr_button_mapping left_joycon_s_button_mappings[] =;

static const struct joycon_ctlr_button_mapping right_joycon_button_mappings[] =;

/*
 * The unused *left*-side triggers become the SL/SR triggers for the *right*
 * Joy-Con, if and only if we're not using a charging grip.
 */
static const struct joycon_ctlr_button_mapping right_joycon_s_button_mappings[] =;

static const struct joycon_ctlr_button_mapping procon_button_mappings[] =;

static const struct joycon_ctlr_button_mapping nescon_button_mappings[] =;

static const struct joycon_ctlr_button_mapping snescon_button_mappings[] =;

/*
 * "A", "B", and "C" are mapped positionally, rather than by label (e.g., "A"
 * gets assigned to BTN_EAST instead of BTN_A).
 */
static const struct joycon_ctlr_button_mapping gencon_button_mappings[] =;

/*
 * N64's C buttons get assigned to d-pad directions and registered as buttons.
 */
static const struct joycon_ctlr_button_mapping n64con_button_mappings[] =;

enum joycon_msg_type {};

struct joycon_rumble_output {} __packed;

struct joycon_subcmd_request {} __packed;

struct joycon_subcmd_reply {} __packed;

struct joycon_imu_data {} __packed;

struct joycon_input_report {} __packed;

#define JC_MAX_RESP_SIZE
#define JC_RUMBLE_DATA_SIZE
#define JC_RUMBLE_QUEUE_SIZE

static const char * const joycon_player_led_names[] =;
#define JC_NUM_LEDS
#define JC_NUM_LED_PATTERNS
/* Taken from https://www.nintendo.com/my/support/qa/detail/33822 */
static const enum led_brightness joycon_player_led_patterns[JC_NUM_LED_PATTERNS][JC_NUM_LEDS] =;

/* Each physical controller is associated with a joycon_ctlr struct */
struct joycon_ctlr {};

/* Helper macros for checking controller type */
#define jc_type_is_joycon(ctlr)
#define jc_type_is_procon(ctlr)
#define jc_type_is_chrggrip(ctlr)

/* Does this controller have inputs associated with left joycon? */
#define jc_type_has_left(ctlr)

/* Does this controller have inputs associated with right joycon? */
#define jc_type_has_right(ctlr)

/*
 * Controller device helpers
 *
 * These look at the device ID known to the HID subsystem to identify a device,
 * but take caution: some NSO devices lie about themselves (NES Joy-Cons and
 * Sega Genesis controller). See type helpers below.
 *
 * These helpers are most useful early during the HID probe or in conjunction
 * with the capability helpers below.
 */
static inline bool joycon_device_is_chrggrip(struct joycon_ctlr *ctlr)
{}

/*
 * Controller type helpers
 *
 * These are slightly different than the device-ID-based helpers above. They are
 * generally more reliable, since they can distinguish between, e.g., Genesis
 * versus SNES, or NES Joy-Cons versus regular Switch Joy-Cons. They're most
 * useful for reporting available inputs. For other kinds of distinctions, see
 * the capability helpers below.
 *
 * They have two major drawbacks: (1) they're not available until after we set
 * the reporting method and then request the device info; (2) they can't
 * distinguish all controllers (like the Charging Grip from the Pro controller.)
 */
static inline bool joycon_type_is_left_joycon(struct joycon_ctlr *ctlr)
{}

static inline bool joycon_type_is_right_joycon(struct joycon_ctlr *ctlr)
{}

static inline bool joycon_type_is_procon(struct joycon_ctlr *ctlr)
{}

static inline bool joycon_type_is_snescon(struct joycon_ctlr *ctlr)
{}

static inline bool joycon_type_is_gencon(struct joycon_ctlr *ctlr)
{}

static inline bool joycon_type_is_n64con(struct joycon_ctlr *ctlr)
{}

static inline bool joycon_type_is_left_nescon(struct joycon_ctlr *ctlr)
{}

static inline bool joycon_type_is_right_nescon(struct joycon_ctlr *ctlr)
{}

static inline bool joycon_type_is_any_joycon(struct joycon_ctlr *ctlr)
{}

static inline bool joycon_type_is_any_nescon(struct joycon_ctlr *ctlr)
{}

/*
 * Controller capability helpers
 *
 * These helpers combine the use of the helpers above to detect certain
 * capabilities during initialization. They are always accurate but (since they
 * use type helpers) cannot be used early in the HID probe.
 */
static inline bool joycon_has_imu(struct joycon_ctlr *ctlr)
{}

static inline bool joycon_has_joysticks(struct joycon_ctlr *ctlr)
{}

static inline bool joycon_has_rumble(struct joycon_ctlr *ctlr)
{}

static inline bool joycon_using_usb(struct joycon_ctlr *ctlr)
{}

static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len)
{}

static void joycon_wait_for_input_report(struct joycon_ctlr *ctlr)
{}

/*
 * Sending subcommands and/or rumble data at too high a rate can cause bluetooth
 * controller disconnections.
 */
#define JC_INPUT_REPORT_MIN_DELTA
#define JC_INPUT_REPORT_MAX_DELTA
#define JC_SUBCMD_TX_OFFSET_MS
#define JC_SUBCMD_VALID_DELTA_REQ
#define JC_SUBCMD_RATE_MAX_ATTEMPTS
#define JC_SUBCMD_RATE_LIMITER_USB_MS
#define JC_SUBCMD_RATE_LIMITER_BT_MS
#define JC_SUBCMD_RATE_LIMITER_MS(ctlr)
static void joycon_enforce_subcmd_rate(struct joycon_ctlr *ctlr)
{}

static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len,
				u32 timeout)
{}

static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd, u32 timeout)
{}

static int joycon_send_subcmd(struct joycon_ctlr *ctlr,
			      struct joycon_subcmd_request *subcmd,
			      size_t data_len, u32 timeout)
{}

/* Supply nibbles for flash and on. Ones correspond to active */
static int joycon_set_player_leds(struct joycon_ctlr *ctlr, u8 flash, u8 on)
{}

static int joycon_set_home_led(struct joycon_ctlr *ctlr, enum led_brightness brightness)
{}

static int joycon_request_spi_flash_read(struct joycon_ctlr *ctlr,
					 u32 start_addr, u8 size, u8 **reply)
{}

/*
 * User calibration's presence is denoted with a magic byte preceding it.
 * returns 0 if magic val is present, 1 if not present, < 0 on error
 */
static int joycon_check_for_cal_magic(struct joycon_ctlr *ctlr, u32 flash_addr)
{}

static int joycon_read_stick_calibration(struct joycon_ctlr *ctlr, u16 cal_addr,
					 struct joycon_stick_cal *cal_x,
					 struct joycon_stick_cal *cal_y,
					 bool left_stick)
{}

static const u16 DFLT_STICK_CAL_CEN =;
static const u16 DFLT_STICK_CAL_MAX =;
static const u16 DFLT_STICK_CAL_MIN =;
static void joycon_use_default_calibration(struct hid_device *hdev,
					   struct joycon_stick_cal *cal_x,
					   struct joycon_stick_cal *cal_y,
					   const char *stick, int ret)
{}

static int joycon_request_calibration(struct joycon_ctlr *ctlr)
{}

/*
 * These divisors are calculated once rather than for each sample. They are only
 * dependent on the IMU calibration values. They are used when processing the
 * IMU input reports.
 */
static void joycon_calc_imu_cal_divisors(struct joycon_ctlr *ctlr)
{}

static const s16 DFLT_ACCEL_OFFSET /*= 0*/;
static const s16 DFLT_ACCEL_SCALE =;
static const s16 DFLT_GYRO_OFFSET /*= 0*/;
static const s16 DFLT_GYRO_SCALE  =;
static int joycon_request_imu_calibration(struct joycon_ctlr *ctlr)
{}

static int joycon_set_report_mode(struct joycon_ctlr *ctlr)
{}

static int joycon_enable_rumble(struct joycon_ctlr *ctlr)
{}

static int joycon_enable_imu(struct joycon_ctlr *ctlr)
{}

static s32 joycon_map_stick_val(struct joycon_stick_cal *cal, s32 val)
{}

static void joycon_input_report_parse_imu_data(struct joycon_ctlr *ctlr,
					       struct joycon_input_report *rep,
					       struct joycon_imu_data *imu_data)
{}

static void joycon_parse_imu_report(struct joycon_ctlr *ctlr,
				    struct joycon_input_report *rep)
{}

static void joycon_handle_rumble_report(struct joycon_ctlr *ctlr, struct joycon_input_report *rep)
{}

static void joycon_parse_battery_status(struct joycon_ctlr *ctlr, struct joycon_input_report *rep)
{}

static void joycon_report_left_stick(struct joycon_ctlr *ctlr,
				     struct joycon_input_report *rep)
{}

static void joycon_report_right_stick(struct joycon_ctlr *ctlr,
				      struct joycon_input_report *rep)
{}

static void joycon_report_dpad(struct joycon_ctlr *ctlr,
			       struct joycon_input_report *rep)
{}

static void joycon_report_buttons(struct joycon_ctlr *ctlr,
				  struct joycon_input_report *rep,
				  const struct joycon_ctlr_button_mapping button_mappings[])
{}

static void joycon_parse_report(struct joycon_ctlr *ctlr,
				struct joycon_input_report *rep)
{}

static int joycon_send_rumble_data(struct joycon_ctlr *ctlr)
{}

static void joycon_rumble_worker(struct work_struct *work)
{}

#if IS_ENABLED(CONFIG_NINTENDO_FF)
static struct joycon_rumble_freq_data joycon_find_rumble_freq(u16 freq)
{}

static struct joycon_rumble_amp_data joycon_find_rumble_amp(u16 amp)
{}

static void joycon_encode_rumble(u8 *data, u16 freq_low, u16 freq_high, u16 amp)
{}

static const u16 JOYCON_MAX_RUMBLE_HIGH_FREQ	=;
static const u16 JOYCON_MIN_RUMBLE_HIGH_FREQ	=;
static const u16 JOYCON_MAX_RUMBLE_LOW_FREQ	=;
static const u16 JOYCON_MIN_RUMBLE_LOW_FREQ	=;

static void joycon_clamp_rumble_freqs(struct joycon_ctlr *ctlr)
{}

static int joycon_set_rumble(struct joycon_ctlr *ctlr, u16 amp_r, u16 amp_l,
			     bool schedule_now)
{}

static int joycon_play_effect(struct input_dev *dev, void *data,
						     struct ff_effect *effect)
{}
#endif /* IS_ENABLED(CONFIG_NINTENDO_FF) */

static void joycon_config_left_stick(struct input_dev *idev)
{}

static void joycon_config_right_stick(struct input_dev *idev)
{}

static void joycon_config_dpad(struct input_dev *idev)
{}

static void joycon_config_buttons(struct input_dev *idev,
		 const struct joycon_ctlr_button_mapping button_mappings[])
{}

static void joycon_config_rumble(struct joycon_ctlr *ctlr)
{}

static int joycon_imu_input_create(struct joycon_ctlr *ctlr)
{}

static int joycon_input_create(struct joycon_ctlr *ctlr)
{}

/* Because the subcommand sets all the leds at once, the brightness argument is ignored */
static int joycon_player_led_brightness_set(struct led_classdev *led,
					    enum led_brightness brightness)
{}

static int joycon_home_led_brightness_set(struct led_classdev *led,
					  enum led_brightness brightness)
{}

static DEFINE_IDA(nintendo_player_id_allocator);

static int joycon_leds_create(struct joycon_ctlr *ctlr)
{}

static int joycon_battery_get_property(struct power_supply *supply,
				       enum power_supply_property prop,
				       union power_supply_propval *val)
{}

static enum power_supply_property joycon_battery_props[] =;

static int joycon_power_supply_create(struct joycon_ctlr *ctlr)
{}

static int joycon_read_info(struct joycon_ctlr *ctlr)
{}

static int joycon_init(struct hid_device *hdev)
{}

/* Common handler for parsing inputs */
static int joycon_ctlr_read_handler(struct joycon_ctlr *ctlr, u8 *data,
							      int size)
{}

static int joycon_ctlr_handle_event(struct joycon_ctlr *ctlr, u8 *data,
							      int size)
{}

static int nintendo_hid_event(struct hid_device *hdev,
			      struct hid_report *report, u8 *raw_data, int size)
{}

static int nintendo_hid_probe(struct hid_device *hdev,
			    const struct hid_device_id *id)
{}

static void nintendo_hid_remove(struct hid_device *hdev)
{}

#ifdef CONFIG_PM

static int nintendo_hid_resume(struct hid_device *hdev)
{}

#endif

static const struct hid_device_id nintendo_hid_devices[] =;
MODULE_DEVICE_TABLE(hid, nintendo_hid_devices);

static struct hid_driver nintendo_hid_driver =;
static int __init nintendo_init(void)
{}

static void __exit nintendo_exit(void)
{}

module_init();
module_exit(nintendo_exit);

MODULE_LICENSE();
MODULE_AUTHOR();
MODULE_AUTHOR();
MODULE_AUTHOR();
MODULE_DESCRIPTION();