// 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;
}
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.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.