// SPDX-License-Identifier: GPL-2.0+ /* * HID driver for Nintendo Switch Joy-Cons and Pro Controllers * * Copyright (c) 2019-2021 Daniel J. Ogorchock <djogorchock@gmail.com> * Portions Copyright (c) 2020 Nadia Holmquist Pedersen <nadia@nhp.sh> * Copyright (c) 2022 Emily Strickland <linux@emily.st> * Copyright (c) 2023 Ryan McClelland <rymcclel@gmail.com> * * 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. *
*/
/* SPI storage addresses of factory calibration data */ #define JC_CAL_FCT_DATA_LEFT_ADDR 0x603d #define JC_CAL_FCT_DATA_RIGHT_ADDR 0x6046
/* SPI storage addresses of IMU factory calibration data */ #define JC_IMU_CAL_FCT_DATA_ADDR 0x6020 #define JC_IMU_CAL_FCT_DATA_END 0x6037 #define JC_IMU_CAL_DATA_SIZE \
(JC_IMU_CAL_FCT_DATA_END - JC_IMU_CAL_FCT_DATA_ADDR + 1) /* SPI storage addresses of IMU user calibration data */ #define JC_IMU_CAL_USR_MAGIC_ADDR 0x8026 #define JC_IMU_CAL_USR_DATA_ADDR 0x8028
/* The raw analog joystick values will be mapped in terms of this magnitude */ #define JC_MAX_STICK_MAG 32767 #define JC_STICK_FUZZ 250 #define JC_STICK_FLAT 500
/* Hat values for pro controller's d-pad */ #define JC_MAX_DPAD_MAG 1 #define JC_DPAD_FUZZ 0 #define JC_DPAD_FLAT 0
/* Under most circumstances IMU reports are pushed every 15ms; use as default */ #define JC_IMU_DFLT_AVG_DELTA_MS 15 /* How many samples to sum before calculating average IMU report delta */ #define JC_IMU_SAMPLES_PER_DELTA_AVG 300 /* Controls how many dropped IMU packets at once trigger a warning message */ #define 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
*/ #define JC_IMU_MAX_ACCEL_MAG 32767 #define JC_IMU_ACCEL_RES_PER_G 4096 #define JC_IMU_ACCEL_FUZZ 10 #define JC_IMU_ACCEL_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.
*/ #define JC_IMU_PREC_RANGE_SCALE 1000 /* Note: change mag and res_per_dps if prec_range_scale is ever altered */ #define JC_IMU_MAX_GYRO_MAG 32767000/* (2^16-1)*1000 */ #define JC_IMU_GYRO_RES_PER_DPS 14247/* (14.247*1000) */ #define JC_IMU_GYRO_FUZZ 10 #define JC_IMU_GYRO_FLAT 0
/* States for controller state machine */ enum joycon_ctlr_state {
JOYCON_CTLR_STATE_INIT,
JOYCON_CTLR_STATE_READ,
JOYCON_CTLR_STATE_REMOVED,
JOYCON_CTLR_STATE_SUSPENDED,
};
/* Controller type received as part of device info */ enum joycon_ctlr_type {
JOYCON_CTLR_TYPE_JCL = 0x01,
JOYCON_CTLR_TYPE_JCR = 0x02,
JOYCON_CTLR_TYPE_PRO = 0x03,
JOYCON_CTLR_TYPE_NESL = 0x09,
JOYCON_CTLR_TYPE_NESR = 0x0A,
JOYCON_CTLR_TYPE_SNES = 0x0B,
JOYCON_CTLR_TYPE_GEN = 0x0D,
JOYCON_CTLR_TYPE_N64 = 0x0C,
};
/* * 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.
*/ staticconststruct joycon_ctlr_button_mapping left_joycon_s_button_mappings[] = {
{ BTN_TR, JC_BTN_SL_L, },
{ BTN_TR2, JC_BTN_SR_L, },
{ /* sentinel */ },
};
/* * 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.
*/ staticconststruct joycon_ctlr_button_mapping right_joycon_s_button_mappings[] = {
{ BTN_TL, JC_BTN_SL_R, },
{ BTN_TL2, JC_BTN_SR_R, },
{ /* sentinel */ },
};
struct joycon_subcmd_request {
u8 output_id; /* must be 0x01 for subcommand, 0x10 for rumble only */
u8 packet_num; /* incremented every send */
u8 rumble_data[8];
u8 subcmd_id;
u8 data[]; /* length depends on the subcommand */
} __packed;
struct joycon_subcmd_reply {
u8 ack; /* MSB 1 for ACK, 0 for NACK */
u8 id; /* id of requested subcmd */
u8 data[]; /* will be at most 35 bytes */
} __packed;
/* imu */ struct input_dev *imu_input; bool imu_first_packet_received; /* helps in initiating timestamp */ unsignedint imu_timestamp_us; /* timestamp we report to userspace */ unsignedint imu_last_pkt_ms; /* used to calc imu report delta */ /* the following are used to track the average imu report time delta */ unsignedint imu_delta_samples_count; unsignedint imu_delta_samples_sum; unsignedint imu_avg_delta_ms;
};
/* Does this controller have inputs associated with left joycon? */ #define jc_type_has_left(ctlr) \
(ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCL || \
ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO || \
ctlr->ctlr_type == JOYCON_CTLR_TYPE_N64)
/* Does this controller have inputs associated with right joycon? */ #define jc_type_has_right(ctlr) \
(ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCR || \
ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO)
/* * 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.
*/ staticinlinebool joycon_device_is_chrggrip(struct joycon_ctlr *ctlr)
{ return ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_CHRGGRIP;
}
/* * 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.)
*/ staticinlinebool joycon_type_is_left_joycon(struct joycon_ctlr *ctlr)
{ return ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCL;
}
/* * 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.
*/ staticinlinebool joycon_has_imu(struct joycon_ctlr *ctlr)
{ return joycon_device_is_chrggrip(ctlr) ||
joycon_type_is_any_joycon(ctlr) ||
joycon_type_is_procon(ctlr);
}
buf = kmemdup(data, len, GFP_KERNEL); if (!buf) return -ENOMEM;
ret = hid_hw_output_report(hdev, buf, len);
kfree(buf); if (ret < 0)
hid_dbg(hdev, "Failed to send output report ret=%d\n", ret); return ret;
}
staticvoid joycon_wait_for_input_report(struct joycon_ctlr *ctlr)
{ int ret;
/* * If we are in the proper reporting mode, wait for an input * report prior to sending the subcommand. This improves * reliability considerably.
*/ if (ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) { unsignedlong flags;
spin_lock_irqsave(&ctlr->lock, flags);
ctlr->received_input_report = false;
spin_unlock_irqrestore(&ctlr->lock, flags);
ret = wait_event_timeout(ctlr->wait,
ctlr->received_input_report,
HZ / 4); /* We will still proceed, even with a timeout here */ if (!ret)
hid_warn(ctlr->hdev, "timeout waiting for input report\n");
}
}
/* * Sending subcommands and/or rumble data at too high a rate can cause bluetooth * controller disconnections.
*/ #define JC_INPUT_REPORT_MIN_DELTA 8 #define JC_INPUT_REPORT_MAX_DELTA 17 #define JC_SUBCMD_TX_OFFSET_MS 4 #define JC_SUBCMD_VALID_DELTA_REQ 3 #define JC_SUBCMD_RATE_MAX_ATTEMPTS 500 #define JC_SUBCMD_RATE_LIMITER_USB_MS 20 #define JC_SUBCMD_RATE_LIMITER_BT_MS 60 #define JC_SUBCMD_RATE_LIMITER_MS(ctlr) ((ctlr)->hdev->bus == BUS_USB ? JC_SUBCMD_RATE_LIMITER_USB_MS : JC_SUBCMD_RATE_LIMITER_BT_MS) staticvoid joycon_enforce_subcmd_rate(struct joycon_ctlr *ctlr)
{ unsignedint current_ms; unsignedlong subcmd_delta; int consecutive_valid_deltas = 0; int attempts = 0; unsignedlong flags;
if (unlikely(ctlr->ctlr_state != JOYCON_CTLR_STATE_READ)) return;
if (attempts >= JC_SUBCMD_RATE_MAX_ATTEMPTS) {
hid_warn(ctlr->hdev, "%s: exceeded max attempts", __func__); return;
}
ctlr->last_subcmd_sent_msecs = current_ms;
/* * Wait a short time after receiving an input report before * transmitting. This should reduce odds of a TX coinciding with an RX. * Minimizing concurrent BT traffic with the controller seems to lower * the rate of disconnections.
*/
msleep(JC_SUBCMD_TX_OFFSET_MS);
}
staticint joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len,
u32 timeout)
{ int ret; int tries = 2;
/* * The controller occasionally seems to drop subcommands. In testing, * doing one retry after a timeout appears to always work.
*/ while (tries--) {
joycon_enforce_subcmd_rate(ctlr);
ret = __joycon_hid_send(ctlr->hdev, data, len); if (ret < 0) {
memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE); return ret;
}
ret = wait_event_timeout(ctlr->wait, ctlr->received_resp,
timeout); if (!ret) {
hid_dbg(ctlr->hdev, "synchronous send/receive timed out\n"); if (tries) {
hid_dbg(ctlr->hdev, "retrying sync send after timeout\n");
}
memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE);
ret = -ETIMEDOUT;
} else {
ret = 0; break;
}
}
spin_lock_irqsave(&ctlr->lock, flags); /* * If the controller has been removed, just return ENODEV so the LED * subsystem doesn't print invalid errors on removal.
*/ if (ctlr->ctlr_state == JOYCON_CTLR_STATE_REMOVED) {
spin_unlock_irqrestore(&ctlr->lock, flags); return -ENODEV;
}
memcpy(subcmd->rumble_data, ctlr->rumble_data[ctlr->rumble_queue_tail],
JC_RUMBLE_DATA_SIZE);
spin_unlock_irqrestore(&ctlr->lock, flags);
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
*/ staticint 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;
/* 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)
joycon_use_default_calibration(ctlr->hdev,
&ctlr->left_stick_cal_x,
&ctlr->left_stick_cal_y, "left", ret);
/* 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)
joycon_use_default_calibration(ctlr->hdev,
&ctlr->right_stick_cal_x,
&ctlr->right_stick_cal_y, "right", ret);
/* * 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.
*/ staticvoid joycon_calc_imu_cal_divisors(struct joycon_ctlr *ctlr)
{ int i, divz = 0;
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];
if (ctlr->imu_cal_accel_divisor[i] == 0) {
ctlr->imu_cal_accel_divisor[i] = 1;
divz++;
}
/* 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_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",
ret);
for (i = 0; i < 3; i++) {
ctlr->accel_cal.offset[i] = DFLT_ACCEL_OFFSET;
ctlr->accel_cal.scale[i] = DFLT_ACCEL_SCALE;
ctlr->gyro_cal.offset[i] = DFLT_GYRO_OFFSET;
ctlr->gyro_cal.scale[i] = DFLT_GYRO_SCALE;
}
joycon_calc_imu_cal_divisors(ctlr); return ret;
}
/* IMU calibration parsing */ for (i = 0; i < 3; i++) { int j = i * 2;
/* * 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 { unsignedint delta = msecs - last_msecs; unsignedint dropped_pkts; unsignedint dropped_threshold;
/* 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]);
/* * 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;
}
}
spin_lock_irqsave(&ctlr->lock, flags); if (IS_ENABLED(CONFIG_NINTENDO_FF) && rep->vibrator_report &&
ctlr->ctlr_state != JOYCON_CTLR_STATE_REMOVED &&
(msecs - ctlr->rumble_msecs) >= JC_RUMBLE_PERIOD_MS &&
(ctlr->rumble_queue_head != ctlr->rumble_queue_tail ||
ctlr->rumble_zero_countdown > 0)) { /* * When this value reaches 0, we know we've sent multiple * packets to the controller instructing it to disable rumble. * We can safely stop sending periodic rumble packets until the * next ff effect.
*/ if (ctlr->rumble_zero_countdown > 0)
ctlr->rumble_zero_countdown--;
queue_work(ctlr->rumble_queue, &ctlr->rumble_worker);
}
spin_lock_irqsave(&ctlr->lock, flags);
ctlr->last_input_report_msecs = msecs; /* * Was this input report a reasonable time delta compared to the prior * report? We use this information to decide when a safe time is to send * rumble packets or subcommand packets.
*/ if (report_delta_ms >= JC_INPUT_REPORT_MIN_DELTA &&
report_delta_ms <= JC_INPUT_REPORT_MAX_DELTA) { if (ctlr->consecutive_valid_report_deltas < JC_SUBCMD_VALID_DELTA_REQ)
ctlr->consecutive_valid_report_deltas++;
} else {
ctlr->consecutive_valid_report_deltas = 0;
} /* * Our consecutive valid report tracking is only relevant for * bluetooth-connected controllers. For USB devices, we're beholden to * USB's underlying polling rate anyway. Always set to the consecutive * delta requirement.
*/ if (ctlr->hdev->bus == BUS_USB)
ctlr->consecutive_valid_report_deltas = JC_SUBCMD_VALID_DELTA_REQ;
spin_unlock_irqrestore(&ctlr->lock, flags);
/* * Immediately after receiving a report is the most reliable time to * send a subcommand to the controller. Wake any subcommand senders * waiting for a report.
*/ if (unlikely(mutex_is_locked(&ctlr->output_mutex))) {
spin_lock_irqsave(&ctlr->lock, flags);
ctlr->received_input_report = true;
spin_unlock_irqrestore(&ctlr->lock, flags);
wake_up(&ctlr->wait);
}
/* parse IMU data if present */ if ((rep->id == JC_INPUT_IMU_DATA) && joycon_has_imu(ctlr))
joycon_parse_imu_report(ctlr, rep);
}
spin_lock_irqsave(&ctlr->lock, flags); /* * If the controller has been removed, just return ENODEV so the LED * subsystem doesn't print invalid errors on removal.
*/ if (ctlr->ctlr_state == JOYCON_CTLR_STATE_REMOVED) {
spin_unlock_irqrestore(&ctlr->lock, flags); return -ENODEV;
}
memcpy(rumble_output.rumble_data,
ctlr->rumble_data[ctlr->rumble_queue_tail],
JC_RUMBLE_DATA_SIZE);
spin_unlock_irqrestore(&ctlr->lock, flags);
while (again) {
mutex_lock(&ctlr->output_mutex);
ret = joycon_send_rumble_data(ctlr);
mutex_unlock(&ctlr->output_mutex);
/* -ENODEV means the controller was just unplugged */
spin_lock_irqsave(&ctlr->lock, flags); if (ret < 0 && ret != -ENODEV &&
ctlr->ctlr_state != JOYCON_CTLR_STATE_REMOVED)
hid_warn(ctlr->hdev, "Failed to set rumble; e=%d", ret);
ctlr->rumble_msecs = jiffies_to_msecs(jiffies); if (ctlr->rumble_queue_tail != ctlr->rumble_queue_head) { if (++ctlr->rumble_queue_tail >= JC_RUMBLE_QUEUE_SIZE)
ctlr->rumble_queue_tail = 0;
} else {
again = false;
}
spin_unlock_irqrestore(&ctlr->lock, flags);
}
}
/* Did we overrun the circular buffer? * If so, be sure we keep the latest intended rumble state.
*/ if (next_rq_head == ctlr->rumble_queue_tail) {
hid_dbg(ctlr->hdev, "rumble queue is full"); /* overwrite the prior value at the end of the circular buf */
next_rq_head = ctlr->rumble_queue_head;
}
if (joycon_has_imu(ctlr)) {
ret = joycon_imu_input_create(ctlr); if (ret) return ret;
}
if (joycon_has_rumble(ctlr))
joycon_config_rumble(ctlr);
return0;
}
/* Because the subcommand sets all the leds at once, the brightness argument is ignored */ staticint joycon_player_led_brightness_set(struct led_classdev *led, enum led_brightness brightness)
{ struct device *dev = led->dev->parent; struct hid_device *hdev = to_hid_device(dev); struct joycon_ctlr *ctlr; int val = 0; int i; int ret;
ctlr = hid_get_drvdata(hdev); if (!ctlr) {
hid_err(hdev, "No controller data\n"); return -ENODEV;
}
for (i = 0; i < JC_NUM_LEDS; i++)
val |= ctlr->leds[i].brightness << i;
mutex_lock(&ctlr->output_mutex);
ret = joycon_set_player_leds(ctlr, 0, val);
mutex_unlock(&ctlr->output_mutex);
ctlr = hid_get_drvdata(hdev); if (!ctlr) {
hid_err(hdev, "No controller data\n"); return -ENODEV;
}
mutex_lock(&ctlr->output_mutex);
ret = joycon_set_home_led(ctlr, brightness);
mutex_unlock(&ctlr->output_mutex); return ret;
}
static DEFINE_IDA(nintendo_player_id_allocator);
staticint joycon_leds_create(struct joycon_ctlr *ctlr)
{ struct hid_device *hdev = ctlr->hdev; struct device *dev = &hdev->dev; constchar *d_name = dev_name(dev); struct led_classdev *led; int led_val = 0; char *name; int ret; int i; int player_led_pattern;
/* configure the player LEDs */
ctlr->player_id = U32_MAX;
ret = ida_alloc(&nintendo_player_id_allocator, GFP_KERNEL); if (ret < 0) {
hid_warn(hdev, "Failed to allocate player ID, skipping; ret=%d\n", ret); goto home_led;
}
ctlr->player_id = ret;
player_led_pattern = ret % JC_NUM_LED_PATTERNS;
hid_info(ctlr->hdev, "assigned player %d led pattern", player_led_pattern + 1);
for (i = 0; i < JC_NUM_LEDS; i++) {
name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%s",
d_name, "green",
joycon_player_led_names[i]); if (!name) return -ENOMEM;
led_val |= joycon_player_led_patterns[player_led_pattern][i] << i;
}
mutex_lock(&ctlr->output_mutex);
ret = joycon_set_player_leds(ctlr, 0, led_val);
mutex_unlock(&ctlr->output_mutex); if (ret) {
hid_warn(hdev, "Failed to set players LEDs, skipping registration; ret=%d\n", ret); goto home_led;
}
for (i = 0; i < JC_NUM_LEDS; i++) {
led = &ctlr->leds[i];
ret = devm_led_classdev_register(&hdev->dev, led); if (ret) {
hid_err(hdev, "Failed to register player %d LED; ret=%d\n", i + 1, ret); return ret;
}
}
home_led: /* configure the home LED */ if (jc_type_has_right(ctlr)) {
name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%s",
d_name, "blue",
LED_FUNCTION_PLAYER5); if (!name) return -ENOMEM;
/* Set the home LED to 0 as default state */
mutex_lock(&ctlr->output_mutex);
ret = joycon_set_home_led(ctlr, 0);
mutex_unlock(&ctlr->output_mutex); if (ret) {
hid_warn(hdev, "Failed to set home LED, skipping registration; ret=%d\n", ret); return0;
}
ret = devm_led_classdev_register(&hdev->dev, led); if (ret) {
hid_err(hdev, "Failed to register home LED; ret=%d\n", ret); return ret;
}
}
return0;
}
staticint joycon_battery_get_property(struct power_supply *supply, enum power_supply_property prop, union power_supply_propval *val)
{ struct joycon_ctlr *ctlr = power_supply_get_drvdata(supply); unsignedlong flags; int ret = 0;
u8 capacity; bool charging; bool powered;
/* * Retrieve the type so we can distinguish the controller type * Unfortantly the hdev->product can't always be used due to a ?bug? * with the NSO Genesis controller. Over USB, it will report the * PID as 0x201E, but over bluetooth it will report the PID as 0x2017 * which is the same as the NSO SNES controller. This is different from * the rest of the controllers which will report the same PID over USB * and bluetooth.
*/
ctlr->ctlr_type = report->subcmd_reply.data[2];
hid_dbg(ctlr->hdev, "controller type = 0x%02X\n", ctlr->ctlr_type);
return0;
}
staticint joycon_init(struct hid_device *hdev)
{ struct joycon_ctlr *ctlr = hid_get_drvdata(hdev); int ret = 0;
mutex_lock(&ctlr->output_mutex); /* if handshake command fails, assume ble pro controller */ if (joycon_using_usb(ctlr) && !joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ)) {
hid_dbg(hdev, "detected USB controller\n"); /* set baudrate for improved latency */
ret = joycon_send_usb(ctlr, JC_USB_CMD_BAUDRATE_3M, HZ); if (ret) { /* * We can function with the default baudrate. * Provide a warning, and continue on.
*/
hid_warn(hdev, "Failed to set baudrate (ret=%d), continuing anyway\n", ret);
} /* handshake */
ret = joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ); if (ret) {
hid_err(hdev, "Failed handshake; ret=%d\n", ret); goto out_unlock;
} /* * Set no timeout (to keep controller in USB mode). * This doesn't send a response, so ignore the timeout.
*/
joycon_send_usb(ctlr, JC_USB_CMD_NO_TIMEOUT, HZ/10);
} elseif (jc_type_is_chrggrip(ctlr)) {
hid_err(hdev, "Failed charging grip handshake\n");
ret = -ETIMEDOUT; goto out_unlock;
}
/* needed to retrieve the controller type */
ret = joycon_read_info(ctlr); if (ret) {
hid_err(hdev, "Failed to retrieve controller info; ret=%d\n",
ret); goto out_unlock;
}
if (joycon_has_joysticks(ctlr)) { /* get controller calibration data, and parse it */
ret = joycon_request_calibration(ctlr); if (ret) { /* * We can function with default calibration, but it may be * inaccurate. Provide a warning, and continue on.
*/
hid_warn(hdev, "Analog stick positions may be inaccurate\n");
}
}
if (joycon_has_imu(ctlr)) { /* get IMU calibration data, and parse it */
ret = joycon_request_imu_calibration(ctlr); if (ret) { /* * We can function with default calibration, but it may be * inaccurate. Provide a warning, and continue on.
*/
hid_warn(hdev, "Unable to read IMU calibration data\n");
}
/* Enable the IMU */
ret = joycon_enable_imu(ctlr); if (ret) {
hid_err(hdev, "Failed to enable the IMU; ret=%d\n", ret); goto out_unlock;
}
}
/* Set the reporting mode to 0x30, which is the full report mode */
ret = joycon_set_report_mode(ctlr); if (ret) {
hid_err(hdev, "Failed to set report mode; ret=%d\n", ret); goto out_unlock;
}
if (joycon_has_rumble(ctlr)) { /* Enable rumble */
ret = joycon_enable_rumble(ctlr); if (ret) {
hid_err(hdev, "Failed to enable rumble; ret=%d\n", ret); goto out_unlock;
}
}
/* Common handler for parsing inputs */ staticint joycon_ctlr_read_handler(struct joycon_ctlr *ctlr, u8 *data, int size)
{ if (data[0] == JC_INPUT_SUBCMD_REPLY || data[0] == JC_INPUT_IMU_DATA ||
data[0] == JC_INPUT_MCU_DATA) { if (size >= 12) /* make sure it contains the input report */
joycon_parse_report(ctlr,
(struct joycon_input_report *)data);
}
return0;
}
staticint joycon_ctlr_handle_event(struct joycon_ctlr *ctlr, u8 *data, int size)
{ int ret = 0; bool match = false; struct joycon_input_report *report;
if (unlikely(mutex_is_locked(&ctlr->output_mutex)) &&
ctlr->msg_type != JOYCON_MSG_TYPE_NONE) { switch (ctlr->msg_type) { case JOYCON_MSG_TYPE_USB: if (size < 2) break; if (data[0] == JC_INPUT_USB_RESPONSE &&
data[1] == ctlr->usb_ack_match)
match = true; break; case JOYCON_MSG_TYPE_SUBCMD: if (size < sizeof(struct joycon_input_report) ||
data[0] != JC_INPUT_SUBCMD_REPLY) break;
report = (struct joycon_input_report *)data; if (report->subcmd_reply.id == ctlr->subcmd_ack_match)
match = true; break; default: break;
}
ret = hid_parse(hdev); if (ret) {
hid_err(hdev, "HID parse failed\n"); goto err_wq;
}
/* * Patch the hw version of pro controller/joycons, so applications can * distinguish between the default HID mappings and the mappings defined * by the Linux game controller spec. This is important for the SDL2 * library, which has a game controller database, which uses device ids * in combination with version as a key.
*/
hdev->version |= 0x8000;
ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); if (ret) {
hid_err(hdev, "HW start failed\n"); goto err_wq;
}
ret = hid_hw_open(hdev); if (ret) {
hid_err(hdev, "cannot start hardware I/O\n"); goto err_stop;
}
hid_device_io_start(hdev);
ret = joycon_init(hdev); if (ret) {
hid_err(hdev, "Failed to initialize controller; ret=%d\n", ret); goto err_close;
}
/* Initialize the leds */
ret = joycon_leds_create(ctlr); if (ret) {
hid_err(hdev, "Failed to create leds; ret=%d\n", ret); goto err_close;
}
/* Initialize the battery power supply */
ret = joycon_power_supply_create(ctlr); if (ret) {
hid_err(hdev, "Failed to create power_supply; ret=%d\n", ret); goto err_ida;
}
ret = joycon_input_create(ctlr); if (ret) {
hid_err(hdev, "Failed to create input device; ret=%d\n", ret); goto err_ida;
}
hid_dbg(hdev, "resume\n"); if (!joycon_using_usb(ctlr)) {
hid_dbg(hdev, "no-op resume for bt ctlr\n");
ctlr->ctlr_state = JOYCON_CTLR_STATE_READ; return0;
}
ret = joycon_init(hdev); if (ret)
hid_err(hdev, "Failed to restore controller after resume: %d\n",
ret); else
ctlr->ctlr_state = JOYCON_CTLR_STATE_READ;
hid_dbg(hdev, "suspend: %d\n", message.event); /* * Avoid any blocking loops in suspend/resume transitions. * * joycon_enforce_subcmd_rate() can result in repeated retries if for * whatever reason the controller stops providing input reports. * * This has been observed with bluetooth controllers which lose * connectivity prior to suspend (but not long enough to result in * complete disconnection).
*/
ctlr->ctlr_state = JOYCON_CTLR_STATE_SUSPENDED; return0;
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ryan McClelland <rymcclel@gmail.com>");
MODULE_AUTHOR("Emily Strickland <linux@emily.st>");
MODULE_AUTHOR("Daniel J. Ogorchock <djogorchock@gmail.com>");
MODULE_DESCRIPTION("Driver for Nintendo Switch Controllers");
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.24Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-07)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Artikel über Sicherheit
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.