summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorNadia Holmquist Pedersen2021-01-07 18:08:28 +0100
committerNadia Holmquist Pedersen2021-01-07 18:08:28 +0100
commitffce16e529f8c3cacf1e9084486664509fb32cc9 (patch)
treeda5749cb9c5a256df5cca96a04f2de50f0179564
parent2f0de9deadcf1a81447c1f91ddf6b244441ba16e (diff)
downloadaur-ffce16e529f8c3cacf1e9084486664509fb32cc9.tar.gz
Update for latest hid-nintendo source
-rw-r--r--.SRCINFO6
-rw-r--r--PKGBUILD6
-rw-r--r--hid-nintendo.c775
3 files changed, 585 insertions, 202 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 2ab5e19014ec..e5377ebea451 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,7 +1,7 @@
pkgbase = hid-nintendo-nso-dkms
pkgdesc = HID driver for Nintendo Switch controllers patched with Switch Online NES and SNES controller support.
- pkgver = 1.1
- pkgrel = 2
+ pkgver = 1.2
+ pkgrel = 1
url = https://github.com/nadiaholmquist/linux/tree/hid-nintendo
arch = x86_64
arch = aarch64
@@ -12,7 +12,7 @@ pkgbase = hid-nintendo-nso-dkms
source = dkms.conf
source = hid-ids.h
source = Makefile
- md5sums = dbc0bd6f144503547cb6e140c1ea2729
+ md5sums = f6592f96d94c215e3de807bd8a80c612
md5sums = 6d97239c33773b3f2fc5d497e98a1017
md5sums = 6d1c428af9d73b4fd493ee1d4465700b
md5sums = 3a628988ca479023b45d2bd6b9b1cfb8
diff --git a/PKGBUILD b/PKGBUILD
index 383b4ed2826d..e48f3394d6fe 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -1,8 +1,8 @@
_pkgbase=hid-nintendo
pkgname=hid-nintendo-nso-dkms
-pkgver=1.1
-pkgrel=2
+pkgver=1.2
+pkgrel=1
pkgdesc="HID driver for Nintendo Switch controllers patched with Switch Online NES and SNES controller support."
arch=('x86_64' 'aarch64')
url="https://github.com/nadiaholmquist/linux/tree/hid-nintendo"
@@ -11,7 +11,7 @@ depends=('dkms')
source=('hid-nintendo.c' 'dkms.conf' 'hid-ids.h' 'Makefile')
conflicts=(hid-nintendo-dkms)
-md5sums=('dbc0bd6f144503547cb6e140c1ea2729'
+md5sums=('f6592f96d94c215e3de807bd8a80c612'
'6d97239c33773b3f2fc5d497e98a1017'
'6d1c428af9d73b4fd493ee1d4465700b'
'3a628988ca479023b45d2bd6b9b1cfb8')
diff --git a/hid-nintendo.c b/hid-nintendo.c
index 71d564e74b7e..56e9b8fbd9d4 100644
--- a/hid-nintendo.c
+++ b/hid-nintendo.c
@@ -2,7 +2,7 @@
/*
* HID driver for Nintendo Switch Joy-Cons and Pro Controllers
*
- * Copyright (c) 2019 Daniel J. Ogorchock <djogorchock@gmail.com>
+ * Copyright (c) 2019-2020 Daniel J. Ogorchock <djogorchock@gmail.com>
*
* The following resources/projects were referenced for this driver:
* https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
@@ -23,8 +23,10 @@
*/
#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/input.h>
#include <linux/jiffies.h>
@@ -96,33 +98,83 @@ static const u8 JC_USB_RESET = 0x06;
static const u8 JC_USB_PRE_HANDSHAKE = 0x91;
static const u8 JC_USB_SEND_UART = 0x92;
+/* Magic value denoting presence of user calibration */
+static const u16 JC_CAL_USR_MAGIC_0 = 0xB2;
+static const u16 JC_CAL_USR_MAGIC_1 = 0xA1;
+static const u8 JC_CAL_USR_MAGIC_SIZE = 2;
+
+/* SPI storage addresses of user calibration data */
+static const u16 JC_CAL_USR_LEFT_MAGIC_ADDR = 0x8010;
+static const u16 JC_CAL_USR_LEFT_DATA_ADDR = 0x8012;
+static const u16 JC_CAL_USR_LEFT_DATA_END = 0x801A;
+static const u16 JC_CAL_USR_RIGHT_MAGIC_ADDR = 0x801B;
+static const u16 JC_CAL_USR_RIGHT_DATA_ADDR = 0x801D;
+#define JC_CAL_STICK_DATA_SIZE \
+ (JC_CAL_USR_LEFT_DATA_END - JC_CAL_USR_LEFT_DATA_ADDR + 1)
+
/* SPI storage addresses of factory calibration data */
-static const u16 JC_CAL_DATA_START = 0x603d;
-static const u16 JC_CAL_DATA_END = 0x604e;
-#define JC_CAL_DATA_SIZE (JC_CAL_DATA_END - JC_CAL_DATA_START + 1)
+static const u16 JC_CAL_FCT_DATA_LEFT_ADDR = 0x603d;
+static const u16 JC_CAL_FCT_DATA_RIGHT_ADDR = 0x6046;
/* SPI storage addresses of IMU factory calibration data */
-static const u16 JC_IMU_CAL_DATA_START = 0x6020;
-static const u16 JC_IMU_CAL_DATA_END = 0x6037;
+static const u16 JC_IMU_CAL_FCT_DATA_ADDR = 0x6020;
+static const u16 JC_IMU_CAL_FCT_DATA_END = 0x6037;
#define JC_IMU_CAL_DATA_SIZE \
- (JC_IMU_CAL_DATA_END - JC_IMU_CAL_DATA_START + 1)
+ (JC_IMU_CAL_FCT_DATA_END - JC_IMU_CAL_FCT_DATA_ADDR + 1)
+/* SPI storage addresses of IMU user calibration data */
+static const u16 JC_IMU_CAL_USR_MAGIC_ADDR = 0x8026;
+static const u16 JC_IMU_CAL_USR_DATA_ADDR = 0x8028;
/* The raw analog joystick values will be mapped in terms of this magnitude */
static const u16 JC_MAX_STICK_MAG = 32767;
static const u16 JC_STICK_FUZZ = 250;
static const u16 JC_STICK_FLAT = 500;
-/* The accel axes will be mapped in terms of this magnitude */
-static const u16 JC_MAX_ACCEL_MAG = 32767;
-static const u16 JC_ACCEL_RES = 4096;
-static const u16 JC_ACCEL_FUZZ = 10;
-static const u16 JC_ACCEL_FLAT /*= 0*/;
+/* Hat values for pro controller's d-pad */
+static const u16 JC_MAX_DPAD_MAG = 1;
+static const u16 JC_DPAD_FUZZ /*= 0*/;
+static const u16 JC_DPAD_FLAT /*= 0*/;
+
+/* Under most circumstances IMU reports are pushed every 15ms; use as default */
+static const u16 JC_IMU_DFLT_AVG_DELTA_MS = 15;
+/* How many samples to sum before calculating average IMU report delta */
+static const u16 JC_IMU_SAMPLES_PER_DELTA_AVG = 300;
+/* Controls how many dropped IMU packets at once trigger a warning message */
+static const u16 JC_IMU_DROPPED_PKT_WARNING = 3;
+
+/*
+ * 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
+ */
+static const s32 JC_IMU_MAX_ACCEL_MAG = 32767;
+static const u16 JC_IMU_ACCEL_RES_PER_G = 4096;
+static const u16 JC_IMU_ACCEL_FUZZ = 10;
+static const u16 JC_IMU_ACCEL_FLAT /*= 0*/;
-/* The gyro axes will be mapped in terms of this magnitude */
-static const u16 JC_MAX_GYRO_MAG = 32767;
-static const u16 JC_GYRO_RES = 13371 / 936; /* 14 (14.285) */
-static const u16 JC_GYRO_FUZZ = 10;
-static const u16 JC_GYRO_FLAT /*= 0*/;
+/*
+ * 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.
+ */
+static const s32 JC_IMU_PREC_RANGE_SCALE = 1000;
+/* Note: change mag and res_per_dps if prec_range_scale is ever altered */
+static const s32 JC_IMU_MAX_GYRO_MAG = 32767000; /* (2^16-1)*1000 */
+static const u16 JC_IMU_GYRO_RES_PER_DPS = 14247; /* (14.247*1000) */
+static const u16 JC_IMU_GYRO_FUZZ = 10;
+static const u16 JC_IMU_GYRO_FLAT /*= 0*/;
/* frequency/amplitude tables for rumble */
struct joycon_rumble_freq_data {
@@ -322,10 +374,6 @@ struct joycon_imu_data {
s16 gyro_z;
} __packed;
-struct joycon_imu_report {
- struct joycon_imu_data data[3];
-} __packed;
-
struct joycon_input_report {
u8 id;
u8 timer;
@@ -337,7 +385,8 @@ struct joycon_input_report {
union {
struct joycon_subcmd_reply subcmd_reply;
- struct joycon_imu_report imu_report;
+ /* IMU input reports contain 3 samples */
+ u8 imu_raw_bytes[sizeof(struct joycon_imu_data) * 3];
};
} __packed;
@@ -382,6 +431,10 @@ struct joycon_ctlr {
struct joycon_imu_cal accel_cal;
struct joycon_imu_cal gyro_cal;
+ /* prevents needlessly recalculating these divisors every sample */
+ s32 imu_cal_accel_divisor[3];
+ s32 imu_cal_gyro_divisor[3];
+
/* power supply data */
struct power_supply *battery;
struct power_supply_desc battery_desc;
@@ -400,10 +453,17 @@ struct joycon_ctlr {
u16 rumble_lh_freq;
u16 rumble_rl_freq;
u16 rumble_rh_freq;
+ bool rumble_zero_amp;
/* imu */
struct input_dev *imu_input;
- int timestamp;
+ bool imu_first_packet_received; /* helps in initiating timestamp */
+ unsigned int imu_timestamp_us; /* timestamp we report to userspace */
+ unsigned int imu_last_pkt_ms; /* used to calc imu report delta */
+ /* the following are used to track the average imu report time delta */
+ unsigned int imu_delta_samples_count;
+ unsigned int imu_delta_samples_sum;
+ unsigned int imu_avg_delta_ms;
};
/* Helper macros for checking controller type */
@@ -441,7 +501,11 @@ struct joycon_ctlr {
/* Does this controller have motion sensors */
#define jc_has_imu(ctlr) \
- (jc_type_is_joycon(ctlr) || jc_type_is_procon(ctlr))
+ (!jc_type_is_nescon(ctlr) && !jc_type_is_snescon(ctlr))
+
+/* Does this controller have rumble */
+#define jc_has_rumble(ctlr) \
+ (!jc_type_is_nescon(ctlr) && !jc_type_is_snescon(ctlr))
static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len)
{
@@ -483,12 +547,10 @@ static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len,
ret = wait_event_timeout(ctlr->wait,
ctlr->received_input_report,
HZ / 4);
- spin_lock_irqsave(&ctlr->lock, flags);
/* We will still proceed, even with a timeout here */
if (!ret)
hid_warn(ctlr->hdev,
"timeout waiting for input report\n");
- spin_unlock_irqrestore(&ctlr->lock, flags);
}
ret = __joycon_hid_send(ctlr->hdev, data, len);
@@ -582,38 +644,140 @@ static int joycon_set_player_leds(struct joycon_ctlr *ctlr, u8 flash, u8 on)
return joycon_send_subcmd(ctlr, req, 1, HZ/4);
}
-static const u16 DFLT_STICK_CAL_CEN = 2000;
-static const u16 DFLT_STICK_CAL_MAX = 3500;
-static const u16 DFLT_STICK_CAL_MIN = 500;
-static int joycon_request_calibration(struct joycon_ctlr *ctlr)
+static int joycon_request_spi_flash_read(struct joycon_ctlr *ctlr,
+ u32 start_addr, u8 size, u8 **reply)
{
struct joycon_subcmd_request *req;
- u8 buffer[sizeof(*req) + 5] = { 0 };
struct joycon_input_report *report;
- struct joycon_stick_cal *cal_x;
- struct joycon_stick_cal *cal_y;
+ u8 buffer[sizeof(*req) + 5] = { 0 };
+ u8 *data;
+ int ret;
+
+ if (!reply)
+ return -EINVAL;
+
+ req = (struct joycon_subcmd_request *)buffer;
+ req->subcmd_id = JC_SUBCMD_SPI_FLASH_READ;
+ data = req->data;
+ put_unaligned_le32(start_addr, data);
+ data[4] = size;
+
+ hid_dbg(ctlr->hdev, "requesting SPI flash data\n");
+ ret = joycon_send_subcmd(ctlr, req, 5, HZ);
+ if (ret) {
+ hid_err(ctlr->hdev, "failed reading SPI flash; ret=%d\n", ret);
+ } else {
+ report = (struct joycon_input_report *)ctlr->input_buf;
+ /* The read data starts at the 6th byte */
+ *reply = &report->subcmd_reply.data[5];
+ }
+ return ret;
+}
+
+/*
+ * 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)
+{
+ int ret;
+ u8 *reply;
+
+ ret = joycon_request_spi_flash_read(ctlr, flash_addr,
+ JC_CAL_USR_MAGIC_SIZE, &reply);
+ if (ret)
+ return ret;
+
+ return reply[0] != JC_CAL_USR_MAGIC_0 || reply[1] != JC_CAL_USR_MAGIC_1;
+}
+
+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)
+{
s32 x_max_above;
s32 x_min_below;
s32 y_max_above;
s32 y_min_below;
- u8 *data;
u8 *raw_cal;
int ret;
- req = (struct joycon_subcmd_request *)buffer;
- req->subcmd_id = JC_SUBCMD_SPI_FLASH_READ;
- data = req->data;
- data[0] = 0xFF & JC_CAL_DATA_START;
- data[1] = 0xFF & (JC_CAL_DATA_START >> 8);
- data[2] = 0xFF & (JC_CAL_DATA_START >> 16);
- data[3] = 0xFF & (JC_CAL_DATA_START >> 24);
- data[4] = JC_CAL_DATA_SIZE;
+ ret = joycon_request_spi_flash_read(ctlr, cal_addr,
+ JC_CAL_STICK_DATA_SIZE, &raw_cal);
+ if (ret)
+ return ret;
+
+ /* stick calibration parsing: note the order differs based on stick */
+ if (left_stick) {
+ x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0,
+ 12);
+ y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4,
+ 12);
+ cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0,
+ 12);
+ cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4,
+ 12);
+ x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0,
+ 12);
+ y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4,
+ 12);
+ } else {
+ cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0,
+ 12);
+ cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4,
+ 12);
+ x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0,
+ 12);
+ y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4,
+ 12);
+ x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0,
+ 12);
+ y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4,
+ 12);
+ }
+
+ cal_x->max = cal_x->center + x_max_above;
+ cal_x->min = cal_x->center - x_min_below;
+ cal_y->max = cal_y->center + y_max_above;
+ cal_y->min = cal_y->center - y_min_below;
+
+ return 0;
+}
+
+static const u16 DFLT_STICK_CAL_CEN = 2000;
+static const u16 DFLT_STICK_CAL_MAX = 3500;
+static const u16 DFLT_STICK_CAL_MIN = 500;
+static int joycon_request_calibration(struct joycon_ctlr *ctlr)
+{
+ u16 left_stick_addr = JC_CAL_FCT_DATA_LEFT_ADDR;
+ u16 right_stick_addr = JC_CAL_FCT_DATA_RIGHT_ADDR;
+ int ret;
hid_dbg(ctlr->hdev, "requesting cal data\n");
- ret = joycon_send_subcmd(ctlr, req, 5, HZ);
+
+ /* check if user stick calibrations are present */
+ if (!joycon_check_for_cal_magic(ctlr, JC_CAL_USR_LEFT_MAGIC_ADDR)) {
+ left_stick_addr = JC_CAL_USR_LEFT_DATA_ADDR;
+ hid_info(ctlr->hdev, "using user cal for left stick\n");
+ } else {
+ hid_info(ctlr->hdev, "using factory cal for left stick\n");
+ }
+ if (!joycon_check_for_cal_magic(ctlr, JC_CAL_USR_RIGHT_MAGIC_ADDR)) {
+ right_stick_addr = JC_CAL_USR_RIGHT_DATA_ADDR;
+ hid_info(ctlr->hdev, "using user cal for right stick\n");
+ } else {
+ hid_info(ctlr->hdev, "using factory cal for right stick\n");
+ }
+
+ /* read the left stick calibration data */
+ ret = joycon_read_stick_calibration(ctlr, left_stick_addr,
+ &ctlr->left_stick_cal_x,
+ &ctlr->left_stick_cal_y,
+ true);
if (ret) {
hid_warn(ctlr->hdev,
- "Failed to read stick cal, using defaults; ret=%d\n",
+ "Failed to read left stick cal, using dflts; e=%d\n",
ret);
ctlr->left_stick_cal_x.center = DFLT_STICK_CAL_CEN;
@@ -623,6 +787,17 @@ static int joycon_request_calibration(struct joycon_ctlr *ctlr)
ctlr->left_stick_cal_y.center = DFLT_STICK_CAL_CEN;
ctlr->left_stick_cal_y.max = DFLT_STICK_CAL_MAX;
ctlr->left_stick_cal_y.min = DFLT_STICK_CAL_MIN;
+ }
+
+ /* read the right stick calibration data */
+ ret = joycon_read_stick_calibration(ctlr, right_stick_addr,
+ &ctlr->right_stick_cal_x,
+ &ctlr->right_stick_cal_y,
+ false);
+ if (ret) {
+ hid_warn(ctlr->hdev,
+ "Failed to read right stick cal, using dflts; e=%d\n",
+ ret);
ctlr->right_stick_cal_x.center = DFLT_STICK_CAL_CEN;
ctlr->right_stick_cal_x.max = DFLT_STICK_CAL_MAX;
@@ -631,44 +806,8 @@ static int joycon_request_calibration(struct joycon_ctlr *ctlr)
ctlr->right_stick_cal_y.center = DFLT_STICK_CAL_CEN;
ctlr->right_stick_cal_y.max = DFLT_STICK_CAL_MAX;
ctlr->right_stick_cal_y.min = DFLT_STICK_CAL_MIN;
-
- return ret;
}
- report = (struct joycon_input_report *)ctlr->input_buf;
- raw_cal = &report->subcmd_reply.data[5];
-
- /* left stick calibration parsing */
- cal_x = &ctlr->left_stick_cal_x;
- cal_y = &ctlr->left_stick_cal_y;
-
- x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0, 12);
- y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4, 12);
- cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0, 12);
- cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4, 12);
- x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0, 12);
- y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4, 12);
- cal_x->max = cal_x->center + x_max_above;
- cal_x->min = cal_x->center - x_min_below;
- cal_y->max = cal_y->center + y_max_above;
- cal_y->min = cal_y->center - y_min_below;
-
- /* right stick calibration parsing */
- raw_cal += 9;
- cal_x = &ctlr->right_stick_cal_x;
- cal_y = &ctlr->right_stick_cal_y;
-
- cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0, 12);
- cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4, 12);
- x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0, 12);
- y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4, 12);
- x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0, 12);
- y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4, 12);
- cal_x->max = cal_x->center + x_max_above;
- cal_x->min = cal_x->center - x_min_below;
- cal_y->max = cal_y->center + y_max_above;
- cal_y->min = cal_y->center - y_min_below;
-
hid_dbg(ctlr->hdev, "calibration:\n"
"l_x_c=%d l_x_max=%d l_x_min=%d\n"
"l_y_c=%d l_y_max=%d l_y_min=%d\n"
@@ -690,33 +829,46 @@ static int joycon_request_calibration(struct joycon_ctlr *ctlr)
return 0;
}
+/*
+ * 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)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ ctlr->imu_cal_accel_divisor[i] = ctlr->accel_cal.scale[i] -
+ ctlr->accel_cal.offset[i];
+ ctlr->imu_cal_gyro_divisor[i] = ctlr->gyro_cal.scale[i] -
+ ctlr->gyro_cal.offset[i];
+ }
+}
+
static const s16 DFLT_ACCEL_OFFSET /*= 0*/;
static const s16 DFLT_ACCEL_SCALE = 16384;
static const s16 DFLT_GYRO_OFFSET /*= 0*/;
static const s16 DFLT_GYRO_SCALE = 13371;
static int joycon_request_imu_calibration(struct joycon_ctlr *ctlr)
{
- struct joycon_subcmd_request *req;
- u8 buffer[sizeof(*req) + 5] = { 0 };
- struct joycon_input_report *report;
- u8 *data;
+ u16 imu_cal_addr = JC_IMU_CAL_FCT_DATA_ADDR;
u8 *raw_cal;
int ret;
int i;
- /* request IMU calibration data */
- req = (struct joycon_subcmd_request *)buffer;
- req->subcmd_id = JC_SUBCMD_SPI_FLASH_READ;
- data = req->data;
- data[0] = 0xFF & JC_IMU_CAL_DATA_START;
- data[1] = 0xFF & (JC_IMU_CAL_DATA_START >> 8);
- data[2] = 0xFF & (JC_IMU_CAL_DATA_START >> 16);
- data[3] = 0xFF & (JC_IMU_CAL_DATA_START >> 24);
- data[4] = JC_IMU_CAL_DATA_SIZE;
+ /* check if user calibration exists */
+ if (!joycon_check_for_cal_magic(ctlr, JC_IMU_CAL_USR_MAGIC_ADDR)) {
+ imu_cal_addr = JC_IMU_CAL_USR_DATA_ADDR;
+ hid_info(ctlr->hdev, "using user cal for IMU\n");
+ } else {
+ hid_info(ctlr->hdev, "using factory cal for IMU\n");
+ }
+ /* request IMU calibration data */
hid_dbg(ctlr->hdev, "requesting IMU cal data\n");
- ret = joycon_send_subcmd(ctlr, req, 5, HZ);
-
+ ret = joycon_request_spi_flash_read(ctlr, imu_cal_addr,
+ JC_IMU_CAL_DATA_SIZE, &raw_cal);
if (ret) {
hid_warn(ctlr->hdev,
"Failed to read IMU cal, using defaults; ret=%d\n",
@@ -728,26 +880,22 @@ static int joycon_request_imu_calibration(struct joycon_ctlr *ctlr)
ctlr->gyro_cal.offset[i] = DFLT_GYRO_OFFSET;
ctlr->gyro_cal.scale[i] = DFLT_GYRO_SCALE;
}
+ joycon_calc_imu_cal_divisors(ctlr);
return ret;
}
- report = (struct joycon_input_report *)ctlr->input_buf;
- raw_cal = &report->subcmd_reply.data[5];
-
/* IMU calibration parsing */
for (i = 0; i < 3; i++) {
int j = i * 2;
- ctlr->accel_cal.offset[i] = raw_cal[j + 0] |
- ((s16)raw_cal[j + 1] << 8);
- ctlr->accel_cal.scale[i] = raw_cal[j + 6] |
- ((s16)raw_cal[j + 7] << 8);
- ctlr->gyro_cal.offset[i] = raw_cal[j + 12] |
- ((s16)raw_cal[j + 13] << 8);
- ctlr->gyro_cal.scale[i] = raw_cal[j + 18] |
- ((s16)raw_cal[j + 19] << 8);
+ ctlr->accel_cal.offset[i] = get_unaligned_le16(raw_cal + j);
+ ctlr->accel_cal.scale[i] = get_unaligned_le16(raw_cal + j + 6);
+ ctlr->gyro_cal.offset[i] = get_unaligned_le16(raw_cal + j + 12);
+ ctlr->gyro_cal.scale[i] = get_unaligned_le16(raw_cal + j + 18);
}
+ joycon_calc_imu_cal_divisors(ctlr);
+
hid_dbg(ctlr->hdev, "IMU calibration:\n"
"a_o[0]=%d a_o[1]=%d a_o[2]=%d\n"
"a_s[0]=%d a_s[1]=%d a_s[2]=%d\n"
@@ -826,6 +974,224 @@ static s32 joycon_map_stick_val(struct joycon_stick_cal *cal, s32 val)
return new_val;
}
+static void joycon_input_report_parse_imu_data(struct joycon_ctlr *ctlr,
+ struct joycon_input_report *rep,
+ struct joycon_imu_data *imu_data)
+{
+ u8 *raw = rep->imu_raw_bytes;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ struct joycon_imu_data *data = &imu_data[i];
+
+ data->accel_x = get_unaligned_le16(raw + 0);
+ data->accel_y = get_unaligned_le16(raw + 2);
+ data->accel_z = get_unaligned_le16(raw + 4);
+ data->gyro_x = get_unaligned_le16(raw + 6);
+ data->gyro_y = get_unaligned_le16(raw + 8);
+ data->gyro_z = get_unaligned_le16(raw + 10);
+ /* point to next imu sample */
+ raw += sizeof(struct joycon_imu_data);
+ }
+}
+
+static void joycon_parse_imu_report(struct joycon_ctlr *ctlr,
+ struct joycon_input_report *rep)
+{
+ struct joycon_imu_data imu_data[3] = {0}; /* 3 reports per packet */
+ struct input_dev *idev = ctlr->imu_input;
+ unsigned int msecs = jiffies_to_msecs(jiffies);
+ unsigned int last_msecs = ctlr->imu_last_pkt_ms;
+ int i;
+ int value[6];
+
+ joycon_input_report_parse_imu_data(ctlr, rep, imu_data);
+
+ /*
+ * There are complexities surrounding how we determine the timestamps we
+ * associate with the samples we pass to userspace. The IMU input
+ * reports do not provide us with a good timestamp. There's a quickly
+ * incrementing 8-bit counter per input report, but it is not very
+ * useful for this purpose (it is not entirely clear what rate it
+ * increments at or if it varies based on packet push rate - more on
+ * the push rate below...).
+ *
+ * The reverse engineering work done on the joy-cons and pro controllers
+ * by the community seems to indicate the following:
+ * - The controller samples the IMU every 1.35ms. It then does some of
+ * its own processing, probably averaging the samples out.
+ * - Each imu input report contains 3 IMU samples, (usually 5ms apart).
+ * - In the standard reporting mode (which this driver uses exclusively)
+ * input reports are pushed from the controller as follows:
+ * * joy-con (bluetooth): every 15 ms
+ * * joy-cons (in charging grip via USB): every 15 ms
+ * * pro controller (USB): every 15 ms
+ * * pro controller (bluetooth): every 8 ms (this is the wildcard)
+ *
+ * Further complicating matters is that some bluetooth stacks are known
+ * to alter the controller's packet rate by hardcoding the bluetooth
+ * SSR for the switch controllers (android's stack currently sets the
+ * SSR to 11ms for both the joy-cons and pro controllers).
+ *
+ * In my own testing, I've discovered that my pro controller either
+ * reports IMU sample batches every 11ms or every 15ms. This rate is
+ * stable after connecting. It isn't 100% clear what determines this
+ * rate. Importantly, even when sending every 11ms, none of the samples
+ * are duplicates. This seems to indicate that the time deltas between
+ * reported samples can vary based on the input report rate.
+ *
+ * The solution employed in this driver is to keep track of the average
+ * time delta between IMU input reports. In testing, this value has
+ * proven to be stable, staying at 15ms or 11ms, though other hardware
+ * configurations and bluetooth stacks could potentially see other rates
+ * (hopefully this will become more clear as more people use the
+ * driver).
+ *
+ * Keeping track of the average report delta allows us to submit our
+ * timestamps to userspace based on that. Each report contains 3
+ * samples, so the IMU sampling rate should be avg_time_delta/3. We can
+ * also use this average to detect events where we have dropped a
+ * packet. The userspace timestamp for the samples will be adjusted
+ * accordingly to prevent unwanted behvaior.
+ */
+ if (!ctlr->imu_first_packet_received) {
+ ctlr->imu_timestamp_us = 0;
+ ctlr->imu_delta_samples_count = 0;
+ ctlr->imu_delta_samples_sum = 0;
+ ctlr->imu_avg_delta_ms = JC_IMU_DFLT_AVG_DELTA_MS;
+ ctlr->imu_first_packet_received = true;
+ } else {
+ unsigned int delta = msecs - last_msecs;
+ unsigned int dropped_pkts;
+ unsigned int dropped_threshold;
+
+ /* avg imu report delta housekeeping */
+ ctlr->imu_delta_samples_sum += delta;
+ ctlr->imu_delta_samples_count++;
+ if (ctlr->imu_delta_samples_count >=
+ JC_IMU_SAMPLES_PER_DELTA_AVG) {
+ ctlr->imu_avg_delta_ms = ctlr->imu_delta_samples_sum /
+ ctlr->imu_delta_samples_count;
+ /* don't ever want divide by zero shenanigans */
+ if (ctlr->imu_avg_delta_ms == 0) {
+ ctlr->imu_avg_delta_ms = 1;
+ hid_warn(ctlr->hdev,
+ "calculated avg imu delta of 0\n");
+ }
+ ctlr->imu_delta_samples_count = 0;
+ ctlr->imu_delta_samples_sum = 0;
+ }
+
+ /* useful for debugging IMU sample rate */
+ hid_dbg(ctlr->hdev,
+ "imu_report: ms=%u last_ms=%u delta=%u avg_delta=%u\n",
+ msecs, last_msecs, delta, ctlr->imu_avg_delta_ms);
+
+ /* check if any packets have been dropped */
+ dropped_threshold = ctlr->imu_avg_delta_ms * 3 / 2;
+ dropped_pkts = (delta - min(delta, dropped_threshold)) /
+ ctlr->imu_avg_delta_ms;
+ ctlr->imu_timestamp_us += 1000 * ctlr->imu_avg_delta_ms;
+ if (dropped_pkts > JC_IMU_DROPPED_PKT_WARNING) {
+ hid_warn(ctlr->hdev,
+ "compensating for %u dropped IMU reports\n",
+ dropped_pkts);
+ hid_warn(ctlr->hdev,
+ "delta=%u avg_delta=%u\n",
+ delta, ctlr->imu_avg_delta_ms);
+ }
+ }
+ ctlr->imu_last_pkt_ms = msecs;
+
+ /* Each IMU input report contains three samples */
+ for (i = 0; i < 3; i++) {
+ input_event(idev, EV_MSC, MSC_TIMESTAMP,
+ ctlr->imu_timestamp_us);
+
+ /*
+ * These calculations (which use the controller's calibration
+ * settings to improve the final values) are based on those
+ * found in the community's reverse-engineering repo (linked at
+ * top of driver). For hid-nintendo, we make sure that the final
+ * value given to userspace is always in terms of the axis
+ * resolution we provided.
+ *
+ * Currently only the gyro calculations subtract the calibration
+ * offsets from the raw value itself. In testing, doing the same
+ * for the accelerometer raw values decreased accuracy.
+ *
+ * Note that the gyro values are multiplied by the
+ * precision-saving scaling factor to prevent large inaccuracies
+ * due to truncation of the resolution value which would
+ * otherwise occur. To prevent overflow (without resorting to 64
+ * bit integer math), the mult_frac macro is used.
+ */
+ value[0] = mult_frac((JC_IMU_PREC_RANGE_SCALE *
+ (imu_data[i].gyro_x -
+ ctlr->gyro_cal.offset[0])),
+ ctlr->gyro_cal.scale[0],
+ ctlr->imu_cal_gyro_divisor[0]);
+ value[1] = mult_frac((JC_IMU_PREC_RANGE_SCALE *
+ (imu_data[i].gyro_y -
+ ctlr->gyro_cal.offset[1])),
+ ctlr->gyro_cal.scale[1],
+ ctlr->imu_cal_gyro_divisor[1]);
+ value[2] = mult_frac((JC_IMU_PREC_RANGE_SCALE *
+ (imu_data[i].gyro_z -
+ ctlr->gyro_cal.offset[2])),
+ ctlr->gyro_cal.scale[2],
+ ctlr->imu_cal_gyro_divisor[2]);
+
+ value[3] = ((s32)imu_data[i].accel_x *
+ ctlr->accel_cal.scale[0]) /
+ ctlr->imu_cal_accel_divisor[0];
+ value[4] = ((s32)imu_data[i].accel_y *
+ ctlr->accel_cal.scale[1]) /
+ ctlr->imu_cal_accel_divisor[1];
+ value[5] = ((s32)imu_data[i].accel_z *
+ ctlr->accel_cal.scale[2]) /
+ ctlr->imu_cal_accel_divisor[2];
+
+ hid_dbg(ctlr->hdev, "raw_gyro: g_x=%hd g_y=%hd g_z=%hd\n",
+ imu_data[i].gyro_x, imu_data[i].gyro_y,
+ imu_data[i].gyro_z);
+ hid_dbg(ctlr->hdev, "raw_accel: a_x=%hd a_y=%hd a_z=%hd\n",
+ imu_data[i].accel_x, imu_data[i].accel_y,
+ imu_data[i].accel_z);
+
+ /*
+ * The right joy-con has 2 axes negated, Y and Z. This is due to
+ * the orientation of the IMU in the controller. We negate those
+ * axes' values in order to be consistent with the left joy-con
+ * and the pro controller:
+ * X: positive is pointing toward the triggers
+ * Y: positive is pointing to the left
+ * Z: positive is pointing up (out of the buttons/sticks)
+ * The axes follow the right-hand rule.
+ */
+ if (jc_type_is_joycon(ctlr) && jc_type_has_right(ctlr)) {
+ int j;
+
+ /* negate all but x axis */
+ for (j = 1; j < 6; ++j) {
+ if (j == 3)
+ continue;
+ value[j] *= -1;
+ }
+ }
+
+ input_report_abs(idev, ABS_RX, value[0]);
+ input_report_abs(idev, ABS_RY, value[1]);
+ input_report_abs(idev, ABS_RZ, value[2]);
+ input_report_abs(idev, ABS_X, value[3]);
+ input_report_abs(idev, ABS_Y, value[4]);
+ input_report_abs(idev, ABS_Z, value[5]);
+ input_sync(idev);
+ /* convert to micros and divide by 3 (3 samples per report). */
+ ctlr->imu_timestamp_us += ctlr->imu_avg_delta_ms * 1000 / 3;
+ }
+}
+
static void joycon_parse_report(struct joycon_ctlr *ctlr,
struct joycon_input_report *rep)
{
@@ -836,8 +1202,11 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr,
unsigned long msecs = jiffies_to_msecs(jiffies);
spin_lock_irqsave(&ctlr->lock, flags);
- if (/*IS_ENABLED(CONFIG_NINTENDO_FF) &&*/ rep->vibrator_report &&
- (msecs - ctlr->rumble_msecs) >= JC_RUMBLE_PERIOD_MS)
+ if (/*IS_ENABLED(CONFIG_NINTENDO_FF) && */ jc_has_rumble(ctlr) && \
+ rep->vibrator_report &&
+ (msecs - ctlr->rumble_msecs) >= JC_RUMBLE_PERIOD_MS &&
+ (ctlr->rumble_queue_head != ctlr->rumble_queue_tail ||
+ !ctlr->rumble_zero_amp))
queue_work(ctlr->rumble_queue, &ctlr->rumble_worker);
/* Parse the battery status */
@@ -891,18 +1260,41 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr,
/* report buttons */
input_report_key(dev, BTN_TL, btns & JC_BTN_L);
input_report_key(dev, BTN_TL2, btns & JC_BTN_ZL);
+ input_report_key(dev, BTN_SELECT, btns & JC_BTN_MINUS);
+ input_report_key(dev, BTN_THUMBL, btns & JC_BTN_LSTICK);
+ input_report_key(dev, BTN_Z, btns & JC_BTN_CAP);
+
if (jc_type_is_joycon(ctlr)) {
/* Report the S buttons as the non-existent triggers */
input_report_key(dev, BTN_TR, btns & JC_BTN_SL_L);
input_report_key(dev, BTN_TR2, btns & JC_BTN_SR_L);
+
+ /* Report d-pad as digital buttons for the joy-cons */
+ input_report_key(dev, BTN_DPAD_DOWN,
+ btns & JC_BTN_DOWN);
+ input_report_key(dev, BTN_DPAD_UP, btns & JC_BTN_UP);
+ input_report_key(dev, BTN_DPAD_RIGHT,
+ btns & JC_BTN_RIGHT);
+ input_report_key(dev, BTN_DPAD_LEFT,
+ btns & JC_BTN_LEFT);
+ } else {
+ int hatx = 0;
+ int haty = 0;
+
+ /* d-pad x */
+ if (btns & JC_BTN_LEFT)
+ hatx = -1;
+ else if (btns & JC_BTN_RIGHT)
+ hatx = 1;
+ input_report_abs(dev, ABS_HAT0X, hatx);
+
+ /* d-pad y */
+ if (btns & JC_BTN_UP)
+ haty = -1;
+ else if (btns & JC_BTN_DOWN)
+ haty = 1;
+ input_report_abs(dev, ABS_HAT0Y, haty);
}
- input_report_key(dev, BTN_SELECT, btns & JC_BTN_MINUS);
- input_report_key(dev, BTN_THUMBL, btns & JC_BTN_LSTICK);
- input_report_key(dev, BTN_Z, btns & JC_BTN_CAP);
- input_report_key(dev, BTN_DPAD_DOWN, btns & JC_BTN_DOWN);
- input_report_key(dev, BTN_DPAD_UP, btns & JC_BTN_UP);
- input_report_key(dev, BTN_DPAD_RIGHT, btns & JC_BTN_RIGHT);
- input_report_key(dev, BTN_DPAD_LEFT, btns & JC_BTN_LEFT);
}
if (jc_type_has_right(ctlr)) {
u16 raw_x;
@@ -986,52 +1378,8 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr,
}
/* parse IMU data if present */
- if (rep->id == JC_INPUT_IMU_DATA && jc_has_imu(ctlr)) {
- struct joycon_imu_data *imu_data = rep->imu_report.data;
- struct input_dev *idev = ctlr->imu_input;
- int i;
- int value[6];
-
- for (i = 0; i < 3; i++) { /* there are 3 reports */
- ctlr->timestamp += 5000; /* each is 5 ms apart */
- input_event(idev, EV_MSC, MSC_TIMESTAMP,
- ctlr->timestamp);
-
- /*
- * Rather than convert to floats, we adjust by
- * calibration factors and then multiply by the default
- * scaling values. This way, the consumer only needs to
- * divide by the default scale values.
- */
- value[0] = (imu_data[i].gyro_x -
- ctlr->gyro_cal.offset[0]) *
- DFLT_GYRO_SCALE / ctlr->gyro_cal.scale[0];
- value[1] = (imu_data[i].gyro_y -
- ctlr->gyro_cal.offset[1]) *
- DFLT_GYRO_SCALE / ctlr->gyro_cal.scale[1];
- value[2] = (imu_data[i].gyro_z -
- ctlr->gyro_cal.offset[2]) *
- DFLT_GYRO_SCALE / ctlr->gyro_cal.scale[2];
-
- value[3] = (imu_data[i].accel_x -
- ctlr->accel_cal.offset[0]) *
- DFLT_ACCEL_SCALE / ctlr->accel_cal.scale[0];
- value[4] = (imu_data[i].accel_y -
- ctlr->accel_cal.offset[1]) *
- DFLT_ACCEL_SCALE / ctlr->accel_cal.scale[1];
- value[5] = (imu_data[i].accel_z -
- ctlr->accel_cal.offset[2]) *
- DFLT_ACCEL_SCALE / ctlr->accel_cal.scale[2];
-
- input_report_abs(idev, ABS_RX, value[0]);
- input_report_abs(idev, ABS_RY, value[1]);
- input_report_abs(idev, ABS_RZ, value[2]);
- input_report_abs(idev, ABS_X, value[3]);
- input_report_abs(idev, ABS_Y, value[4]);
- input_report_abs(idev, ABS_Z, value[5]);
- input_sync(idev);
- }
- }
+ if (jc_has_imu(ctlr) && rep->id == JC_INPUT_IMU_DATA)
+ joycon_parse_imu_report(ctlr, rep);
}
static void joycon_rumble_worker(struct work_struct *work)
@@ -1042,6 +1390,10 @@ static void joycon_rumble_worker(struct work_struct *work)
bool again = true;
int ret;
+ if (!jc_has_rumble(ctlr)) {
+ return;
+ }
+
while (again) {
mutex_lock(&ctlr->output_mutex);
ret = joycon_enable_rumble(ctlr, true);
@@ -1154,6 +1506,8 @@ static int joycon_set_rumble(struct joycon_ctlr *ctlr, u16 amp_r, u16 amp_l,
freq_r_high = ctlr->rumble_rh_freq;
freq_l_low = ctlr->rumble_ll_freq;
freq_l_high = ctlr->rumble_lh_freq;
+ /* this flag is used to reduce subcommand traffic */
+ ctlr->rumble_zero_amp = (amp_l == 0) && (amp_r == 0);
spin_unlock_irqrestore(&ctlr->lock, flags);
/* right joy-con */
@@ -1195,7 +1549,6 @@ static int joycon_play_effect(struct input_dev *dev, void *data,
static const unsigned int joycon_button_inputs_l[] = {
BTN_SELECT, BTN_Z, BTN_THUMBL,
- BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT,
BTN_TL, BTN_TL2,
0 /* 0 signals end of array */
};
@@ -1207,6 +1560,11 @@ static const unsigned int joycon_button_inputs_r[] = {
0 /* 0 signals end of array */
};
+/* We report joy-con d-pad inputs as buttons and pro controller as a hat. */
+static const unsigned int joycon_dpad_inputs_jc[] = {
+ BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT,
+};
+
static const unsigned int nescon_button_inputs[] = {
BTN_SELECT, BTN_START, BTN_SOUTH, BTN_EAST, BTN_TL, BTN_TR,
0 /* 0 signals end of array */
@@ -1247,15 +1605,15 @@ static int joycon_input_create(struct joycon_ctlr *ctlr)
imu_name = "Nintendo Switch Left Joy-Con IMU";
break;
case USB_DEVICE_ID_NINTENDO_JOYCONR:
- if (ctlr->ctlr_type == JOYCON_CTLR_TYPE_NESL) {
+ if (ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCR) {
+ name = "Nintendo Switch Right Joy-Con";
+ imu_name = "Nintendo Switch Right Joy-Con IMU";
+ } else if (ctlr->ctlr_type == JOYCON_CTLR_TYPE_NESL) {
name = "Nintendo Switch NES Controller (L)";
imu_name = NULL;
} else if (ctlr->ctlr_type == JOYCON_CTLR_TYPE_NESR) {
name = "Nintendo Switch NES Controller (R)";
imu_name = NULL;
- } else {
- name = "Nintendo Switch Right Joy-Con";
- imu_name = "Nintendo Switch Right Joy-Con IMU";
}
break;
case USB_DEVICE_ID_NINTENDO_SNESCON:
@@ -1278,28 +1636,41 @@ static int joycon_input_create(struct joycon_ctlr *ctlr)
ctlr->input->name = name;
input_set_drvdata(ctlr->input, ctlr);
+ /* set up sticks and buttons */
if (jc_type_has_left(ctlr)) {
- /* set up sticks */
input_set_abs_params(ctlr->input, ABS_X,
-JC_MAX_STICK_MAG, JC_MAX_STICK_MAG,
JC_STICK_FUZZ, JC_STICK_FLAT);
input_set_abs_params(ctlr->input, ABS_Y,
-JC_MAX_STICK_MAG, JC_MAX_STICK_MAG,
JC_STICK_FUZZ, JC_STICK_FLAT);
- /* set up buttons */
+
for (i = 0; joycon_button_inputs_l[i] > 0; i++)
input_set_capability(ctlr->input, EV_KEY,
joycon_button_inputs_l[i]);
+
+ /* configure d-pad differently for joy-con vs pro controller */
+ if (hdev->product != USB_DEVICE_ID_NINTENDO_PROCON) {
+ for (i = 0; joycon_dpad_inputs_jc[i] > 0; i++)
+ input_set_capability(ctlr->input, EV_KEY,
+ joycon_dpad_inputs_jc[i]);
+ } else {
+ input_set_abs_params(ctlr->input, ABS_HAT0X,
+ -JC_MAX_DPAD_MAG, JC_MAX_DPAD_MAG,
+ JC_DPAD_FUZZ, JC_DPAD_FLAT);
+ input_set_abs_params(ctlr->input, ABS_HAT0Y,
+ -JC_MAX_DPAD_MAG, JC_MAX_DPAD_MAG,
+ JC_DPAD_FUZZ, JC_DPAD_FLAT);
+ }
}
if (jc_type_has_right(ctlr)) {
- /* set up sticks */
input_set_abs_params(ctlr->input, ABS_RX,
-JC_MAX_STICK_MAG, JC_MAX_STICK_MAG,
JC_STICK_FUZZ, JC_STICK_FLAT);
input_set_abs_params(ctlr->input, ABS_RY,
-JC_MAX_STICK_MAG, JC_MAX_STICK_MAG,
JC_STICK_FUZZ, JC_STICK_FLAT);
- /* set up buttons */
+
for (i = 0; joycon_button_inputs_r[i] > 0; i++)
input_set_capability(ctlr->input, EV_KEY,
joycon_button_inputs_r[i]);
@@ -1309,8 +1680,12 @@ static int joycon_input_create(struct joycon_ctlr *ctlr)
nescon_button_inputs : snescon_button_inputs;
/* set up d-pad hat */
- input_set_abs_params(ctlr->input, ABS_HAT0X, -1, 1, 0, 0);
- input_set_abs_params(ctlr->input, ABS_HAT0Y, -1, 1, 0, 0);
+ input_set_abs_params(ctlr->input, ABS_HAT0X,
+ -JC_MAX_DPAD_MAG, JC_MAX_DPAD_MAG,
+ JC_DPAD_FUZZ, JC_DPAD_FLAT);
+ input_set_abs_params(ctlr->input, ABS_HAT0Y,
+ -JC_MAX_DPAD_MAG, JC_MAX_DPAD_MAG,
+ JC_DPAD_FUZZ, JC_DPAD_FLAT);
/* set up buttons */
for (i = 0; inputs[i] > 0; i++)
@@ -1324,6 +1699,15 @@ static int joycon_input_create(struct joycon_ctlr *ctlr)
return 0;
}
+ /* Let's report joy-con S triggers separately */
+ if (hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONL) {
+ input_set_capability(ctlr->input, EV_KEY, BTN_TR);
+ input_set_capability(ctlr->input, EV_KEY, BTN_TR2);
+ } else if (hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONR) {
+ input_set_capability(ctlr->input, EV_KEY, BTN_TL);
+ input_set_capability(ctlr->input, EV_KEY, BTN_TL2);
+ }
+
//#if IS_ENABLED(CONFIG_NINTENDO_FF)
/* set up rumble */
input_set_capability(ctlr->input, EV_FF, FF_RUMBLE);
@@ -1356,31 +1740,31 @@ static int joycon_input_create(struct joycon_ctlr *ctlr)
/* configure imu axes */
input_set_abs_params(ctlr->imu_input, ABS_X,
- -JC_MAX_ACCEL_MAG, JC_MAX_ACCEL_MAG,
- JC_ACCEL_FUZZ, JC_ACCEL_FLAT);
+ -JC_IMU_MAX_ACCEL_MAG, JC_IMU_MAX_ACCEL_MAG,
+ JC_IMU_ACCEL_FUZZ, JC_IMU_ACCEL_FLAT);
input_set_abs_params(ctlr->imu_input, ABS_Y,
- -JC_MAX_ACCEL_MAG, JC_MAX_ACCEL_MAG,
- JC_ACCEL_FUZZ, JC_ACCEL_FLAT);
+ -JC_IMU_MAX_ACCEL_MAG, JC_IMU_MAX_ACCEL_MAG,
+ JC_IMU_ACCEL_FUZZ, JC_IMU_ACCEL_FLAT);
input_set_abs_params(ctlr->imu_input, ABS_Z,
- -JC_MAX_ACCEL_MAG, JC_MAX_ACCEL_MAG,
- JC_ACCEL_FUZZ, JC_ACCEL_FLAT);
- input_abs_set_res(ctlr->imu_input, ABS_X, JC_ACCEL_RES);
- input_abs_set_res(ctlr->imu_input, ABS_Y, JC_ACCEL_RES);
- input_abs_set_res(ctlr->imu_input, ABS_Z, JC_ACCEL_RES);
+ -JC_IMU_MAX_ACCEL_MAG, JC_IMU_MAX_ACCEL_MAG,
+ JC_IMU_ACCEL_FUZZ, JC_IMU_ACCEL_FLAT);
+ input_abs_set_res(ctlr->imu_input, ABS_X, JC_IMU_ACCEL_RES_PER_G);
+ input_abs_set_res(ctlr->imu_input, ABS_Y, JC_IMU_ACCEL_RES_PER_G);
+ input_abs_set_res(ctlr->imu_input, ABS_Z, JC_IMU_ACCEL_RES_PER_G);
input_set_abs_params(ctlr->imu_input, ABS_RX,
- -JC_MAX_GYRO_MAG, JC_MAX_GYRO_MAG,
- JC_GYRO_FUZZ, JC_GYRO_FLAT);
+ -JC_IMU_MAX_GYRO_MAG, JC_IMU_MAX_GYRO_MAG,
+ JC_IMU_GYRO_FUZZ, JC_IMU_GYRO_FLAT);
input_set_abs_params(ctlr->imu_input, ABS_RY,
- -JC_MAX_GYRO_MAG, JC_MAX_GYRO_MAG,
- JC_GYRO_FUZZ, JC_GYRO_FLAT);
+ -JC_IMU_MAX_GYRO_MAG, JC_IMU_MAX_GYRO_MAG,
+ JC_IMU_GYRO_FUZZ, JC_IMU_GYRO_FLAT);
input_set_abs_params(ctlr->imu_input, ABS_RZ,
- -JC_MAX_GYRO_MAG, JC_MAX_GYRO_MAG,
- JC_GYRO_FUZZ, JC_GYRO_FLAT);
+ -JC_IMU_MAX_GYRO_MAG, JC_IMU_MAX_GYRO_MAG,
+ JC_IMU_GYRO_FUZZ, JC_IMU_GYRO_FLAT);
- input_abs_set_res(ctlr->imu_input, ABS_RX, JC_GYRO_RES);
- input_abs_set_res(ctlr->imu_input, ABS_RY, JC_GYRO_RES);
- input_abs_set_res(ctlr->imu_input, ABS_RZ, JC_GYRO_RES);
+ input_abs_set_res(ctlr->imu_input, ABS_RX, JC_IMU_GYRO_RES_PER_DPS);
+ input_abs_set_res(ctlr->imu_input, ABS_RY, JC_IMU_GYRO_RES_PER_DPS);
+ input_abs_set_res(ctlr->imu_input, ABS_RZ, JC_IMU_GYRO_RES_PER_DPS);
__set_bit(EV_MSC, ctlr->imu_input->evbit);
__set_bit(MSC_TIMESTAMP, ctlr->imu_input->mscbit);
@@ -1612,6 +1996,7 @@ static int joycon_power_supply_create(struct joycon_ctlr *ctlr)
ctlr->battery_desc.num_properties =
ARRAY_SIZE(joycon_battery_props);
ctlr->battery_desc.get_property = joycon_battery_get_property;
+ ctlr->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
ctlr->battery_desc.use_for_apm = 0;
ctlr->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
name_fmt,
@@ -1854,21 +2239,18 @@ static int nintendo_hid_probe(struct hid_device *hdev,
goto err_mutex;
}
-
- if (!jc_type_is_snescon(ctlr)) {
+ if (!jc_type_is_nescon(ctlr) && !jc_type_is_snescon(ctlr)) {
/* Enable rumble */
ret = joycon_enable_rumble(ctlr, true);
if (ret) {
- hid_err(hdev,
- "Failed to enable rumble; ret=%d\n", ret);
+ hid_err(hdev, "Failed to enable rumble; ret=%d\n", ret);
goto err_mutex;
}
/* Enable the IMU */
ret = joycon_enable_imu(ctlr, true);
if (ret) {
- hid_err(hdev,
- "Failed to enable the IMU; ret=%d\n", ret);
+ hid_err(hdev, "Failed to enable the IMU; ret=%d\n", ret);
goto err_mutex;
}
}
@@ -1969,3 +2351,4 @@ module_hid_driver(nintendo_hid_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Daniel J. Ogorchock <djogorchock@gmail.com>");
MODULE_DESCRIPTION("Driver for Nintendo Switch Controllers");
+